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 <kstandarddirs.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 
92 namespace Akregator {
93 
94 typedef KParts::GenericFactory<Part> AkregatorFactory;
95 K_EXPORT_COMPONENT_FACTORY( libakregatorpart, AkregatorFactory )
96 
97 BrowserExtension::BrowserExtension(Part *p, const char *name)
98  : KParts::BrowserExtension( p, name )
99 {
100  m_part=p;
101 }
102 
103 void BrowserExtension::saveSettings()
104 {
105  m_part->saveSettings();
106 }
107 
108 class 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 
121 Part::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  kapp, TQ_SLOT(quit())) ;
224 
225  connect( m_view, TQ_SIGNAL(signalUnreadCountChanged(int)), trayIcon, TQ_SLOT(slotSetUnread(int)) );
226 
227  connect(kapp, 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 
253 void 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 
275 void 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 
313 void Part::readProperties(TDEConfig* config)
314 {
315  m_backedUpList = false;
317 
318  if(m_view)
319  m_view->readProperties(config);
320 }
321 
322 void Part::saveProperties(TDEConfig* config)
323 {
324  if (m_view)
325  {
327  m_view->saveProperties(config);
328  }
329 }
330 
331 bool 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 
343 TQDomDocument 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 
502 bool 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 = kapp->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 
563 void 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 
589 void 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 
607 void 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 
643 void 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 
685 void 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 
705 void Part::fileGetFeeds()
706 {
707  /*GetFeeds *gf = new GetFeeds();
708  gf->show();*/
709  //KNS::DownloadDialog::open("akregator/feeds", i18n("Get New Feeds"));
710 }
711 
712 void 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  kapp->invokeMailer("",
725  "",
726  "",
727  title,
728  text,
729  "",
730  text);
731  }
732  else {
733  kapp->invokeMailer("",
734  "",
735  "",
736  title,
737  text);
738  }
739 }
740 
742 {
743  m_view->slotFetchAllFeeds();
744 }
745 
746 void Part::fetchFeedUrl(const TQString&s)
747 {
748  kdDebug() << "fetchFeedURL==" << s << endl;
749 }
750 
751 void 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 
761 void Part::addFeed()
762 {
763  m_view->slotFeedAdd();
764 }
765 
766 TDEAboutData *Part::createAboutData()
767 {
768  return new Akregator::AboutData;
769 }
770 
771 void 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 
793 void 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 
806 KParts::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 
827 void 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 
908 bool 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 
934 static 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
946 bool Part::tryToLock(const TQString& backendName)
947 {
948 // Check and create a lock file to prevent concurrent access to metakit archive
949  TQString appName = kapp->instanceName();
950  if ( appName.isEmpty() )
951  appName = "akregator";
952 
953  TQString programName;
954  const TDEAboutData *about = kapp->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