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"