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 <tdeaboutdata.h>
00027 #include <tdeaction.h>
00028 #include <tdeactionclasses.h>
00029 #include <tdeactioncollection.h>
00030 #include <tdeapplication.h>
00031 #include <tdeconfig.h>
00032 #include <tdeconfigdialog.h>
00033 #include <tdefiledialog.h>
00034 #include <tdeglobalsettings.h>
00035 #include <tdehtmldefaults.h>
00036 #include <kinstance.h>
00037 #include <tdemainwindow.h>
00038 #include <tdemessagebox.h>
00039 #include <knotifyclient.h>
00040 #include <knotifydialog.h>
00041 #include <tdepopupmenu.h>
00042 #include <kservice.h>
00043 #include <kstandarddirs.h>
00044 #include <kstdaction.h>
00045 #include <tdetempfile.h>
00046 #include <ktrader.h>
00047 #include <tdeio/netaccess.h>
00048 #include <tdeparts/browserinterface.h>
00049 #include <tdeparts/genericfactory.h>
00050 #include <tdeparts/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 ~ApplyFiltersInterceptor() {}
00112 
00113     virtual void processArticle(Article& article)
00114     {
00115         Filters::ArticleFilterList list = Kernel::self()->articleFilterList();
00116         for (Filters::ArticleFilterList::ConstIterator it = list.begin(); it != list.end(); ++it)
00117             (*it).applyTo(article);
00118     }
00119 };
00120 
00121 Part::Part( TQWidget *parentWidget, const char * /*widgetName*/,
00122                               TQObject *parent, const char *name, const TQStringList& )
00123     : DCOPObject("AkregatorIface")
00124        , MyBasePart(parent, name)
00125        , m_standardListLoaded(false)
00126        , m_shuttingDown(false)
00127        , m_mergedPart(0)
00128        , m_view(0)
00129        , m_backedUpList(false)
00130        , m_storage(0)
00131 {
00132     // we need an instance
00133     setInstance( AkregatorFactory::instance() );
00134 
00135     // start knotifyclient if not already started. makes it work for people who doesn't use full kde, according to kmail devels
00136     KNotifyClient::startDaemon();
00137 
00138     m_standardFeedList = TDEGlobal::dirs()->saveLocation("data", "akregator/data") + "/feeds.opml";
00139 
00140     m_tagSetPath = TDEGlobal::dirs()->saveLocation("data", "akregator/data") + "/tagset.xml";
00141 
00142     Backend::StorageFactoryDummyImpl* dummyFactory = new Backend::StorageFactoryDummyImpl();
00143     Backend::StorageFactoryRegistry::self()->registerFactory(dummyFactory, dummyFactory->key());
00144     loadPlugins(); // FIXME: also unload them!
00145 
00146     m_storage = 0;
00147     Backend::StorageFactory* factory = Backend::StorageFactoryRegistry::self()->getFactory(Settings::archiveBackend());
00148    
00149     TQStringList storageParams;
00150     
00151     storageParams.append(TQString("taggingEnabled=%1").arg(Settings::showTaggingGUI() ? "true" : "false"));
00152     
00153     if (factory != 0)
00154     {
00155         if (factory->allowsMultipleWriteAccess())
00156         {
00157             m_storage = factory->createStorage(storageParams);
00158         } 
00159         else
00160         {
00161             if (tryToLock(factory->name()))
00162                 m_storage = factory->createStorage(storageParams);
00163             else 
00164                 m_storage = dummyFactory->createStorage(storageParams);
00165         }
00166     }
00167     
00168 
00169     if (!m_storage) // Houston, we have a problem
00170     {
00171         m_storage = Backend::StorageFactoryRegistry::self()->getFactory("dummy")->createStorage(storageParams);
00172 
00173         KMessageBox::error(parentWidget, i18n("Unable to load storage backend plugin \"%1\". No feeds are archived.").arg(Settings::archiveBackend()), i18n("Plugin error") );
00174     }
00175 
00176     Filters::ArticleFilterList list;
00177     list.readConfig(Settings::self()->config());
00178     Kernel::self()->setArticleFilterList(list);
00179 
00180     m_applyFiltersInterceptor = new ApplyFiltersInterceptor();
00181     ArticleInterceptorManager::self()->addInterceptor(m_applyFiltersInterceptor);
00182 
00183     m_storage->open(true);
00184     Kernel::self()->setStorage(m_storage);
00185     Backend::Storage::setInstance(m_storage); // TODO: kill this one
00186 
00187     loadTagSet(m_tagSetPath);
00188 
00189     m_actionManager = new ActionManagerImpl(this);
00190     ActionManager::setInstance(m_actionManager);
00191 
00192     m_view = new Akregator::View(this, parentWidget, m_actionManager, "akregator_view");
00193     m_actionManager->initView(m_view);
00194     m_actionManager->setTagSet(Kernel::self()->tagSet());
00195 
00196     m_extension = new BrowserExtension(this, "ak_extension");
00197 
00198     connect(m_view, TQT_SIGNAL(setWindowCaption(const TQString&)), this, TQT_SIGNAL(setWindowCaption(const TQString&)));
00199     connect(m_view, TQT_SIGNAL(setStatusBarText(const TQString&)), this, TQT_SIGNAL(setStatusBarText(const TQString&)));
00200     connect(m_view, TQT_SIGNAL(setProgress(int)), m_extension, TQT_SIGNAL(loadingProgress(int)));
00201     connect(m_view, TQT_SIGNAL(signalCanceled(const TQString&)), this, TQT_SIGNAL(canceled(const TQString&)));
00202     connect(m_view, TQT_SIGNAL(signalStarted(TDEIO::Job*)), this, TQT_SIGNAL(started(TDEIO::Job*)));
00203     connect(m_view, TQT_SIGNAL(signalCompleted()), this, TQT_SIGNAL(completed()));
00204 
00205     // notify the part that this is our internal widget
00206     setWidget(m_view);
00207 
00208     TrayIcon* trayIcon = new TrayIcon( getMainWindow() );
00209     TrayIcon::setInstance(trayIcon);
00210     m_actionManager->initTrayIcon(trayIcon);
00211 
00212     connect(trayIcon, TQT_SIGNAL(showPart()), this, TQT_SIGNAL(showPart()));
00213 
00214     if ( isTrayIconEnabled() )
00215     {
00216         trayIcon->show();
00217         NotificationManager::self()->setWidget(trayIcon, instance());
00218     }
00219     else
00220         NotificationManager::self()->setWidget(getMainWindow(), instance());
00221 
00222     connect( trayIcon, TQT_SIGNAL(quitSelected()),
00223             kapp, TQT_SLOT(quit())) ;
00224 
00225     connect( m_view, TQT_SIGNAL(signalUnreadCountChanged(int)), trayIcon, TQT_SLOT(slotSetUnread(int)) );
00226 
00227     connect(kapp, TQT_SIGNAL(shutDown()), this, TQT_SLOT(slotOnShutdown()));
00228 
00229     m_autosaveTimer = new TQTimer(this);
00230     connect(m_autosaveTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotSaveFeedList()));
00231     m_autosaveTimer->start(5*60*1000); // 5 minutes
00232 
00233     setXMLFile("akregator_part.rc", true);
00234 
00235     initFonts();
00236 
00237     RSS::FileRetriever::setUserAgent(TQString("Akregator/%1; librss/remnants").arg(AKREGATOR_VERSION));
00238 }
00239 
00240 void Part::loadPlugins()
00241 {
00242     // "[X-TDE-akregator-plugintype] == 'storage'"
00243     TDETrader::OfferList offers = PluginManager::query();
00244 
00245     for( TDETrader::OfferList::ConstIterator it = offers.begin(), end = offers.end(); it != end; ++it )
00246     {
00247         Akregator::Plugin* plugin = PluginManager::createFromService(*it);
00248         if (plugin)
00249             plugin->init();
00250     }
00251 }
00252 
00253 void Part::slotOnShutdown()
00254 {
00255     m_shuttingDown = true;
00256     
00257     const TQString lockLocation = locateLocal("data", "akregator/lock");
00258     KSimpleConfig config(lockLocation);
00259     config.writeEntry("pid", -1);
00260     config.sync();
00261 
00262     m_autosaveTimer->stop();
00263     saveSettings();
00264     slotSaveFeedList();
00265     saveTagSet(m_tagSetPath);
00266     m_view->slotOnShutdown();
00267     //delete m_view;
00268     delete TrayIcon::getInstance();
00269     TrayIcon::setInstance(0L);
00270     delete m_storage;
00271     m_storage = 0;
00272     //delete m_actionManager;
00273 }
00274 
00275 void Part::slotSettingsChanged()
00276 {
00277     NotificationManager::self()->setWidget(isTrayIconEnabled() ? TrayIcon::getInstance() : getMainWindow(), instance());
00278 
00279     RSS::FileRetriever::setUseCache(Settings::useHTMLCache());
00280 
00281     TQStringList fonts;
00282     fonts.append(Settings::standardFont());
00283     fonts.append(Settings::fixedFont());
00284     fonts.append(Settings::sansSerifFont());
00285     fonts.append(Settings::serifFont());
00286     fonts.append(Settings::standardFont());
00287     fonts.append(Settings::standardFont());
00288     fonts.append("0");
00289     Settings::setFonts(fonts);
00290 
00291     if (Settings::minimumFontSize() > Settings::mediumFontSize())
00292         Settings::setMediumFontSize(Settings::minimumFontSize());
00293     saveSettings();
00294     m_view->slotSettingsChanged();
00295     emit signalSettingsChanged();
00296 }
00297 void Part::saveSettings()
00298 {
00299     Kernel::self()->articleFilterList().writeConfig(Settings::self()->config());
00300     m_view->saveSettings();
00301 }
00302 
00303 Part::~Part()
00304 {
00305     kdDebug() << "Part::~Part() enter" << endl;
00306     if (!m_shuttingDown)
00307         slotOnShutdown();
00308     kdDebug() << "Part::~Part(): leaving" << endl;
00309     ArticleInterceptorManager::self()->removeInterceptor(m_applyFiltersInterceptor);
00310     delete m_applyFiltersInterceptor;
00311 }
00312 
00313 void Part::readProperties(TDEConfig* config)
00314 {
00315     m_backedUpList = false;
00316     openStandardFeedList();
00317 
00318     if(m_view)
00319         m_view->readProperties(config);
00320 }
00321 
00322 void Part::saveProperties(TDEConfig* config)
00323 {
00324     if (m_view)
00325     {
00326         slotSaveFeedList();
00327         m_view->saveProperties(config);
00328     }
00329 }
00330 
00331 bool Part::openURL(const KURL& url)
00332 {
00333     m_file = url.path();
00334     return openFile();
00335 }
00336 
00337 void Part::openStandardFeedList()
00338 {
00339     if ( !m_standardFeedList.isEmpty() && openURL(m_standardFeedList) )
00340         m_standardListLoaded = true;
00341 }
00342 
00343 TQDomDocument Part::createDefaultFeedList()
00344 {
00345     TQDomDocument doc;
00346     TQDomProcessingInstruction z = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
00347     doc.appendChild( z );
00348 
00349     TQDomElement root = doc.createElement( "opml" );
00350     root.setAttribute("version","1.0");
00351     doc.appendChild( root );
00352 
00353     TQDomElement head = doc.createElement( "head" );
00354     root.appendChild(head);
00355 
00356     TQDomElement text = doc.createElement( "text" );
00357     text.appendChild(doc.createTextNode(i18n("Feeds")));
00358     head.appendChild(text);
00359 
00360     TQDomElement body = doc.createElement( "body" );
00361     root.appendChild(body);
00362 
00363     TQDomElement mainFolder = doc.createElement( "outline" );
00364     mainFolder.setAttribute("text","Free/Libre Software News");
00365     body.appendChild(mainFolder);
00366 
00367     TQDomElement tde = doc.createElement( "outline" );
00368     tde.setAttribute("text",i18n("Trinity Desktop News"));
00369     tde.setAttribute("xmlUrl","http://trinitydesktop.org/rss.php");
00370     mainFolder.appendChild(tde);
00371 
00372     TQDomElement lxer = doc.createElement( "outline" );
00373     lxer.setAttribute("text",i18n("LXer Linux News"));
00374     lxer.setAttribute("xmlUrl","http://lxer.com/module/newswire/headlines.rss");
00375     mainFolder.appendChild(lxer);
00376 
00377     TQDomElement tux = doc.createElement( "outline" );
00378     tux.setAttribute("text",i18n("Tuxmachines"));
00379     tux.setAttribute("xmlUrl","http://www.tuxmachines.org/node/feed");
00380     mainFolder.appendChild(tux);
00381 
00382     TQDomElement lwn = doc.createElement( "outline" );
00383     lwn.setAttribute("text",i18n("lwn.net"));
00384     lwn.setAttribute("xmlUrl","http://lwn.net/headlines/rss");
00385     mainFolder.appendChild(lwn);
00386 
00387     return doc;
00388 }
00389 
00390 bool Part::openFile()
00391 {
00392     emit setStatusBarText(i18n("Opening Feed List...") );
00393 
00394     TQString str;
00395     // m_file is always local so we can use TQFile on it
00396     TQFile file(m_file);
00397 
00398     bool fileExists = file.exists();
00399     TQString listBackup = m_storage->restoreFeedList();
00400      
00401     TQDomDocument doc;
00402 
00403     if (!fileExists)
00404     {
00405         doc = createDefaultFeedList();
00406     }
00407     else 
00408     {
00409         if (file.open(IO_ReadOnly))
00410         {
00411             // Read OPML feeds list and build TQDom tree.
00412             TQTextStream stream(&file);
00413             stream.setEncoding(TQTextStream::UnicodeUTF8); // FIXME not all opmls are in utf8
00414             str = stream.read();
00415             file.close();
00416         }
00417 
00418         if (!doc.setContent(str))
00419         {
00420 
00421             if (file.size() > 0) // don't backup empty files 
00422             {
00423                 TQString backup = m_file + "-backup." +  TQString::number(TQDateTime::currentDateTime().toTime_t());
00424         
00425                 copyFile(backup);
00426         
00427                 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") );
00428             }
00429 
00430             if (!doc.setContent(listBackup))
00431                 doc = createDefaultFeedList();
00432         }
00433     }
00434 
00435     if (!m_view->loadFeeds(doc))
00436     {
00437         if (file.size() > 0) // don't backup empty files 
00438         {
00439             TQString backup = m_file + "-backup." +  TQString::number(TQDateTime::currentDateTime().toTime_t());
00440             copyFile(backup);
00441 
00442             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") );
00443         }
00444         m_view->loadFeeds(createDefaultFeedList());
00445     }
00446 
00447     emit setStatusBarText(TQString());
00448     
00449 
00450     if( Settings::markAllFeedsReadOnStartup() )
00451         m_view->slotMarkAllFeedsRead();
00452 
00453     if (Settings::fetchOnStartup())
00454             m_view->slotFetchAllFeeds();
00455 
00456     return true;
00457 }
00458 
00459 void Part::slotSaveFeedList()
00460 {
00461     // don't save to the standard feed list, when it wasn't completely loaded before
00462     if (!m_standardListLoaded)
00463         return;
00464 
00465     // the first time we overwrite the feed list, we create a backup
00466     if (!m_backedUpList)
00467     {
00468         TQString backup = m_file + "~";
00469 
00470         if (copyFile(backup))
00471             m_backedUpList = true;
00472     }
00473 
00474     TQString xmlStr = m_view->feedListToOPML().toString();
00475     m_storage->storeFeedList(xmlStr);
00476 
00477     TQFile file(m_file);
00478     if (file.open(IO_WriteOnly) == false)
00479     {
00480         //FIXME: allow to save the feedlist into different location -tpr 20041118
00481         KMessageBox::error(m_view, i18n("Access denied: cannot save feed list (%1)").arg(m_file), i18n("Write error") );
00482         return;
00483     }
00484 
00485     // use TQTextStream to dump the text to the file
00486     TQTextStream stream(&file);
00487     stream.setEncoding(TQTextStream::UnicodeUTF8);
00488 
00489     // Write OPML data file.
00490     // Archive data files are saved elsewhere.
00491 
00492     stream << xmlStr << endl;
00493 
00494     file.close();
00495 }
00496 
00497 bool Part::isTrayIconEnabled() const
00498 {
00499     return Settings::showTrayIcon();
00500 }
00501 
00502 bool Part::mergePart(KParts::Part* part)
00503 {
00504     if (part != m_mergedPart)
00505     {
00506         if (!factory())
00507         {
00508           if (m_mergedPart)
00509             removeChildClient(m_mergedPart);
00510           else
00511             insertChildClient(part);
00512         }
00513         else
00514         {
00515           if (m_mergedPart) {
00516             factory()->removeClient(m_mergedPart);
00517             if (childClients()->containsRef(m_mergedPart))
00518               removeChildClient(m_mergedPart);
00519           }
00520           if (part)
00521             factory()->addClient(part);
00522         }
00523 
00524         m_mergedPart = part;
00525     }
00526     return true;
00527 }
00528 
00529 TQWidget* Part::getMainWindow()
00530 {
00531     // this is a dirty fix to get the main window used for the tray icon
00532     
00533     TQWidgetList *l = kapp->topLevelWidgets();
00534     TQWidgetListIt it( *l );
00535     TQWidget *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 (TQString(wid->name()) == "akregator_mainwindow")
00543         {
00544             delete l;
00545             return wid;
00546         }
00547     }
00548     // if not, check for kontact main window
00549     TQWidgetListIt it2( *l );
00550     while ( (wid = it2.current()) != 0 )
00551     {
00552         ++it2;
00553         if (TQString(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 TQString& path)
00564 {
00565     TQDomDocument doc;
00566 
00567     TQFile file(path);
00568     if (file.open(IO_ReadOnly))
00569     {
00570         doc.setContent(TQByteArray(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 TQString& path)
00590 {
00591     TQString xmlStr = Kernel::self()->tagSet()->toXML().toString();
00592 
00593     m_storage->storeTagSet(xmlStr);
00594     
00595     TQFile file(path);
00596     
00597     if ( file.open(IO_WriteOnly) )
00598     {
00599 
00600         TQTextStream stream(&file);
00601         stream.setEncoding(TQTextStream::UnicodeUTF8);
00602         stream << xmlStr << "\n";
00603         file.close();
00604     }
00605 }
00606 
00607 void Part::importFile(const KURL& url)
00608 {
00609     TQString filename;
00610 
00611     bool isRemote = false;
00612 
00613     if (url.isLocalFile())
00614         filename = url.path();
00615     else
00616     {
00617         isRemote = true;
00618 
00619         if (!TDEIO::NetAccess::download(url, filename, m_view) )
00620         {
00621             KMessageBox::error(m_view, TDEIO::NetAccess::lastErrorString() );
00622             return;
00623         }
00624     }
00625 
00626     TQFile file(filename);
00627     if (file.open(IO_ReadOnly))
00628     {
00629         // Read OPML feeds list and build TQDom tree.
00630         TQDomDocument doc;
00631         if (doc.setContent(TQByteArray(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         TDEIO::NetAccess::removeTempFile(filename);
00641 }
00642 
00643 void Part::exportFile(const KURL& url)
00644 {
00645     if (url.isLocalFile())
00646     {
00647         TQFile 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         TQTextStream stream(&file);
00664         stream.setEncoding(TQTextStream::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         TQTextStream stream(tmpfile.file());
00675         stream.setEncoding(TQTextStream::UnicodeUTF8);
00676 
00677         stream << m_view->feedListToOPML().toString() << "\n";
00678         tmpfile.close();
00679 
00680         if (!TDEIO::NetAccess::upload(tmpfile.name(), url, m_view))
00681             KMessageBox::error(m_view, TDEIO::NetAccess::lastErrorString() );
00682     }
00683 }
00684 
00685 void Part::fileImport()
00686 {
00687     KURL url = KFileDialog::getOpenURL( TQString(),
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( TQString(),
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     TQString 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 TQString&s)
00747 {
00748     kdDebug() << "fetchFeedURL==" << s << endl;
00749 }
00750 
00751 void Part::addFeedsToGroup(const TQStringList& urls, const TQString& group)
00752 {
00753     for (TQStringList::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 TDEAboutData *Part::createAboutData()
00767 {
00768     return new Akregator::AboutData;
00769 }
00770 
00771 void Part::showKNotifyOptions()
00772 {
00773     TDEAboutData* 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 ( TDEConfigDialog::showDialog( "settings" ) )
00781         return;
00782 
00783     TDEConfigDialog* dialog = new ConfigDialog( m_view, "settings", Settings::self() );
00784 
00785     connect( dialog, TQT_SIGNAL(settingsChanged()),
00786              this, TQT_SLOT(slotSettingsChanged()) );
00787     connect( dialog, TQT_SIGNAL(settingsChanged()),
00788              TrayIcon::getInstance(), TQT_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(TQWidget *widget, const TQPoint &globalPos)
00807 {
00808     bool child = false;
00809     TQWidget *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     TQStringList fonts = Settings::fonts();
00830     if (fonts.isEmpty())
00831     {
00832         fonts.append(TDEGlobalSettings::generalFont().family());
00833         fonts.append(TDEGlobalSettings::fixedFont().family());
00834         fonts.append(TDEGlobalSettings::generalFont().family());
00835         fonts.append(TDEGlobalSettings::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     TDEConfig* conf = Settings::self()->config();
00849     conf->setGroup("HTML Settings");
00850 
00851     TDEConfig 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 = TDEGlobalSettings::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 = TDEGlobalSettings::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     if (!conf->hasKey("EnableFavicon"))
00887     {
00888         bool enableFavicon = true;
00889         if (konq.hasKey("EnableFavicon"))
00890             enableFavicon = konq.readBoolEntry("EnableFavicon");
00891 
00892         kdDebug() << "Part::initFonts(): set EnableFavicon to " << enableFavicon << endl;
00893         Settings::setEnableFavIcon(enableFavicon);
00894     }
00895 
00896     if (!conf->hasKey("AutoLoadImages"))
00897     {
00898         bool autoLoadImages = true;
00899         if (konq.hasKey("AutoLoadImages"))
00900             autoLoadImages = konq.readBoolEntry("AutoLoadImages");
00901 
00902         kdDebug() << "Part::initFonts(): set AutoLoadImages to " << autoLoadImages << endl;
00903         Settings::setAutoLoadImages(autoLoadImages);
00904     }
00905 
00906 }
00907 
00908 bool Part::copyFile(const TQString& backup)
00909 {
00910     TQFile file(m_file);
00911 
00912     if (file.open(IO_ReadOnly))
00913     {
00914         TQFile backupFile(backup);
00915         if (backupFile.open(IO_WriteOnly))
00916         {
00917             TQTextStream in(&file);
00918             TQTextStream out(&backupFile);
00919             while (!in.atEnd())
00920                 out << in.readLine();
00921             backupFile.close();
00922             file.close();
00923             return true;
00924         }
00925         else
00926         {
00927             file.close();
00928             return false;
00929         }
00930     }
00931     return false;
00932 }
00933 
00934 static TQString getMyHostName()
00935 {
00936     char hostNameC[256];
00937     // null terminate this C string
00938     hostNameC[255] = 0;
00939     // set the string to 0 length if gethostname fails
00940     if(gethostname(hostNameC, 255))
00941         hostNameC[0] = 0;
00942     return TQString::fromLocal8Bit(hostNameC);
00943 }
00944 
00945 // taken from KMail
00946 bool Part::tryToLock(const TQString& backendName)
00947 {
00948 // Check and create a lock file to prevent concurrent access to metakit archive
00949     TQString appName = kapp->instanceName();
00950     if ( appName.isEmpty() )
00951         appName = "akregator";
00952 
00953     TQString programName;
00954     const TDEAboutData *about = kapp->aboutData();
00955     if ( about )
00956         programName = about->programName();
00957     if ( programName.isEmpty() )
00958         programName = i18n("Akregator");
00959 
00960     TQString lockLocation = locateLocal("data", "akregator/lock");
00961     KSimpleConfig config(lockLocation);
00962     int oldPid = config.readNumEntry("pid", -1);
00963     const TQString oldHostName = config.readEntry("hostname");
00964     const TQString oldAppName = config.readEntry( "appName", appName );
00965     const TQString oldProgramName = config.readEntry( "programName", programName );
00966     const TQString hostName = getMyHostName();
00967     bool first_instance = false;
00968     if ( oldPid == -1 )
00969         first_instance = true;
00970   // check if the lock file is stale by trying to see if
00971   // the other pid is currently running.
00972   // Not 100% correct but better safe than sorry
00973     else if (hostName == oldHostName && oldPid != getpid()) {
00974         if ( kill(oldPid, 0) == -1 )
00975             first_instance = ( errno == ESRCH );
00976     }
00977 
00978     if ( !first_instance )
00979     {
00980         TQString msg;
00981         if ( oldHostName == hostName ) 
00982         {
00983             // this can only happen if the user is running this application on
00984             // different displays on the same machine. All other cases will be
00985             // taken care of by KUniqueApplication()
00986             if ( oldAppName == appName )
00987                 msg = i18n("<qt>%1 already seems to be running on another display on "
00988                         "this machine. <b>Running %2 more than once is not supported "
00989                         "by the %3 backend and "
00990                         "can cause 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( programName, programName, backendName );
00994               // TQString::arg( st ) only replaces the first occurrence of %1
00995               // with st while TQString::arg( s1, s2 ) replacess all occurrences
00996               // of %1 with s1 and all occurrences of %2 with s2. So don't
00997               // even think about changing the above to .arg( programName ).
00998             else
00999                 msg = i18n("<qt>%1 seems to be running on another display on this "
01000                         "machine. <b>Running %1 and %2 at the same "
01001                         "time is not supported by the %3 backend and can cause "
01002                         "the loss of archived articles and crashes at startup.</b> "
01003                         "You should disable the archive for now "
01004                         "unless you are sure that %2 is not already running.</qt>")
01005                         .arg( oldProgramName, programName, backendName );
01006         }
01007         else
01008         {
01009             if ( oldAppName == appName )
01010                 msg = i18n("<qt>%1 already seems to be running on %2. <b>Running %1 more "
01011                         "than once is not supported by the %3 backend and can cause "
01012                         "the loss of archived articles and crashes at startup.</b> "
01013                         "You should disable the archive for now "
01014                         "unless you are sure that it is "
01015                         "not already running on %2.</qt>")
01016                         .arg( programName, oldHostName, backendName );
01017             else
01018                 msg = i18n("<qt>%1 seems to be running on %3. <b>Running %1 and %2 at the "
01019                         "same time is not supported by the %4 backend and can cause "
01020                         "the loss of archived articles and crashes at startup.</b> "
01021                         "You should disable the archive for now "
01022                         "unless you are sure that %1 is "
01023                         "not running on %3.</qt>")
01024                         .arg( oldProgramName, programName, oldHostName, backendName );
01025         }
01026 
01027         KCursorSaver idle( KBusyPtr::idle() );
01028         if ( KMessageBox::No ==
01029              KMessageBox::warningYesNo( 0, msg, TQString(),
01030                                         i18n("Force Access"),
01031                                         i18n("Disable Archive")) )
01032                                         {
01033                                             return false;
01034                                         }
01035     }
01036 
01037     config.writeEntry("pid", getpid());
01038     config.writeEntry("hostname", hostName);
01039     config.writeEntry( "appName", appName );
01040     config.writeEntry( "programName", programName );
01041     config.sync();
01042     return true;
01043 }
01044 
01045 
01046 } // namespace Akregator
01047 #include "akregator_part.moc"