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"