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 <kaboutdata.h>
27 #include <kaction.h>
28 #include <kactionclasses.h>
29 #include <kactioncollection.h>
30 #include <kapplication.h>
31 #include <kconfig.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>
42 #include <kservice.h>
43 #include <kstandarddirs.h>
44 #include <kstdaction.h>
45 #include <ktempfile.h>
46 #include <ktrader.h>
47 #include <kio/netaccess.h>
48 #include <kparts/browserinterface.h>
49 #include <kparts/genericfactory.h>
50 #include <kparts/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 <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 void processArticle(Article& article)
112  {
113  Filters::ArticleFilterList list = Kernel::self()->articleFilterList();
114  for (Filters::ArticleFilterList::ConstIterator it = list.begin(); it != list.end(); ++it)
115  (*it).applyTo(article);
116  }
117 };
118 
119 Part::Part( TQWidget *parentWidget, const char * /*widgetName*/,
120  TQObject *parent, const char *name, const TQStringList& )
121  : DCOPObject("AkregatorIface")
122  , MyBasePart(parent, name)
123  , m_standardListLoaded(false)
124  , m_shuttingDown(false)
125  , m_mergedPart(0)
126  , m_view(0)
127  , m_backedUpList(false)
128  , m_storage(0)
129 {
130  // we need an instance
131  setInstance( AkregatorFactory::instance() );
132 
133  // start knotifyclient if not already started. makes it work for people who doesn't use full kde, according to kmail devels
134  KNotifyClient::startDaemon();
135 
136  m_standardFeedList = KGlobal::dirs()->saveLocation("data", "akregator/data") + "/feeds.opml";
137 
138  m_tagSetPath = KGlobal::dirs()->saveLocation("data", "akregator/data") + "/tagset.xml";
139 
140  Backend::StorageFactoryDummyImpl* dummyFactory = new Backend::StorageFactoryDummyImpl();
141  Backend::StorageFactoryRegistry::self()->registerFactory(dummyFactory, dummyFactory->key());
142  loadPlugins(); // FIXME: also unload them!
143 
144  m_storage = 0;
145  Backend::StorageFactory* factory = Backend::StorageFactoryRegistry::self()->getFactory(Settings::archiveBackend());
146 
147  TQStringList storageParams;
148 
149  storageParams.append(TQString("taggingEnabled=%1").arg(Settings::showTaggingGUI() ? "true" : "false"));
150 
151  if (factory != 0)
152  {
153  if (factory->allowsMultipleWriteAccess())
154  {
155  m_storage = factory->createStorage(storageParams);
156  }
157  else
158  {
159  if (tryToLock(factory->name()))
160  m_storage = factory->createStorage(storageParams);
161  else
162  m_storage = dummyFactory->createStorage(storageParams);
163  }
164  }
165 
166 
167  if (!m_storage) // Houston, we have a problem
168  {
169  m_storage = Backend::StorageFactoryRegistry::self()->getFactory("dummy")->createStorage(storageParams);
170 
171  KMessageBox::error(parentWidget, i18n("Unable to load storage backend plugin \"%1\". No feeds are archived.").arg(Settings::archiveBackend()), i18n("Plugin error") );
172  }
173 
174  Filters::ArticleFilterList list;
175  list.readConfig(Settings::self()->config());
176  Kernel::self()->setArticleFilterList(list);
177 
178  m_applyFiltersInterceptor = new ApplyFiltersInterceptor();
179  ArticleInterceptorManager::self()->addInterceptor(m_applyFiltersInterceptor);
180 
181  m_storage->open(true);
182  Kernel::self()->setStorage(m_storage);
183  Backend::Storage::setInstance(m_storage); // TODO: kill this one
184 
185  loadTagSet(m_tagSetPath);
186 
187  m_actionManager = new ActionManagerImpl(this);
188  ActionManager::setInstance(m_actionManager);
189 
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());
193 
194  m_extension = new BrowserExtension(this, "ak_extension");
195 
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()));
202 
203  // notify the part that this is our internal widget
204  setWidget(m_view);
205 
206  TrayIcon* trayIcon = new TrayIcon( getMainWindow() );
207  TrayIcon::setInstance(trayIcon);
208  m_actionManager->initTrayIcon(trayIcon);
209 
210  connect(trayIcon, TQT_SIGNAL(showPart()), this, TQT_SIGNAL(showPart()));
211 
212  if ( isTrayIconEnabled() )
213  {
214  trayIcon->show();
215  NotificationManager::self()->setWidget(trayIcon, instance());
216  }
217  else
219 
220  connect( trayIcon, TQT_SIGNAL(quitSelected()),
221  kapp, TQT_SLOT(quit())) ;
222 
223  connect( m_view, TQT_SIGNAL(signalUnreadCountChanged(int)), trayIcon, TQT_SLOT(slotSetUnread(int)) );
224 
225  connect(kapp, TQT_SIGNAL(shutDown()), this, TQT_SLOT(slotOnShutdown()));
226 
227  m_autosaveTimer = new TQTimer(this);
228  connect(m_autosaveTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotSaveFeedList()));
229  m_autosaveTimer->start(5*60*1000); // 5 minutes
230 
231  setXMLFile("akregator_part.rc", true);
232 
233  initFonts();
234 
235  RSS::FileRetriever::setUserAgent(TQString("Akregator/%1; librss/remnants").arg(AKREGATOR_VERSION));
236 }
237 
239 {
240  // "[X-KDE-akregator-plugintype] == 'storage'"
241  KTrader::OfferList offers = PluginManager::query();
242 
243  for( KTrader::OfferList::ConstIterator it = offers.begin(), end = offers.end(); it != end; ++it )
244  {
245  Akregator::Plugin* plugin = PluginManager::createFromService(*it);
246  if (plugin)
247  plugin->init();
248  }
249 }
250 
251 void Part::slotOnShutdown()
252 {
253  m_shuttingDown = true;
254 
255  const TQString lockLocation = locateLocal("data", "akregator/lock");
256  KSimpleConfig config(lockLocation);
257  config.writeEntry("pid", -1);
258  config.sync();
259 
260  m_autosaveTimer->stop();
261  saveSettings();
263  saveTagSet(m_tagSetPath);
264  m_view->slotOnShutdown();
265  //delete m_view;
266  delete TrayIcon::getInstance();
267  TrayIcon::setInstance(0L);
268  delete m_storage;
269  m_storage = 0;
270  //delete m_actionManager;
271 }
272 
273 void Part::slotSettingsChanged()
274 {
275  NotificationManager::self()->setWidget(isTrayIconEnabled() ? TrayIcon::getInstance() : getMainWindow(), instance());
276 
277  RSS::FileRetriever::setUseCache(Settings::useHTMLCache());
278 
279  TQStringList fonts;
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());
286  fonts.append("0");
287  Settings::setFonts(fonts);
288 
289  if (Settings::minimumFontSize() > Settings::mediumFontSize())
290  Settings::setMediumFontSize(Settings::minimumFontSize());
291  saveSettings();
292  m_view->slotSettingsChanged();
293  emit signalSettingsChanged();
294 }
296 {
297  Kernel::self()->articleFilterList().writeConfig(Settings::self()->config());
298  m_view->saveSettings();
299 }
300 
302 {
303  kdDebug() << "Part::~Part() enter" << endl;
304  if (!m_shuttingDown)
305  slotOnShutdown();
306  kdDebug() << "Part::~Part(): leaving" << endl;
307  ArticleInterceptorManager::self()->removeInterceptor(m_applyFiltersInterceptor);
308  delete m_applyFiltersInterceptor;
309 }
310 
311 void Part::readProperties(KConfig* config)
312 {
313  m_backedUpList = false;
315 
316  if(m_view)
317  m_view->readProperties(config);
318 }
319 
320 void Part::saveProperties(KConfig* config)
321 {
322  if (m_view)
323  {
325  m_view->saveProperties(config);
326  }
327 }
328 
329 bool Part::openURL(const KURL& url)
330 {
331  m_file = url.path();
332  return openFile();
333 }
334 
336 {
337  if ( !m_standardFeedList.isEmpty() && openURL(m_standardFeedList) )
338  m_standardListLoaded = true;
339 }
340 
341 TQDomDocument Part::createDefaultFeedList()
342 {
343  TQDomDocument doc;
344  TQDomProcessingInstruction z = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
345  doc.appendChild( z );
346 
347  TQDomElement root = doc.createElement( "opml" );
348  root.setAttribute("version","1.0");
349  doc.appendChild( root );
350 
351  TQDomElement head = doc.createElement( "head" );
352  root.appendChild(head);
353 
354  TQDomElement text = doc.createElement( "text" );
355  text.appendChild(doc.createTextNode(i18n("Feeds")));
356  head.appendChild(text);
357 
358  TQDomElement body = doc.createElement( "body" );
359  root.appendChild(body);
360 
361  TQDomElement mainFolder = doc.createElement( "outline" );
362  mainFolder.setAttribute("text","KDE");
363  body.appendChild(mainFolder);
364 
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);
369 
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);
374 
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);
379 
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);
384 
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);
389 
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);
394 
395  return doc;
396 }
397 
399 {
400  emit setStatusBarText(i18n("Opening Feed List...") );
401 
402  TQString str;
403  // m_file is always local so we can use TQFile on it
404  TQFile file(m_file);
405 
406  bool fileExists = file.exists();
407  TQString listBackup = m_storage->restoreFeedList();
408 
409  TQDomDocument doc;
410 
411  if (!fileExists)
412  {
413  doc = createDefaultFeedList();
414  }
415  else
416  {
417  if (file.open(IO_ReadOnly))
418  {
419  // Read OPML feeds list and build TQDom tree.
420  TQTextStream stream(&file);
421  stream.setEncoding(TQTextStream::UnicodeUTF8); // FIXME not all opmls are in utf8
422  str = stream.read();
423  file.close();
424  }
425 
426  if (!doc.setContent(str))
427  {
428 
429  if (file.size() > 0) // don't backup empty files
430  {
431  TQString backup = m_file + "-backup." + TQString::number(TQDateTime::currentDateTime().toTime_t());
432 
433  copyFile(backup);
434 
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") );
436  }
437 
438  if (!doc.setContent(listBackup))
439  doc = createDefaultFeedList();
440  }
441  }
442 
443  if (!m_view->loadFeeds(doc))
444  {
445  if (file.size() > 0) // don't backup empty files
446  {
447  TQString backup = m_file + "-backup." + TQString::number(TQDateTime::currentDateTime().toTime_t());
448  copyFile(backup);
449 
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") );
451  }
452  m_view->loadFeeds(createDefaultFeedList());
453  }
454 
455  emit setStatusBarText(TQString());
456 
457 
458  if( Settings::markAllFeedsReadOnStartup() )
459  m_view->slotMarkAllFeedsRead();
460 
461  if (Settings::fetchOnStartup())
462  m_view->slotFetchAllFeeds();
463 
464  return true;
465 }
466 
468 {
469  // don't save to the standard feed list, when it wasn't completely loaded before
470  if (!m_standardListLoaded)
471  return;
472 
473  // the first time we overwrite the feed list, we create a backup
474  if (!m_backedUpList)
475  {
476  TQString backup = m_file + "~";
477 
478  if (copyFile(backup))
479  m_backedUpList = true;
480  }
481 
482  TQString xmlStr = m_view->feedListToOPML().toString();
483  m_storage->storeFeedList(xmlStr);
484 
485  TQFile file(m_file);
486  if (file.open(IO_WriteOnly) == false)
487  {
488  //FIXME: allow to save the feedlist into different location -tpr 20041118
489  KMessageBox::error(m_view, i18n("Access denied: cannot save feed list (%1)").arg(m_file), i18n("Write error") );
490  return;
491  }
492 
493  // use TQTextStream to dump the text to the file
494  TQTextStream stream(&file);
495  stream.setEncoding(TQTextStream::UnicodeUTF8);
496 
497  // Write OPML data file.
498  // Archive data files are saved elsewhere.
499 
500  stream << xmlStr << endl;
501 
502  file.close();
503 }
504 
506 {
507  return Settings::showTrayIcon();
508 }
509 
510 bool Part::mergePart(KParts::Part* part)
511 {
512  if (part != m_mergedPart)
513  {
514  if (!factory())
515  {
516  if (m_mergedPart)
517  removeChildClient(m_mergedPart);
518  else
519  insertChildClient(part);
520  }
521  else
522  {
523  if (m_mergedPart) {
524  factory()->removeClient(m_mergedPart);
525  if (childClients()->containsRef(m_mergedPart))
526  removeChildClient(m_mergedPart);
527  }
528  if (part)
529  factory()->addClient(part);
530  }
531 
532  m_mergedPart = part;
533  }
534  return true;
535 }
536 
538 {
539  // this is a dirty fix to get the main window used for the tray icon
540 
541  TQWidgetList *l = kapp->topLevelWidgets();
542  TQWidgetListIt it( *l );
543  TQWidget *wid;
544 
545  // check if there is an akregator main window
546  while ( (wid = it.current()) != 0 )
547  {
548  ++it;
549  //kdDebug() << "win name: " << wid->name() << endl;
550  if (TQString(wid->name()) == "akregator_mainwindow")
551  {
552  delete l;
553  return wid;
554  }
555  }
556  // if not, check for kontact main window
557  TQWidgetListIt it2( *l );
558  while ( (wid = it2.current()) != 0 )
559  {
560  ++it2;
561  if (TQString(wid->name()).startsWith("kontact-mainwindow"))
562  {
563  delete l;
564  return wid;
565  }
566  }
567  delete l;
568  return 0;
569 }
570 
571 void Part::loadTagSet(const TQString& path)
572 {
573  TQDomDocument doc;
574 
575  TQFile file(path);
576  if (file.open(IO_ReadOnly))
577  {
578  doc.setContent(TQByteArray(file.readAll()));
579  file.close();
580  }
581  // if we can't load the tagset from the xml file, check for the backup in the backend
582  if (doc.isNull())
583  {
584  doc.setContent(m_storage->restoreTagSet());
585  }
586 
587  if (!doc.isNull())
588  {
589  Kernel::self()->tagSet()->readFromXML(doc);
590  }
591  else
592  {
593  Kernel::self()->tagSet()->insert(Tag("http://akregator.sf.net/tags/Interesting", i18n("Interesting")));
594  }
595 }
596 
597 void Part::saveTagSet(const TQString& path)
598 {
599  TQString xmlStr = Kernel::self()->tagSet()->toXML().toString();
600 
601  m_storage->storeTagSet(xmlStr);
602 
603  TQFile file(path);
604 
605  if ( file.open(IO_WriteOnly) )
606  {
607 
608  TQTextStream stream(&file);
609  stream.setEncoding(TQTextStream::UnicodeUTF8);
610  stream << xmlStr << "\n";
611  file.close();
612  }
613 }
614 
615 void Part::importFile(const KURL& url)
616 {
617  TQString filename;
618 
619  bool isRemote = false;
620 
621  if (url.isLocalFile())
622  filename = url.path();
623  else
624  {
625  isRemote = true;
626 
627  if (!KIO::NetAccess::download(url, filename, m_view) )
628  {
629  KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() );
630  return;
631  }
632  }
633 
634  TQFile file(filename);
635  if (file.open(IO_ReadOnly))
636  {
637  // Read OPML feeds list and build TQDom tree.
638  TQDomDocument doc;
639  if (doc.setContent(TQByteArray(file.readAll())))
640  m_view->importFeeds(doc);
641  else
642  KMessageBox::error(m_view, i18n("Could not import the file %1 (no valid OPML)").arg(filename), i18n("OPML Parsing Error") );
643  }
644  else
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"));
646 
647  if (isRemote)
648  KIO::NetAccess::removeTempFile(filename);
649 }
650 
651 void Part::exportFile(const KURL& url)
652 {
653  if (url.isLocalFile())
654  {
655  TQFile file(url.path());
656 
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()),
660  i18n("Export"),
661  i18n("Overwrite"),
662  KStdGuiItem::cancel()) == KMessageBox::No )
663  return;
664 
665  if ( !file.open(IO_WriteOnly) )
666  {
667  KMessageBox::error(m_view, i18n("Access denied: cannot write to file %1").arg(file.name()), i18n("Write Error") );
668  return;
669  }
670 
671  TQTextStream stream(&file);
672  stream.setEncoding(TQTextStream::UnicodeUTF8);
673 
674  stream << m_view->feedListToOPML().toString() << "\n";
675  file.close();
676  }
677  else
678  {
679  KTempFile tmpfile;
680  tmpfile.setAutoDelete(true);
681 
682  TQTextStream stream(tmpfile.file());
683  stream.setEncoding(TQTextStream::UnicodeUTF8);
684 
685  stream << m_view->feedListToOPML().toString() << "\n";
686  tmpfile.close();
687 
688  if (!KIO::NetAccess::upload(tmpfile.name(), url, m_view))
689  KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() );
690  }
691 }
692 
693 void Part::fileImport()
694 {
695  KURL url = KFileDialog::getOpenURL( TQString(),
696  "*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)")
697  +"\n*|" + i18n("All Files") );
698 
699  if (!url.isEmpty())
700  importFile(url);
701 }
702 
703  void Part::fileExport()
704 {
705  KURL url= KFileDialog::getSaveURL( TQString(),
706  "*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)")
707  +"\n*|" + i18n("All Files") );
708 
709  if ( !url.isEmpty() )
710  exportFile(url);
711 }
712 
713 void Part::fileGetFeeds()
714 {
715  /*GetFeeds *gf = new GetFeeds();
716  gf->show();*/
717  //KNS::DownloadDialog::open("akregator/feeds", i18n("Get New Feeds"));
718 }
719 
720 void Part::fileSendArticle(bool attach)
721 {
722  // FIXME: you have to open article to tab to be able to send...
723  TQString title, text;
724 
725  text = m_view->currentFrame()->part()->url().prettyURL();
726  if(text.isEmpty() || text.isNull())
727  return;
728 
729  title = m_view->currentFrame()->title();
730 
731  if(attach) {
732  kapp->invokeMailer("",
733  "",
734  "",
735  title,
736  text,
737  "",
738  text);
739  }
740  else {
741  kapp->invokeMailer("",
742  "",
743  "",
744  title,
745  text);
746  }
747 }
748 
750 {
751  m_view->slotFetchAllFeeds();
752 }
753 
754 void Part::fetchFeedUrl(const TQString&s)
755 {
756  kdDebug() << "fetchFeedURL==" << s << endl;
757 }
758 
759 void Part::addFeedsToGroup(const TQStringList& urls, const TQString& group)
760 {
761  for (TQStringList::ConstIterator it = urls.begin(); it != urls.end(); ++it)
762  {
763  kdDebug() << "Akregator::Part::addFeedToGroup adding feed with URL " << *it << " to group " << group << endl;
764  m_view->addFeedToGroup(*it, group);
765  }
767 }
768 
769 void Part::addFeed()
770 {
771  m_view->slotFeedAdd();
772 }
773 
775 {
776  return new Akregator::AboutData;
777 }
778 
779 void Part::showKNotifyOptions()
780 {
781  KAboutData* about = new Akregator::AboutData;
782  KNotifyDialog::configure(m_view, "akregator_knotify_config", about);
783  delete about;
784 }
785 
787 {
788  if ( KConfigDialog::showDialog( "settings" ) )
789  return;
790 
791  KConfigDialog* dialog = new ConfigDialog( m_view, "settings", Settings::self() );
792 
793  connect( dialog, TQT_SIGNAL(settingsChanged()),
794  this, TQT_SLOT(slotSettingsChanged()) );
795  connect( dialog, TQT_SIGNAL(settingsChanged()),
796  TrayIcon::getInstance(), TQT_SLOT(settingsChanged()) );
797 
798  dialog->show();
799 }
800 
801 void Part::partActivateEvent(KParts::PartActivateEvent* event)
802 {
803  if (factory() && m_mergedPart)
804  {
805  if (event->activated())
806  factory()->addClient(m_mergedPart);
807  else
808  factory()->removeClient(m_mergedPart);
809  }
810 
812 }
813 
814 KParts::Part* Part::hitTest(TQWidget *widget, const TQPoint &globalPos)
815 {
816  bool child = false;
817  TQWidget *me = this->widget();
818  while (widget) {
819  if (widget == me) {
820  child = true;
821  break;
822  }
823  if (!widget) {
824  break;
825  }
826  widget = widget->parentWidget();
827  }
828  if (m_view && m_view->currentFrame() && child) {
829  return m_view->currentFrame()->part();
830  } else {
831  return MyBasePart::hitTest(widget, globalPos);
832  }
833 }
834 
835 void Part::initFonts()
836 {
837  TQStringList fonts = Settings::fonts();
838  if (fonts.isEmpty())
839  {
840  fonts.append(KGlobalSettings::generalFont().family());
841  fonts.append(KGlobalSettings::fixedFont().family());
842  fonts.append(KGlobalSettings::generalFont().family());
843  fonts.append(KGlobalSettings::generalFont().family());
844  fonts.append("0");
845  }
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]);
855 
856  KConfig* conf = Settings::self()->config();
857  conf->setGroup("HTML Settings");
858 
859  KConfig konq("konquerorrc", true, false);
860  konq.setGroup("HTML Settings");
861 
862  if (!conf->hasKey("MinimumFontSize"))
863  {
864  int minfs;
865  if (konq.hasKey("MinimumFontSize"))
866  minfs = konq.readNumEntry("MinimumFontSize");
867  else
868  minfs = KGlobalSettings::generalFont().pointSize();
869  kdDebug() << "Part::initFonts(): set MinimumFontSize to " << minfs << endl;
870  Settings::setMinimumFontSize(minfs);
871  }
872 
873  if (!conf->hasKey("MediumFontSize"))
874  {
875  int medfs;
876  if (konq.hasKey("MediumFontSize"))
877  medfs = konq.readNumEntry("MediumFontSize");
878  else
879  medfs = KGlobalSettings::generalFont().pointSize();
880  kdDebug() << "Part::initFonts(): set MediumFontSize to " << medfs << endl;
881  Settings::setMediumFontSize(medfs);
882  }
883 
884  if (!conf->hasKey("UnderlineLinks"))
885  {
886  bool underline = true;
887  if (konq.hasKey("UnderlineLinks"))
888  underline = konq.readBoolEntry("UnderlineLinks");
889 
890  kdDebug() << "Part::initFonts(): set UnderlineLinks to " << underline << endl;
891  Settings::setUnderlineLinks(underline);
892  }
893 
894 }
895 
896 bool Part::copyFile(const TQString& backup)
897 {
898  TQFile file(m_file);
899 
900  if (file.open(IO_ReadOnly))
901  {
902  TQFile backupFile(backup);
903  if (backupFile.open(IO_WriteOnly))
904  {
905  TQTextStream in(&file);
906  TQTextStream out(&backupFile);
907  while (!in.atEnd())
908  out << in.readLine();
909  backupFile.close();
910  file.close();
911  return true;
912  }
913  else
914  {
915  file.close();
916  return false;
917  }
918  }
919  return false;
920 }
921 
922 static TQString getMyHostName()
923 {
924  char hostNameC[256];
925  // null terminate this C string
926  hostNameC[255] = 0;
927  // set the string to 0 length if gethostname fails
928  if(gethostname(hostNameC, 255))
929  hostNameC[0] = 0;
930  return TQString::fromLocal8Bit(hostNameC);
931 }
932 
933 // taken from KMail
934 bool Part::tryToLock(const TQString& backendName)
935 {
936 // Check and create a lock file to prevent concurrent access to metakit archive
937  TQString appName = kapp->instanceName();
938  if ( appName.isEmpty() )
939  appName = "akregator";
940 
941  TQString programName;
942  const KAboutData *about = kapp->aboutData();
943  if ( about )
944  programName = about->programName();
945  if ( programName.isEmpty() )
946  programName = i18n("Akregator");
947 
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;
956  if ( oldPid == -1 )
957  first_instance = true;
958  // check if the lock file is stale by trying to see if
959  // the other pid is currently running.
960  // Not 100% correct but better safe than sorry
961  else if (hostName == oldHostName && oldPid != getpid()) {
962  if ( kill(oldPid, 0) == -1 )
963  first_instance = ( errno == ESRCH );
964  }
965 
966  if ( !first_instance )
967  {
968  TQString msg;
969  if ( oldHostName == hostName )
970  {
971  // this can only happen if the user is running this application on
972  // different displays on the same machine. All other cases will be
973  // taken care of by KUniqueApplication()
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 );
982  // TQString::arg( st ) only replaces the first occurrence of %1
983  // with st while TQString::arg( s1, s2 ) replacess all occurrences
984  // of %1 with s1 and all occurrences of %2 with s2. So don't
985  // even think about changing the above to .arg( programName ).
986  else
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 );
994  }
995  else
996  {
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 );
1005  else
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 );
1013  }
1014 
1015  KCursorSaver idle( KBusyPtr::idle() );
1016  if ( KMessageBox::No ==
1017  KMessageBox::warningYesNo( 0, msg, TQString(),
1018  i18n("Force Access"),
1019  i18n("Disable Archive")) )
1020  {
1021  return false;
1022  }
1023  }
1024 
1025  config.writeEntry("pid", getpid());
1026  config.writeEntry("hostname", hostName);
1027  config.writeEntry( "appName", appName );
1028  config.writeEntry( "programName", programName );
1029  config.sync();
1030  return true;
1031 }
1032 
1033 
1034 } // namespace Akregator
1035 #include "akregator_part.moc"