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