akregator/src

akregator_part.cpp

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