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 TQt, and distribute the resulting executable,
00023     without including the source code for TQt 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 <tqfile.h>
00053 #include <tqobjectlist.h>
00054 #include <tqstringlist.h>
00055 #include <tqtimer.h>
00056 #include <tqwidgetlist.h>
00057 #include <tqucomextra_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( TQWidget *parentWidget, const char * /*widgetName*/,
00120                               TQObject *parent, const char *name, const TQStringList& )
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     TQStringList storageParams;
00148     
00149     storageParams.append(TQString("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, TQT_SIGNAL(setWindowCaption(const TQString&)), this, TQT_SIGNAL(setWindowCaption(const TQString&)));
00197     connect(m_view, TQT_SIGNAL(setStatusBarText(const TQString&)), this, TQT_SIGNAL(setStatusBarText(const TQString&)));
00198     connect(m_view, TQT_SIGNAL(setProgress(int)), m_extension, TQT_SIGNAL(loadingProgress(int)));
00199     connect(m_view, TQT_SIGNAL(signalCanceled(const TQString&)), this, TQT_SIGNAL(canceled(const TQString&)));
00200     connect(m_view, TQT_SIGNAL(signalStarted(KIO::Job*)), this, TQT_SIGNAL(started(KIO::Job*)));
00201     connect(m_view, TQT_SIGNAL(signalCompleted()), this, TQT_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, TQT_SIGNAL(showPart()), this, TQT_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, TQT_SIGNAL(quitSelected()),
00221             kapp, TQT_SLOT(quit())) ;
00222 
00223     connect( m_view, TQT_SIGNAL(signalUnreadCountChanged(int)), trayIcon, TQT_SLOT(slotSetUnread(int)) );
00224 
00225     connect(kapp, TQT_SIGNAL(shutDown()), this, TQT_SLOT(slotOnShutdown()));
00226 
00227     m_autosaveTimer = new TQTimer(this);
00228     connect(m_autosaveTimer, TQT_SIGNAL(timeout()), this, TQT_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(TQString("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 TQString 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     TQStringList 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 TQDomDocument Part::createDefaultFeedList()
00342 {
00343     TQDomDocument doc;
00344     TQDomProcessingInstruction z = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
00345     doc.appendChild( z );
00346 
00347     TQDomElement root = doc.createElement( "opml" );
00348     root.setAttribute("version","1.0");
00349     doc.appendChild( root );
00350 
00351     TQDomElement head = doc.createElement( "head" );
00352     root.appendChild(head);
00353 
00354     TQDomElement text = doc.createElement( "text" );
00355     text.appendChild(doc.createTextNode(i18n("Feeds")));
00356     head.appendChild(text);
00357 
00358     TQDomElement body = doc.createElement( "body" );
00359     root.appendChild(body);
00360 
00361     TQDomElement mainFolder = doc.createElement( "outline" );
00362     mainFolder.setAttribute("text","KDE");
00363     body.appendChild(mainFolder);
00364 
00365     TQDomElement 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     TQDomElement 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     TQDomElement 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     TQDomElement 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     TQDomElement 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     TQDomElement 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     TQString str;
00403     // m_file is always local so we can use TQFile on it
00404     TQFile file(m_file);
00405 
00406     bool fileExists = file.exists();
00407     TQString listBackup = m_storage->restoreFeedList();
00408      
00409     TQDomDocument 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 TQDom tree.
00420             TQTextStream stream(&file);
00421             stream.setEncoding(TQTextStream::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                 TQString backup = m_file + "-backup." +  TQString::number(TQDateTime::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             TQString backup = m_file + "-backup." +  TQString::number(TQDateTime::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(TQString());
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         TQString backup = m_file + "~";
00477 
00478         if (copyFile(backup))
00479             m_backedUpList = true;
00480     }
00481 
00482     TQString xmlStr = m_view->feedListToOPML().toString();
00483     m_storage->storeFeedList(xmlStr);
00484 
00485     TQFile 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 TQTextStream to dump the text to the file
00494     TQTextStream stream(&file);
00495     stream.setEncoding(TQTextStream::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           if (m_mergedPart)
00517             removeChildClient(m_mergedPart);
00518           else
00519             insertChildClient(part);
00520         }
00521         else
00522         {
00523           if (m_mergedPart) {
00524             factory()->removeClient(m_mergedPart);
00525             if (childClients()->containsRef(m_mergedPart))
00526               removeChildClient(m_mergedPart);
00527           }
00528           if (part)
00529             factory()->addClient(part);
00530         }
00531 
00532         m_mergedPart = part;
00533     }
00534     return true;
00535 }
00536 
00537 TQWidget* Part::getMainWindow()
00538 {
00539     // this is a dirty fix to get the main window used for the tray icon
00540     
00541     TQWidgetList *l = kapp->topLevelWidgets();
00542     TQWidgetListIt it( *l );
00543     TQWidget *wid;
00544 
00545     // check if there is an akregator main window
00546     while ( (wid = it.current()) != 0 )
00547     {
00548         ++it;
00549         //kdDebug() << "win name: " << wid->name() << endl;
00550         if (TQString(wid->name()) == "akregator_mainwindow")
00551         {
00552             delete l;
00553             return wid;
00554         }
00555     }
00556     // if not, check for kontact main window
00557     TQWidgetListIt it2( *l );
00558     while ( (wid = it2.current()) != 0 )
00559     {
00560         ++it2;
00561         if (TQString(wid->name()).startsWith("kontact-mainwindow"))
00562         {
00563             delete l;
00564             return wid;
00565         }
00566     }
00567     delete l;
00568     return 0;
00569 }
00570 
00571 void Part::loadTagSet(const TQString& path)
00572 {
00573     TQDomDocument doc;
00574 
00575     TQFile file(path);
00576     if (file.open(IO_ReadOnly))
00577     {
00578         doc.setContent(TQByteArray(file.readAll()));
00579         file.close();
00580     }
00581     // if we can't load the tagset from the xml file, check for the backup in the backend
00582     if (doc.isNull())
00583     {
00584         doc.setContent(m_storage->restoreTagSet());
00585     }
00586 
00587     if (!doc.isNull())
00588     {
00589         Kernel::self()->tagSet()->readFromXML(doc);
00590     }
00591     else
00592     {
00593         Kernel::self()->tagSet()->insert(Tag("http://akregator.sf.net/tags/Interesting", i18n("Interesting")));
00594     }
00595 }
00596 
00597 void Part::saveTagSet(const TQString& path)
00598 {
00599     TQString xmlStr = Kernel::self()->tagSet()->toXML().toString();
00600 
00601     m_storage->storeTagSet(xmlStr);
00602     
00603     TQFile file(path);
00604     
00605     if ( file.open(IO_WriteOnly) )
00606     {
00607 
00608         TQTextStream stream(&file);
00609         stream.setEncoding(TQTextStream::UnicodeUTF8);
00610         stream << xmlStr << "\n";
00611         file.close();
00612     }
00613 }
00614 
00615 void Part::importFile(const KURL& url)
00616 {
00617     TQString filename;
00618 
00619     bool isRemote = false;
00620 
00621     if (url.isLocalFile())
00622         filename = url.path();
00623     else
00624     {
00625         isRemote = true;
00626 
00627         if (!KIO::NetAccess::download(url, filename, m_view) )
00628         {
00629             KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() );
00630             return;
00631         }
00632     }
00633 
00634     TQFile file(filename);
00635     if (file.open(IO_ReadOnly))
00636     {
00637         // Read OPML feeds list and build TQDom tree.
00638         TQDomDocument doc;
00639         if (doc.setContent(TQByteArray(file.readAll())))
00640             m_view->importFeeds(doc);
00641         else
00642             KMessageBox::error(m_view, i18n("Could not import the file %1 (no valid OPML)").arg(filename), i18n("OPML Parsing Error") );
00643     }
00644     else
00645         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"));
00646 
00647     if (isRemote)
00648         KIO::NetAccess::removeTempFile(filename);
00649 }
00650 
00651 void Part::exportFile(const KURL& url)
00652 {
00653     if (url.isLocalFile())
00654     {
00655         TQFile file(url.path());
00656 
00657         if ( file.exists() &&
00658                 KMessageBox::questionYesNo(m_view,
00659             i18n("The file %1 already exists; do you want to overwrite it?").arg(file.name()),
00660             i18n("Export"),
00661             i18n("Overwrite"),
00662             KStdGuiItem::cancel()) == KMessageBox::No )
00663             return;
00664 
00665         if ( !file.open(IO_WriteOnly) )
00666         {
00667             KMessageBox::error(m_view, i18n("Access denied: cannot write to file %1").arg(file.name()), i18n("Write Error") );
00668             return;
00669         }
00670 
00671         TQTextStream stream(&file);
00672         stream.setEncoding(TQTextStream::UnicodeUTF8);
00673 
00674         stream << m_view->feedListToOPML().toString() << "\n";
00675         file.close();
00676     }
00677     else
00678     {
00679         KTempFile tmpfile;
00680         tmpfile.setAutoDelete(true);
00681 
00682         TQTextStream stream(tmpfile.file());
00683         stream.setEncoding(TQTextStream::UnicodeUTF8);
00684 
00685         stream << m_view->feedListToOPML().toString() << "\n";
00686         tmpfile.close();
00687 
00688         if (!KIO::NetAccess::upload(tmpfile.name(), url, m_view))
00689             KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() );
00690     }
00691 }
00692 
00693 void Part::fileImport()
00694 {
00695     KURL url = KFileDialog::getOpenURL( TQString(),
00696                         "*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)")
00697                         +"\n*|" + i18n("All Files") );
00698 
00699     if (!url.isEmpty())
00700         importFile(url);
00701 }
00702 
00703     void Part::fileExport()
00704 {
00705     KURL url= KFileDialog::getSaveURL( TQString(),
00706                         "*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)")
00707                         +"\n*|" + i18n("All Files") );
00708 
00709     if ( !url.isEmpty() )
00710         exportFile(url);
00711 }
00712 
00713 void Part::fileGetFeeds()
00714 {
00715     /*GetFeeds *gf = new GetFeeds();
00716     gf->show();*/
00717      //KNS::DownloadDialog::open("akregator/feeds", i18n("Get New Feeds"));
00718 }
00719 
00720 void Part::fileSendArticle(bool attach)
00721 {
00722     // FIXME: you have to open article to tab to be able to send...
00723     TQString title, text;
00724 
00725     text = m_view->currentFrame()->part()->url().prettyURL();
00726     if(text.isEmpty() || text.isNull())
00727         return;
00728 
00729     title = m_view->currentFrame()->title();
00730 
00731     if(attach) {
00732         kapp->invokeMailer("",
00733                            "",
00734                            "",
00735                            title,
00736                            text,
00737                            "",
00738                            text);
00739     }
00740     else {
00741         kapp->invokeMailer("",
00742                            "",
00743                            "",
00744                            title,
00745                            text);
00746     }
00747 }
00748 
00749 void Part::fetchAllFeeds()
00750 {
00751     m_view->slotFetchAllFeeds();
00752 }
00753 
00754 void Part::fetchFeedUrl(const TQString&s)
00755 {
00756     kdDebug() << "fetchFeedURL==" << s << endl;
00757 }
00758 
00759 void Part::addFeedsToGroup(const TQStringList& urls, const TQString& group)
00760 {
00761     for (TQStringList::ConstIterator it = urls.begin(); it != urls.end(); ++it)
00762     {
00763         kdDebug() << "Akregator::Part::addFeedToGroup adding feed with URL " << *it << " to group " << group << endl;
00764         m_view->addFeedToGroup(*it, group);
00765     }
00766     NotificationManager::self()->slotNotifyFeeds(urls);
00767 }
00768 
00769 void Part::addFeed()
00770 {
00771     m_view->slotFeedAdd();
00772 }
00773 
00774 KAboutData *Part::createAboutData()
00775 {
00776     return new Akregator::AboutData;
00777 }
00778 
00779 void Part::showKNotifyOptions()
00780 {
00781     KAboutData* about = new Akregator::AboutData;
00782     KNotifyDialog::configure(m_view, "akregator_knotify_config", about);
00783     delete about;
00784 }
00785 
00786 void Part::showOptions()
00787 {
00788     if ( KConfigDialog::showDialog( "settings" ) )
00789         return;
00790 
00791     KConfigDialog* dialog = new ConfigDialog( m_view, "settings", Settings::self() );
00792 
00793     connect( dialog, TQT_SIGNAL(settingsChanged()),
00794              this, TQT_SLOT(slotSettingsChanged()) );
00795     connect( dialog, TQT_SIGNAL(settingsChanged()),
00796              TrayIcon::getInstance(), TQT_SLOT(settingsChanged()) );
00797 
00798     dialog->show();
00799 }
00800 
00801 void Part::partActivateEvent(KParts::PartActivateEvent* event)
00802 {
00803     if (factory() && m_mergedPart)
00804     {
00805         if (event->activated())
00806             factory()->addClient(m_mergedPart);
00807         else
00808             factory()->removeClient(m_mergedPart);
00809     }
00810 
00811     MyBasePart::partActivateEvent(event);
00812 }
00813 
00814 KParts::Part* Part::hitTest(TQWidget *widget, const TQPoint &globalPos)
00815 {
00816     bool child = false;
00817     TQWidget *me = this->widget();
00818     while (widget) {
00819         if (widget == me) {
00820             child = true;
00821             break;
00822         }
00823         if (!widget) {
00824             break;
00825         }
00826         widget = widget->parentWidget();
00827     }
00828     if (m_view && m_view->currentFrame() && child) {
00829         return m_view->currentFrame()->part();
00830     } else {
00831         return MyBasePart::hitTest(widget, globalPos);
00832     }
00833 }
00834 
00835 void Part::initFonts()
00836 {
00837     TQStringList fonts = Settings::fonts();
00838     if (fonts.isEmpty())
00839     {
00840         fonts.append(KGlobalSettings::generalFont().family());
00841         fonts.append(KGlobalSettings::fixedFont().family());
00842         fonts.append(KGlobalSettings::generalFont().family());
00843         fonts.append(KGlobalSettings::generalFont().family());
00844         fonts.append("0");
00845     }
00846     Settings::setFonts(fonts);
00847     if (Settings::standardFont().isEmpty())
00848         Settings::setStandardFont(fonts[0]);
00849     if (Settings::fixedFont().isEmpty())
00850         Settings::setFixedFont(fonts[1]);
00851     if (Settings::sansSerifFont().isEmpty())
00852         Settings::setSansSerifFont(fonts[2]);
00853     if (Settings::serifFont().isEmpty())
00854         Settings::setSerifFont(fonts[3]);
00855 
00856     KConfig* conf = Settings::self()->config();
00857     conf->setGroup("HTML Settings");
00858 
00859     KConfig konq("konquerorrc", true, false);
00860     konq.setGroup("HTML Settings");
00861 
00862     if (!conf->hasKey("MinimumFontSize"))
00863     {
00864         int minfs;
00865         if (konq.hasKey("MinimumFontSize"))
00866             minfs = konq.readNumEntry("MinimumFontSize");
00867         else
00868             minfs = KGlobalSettings::generalFont().pointSize();
00869         kdDebug() << "Part::initFonts(): set MinimumFontSize to " << minfs << endl;
00870         Settings::setMinimumFontSize(minfs);
00871     }
00872 
00873     if (!conf->hasKey("MediumFontSize"))
00874     {
00875         int medfs;
00876         if (konq.hasKey("MediumFontSize"))
00877             medfs = konq.readNumEntry("MediumFontSize");
00878         else
00879             medfs = KGlobalSettings::generalFont().pointSize();
00880         kdDebug() << "Part::initFonts(): set MediumFontSize to " << medfs << endl;
00881         Settings::setMediumFontSize(medfs);
00882     }
00883 
00884     if (!conf->hasKey("UnderlineLinks"))
00885     {
00886         bool underline = true;
00887         if (konq.hasKey("UnderlineLinks"))
00888             underline = konq.readBoolEntry("UnderlineLinks");
00889 
00890         kdDebug() << "Part::initFonts(): set UnderlineLinks to " << underline << endl;
00891         Settings::setUnderlineLinks(underline);
00892     }
00893 
00894 }
00895 
00896 bool Part::copyFile(const TQString& backup)
00897 {
00898     TQFile file(m_file);
00899 
00900     if (file.open(IO_ReadOnly))
00901     {
00902         TQFile backupFile(backup);
00903         if (backupFile.open(IO_WriteOnly))
00904         {
00905             TQTextStream in(&file);
00906             TQTextStream out(&backupFile);
00907             while (!in.atEnd())
00908                 out << in.readLine();
00909             backupFile.close();
00910             file.close();
00911             return true;
00912         }
00913         else
00914         {
00915             file.close();
00916             return false;
00917         }
00918     }
00919     return false;
00920 }
00921 
00922 static TQString getMyHostName()
00923 {
00924     char hostNameC[256];
00925     // null terminate this C string
00926     hostNameC[255] = 0;
00927     // set the string to 0 length if gethostname fails
00928     if(gethostname(hostNameC, 255))
00929         hostNameC[0] = 0;
00930     return TQString::fromLocal8Bit(hostNameC);
00931 }
00932 
00933 // taken from KMail
00934 bool Part::tryToLock(const TQString& backendName)
00935 {
00936 // Check and create a lock file to prevent concurrent access to metakit archive
00937     TQString appName = kapp->instanceName();
00938     if ( appName.isEmpty() )
00939         appName = "akregator";
00940 
00941     TQString programName;
00942     const KAboutData *about = kapp->aboutData();
00943     if ( about )
00944         programName = about->programName();
00945     if ( programName.isEmpty() )
00946         programName = i18n("Akregator");
00947 
00948     TQString lockLocation = locateLocal("data", "akregator/lock");
00949     KSimpleConfig config(lockLocation);
00950     int oldPid = config.readNumEntry("pid", -1);
00951     const TQString oldHostName = config.readEntry("hostname");
00952     const TQString oldAppName = config.readEntry( "appName", appName );
00953     const TQString oldProgramName = config.readEntry( "programName", programName );
00954     const TQString hostName = getMyHostName();
00955     bool first_instance = false;
00956     if ( oldPid == -1 )
00957         first_instance = true;
00958   // check if the lock file is stale by trying to see if
00959   // the other pid is currently running.
00960   // Not 100% correct but better safe than sorry
00961     else if (hostName == oldHostName && oldPid != getpid()) {
00962         if ( kill(oldPid, 0) == -1 )
00963             first_instance = ( errno == ESRCH );
00964     }
00965 
00966     if ( !first_instance )
00967     {
00968         TQString msg;
00969         if ( oldHostName == hostName ) 
00970         {
00971             // this can only happen if the user is running this application on
00972             // different displays on the same machine. All other cases will be
00973             // taken care of by KUniqueApplication()
00974             if ( oldAppName == appName )
00975                 msg = i18n("<qt>%1 already seems to be running on another display on "
00976                         "this machine. <b>Running %2 more than once is not supported "
00977                         "by the %3 backend and "
00978                         "can cause the loss of archived articles and crashes at startup.</b> "
00979                         "You should disable the archive for now "
00980                         "unless you are sure that %2 is not already running.</qt>")
00981                         .arg( programName, programName, backendName );
00982               // TQString::arg( st ) only replaces the first occurrence of %1
00983               // with st while TQString::arg( s1, s2 ) replacess all occurrences
00984               // of %1 with s1 and all occurrences of %2 with s2. So don't
00985               // even think about changing the above to .arg( programName ).
00986             else
00987                 msg = i18n("<qt>%1 seems to be running on another display on this "
00988                         "machine. <b>Running %1 and %2 at the same "
00989                         "time is not supported by the %3 backend and can cause "
00990                         "the loss of archived articles and crashes at startup.</b> "
00991                         "You should disable the archive for now "
00992                         "unless you are sure that %2 is not already running.</qt>")
00993                         .arg( oldProgramName, programName, backendName );
00994         }
00995         else
00996         {
00997             if ( oldAppName == appName )
00998                 msg = i18n("<qt>%1 already seems to be running on %2. <b>Running %1 more "
00999                         "than once is not supported by the %3 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 it is "
01003                         "not already running on %2.</qt>")
01004                         .arg( programName, oldHostName, backendName );
01005             else
01006                 msg = i18n("<qt>%1 seems to be running on %3. <b>Running %1 and %2 at the "
01007                         "same time is not supported by the %4 backend and can cause "
01008                         "the loss of archived articles and crashes at startup.</b> "
01009                         "You should disable the archive for now "
01010                         "unless you are sure that %1 is "
01011                         "not running on %3.</qt>")
01012                         .arg( oldProgramName, programName, oldHostName, backendName );
01013         }
01014 
01015         KCursorSaver idle( KBusyPtr::idle() );
01016         if ( KMessageBox::No ==
01017              KMessageBox::warningYesNo( 0, msg, TQString(),
01018                                         i18n("Force Access"),
01019                                         i18n("Disable Archive")) )
01020                                         {
01021                                             return false;
01022                                         }
01023     }
01024 
01025     config.writeEntry("pid", getpid());
01026     config.writeEntry("hostname", hostName);
01027     config.writeEntry( "appName", appName );
01028     config.writeEntry( "programName", programName );
01029     config.sync();
01030     return true;
01031 }
01032 
01033 
01034 } // namespace Akregator
01035 #include "akregator_part.moc"