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