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 debianFolder = doc.createElement( "outline" );
00396     debianFolder.setAttribute("text","Debian");
00397     body.appendChild(debianFolder);
00398 
00399     QDomElement pland = doc.createElement( "outline" );
00400     pland.setAttribute("text",i18n("Planet Debian"));
00401     pland.setAttribute("xmlUrl","http://planet.debian.org/rss20.xml");
00402     debianFolder.appendChild(pland);
00403 
00404     QDomElement dtimes = doc.createElement( "outline" );
00405     dtimes.setAttribute("text",i18n("Debian Times"));
00406     dtimes.setAttribute("xmlUrl","http://times.debian.net/?format=rss20.xml");
00407     debianFolder.appendChild(dtimes);
00408 
00409     QDomElement kubuntu = doc.createElement( "outline" );
00410     kubuntu.setAttribute("text",i18n("Kubuntu"));
00411     kubuntu.setAttribute("xmlUrl","http://kubuntu.org/kubuntu.rss");
00412     mainFolder.appendChild(kubuntu);
00413 
00414     QDomElement fridge = doc.createElement( "outline" );
00415     fridge.setAttribute("text",i18n("Ubuntu Fridge"));
00416     fridge.setAttribute("xmlUrl","http://fridge.ubuntu.com/node/feed");
00417     mainFolder.appendChild(fridge);
00418 
00419     QDomElement planetubuntu = doc.createElement( "outline" );
00420     planetubuntu.setAttribute("text",i18n("Planet Ubuntu"));
00421     planetubuntu.setAttribute("xmlUrl","http://planet.ubuntu.com/rss20.xml");
00422     mainFolder.appendChild(planetubuntu);
00423 
00424     return doc;
00425 }
00426 
00427 bool Part::openFile()
00428 {
00429     emit setStatusBarText(i18n("Opening Feed List...") );
00430 
00431     QString str;
00432     // m_file is always local so we can use QFile on it
00433     QFile file(m_file);
00434 
00435     bool fileExists = file.exists();
00436     QString listBackup = m_storage->restoreFeedList();
00437      
00438     QDomDocument doc;
00439 
00440     if (!fileExists)
00441     {
00442         doc = createDefaultFeedList();
00443     }
00444     else 
00445     {
00446         if (file.open(IO_ReadOnly))
00447         {
00448             // Read OPML feeds list and build QDom tree.
00449             QTextStream stream(&file);
00450             stream.setEncoding(QTextStream::UnicodeUTF8); // FIXME not all opmls are in utf8
00451             str = stream.read();
00452             file.close();
00453         }
00454 
00455         if (!doc.setContent(str))
00456         {
00457 
00458             if (file.size() > 0) // don't backup empty files 
00459             {
00460                 QString backup = m_file + "-backup." +  QString::number(QDateTime::currentDateTime().toTime_t());
00461         
00462                 copyFile(backup);
00463         
00464                 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") );
00465             }
00466 
00467             if (!doc.setContent(listBackup))
00468                 doc = createDefaultFeedList();
00469         }
00470     }
00471 
00472     if (!m_view->loadFeeds(doc))
00473     {
00474         if (file.size() > 0) // don't backup empty files 
00475         {
00476             QString backup = m_file + "-backup." +  QString::number(QDateTime::currentDateTime().toTime_t());
00477             copyFile(backup);
00478 
00479             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") );
00480         }
00481         m_view->loadFeeds(createDefaultFeedList());
00482     }
00483 
00484     emit setStatusBarText(QString::null);
00485     
00486 
00487     if( Settings::markAllFeedsReadOnStartup() )
00488         m_view->slotMarkAllFeedsRead();
00489 
00490     if (Settings::fetchOnStartup())
00491             m_view->slotFetchAllFeeds();
00492 
00493     return true;
00494 }
00495 
00496 void Part::slotSaveFeedList()
00497 {
00498     // don't save to the standard feed list, when it wasn't completely loaded before
00499     if (!m_standardListLoaded)
00500         return;
00501 
00502     // the first time we overwrite the feed list, we create a backup
00503     if (!m_backedUpList)
00504     {
00505         QString backup = m_file + "~";
00506 
00507         if (copyFile(backup))
00508             m_backedUpList = true;
00509     }
00510 
00511     QString xmlStr = m_view->feedListToOPML().toString();
00512     m_storage->storeFeedList(xmlStr);
00513 
00514     QFile file(m_file);
00515     if (file.open(IO_WriteOnly) == false)
00516     {
00517         //FIXME: allow to save the feedlist into different location -tpr 20041118
00518         KMessageBox::error(m_view, i18n("Access denied: cannot save feed list (%1)").arg(m_file), i18n("Write error") );
00519         return;
00520     }
00521 
00522     // use QTextStream to dump the text to the file
00523     QTextStream stream(&file);
00524     stream.setEncoding(QTextStream::UnicodeUTF8);
00525 
00526     // Write OPML data file.
00527     // Archive data files are saved elsewhere.
00528 
00529     stream << xmlStr << endl;
00530 
00531     file.close();
00532 }
00533 
00534 bool Part::isTrayIconEnabled() const
00535 {
00536     return Settings::showTrayIcon();
00537 }
00538 
00539 bool Part::mergePart(KParts::Part* part)
00540 {
00541     if (part != m_mergedPart)
00542     {
00543         if (!factory())
00544         {
00545             kdDebug() << "Akregator::Part::mergePart(): factory() returns NULL" << endl;
00546             return false;
00547         }
00548         if (m_mergedPart)
00549             factory()->removeClient(m_mergedPart);
00550         if (part)
00551             factory()->addClient(part);
00552 
00553         m_mergedPart = part;
00554     }
00555     return true;
00556 }
00557 
00558 QWidget* Part::getMainWindow()
00559 {
00560     // this is a dirty fix to get the main window used for the tray icon
00561     
00562     QWidgetList *l = kapp->topLevelWidgets();
00563     QWidgetListIt it( *l );
00564     QWidget *wid;
00565 
00566     // check if there is an akregator main window
00567     while ( (wid = it.current()) != 0 )
00568     {
00569         ++it;
00570         //kdDebug() << "win name: " << wid->name() << endl;
00571         if (QString(wid->name()) == "akregator_mainwindow")
00572         {
00573             delete l;
00574             return wid;
00575         }
00576     }
00577     // if not, check for kontact main window
00578     QWidgetListIt it2( *l );
00579     while ( (wid = it2.current()) != 0 )
00580     {
00581         ++it2;
00582         if (QString(wid->name()).startsWith("kontact-mainwindow"))
00583         {
00584             delete l;
00585             return wid;
00586         }
00587     }
00588     delete l;
00589     return 0;
00590 }
00591 
00592 void Part::loadTagSet(const QString& path)
00593 {
00594     QDomDocument doc;
00595 
00596     QFile file(path);
00597     if (file.open(IO_ReadOnly))
00598     {
00599         doc.setContent(file.readAll());
00600         file.close();
00601     }
00602     // if we can't load the tagset from the xml file, check for the backup in the backend
00603     if (doc.isNull())
00604     {
00605         doc.setContent(m_storage->restoreTagSet());
00606     }
00607 
00608     if (!doc.isNull())
00609     {
00610         Kernel::self()->tagSet()->readFromXML(doc);
00611     }
00612     else
00613     {
00614         Kernel::self()->tagSet()->insert(Tag("http://akregator.sf.net/tags/Interesting", i18n("Interesting")));
00615     }
00616 }
00617 
00618 void Part::saveTagSet(const QString& path)
00619 {
00620     QString xmlStr = Kernel::self()->tagSet()->toXML().toString();
00621 
00622     m_storage->storeTagSet(xmlStr);
00623     
00624     QFile file(path);
00625     
00626     if ( file.open(IO_WriteOnly) )
00627     {
00628 
00629         QTextStream stream(&file);
00630         stream.setEncoding(QTextStream::UnicodeUTF8);
00631         stream << xmlStr << "\n";
00632         file.close();
00633     }
00634 }
00635 
00636 void Part::importFile(const KURL& url)
00637 {
00638     QString filename;
00639 
00640     bool isRemote = false;
00641 
00642     if (url.isLocalFile())
00643         filename = url.path();
00644     else
00645     {
00646         isRemote = true;
00647 
00648         if (!KIO::NetAccess::download(url, filename, m_view) )
00649         {
00650             KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() );
00651             return;
00652         }
00653     }
00654 
00655     QFile file(filename);
00656     if (file.open(IO_ReadOnly))
00657     {
00658         // Read OPML feeds list and build QDom tree.
00659         QDomDocument doc;
00660         if (doc.setContent(file.readAll()))
00661             m_view->importFeeds(doc);
00662         else
00663             KMessageBox::error(m_view, i18n("Could not import the file %1 (no valid OPML)").arg(filename), i18n("OPML Parsing Error") );
00664     }
00665     else
00666         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"));
00667 
00668     if (isRemote)
00669         KIO::NetAccess::removeTempFile(filename);
00670 }
00671 
00672 void Part::exportFile(const KURL& url)
00673 {
00674     if (url.isLocalFile())
00675     {
00676         QFile file(url.path());
00677 
00678         if ( file.exists() &&
00679                 KMessageBox::questionYesNo(m_view,
00680             i18n("The file %1 already exists; do you want to overwrite it?").arg(file.name()),
00681             i18n("Export"),
00682             i18n("Overwrite"),
00683             KStdGuiItem::cancel()) == KMessageBox::No )
00684             return;
00685 
00686         if ( !file.open(IO_WriteOnly) )
00687         {
00688             KMessageBox::error(m_view, i18n("Access denied: cannot write to file %1").arg(file.name()), i18n("Write Error") );
00689             return;
00690         }
00691 
00692         QTextStream stream(&file);
00693         stream.setEncoding(QTextStream::UnicodeUTF8);
00694 
00695         stream << m_view->feedListToOPML().toString() << "\n";
00696         file.close();
00697     }
00698     else
00699     {
00700         KTempFile tmpfile;
00701         tmpfile.setAutoDelete(true);
00702 
00703         QTextStream stream(tmpfile.file());
00704         stream.setEncoding(QTextStream::UnicodeUTF8);
00705 
00706         stream << m_view->feedListToOPML().toString() << "\n";
00707         tmpfile.close();
00708 
00709         if (!KIO::NetAccess::upload(tmpfile.name(), url, m_view))
00710             KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() );
00711     }
00712 }
00713 
00714 void Part::fileImport()
00715 {
00716     KURL url = KFileDialog::getOpenURL( QString::null,
00717                         "*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)")
00718                         +"\n*|" + i18n("All Files") );
00719 
00720     if (!url.isEmpty())
00721         importFile(url);
00722 }
00723 
00724     void Part::fileExport()
00725 {
00726     KURL url= KFileDialog::getSaveURL( QString::null,
00727                         "*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)")
00728                         +"\n*|" + i18n("All Files") );
00729 
00730     if ( !url.isEmpty() )
00731         exportFile(url);
00732 }
00733 
00734 void Part::fileGetFeeds()
00735 {
00736     /*GetFeeds *gf = new GetFeeds();
00737     gf->show();*/
00738      //KNS::DownloadDialog::open("akregator/feeds", i18n("Get New Feeds"));
00739 }
00740 
00741 void Part::fileSendArticle(bool attach)
00742 {
00743     // FIXME: you have to open article to tab to be able to send...
00744     QString title, text;
00745 
00746     text = m_view->currentFrame()->part()->url().prettyURL();
00747     if(text.isEmpty() || text.isNull())
00748         return;
00749 
00750     title = m_view->currentFrame()->title();
00751 
00752     if(attach) {
00753         kapp->invokeMailer("",
00754                            "",
00755                            "",
00756                            title,
00757                            text,
00758                            "",
00759                            text);
00760     }
00761     else {
00762         kapp->invokeMailer("",
00763                            "",
00764                            "",
00765                            title,
00766                            text);
00767     }
00768 }
00769 
00770 void Part::fetchAllFeeds()
00771 {
00772     m_view->slotFetchAllFeeds();
00773 }
00774 
00775 void Part::fetchFeedUrl(const QString&s)
00776 {
00777     kdDebug() << "fetchFeedURL==" << s << endl;
00778 }
00779 
00780 void Part::addFeedsToGroup(const QStringList& urls, const QString& group)
00781 {
00782     for (QStringList::ConstIterator it = urls.begin(); it != urls.end(); ++it)
00783     {
00784         kdDebug() << "Akregator::Part::addFeedToGroup adding feed with URL " << *it << " to group " << group << endl;
00785         m_view->addFeedToGroup(*it, group);
00786     }
00787     NotificationManager::self()->slotNotifyFeeds(urls);
00788 }
00789 
00790 void Part::addFeed()
00791 {
00792     m_view->slotFeedAdd();
00793 }
00794 
00795 KAboutData *Part::createAboutData()
00796 {
00797     return new Akregator::AboutData;
00798 }
00799 
00800 void Part::showKNotifyOptions()
00801 {
00802     KAboutData* about = new Akregator::AboutData;
00803     KNotifyDialog::configure(m_view, "akregator_knotify_config", about);
00804     delete about;
00805 }
00806 
00807 void Part::showOptions()
00808 {
00809     if ( KConfigDialog::showDialog( "settings" ) )
00810         return;
00811 
00812     KConfigDialog* dialog = new ConfigDialog( m_view, "settings", Settings::self() );
00813 
00814     connect( dialog, SIGNAL(settingsChanged()),
00815              this, SLOT(slotSettingsChanged()) );
00816     connect( dialog, SIGNAL(settingsChanged()),
00817              TrayIcon::getInstance(), SLOT(settingsChanged()) );
00818 
00819     dialog->show();
00820 }
00821 
00822 void Part::partActivateEvent(KParts::PartActivateEvent* event)
00823 {
00824     if (factory() && m_mergedPart)
00825     {
00826         if (event->activated())
00827             factory()->addClient(m_mergedPart);
00828         else
00829             factory()->removeClient(m_mergedPart);
00830     }
00831 
00832     MyBasePart::partActivateEvent(event);
00833 }
00834 
00835 KParts::Part* Part::hitTest(QWidget *widget, const QPoint &globalPos)
00836 {
00837     bool child = false;
00838     QWidget *me = this->widget();
00839     while (widget) {
00840         if (widget == me) {
00841             child = true;
00842             break;
00843         }
00844         if (!widget) {
00845             break;
00846         }
00847         widget = widget->parentWidget();
00848     }
00849     if (m_view && m_view->currentFrame() && child) {
00850         return m_view->currentFrame()->part();
00851     } else {
00852         return MyBasePart::hitTest(widget, globalPos);
00853     }
00854 }
00855 
00856 void Part::initFonts()
00857 {
00858     QStringList fonts = Settings::fonts();
00859     if (fonts.isEmpty())
00860     {
00861         fonts.append(KGlobalSettings::generalFont().family());
00862         fonts.append(KGlobalSettings::fixedFont().family());
00863         fonts.append(KGlobalSettings::generalFont().family());
00864         fonts.append(KGlobalSettings::generalFont().family());
00865         fonts.append("0");
00866     }
00867     Settings::setFonts(fonts);
00868     if (Settings::standardFont().isEmpty())
00869         Settings::setStandardFont(fonts[0]);
00870     if (Settings::fixedFont().isEmpty())
00871         Settings::setFixedFont(fonts[1]);
00872     if (Settings::sansSerifFont().isEmpty())
00873         Settings::setSansSerifFont(fonts[2]);
00874     if (Settings::serifFont().isEmpty())
00875         Settings::setSerifFont(fonts[3]);
00876 
00877     KConfig* conf = Settings::self()->config();
00878     conf->setGroup("HTML Settings");
00879 
00880     KConfig konq("konquerorrc", true, false);
00881     konq.setGroup("HTML Settings");
00882 
00883     if (!conf->hasKey("MinimumFontSize"))
00884     {
00885         int minfs;
00886         if (konq.hasKey("MinimumFontSize"))
00887             minfs = konq.readNumEntry("MinimumFontSize");
00888         else
00889             minfs = KGlobalSettings::generalFont().pointSize();
00890         kdDebug() << "Part::initFonts(): set MinimumFontSize to " << minfs << endl;
00891         Settings::setMinimumFontSize(minfs);
00892     }
00893 
00894     if (!conf->hasKey("MediumFontSize"))
00895     {
00896         int medfs;
00897         if (konq.hasKey("MediumFontSize"))
00898             medfs = konq.readNumEntry("MediumFontSize");
00899         else
00900             medfs = KGlobalSettings::generalFont().pointSize();
00901         kdDebug() << "Part::initFonts(): set MediumFontSize to " << medfs << endl;
00902         Settings::setMediumFontSize(medfs);
00903     }
00904 
00905     if (!conf->hasKey("UnderlineLinks"))
00906     {
00907         bool underline = true;
00908         if (konq.hasKey("UnderlineLinks"))
00909             underline = konq.readBoolEntry("UnderlineLinks");
00910 
00911         kdDebug() << "Part::initFonts(): set UnderlineLinks to " << underline << endl;
00912         Settings::setUnderlineLinks(underline);
00913     }
00914 
00915 }
00916 
00917 bool Part::copyFile(const QString& backup)
00918 {
00919     QFile file(m_file);
00920 
00921     if (file.open(IO_ReadOnly))
00922     {
00923         QFile backupFile(backup);
00924         if (backupFile.open(IO_WriteOnly))
00925         {
00926             QTextStream in(&file);
00927             QTextStream out(&backupFile);
00928             while (!in.atEnd())
00929                 out << in.readLine();
00930             backupFile.close();
00931             file.close();
00932             return true;
00933         }
00934         else
00935         {
00936             file.close();
00937             return false;
00938         }
00939     }
00940     return false;
00941 }
00942 
00943 static QString getMyHostName()
00944 {
00945     char hostNameC[256];
00946     // null terminate this C string
00947     hostNameC[255] = 0;
00948     // set the string to 0 length if gethostname fails
00949     if(gethostname(hostNameC, 255))
00950         hostNameC[0] = 0;
00951     return QString::fromLocal8Bit(hostNameC);
00952 }
00953 
00954 // taken from KMail
00955 bool Part::tryToLock(const QString& backendName)
00956 {
00957 // Check and create a lock file to prevent concurrent access to metakit archive
00958     QString appName = kapp->instanceName();
00959     if ( appName.isEmpty() )
00960         appName = "akregator";
00961 
00962     QString programName;
00963     const KAboutData *about = kapp->aboutData();
00964     if ( about )
00965         programName = about->programName();
00966     if ( programName.isEmpty() )
00967         programName = i18n("Akregator");
00968 
00969     QString lockLocation = locateLocal("data", "akregator/lock");
00970     KSimpleConfig config(lockLocation);
00971     int oldPid = config.readNumEntry("pid", -1);
00972     const QString oldHostName = config.readEntry("hostname");
00973     const QString oldAppName = config.readEntry( "appName", appName );
00974     const QString oldProgramName = config.readEntry( "programName", programName );
00975     const QString hostName = getMyHostName();
00976     bool first_instance = false;
00977     if ( oldPid == -1 )
00978         first_instance = true;
00979   // check if the lock file is stale by trying to see if
00980   // the other pid is currently running.
00981   // Not 100% correct but better safe than sorry
00982     else if (hostName == oldHostName && oldPid != getpid()) {
00983         if ( kill(oldPid, 0) == -1 )
00984             first_instance = ( errno == ESRCH );
00985     }
00986 
00987     if ( !first_instance )
00988     {
00989         QString msg;
00990         if ( oldHostName == hostName ) 
00991         {
00992             // this can only happen if the user is running this application on
00993             // different displays on the same machine. All other cases will be
00994             // taken care of by KUniqueApplication()
00995             if ( oldAppName == appName )
00996                 msg = i18n("<qt>%1 already seems to be running on another display on "
00997                         "this machine. <b>Running %2 more than once is not supported "
00998                         "by the %3 backend and "
00999                         "can cause the loss of archived articles and crashes at startup.</b> "
01000                         "You should disable the archive for now "
01001                         "unless you are sure that %2 is not already running.</qt>")
01002                         .arg( programName, programName, backendName );
01003               // QString::arg( st ) only replaces the first occurrence of %1
01004               // with st while QString::arg( s1, s2 ) replacess all occurrences
01005               // of %1 with s1 and all occurrences of %2 with s2. So don't
01006               // even think about changing the above to .arg( programName ).
01007             else
01008                 msg = i18n("<qt>%1 seems to be running on another display on this "
01009                         "machine. <b>Running %1 and %2 at the same "
01010                         "time is not supported by the %3 backend and can cause "
01011                         "the loss of archived articles and crashes at startup.</b> "
01012                         "You should disable the archive for now "
01013                         "unless you are sure that %2 is not already running.</qt>")
01014                         .arg( oldProgramName, programName, backendName );
01015         }
01016         else
01017         {
01018             if ( oldAppName == appName )
01019                 msg = i18n("<qt>%1 already seems to be running on %2. <b>Running %1 more "
01020                         "than once is not supported by the %3 backend and can cause "
01021                         "the loss of archived articles and crashes at startup.</b> "
01022                         "You should disable the archive for now "
01023                         "unless you are sure that it is "
01024                         "not already running on %2.</qt>")
01025                         .arg( programName, oldHostName, backendName );
01026             else
01027                 msg = i18n("<qt>%1 seems to be running on %3. <b>Running %1 and %2 at the "
01028                         "same time is not supported by the %4 backend and can cause "
01029                         "the loss of archived articles and crashes at startup.</b> "
01030                         "You should disable the archive for now "
01031                         "unless you are sure that %1 is "
01032                         "not running on %3.</qt>")
01033                         .arg( oldProgramName, programName, oldHostName, backendName );
01034         }
01035 
01036         KCursorSaver idle( KBusyPtr::idle() );
01037         if ( KMessageBox::No ==
01038              KMessageBox::warningYesNo( 0, msg, QString::null,
01039                                         i18n("Force Access"),
01040                                         i18n("Disable Archive")) )
01041                                         {
01042                                             return false;
01043                                         }
01044     }
01045 
01046     config.writeEntry("pid", getpid());
01047     config.writeEntry("hostname", hostName);
01048     config.writeEntry( "appName", appName );
01049     config.writeEntry( "programName", programName );
01050     config.sync();
01051     return true;
01052 }
01053 
01054 
01055 } // namespace Akregator
01056 #include "akregator_part.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys