25 #include <dcopclient.h>
26 #include <kaboutdata.h>
28 #include <kactionclasses.h>
29 #include <kactioncollection.h>
30 #include <kapplication.h>
32 #include <kconfigdialog.h>
33 #include <kfiledialog.h>
34 #include <kglobalsettings.h>
35 #include <khtmldefaults.h>
36 #include <kinstance.h>
37 #include <kmainwindow.h>
38 #include <kmessagebox.h>
39 #include <knotifyclient.h>
40 #include <knotifydialog.h>
41 #include <kpopupmenu.h>
43 #include <kstandarddirs.h>
44 #include <kstdaction.h>
45 #include <ktempfile.h>
47 #include <kio/netaccess.h>
48 #include <kparts/browserinterface.h>
49 #include <kparts/genericfactory.h>
50 #include <kparts/partmanager.h>
53 #include <tqobjectlist.h>
54 #include <tqstringlist.h>
56 #include <tqwidgetlist.h>
57 #include <tqucomextra_p.h>
60 #include <sys/types.h>
66 #include "aboutdata.h"
67 #include "actionmanagerimpl.h"
68 #include "akregator_part.h"
69 #include "akregator_view.h"
70 #include "akregatorconfig.h"
71 #include "articlefilter.h"
72 #include "articleinterceptor.h"
73 #include "configdialog.h"
74 #include "fetchqueue.h"
78 #include "kcursorsaver.h"
79 #include "notificationmanager.h"
80 #include "pageviewer.h"
82 #include "pluginmanager.h"
84 #include "storagefactory.h"
85 #include "storagefactorydummyimpl.h"
86 #include "storagefactoryregistry.h"
87 #include "speechclient.h"
94 typedef KParts::GenericFactory<Part> AkregatorFactory;
95 K_EXPORT_COMPONENT_FACTORY( libakregatorpart, AkregatorFactory )
97 BrowserExtension::BrowserExtension(Part *p, const
char *name)
98 : KParts::BrowserExtension( p, name )
103 void BrowserExtension::saveSettings()
105 m_part->saveSettings();
108 class Part::ApplyFiltersInterceptor :
public ArticleInterceptor
111 virtual void processArticle(Article& article)
113 Filters::ArticleFilterList list = Kernel::self()->articleFilterList();
114 for (Filters::ArticleFilterList::ConstIterator it = list.begin(); it != list.end(); ++it)
115 (*it).applyTo(article);
120 TQObject *parent,
const char *name,
const TQStringList& )
121 : DCOPObject(
"AkregatorIface")
122 , MyBasePart(parent, name)
123 , m_standardListLoaded(false)
124 , m_shuttingDown(false)
127 , m_backedUpList(false)
131 setInstance( AkregatorFactory::instance() );
134 KNotifyClient::startDaemon();
136 m_standardFeedList = KGlobal::dirs()->saveLocation(
"data",
"akregator/data") +
"/feeds.opml";
138 m_tagSetPath = KGlobal::dirs()->saveLocation(
"data",
"akregator/data") +
"/tagset.xml";
140 Backend::StorageFactoryDummyImpl* dummyFactory =
new Backend::StorageFactoryDummyImpl();
141 Backend::StorageFactoryRegistry::self()->registerFactory(dummyFactory, dummyFactory->key());
145 Backend::StorageFactory* factory = Backend::StorageFactoryRegistry::self()->getFactory(Settings::archiveBackend());
147 TQStringList storageParams;
149 storageParams.append(TQString(
"taggingEnabled=%1").arg(Settings::showTaggingGUI() ?
"true" :
"false"));
153 if (factory->allowsMultipleWriteAccess())
155 m_storage = factory->createStorage(storageParams);
159 if (tryToLock(factory->name()))
160 m_storage = factory->createStorage(storageParams);
162 m_storage = dummyFactory->createStorage(storageParams);
169 m_storage = Backend::StorageFactoryRegistry::self()->getFactory(
"dummy")->createStorage(storageParams);
171 KMessageBox::error(parentWidget, i18n(
"Unable to load storage backend plugin \"%1\". No feeds are archived.").arg(Settings::archiveBackend()), i18n(
"Plugin error") );
174 Filters::ArticleFilterList list;
175 list.readConfig(Settings::self()->config());
176 Kernel::self()->setArticleFilterList(list);
178 m_applyFiltersInterceptor =
new ApplyFiltersInterceptor();
179 ArticleInterceptorManager::self()->addInterceptor(m_applyFiltersInterceptor);
181 m_storage->
open(
true);
182 Kernel::self()->setStorage(m_storage);
183 Backend::Storage::setInstance(m_storage);
185 loadTagSet(m_tagSetPath);
188 ActionManager::setInstance(m_actionManager);
190 m_view =
new Akregator::View(
this, parentWidget, m_actionManager,
"akregator_view");
191 m_actionManager->initView(m_view);
192 m_actionManager->setTagSet(Kernel::self()->tagSet());
194 m_extension =
new BrowserExtension(
this,
"ak_extension");
196 connect(m_view, TQT_SIGNAL(setWindowCaption(
const TQString&)),
this, TQT_SIGNAL(setWindowCaption(
const TQString&)));
197 connect(m_view, TQT_SIGNAL(setStatusBarText(
const TQString&)),
this, TQT_SIGNAL(setStatusBarText(
const TQString&)));
198 connect(m_view, TQT_SIGNAL(setProgress(
int)), m_extension, TQT_SIGNAL(loadingProgress(
int)));
199 connect(m_view, TQT_SIGNAL(signalCanceled(
const TQString&)),
this, TQT_SIGNAL(canceled(
const TQString&)));
200 connect(m_view, TQT_SIGNAL(signalStarted(KIO::Job*)),
this, TQT_SIGNAL(started(KIO::Job*)));
201 connect(m_view, TQT_SIGNAL(signalCompleted()),
this, TQT_SIGNAL(completed()));
207 TrayIcon::setInstance(trayIcon);
208 m_actionManager->initTrayIcon(trayIcon);
210 connect(trayIcon, TQT_SIGNAL(showPart()),
this, TQT_SIGNAL(showPart()));
220 connect( trayIcon, TQT_SIGNAL(quitSelected()),
221 kapp, TQT_SLOT(quit())) ;
223 connect( m_view, TQT_SIGNAL(signalUnreadCountChanged(
int)), trayIcon, TQT_SLOT(slotSetUnread(
int)) );
225 connect(kapp, TQT_SIGNAL(shutDown()),
this, TQT_SLOT(slotOnShutdown()));
227 m_autosaveTimer =
new TQTimer(
this);
228 connect(m_autosaveTimer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(
slotSaveFeedList()));
229 m_autosaveTimer->start(5*60*1000);
231 setXMLFile(
"akregator_part.rc",
true);
235 RSS::FileRetriever::setUserAgent(TQString(
"Akregator/%1; librss/remnants").arg(AKREGATOR_VERSION));
241 KTrader::OfferList offers = PluginManager::query();
243 for( KTrader::OfferList::ConstIterator it = offers.begin(), end = offers.end(); it != end; ++it )
245 Akregator::Plugin* plugin = PluginManager::createFromService(*it);
251 void Part::slotOnShutdown()
253 m_shuttingDown =
true;
255 const TQString lockLocation = locateLocal(
"data",
"akregator/lock");
256 KSimpleConfig config(lockLocation);
257 config.writeEntry(
"pid", -1);
260 m_autosaveTimer->stop();
263 saveTagSet(m_tagSetPath);
264 m_view->slotOnShutdown();
266 delete TrayIcon::getInstance();
267 TrayIcon::setInstance(0L);
273 void Part::slotSettingsChanged()
277 RSS::FileRetriever::setUseCache(Settings::useHTMLCache());
280 fonts.append(Settings::standardFont());
281 fonts.append(Settings::fixedFont());
282 fonts.append(Settings::sansSerifFont());
283 fonts.append(Settings::serifFont());
284 fonts.append(Settings::standardFont());
285 fonts.append(Settings::standardFont());
287 Settings::setFonts(fonts);
289 if (Settings::minimumFontSize() > Settings::mediumFontSize())
290 Settings::setMediumFontSize(Settings::minimumFontSize());
292 m_view->slotSettingsChanged();
293 emit signalSettingsChanged();
297 Kernel::self()->articleFilterList().writeConfig(Settings::self()->config());
303 kdDebug() <<
"Part::~Part() enter" << endl;
306 kdDebug() <<
"Part::~Part(): leaving" << endl;
307 ArticleInterceptorManager::self()->removeInterceptor(m_applyFiltersInterceptor);
308 delete m_applyFiltersInterceptor;
313 m_backedUpList =
false;
325 m_view->saveProperties(config);
337 if ( !m_standardFeedList.isEmpty() &&
openURL(m_standardFeedList) )
338 m_standardListLoaded =
true;
341 TQDomDocument Part::createDefaultFeedList()
344 TQDomProcessingInstruction z = doc.createProcessingInstruction(
"xml",
"version=\"1.0\" encoding=\"UTF-8\"");
345 doc.appendChild( z );
347 TQDomElement root = doc.createElement(
"opml" );
348 root.setAttribute(
"version",
"1.0");
349 doc.appendChild( root );
351 TQDomElement head = doc.createElement(
"head" );
352 root.appendChild(head);
354 TQDomElement text = doc.createElement(
"text" );
355 text.appendChild(doc.createTextNode(i18n(
"Feeds")));
356 head.appendChild(text);
358 TQDomElement body = doc.createElement(
"body" );
359 root.appendChild(body);
361 TQDomElement mainFolder = doc.createElement(
"outline" );
362 mainFolder.setAttribute(
"text",
"KDE");
363 body.appendChild(mainFolder);
365 TQDomElement ak = doc.createElement(
"outline" );
366 ak.setAttribute(
"text",i18n(
"Akregator News"));
367 ak.setAttribute(
"xmlUrl",
"http://akregator.sf.net/rss2.php");
368 mainFolder.appendChild(ak);
370 TQDomElement akb = doc.createElement(
"outline" );
371 akb.setAttribute(
"text",i18n(
"Akregator Blog"));
372 akb.setAttribute(
"xmlUrl",
"http://akregator.pwsp.net/blog/?feed=rss2");
373 mainFolder.appendChild(akb);
375 TQDomElement dot = doc.createElement(
"outline" );
376 dot.setAttribute(
"text",i18n(
"KDE Dot News"));
377 dot.setAttribute(
"xmlUrl",
"http://www.kde.org/dotkdeorg.rdf");
378 mainFolder.appendChild(dot);
380 TQDomElement plan = doc.createElement(
"outline" );
381 plan.setAttribute(
"text",i18n(
"Planet KDE"));
382 plan.setAttribute(
"xmlUrl",
"http://planetkde.org/rss20.xml");
383 mainFolder.appendChild(plan);
385 TQDomElement apps = doc.createElement(
"outline" );
386 apps.setAttribute(
"text",i18n(
"KDE Apps"));
387 apps.setAttribute(
"xmlUrl",
"http://www.kde.org/dot/kde-apps-content.rdf");
388 mainFolder.appendChild(apps);
390 TQDomElement look = doc.createElement(
"outline" );
391 look.setAttribute(
"text",i18n(
"KDE Look"));
392 look.setAttribute(
"xmlUrl",
"http://www.kde.org/kde-look-content.rdf");
393 mainFolder.appendChild(look);
400 emit setStatusBarText(i18n(
"Opening Feed List...") );
406 bool fileExists = file.exists();
407 TQString listBackup = m_storage->restoreFeedList();
413 doc = createDefaultFeedList();
417 if (file.open(IO_ReadOnly))
420 TQTextStream stream(&file);
421 stream.setEncoding(TQTextStream::UnicodeUTF8);
426 if (!doc.setContent(str))
431 TQString backup = m_file +
"-backup." + TQString::number(TQDateTime::currentDateTime().toTime_t());
435 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") );
438 if (!doc.setContent(listBackup))
439 doc = createDefaultFeedList();
447 TQString backup = m_file +
"-backup." + TQString::number(TQDateTime::currentDateTime().toTime_t());
450 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") );
452 m_view->
loadFeeds(createDefaultFeedList());
455 emit setStatusBarText(TQString());
458 if( Settings::markAllFeedsReadOnStartup() )
461 if (Settings::fetchOnStartup())
470 if (!m_standardListLoaded)
476 TQString backup = m_file +
"~";
478 if (copyFile(backup))
479 m_backedUpList =
true;
486 if (file.open(IO_WriteOnly) ==
false)
489 KMessageBox::error(m_view, i18n(
"Access denied: cannot save feed list (%1)").arg(m_file), i18n(
"Write error") );
494 TQTextStream stream(&file);
495 stream.setEncoding(TQTextStream::UnicodeUTF8);
500 stream << xmlStr << endl;
507 return Settings::showTrayIcon();
512 if (part != m_mergedPart)
517 removeChildClient(m_mergedPart);
519 insertChildClient(part);
524 factory()->removeClient(m_mergedPart);
525 if (childClients()->containsRef(m_mergedPart))
526 removeChildClient(m_mergedPart);
529 factory()->addClient(part);
541 TQWidgetList *l = kapp->topLevelWidgets();
542 TQWidgetListIt it( *l );
546 while ( (wid = it.current()) != 0 )
550 if (TQString(wid->name()) ==
"akregator_mainwindow")
557 TQWidgetListIt it2( *l );
558 while ( (wid = it2.current()) != 0 )
561 if (TQString(wid->name()).startsWith(
"kontact-mainwindow"))
571 void Part::loadTagSet(
const TQString& path)
576 if (file.open(IO_ReadOnly))
578 doc.setContent(TQByteArray(file.readAll()));
584 doc.setContent(m_storage->restoreTagSet());
589 Kernel::self()->tagSet()->readFromXML(doc);
593 Kernel::self()->tagSet()->insert(Tag(
"http://akregator.sf.net/tags/Interesting", i18n(
"Interesting")));
597 void Part::saveTagSet(
const TQString& path)
599 TQString xmlStr = Kernel::self()->tagSet()->toXML().toString();
601 m_storage->storeTagSet(xmlStr);
605 if ( file.open(IO_WriteOnly) )
608 TQTextStream stream(&file);
609 stream.setEncoding(TQTextStream::UnicodeUTF8);
610 stream << xmlStr <<
"\n";
615 void Part::importFile(
const KURL& url)
619 bool isRemote =
false;
621 if (url.isLocalFile())
622 filename = url.path();
627 if (!KIO::NetAccess::download(url, filename, m_view) )
629 KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() );
634 TQFile file(filename);
635 if (file.open(IO_ReadOnly))
639 if (doc.setContent(TQByteArray(file.readAll())))
642 KMessageBox::error(m_view, i18n(
"Could not import the file %1 (no valid OPML)").arg(filename), i18n(
"OPML Parsing Error") );
645 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"));
648 KIO::NetAccess::removeTempFile(filename);
651 void Part::exportFile(
const KURL& url)
653 if (url.isLocalFile())
655 TQFile file(url.path());
657 if ( file.exists() &&
658 KMessageBox::questionYesNo(m_view,
659 i18n(
"The file %1 already exists; do you want to overwrite it?").arg(file.name()),
662 KStdGuiItem::cancel()) == KMessageBox::No )
665 if ( !file.open(IO_WriteOnly) )
667 KMessageBox::error(m_view, i18n(
"Access denied: cannot write to file %1").arg(file.name()), i18n(
"Write Error") );
671 TQTextStream stream(&file);
672 stream.setEncoding(TQTextStream::UnicodeUTF8);
680 tmpfile.setAutoDelete(
true);
682 TQTextStream stream(tmpfile.file());
683 stream.setEncoding(TQTextStream::UnicodeUTF8);
688 if (!KIO::NetAccess::upload(tmpfile.name(), url, m_view))
689 KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() );
693 void Part::fileImport()
695 KURL url = KFileDialog::getOpenURL( TQString(),
696 "*.opml *.xml|" + i18n(
"OPML Outlines (*.opml, *.xml)")
697 +
"\n*|" + i18n(
"All Files") );
703 void Part::fileExport()
705 KURL url= KFileDialog::getSaveURL( TQString(),
706 "*.opml *.xml|" + i18n(
"OPML Outlines (*.opml, *.xml)")
707 +
"\n*|" + i18n(
"All Files") );
709 if ( !url.isEmpty() )
713 void Part::fileGetFeeds()
720 void Part::fileSendArticle(
bool attach)
723 TQString title, text;
725 text = m_view->currentFrame()->part()->url().prettyURL();
726 if(text.isEmpty() || text.isNull())
729 title = m_view->currentFrame()->title();
732 kapp->invokeMailer(
"",
741 kapp->invokeMailer(
"",
754 void Part::fetchFeedUrl(
const TQString&s)
756 kdDebug() <<
"fetchFeedURL==" << s << endl;
761 for (TQStringList::ConstIterator it = urls.begin(); it != urls.end(); ++it)
763 kdDebug() <<
"Akregator::Part::addFeedToGroup adding feed with URL " << *it <<
" to group " << group << endl;
779 void Part::showKNotifyOptions()
782 KNotifyDialog::configure(m_view,
"akregator_knotify_config", about);
788 if ( KConfigDialog::showDialog(
"settings" ) )
791 KConfigDialog* dialog =
new ConfigDialog( m_view,
"settings", Settings::self() );
793 connect( dialog, TQT_SIGNAL(settingsChanged()),
794 this, TQT_SLOT(slotSettingsChanged()) );
795 connect( dialog, TQT_SIGNAL(settingsChanged()),
796 TrayIcon::getInstance(), TQT_SLOT(settingsChanged()) );
803 if (factory() && m_mergedPart)
805 if (event->activated())
806 factory()->addClient(m_mergedPart);
808 factory()->removeClient(m_mergedPart);
814 KParts::Part* Part::hitTest(TQWidget *widget,
const TQPoint &globalPos)
817 TQWidget *me = this->widget();
826 widget = widget->parentWidget();
828 if (m_view && m_view->currentFrame() && child) {
829 return m_view->currentFrame()->part();
831 return MyBasePart::hitTest(widget, globalPos);
835 void Part::initFonts()
837 TQStringList fonts = Settings::fonts();
840 fonts.append(KGlobalSettings::generalFont().family());
841 fonts.append(KGlobalSettings::fixedFont().family());
842 fonts.append(KGlobalSettings::generalFont().family());
843 fonts.append(KGlobalSettings::generalFont().family());
846 Settings::setFonts(fonts);
847 if (Settings::standardFont().isEmpty())
848 Settings::setStandardFont(fonts[0]);
849 if (Settings::fixedFont().isEmpty())
850 Settings::setFixedFont(fonts[1]);
851 if (Settings::sansSerifFont().isEmpty())
852 Settings::setSansSerifFont(fonts[2]);
853 if (Settings::serifFont().isEmpty())
854 Settings::setSerifFont(fonts[3]);
856 KConfig* conf = Settings::self()->config();
857 conf->setGroup(
"HTML Settings");
859 KConfig konq(
"konquerorrc",
true,
false);
860 konq.setGroup(
"HTML Settings");
862 if (!conf->hasKey(
"MinimumFontSize"))
865 if (konq.hasKey(
"MinimumFontSize"))
866 minfs = konq.readNumEntry(
"MinimumFontSize");
868 minfs = KGlobalSettings::generalFont().pointSize();
869 kdDebug() <<
"Part::initFonts(): set MinimumFontSize to " << minfs << endl;
870 Settings::setMinimumFontSize(minfs);
873 if (!conf->hasKey(
"MediumFontSize"))
876 if (konq.hasKey(
"MediumFontSize"))
877 medfs = konq.readNumEntry(
"MediumFontSize");
879 medfs = KGlobalSettings::generalFont().pointSize();
880 kdDebug() <<
"Part::initFonts(): set MediumFontSize to " << medfs << endl;
881 Settings::setMediumFontSize(medfs);
884 if (!conf->hasKey(
"UnderlineLinks"))
886 bool underline =
true;
887 if (konq.hasKey(
"UnderlineLinks"))
888 underline = konq.readBoolEntry(
"UnderlineLinks");
890 kdDebug() <<
"Part::initFonts(): set UnderlineLinks to " << underline << endl;
891 Settings::setUnderlineLinks(underline);
896 bool Part::copyFile(
const TQString& backup)
900 if (file.open(IO_ReadOnly))
902 TQFile backupFile(backup);
903 if (backupFile.open(IO_WriteOnly))
905 TQTextStream in(&file);
906 TQTextStream out(&backupFile);
908 out << in.readLine();
922 static TQString getMyHostName()
928 if(gethostname(hostNameC, 255))
930 return TQString::fromLocal8Bit(hostNameC);
934 bool Part::tryToLock(
const TQString& backendName)
937 TQString appName = kapp->instanceName();
938 if ( appName.isEmpty() )
939 appName =
"akregator";
941 TQString programName;
942 const KAboutData *about = kapp->aboutData();
944 programName = about->programName();
945 if ( programName.isEmpty() )
946 programName = i18n(
"Akregator");
948 TQString lockLocation = locateLocal(
"data",
"akregator/lock");
949 KSimpleConfig config(lockLocation);
950 int oldPid = config.readNumEntry(
"pid", -1);
951 const TQString oldHostName = config.readEntry(
"hostname");
952 const TQString oldAppName = config.readEntry(
"appName", appName );
953 const TQString oldProgramName = config.readEntry(
"programName", programName );
954 const TQString hostName = getMyHostName();
955 bool first_instance =
false;
957 first_instance =
true;
961 else if (hostName == oldHostName && oldPid != getpid()) {
962 if ( kill(oldPid, 0) == -1 )
963 first_instance = ( errno == ESRCH );
966 if ( !first_instance )
969 if ( oldHostName == hostName )
974 if ( oldAppName == appName )
975 msg = i18n(
"<qt>%1 already seems to be running on another display on "
976 "this machine. <b>Running %2 more than once is not supported "
977 "by the %3 backend and "
978 "can cause the loss of archived articles and crashes at startup.</b> "
979 "You should disable the archive for now "
980 "unless you are sure that %2 is not already running.</qt>")
981 .arg( programName, programName, backendName );
987 msg = i18n(
"<qt>%1 seems to be running on another display on this "
988 "machine. <b>Running %1 and %2 at the same "
989 "time is not supported by the %3 backend and can cause "
990 "the loss of archived articles and crashes at startup.</b> "
991 "You should disable the archive for now "
992 "unless you are sure that %2 is not already running.</qt>")
993 .arg( oldProgramName, programName, backendName );
997 if ( oldAppName == appName )
998 msg = i18n(
"<qt>%1 already seems to be running on %2. <b>Running %1 more "
999 "than once is not supported by the %3 backend and can cause "
1000 "the loss of archived articles and crashes at startup.</b> "
1001 "You should disable the archive for now "
1002 "unless you are sure that it is "
1003 "not already running on %2.</qt>")
1004 .arg( programName, oldHostName, backendName );
1006 msg = i18n(
"<qt>%1 seems to be running on %3. <b>Running %1 and %2 at the "
1007 "same time is not supported by the %4 backend and can cause "
1008 "the loss of archived articles and crashes at startup.</b> "
1009 "You should disable the archive for now "
1010 "unless you are sure that %1 is "
1011 "not running on %3.</qt>")
1012 .arg( oldProgramName, programName, oldHostName, backendName );
1015 KCursorSaver idle( KBusyPtr::idle() );
1016 if ( KMessageBox::No ==
1017 KMessageBox::warningYesNo( 0, msg, TQString(),
1018 i18n(
"Force Access"),
1019 i18n(
"Disable Archive")) )
1025 config.writeEntry(
"pid", getpid());
1026 config.writeEntry(
"hostname", hostName);
1027 config.writeEntry(
"appName", appName );
1028 config.writeEntry(
"programName", programName );
1035 #include "akregator_part.moc"