akregator/src

akregator_part.cpp

00001 /*
00002     This file is part of Akregator.
00003 
00004     Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net>
00005                   2005 Frank Osterfeld <frank.osterfeld at kdemail.net>
00006 
00007     This program is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020 
00021     As a special exception, permission is given to link this program
00022     with any edition of Qt, and distribute the resulting executable,
00023     without including the source code for Qt in the source distribution.
00024 */
00025 #include <dcopclient.h>
00026 #include <kaboutdata.h>
00027 #include <kaction.h>
00028 #include <kactionclasses.h>
00029 #include <kactioncollection.h>
00030 #include <kapplication.h>
00031 #include <kconfig.h>
00032 #include <kconfigdialog.h>
00033 #include <kfiledialog.h>
00034 #include <kglobalsettings.h>
00035 #include <khtmldefaults.h>
00036 #include <kinstance.h>
00037 #include <kmainwindow.h>
00038 #include <kmessagebox.h>
00039 #include <knotifyclient.h>
00040 #include <knotifydialog.h>
00041 #include <kpopupmenu.h>
00042 #include <kservice.h>
00043 #include <kstandarddirs.h>
00044 #include <kstdaction.h>
00045 #include <ktempfile.h>
00046 #include <ktrader.h>
00047 #include <kio/netaccess.h>
00048 #include <kparts/browserinterface.h>
00049 #include <kparts/genericfactory.h>
00050 #include <kparts/partmanager.h>
00051 
00052 #include <qfile.h>
00053 #include <qobjectlist.h>
00054 #include <qstringlist.h>
00055 #include <qtimer.h>
00056 #include <qwidgetlist.h>
00057 #include <private/qucomextra_p.h>
00058 
00059 #include <cerrno>
00060 #include <sys/types.h>
00061 #include <signal.h>
00062 #include <stdio.h>
00063 #include <stdlib.h>
00064 #include <unistd.h>
00065 
00066 #include "aboutdata.h"
00067 #include "actionmanagerimpl.h"
00068 #include "akregator_part.h"
00069 #include "akregator_view.h"
00070 #include "akregatorconfig.h"
00071 #include "articlefilter.h"
00072 #include "articleinterceptor.h"
00073 #include "configdialog.h"
00074 #include "fetchqueue.h"
00075 #include "frame.h"
00076 #include "article.h"
00077 #include "kernel.h"
00078 #include "kcursorsaver.h"
00079 #include "notificationmanager.h"
00080 #include "pageviewer.h"
00081 #include "plugin.h"
00082 #include "pluginmanager.h"
00083 #include "storage.h"
00084 #include "storagefactory.h"
00085 #include "storagefactorydummyimpl.h"
00086 #include "storagefactoryregistry.h"
00087 #include "speechclient.h"
00088 #include "trayicon.h"
00089 #include "tagset.h"
00090 #include "tag.h"
00091 
00092 namespace Akregator {
00093 
00094 typedef KParts::GenericFactory<Part> AkregatorFactory;
00095 K_EXPORT_COMPONENT_FACTORY( libakregatorpart, AkregatorFactory )
00096 
00097 BrowserExtension::BrowserExtension(Part *p, const char *name)
00098         : KParts::BrowserExtension( p, name )
00099 {
00100     m_part=p;
00101 }
00102 
00103 void BrowserExtension::saveSettings()
00104 {
00105     m_part->saveSettings();
00106 }
00107 
00108 class Part::ApplyFiltersInterceptor : public ArticleInterceptor
00109 {
00110     public:
00111     virtual void processArticle(Article& article)
00112     {
00113         Filters::ArticleFilterList list = Kernel::self()->articleFilterList();
00114         for (Filters::ArticleFilterList::ConstIterator it = list.begin(); it != list.end(); ++it)
00115             (*it).applyTo(article);
00116     }
00117 };
00118 
00119 Part::Part( QWidget *parentWidget, const char * /*widgetName*/,
00120                               QObject *parent, const char *name, const QStringList& )
00121     : DCOPObject("AkregatorIface")
00122        , MyBasePart(parent, name)
00123        , m_standardListLoaded(false)
00124        , m_shuttingDown(false)
00125        , m_mergedPart(0)
00126        , m_view(0)
00127        , m_backedUpList(false)
00128        , m_storage(0)
00129 {
00130     // we need an instance
00131     setInstance( AkregatorFactory::instance() );
00132 
00133     // start knotifyclient if not already started. makes it work for people who doesn't use full kde, according to kmail devels
00134     KNotifyClient::startDaemon();
00135 
00136     m_standardFeedList = KGlobal::dirs()->saveLocation("data", "akregator/data") + "/feeds.opml";
00137 
00138     m_tagSetPath = KGlobal::dirs()->saveLocation("data", "akregator/data") + "/tagset.xml";
00139 
00140     Backend::StorageFactoryDummyImpl* dummyFactory = new Backend::StorageFactoryDummyImpl();
00141     Backend::StorageFactoryRegistry::self()->registerFactory(dummyFactory, dummyFactory->key());
00142     loadPlugins(); // FIXME: also unload them!
00143 
00144     m_storage = 0;
00145     Backend::StorageFactory* factory = Backend::StorageFactoryRegistry::self()->getFactory(Settings::archiveBackend());
00146    
00147     QStringList storageParams;
00148     
00149     storageParams.append(QString("taggingEnabled=%1").arg(Settings::showTaggingGUI() ? "true" : "false"));
00150     
00151     if (factory != 0)
00152     {
00153         if (factory->allowsMultipleWriteAccess())
00154         {
00155             m_storage = factory->createStorage(storageParams);
00156         } 
00157         else
00158         {
00159             if (tryToLock(factory->name()))
00160                 m_storage = factory->createStorage(storageParams);
00161             else 
00162                 m_storage = dummyFactory->createStorage(storageParams);
00163         }
00164     }
00165     
00166 
00167     if (!m_storage) // Houston, we have a problem
00168     {
00169         m_storage = Backend::StorageFactoryRegistry::self()->getFactory("dummy")->createStorage(storageParams);
00170 
00171         KMessageBox::error(parentWidget, i18n("Unable to load storage backend plugin \"%1\". No feeds are archived.").arg(Settings::archiveBackend()), i18n("Plugin error") );
00172     }
00173 
00174     Filters::ArticleFilterList list;
00175     list.readConfig(Settings::self()->config());
00176     Kernel::self()->setArticleFilterList(list);
00177 
00178     m_applyFiltersInterceptor = new ApplyFiltersInterceptor();
00179     ArticleInterceptorManager::self()->addInterceptor(m_applyFiltersInterceptor);
00180 
00181     m_storage->open(true);
00182     Kernel::self()->setStorage(m_storage);
00183     Backend::Storage::setInstance(m_storage); // TODO: kill this one
00184 
00185     loadTagSet(m_tagSetPath);
00186 
00187     m_actionManager = new ActionManagerImpl(this);
00188     ActionManager::setInstance(m_actionManager);
00189 
00190     m_view = new Akregator::View(this, parentWidget, m_actionManager, "akregator_view");
00191     m_actionManager->initView(m_view);
00192     m_actionManager->setTagSet(Kernel::self()->tagSet());
00193 
00194     m_extension = new BrowserExtension(this, "ak_extension");
00195 
00196     connect(m_view, SIGNAL(setWindowCaption(const QString&)), this, SIGNAL(setWindowCaption(const QString&)));
00197     connect(m_view, SIGNAL(setStatusBarText(const QString&)), this, SIGNAL(setStatusBarText(const QString&)));
00198     connect(m_view, SIGNAL(setProgress(int)), m_extension, SIGNAL(loadingProgress(int)));
00199     connect(m_view, SIGNAL(signalCanceled(const QString&)), this, SIGNAL(canceled(const QString&)));
00200     connect(m_view, SIGNAL(signalStarted(KIO::Job*)), this, SIGNAL(started(KIO::Job*)));
00201     connect(m_view, SIGNAL(signalCompleted()), this, SIGNAL(completed()));
00202 
00203     // notify the part that this is our internal widget
00204     setWidget(m_view);
00205 
00206     TrayIcon* trayIcon = new TrayIcon( getMainWindow() );
00207     TrayIcon::setInstance(trayIcon);
00208     m_actionManager->initTrayIcon(trayIcon);
00209 
00210     connect(trayIcon, SIGNAL(showPart()), this, SIGNAL(showPart()));
00211 
00212     if ( isTrayIconEnabled() )
00213     {
00214         trayIcon->show();
00215         NotificationManager::self()->setWidget(trayIcon, instance());
00216     }
00217     else
00218         NotificationManager::self()->setWidget(getMainWindow(), instance());
00219 
00220     connect( trayIcon, SIGNAL(quitSelected()),
00221             kapp, SLOT(quit())) ;
00222 
00223     connect( m_view, SIGNAL(signalUnreadCountChanged(int)), trayIcon, SLOT(slotSetUnread(int)) );
00224 
00225     connect(kapp, SIGNAL(shutDown()), this, SLOT(slotOnShutdown()));
00226 
00227     m_autosaveTimer = new QTimer(this);
00228     connect(m_autosaveTimer, SIGNAL(timeout()), this, SLOT(slotSaveFeedList()));
00229     m_autosaveTimer->start(5*60*1000); // 5 minutes
00230 
00231     setXMLFile("akregator_part.rc", true);
00232 
00233     initFonts();
00234 
00235     RSS::FileRetriever::setUserAgent(QString("Akregator/%1; librss/remnants").arg(AKREGATOR_VERSION));
00236 }
00237 
00238 void Part::loadPlugins()
00239 {
00240     // "[X-KDE-akregator-plugintype] == 'storage'"
00241     KTrader::OfferList offers = PluginManager::query();
00242 
00243     for( KTrader::OfferList::ConstIterator it = offers.begin(), end = offers.end(); it != end; ++it )
00244     {
00245         Akregator::Plugin* plugin = PluginManager::createFromService(*it);
00246         if (plugin)
00247             plugin->init();
00248     }
00249 }
00250 
00251 void Part::slotOnShutdown()
00252 {
00253     m_shuttingDown = true;
00254     
00255     const QString lockLocation = locateLocal("data", "akregator/lock");
00256     KSimpleConfig config(lockLocation);
00257     config.writeEntry("pid", -1);
00258     config.sync();
00259 
00260     m_autosaveTimer->stop();
00261     saveSettings();
00262     slotSaveFeedList();
00263     saveTagSet(m_tagSetPath);
00264     m_view->slotOnShutdown();
00265     //delete m_view;
00266     delete TrayIcon::getInstance();
00267     TrayIcon::setInstance(0L);
00268     delete m_storage;
00269     m_storage = 0;
00270     //delete m_actionManager;
00271 }
00272 
00273 void Part::slotSettingsChanged()
00274 {
00275     NotificationManager::self()->setWidget(isTrayIconEnabled() ? TrayIcon::getInstance() : getMainWindow(), instance());
00276 
00277     RSS::FileRetriever::setUseCache(Settings::useHTMLCache());
00278 
00279     QStringList fonts;
00280     fonts.append(Settings::standardFont());
00281     fonts.append(Settings::fixedFont());
00282     fonts.append(Settings::sansSerifFont());
00283     fonts.append(Settings::serifFont());
00284     fonts.append(Settings::standardFont());
00285     fonts.append(Settings::standardFont());
00286     fonts.append("0");
00287     Settings::setFonts(fonts);
00288 
00289     if (Settings::minimumFontSize() > Settings::mediumFontSize())
00290         Settings::setMediumFontSize(Settings::minimumFontSize());
00291     saveSettings();
00292     m_view->slotSettingsChanged();
00293     emit signalSettingsChanged();
00294 }
00295 void Part::saveSettings()
00296 {
00297     Kernel::self()->articleFilterList().writeConfig(Settings::self()->config());
00298     m_view->saveSettings();
00299 }
00300 
00301 Part::~Part()
00302 {
00303     kdDebug() << "Part::~Part() enter" << endl;
00304     if (!m_shuttingDown)
00305         slotOnShutdown();
00306     kdDebug() << "Part::~Part(): leaving" << endl;
00307     ArticleInterceptorManager::self()->removeInterceptor(m_applyFiltersInterceptor);
00308     delete m_applyFiltersInterceptor;
00309 }
00310 
00311 void Part::readProperties(KConfig* config)
00312 {
00313     m_backedUpList = false;
00314     openStandardFeedList();
00315 
00316     if(m_view)
00317         m_view->readProperties(config);
00318 }
00319 
00320 void Part::saveProperties(KConfig* config)
00321 {
00322     if (m_view)
00323     {
00324         slotSaveFeedList();
00325         m_view->saveProperties(config);
00326     }
00327 }
00328 
00329 bool Part::openURL(const KURL& url)
00330 {
00331     m_file = url.path();
00332     return openFile();
00333 }
00334 
00335 void Part::openStandardFeedList()
00336 {
00337     if ( !m_standardFeedList.isEmpty() && openURL(m_standardFeedList) )
00338         m_standardListLoaded = true;
00339 }
00340 
00341 QDomDocument Part::createDefaultFeedList()
00342 {
00343     QDomDocument doc;
00344     QDomProcessingInstruction z = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
00345     doc.appendChild( z );
00346 
00347     QDomElement root = doc.createElement( "opml" );
00348     root.setAttribute("version","1.0");
00349     doc.appendChild( root );
00350 
00351     QDomElement head = doc.createElement( "head" );
00352     root.appendChild(head);
00353 
00354     QDomElement text = doc.createElement( "text" );
00355     text.appendChild(doc.createTextNode(i18n("Feeds")));
00356     head.appendChild(text);
00357 
00358     QDomElement body = doc.createElement( "body" );
00359     root.appendChild(body);
00360 
00361     QDomElement mainFolder = doc.createElement( "outline" );
00362     mainFolder.setAttribute("text","KDE");
00363     body.appendChild(mainFolder);
00364 
00365     QDomElement ak = doc.createElement( "outline" );
00366     ak.setAttribute("text",i18n("Akregator News"));
00367     ak.setAttribute("xmlUrl","http://akregator.sf.net/rss2.php");
00368     mainFolder.appendChild(ak);
00369 
00370     QDomElement akb = doc.createElement( "outline" );
00371     akb.setAttribute("text",i18n("Akregator Blog"));
00372     akb.setAttribute("xmlUrl","http://akregator.pwsp.net/blog/?feed=rss2");
00373     mainFolder.appendChild(akb);
00374 
00375     QDomElement dot = doc.createElement( "outline" );
00376     dot.setAttribute("text",i18n("KDE Dot News"));
00377     dot.setAttribute("xmlUrl","http://www.kde.org/dotkdeorg.rdf");
00378     mainFolder.appendChild(dot);
00379 
00380     QDomElement plan = doc.createElement( "outline" );
00381     plan.setAttribute("text",i18n("Planet KDE"));
00382     plan.setAttribute("xmlUrl","http://planetkde.org/rss20.xml");
00383     mainFolder.appendChild(plan);
00384 
00385     QDomElement apps = doc.createElement( "outline" );
00386     apps.setAttribute("text",i18n("KDE Apps"));
00387     apps.setAttribute("xmlUrl","http://www.kde.org/dot/kde-apps-content.rdf");
00388     mainFolder.appendChild(apps);
00389 
00390     QDomElement look = doc.createElement( "outline" );
00391     look.setAttribute("text",i18n("KDE Look"));
00392     look.setAttribute("xmlUrl","http://www.kde.org/kde-look-content.rdf");
00393     mainFolder.appendChild(look);
00394 
00395     QDomElement kubuntu = doc.createElement( "outline" );
00396     kubuntu.setAttribute("text",i18n("Kubuntu"));
00397     kubuntu.setAttribute("xmlUrl","http://kubuntu.org/kubuntu.rss");
00398     mainFolder.appendChild(kubuntu);
00399 
00400     QDomElement fridge = doc.createElement( "outline" );
00401     fridge.setAttribute("text",i18n("Ubuntu Fridge"));
00402     fridge.setAttribute("xmlUrl","http://fridge.ubuntu.com/node/feed");
00403     mainFolder.appendChild(fridge);
00404 
00405     QDomElement planetubuntu = doc.createElement( "outline" );
00406     planetubuntu.setAttribute("text",i18n("Planet Ubuntu"));
00407     planetubuntu.setAttribute("xmlUrl","http://planet.ubuntu.com/rss20.xml");
00408     mainFolder.appendChild(planetubuntu);
00409 
00410     return doc;
00411 }
00412 
00413 bool Part::openFile()
00414 {
00415     emit setStatusBarText(i18n("Opening Feed List...") );
00416 
00417     QString str;
00418     // m_file is always local so we can use QFile on it
00419     QFile file(m_file);
00420 
00421     bool fileExists = file.exists();
00422     QString listBackup = m_storage->restoreFeedList();
00423      
00424     QDomDocument doc;
00425 
00426     if (!fileExists)
00427     {
00428         doc = createDefaultFeedList();
00429     }
00430     else 
00431     {
00432         if (file.open(IO_ReadOnly))
00433         {
00434             // Read OPML feeds list and build QDom tree.
00435             QTextStream stream(&file);
00436             stream.setEncoding(QTextStream::UnicodeUTF8); // FIXME not all opmls are in utf8
00437             str = stream.read();
00438             file.close();
00439         }
00440 
00441         if (!doc.setContent(str))
00442         {
00443 
00444             if (file.size() > 0) // don't backup empty files 
00445             {
00446                 QString backup = m_file + "-backup." +  QString::number(QDateTime::currentDateTime().toTime_t());
00447         
00448                 copyFile(backup);
00449         
00450                 KMessageBox::error(m_view, i18n("<qt>The standard feed list is corrupted (invalid XML). A backup was created:<p><b>%2</b></p></qt>").arg(backup), i18n("XML Parsing Error") );
00451             }
00452 
00453             if (!doc.setContent(listBackup))
00454                 doc = createDefaultFeedList();
00455         }
00456     }
00457 
00458     if (!m_view->loadFeeds(doc))
00459     {
00460         if (file.size() > 0) // don't backup empty files 
00461         {
00462             QString backup = m_file + "-backup." +  QString::number(QDateTime::currentDateTime().toTime_t());
00463             copyFile(backup);
00464 
00465             KMessageBox::error(m_view, i18n("<qt>The standard feed list is corrupted (no valid OPML). A backup was created:<p><b>%2</b></p></qt>").arg(backup), i18n("OPML Parsing Error") );
00466         }
00467         m_view->loadFeeds(createDefaultFeedList());
00468     }
00469 
00470     emit setStatusBarText(QString::null);
00471     
00472 
00473     if( Settings::markAllFeedsReadOnStartup() )
00474         m_view->slotMarkAllFeedsRead();
00475 
00476     if (Settings::fetchOnStartup())
00477             m_view->slotFetchAllFeeds();
00478 
00479     return true;
00480 }
00481 
00482 void Part::slotSaveFeedList()
00483 {
00484     // don't save to the standard feed list, when it wasn't completely loaded before
00485     if (!m_standardListLoaded)
00486         return;
00487 
00488     // the first time we overwrite the feed list, we create a backup
00489     if (!m_backedUpList)
00490     {
00491         QString backup = m_file + "~";
00492 
00493         if (copyFile(backup))
00494             m_backedUpList = true;
00495     }
00496 
00497     QString xmlStr = m_view->feedListToOPML().toString();
00498     m_storage->storeFeedList(xmlStr);
00499 
00500     QFile file(m_file);
00501     if (file.open(IO_WriteOnly) == false)
00502     {
00503         //FIXME: allow to save the feedlist into different location -tpr 20041118
00504         KMessageBox::error(m_view, i18n("Access denied: cannot save feed list (%1)").arg(m_file), i18n("Write error") );
00505         return;
00506     }
00507 
00508     // use QTextStream to dump the text to the file
00509     QTextStream stream(&file);
00510     stream.setEncoding(QTextStream::UnicodeUTF8);
00511 
00512     // Write OPML data file.
00513     // Archive data files are saved elsewhere.
00514 
00515     stream << xmlStr << endl;
00516 
00517     file.close();
00518 }
00519 
00520 bool Part::isTrayIconEnabled() const
00521 {
00522     return Settings::showTrayIcon();
00523 }
00524 
00525 bool Part::mergePart(KParts::Part* part)
00526 {
00527     if (part != m_mergedPart)
00528     {
00529         if (!factory())
00530         {
00531             kdDebug() << "Akregator::Part::mergePart(): factory() returns NULL" << endl;
00532             return false;
00533         }
00534         if (m_mergedPart)
00535             factory()->removeClient(m_mergedPart);
00536         if (part)
00537             factory()->addClient(part);
00538 
00539         m_mergedPart = part;
00540     }
00541     return true;
00542 }
00543 
00544 QWidget* Part::getMainWindow()
00545 {
00546     // this is a dirty fix to get the main window used for the tray icon
00547     
00548     QWidgetList *l = kapp->topLevelWidgets();
00549     QWidgetListIt it( *l );
00550     QWidget *wid;
00551 
00552     // check if there is an akregator main window
00553     while ( (wid = it.current()) != 0 )
00554     {
00555         ++it;
00556         //kdDebug() << "win name: " << wid->name() << endl;
00557         if (QString(wid->name()) == "akregator_mainwindow")
00558         {
00559             delete l;
00560             return wid;
00561         }
00562     }
00563     // if not, check for kontact main window
00564     QWidgetListIt it2( *l );
00565     while ( (wid = it2.current()) != 0 )
00566     {
00567         ++it2;
00568         if (QString(wid->name()).startsWith("kontact-mainwindow"))
00569         {
00570             delete l;
00571             return wid;
00572         }
00573     }
00574     delete l;
00575     return 0;
00576 }
00577 
00578 void Part::loadTagSet(const QString& path)
00579 {
00580     QDomDocument doc;
00581 
00582     QFile file(path);
00583     if (file.open(IO_ReadOnly))
00584     {
00585         doc.setContent(file.readAll());
00586         file.close();
00587     }
00588     // if we can't load the tagset from the xml file, check for the backup in the backend
00589     if (doc.isNull())
00590     {
00591         doc.setContent(m_storage->restoreTagSet());
00592     }
00593 
00594     if (!doc.isNull())
00595     {
00596         Kernel::self()->tagSet()->readFromXML(doc);
00597     }
00598     else
00599     {
00600         Kernel::self()->tagSet()->insert(Tag("http://akregator.sf.net/tags/Interesting", i18n("Interesting")));
00601     }
00602 }
00603 
00604 void Part::saveTagSet(const QString& path)
00605 {
00606     QString xmlStr = Kernel::self()->tagSet()->toXML().toString();
00607 
00608     m_storage->storeTagSet(xmlStr);
00609     
00610     QFile file(path);
00611     
00612     if ( file.open(IO_WriteOnly) )
00613     {
00614 
00615         QTextStream stream(&file);
00616         stream.setEncoding(QTextStream::UnicodeUTF8);
00617         stream << xmlStr << "\n";
00618         file.close();
00619     }
00620 }
00621 
00622 void Part::importFile(const KURL& url)
00623 {
00624     QString filename;
00625 
00626     bool isRemote = false;
00627 
00628     if (url.isLocalFile())
00629         filename = url.path();
00630     else
00631     {
00632         isRemote = true;
00633 
00634         if (!KIO::NetAccess::download(url, filename, m_view) )
00635         {
00636             KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() );
00637             return;
00638         }
00639     }
00640 
00641     QFile file(filename);
00642     if (file.open(IO_ReadOnly))
00643     {
00644         // Read OPML feeds list and build QDom tree.
00645         QDomDocument doc;
00646         if (doc.setContent(file.readAll()))
00647             m_view->importFeeds(doc);
00648         else
00649             KMessageBox::error(m_view, i18n("Could not import the file %1 (no valid OPML)").arg(filename), i18n("OPML Parsing Error") );
00650     }
00651     else
00652         KMessageBox::error(m_view, i18n("The file %1 could not be read, check if it exists or if it is readable for the current user.").arg(filename), i18n("Read Error"));
00653 
00654     if (isRemote)
00655         KIO::NetAccess::removeTempFile(filename);
00656 }
00657 
00658 void Part::exportFile(const KURL& url)
00659 {
00660     if (url.isLocalFile())
00661     {
00662         QFile file(url.path());
00663 
00664         if ( file.exists() &&
00665                 KMessageBox::questionYesNo(m_view,
00666             i18n("The file %1 already exists; do you want to overwrite it?").arg(file.name()),
00667             i18n("Export"),
00668             i18n("Overwrite"),
00669             KStdGuiItem::cancel()) == KMessageBox::No )
00670             return;
00671 
00672         if ( !file.open(IO_WriteOnly) )
00673         {
00674             KMessageBox::error(m_view, i18n("Access denied: cannot write to file %1").arg(file.name()), i18n("Write Error") );
00675             return;
00676         }
00677 
00678         QTextStream stream(&file);
00679         stream.setEncoding(QTextStream::UnicodeUTF8);
00680 
00681         stream << m_view->feedListToOPML().toString() << "\n";
00682         file.close();
00683     }
00684     else
00685     {
00686         KTempFile tmpfile;
00687         tmpfile.setAutoDelete(true);
00688 
00689         QTextStream stream(tmpfile.file());
00690         stream.setEncoding(QTextStream::UnicodeUTF8);
00691 
00692         stream << m_view->feedListToOPML().toString() << "\n";
00693         tmpfile.close();
00694 
00695         if (!KIO::NetAccess::upload(tmpfile.name(), url, m_view))
00696             KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() );
00697     }
00698 }
00699 
00700 void Part::fileImport()
00701 {
00702     KURL url = KFileDialog::getOpenURL( QString::null,
00703                         "*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)")
00704                         +"\n*|" + i18n("All Files") );
00705 
00706     if (!url.isEmpty())
00707         importFile(url);
00708 }
00709 
00710     void Part::fileExport()
00711 {
00712     KURL url= KFileDialog::getSaveURL( QString::null,
00713                         "*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)")
00714                         +"\n*|" + i18n("All Files") );
00715 
00716     if ( !url.isEmpty() )
00717         exportFile(url);
00718 }
00719 
00720 void Part::fileGetFeeds()
00721 {
00722     /*GetFeeds *gf = new GetFeeds();
00723     gf->show();*/
00724      //KNS::DownloadDialog::open("akregator/feeds", i18n("Get New Feeds"));
00725 }
00726 
00727 void Part::fileSendArticle(bool attach)
00728 {
00729     // FIXME: you have to open article to tab to be able to send...
00730     QString title, text;
00731 
00732     text = m_view->currentFrame()->part()->url().prettyURL();
00733     if(text.isEmpty() || text.isNull())
00734         return;
00735 
00736     title = m_view->currentFrame()->title();
00737 
00738     if(attach) {
00739         kapp->invokeMailer("",
00740                            "",
00741                            "",
00742                            title,
00743                            text,
00744                            "",
00745                            text);
00746     }
00747     else {
00748         kapp->invokeMailer("",
00749                            "",
00750                            "",
00751                            title,
00752                            text);
00753     }
00754 }
00755 
00756 void Part::fetchAllFeeds()
00757 {
00758     m_view->slotFetchAllFeeds();
00759 }
00760 
00761 void Part::fetchFeedUrl(const QString&s)
00762 {
00763     kdDebug() << "fetchFeedURL==" << s << endl;
00764 }
00765 
00766 void Part::addFeedsToGroup(const QStringList& urls, const QString& group)
00767 {
00768     for (QStringList::ConstIterator it = urls.begin(); it != urls.end(); ++it)
00769     {
00770         kdDebug() << "Akregator::Part::addFeedToGroup adding feed with URL " << *it << " to group " << group << endl;
00771         m_view->addFeedToGroup(*it, group);
00772     }
00773     NotificationManager::self()->slotNotifyFeeds(urls);
00774 }
00775 
00776 void Part::addFeed()
00777 {
00778     m_view->slotFeedAdd();
00779 }
00780 
00781 KAboutData *Part::createAboutData()
00782 {
00783     return new Akregator::AboutData;
00784 }
00785 
00786 void Part::showKNotifyOptions()
00787 {
00788     KAboutData* about = new Akregator::AboutData;
00789     KNotifyDialog::configure(m_view, "akregator_knotify_config", about);
00790     delete about;
00791 }
00792 
00793 void Part::showOptions()
00794 {
00795     if ( KConfigDialog::showDialog( "settings" ) )
00796         return;
00797 
00798     KConfigDialog* dialog = new ConfigDialog( m_view, "settings", Settings::self() );
00799 
00800     connect( dialog, SIGNAL(settingsChanged()),
00801              this, SLOT(slotSettingsChanged()) );
00802     connect( dialog, SIGNAL(settingsChanged()),
00803              TrayIcon::getInstance(), SLOT(settingsChanged()) );
00804 
00805     dialog->show();
00806 }
00807 
00808 void Part::partActivateEvent(KParts::PartActivateEvent* event)
00809 {
00810     if (factory() && m_mergedPart)
00811     {
00812         if (event->activated())
00813             factory()->addClient(m_mergedPart);
00814         else
00815             factory()->removeClient(m_mergedPart);
00816     }
00817 
00818     MyBasePart::partActivateEvent(event);
00819 }
00820 
00821 KParts::Part* Part::hitTest(QWidget *widget, const QPoint &globalPos)
00822 {
00823     bool child = false;
00824     QWidget *me = this->widget();
00825     while (widget) {
00826         if (widget == me) {
00827             child = true;
00828             break;
00829         }
00830         if (!widget) {
00831             break;
00832         }
00833         widget = widget->parentWidget();
00834     }
00835     if (m_view && m_view->currentFrame() && child) {
00836         return m_view->currentFrame()->part();
00837     } else {
00838         return MyBasePart::hitTest(widget, globalPos);
00839     }
00840 }
00841 
00842 void Part::initFonts()
00843 {
00844     QStringList fonts = Settings::fonts();
00845     if (fonts.isEmpty())
00846     {
00847         fonts.append(KGlobalSettings::generalFont().family());
00848         fonts.append(KGlobalSettings::fixedFont().family());
00849         fonts.append(KGlobalSettings::generalFont().family());
00850         fonts.append(KGlobalSettings::generalFont().family());
00851         fonts.append("0");
00852     }
00853     Settings::setFonts(fonts);
00854     if (Settings::standardFont().isEmpty())
00855         Settings::setStandardFont(fonts[0]);
00856     if (Settings::fixedFont().isEmpty())
00857         Settings::setFixedFont(fonts[1]);
00858     if (Settings::sansSerifFont().isEmpty())
00859         Settings::setSansSerifFont(fonts[2]);
00860     if (Settings::serifFont().isEmpty())
00861         Settings::setSerifFont(fonts[3]);
00862 
00863     KConfig* conf = Settings::self()->config();
00864     conf->setGroup("HTML Settings");
00865 
00866     KConfig konq("konquerorrc", true, false);
00867     konq.setGroup("HTML Settings");
00868 
00869     if (!conf->hasKey("MinimumFontSize"))
00870     {
00871         int minfs;
00872         if (konq.hasKey("MinimumFontSize"))
00873             minfs = konq.readNumEntry("MinimumFontSize");
00874         else
00875             minfs = KGlobalSettings::generalFont().pointSize();
00876         kdDebug() << "Part::initFonts(): set MinimumFontSize to " << minfs << endl;
00877         Settings::setMinimumFontSize(minfs);
00878     }
00879 
00880     if (!conf->hasKey("MediumFontSize"))
00881     {
00882         int medfs;
00883         if (konq.hasKey("MediumFontSize"))
00884             medfs = konq.readNumEntry("MediumFontSize");
00885         else
00886             medfs = KGlobalSettings::generalFont().pointSize();
00887         kdDebug() << "Part::initFonts(): set MediumFontSize to " << medfs << endl;
00888         Settings::setMediumFontSize(medfs);
00889     }
00890 
00891     if (!conf->hasKey("UnderlineLinks"))
00892     {
00893         bool underline = true;
00894         if (konq.hasKey("UnderlineLinks"))
00895             underline = konq.readBoolEntry("UnderlineLinks");
00896 
00897         kdDebug() << "Part::initFonts(): set UnderlineLinks to " << underline << endl;
00898         Settings::setUnderlineLinks(underline);
00899     }
00900 
00901 }
00902 
00903 bool Part::copyFile(const QString& backup)
00904 {
00905     QFile file(m_file);
00906 
00907     if (file.open(IO_ReadOnly))
00908     {
00909         QFile backupFile(backup);
00910         if (backupFile.open(IO_WriteOnly))
00911         {
00912             QTextStream in(&file);
00913             QTextStream out(&backupFile);
00914             while (!in.atEnd())
00915                 out << in.readLine();
00916             backupFile.close();
00917             file.close();
00918             return true;
00919         }
00920         else
00921         {
00922             file.close();
00923             return false;
00924         }
00925     }
00926     return false;
00927 }
00928 
00929 static QString getMyHostName()
00930 {
00931     char hostNameC[256];
00932     // null terminate this C string
00933     hostNameC[255] = 0;
00934     // set the string to 0 length if gethostname fails
00935     if(gethostname(hostNameC, 255))
00936         hostNameC[0] = 0;
00937     return QString::fromLocal8Bit(hostNameC);
00938 }
00939 
00940 // taken from KMail
00941 bool Part::tryToLock(const QString& backendName)
00942 {
00943 // Check and create a lock file to prevent concurrent access to metakit archive
00944     QString appName = kapp->instanceName();
00945     if ( appName.isEmpty() )
00946         appName = "akregator";
00947 
00948     QString programName;
00949     const KAboutData *about = kapp->aboutData();
00950     if ( about )
00951         programName = about->programName();
00952     if ( programName.isEmpty() )
00953         programName = i18n("Akregator");
00954 
00955     QString lockLocation = locateLocal("data", "akregator/lock");
00956     KSimpleConfig config(lockLocation);
00957     int oldPid = config.readNumEntry("pid", -1);
00958     const QString oldHostName = config.readEntry("hostname");
00959     const QString oldAppName = config.readEntry( "appName", appName );
00960     const QString oldProgramName = config.readEntry( "programName", programName );
00961     const QString hostName = getMyHostName();
00962     bool first_instance = false;
00963     if ( oldPid == -1 )
00964         first_instance = true;
00965   // check if the lock file is stale by trying to see if
00966   // the other pid is currently running.
00967   // Not 100% correct but better safe than sorry
00968     else if (hostName == oldHostName && oldPid != getpid()) {
00969         if ( kill(oldPid, 0) == -1 )
00970             first_instance = ( errno == ESRCH );
00971     }
00972 
00973     if ( !first_instance )
00974     {
00975         QString msg;
00976         if ( oldHostName == hostName ) 
00977         {
00978             // this can only happen if the user is running this application on
00979             // different displays on the same machine. All other cases will be
00980             // taken care of by KUniqueApplication()
00981             if ( oldAppName == appName )
00982                 msg = i18n("<qt>%1 already seems to be running on another display on "
00983                         "this machine. <b>Running %2 more than once is not supported "
00984                         "by the %3 backend and "
00985                         "can cause the loss of archived articles and crashes at startup.</b> "
00986                         "You should disable the archive for now "
00987                         "unless you are sure that %2 is not already running.</qt>")
00988                         .arg( programName, programName, backendName );
00989               // QString::arg( st ) only replaces the first occurrence of %1
00990               // with st while QString::arg( s1, s2 ) replacess all occurrences
00991               // of %1 with s1 and all occurrences of %2 with s2. So don't
00992               // even think about changing the above to .arg( programName ).
00993             else
00994                 msg = i18n("<qt>%1 seems to be running on another display on this "
00995                         "machine. <b>Running %1 and %2 at the same "
00996                         "time is not supported by the %3 backend and can cause "
00997                         "the loss of archived articles and crashes at startup.</b> "
00998                         "You should disable the archive for now "
00999                         "unless you are sure that %2 is not already running.</qt>")
01000                         .arg( oldProgramName, programName, backendName );
01001         }
01002         else
01003         {
01004             if ( oldAppName == appName )
01005                 msg = i18n("<qt>%1 already seems to be running on %2. <b>Running %1 more "
01006                         "than once is not supported by the %3 backend and can cause "
01007                         "the loss of archived articles and crashes at startup.</b> "
01008                         "You should disable the archive for now "
01009                         "unless you are sure that it is "
01010                         "not already running on %2.</qt>")
01011                         .arg( programName, oldHostName, backendName );
01012             else
01013                 msg = i18n("<qt>%1 seems to be running on %3. <b>Running %1 and %2 at the "
01014                         "same time is not supported by the %4 backend and can cause "
01015                         "the loss of archived articles and crashes at startup.</b> "
01016                         "You should disable the archive for now "
01017                         "unless you are sure that %1 is "
01018                         "not running on %3.</qt>")
01019                         .arg( oldProgramName, programName, oldHostName, backendName );
01020         }
01021 
01022         KCursorSaver idle( KBusyPtr::idle() );
01023         if ( KMessageBox::No ==
01024              KMessageBox::warningYesNo( 0, msg, QString::null,
01025                                         i18n("Force Access"),
01026                                         i18n("Disable Archive")) )
01027                                         {
01028                                             return false;
01029                                         }
01030     }
01031 
01032     config.writeEntry("pid", getpid());
01033     config.writeEntry("hostname", hostName);
01034     config.writeEntry( "appName", appName );
01035     config.writeEntry( "programName", programName );
01036     config.sync();
01037     return true;
01038 }
01039 
01040 
01041 } // namespace Akregator
01042 #include "akregator_part.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys