summaryrefslogtreecommitdiffstats
path: root/knewsticker/knewsticker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'knewsticker/knewsticker.cpp')
-rw-r--r--knewsticker/knewsticker.cpp546
1 files changed, 546 insertions, 0 deletions
diff --git a/knewsticker/knewsticker.cpp b/knewsticker/knewsticker.cpp
new file mode 100644
index 00000000..6d2f90cb
--- /dev/null
+++ b/knewsticker/knewsticker.cpp
@@ -0,0 +1,546 @@
+/*
+ * knewsticker.cpp
+ *
+ * Copyright (c) 2000, 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "knewsticker.h"
+#include "newsengine.h"
+#include "newsscroller.h"
+#include "configaccess.h"
+#include "newsiconmgr.h"
+#include "knewstickerconfig.h"
+
+#include <kaboutapplication.h>
+#include <kapplication.h>
+#include <karrowbutton.h>
+#include <kbugreport.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <knotifyclient.h>
+#include <kprocess.h>
+#include <kprotocolmanager.h>
+#include <kstandarddirs.h>
+#include <kstartupinfo.h>
+
+#include <qcursor.h>
+#include <qlayout.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+
+#include <dcopclient.h>
+
+KNewsTicker::KNewsTicker(const QString &cfgFile, Type t, int actions, QWidget *parent, const char *name)
+ : ConfigIface(), DCOPObject("KNewsTicker"),
+ KPanelApplet(cfgFile, t, actions, parent, name),
+ m_instance(new KInstance("knewsticker")),
+ m_dcopClient(new DCOPClient()),
+ m_cfg(new ConfigAccess(config())),
+ m_newsTimer(new QTimer(this)),
+ m_updateTimer(new QTimer(this)),
+ m_newsIconMgr(NewsIconMgr::self()),
+ m_aboutData(new KAboutData("knewsticker", I18N_NOOP("KNewsTicker"), "v0.2",
+ I18N_NOOP("A news ticker applet."), KAboutData::License_BSD,
+ I18N_NOOP("(c) 2000, 2001 The KNewsTicker developers")))
+{
+ QHBoxLayout *layout = new QHBoxLayout(this);
+
+ m_contextMenu = new KNewsTickerMenu(this);
+ connect(m_contextMenu, SIGNAL(aboutToHide()),
+ SLOT(slotContextMenuAboutToHide()));
+ setCustomMenu(m_contextMenu);
+
+ m_arrowButton = new KArrowButton(this);
+ QToolTip::add(m_arrowButton, i18n("Show menu"));
+ connect(m_arrowButton, SIGNAL(clicked()), SLOT(slotArrowButtonPressed()));
+ m_arrowButton->setFocusPolicy(NoFocus);
+ setupArrowButton();
+ layout->addWidget(m_arrowButton);
+
+ m_scroller = new NewsScroller(this, m_cfg);
+ layout->addWidget(m_scroller);
+
+ m_dcopClient->registerAs("knewsticker", false);
+
+ QToolTip::add(m_scroller, QString::null);
+ connect(m_scroller, SIGNAL(contextMenu()), SLOT(slotOpenContextMenu()));
+
+ connect(m_newsTimer, SIGNAL(timeout()), SLOT(slotUpdateNews()));
+
+ connect(m_updateTimer, SIGNAL(timeout()), SLOT(slotNotifyOfFailures()));
+
+ m_aboutData->addAuthor("Frerich Raabe", I18N_NOOP("Original author"),
+ "raabe@kde.org");
+ m_aboutData->addAuthor("Malte Starostik", I18N_NOOP("Hypertext headlines"
+ " and much more"), "malte@kde.org");
+ m_aboutData->addAuthor("Wilco Greven", I18N_NOOP("Mouse wheel support"),
+ "greven@kde.org");
+ m_aboutData->addAuthor("Adriaan de Groot", I18N_NOOP("Rotated scrolling text"
+ " modes"), "adridg@sci.kun.nl");
+
+ reparseConfig();
+
+ KStartupInfo::appStarted();
+}
+
+KNewsTicker::~KNewsTicker()
+{
+ delete m_cfg;
+ delete m_dcopClient;
+}
+
+int KNewsTicker::heightForWidth(int) const
+{
+ return m_scroller->sizeHint().height() + m_arrowButton->height();
+}
+
+int KNewsTicker::widthForHeight(int) const
+{
+ return m_scroller->sizeHint().width() + m_arrowButton->width();
+}
+
+void KNewsTicker::preferences()
+{
+ KNewsTickerConfig dlg(m_cfg, this);
+ if (dlg.exec() == QDialog::Accepted) {
+ reparseConfig();
+ }
+}
+
+void KNewsTicker::about()
+{
+ KAboutApplication aboutDlg(m_aboutData);
+ aboutDlg.exec();
+}
+
+void KNewsTicker::help()
+{
+ kapp->invokeHelp(QString::null, QString::fromLatin1("knewsticker"));
+}
+
+void KNewsTicker::reportBug()
+{
+ KBugReport bugReport(this, true, m_aboutData);
+ bugReport.exec();
+}
+
+void KNewsTicker::reparseConfig()
+{
+ m_cfg->reparseConfiguration();
+ m_newsSources.clear();
+
+ QStringList newsSources = m_cfg->newsSources();
+ QStringList::ConstIterator it = newsSources.begin();
+ QStringList::ConstIterator end = newsSources.end();
+ for (; it != end; ++it) {
+ NewsSourceBase::Ptr ns = m_cfg->newsSource((*it));
+ if (!ns->data().enabled)
+ continue;
+
+ connect(ns, SIGNAL(newNewsAvailable(const NewsSourceBase::Ptr &, bool)),
+ SLOT(slotNewsSourceUpdated(const NewsSourceBase::Ptr &, bool)));
+ connect(ns, SIGNAL(invalidInput(const NewsSourceBase::Ptr &)),
+ SLOT(slotNewsSourceFailed(const NewsSourceBase::Ptr &)));
+ m_newsSources.append(ns);
+ }
+
+ setOfflineMode(m_cfg->offlineMode());
+ if (!m_cfg->offlineMode())
+ slotUpdateNews();
+}
+
+void KNewsTicker::slotUpdateNews()
+{
+ kdDebug(5005) << "slotUpdateNews()" << endl;
+ m_newNews = false;
+
+ m_updateTimer->start(KProtocolManager::responseTimeout(), true);
+
+ m_failedNewsUpdates.clear();
+ m_pendingNewsUpdates.clear();
+
+ m_scroller->clear();
+
+ NewsSourceBase::List::Iterator it = m_newsSources.begin();
+ NewsSourceBase::List::Iterator end = m_newsSources.end();
+ for (; it != end; ++it) {
+ m_pendingNewsUpdates += (*it)->data().name;
+ (*it)->retrieveNews();
+ (*it)->getIcon();
+ }
+ kdDebug(5005) << "m_pendingNewsUpdates = " << m_pendingNewsUpdates.join(",")
+ << endl;
+}
+
+void KNewsTicker::slotNewsSourceUpdated(const NewsSourceBase::Ptr &ns,
+ bool newNews)
+{
+ kdDebug(5005) << "slotNewsSourceUpdate()" << endl;
+ if (newNews)
+ m_newNews = true;
+
+ if (!ns->articles().isEmpty())
+ if (m_cfg->scrollMostRecentOnly())
+ m_scroller->addHeadline(ns->articles().first());
+ else {
+ Article::List articles = ns->articles();
+ Article::List::ConstIterator artIt = articles.begin();
+ Article::List::ConstIterator artEnd = articles.end();
+ for (; artIt != artEnd; ++artIt)
+ m_scroller->addHeadline(*artIt);
+ }
+
+ m_scroller->reset(true);
+
+ m_pendingNewsUpdates.remove(ns->data().name);
+ kdDebug(5005) << "Updated news source: '" << ns->data().name << "'" << "\n"
+ << "m_pendingNewsUpdates = " << m_pendingNewsUpdates.join(",") << "\n"
+ << "m_failedNewsUpdates = " << m_failedNewsUpdates.join(",")
+ << endl;
+
+ if (!m_pendingNewsUpdates.isEmpty())
+ return;
+
+ m_updateTimer->stop();
+
+ if (!m_failedNewsUpdates.isEmpty())
+ slotNotifyOfFailures();
+
+ if (m_newNews) {
+ KNotifyClient::Instance instance(m_instance);
+ KNotifyClient::event(winId(), QString::fromLatin1("NewNews"));
+ }
+}
+
+void KNewsTicker::slotNewsSourceFailed(const NewsSourceBase::Ptr &ns)
+{
+ m_failedNewsUpdates += ns->newsSourceName();
+ slotNewsSourceUpdated(ns);
+}
+
+void KNewsTicker::mousePressEvent(QMouseEvent *e)
+{
+ if (e->button() == QMouseEvent::RightButton)
+ slotOpenContextMenu();
+}
+
+void KNewsTicker::slotOpenContextMenu()
+{
+ m_contextMenu->setFullMenu(true);
+ m_contextMenu->exec(QCursor::pos());
+}
+
+void KNewsTicker::slotArrowButtonPressed()
+{
+ QPoint pos(m_arrowButton->mapToGlobal(QPoint(0, 0)));
+ QSize size(m_arrowButton->size());
+
+ if (position() == KPanelApplet::pTop) {
+ pos.setY(pos.y() + size.height() + 2);
+ } else if (position() == KPanelApplet::pBottom) {
+ const int y = pos.y() - m_contextMenu->sizeHint().height() - 2;
+ pos.setY(QMAX(0, y));
+ } else if (position() == KPanelApplet::pLeft ) {
+ pos.setX(pos.x() + size.width() + 2);
+ } else { // position() == KPanelApplet::pRight
+ const int x = pos.x() - m_contextMenu->sizeHint().width() - 2;
+ pos.setX(QMAX(0, x));
+ }
+
+ m_contextMenu->setFullMenu(true);
+ m_contextMenu->exec(pos);
+}
+
+void KNewsTicker::positionChange(Position)
+{
+ delete layout();
+
+ QBoxLayout *layout;
+
+ if (orientation() == Horizontal)
+ layout = new QHBoxLayout(this);
+ else
+ layout = new QVBoxLayout(this);
+
+ if (m_arrowButton) {
+ layout->addWidget(m_arrowButton);
+ setupArrowButton();
+ }
+
+ layout->addWidget(m_scroller);
+}
+
+void KNewsTicker::slotContextMenuAboutToHide()
+{
+ if (m_arrowButton)
+ m_arrowButton->setDown(false);
+}
+
+void KNewsTicker::slotNotifyOfFailures()
+{
+ KNotifyClient::Instance instance(m_instance);
+ QString notification = QString::null;
+
+ if (m_failedNewsUpdates.count() == 1)
+ notification = i18n("<qt>Could not update news site '%1'.<br>"
+ "The supplied resource file is probably invalid or"
+ " broken.</qt>").arg(m_failedNewsUpdates.first());
+ else if (m_failedNewsUpdates.count() > 1 && m_failedNewsUpdates.count() < 8) {
+ notification = i18n("<qt>The following news sites had problems. Their"
+ " resource files are probably invalid or broken.<ul>");
+ QStringList::ConstIterator it = m_failedNewsUpdates.begin();
+ QStringList::ConstIterator end = m_failedNewsUpdates.end();
+ for (; it != end; ++it)
+ notification += QString::fromLatin1("<li>%1</li>").arg(*it);
+ notification += QString::fromLatin1("</ul></qt>");
+ } else
+ notification = i18n("Failed to update several news"
+ " sites. The Internet connection might be cut.");
+
+ KNotifyClient::event(winId(), QString::fromLatin1("InvalidRDF"), notification);
+}
+
+void KNewsTicker::setInterval(const uint interval)
+{
+ m_cfg->setInterval(interval);
+ if ( interval > 4 )
+ m_newsTimer->changeInterval(interval * 60 * 1000);
+}
+
+void KNewsTicker::setScrollingSpeed(const uint scrollingSpeed)
+{
+ m_cfg->setScrollingSpeed(scrollingSpeed);
+ m_scroller->reset(true);
+}
+
+void KNewsTicker::setMouseWheelSpeed(const uint mouseWheelSpeed)
+{
+ m_cfg->setMouseWheelSpeed(mouseWheelSpeed);
+}
+
+void KNewsTicker::setScrollingDirection(const uint scrollingDirection)
+{
+ m_cfg->setScrollingDirection(scrollingDirection);
+ m_scroller->reset(true);
+}
+
+void KNewsTicker::setCustomNames(bool customNames)
+{
+ m_cfg->setCustomNames(customNames);
+}
+
+void KNewsTicker::setScrollMostRecentOnly(bool scrollMostRecentOnly)
+{
+ m_cfg->setScrollMostRecentOnly(scrollMostRecentOnly);
+ m_scroller->reset(true);
+}
+
+void KNewsTicker::setOfflineMode(bool offlineMode)
+{
+ if (offlineMode)
+ m_newsTimer->stop();
+ else
+ if ( m_cfg->interval() > 4 )
+ m_newsTimer->start(m_cfg->interval() * 1000 * 60);
+
+ m_cfg->setOfflineMode(offlineMode);
+}
+
+void KNewsTicker::setUnderlineHighlighted(bool underlineHighlighted)
+{
+ m_cfg->setUnderlineHighlighted(underlineHighlighted);
+ m_scroller->reset(true);
+}
+
+void KNewsTicker::setShowIcons(bool showIcons)
+{
+ m_cfg->setShowIcons(showIcons);
+ m_scroller->reset(true);
+}
+
+void KNewsTicker::setSlowedScrolling(bool slowedScrolling)
+{
+ m_cfg->setSlowedScrolling(slowedScrolling);
+}
+
+void KNewsTicker::setForegroundColor(const QColor &foregroundColor)
+{
+ m_cfg->setForegroundColor(foregroundColor);
+ m_scroller->reset(false);
+}
+
+void KNewsTicker::setBackgroundColor(const QColor &backgroundColor)
+{
+ m_cfg->setBackgroundColor(backgroundColor);
+ m_scroller->reset(false);
+}
+
+void KNewsTicker::setHighlightedColor(const QColor &highlightedColor)
+{
+ m_cfg->setHighlightedColor(highlightedColor);
+ m_scroller->reset(false);
+}
+
+void KNewsTicker::setupArrowButton()
+{
+ ArrowType at;
+
+ if (orientation() == Horizontal) {
+ m_arrowButton->setFixedWidth(12);
+ m_arrowButton->setMaximumHeight(128);
+ at = (position() == KPanelApplet::pTop ? DownArrow : UpArrow);
+ } else {
+ m_arrowButton->setMaximumWidth(128);
+ m_arrowButton->setFixedHeight(12);
+ at = (position() == KPanelApplet::pLeft ? RightArrow : LeftArrow);
+ }
+ m_arrowButton->setArrowType(at);
+}
+
+KNewsTickerMenu::KNewsTickerMenu(KNewsTicker *parent, const char *name)
+ : KPopupMenu(parent, name),
+ m_parent(parent),
+ m_fullMenu(false)
+{
+ populateMenu();
+}
+
+void KNewsTickerMenu::populateMenu()
+{
+ clear();
+
+ /*
+ * Perhaps this hardcoded stuff should be replaced by some kind of
+ * themeing functionality?
+ */
+ const QPixmap lookIcon = SmallIcon(QString::fromLatin1("viewmag"));
+ const QPixmap newArticleIcon = SmallIcon(QString::fromLatin1("info"));
+ const QPixmap oldArticleIcon = SmallIcon(QString::fromLatin1("mime_empty"));
+ const QPixmap noArticlesIcon = SmallIcon(QString::fromLatin1("remove"));
+
+ unsigned int articleIdx = 0;
+ const NewsSourceBase::List sources = m_parent->m_newsSources;
+ NewsSourceBase::List::ConstIterator nIt = sources.begin();
+ for (; nIt != sources.end(); ++nIt) {
+ NewsSourceBase::Ptr ns = *nIt;
+
+ KPopupMenu *submenu = new KPopupMenu;
+ int checkNewsId = submenu->insertItem(lookIcon, i18n("Check News"), this, SLOT(slotCheckNews(int)), 0, sources.findIndex(ns) + 1000);
+ setItemParameter(checkNewsId, sources.findIndex(ns));
+
+ submenu->insertSeparator();
+
+ if (m_parent->m_pendingNewsUpdates.contains(ns->newsSourceName())) {
+ submenu->insertItem(noArticlesIcon, i18n("Currently Being Updated, No Articles Available"));
+ } else if (!ns->articles().isEmpty()) {
+ const Article::List articles = ns->articles();
+ Article::List::ConstIterator artIt = articles.begin();
+ for (; artIt != articles.end(); ++artIt) {
+ Article::Ptr a = *artIt;
+ QString headline = a->headline().replace('&', "&&");
+ int id;
+ if ( a->read() )
+ id = submenu->insertItem(oldArticleIcon, headline, this, SLOT(slotOpenArticle(int)), 0, articleIdx+2000);
+ else
+ id = submenu->insertItem(newArticleIcon, headline, this, SLOT(slotOpenArticle(int)), 0, articleIdx+2000);
+ kdDebug( 5005 ) << "Setting articles index for " << a->headline() << " to " << articleIdx << endl;
+ setItemParameter( id, articleIdx++ );
+ }
+ } else {
+ submenu->insertItem(noArticlesIcon, i18n("No Articles Available"));
+ }
+
+ insertItem(ns->icon(), ns->newsSourceName().replace('&', "&&"), submenu);
+ }
+
+ if (!m_parent->m_cfg->newsSources().isEmpty())
+ insertSeparator();
+
+ insertItem(lookIcon, i18n("Check News"), m_parent, SLOT(slotUpdateNews()));
+ int i = insertItem(i18n("Offline Mode"), this, SLOT(slotToggleOfflineMode()), 0, 4711 );
+ setItemChecked(i, m_parent->m_cfg->offlineMode());
+
+ if (m_fullMenu) {
+ insertSeparator();
+
+ const QPixmap logoIcon = SmallIcon(QString::fromLatin1("knewsticker"));
+ const QPixmap helpIcon = SmallIcon(QString::fromLatin1("help"));
+ const QPixmap confIcon = SmallIcon(QString::fromLatin1("configure"));
+
+ insertTitle(logoIcon, i18n("KNewsTicker"), 0, 0);
+
+ insertItem(helpIcon, i18n("Help"), this, SLOT(slotShowHelp()));
+ insertItem(logoIcon, i18n("About KNewsTicker"), this, SLOT(slotShowAbout()));
+ insertSeparator();
+ insertItem(confIcon, i18n("Configure KNewsTicker..."), this, SLOT(slotConfigure()));
+ }
+}
+
+void KNewsTickerMenu::slotShowHelp()
+{
+ m_parent->help();
+}
+
+void KNewsTickerMenu::slotShowAbout()
+{
+ m_parent->about();
+}
+
+void KNewsTickerMenu::slotConfigure()
+{
+ m_parent->preferences();
+}
+
+void KNewsTickerMenu::slotToggleOfflineMode()
+{
+ m_parent->setOfflineMode(!m_parent->m_cfg->offlineMode());
+ setItemChecked( indexOf( 4711 ), !m_parent->m_cfg->offlineMode() );
+}
+
+void KNewsTickerMenu::slotCheckNews(int idx)
+{
+ m_parent->m_newsSources[ idx - 1000 ]->retrieveNews();
+}
+
+void KNewsTickerMenu::slotOpenArticle(int idx)
+{
+ unsigned int i = idx - 2000;
+ const NewsSourceBase::List sources = m_parent->m_newsSources;
+ NewsSourceBase::List::ConstIterator it = sources.begin();
+ while ( it != sources.end() ) {
+ if ( ( *it )->articles().isEmpty() ) {
+ ++it;
+ continue;
+ }
+
+ if ( i <= ( *it )->articles().count() - 1 )
+ break;
+
+ i -= ( *it )->articles().count();
+
+ ++it;
+ }
+
+ if ( it == sources.end() )
+ return;
+
+ ( *it )->articles()[ i ]->open();
+}
+
+extern "C"
+{
+ KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString &configFile)
+ {
+ KGlobal::locale()->insertCatalogue(QString::fromLatin1("knewsticker"));
+ return new KNewsTicker(configFile, KPanelApplet::Stretch,
+ KPanelApplet::Preferences | KPanelApplet::About |
+ KPanelApplet::Help | KPanelApplet::ReportBug,
+ parent, "knewsticker");
+ }
+}
+
+#include "knewsticker.moc"