You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1036 lines
31 KiB
C++
1036 lines
31 KiB
C++
/*
|
|
This file is part of Akregator.
|
|
|
|
Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net>
|
|
2005 Frank Osterfeld <frank.osterfeld at kdemail.net>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
As a special exception, permission is given to link this program
|
|
with any edition of TQt, and distribute the resulting executable,
|
|
without including the source code for TQt in the source distribution.
|
|
*/
|
|
#include <dcopclient.h>
|
|
#include <kaboutdata.h>
|
|
#include <kaction.h>
|
|
#include <kactionclasses.h>
|
|
#include <kactioncollection.h>
|
|
#include <kapplication.h>
|
|
#include <kconfig.h>
|
|
#include <kconfigdialog.h>
|
|
#include <kfiledialog.h>
|
|
#include <kglobalsettings.h>
|
|
#include <khtmldefaults.h>
|
|
#include <kinstance.h>
|
|
#include <kmainwindow.h>
|
|
#include <kmessagebox.h>
|
|
#include <knotifyclient.h>
|
|
#include <knotifydialog.h>
|
|
#include <kpopupmenu.h>
|
|
#include <kservice.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kstdaction.h>
|
|
#include <ktempfile.h>
|
|
#include <ktrader.h>
|
|
#include <kio/netaccess.h>
|
|
#include <kparts/browserinterface.h>
|
|
#include <kparts/genericfactory.h>
|
|
#include <kparts/partmanager.h>
|
|
|
|
#include <tqfile.h>
|
|
#include <tqobjectlist.h>
|
|
#include <tqstringlist.h>
|
|
#include <tqtimer.h>
|
|
#include <tqwidgetlist.h>
|
|
#include <tqucomextra_p.h>
|
|
|
|
#include <cerrno>
|
|
#include <sys/types.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include "aboutdata.h"
|
|
#include "actionmanagerimpl.h"
|
|
#include "akregator_part.h"
|
|
#include "akregator_view.h"
|
|
#include "akregatorconfig.h"
|
|
#include "articlefilter.h"
|
|
#include "articleinterceptor.h"
|
|
#include "configdialog.h"
|
|
#include "fetchqueue.h"
|
|
#include "frame.h"
|
|
#include "article.h"
|
|
#include "kernel.h"
|
|
#include "kcursorsaver.h"
|
|
#include "notificationmanager.h"
|
|
#include "pageviewer.h"
|
|
#include "plugin.h"
|
|
#include "pluginmanager.h"
|
|
#include "storage.h"
|
|
#include "storagefactory.h"
|
|
#include "storagefactorydummyimpl.h"
|
|
#include "storagefactoryregistry.h"
|
|
#include "speechclient.h"
|
|
#include "trayicon.h"
|
|
#include "tagset.h"
|
|
#include "tag.h"
|
|
|
|
namespace Akregator {
|
|
|
|
typedef KParts::GenericFactory<Part> AkregatorFactory;
|
|
K_EXPORT_COMPONENT_FACTORY( libakregatorpart, AkregatorFactory )
|
|
|
|
BrowserExtension::BrowserExtension(Part *p, const char *name)
|
|
: KParts::BrowserExtension( p, name )
|
|
{
|
|
m_part=p;
|
|
}
|
|
|
|
void BrowserExtension::saveSettings()
|
|
{
|
|
m_part->saveSettings();
|
|
}
|
|
|
|
class Part::ApplyFiltersInterceptor : public ArticleInterceptor
|
|
{
|
|
public:
|
|
virtual void processArticle(Article& article)
|
|
{
|
|
Filters::ArticleFilterList list = Kernel::self()->articleFilterList();
|
|
for (Filters::ArticleFilterList::ConstIterator it = list.begin(); it != list.end(); ++it)
|
|
(*it).applyTo(article);
|
|
}
|
|
};
|
|
|
|
Part::Part( TQWidget *parentWidget, const char * /*widgetName*/,
|
|
TQObject *parent, const char *name, const TQStringList& )
|
|
: DCOPObject("AkregatorIface")
|
|
, MyBasePart(parent, name)
|
|
, m_standardListLoaded(false)
|
|
, m_shuttingDown(false)
|
|
, m_mergedPart(0)
|
|
, m_view(0)
|
|
, m_backedUpList(false)
|
|
, m_storage(0)
|
|
{
|
|
// we need an instance
|
|
setInstance( AkregatorFactory::instance() );
|
|
|
|
// start knotifyclient if not already started. makes it work for people who doesn't use full kde, according to kmail devels
|
|
KNotifyClient::startDaemon();
|
|
|
|
m_standardFeedList = KGlobal::dirs()->saveLocation("data", "akregator/data") + "/feeds.opml";
|
|
|
|
m_tagSetPath = KGlobal::dirs()->saveLocation("data", "akregator/data") + "/tagset.xml";
|
|
|
|
Backend::StorageFactoryDummyImpl* dummyFactory = new Backend::StorageFactoryDummyImpl();
|
|
Backend::StorageFactoryRegistry::self()->registerFactory(dummyFactory, dummyFactory->key());
|
|
loadPlugins(); // FIXME: also unload them!
|
|
|
|
m_storage = 0;
|
|
Backend::StorageFactory* factory = Backend::StorageFactoryRegistry::self()->getFactory(Settings::archiveBackend());
|
|
|
|
TQStringList storageParams;
|
|
|
|
storageParams.append(TQString("taggingEnabled=%1").arg(Settings::showTaggingGUI() ? "true" : "false"));
|
|
|
|
if (factory != 0)
|
|
{
|
|
if (factory->allowsMultipleWriteAccess())
|
|
{
|
|
m_storage = factory->createStorage(storageParams);
|
|
}
|
|
else
|
|
{
|
|
if (tryToLock(factory->name()))
|
|
m_storage = factory->createStorage(storageParams);
|
|
else
|
|
m_storage = dummyFactory->createStorage(storageParams);
|
|
}
|
|
}
|
|
|
|
|
|
if (!m_storage) // Houston, we have a problem
|
|
{
|
|
m_storage = Backend::StorageFactoryRegistry::self()->getFactory("dummy")->createStorage(storageParams);
|
|
|
|
KMessageBox::error(parentWidget, i18n("Unable to load storage backend plugin \"%1\". No feeds are archived.").arg(Settings::archiveBackend()), i18n("Plugin error") );
|
|
}
|
|
|
|
Filters::ArticleFilterList list;
|
|
list.readConfig(Settings::self()->config());
|
|
Kernel::self()->setArticleFilterList(list);
|
|
|
|
m_applyFiltersInterceptor = new ApplyFiltersInterceptor();
|
|
ArticleInterceptorManager::self()->addInterceptor(m_applyFiltersInterceptor);
|
|
|
|
m_storage->open(true);
|
|
Kernel::self()->setStorage(m_storage);
|
|
Backend::Storage::setInstance(m_storage); // TODO: kill this one
|
|
|
|
loadTagSet(m_tagSetPath);
|
|
|
|
m_actionManager = new ActionManagerImpl(this);
|
|
ActionManager::setInstance(m_actionManager);
|
|
|
|
m_view = new Akregator::View(this, parentWidget, m_actionManager, "akregator_view");
|
|
m_actionManager->initView(m_view);
|
|
m_actionManager->setTagSet(Kernel::self()->tagSet());
|
|
|
|
m_extension = new BrowserExtension(this, "ak_extension");
|
|
|
|
connect(m_view, TQT_SIGNAL(setWindowCaption(const TQString&)), this, TQT_SIGNAL(setWindowCaption(const TQString&)));
|
|
connect(m_view, TQT_SIGNAL(setStatusBarText(const TQString&)), this, TQT_SIGNAL(setStatusBarText(const TQString&)));
|
|
connect(m_view, TQT_SIGNAL(setProgress(int)), m_extension, TQT_SIGNAL(loadingProgress(int)));
|
|
connect(m_view, TQT_SIGNAL(signalCanceled(const TQString&)), this, TQT_SIGNAL(canceled(const TQString&)));
|
|
connect(m_view, TQT_SIGNAL(signalStarted(KIO::Job*)), this, TQT_SIGNAL(started(KIO::Job*)));
|
|
connect(m_view, TQT_SIGNAL(signalCompleted()), this, TQT_SIGNAL(completed()));
|
|
|
|
// notify the part that this is our internal widget
|
|
setWidget(m_view);
|
|
|
|
TrayIcon* trayIcon = new TrayIcon( getMainWindow() );
|
|
TrayIcon::setInstance(trayIcon);
|
|
m_actionManager->initTrayIcon(trayIcon);
|
|
|
|
connect(trayIcon, TQT_SIGNAL(showPart()), this, TQT_SIGNAL(showPart()));
|
|
|
|
if ( isTrayIconEnabled() )
|
|
{
|
|
trayIcon->show();
|
|
NotificationManager::self()->setWidget(trayIcon, instance());
|
|
}
|
|
else
|
|
NotificationManager::self()->setWidget(getMainWindow(), instance());
|
|
|
|
connect( trayIcon, TQT_SIGNAL(quitSelected()),
|
|
kapp, TQT_SLOT(quit())) ;
|
|
|
|
connect( m_view, TQT_SIGNAL(signalUnreadCountChanged(int)), trayIcon, TQT_SLOT(slotSetUnread(int)) );
|
|
|
|
connect(kapp, TQT_SIGNAL(shutDown()), this, TQT_SLOT(slotOnShutdown()));
|
|
|
|
m_autosaveTimer = new TQTimer(this);
|
|
connect(m_autosaveTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotSaveFeedList()));
|
|
m_autosaveTimer->start(5*60*1000); // 5 minutes
|
|
|
|
setXMLFile("akregator_part.rc", true);
|
|
|
|
initFonts();
|
|
|
|
RSS::FileRetriever::setUserAgent(TQString("Akregator/%1; librss/remnants").arg(AKREGATOR_VERSION));
|
|
}
|
|
|
|
void Part::loadPlugins()
|
|
{
|
|
// "[X-KDE-akregator-plugintype] == 'storage'"
|
|
KTrader::OfferList offers = PluginManager::query();
|
|
|
|
for( KTrader::OfferList::ConstIterator it = offers.begin(), end = offers.end(); it != end; ++it )
|
|
{
|
|
Akregator::Plugin* plugin = PluginManager::createFromService(*it);
|
|
if (plugin)
|
|
plugin->init();
|
|
}
|
|
}
|
|
|
|
void Part::slotOnShutdown()
|
|
{
|
|
m_shuttingDown = true;
|
|
|
|
const TQString lockLocation = locateLocal("data", "akregator/lock");
|
|
KSimpleConfig config(lockLocation);
|
|
config.writeEntry("pid", -1);
|
|
config.sync();
|
|
|
|
m_autosaveTimer->stop();
|
|
saveSettings();
|
|
slotSaveFeedList();
|
|
saveTagSet(m_tagSetPath);
|
|
m_view->slotOnShutdown();
|
|
//delete m_view;
|
|
delete TrayIcon::getInstance();
|
|
TrayIcon::setInstance(0L);
|
|
delete m_storage;
|
|
m_storage = 0;
|
|
//delete m_actionManager;
|
|
}
|
|
|
|
void Part::slotSettingsChanged()
|
|
{
|
|
NotificationManager::self()->setWidget(isTrayIconEnabled() ? TrayIcon::getInstance() : getMainWindow(), instance());
|
|
|
|
RSS::FileRetriever::setUseCache(Settings::useHTMLCache());
|
|
|
|
TQStringList fonts;
|
|
fonts.append(Settings::standardFont());
|
|
fonts.append(Settings::fixedFont());
|
|
fonts.append(Settings::sansSerifFont());
|
|
fonts.append(Settings::serifFont());
|
|
fonts.append(Settings::standardFont());
|
|
fonts.append(Settings::standardFont());
|
|
fonts.append("0");
|
|
Settings::setFonts(fonts);
|
|
|
|
if (Settings::minimumFontSize() > Settings::mediumFontSize())
|
|
Settings::setMediumFontSize(Settings::minimumFontSize());
|
|
saveSettings();
|
|
m_view->slotSettingsChanged();
|
|
emit signalSettingsChanged();
|
|
}
|
|
void Part::saveSettings()
|
|
{
|
|
Kernel::self()->articleFilterList().writeConfig(Settings::self()->config());
|
|
m_view->saveSettings();
|
|
}
|
|
|
|
Part::~Part()
|
|
{
|
|
kdDebug() << "Part::~Part() enter" << endl;
|
|
if (!m_shuttingDown)
|
|
slotOnShutdown();
|
|
kdDebug() << "Part::~Part(): leaving" << endl;
|
|
ArticleInterceptorManager::self()->removeInterceptor(m_applyFiltersInterceptor);
|
|
delete m_applyFiltersInterceptor;
|
|
}
|
|
|
|
void Part::readProperties(KConfig* config)
|
|
{
|
|
m_backedUpList = false;
|
|
openStandardFeedList();
|
|
|
|
if(m_view)
|
|
m_view->readProperties(config);
|
|
}
|
|
|
|
void Part::saveProperties(KConfig* config)
|
|
{
|
|
if (m_view)
|
|
{
|
|
slotSaveFeedList();
|
|
m_view->saveProperties(config);
|
|
}
|
|
}
|
|
|
|
bool Part::openURL(const KURL& url)
|
|
{
|
|
m_file = url.path();
|
|
return openFile();
|
|
}
|
|
|
|
void Part::openStandardFeedList()
|
|
{
|
|
if ( !m_standardFeedList.isEmpty() && openURL(m_standardFeedList) )
|
|
m_standardListLoaded = true;
|
|
}
|
|
|
|
TQDomDocument Part::createDefaultFeedList()
|
|
{
|
|
TQDomDocument doc;
|
|
TQDomProcessingInstruction z = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
|
|
doc.appendChild( z );
|
|
|
|
TQDomElement root = doc.createElement( "opml" );
|
|
root.setAttribute("version","1.0");
|
|
doc.appendChild( root );
|
|
|
|
TQDomElement head = doc.createElement( "head" );
|
|
root.appendChild(head);
|
|
|
|
TQDomElement text = doc.createElement( "text" );
|
|
text.appendChild(doc.createTextNode(i18n("Feeds")));
|
|
head.appendChild(text);
|
|
|
|
TQDomElement body = doc.createElement( "body" );
|
|
root.appendChild(body);
|
|
|
|
TQDomElement mainFolder = doc.createElement( "outline" );
|
|
mainFolder.setAttribute("text","KDE");
|
|
body.appendChild(mainFolder);
|
|
|
|
TQDomElement ak = doc.createElement( "outline" );
|
|
ak.setAttribute("text",i18n("Akregator News"));
|
|
ak.setAttribute("xmlUrl","http://akregator.sf.net/rss2.php");
|
|
mainFolder.appendChild(ak);
|
|
|
|
TQDomElement akb = doc.createElement( "outline" );
|
|
akb.setAttribute("text",i18n("Akregator Blog"));
|
|
akb.setAttribute("xmlUrl","http://akregator.pwsp.net/blog/?feed=rss2");
|
|
mainFolder.appendChild(akb);
|
|
|
|
TQDomElement dot = doc.createElement( "outline" );
|
|
dot.setAttribute("text",i18n("KDE Dot News"));
|
|
dot.setAttribute("xmlUrl","http://www.kde.org/dotkdeorg.rdf");
|
|
mainFolder.appendChild(dot);
|
|
|
|
TQDomElement plan = doc.createElement( "outline" );
|
|
plan.setAttribute("text",i18n("Planet KDE"));
|
|
plan.setAttribute("xmlUrl","http://planetkde.org/rss20.xml");
|
|
mainFolder.appendChild(plan);
|
|
|
|
TQDomElement apps = doc.createElement( "outline" );
|
|
apps.setAttribute("text",i18n("KDE Apps"));
|
|
apps.setAttribute("xmlUrl","http://www.kde.org/dot/kde-apps-content.rdf");
|
|
mainFolder.appendChild(apps);
|
|
|
|
TQDomElement look = doc.createElement( "outline" );
|
|
look.setAttribute("text",i18n("KDE Look"));
|
|
look.setAttribute("xmlUrl","http://www.kde.org/kde-look-content.rdf");
|
|
mainFolder.appendChild(look);
|
|
|
|
return doc;
|
|
}
|
|
|
|
bool Part::openFile()
|
|
{
|
|
emit setStatusBarText(i18n("Opening Feed List...") );
|
|
|
|
TQString str;
|
|
// m_file is always local so we can use TQFile on it
|
|
TQFile file(m_file);
|
|
|
|
bool fileExists = file.exists();
|
|
TQString listBackup = m_storage->restoreFeedList();
|
|
|
|
TQDomDocument doc;
|
|
|
|
if (!fileExists)
|
|
{
|
|
doc = createDefaultFeedList();
|
|
}
|
|
else
|
|
{
|
|
if (file.open(IO_ReadOnly))
|
|
{
|
|
// Read OPML feeds list and build TQDom tree.
|
|
TQTextStream stream(&file);
|
|
stream.setEncoding(TQTextStream::UnicodeUTF8); // FIXME not all opmls are in utf8
|
|
str = stream.read();
|
|
file.close();
|
|
}
|
|
|
|
if (!doc.setContent(str))
|
|
{
|
|
|
|
if (file.size() > 0) // don't backup empty files
|
|
{
|
|
TQString backup = m_file + "-backup." + TQString::number(TQDateTime::currentDateTime().toTime_t());
|
|
|
|
copyFile(backup);
|
|
|
|
KMessageBox::error(m_view, i18n("<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") );
|
|
}
|
|
|
|
if (!doc.setContent(listBackup))
|
|
doc = createDefaultFeedList();
|
|
}
|
|
}
|
|
|
|
if (!m_view->loadFeeds(doc))
|
|
{
|
|
if (file.size() > 0) // don't backup empty files
|
|
{
|
|
TQString backup = m_file + "-backup." + TQString::number(TQDateTime::currentDateTime().toTime_t());
|
|
copyFile(backup);
|
|
|
|
KMessageBox::error(m_view, i18n("<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") );
|
|
}
|
|
m_view->loadFeeds(createDefaultFeedList());
|
|
}
|
|
|
|
emit setStatusBarText(TQString());
|
|
|
|
|
|
if( Settings::markAllFeedsReadOnStartup() )
|
|
m_view->slotMarkAllFeedsRead();
|
|
|
|
if (Settings::fetchOnStartup())
|
|
m_view->slotFetchAllFeeds();
|
|
|
|
return true;
|
|
}
|
|
|
|
void Part::slotSaveFeedList()
|
|
{
|
|
// don't save to the standard feed list, when it wasn't completely loaded before
|
|
if (!m_standardListLoaded)
|
|
return;
|
|
|
|
// the first time we overwrite the feed list, we create a backup
|
|
if (!m_backedUpList)
|
|
{
|
|
TQString backup = m_file + "~";
|
|
|
|
if (copyFile(backup))
|
|
m_backedUpList = true;
|
|
}
|
|
|
|
TQString xmlStr = m_view->feedListToOPML().toString();
|
|
m_storage->storeFeedList(xmlStr);
|
|
|
|
TQFile file(m_file);
|
|
if (file.open(IO_WriteOnly) == false)
|
|
{
|
|
//FIXME: allow to save the feedlist into different location -tpr 20041118
|
|
KMessageBox::error(m_view, i18n("Access denied: cannot save feed list (%1)").arg(m_file), i18n("Write error") );
|
|
return;
|
|
}
|
|
|
|
// use TQTextStream to dump the text to the file
|
|
TQTextStream stream(&file);
|
|
stream.setEncoding(TQTextStream::UnicodeUTF8);
|
|
|
|
// Write OPML data file.
|
|
// Archive data files are saved elsewhere.
|
|
|
|
stream << xmlStr << endl;
|
|
|
|
file.close();
|
|
}
|
|
|
|
bool Part::isTrayIconEnabled() const
|
|
{
|
|
return Settings::showTrayIcon();
|
|
}
|
|
|
|
bool Part::mergePart(KParts::Part* part)
|
|
{
|
|
if (part != m_mergedPart)
|
|
{
|
|
if (!factory())
|
|
{
|
|
if (m_mergedPart)
|
|
removeChildClient(m_mergedPart);
|
|
else
|
|
insertChildClient(part);
|
|
}
|
|
else
|
|
{
|
|
if (m_mergedPart) {
|
|
factory()->removeClient(m_mergedPart);
|
|
if (childClients()->containsRef(m_mergedPart))
|
|
removeChildClient(m_mergedPart);
|
|
}
|
|
if (part)
|
|
factory()->addClient(part);
|
|
}
|
|
|
|
m_mergedPart = part;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
TQWidget* Part::getMainWindow()
|
|
{
|
|
// this is a dirty fix to get the main window used for the tray icon
|
|
|
|
TQWidgetList *l = kapp->topLevelWidgets();
|
|
TQWidgetListIt it( *l );
|
|
TQWidget *wid;
|
|
|
|
// check if there is an akregator main window
|
|
while ( (wid = it.current()) != 0 )
|
|
{
|
|
++it;
|
|
//kdDebug() << "win name: " << wid->name() << endl;
|
|
if (TQString(wid->name()) == "akregator_mainwindow")
|
|
{
|
|
delete l;
|
|
return wid;
|
|
}
|
|
}
|
|
// if not, check for kontact main window
|
|
TQWidgetListIt it2( *l );
|
|
while ( (wid = it2.current()) != 0 )
|
|
{
|
|
++it2;
|
|
if (TQString(wid->name()).startsWith("kontact-mainwindow"))
|
|
{
|
|
delete l;
|
|
return wid;
|
|
}
|
|
}
|
|
delete l;
|
|
return 0;
|
|
}
|
|
|
|
void Part::loadTagSet(const TQString& path)
|
|
{
|
|
TQDomDocument doc;
|
|
|
|
TQFile file(path);
|
|
if (file.open(IO_ReadOnly))
|
|
{
|
|
doc.setContent(TQByteArray(file.readAll()));
|
|
file.close();
|
|
}
|
|
// if we can't load the tagset from the xml file, check for the backup in the backend
|
|
if (doc.isNull())
|
|
{
|
|
doc.setContent(m_storage->restoreTagSet());
|
|
}
|
|
|
|
if (!doc.isNull())
|
|
{
|
|
Kernel::self()->tagSet()->readFromXML(doc);
|
|
}
|
|
else
|
|
{
|
|
Kernel::self()->tagSet()->insert(Tag("http://akregator.sf.net/tags/Interesting", i18n("Interesting")));
|
|
}
|
|
}
|
|
|
|
void Part::saveTagSet(const TQString& path)
|
|
{
|
|
TQString xmlStr = Kernel::self()->tagSet()->toXML().toString();
|
|
|
|
m_storage->storeTagSet(xmlStr);
|
|
|
|
TQFile file(path);
|
|
|
|
if ( file.open(IO_WriteOnly) )
|
|
{
|
|
|
|
TQTextStream stream(&file);
|
|
stream.setEncoding(TQTextStream::UnicodeUTF8);
|
|
stream << xmlStr << "\n";
|
|
file.close();
|
|
}
|
|
}
|
|
|
|
void Part::importFile(const KURL& url)
|
|
{
|
|
TQString filename;
|
|
|
|
bool isRemote = false;
|
|
|
|
if (url.isLocalFile())
|
|
filename = url.path();
|
|
else
|
|
{
|
|
isRemote = true;
|
|
|
|
if (!KIO::NetAccess::download(url, filename, m_view) )
|
|
{
|
|
KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() );
|
|
return;
|
|
}
|
|
}
|
|
|
|
TQFile file(filename);
|
|
if (file.open(IO_ReadOnly))
|
|
{
|
|
// Read OPML feeds list and build TQDom tree.
|
|
TQDomDocument doc;
|
|
if (doc.setContent(TQByteArray(file.readAll())))
|
|
m_view->importFeeds(doc);
|
|
else
|
|
KMessageBox::error(m_view, i18n("Could not import the file %1 (no valid OPML)").arg(filename), i18n("OPML Parsing Error") );
|
|
}
|
|
else
|
|
KMessageBox::error(m_view, i18n("The file %1 could not be read, check if it exists or if it is readable for the current user.").arg(filename), i18n("Read Error"));
|
|
|
|
if (isRemote)
|
|
KIO::NetAccess::removeTempFile(filename);
|
|
}
|
|
|
|
void Part::exportFile(const KURL& url)
|
|
{
|
|
if (url.isLocalFile())
|
|
{
|
|
TQFile file(url.path());
|
|
|
|
if ( file.exists() &&
|
|
KMessageBox::questionYesNo(m_view,
|
|
i18n("The file %1 already exists; do you want to overwrite it?").arg(file.name()),
|
|
i18n("Export"),
|
|
i18n("Overwrite"),
|
|
KStdGuiItem::cancel()) == KMessageBox::No )
|
|
return;
|
|
|
|
if ( !file.open(IO_WriteOnly) )
|
|
{
|
|
KMessageBox::error(m_view, i18n("Access denied: cannot write to file %1").arg(file.name()), i18n("Write Error") );
|
|
return;
|
|
}
|
|
|
|
TQTextStream stream(&file);
|
|
stream.setEncoding(TQTextStream::UnicodeUTF8);
|
|
|
|
stream << m_view->feedListToOPML().toString() << "\n";
|
|
file.close();
|
|
}
|
|
else
|
|
{
|
|
KTempFile tmpfile;
|
|
tmpfile.setAutoDelete(true);
|
|
|
|
TQTextStream stream(tmpfile.file());
|
|
stream.setEncoding(TQTextStream::UnicodeUTF8);
|
|
|
|
stream << m_view->feedListToOPML().toString() << "\n";
|
|
tmpfile.close();
|
|
|
|
if (!KIO::NetAccess::upload(tmpfile.name(), url, m_view))
|
|
KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() );
|
|
}
|
|
}
|
|
|
|
void Part::fileImport()
|
|
{
|
|
KURL url = KFileDialog::getOpenURL( TQString(),
|
|
"*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)")
|
|
+"\n*|" + i18n("All Files") );
|
|
|
|
if (!url.isEmpty())
|
|
importFile(url);
|
|
}
|
|
|
|
void Part::fileExport()
|
|
{
|
|
KURL url= KFileDialog::getSaveURL( TQString(),
|
|
"*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)")
|
|
+"\n*|" + i18n("All Files") );
|
|
|
|
if ( !url.isEmpty() )
|
|
exportFile(url);
|
|
}
|
|
|
|
void Part::fileGetFeeds()
|
|
{
|
|
/*GetFeeds *gf = new GetFeeds();
|
|
gf->show();*/
|
|
//KNS::DownloadDialog::open("akregator/feeds", i18n("Get New Feeds"));
|
|
}
|
|
|
|
void Part::fileSendArticle(bool attach)
|
|
{
|
|
// FIXME: you have to open article to tab to be able to send...
|
|
TQString title, text;
|
|
|
|
text = m_view->currentFrame()->part()->url().prettyURL();
|
|
if(text.isEmpty() || text.isNull())
|
|
return;
|
|
|
|
title = m_view->currentFrame()->title();
|
|
|
|
if(attach) {
|
|
kapp->invokeMailer("",
|
|
"",
|
|
"",
|
|
title,
|
|
text,
|
|
"",
|
|
text);
|
|
}
|
|
else {
|
|
kapp->invokeMailer("",
|
|
"",
|
|
"",
|
|
title,
|
|
text);
|
|
}
|
|
}
|
|
|
|
void Part::fetchAllFeeds()
|
|
{
|
|
m_view->slotFetchAllFeeds();
|
|
}
|
|
|
|
void Part::fetchFeedUrl(const TQString&s)
|
|
{
|
|
kdDebug() << "fetchFeedURL==" << s << endl;
|
|
}
|
|
|
|
void Part::addFeedsToGroup(const TQStringList& urls, const TQString& group)
|
|
{
|
|
for (TQStringList::ConstIterator it = urls.begin(); it != urls.end(); ++it)
|
|
{
|
|
kdDebug() << "Akregator::Part::addFeedToGroup adding feed with URL " << *it << " to group " << group << endl;
|
|
m_view->addFeedToGroup(*it, group);
|
|
}
|
|
NotificationManager::self()->slotNotifyFeeds(urls);
|
|
}
|
|
|
|
void Part::addFeed()
|
|
{
|
|
m_view->slotFeedAdd();
|
|
}
|
|
|
|
KAboutData *Part::createAboutData()
|
|
{
|
|
return new Akregator::AboutData;
|
|
}
|
|
|
|
void Part::showKNotifyOptions()
|
|
{
|
|
KAboutData* about = new Akregator::AboutData;
|
|
KNotifyDialog::configure(m_view, "akregator_knotify_config", about);
|
|
delete about;
|
|
}
|
|
|
|
void Part::showOptions()
|
|
{
|
|
if ( KConfigDialog::showDialog( "settings" ) )
|
|
return;
|
|
|
|
KConfigDialog* dialog = new ConfigDialog( m_view, "settings", Settings::self() );
|
|
|
|
connect( dialog, TQT_SIGNAL(settingsChanged()),
|
|
this, TQT_SLOT(slotSettingsChanged()) );
|
|
connect( dialog, TQT_SIGNAL(settingsChanged()),
|
|
TrayIcon::getInstance(), TQT_SLOT(settingsChanged()) );
|
|
|
|
dialog->show();
|
|
}
|
|
|
|
void Part::partActivateEvent(KParts::PartActivateEvent* event)
|
|
{
|
|
if (factory() && m_mergedPart)
|
|
{
|
|
if (event->activated())
|
|
factory()->addClient(m_mergedPart);
|
|
else
|
|
factory()->removeClient(m_mergedPart);
|
|
}
|
|
|
|
MyBasePart::partActivateEvent(event);
|
|
}
|
|
|
|
KParts::Part* Part::hitTest(TQWidget *widget, const TQPoint &globalPos)
|
|
{
|
|
bool child = false;
|
|
TQWidget *me = this->widget();
|
|
while (widget) {
|
|
if (widget == me) {
|
|
child = true;
|
|
break;
|
|
}
|
|
if (!widget) {
|
|
break;
|
|
}
|
|
widget = widget->parentWidget();
|
|
}
|
|
if (m_view && m_view->currentFrame() && child) {
|
|
return m_view->currentFrame()->part();
|
|
} else {
|
|
return MyBasePart::hitTest(widget, globalPos);
|
|
}
|
|
}
|
|
|
|
void Part::initFonts()
|
|
{
|
|
TQStringList fonts = Settings::fonts();
|
|
if (fonts.isEmpty())
|
|
{
|
|
fonts.append(KGlobalSettings::generalFont().family());
|
|
fonts.append(KGlobalSettings::fixedFont().family());
|
|
fonts.append(KGlobalSettings::generalFont().family());
|
|
fonts.append(KGlobalSettings::generalFont().family());
|
|
fonts.append("0");
|
|
}
|
|
Settings::setFonts(fonts);
|
|
if (Settings::standardFont().isEmpty())
|
|
Settings::setStandardFont(fonts[0]);
|
|
if (Settings::fixedFont().isEmpty())
|
|
Settings::setFixedFont(fonts[1]);
|
|
if (Settings::sansSerifFont().isEmpty())
|
|
Settings::setSansSerifFont(fonts[2]);
|
|
if (Settings::serifFont().isEmpty())
|
|
Settings::setSerifFont(fonts[3]);
|
|
|
|
KConfig* conf = Settings::self()->config();
|
|
conf->setGroup("HTML Settings");
|
|
|
|
KConfig konq("konquerorrc", true, false);
|
|
konq.setGroup("HTML Settings");
|
|
|
|
if (!conf->hasKey("MinimumFontSize"))
|
|
{
|
|
int minfs;
|
|
if (konq.hasKey("MinimumFontSize"))
|
|
minfs = konq.readNumEntry("MinimumFontSize");
|
|
else
|
|
minfs = KGlobalSettings::generalFont().pointSize();
|
|
kdDebug() << "Part::initFonts(): set MinimumFontSize to " << minfs << endl;
|
|
Settings::setMinimumFontSize(minfs);
|
|
}
|
|
|
|
if (!conf->hasKey("MediumFontSize"))
|
|
{
|
|
int medfs;
|
|
if (konq.hasKey("MediumFontSize"))
|
|
medfs = konq.readNumEntry("MediumFontSize");
|
|
else
|
|
medfs = KGlobalSettings::generalFont().pointSize();
|
|
kdDebug() << "Part::initFonts(): set MediumFontSize to " << medfs << endl;
|
|
Settings::setMediumFontSize(medfs);
|
|
}
|
|
|
|
if (!conf->hasKey("UnderlineLinks"))
|
|
{
|
|
bool underline = true;
|
|
if (konq.hasKey("UnderlineLinks"))
|
|
underline = konq.readBoolEntry("UnderlineLinks");
|
|
|
|
kdDebug() << "Part::initFonts(): set UnderlineLinks to " << underline << endl;
|
|
Settings::setUnderlineLinks(underline);
|
|
}
|
|
|
|
}
|
|
|
|
bool Part::copyFile(const TQString& backup)
|
|
{
|
|
TQFile file(m_file);
|
|
|
|
if (file.open(IO_ReadOnly))
|
|
{
|
|
TQFile backupFile(backup);
|
|
if (backupFile.open(IO_WriteOnly))
|
|
{
|
|
TQTextStream in(&file);
|
|
TQTextStream out(&backupFile);
|
|
while (!in.atEnd())
|
|
out << in.readLine();
|
|
backupFile.close();
|
|
file.close();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
file.close();
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static TQString getMyHostName()
|
|
{
|
|
char hostNameC[256];
|
|
// null terminate this C string
|
|
hostNameC[255] = 0;
|
|
// set the string to 0 length if gethostname fails
|
|
if(gethostname(hostNameC, 255))
|
|
hostNameC[0] = 0;
|
|
return TQString::fromLocal8Bit(hostNameC);
|
|
}
|
|
|
|
// taken from KMail
|
|
bool Part::tryToLock(const TQString& backendName)
|
|
{
|
|
// Check and create a lock file to prevent concurrent access to metakit archive
|
|
TQString appName = kapp->instanceName();
|
|
if ( appName.isEmpty() )
|
|
appName = "akregator";
|
|
|
|
TQString programName;
|
|
const KAboutData *about = kapp->aboutData();
|
|
if ( about )
|
|
programName = about->programName();
|
|
if ( programName.isEmpty() )
|
|
programName = i18n("Akregator");
|
|
|
|
TQString lockLocation = locateLocal("data", "akregator/lock");
|
|
KSimpleConfig config(lockLocation);
|
|
int oldPid = config.readNumEntry("pid", -1);
|
|
const TQString oldHostName = config.readEntry("hostname");
|
|
const TQString oldAppName = config.readEntry( "appName", appName );
|
|
const TQString oldProgramName = config.readEntry( "programName", programName );
|
|
const TQString hostName = getMyHostName();
|
|
bool first_instance = false;
|
|
if ( oldPid == -1 )
|
|
first_instance = true;
|
|
// check if the lock file is stale by trying to see if
|
|
// the other pid is currently running.
|
|
// Not 100% correct but better safe than sorry
|
|
else if (hostName == oldHostName && oldPid != getpid()) {
|
|
if ( kill(oldPid, 0) == -1 )
|
|
first_instance = ( errno == ESRCH );
|
|
}
|
|
|
|
if ( !first_instance )
|
|
{
|
|
TQString msg;
|
|
if ( oldHostName == hostName )
|
|
{
|
|
// this can only happen if the user is running this application on
|
|
// different displays on the same machine. All other cases will be
|
|
// taken care of by KUniqueApplication()
|
|
if ( oldAppName == appName )
|
|
msg = i18n("<qt>%1 already seems to be running on another display on "
|
|
"this machine. <b>Running %2 more than once is not supported "
|
|
"by the %3 backend and "
|
|
"can cause the loss of archived articles and crashes at startup.</b> "
|
|
"You should disable the archive for now "
|
|
"unless you are sure that %2 is not already running.</qt>")
|
|
.arg( programName, programName, backendName );
|
|
// TQString::arg( st ) only replaces the first occurrence of %1
|
|
// with st while TQString::arg( s1, s2 ) replacess all occurrences
|
|
// of %1 with s1 and all occurrences of %2 with s2. So don't
|
|
// even think about changing the above to .arg( programName ).
|
|
else
|
|
msg = i18n("<qt>%1 seems to be running on another display on this "
|
|
"machine. <b>Running %1 and %2 at the same "
|
|
"time is not supported by the %3 backend and can cause "
|
|
"the loss of archived articles and crashes at startup.</b> "
|
|
"You should disable the archive for now "
|
|
"unless you are sure that %2 is not already running.</qt>")
|
|
.arg( oldProgramName, programName, backendName );
|
|
}
|
|
else
|
|
{
|
|
if ( oldAppName == appName )
|
|
msg = i18n("<qt>%1 already seems to be running on %2. <b>Running %1 more "
|
|
"than once is not supported by the %3 backend and can cause "
|
|
"the loss of archived articles and crashes at startup.</b> "
|
|
"You should disable the archive for now "
|
|
"unless you are sure that it is "
|
|
"not already running on %2.</qt>")
|
|
.arg( programName, oldHostName, backendName );
|
|
else
|
|
msg = i18n("<qt>%1 seems to be running on %3. <b>Running %1 and %2 at the "
|
|
"same time is not supported by the %4 backend and can cause "
|
|
"the loss of archived articles and crashes at startup.</b> "
|
|
"You should disable the archive for now "
|
|
"unless you are sure that %1 is "
|
|
"not running on %3.</qt>")
|
|
.arg( oldProgramName, programName, oldHostName, backendName );
|
|
}
|
|
|
|
KCursorSaver idle( KBusyPtr::idle() );
|
|
if ( KMessageBox::No ==
|
|
KMessageBox::warningYesNo( 0, msg, TQString(),
|
|
i18n("Force Access"),
|
|
i18n("Disable Archive")) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
config.writeEntry("pid", getpid());
|
|
config.writeEntry("hostname", hostName);
|
|
config.writeEntry( "appName", appName );
|
|
config.writeEntry( "programName", programName );
|
|
config.sync();
|
|
return true;
|
|
}
|
|
|
|
|
|
} // namespace Akregator
|
|
#include "akregator_part.moc"
|