akregator/src

akregator_part.cpp
1/*
2 This file is part of Akregator.
3
4 Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net>
5 2005 Frank Osterfeld <frank.osterfeld at kdemail.net>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
21 As a special exception, permission is given to link this program
22 with any edition of TQt, and distribute the resulting executable,
23 without including the source code for TQt in the source distribution.
24*/
25#include <dcopclient.h>
26#include <tdeaboutdata.h>
27#include <tdeaction.h>
28#include <tdeactionclasses.h>
29#include <tdeactioncollection.h>
30#include <tdeapplication.h>
31#include <tdeconfig.h>
32#include <tdeconfigdialog.h>
33#include <tdefiledialog.h>
34#include <tdeglobalsettings.h>
35#include <tdehtmldefaults.h>
36#include <kinstance.h>
37#include <tdemainwindow.h>
38#include <tdemessagebox.h>
39#include <knotifyclient.h>
40#include <knotifydialog.h>
41#include <tdepopupmenu.h>
42#include <kservice.h>
43#include <tdestandarddirs.h>
44#include <kstdaction.h>
45#include <tdetempfile.h>
46#include <ktrader.h>
47#include <tdeio/netaccess.h>
48#include <tdeparts/browserinterface.h>
49#include <tdeparts/genericfactory.h>
50#include <tdeparts/partmanager.h>
51
52#include <tqfile.h>
53#include <tqobjectlist.h>
54#include <tqstringlist.h>
55#include <tqtimer.h>
56#include <tqwidgetlist.h>
57#include <private/tqucomextra_p.h>
58
59#include <cerrno>
60#include <sys/types.h>
61#include <signal.h>
62#include <stdio.h>
63#include <stdlib.h>
64#include <unistd.h>
65
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"
75#include "frame.h"
76#include "article.h"
77#include "kernel.h"
78#include "kcursorsaver.h"
79#include "notificationmanager.h"
80#include "pageviewer.h"
81#include "plugin.h"
82#include "pluginmanager.h"
83#include "storage.h"
84#include "storagefactory.h"
85#include "storagefactorydummyimpl.h"
86#include "storagefactoryregistry.h"
87#include "speechclient.h"
88#include "trayicon.h"
89#include "tagset.h"
90#include "tag.h"
91
92namespace Akregator {
93
94typedef KParts::GenericFactory<Part> AkregatorFactory;
95K_EXPORT_COMPONENT_FACTORY( libakregatorpart, AkregatorFactory )
96
97BrowserExtension::BrowserExtension(Part *p, const char *name)
98 : KParts::BrowserExtension( p, name )
99{
100 m_part=p;
101}
102
103void BrowserExtension::saveSettings()
104{
105 m_part->saveSettings();
106}
107
108class Part::ApplyFiltersInterceptor : public ArticleInterceptor
109{
110 public:
111 virtual ~ApplyFiltersInterceptor() {}
112
113 virtual void processArticle(Article& article)
114 {
115 Filters::ArticleFilterList list = Kernel::self()->articleFilterList();
116 for (Filters::ArticleFilterList::ConstIterator it = list.begin(); it != list.end(); ++it)
117 (*it).applyTo(article);
118 }
119};
120
121Part::Part( TQWidget *parentWidget, const char * /*widgetName*/,
122 TQObject *parent, const char *name, const TQStringList& )
123 : DCOPObject("AkregatorIface")
124 , MyBasePart(parent, name)
125 , m_standardListLoaded(false)
126 , m_shuttingDown(false)
127 , m_mergedPart(0)
128 , m_view(0)
129 , m_backedUpList(false)
130 , m_storage(0)
131{
132 // we need an instance
133 setInstance( AkregatorFactory::instance() );
134
135 // start knotifyclient if not already started. makes it work for people who doesn't use full kde, according to kmail devels
136 KNotifyClient::startDaemon();
137
138 m_standardFeedList = TDEGlobal::dirs()->saveLocation("data", "akregator/data") + "/feeds.opml";
139
140 m_tagSetPath = TDEGlobal::dirs()->saveLocation("data", "akregator/data") + "/tagset.xml";
141
142 Backend::StorageFactoryDummyImpl* dummyFactory = new Backend::StorageFactoryDummyImpl();
143 Backend::StorageFactoryRegistry::self()->registerFactory(dummyFactory, dummyFactory->key());
144 loadPlugins(); // FIXME: also unload them!
145
146 m_storage = 0;
147 Backend::StorageFactory* factory = Backend::StorageFactoryRegistry::self()->getFactory(Settings::archiveBackend());
148
149 TQStringList storageParams;
150
151 storageParams.append(TQString("taggingEnabled=%1").arg(Settings::showTaggingGUI() ? "true" : "false"));
152
153 if (factory != 0)
154 {
155 if (factory->allowsMultipleWriteAccess())
156 {
157 m_storage = factory->createStorage(storageParams);
158 }
159 else
160 {
161 if (tryToLock(factory->name()))
162 m_storage = factory->createStorage(storageParams);
163 else
164 m_storage = dummyFactory->createStorage(storageParams);
165 }
166 }
167
168
169 if (!m_storage) // Houston, we have a problem
170 {
171 m_storage = Backend::StorageFactoryRegistry::self()->getFactory("dummy")->createStorage(storageParams);
172
173 KMessageBox::error(parentWidget, i18n("Unable to load storage backend plugin \"%1\". No feeds are archived.").arg(Settings::archiveBackend()), i18n("Plugin error") );
174 }
175
176 Filters::ArticleFilterList list;
177 list.readConfig(Settings::self()->config());
178 Kernel::self()->setArticleFilterList(list);
179
180 m_applyFiltersInterceptor = new ApplyFiltersInterceptor();
181 ArticleInterceptorManager::self()->addInterceptor(m_applyFiltersInterceptor);
182
183 m_storage->open(true);
184 Kernel::self()->setStorage(m_storage);
185 Backend::Storage::setInstance(m_storage); // TODO: kill this one
186
187 loadTagSet(m_tagSetPath);
188
189 m_actionManager = new ActionManagerImpl(this);
190 ActionManager::setInstance(m_actionManager);
191
192 m_view = new Akregator::View(this, parentWidget, m_actionManager, "akregator_view");
193 m_actionManager->initView(m_view);
194 m_actionManager->setTagSet(Kernel::self()->tagSet());
195
196 m_extension = new BrowserExtension(this, "ak_extension");
197
198 connect(m_view, TQ_SIGNAL(setWindowCaption(const TQString&)), this, TQ_SIGNAL(setWindowCaption(const TQString&)));
199 connect(m_view, TQ_SIGNAL(setStatusBarText(const TQString&)), this, TQ_SIGNAL(setStatusBarText(const TQString&)));
200 connect(m_view, TQ_SIGNAL(setProgress(int)), m_extension, TQ_SIGNAL(loadingProgress(int)));
201 connect(m_view, TQ_SIGNAL(signalCanceled(const TQString&)), this, TQ_SIGNAL(canceled(const TQString&)));
202 connect(m_view, TQ_SIGNAL(signalStarted(TDEIO::Job*)), this, TQ_SIGNAL(started(TDEIO::Job*)));
203 connect(m_view, TQ_SIGNAL(signalCompleted()), this, TQ_SIGNAL(completed()));
204
205 // notify the part that this is our internal widget
206 setWidget(m_view);
207
208 TrayIcon* trayIcon = new TrayIcon( getMainWindow() );
209 TrayIcon::setInstance(trayIcon);
210 m_actionManager->initTrayIcon(trayIcon);
211
212 connect(trayIcon, TQ_SIGNAL(showPart()), this, TQ_SIGNAL(showPart()));
213
214 if ( isTrayIconEnabled() )
215 {
216 trayIcon->show();
217 NotificationManager::self()->setWidget(trayIcon, instance());
218 }
219 else
221
222 connect( trayIcon, TQ_SIGNAL(quitSelected()),
223 tdeApp, TQ_SLOT(quit())) ;
224
225 connect( m_view, TQ_SIGNAL(signalUnreadCountChanged(int)), trayIcon, TQ_SLOT(slotSetUnread(int)) );
226
227 connect(tdeApp, TQ_SIGNAL(shutDown()), this, TQ_SLOT(slotOnShutdown()));
228
229 m_autosaveTimer = new TQTimer(this);
230 connect(m_autosaveTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotSaveFeedList()));
231 m_autosaveTimer->start(5*60*1000); // 5 minutes
232
233 setXMLFile("akregator_part.rc", true);
234
235 initFonts();
236
237 RSS::FileRetriever::setUserAgent(TQString("Akregator/%1; librss/remnants").arg(AKREGATOR_VERSION));
238}
239
241{
242 // "[X-TDE-akregator-plugintype] == 'storage'"
243 TDETrader::OfferList offers = PluginManager::query();
244
245 for( TDETrader::OfferList::ConstIterator it = offers.begin(), end = offers.end(); it != end; ++it )
246 {
247 Akregator::Plugin* plugin = PluginManager::createFromService(*it);
248 if (plugin)
249 plugin->init();
250 }
251}
252
253void Part::slotOnShutdown()
254{
255 m_shuttingDown = true;
256
257 const TQString lockLocation = locateLocal("data", "akregator/lock");
258 KSimpleConfig config(lockLocation);
259 config.writeEntry("pid", -1);
260 config.sync();
261
262 m_autosaveTimer->stop();
263 saveSettings();
265 saveTagSet(m_tagSetPath);
266 m_view->slotOnShutdown();
267 //delete m_view;
268 delete TrayIcon::getInstance();
269 TrayIcon::setInstance(0L);
270 delete m_storage;
271 m_storage = 0;
272 //delete m_actionManager;
273}
274
275void Part::slotSettingsChanged()
276{
277 NotificationManager::self()->setWidget(isTrayIconEnabled() ? TrayIcon::getInstance() : getMainWindow(), instance());
278
279 RSS::FileRetriever::setUseCache(Settings::useHTMLCache());
280
281 TQStringList fonts;
282 fonts.append(Settings::standardFont());
283 fonts.append(Settings::fixedFont());
284 fonts.append(Settings::sansSerifFont());
285 fonts.append(Settings::serifFont());
286 fonts.append(Settings::standardFont());
287 fonts.append(Settings::standardFont());
288 fonts.append("0");
289 Settings::setFonts(fonts);
290
291 if (Settings::minimumFontSize() > Settings::mediumFontSize())
292 Settings::setMediumFontSize(Settings::minimumFontSize());
293 saveSettings();
294 m_view->slotSettingsChanged();
295 emit signalSettingsChanged();
296}
298{
299 Kernel::self()->articleFilterList().writeConfig(Settings::self()->config());
300 m_view->saveSettings();
301}
302
304{
305 kdDebug() << "Part::~Part() enter" << endl;
306 if (!m_shuttingDown)
307 slotOnShutdown();
308 kdDebug() << "Part::~Part(): leaving" << endl;
309 ArticleInterceptorManager::self()->removeInterceptor(m_applyFiltersInterceptor);
310 delete m_applyFiltersInterceptor;
311}
312
313void Part::readProperties(TDEConfig* config)
314{
315 m_backedUpList = false;
317
318 if(m_view)
319 m_view->readProperties(config);
320}
321
322void Part::saveProperties(TDEConfig* config)
323{
324 if (m_view)
325 {
327 m_view->saveProperties(config);
328 }
329}
330
331bool Part::openURL(const KURL& url)
332{
333 m_file = url.path();
334 return openFile();
335}
336
338{
339 if ( !m_standardFeedList.isEmpty() && openURL(m_standardFeedList) )
340 m_standardListLoaded = true;
341}
342
343TQDomDocument Part::createDefaultFeedList()
344{
345 TQDomDocument doc;
346 TQDomProcessingInstruction z = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
347 doc.appendChild( z );
348
349 TQDomElement root = doc.createElement( "opml" );
350 root.setAttribute("version","1.0");
351 doc.appendChild( root );
352
353 TQDomElement head = doc.createElement( "head" );
354 root.appendChild(head);
355
356 TQDomElement text = doc.createElement( "text" );
357 text.appendChild(doc.createTextNode(i18n("Feeds")));
358 head.appendChild(text);
359
360 TQDomElement body = doc.createElement( "body" );
361 root.appendChild(body);
362
363 TQDomElement mainFolder = doc.createElement( "outline" );
364 mainFolder.setAttribute("text","Free/Libre Software News");
365 body.appendChild(mainFolder);
366
367 TQDomElement tde = doc.createElement( "outline" );
368 tde.setAttribute("text",i18n("Trinity Desktop News"));
369 tde.setAttribute("xmlUrl","http://trinitydesktop.org/rss.php");
370 mainFolder.appendChild(tde);
371
372 TQDomElement lxer = doc.createElement( "outline" );
373 lxer.setAttribute("text",i18n("LXer Linux News"));
374 lxer.setAttribute("xmlUrl","http://lxer.com/module/newswire/headlines.rss");
375 mainFolder.appendChild(lxer);
376
377 TQDomElement tux = doc.createElement( "outline" );
378 tux.setAttribute("text",i18n("Tuxmachines"));
379 tux.setAttribute("xmlUrl","http://www.tuxmachines.org/node/feed");
380 mainFolder.appendChild(tux);
381
382 TQDomElement lwn = doc.createElement( "outline" );
383 lwn.setAttribute("text",i18n("lwn.net"));
384 lwn.setAttribute("xmlUrl","http://lwn.net/headlines/rss");
385 mainFolder.appendChild(lwn);
386
387 return doc;
388}
389
391{
392 emit setStatusBarText(i18n("Opening Feed List...") );
393
394 TQString str;
395 // m_file is always local so we can use TQFile on it
396 TQFile file(m_file);
397
398 bool fileExists = file.exists();
399 TQString listBackup = m_storage->restoreFeedList();
400
401 TQDomDocument doc;
402
403 if (!fileExists)
404 {
405 doc = createDefaultFeedList();
406 }
407 else
408 {
409 if (file.open(IO_ReadOnly))
410 {
411 // Read OPML feeds list and build TQDom tree.
412 TQTextStream stream(&file);
413 stream.setEncoding(TQTextStream::UnicodeUTF8); // FIXME not all opmls are in utf8
414 str = stream.read();
415 file.close();
416 }
417
418 if (!doc.setContent(str))
419 {
420
421 if (file.size() > 0) // don't backup empty files
422 {
423 TQString backup = m_file + "-backup." + TQString::number(TQDateTime::currentDateTime().toTime_t());
424
425 copyFile(backup);
426
427 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") );
428 }
429
430 if (!doc.setContent(listBackup))
431 doc = createDefaultFeedList();
432 }
433 }
434
435 if (!m_view->loadFeeds(doc))
436 {
437 if (file.size() > 0) // don't backup empty files
438 {
439 TQString backup = m_file + "-backup." + TQString::number(TQDateTime::currentDateTime().toTime_t());
440 copyFile(backup);
441
442 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") );
443 }
444 m_view->loadFeeds(createDefaultFeedList());
445 }
446
447 emit setStatusBarText(TQString());
448
449
450 if( Settings::markAllFeedsReadOnStartup() )
451 m_view->slotMarkAllFeedsRead();
452
453 if (Settings::fetchOnStartup())
454 m_view->slotFetchAllFeeds();
455
456 return true;
457}
458
460{
461 // don't save to the standard feed list, when it wasn't completely loaded before
462 if (!m_standardListLoaded)
463 return;
464
465 // the first time we overwrite the feed list, we create a backup
466 if (!m_backedUpList)
467 {
468 TQString backup = m_file + "~";
469
470 if (copyFile(backup))
471 m_backedUpList = true;
472 }
473
474 TQString xmlStr = m_view->feedListToOPML().toString();
475 m_storage->storeFeedList(xmlStr);
476
477 TQFile file(m_file);
478 if (file.open(IO_WriteOnly) == false)
479 {
480 //FIXME: allow to save the feedlist into different location -tpr 20041118
481 KMessageBox::error(m_view, i18n("Access denied: cannot save feed list (%1)").arg(m_file), i18n("Write error") );
482 return;
483 }
484
485 // use TQTextStream to dump the text to the file
486 TQTextStream stream(&file);
487 stream.setEncoding(TQTextStream::UnicodeUTF8);
488
489 // Write OPML data file.
490 // Archive data files are saved elsewhere.
491
492 stream << xmlStr << endl;
493
494 file.close();
495}
496
498{
499 return Settings::showTrayIcon();
500}
501
502bool Part::mergePart(KParts::Part* part)
503{
504 if (part != m_mergedPart)
505 {
506 if (!factory())
507 {
508 if (m_mergedPart)
509 removeChildClient(m_mergedPart);
510 else
511 insertChildClient(part);
512 }
513 else
514 {
515 if (m_mergedPart) {
516 factory()->removeClient(m_mergedPart);
517 if (childClients()->containsRef(m_mergedPart))
518 removeChildClient(m_mergedPart);
519 }
520 if (part)
521 factory()->addClient(part);
522 }
523
524 m_mergedPart = part;
525 }
526 return true;
527}
528
530{
531 // this is a dirty fix to get the main window used for the tray icon
532
533 TQWidgetList *l = tdeApp->topLevelWidgets();
534 TQWidgetListIt it( *l );
535 TQWidget *wid;
536
537 // check if there is an akregator main window
538 while ( (wid = it.current()) != 0 )
539 {
540 ++it;
541 //kdDebug() << "win name: " << wid->name() << endl;
542 if (TQString(wid->name()) == "akregator_mainwindow")
543 {
544 delete l;
545 return wid;
546 }
547 }
548 // if not, check for kontact main window
549 TQWidgetListIt it2( *l );
550 while ( (wid = it2.current()) != 0 )
551 {
552 ++it2;
553 if (TQString(wid->name()).startsWith("kontact-mainwindow"))
554 {
555 delete l;
556 return wid;
557 }
558 }
559 delete l;
560 return 0;
561}
562
563void Part::loadTagSet(const TQString& path)
564{
565 TQDomDocument doc;
566
567 TQFile file(path);
568 if (file.open(IO_ReadOnly))
569 {
570 doc.setContent(TQByteArray(file.readAll()));
571 file.close();
572 }
573 // if we can't load the tagset from the xml file, check for the backup in the backend
574 if (doc.isNull())
575 {
576 doc.setContent(m_storage->restoreTagSet());
577 }
578
579 if (!doc.isNull())
580 {
581 Kernel::self()->tagSet()->readFromXML(doc);
582 }
583 else
584 {
585 Kernel::self()->tagSet()->insert(Tag("http://akregator.sf.net/tags/Interesting", i18n("Interesting")));
586 }
587}
588
589void Part::saveTagSet(const TQString& path)
590{
591 TQString xmlStr = Kernel::self()->tagSet()->toXML().toString();
592
593 m_storage->storeTagSet(xmlStr);
594
595 TQFile file(path);
596
597 if ( file.open(IO_WriteOnly) )
598 {
599
600 TQTextStream stream(&file);
601 stream.setEncoding(TQTextStream::UnicodeUTF8);
602 stream << xmlStr << "\n";
603 file.close();
604 }
605}
606
607void Part::importFile(const KURL& url)
608{
609 TQString filename;
610
611 bool isRemote = false;
612
613 if (url.isLocalFile())
614 filename = url.path();
615 else
616 {
617 isRemote = true;
618
619 if (!TDEIO::NetAccess::download(url, filename, m_view) )
620 {
621 KMessageBox::error(m_view, TDEIO::NetAccess::lastErrorString() );
622 return;
623 }
624 }
625
626 TQFile file(filename);
627 if (file.open(IO_ReadOnly))
628 {
629 // Read OPML feeds list and build TQDom tree.
630 TQDomDocument doc;
631 if (doc.setContent(TQByteArray(file.readAll())))
632 m_view->importFeeds(doc);
633 else
634 KMessageBox::error(m_view, i18n("Could not import the file %1 (no valid OPML)").arg(filename), i18n("OPML Parsing Error") );
635 }
636 else
637 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"));
638
639 if (isRemote)
640 TDEIO::NetAccess::removeTempFile(filename);
641}
642
643void Part::exportFile(const KURL& url)
644{
645 if (url.isLocalFile())
646 {
647 TQFile file(url.path());
648
649 if ( file.exists() &&
650 KMessageBox::questionYesNo(m_view,
651 i18n("The file %1 already exists; do you want to overwrite it?").arg(file.name()),
652 i18n("Export"),
653 i18n("Overwrite"),
654 KStdGuiItem::cancel()) == KMessageBox::No )
655 return;
656
657 if ( !file.open(IO_WriteOnly) )
658 {
659 KMessageBox::error(m_view, i18n("Access denied: cannot write to file %1").arg(file.name()), i18n("Write Error") );
660 return;
661 }
662
663 TQTextStream stream(&file);
664 stream.setEncoding(TQTextStream::UnicodeUTF8);
665
666 stream << m_view->feedListToOPML().toString() << "\n";
667 file.close();
668 }
669 else
670 {
671 KTempFile tmpfile;
672 tmpfile.setAutoDelete(true);
673
674 TQTextStream stream(tmpfile.file());
675 stream.setEncoding(TQTextStream::UnicodeUTF8);
676
677 stream << m_view->feedListToOPML().toString() << "\n";
678 tmpfile.close();
679
680 if (!TDEIO::NetAccess::upload(tmpfile.name(), url, m_view))
681 KMessageBox::error(m_view, TDEIO::NetAccess::lastErrorString() );
682 }
683}
684
685void Part::fileImport()
686{
687 KURL url = KFileDialog::getOpenURL( TQString(),
688 "*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)")
689 +"\n*|" + i18n("All Files") );
690
691 if (!url.isEmpty())
692 importFile(url);
693}
694
695 void Part::fileExport()
696{
697 KURL url= KFileDialog::getSaveURL( TQString(),
698 "*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)")
699 +"\n*|" + i18n("All Files") );
700
701 if ( !url.isEmpty() )
702 exportFile(url);
703}
704
705void Part::fileGetFeeds()
706{
707 /*GetFeeds *gf = new GetFeeds();
708 gf->show();*/
709 //KNS::DownloadDialog::open("akregator/feeds", i18n("Get New Feeds"));
710}
711
712void Part::fileSendArticle(bool attach)
713{
714 // FIXME: you have to open article to tab to be able to send...
715 TQString title, text;
716
717 text = m_view->currentFrame()->part()->url().prettyURL();
718 if(text.isEmpty() || text.isNull())
719 return;
720
721 title = m_view->currentFrame()->title();
722
723 if(attach) {
724 tdeApp->invokeMailer("",
725 "",
726 "",
727 title,
728 text,
729 "",
730 text);
731 }
732 else {
733 tdeApp->invokeMailer("",
734 "",
735 "",
736 title,
737 text);
738 }
739}
740
742{
743 m_view->slotFetchAllFeeds();
744}
745
746void Part::fetchFeedUrl(const TQString&s)
747{
748 kdDebug() << "fetchFeedURL==" << s << endl;
749}
750
751void Part::addFeedsToGroup(const TQStringList& urls, const TQString& group)
752{
753 for (TQStringList::ConstIterator it = urls.begin(); it != urls.end(); ++it)
754 {
755 kdDebug() << "Akregator::Part::addFeedToGroup adding feed with URL " << *it << " to group " << group << endl;
756 m_view->addFeedToGroup(*it, group);
757 }
759}
760
761void Part::addFeed()
762{
763 m_view->slotFeedAdd();
764}
765
767{
768 return new Akregator::AboutData;
769}
770
771void Part::showKNotifyOptions()
772{
773 TDEAboutData* about = new Akregator::AboutData;
774 KNotifyDialog::configure(m_view, "akregator_knotify_config", about);
775 delete about;
776}
777
779{
780 if ( TDEConfigDialog::showDialog( "settings" ) )
781 return;
782
783 TDEConfigDialog* dialog = new ConfigDialog( m_view, "settings", Settings::self() );
784
785 connect( dialog, TQ_SIGNAL(settingsChanged()),
786 this, TQ_SLOT(slotSettingsChanged()) );
787 connect( dialog, TQ_SIGNAL(settingsChanged()),
788 TrayIcon::getInstance(), TQ_SLOT(settingsChanged()) );
789
790 dialog->show();
791}
792
793void Part::partActivateEvent(KParts::PartActivateEvent* event)
794{
795 if (factory() && m_mergedPart)
796 {
797 if (event->activated())
798 factory()->addClient(m_mergedPart);
799 else
800 factory()->removeClient(m_mergedPart);
801 }
802
803 MyBasePart::partActivateEvent(event);
804}
805
806KParts::Part* Part::hitTest(TQWidget *widget, const TQPoint &globalPos)
807{
808 bool child = false;
809 TQWidget *me = this->widget();
810 while (widget) {
811 if (widget == me) {
812 child = true;
813 break;
814 }
815 if (!widget) {
816 break;
817 }
818 widget = widget->parentWidget();
819 }
820 if (m_view && m_view->currentFrame() && child) {
821 return m_view->currentFrame()->part();
822 } else {
823 return MyBasePart::hitTest(widget, globalPos);
824 }
825}
826
827void Part::initFonts()
828{
829 TQStringList fonts = Settings::fonts();
830 if (fonts.isEmpty())
831 {
832 fonts.append(TDEGlobalSettings::generalFont().family());
833 fonts.append(TDEGlobalSettings::fixedFont().family());
834 fonts.append(TDEGlobalSettings::generalFont().family());
835 fonts.append(TDEGlobalSettings::generalFont().family());
836 fonts.append("0");
837 }
838 Settings::setFonts(fonts);
839 if (Settings::standardFont().isEmpty())
840 Settings::setStandardFont(fonts[0]);
841 if (Settings::fixedFont().isEmpty())
842 Settings::setFixedFont(fonts[1]);
843 if (Settings::sansSerifFont().isEmpty())
844 Settings::setSansSerifFont(fonts[2]);
845 if (Settings::serifFont().isEmpty())
846 Settings::setSerifFont(fonts[3]);
847
848 TDEConfig* conf = Settings::self()->config();
849 conf->setGroup("HTML Settings");
850
851 TDEConfig konq("konquerorrc", true, false);
852 konq.setGroup("HTML Settings");
853
854 if (!conf->hasKey("MinimumFontSize"))
855 {
856 int minfs;
857 if (konq.hasKey("MinimumFontSize"))
858 minfs = konq.readNumEntry("MinimumFontSize");
859 else
860 minfs = TDEGlobalSettings::generalFont().pointSize();
861 kdDebug() << "Part::initFonts(): set MinimumFontSize to " << minfs << endl;
862 Settings::setMinimumFontSize(minfs);
863 }
864
865 if (!conf->hasKey("MediumFontSize"))
866 {
867 int medfs;
868 if (konq.hasKey("MediumFontSize"))
869 medfs = konq.readNumEntry("MediumFontSize");
870 else
871 medfs = TDEGlobalSettings::generalFont().pointSize();
872 kdDebug() << "Part::initFonts(): set MediumFontSize to " << medfs << endl;
873 Settings::setMediumFontSize(medfs);
874 }
875
876 if (!conf->hasKey("UnderlineLinks"))
877 {
878 bool underline = true;
879 if (konq.hasKey("UnderlineLinks"))
880 underline = konq.readBoolEntry("UnderlineLinks");
881
882 kdDebug() << "Part::initFonts(): set UnderlineLinks to " << underline << endl;
883 Settings::setUnderlineLinks(underline);
884 }
885
886 if (!conf->hasKey("EnableFavicon"))
887 {
888 bool enableFavicon = true;
889 if (konq.hasKey("EnableFavicon"))
890 enableFavicon = konq.readBoolEntry("EnableFavicon");
891
892 kdDebug() << "Part::initFonts(): set EnableFavicon to " << enableFavicon << endl;
893 Settings::setEnableFavIcon(enableFavicon);
894 }
895
896 if (!conf->hasKey("AutoLoadImages"))
897 {
898 bool autoLoadImages = true;
899 if (konq.hasKey("AutoLoadImages"))
900 autoLoadImages = konq.readBoolEntry("AutoLoadImages");
901
902 kdDebug() << "Part::initFonts(): set AutoLoadImages to " << autoLoadImages << endl;
903 Settings::setAutoLoadImages(autoLoadImages);
904 }
905
906}
907
908bool Part::copyFile(const TQString& backup)
909{
910 TQFile file(m_file);
911
912 if (file.open(IO_ReadOnly))
913 {
914 TQFile backupFile(backup);
915 if (backupFile.open(IO_WriteOnly))
916 {
917 TQTextStream in(&file);
918 TQTextStream out(&backupFile);
919 while (!in.atEnd())
920 out << in.readLine();
921 backupFile.close();
922 file.close();
923 return true;
924 }
925 else
926 {
927 file.close();
928 return false;
929 }
930 }
931 return false;
932}
933
934static TQString getMyHostName()
935{
936 char hostNameC[256];
937 // null terminate this C string
938 hostNameC[255] = 0;
939 // set the string to 0 length if gethostname fails
940 if(gethostname(hostNameC, 255))
941 hostNameC[0] = 0;
942 return TQString::fromLocal8Bit(hostNameC);
943}
944
945// taken from KMail
946bool Part::tryToLock(const TQString& backendName)
947{
948// Check and create a lock file to prevent concurrent access to metakit archive
949 TQString appName = tdeApp->instanceName();
950 if ( appName.isEmpty() )
951 appName = "akregator";
952
953 TQString programName;
954 const TDEAboutData *about = tdeApp->aboutData();
955 if ( about )
956 programName = about->programName();
957 if ( programName.isEmpty() )
958 programName = i18n("Akregator");
959
960 TQString lockLocation = locateLocal("data", "akregator/lock");
961 KSimpleConfig config(lockLocation);
962 int oldPid = config.readNumEntry("pid", -1);
963 const TQString oldHostName = config.readEntry("hostname");
964 const TQString oldAppName = config.readEntry( "appName", appName );
965 const TQString oldProgramName = config.readEntry( "programName", programName );
966 const TQString hostName = getMyHostName();
967 bool first_instance = false;
968 if ( oldPid == -1 )
969 first_instance = true;
970 // check if the lock file is stale by trying to see if
971 // the other pid is currently running.
972 // Not 100% correct but better safe than sorry
973 else if (hostName == oldHostName && oldPid != getpid()) {
974 if ( kill(oldPid, 0) == -1 )
975 first_instance = ( errno == ESRCH );
976 }
977
978 if ( !first_instance )
979 {
980 TQString msg;
981 if ( oldHostName == hostName )
982 {
983 // this can only happen if the user is running this application on
984 // different displays on the same machine. All other cases will be
985 // taken care of by TDEUniqueApplication()
986 if ( oldAppName == appName )
987 msg = i18n("<qt>%1 already seems to be running on another display on "
988 "this machine. <b>Running %2 more than once is not supported "
989 "by the %3 backend and "
990 "can cause 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( programName, programName, backendName );
994 // TQString::arg( st ) only replaces the first occurrence of %1
995 // with st while TQString::arg( s1, s2 ) replacess all occurrences
996 // of %1 with s1 and all occurrences of %2 with s2. So don't
997 // even think about changing the above to .arg( programName ).
998 else
999 msg = i18n("<qt>%1 seems to be running on another display on this "
1000 "machine. <b>Running %1 and %2 at the same "
1001 "time is not supported by the %3 backend and can cause "
1002 "the loss of archived articles and crashes at startup.</b> "
1003 "You should disable the archive for now "
1004 "unless you are sure that %2 is not already running.</qt>")
1005 .arg( oldProgramName, programName, backendName );
1006 }
1007 else
1008 {
1009 if ( oldAppName == appName )
1010 msg = i18n("<qt>%1 already seems to be running on %2. <b>Running %1 more "
1011 "than once is not supported by the %3 backend and can cause "
1012 "the loss of archived articles and crashes at startup.</b> "
1013 "You should disable the archive for now "
1014 "unless you are sure that it is "
1015 "not already running on %2.</qt>")
1016 .arg( programName, oldHostName, backendName );
1017 else
1018 msg = i18n("<qt>%1 seems to be running on %3. <b>Running %1 and %2 at the "
1019 "same time is not supported by the %4 backend and can cause "
1020 "the loss of archived articles and crashes at startup.</b> "
1021 "You should disable the archive for now "
1022 "unless you are sure that %1 is "
1023 "not running on %3.</qt>")
1024 .arg( oldProgramName, programName, oldHostName, backendName );
1025 }
1026
1027 KCursorSaver idle( KBusyPtr::idle() );
1028 if ( KMessageBox::No ==
1029 KMessageBox::warningYesNo( 0, msg, TQString(),
1030 i18n("Force Access"),
1031 i18n("Disable Archive")) )
1032 {
1033 return false;
1034 }
1035 }
1036
1037 config.writeEntry("pid", getpid());
1038 config.writeEntry("hostname", hostName);
1039 config.writeEntry( "appName", appName );
1040 config.writeEntry( "programName", programName );
1041 config.sync();
1042 return true;
1043}
1044
1045
1046} // namespace Akregator
1047#include "akregator_part.moc"
Akregator-specific implementation of the ActionManager interface.
virtual void storeFeedList(const TQString &opmlStr)=0
stores the feed list in the storage backend.
virtual bool open(bool autoCommit=false)=0
Open storage and prepare it for work.
void slotNotifyFeeds(const TQStringList &feeds)
notifies the addition of feeds (used when added via DCOP or command line)
void setWidget(TQWidget *widget, TDEInstance *inst=0)
the widget used for notification, normally either the mainwindow or the tray icon
static NotificationManager * self()
singleton instance of notification manager
virtual bool openFile()
This must be implemented by each part.
TQWidget * getMainWindow()
FIXME: hack to get the tray icon working.
virtual void partActivateEvent(KParts::PartActivateEvent *event)
reimplemented to load/unload the merged parts on selection/deselection
virtual bool isTrayIconEnabled() const
static TDEAboutData * createAboutData()
Create TDEAboutData for this KPart.
virtual bool mergePart(KParts::Part *)
merges a nested part's GUI into the gui of this part
virtual void fetchAllFeeds()
Fetch all feeds in the feed tree.
virtual void saveProperties(TDEConfig *config)
This method is called when it is time for the app to save its properties for session management purpo...
void slotSaveFeedList()
Saves the standard feed list to it's default location.
virtual bool openURL(const KURL &url)
Opens feedlist.
virtual void openStandardFeedList()
Opens standard feedlist.
void loadPlugins()
loads all Akregator plugins
virtual void addFeedsToGroup(const TQStringList &urls, const TQString &group)
Add a feed to a group.
virtual ~Part()
Destructor.
virtual void readProperties(TDEConfig *config)
This method is called when this app is restored.
virtual void saveSettings()
Used to save settings after changing them from configuration dialog.
void showOptions()
Shows configuration dialog.
This is the main widget of the view, containing tree view, article list, viewer etc.
void slotFeedAdd()
adds a new feed to the feed tree
bool importFeeds(const TQDomDocument &doc)
Adds the feeds in doc to the "Imported Folder".
TQDomDocument feedListToOPML()
virtual void readProperties(TDEConfig *config)
session management
void saveSettings()
saves settings.
void addFeedToGroup(const TQString &url, const TQString &group)
Add a feed to a group.
void slotFetchAllFeeds()
starts fetching of all feeds in the tree
bool loadFeeds(const TQDomDocument &doc, Folder *parent=0)
Parse OPML presentation of feeds and read in articles archive, if present.
void slotMarkAllFeedsRead()
marks all articles in all feeds in the tree as read