/* This file is part of Akregator. Copyright (C) 2004 Stanislav Karchebny 2005 Frank Osterfeld This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of TQt, and distribute the resulting executable, without including the source code for TQt in the source distribution. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "aboutdata.h" #include "actionmanagerimpl.h" #include "akregator_part.h" #include "akregator_view.h" #include "akregatorconfig.h" #include "articlefilter.h" #include "articleinterceptor.h" #include "configdialog.h" #include "fetchqueue.h" #include "frame.h" #include "article.h" #include "kernel.h" #include "kcursorsaver.h" #include "notificationmanager.h" #include "pageviewer.h" #include "plugin.h" #include "pluginmanager.h" #include "storage.h" #include "storagefactory.h" #include "storagefactorydummyimpl.h" #include "storagefactoryregistry.h" #include "speechclient.h" #include "trayicon.h" #include "tagset.h" #include "tag.h" namespace Akregator { typedef KParts::GenericFactory AkregatorFactory; K_EXPORT_COMPONENT_FACTORY( libakregatorpart, AkregatorFactory ) BrowserExtension::BrowserExtension(Part *p, const char *name) : KParts::BrowserExtension( p, name ) { m_part=p; } void BrowserExtension::saveSettings() { m_part->saveSettings(); } class Part::ApplyFiltersInterceptor : public ArticleInterceptor { public: virtual void processArticle(Article& article) { Filters::ArticleFilterList list = Kernel::self()->articleFilterList(); for (Filters::ArticleFilterList::ConstIterator it = list.begin(); it != list.end(); ++it) (*it).applyTo(article); } }; Part::Part( TQWidget *parentWidget, const char * /*widgetName*/, TQObject *parent, const char *name, const TQStringList& ) : DCOPObject("AkregatorIface") , MyBasePart(parent, name) , m_standardListLoaded(false) , m_shuttingDown(false) , m_mergedPart(0) , m_view(0) , m_backedUpList(false) , m_storage(0) { // we need an instance setInstance( AkregatorFactory::instance() ); // start knotifyclient if not already started. makes it work for people who doesn't use full kde, according to kmail devels KNotifyClient::startDaemon(); m_standardFeedList = KGlobal::dirs()->saveLocation("data", "akregator/data") + "/feeds.opml"; m_tagSetPath = KGlobal::dirs()->saveLocation("data", "akregator/data") + "/tagset.xml"; Backend::StorageFactoryDummyImpl* dummyFactory = new Backend::StorageFactoryDummyImpl(); Backend::StorageFactoryRegistry::self()->registerFactory(dummyFactory, dummyFactory->key()); loadPlugins(); // FIXME: also unload them! m_storage = 0; Backend::StorageFactory* factory = Backend::StorageFactoryRegistry::self()->getFactory(Settings::archiveBackend()); TQStringList storageParams; storageParams.append(TQString("taggingEnabled=%1").arg(Settings::showTaggingGUI() ? "true" : "false")); if (factory != 0) { if (factory->allowsMultipleWriteAccess()) { m_storage = factory->createStorage(storageParams); } else { if (tryToLock(factory->name())) m_storage = factory->createStorage(storageParams); else m_storage = dummyFactory->createStorage(storageParams); } } if (!m_storage) // Houston, we have a problem { m_storage = Backend::StorageFactoryRegistry::self()->getFactory("dummy")->createStorage(storageParams); KMessageBox::error(parentWidget, i18n("Unable to load storage backend plugin \"%1\". No feeds are archived.").arg(Settings::archiveBackend()), i18n("Plugin error") ); } Filters::ArticleFilterList list; list.readConfig(Settings::self()->config()); Kernel::self()->setArticleFilterList(list); m_applyFiltersInterceptor = new ApplyFiltersInterceptor(); ArticleInterceptorManager::self()->addInterceptor(m_applyFiltersInterceptor); m_storage->open(true); Kernel::self()->setStorage(m_storage); Backend::Storage::setInstance(m_storage); // TODO: kill this one loadTagSet(m_tagSetPath); m_actionManager = new ActionManagerImpl(this); ActionManager::setInstance(m_actionManager); m_view = new Akregator::View(this, parentWidget, m_actionManager, "akregator_view"); m_actionManager->initView(m_view); m_actionManager->setTagSet(Kernel::self()->tagSet()); m_extension = new BrowserExtension(this, "ak_extension"); connect(m_view, TQT_SIGNAL(setWindowCaption(const TQString&)), this, TQT_SIGNAL(setWindowCaption(const TQString&))); connect(m_view, TQT_SIGNAL(setStatusBarText(const TQString&)), this, TQT_SIGNAL(setStatusBarText(const TQString&))); connect(m_view, TQT_SIGNAL(setProgress(int)), m_extension, TQT_SIGNAL(loadingProgress(int))); connect(m_view, TQT_SIGNAL(signalCanceled(const TQString&)), this, TQT_SIGNAL(canceled(const TQString&))); connect(m_view, TQT_SIGNAL(signalStarted(KIO::Job*)), this, TQT_SIGNAL(started(KIO::Job*))); connect(m_view, TQT_SIGNAL(signalCompleted()), this, TQT_SIGNAL(completed())); // notify the part that this is our internal widget setWidget(m_view); TrayIcon* trayIcon = new TrayIcon( getMainWindow() ); TrayIcon::setInstance(trayIcon); m_actionManager->initTrayIcon(trayIcon); connect(trayIcon, TQT_SIGNAL(showPart()), this, TQT_SIGNAL(showPart())); if ( isTrayIconEnabled() ) { trayIcon->show(); NotificationManager::self()->setWidget(trayIcon, instance()); } else NotificationManager::self()->setWidget(getMainWindow(), instance()); connect( trayIcon, TQT_SIGNAL(quitSelected()), kapp, TQT_SLOT(quit())) ; connect( m_view, TQT_SIGNAL(signalUnreadCountChanged(int)), trayIcon, TQT_SLOT(slotSetUnread(int)) ); connect(kapp, TQT_SIGNAL(shutDown()), this, TQT_SLOT(slotOnShutdown())); m_autosaveTimer = new TQTimer(this); connect(m_autosaveTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotSaveFeedList())); m_autosaveTimer->start(5*60*1000); // 5 minutes setXMLFile("akregator_part.rc", true); initFonts(); RSS::FileRetriever::setUserAgent(TQString("Akregator/%1; librss/remnants").arg(AKREGATOR_VERSION)); } void Part::loadPlugins() { // "[X-KDE-akregator-plugintype] == 'storage'" KTrader::OfferList offers = PluginManager::query(); for( KTrader::OfferList::ConstIterator it = offers.begin(), end = offers.end(); it != end; ++it ) { Akregator::Plugin* plugin = PluginManager::createFromService(*it); if (plugin) plugin->init(); } } void Part::slotOnShutdown() { m_shuttingDown = true; const TQString lockLocation = locateLocal("data", "akregator/lock"); KSimpleConfig config(lockLocation); config.writeEntry("pid", -1); config.sync(); m_autosaveTimer->stop(); saveSettings(); slotSaveFeedList(); saveTagSet(m_tagSetPath); m_view->slotOnShutdown(); //delete m_view; delete TrayIcon::getInstance(); TrayIcon::setInstance(0L); delete m_storage; m_storage = 0; //delete m_actionManager; } void Part::slotSettingsChanged() { NotificationManager::self()->setWidget(isTrayIconEnabled() ? TrayIcon::getInstance() : getMainWindow(), instance()); RSS::FileRetriever::setUseCache(Settings::useHTMLCache()); TQStringList fonts; fonts.append(Settings::standardFont()); fonts.append(Settings::fixedFont()); fonts.append(Settings::sansSerifFont()); fonts.append(Settings::serifFont()); fonts.append(Settings::standardFont()); fonts.append(Settings::standardFont()); fonts.append("0"); Settings::setFonts(fonts); if (Settings::minimumFontSize() > Settings::mediumFontSize()) Settings::setMediumFontSize(Settings::minimumFontSize()); saveSettings(); m_view->slotSettingsChanged(); emit signalSettingsChanged(); } void Part::saveSettings() { Kernel::self()->articleFilterList().writeConfig(Settings::self()->config()); m_view->saveSettings(); } Part::~Part() { kdDebug() << "Part::~Part() enter" << endl; if (!m_shuttingDown) slotOnShutdown(); kdDebug() << "Part::~Part(): leaving" << endl; ArticleInterceptorManager::self()->removeInterceptor(m_applyFiltersInterceptor); delete m_applyFiltersInterceptor; } void Part::readProperties(KConfig* config) { m_backedUpList = false; openStandardFeedList(); if(m_view) m_view->readProperties(config); } void Part::saveProperties(KConfig* config) { if (m_view) { slotSaveFeedList(); m_view->saveProperties(config); } } bool Part::openURL(const KURL& url) { m_file = url.path(); return openFile(); } void Part::openStandardFeedList() { if ( !m_standardFeedList.isEmpty() && openURL(m_standardFeedList) ) m_standardListLoaded = true; } TQDomDocument Part::createDefaultFeedList() { TQDomDocument doc; TQDomProcessingInstruction z = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\""); doc.appendChild( z ); TQDomElement root = doc.createElement( "opml" ); root.setAttribute("version","1.0"); doc.appendChild( root ); TQDomElement head = doc.createElement( "head" ); root.appendChild(head); TQDomElement text = doc.createElement( "text" ); text.appendChild(doc.createTextNode(i18n("Feeds"))); head.appendChild(text); TQDomElement body = doc.createElement( "body" ); root.appendChild(body); TQDomElement mainFolder = doc.createElement( "outline" ); mainFolder.setAttribute("text","Free/Libre Software News"); body.appendChild(mainFolder); TQDomElement tde = doc.createElement( "outline" ); tde.setAttribute("text",i18n("Trinity Desktop News")); tde.setAttribute("xmlUrl","http://trinitydesktop.org/rss.php"); mainFolder.appendChild(tde); TQDomElement lxer = doc.createElement( "outline" ); lxer.setAttribute("text",i18n("LXer Linux News")); lxer.setAttribute("xmlUrl","http://lxer.com/module/newswire/headlines.rss"); mainFolder.appendChild(lxer); TQDomElement tux = doc.createElement( "outline" ); tux.setAttribute("text",i18n("Tuxmachines")); tux.setAttribute("xmlUrl","http://www.tuxmachines.org/node/feed"); mainFolder.appendChild(tux); TQDomElement lwn = doc.createElement( "outline" ); lwn.setAttribute("text",i18n("lwn.net")); lwn.setAttribute("xmlUrl","http://lwn.net/headlines/rss"); mainFolder.appendChild(lwn); TQDomElement hlin = doc.createElement( "outline" ); hlin.setAttribute("text",i18n("H-Online")); hlin.setAttribute("xmlUrl","http://www.h-online.com/grand-atom.xml"); mainFolder.appendChild(hlin); return doc; } bool Part::openFile() { emit setStatusBarText(i18n("Opening Feed List...") ); TQString str; // m_file is always local so we can use TQFile on it TQFile file(m_file); bool fileExists = file.exists(); TQString listBackup = m_storage->restoreFeedList(); TQDomDocument doc; if (!fileExists) { doc = createDefaultFeedList(); } else { if (file.open(IO_ReadOnly)) { // Read OPML feeds list and build TQDom tree. TQTextStream stream(&file); stream.setEncoding(TQTextStream::UnicodeUTF8); // FIXME not all opmls are in utf8 str = stream.read(); file.close(); } if (!doc.setContent(str)) { if (file.size() > 0) // don't backup empty files { TQString backup = m_file + "-backup." + TQString::number(TQDateTime::currentDateTime().toTime_t()); copyFile(backup); KMessageBox::error(m_view, i18n("The standard feed list is corrupted (invalid XML). A backup was created:

%2

").arg(backup), i18n("XML Parsing Error") ); } if (!doc.setContent(listBackup)) doc = createDefaultFeedList(); } } if (!m_view->loadFeeds(doc)) { if (file.size() > 0) // don't backup empty files { TQString backup = m_file + "-backup." + TQString::number(TQDateTime::currentDateTime().toTime_t()); copyFile(backup); KMessageBox::error(m_view, i18n("The standard feed list is corrupted (no valid OPML). A backup was created:

%2

").arg(backup), i18n("OPML Parsing Error") ); } m_view->loadFeeds(createDefaultFeedList()); } emit setStatusBarText(TQString()); if( Settings::markAllFeedsReadOnStartup() ) m_view->slotMarkAllFeedsRead(); if (Settings::fetchOnStartup()) m_view->slotFetchAllFeeds(); return true; } void Part::slotSaveFeedList() { // don't save to the standard feed list, when it wasn't completely loaded before if (!m_standardListLoaded) return; // the first time we overwrite the feed list, we create a backup if (!m_backedUpList) { TQString backup = m_file + "~"; if (copyFile(backup)) m_backedUpList = true; } TQString xmlStr = m_view->feedListToOPML().toString(); m_storage->storeFeedList(xmlStr); TQFile file(m_file); if (file.open(IO_WriteOnly) == false) { //FIXME: allow to save the feedlist into different location -tpr 20041118 KMessageBox::error(m_view, i18n("Access denied: cannot save feed list (%1)").arg(m_file), i18n("Write error") ); return; } // use TQTextStream to dump the text to the file TQTextStream stream(&file); stream.setEncoding(TQTextStream::UnicodeUTF8); // Write OPML data file. // Archive data files are saved elsewhere. stream << xmlStr << endl; file.close(); } bool Part::isTrayIconEnabled() const { return Settings::showTrayIcon(); } bool Part::mergePart(KParts::Part* part) { if (part != m_mergedPart) { if (!factory()) { if (m_mergedPart) removeChildClient(m_mergedPart); else insertChildClient(part); } else { if (m_mergedPart) { factory()->removeClient(m_mergedPart); if (childClients()->containsRef(m_mergedPart)) removeChildClient(m_mergedPart); } if (part) factory()->addClient(part); } m_mergedPart = part; } return true; } TQWidget* Part::getMainWindow() { // this is a dirty fix to get the main window used for the tray icon TQWidgetList *l = kapp->topLevelWidgets(); TQWidgetListIt it( *l ); TQWidget *wid; // check if there is an akregator main window while ( (wid = it.current()) != 0 ) { ++it; //kdDebug() << "win name: " << wid->name() << endl; if (TQString(wid->name()) == "akregator_mainwindow") { delete l; return wid; } } // if not, check for kontact main window TQWidgetListIt it2( *l ); while ( (wid = it2.current()) != 0 ) { ++it2; if (TQString(wid->name()).startsWith("kontact-mainwindow")) { delete l; return wid; } } delete l; return 0; } void Part::loadTagSet(const TQString& path) { TQDomDocument doc; TQFile file(path); if (file.open(IO_ReadOnly)) { doc.setContent(TQByteArray(file.readAll())); file.close(); } // if we can't load the tagset from the xml file, check for the backup in the backend if (doc.isNull()) { doc.setContent(m_storage->restoreTagSet()); } if (!doc.isNull()) { Kernel::self()->tagSet()->readFromXML(doc); } else { Kernel::self()->tagSet()->insert(Tag("http://akregator.sf.net/tags/Interesting", i18n("Interesting"))); } } void Part::saveTagSet(const TQString& path) { TQString xmlStr = Kernel::self()->tagSet()->toXML().toString(); m_storage->storeTagSet(xmlStr); TQFile file(path); if ( file.open(IO_WriteOnly) ) { TQTextStream stream(&file); stream.setEncoding(TQTextStream::UnicodeUTF8); stream << xmlStr << "\n"; file.close(); } } void Part::importFile(const KURL& url) { TQString filename; bool isRemote = false; if (url.isLocalFile()) filename = url.path(); else { isRemote = true; if (!KIO::NetAccess::download(url, filename, m_view) ) { KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() ); return; } } TQFile file(filename); if (file.open(IO_ReadOnly)) { // Read OPML feeds list and build TQDom tree. TQDomDocument doc; if (doc.setContent(TQByteArray(file.readAll()))) m_view->importFeeds(doc); else KMessageBox::error(m_view, i18n("Could not import the file %1 (no valid OPML)").arg(filename), i18n("OPML Parsing Error") ); } else 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")); if (isRemote) KIO::NetAccess::removeTempFile(filename); } void Part::exportFile(const KURL& url) { if (url.isLocalFile()) { TQFile file(url.path()); if ( file.exists() && KMessageBox::questionYesNo(m_view, i18n("The file %1 already exists; do you want to overwrite it?").arg(file.name()), i18n("Export"), i18n("Overwrite"), KStdGuiItem::cancel()) == KMessageBox::No ) return; if ( !file.open(IO_WriteOnly) ) { KMessageBox::error(m_view, i18n("Access denied: cannot write to file %1").arg(file.name()), i18n("Write Error") ); return; } TQTextStream stream(&file); stream.setEncoding(TQTextStream::UnicodeUTF8); stream << m_view->feedListToOPML().toString() << "\n"; file.close(); } else { KTempFile tmpfile; tmpfile.setAutoDelete(true); TQTextStream stream(tmpfile.file()); stream.setEncoding(TQTextStream::UnicodeUTF8); stream << m_view->feedListToOPML().toString() << "\n"; tmpfile.close(); if (!KIO::NetAccess::upload(tmpfile.name(), url, m_view)) KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() ); } } void Part::fileImport() { KURL url = KFileDialog::getOpenURL( TQString(), "*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)") +"\n*|" + i18n("All Files") ); if (!url.isEmpty()) importFile(url); } void Part::fileExport() { KURL url= KFileDialog::getSaveURL( TQString(), "*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)") +"\n*|" + i18n("All Files") ); if ( !url.isEmpty() ) exportFile(url); } void Part::fileGetFeeds() { /*GetFeeds *gf = new GetFeeds(); gf->show();*/ //KNS::DownloadDialog::open("akregator/feeds", i18n("Get New Feeds")); } void Part::fileSendArticle(bool attach) { // FIXME: you have to open article to tab to be able to send... TQString title, text; text = m_view->currentFrame()->part()->url().prettyURL(); if(text.isEmpty() || text.isNull()) return; title = m_view->currentFrame()->title(); if(attach) { kapp->invokeMailer("", "", "", title, text, "", text); } else { kapp->invokeMailer("", "", "", title, text); } } void Part::fetchAllFeeds() { m_view->slotFetchAllFeeds(); } void Part::fetchFeedUrl(const TQString&s) { kdDebug() << "fetchFeedURL==" << s << endl; } void Part::addFeedsToGroup(const TQStringList& urls, const TQString& group) { for (TQStringList::ConstIterator it = urls.begin(); it != urls.end(); ++it) { kdDebug() << "Akregator::Part::addFeedToGroup adding feed with URL " << *it << " to group " << group << endl; m_view->addFeedToGroup(*it, group); } NotificationManager::self()->slotNotifyFeeds(urls); } void Part::addFeed() { m_view->slotFeedAdd(); } KAboutData *Part::createAboutData() { return new Akregator::AboutData; } void Part::showKNotifyOptions() { KAboutData* about = new Akregator::AboutData; KNotifyDialog::configure(m_view, "akregator_knotify_config", about); delete about; } void Part::showOptions() { if ( KConfigDialog::showDialog( "settings" ) ) return; KConfigDialog* dialog = new ConfigDialog( m_view, "settings", Settings::self() ); connect( dialog, TQT_SIGNAL(settingsChanged()), this, TQT_SLOT(slotSettingsChanged()) ); connect( dialog, TQT_SIGNAL(settingsChanged()), TrayIcon::getInstance(), TQT_SLOT(settingsChanged()) ); dialog->show(); } void Part::partActivateEvent(KParts::PartActivateEvent* event) { if (factory() && m_mergedPart) { if (event->activated()) factory()->addClient(m_mergedPart); else factory()->removeClient(m_mergedPart); } MyBasePart::partActivateEvent(event); } KParts::Part* Part::hitTest(TQWidget *widget, const TQPoint &globalPos) { bool child = false; TQWidget *me = this->widget(); while (widget) { if (widget == me) { child = true; break; } if (!widget) { break; } widget = widget->parentWidget(); } if (m_view && m_view->currentFrame() && child) { return m_view->currentFrame()->part(); } else { return MyBasePart::hitTest(widget, globalPos); } } void Part::initFonts() { TQStringList fonts = Settings::fonts(); if (fonts.isEmpty()) { fonts.append(KGlobalSettings::generalFont().family()); fonts.append(KGlobalSettings::fixedFont().family()); fonts.append(KGlobalSettings::generalFont().family()); fonts.append(KGlobalSettings::generalFont().family()); fonts.append("0"); } Settings::setFonts(fonts); if (Settings::standardFont().isEmpty()) Settings::setStandardFont(fonts[0]); if (Settings::fixedFont().isEmpty()) Settings::setFixedFont(fonts[1]); if (Settings::sansSerifFont().isEmpty()) Settings::setSansSerifFont(fonts[2]); if (Settings::serifFont().isEmpty()) Settings::setSerifFont(fonts[3]); KConfig* conf = Settings::self()->config(); conf->setGroup("HTML Settings"); KConfig konq("konquerorrc", true, false); konq.setGroup("HTML Settings"); if (!conf->hasKey("MinimumFontSize")) { int minfs; if (konq.hasKey("MinimumFontSize")) minfs = konq.readNumEntry("MinimumFontSize"); else minfs = KGlobalSettings::generalFont().pointSize(); kdDebug() << "Part::initFonts(): set MinimumFontSize to " << minfs << endl; Settings::setMinimumFontSize(minfs); } if (!conf->hasKey("MediumFontSize")) { int medfs; if (konq.hasKey("MediumFontSize")) medfs = konq.readNumEntry("MediumFontSize"); else medfs = KGlobalSettings::generalFont().pointSize(); kdDebug() << "Part::initFonts(): set MediumFontSize to " << medfs << endl; Settings::setMediumFontSize(medfs); } if (!conf->hasKey("UnderlineLinks")) { bool underline = true; if (konq.hasKey("UnderlineLinks")) underline = konq.readBoolEntry("UnderlineLinks"); kdDebug() << "Part::initFonts(): set UnderlineLinks to " << underline << endl; Settings::setUnderlineLinks(underline); } } bool Part::copyFile(const TQString& backup) { TQFile file(m_file); if (file.open(IO_ReadOnly)) { TQFile backupFile(backup); if (backupFile.open(IO_WriteOnly)) { TQTextStream in(&file); TQTextStream out(&backupFile); while (!in.atEnd()) out << in.readLine(); backupFile.close(); file.close(); return true; } else { file.close(); return false; } } return false; } static TQString getMyHostName() { char hostNameC[256]; // null terminate this C string hostNameC[255] = 0; // set the string to 0 length if gethostname fails if(gethostname(hostNameC, 255)) hostNameC[0] = 0; return TQString::fromLocal8Bit(hostNameC); } // taken from KMail bool Part::tryToLock(const TQString& backendName) { // Check and create a lock file to prevent concurrent access to metakit archive TQString appName = kapp->instanceName(); if ( appName.isEmpty() ) appName = "akregator"; TQString programName; const KAboutData *about = kapp->aboutData(); if ( about ) programName = about->programName(); if ( programName.isEmpty() ) programName = i18n("Akregator"); TQString lockLocation = locateLocal("data", "akregator/lock"); KSimpleConfig config(lockLocation); int oldPid = config.readNumEntry("pid", -1); const TQString oldHostName = config.readEntry("hostname"); const TQString oldAppName = config.readEntry( "appName", appName ); const TQString oldProgramName = config.readEntry( "programName", programName ); const TQString hostName = getMyHostName(); bool first_instance = false; if ( oldPid == -1 ) first_instance = true; // check if the lock file is stale by trying to see if // the other pid is currently running. // Not 100% correct but better safe than sorry else if (hostName == oldHostName && oldPid != getpid()) { if ( kill(oldPid, 0) == -1 ) first_instance = ( errno == ESRCH ); } if ( !first_instance ) { TQString msg; if ( oldHostName == hostName ) { // this can only happen if the user is running this application on // different displays on the same machine. All other cases will be // taken care of by KUniqueApplication() if ( oldAppName == appName ) msg = i18n("%1 already seems to be running on another display on " "this machine. Running %2 more than once is not supported " "by the %3 backend and " "can cause the loss of archived articles and crashes at startup. " "You should disable the archive for now " "unless you are sure that %2 is not already running.") .arg( programName, programName, backendName ); // TQString::arg( st ) only replaces the first occurrence of %1 // with st while TQString::arg( s1, s2 ) replacess all occurrences // of %1 with s1 and all occurrences of %2 with s2. So don't // even think about changing the above to .arg( programName ). else msg = i18n("%1 seems to be running on another display on this " "machine. Running %1 and %2 at the same " "time is not supported by the %3 backend and can cause " "the loss of archived articles and crashes at startup. " "You should disable the archive for now " "unless you are sure that %2 is not already running.") .arg( oldProgramName, programName, backendName ); } else { if ( oldAppName == appName ) msg = i18n("%1 already seems to be running on %2. Running %1 more " "than once is not supported by the %3 backend and can cause " "the loss of archived articles and crashes at startup. " "You should disable the archive for now " "unless you are sure that it is " "not already running on %2.") .arg( programName, oldHostName, backendName ); else msg = i18n("%1 seems to be running on %3. Running %1 and %2 at the " "same time is not supported by the %4 backend and can cause " "the loss of archived articles and crashes at startup. " "You should disable the archive for now " "unless you are sure that %1 is " "not running on %3.") .arg( oldProgramName, programName, oldHostName, backendName ); } KCursorSaver idle( KBusyPtr::idle() ); if ( KMessageBox::No == KMessageBox::warningYesNo( 0, msg, TQString(), i18n("Force Access"), i18n("Disable Archive")) ) { return false; } } config.writeEntry("pid", getpid()); config.writeEntry("hostname", hostName); config.writeEntry( "appName", appName ); config.writeEntry( "programName", programName ); config.sync(); return true; } } // namespace Akregator #include "akregator_part.moc"