Now the systrayicon change it's color when a download is in progress. I simply change...
[kdenetwork.git] / knewsticker / knewsticker.cpp
blob6d2f90cb53bb0abb6054e7c1d83b60e1f49f2cf0
1 /*
2 * knewsticker.cpp
4 * Copyright (c) 2000, 2001 Frerich Raabe <raabe@kde.org>
6 * This program is distributed in the hope that it will be useful, but WITHOUT
7 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
8 * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
9 * accompanying file 'COPYING'.
11 #include "knewsticker.h"
12 #include "newsengine.h"
13 #include "newsscroller.h"
14 #include "configaccess.h"
15 #include "newsiconmgr.h"
16 #include "knewstickerconfig.h"
18 #include <kaboutapplication.h>
19 #include <kapplication.h>
20 #include <karrowbutton.h>
21 #include <kbugreport.h>
22 #include <kdebug.h>
23 #include <kiconloader.h>
24 #include <knotifyclient.h>
25 #include <kprocess.h>
26 #include <kprotocolmanager.h>
27 #include <kstandarddirs.h>
28 #include <kstartupinfo.h>
30 #include <qcursor.h>
31 #include <qlayout.h>
32 #include <qtimer.h>
33 #include <qtooltip.h>
35 #include <dcopclient.h>
37 KNewsTicker::KNewsTicker(const QString &cfgFile, Type t, int actions, QWidget *parent, const char *name)
38 : ConfigIface(), DCOPObject("KNewsTicker"),
39 KPanelApplet(cfgFile, t, actions, parent, name),
40 m_instance(new KInstance("knewsticker")),
41 m_dcopClient(new DCOPClient()),
42 m_cfg(new ConfigAccess(config())),
43 m_newsTimer(new QTimer(this)),
44 m_updateTimer(new QTimer(this)),
45 m_newsIconMgr(NewsIconMgr::self()),
46 m_aboutData(new KAboutData("knewsticker", I18N_NOOP("KNewsTicker"), "v0.2",
47 I18N_NOOP("A news ticker applet."), KAboutData::License_BSD,
48 I18N_NOOP("(c) 2000, 2001 The KNewsTicker developers")))
50 QHBoxLayout *layout = new QHBoxLayout(this);
52 m_contextMenu = new KNewsTickerMenu(this);
53 connect(m_contextMenu, SIGNAL(aboutToHide()),
54 SLOT(slotContextMenuAboutToHide()));
55 setCustomMenu(m_contextMenu);
57 m_arrowButton = new KArrowButton(this);
58 QToolTip::add(m_arrowButton, i18n("Show menu"));
59 connect(m_arrowButton, SIGNAL(clicked()), SLOT(slotArrowButtonPressed()));
60 m_arrowButton->setFocusPolicy(NoFocus);
61 setupArrowButton();
62 layout->addWidget(m_arrowButton);
64 m_scroller = new NewsScroller(this, m_cfg);
65 layout->addWidget(m_scroller);
67 m_dcopClient->registerAs("knewsticker", false);
69 QToolTip::add(m_scroller, QString::null);
70 connect(m_scroller, SIGNAL(contextMenu()), SLOT(slotOpenContextMenu()));
72 connect(m_newsTimer, SIGNAL(timeout()), SLOT(slotUpdateNews()));
74 connect(m_updateTimer, SIGNAL(timeout()), SLOT(slotNotifyOfFailures()));
76 m_aboutData->addAuthor("Frerich Raabe", I18N_NOOP("Original author"),
77 "raabe@kde.org");
78 m_aboutData->addAuthor("Malte Starostik", I18N_NOOP("Hypertext headlines"
79 " and much more"), "malte@kde.org");
80 m_aboutData->addAuthor("Wilco Greven", I18N_NOOP("Mouse wheel support"),
81 "greven@kde.org");
82 m_aboutData->addAuthor("Adriaan de Groot", I18N_NOOP("Rotated scrolling text"
83 " modes"), "adridg@sci.kun.nl");
85 reparseConfig();
87 KStartupInfo::appStarted();
90 KNewsTicker::~KNewsTicker()
92 delete m_cfg;
93 delete m_dcopClient;
96 int KNewsTicker::heightForWidth(int) const
98 return m_scroller->sizeHint().height() + m_arrowButton->height();
101 int KNewsTicker::widthForHeight(int) const
103 return m_scroller->sizeHint().width() + m_arrowButton->width();
106 void KNewsTicker::preferences()
108 KNewsTickerConfig dlg(m_cfg, this);
109 if (dlg.exec() == QDialog::Accepted) {
110 reparseConfig();
114 void KNewsTicker::about()
116 KAboutApplication aboutDlg(m_aboutData);
117 aboutDlg.exec();
120 void KNewsTicker::help()
122 kapp->invokeHelp(QString::null, QString::fromLatin1("knewsticker"));
125 void KNewsTicker::reportBug()
127 KBugReport bugReport(this, true, m_aboutData);
128 bugReport.exec();
131 void KNewsTicker::reparseConfig()
133 m_cfg->reparseConfiguration();
134 m_newsSources.clear();
136 QStringList newsSources = m_cfg->newsSources();
137 QStringList::ConstIterator it = newsSources.begin();
138 QStringList::ConstIterator end = newsSources.end();
139 for (; it != end; ++it) {
140 NewsSourceBase::Ptr ns = m_cfg->newsSource((*it));
141 if (!ns->data().enabled)
142 continue;
144 connect(ns, SIGNAL(newNewsAvailable(const NewsSourceBase::Ptr &, bool)),
145 SLOT(slotNewsSourceUpdated(const NewsSourceBase::Ptr &, bool)));
146 connect(ns, SIGNAL(invalidInput(const NewsSourceBase::Ptr &)),
147 SLOT(slotNewsSourceFailed(const NewsSourceBase::Ptr &)));
148 m_newsSources.append(ns);
151 setOfflineMode(m_cfg->offlineMode());
152 if (!m_cfg->offlineMode())
153 slotUpdateNews();
156 void KNewsTicker::slotUpdateNews()
158 kdDebug(5005) << "slotUpdateNews()" << endl;
159 m_newNews = false;
161 m_updateTimer->start(KProtocolManager::responseTimeout(), true);
163 m_failedNewsUpdates.clear();
164 m_pendingNewsUpdates.clear();
166 m_scroller->clear();
168 NewsSourceBase::List::Iterator it = m_newsSources.begin();
169 NewsSourceBase::List::Iterator end = m_newsSources.end();
170 for (; it != end; ++it) {
171 m_pendingNewsUpdates += (*it)->data().name;
172 (*it)->retrieveNews();
173 (*it)->getIcon();
175 kdDebug(5005) << "m_pendingNewsUpdates = " << m_pendingNewsUpdates.join(",")
176 << endl;
179 void KNewsTicker::slotNewsSourceUpdated(const NewsSourceBase::Ptr &ns,
180 bool newNews)
182 kdDebug(5005) << "slotNewsSourceUpdate()" << endl;
183 if (newNews)
184 m_newNews = true;
186 if (!ns->articles().isEmpty())
187 if (m_cfg->scrollMostRecentOnly())
188 m_scroller->addHeadline(ns->articles().first());
189 else {
190 Article::List articles = ns->articles();
191 Article::List::ConstIterator artIt = articles.begin();
192 Article::List::ConstIterator artEnd = articles.end();
193 for (; artIt != artEnd; ++artIt)
194 m_scroller->addHeadline(*artIt);
197 m_scroller->reset(true);
199 m_pendingNewsUpdates.remove(ns->data().name);
200 kdDebug(5005) << "Updated news source: '" << ns->data().name << "'" << "\n"
201 << "m_pendingNewsUpdates = " << m_pendingNewsUpdates.join(",") << "\n"
202 << "m_failedNewsUpdates = " << m_failedNewsUpdates.join(",")
203 << endl;
205 if (!m_pendingNewsUpdates.isEmpty())
206 return;
208 m_updateTimer->stop();
210 if (!m_failedNewsUpdates.isEmpty())
211 slotNotifyOfFailures();
213 if (m_newNews) {
214 KNotifyClient::Instance instance(m_instance);
215 KNotifyClient::event(winId(), QString::fromLatin1("NewNews"));
219 void KNewsTicker::slotNewsSourceFailed(const NewsSourceBase::Ptr &ns)
221 m_failedNewsUpdates += ns->newsSourceName();
222 slotNewsSourceUpdated(ns);
225 void KNewsTicker::mousePressEvent(QMouseEvent *e)
227 if (e->button() == QMouseEvent::RightButton)
228 slotOpenContextMenu();
231 void KNewsTicker::slotOpenContextMenu()
233 m_contextMenu->setFullMenu(true);
234 m_contextMenu->exec(QCursor::pos());
237 void KNewsTicker::slotArrowButtonPressed()
239 QPoint pos(m_arrowButton->mapToGlobal(QPoint(0, 0)));
240 QSize size(m_arrowButton->size());
242 if (position() == KPanelApplet::pTop) {
243 pos.setY(pos.y() + size.height() + 2);
244 } else if (position() == KPanelApplet::pBottom) {
245 const int y = pos.y() - m_contextMenu->sizeHint().height() - 2;
246 pos.setY(QMAX(0, y));
247 } else if (position() == KPanelApplet::pLeft ) {
248 pos.setX(pos.x() + size.width() + 2);
249 } else { // position() == KPanelApplet::pRight
250 const int x = pos.x() - m_contextMenu->sizeHint().width() - 2;
251 pos.setX(QMAX(0, x));
254 m_contextMenu->setFullMenu(true);
255 m_contextMenu->exec(pos);
258 void KNewsTicker::positionChange(Position)
260 delete layout();
262 QBoxLayout *layout;
264 if (orientation() == Horizontal)
265 layout = new QHBoxLayout(this);
266 else
267 layout = new QVBoxLayout(this);
269 if (m_arrowButton) {
270 layout->addWidget(m_arrowButton);
271 setupArrowButton();
274 layout->addWidget(m_scroller);
277 void KNewsTicker::slotContextMenuAboutToHide()
279 if (m_arrowButton)
280 m_arrowButton->setDown(false);
283 void KNewsTicker::slotNotifyOfFailures()
285 KNotifyClient::Instance instance(m_instance);
286 QString notification = QString::null;
288 if (m_failedNewsUpdates.count() == 1)
289 notification = i18n("<qt>Could not update news site '%1'.<br>"
290 "The supplied resource file is probably invalid or"
291 " broken.</qt>").arg(m_failedNewsUpdates.first());
292 else if (m_failedNewsUpdates.count() > 1 && m_failedNewsUpdates.count() < 8) {
293 notification = i18n("<qt>The following news sites had problems. Their"
294 " resource files are probably invalid or broken.<ul>");
295 QStringList::ConstIterator it = m_failedNewsUpdates.begin();
296 QStringList::ConstIterator end = m_failedNewsUpdates.end();
297 for (; it != end; ++it)
298 notification += QString::fromLatin1("<li>%1</li>").arg(*it);
299 notification += QString::fromLatin1("</ul></qt>");
300 } else
301 notification = i18n("Failed to update several news"
302 " sites. The Internet connection might be cut.");
304 KNotifyClient::event(winId(), QString::fromLatin1("InvalidRDF"), notification);
307 void KNewsTicker::setInterval(const uint interval)
309 m_cfg->setInterval(interval);
310 if ( interval > 4 )
311 m_newsTimer->changeInterval(interval * 60 * 1000);
314 void KNewsTicker::setScrollingSpeed(const uint scrollingSpeed)
316 m_cfg->setScrollingSpeed(scrollingSpeed);
317 m_scroller->reset(true);
320 void KNewsTicker::setMouseWheelSpeed(const uint mouseWheelSpeed)
322 m_cfg->setMouseWheelSpeed(mouseWheelSpeed);
325 void KNewsTicker::setScrollingDirection(const uint scrollingDirection)
327 m_cfg->setScrollingDirection(scrollingDirection);
328 m_scroller->reset(true);
331 void KNewsTicker::setCustomNames(bool customNames)
333 m_cfg->setCustomNames(customNames);
336 void KNewsTicker::setScrollMostRecentOnly(bool scrollMostRecentOnly)
338 m_cfg->setScrollMostRecentOnly(scrollMostRecentOnly);
339 m_scroller->reset(true);
342 void KNewsTicker::setOfflineMode(bool offlineMode)
344 if (offlineMode)
345 m_newsTimer->stop();
346 else
347 if ( m_cfg->interval() > 4 )
348 m_newsTimer->start(m_cfg->interval() * 1000 * 60);
350 m_cfg->setOfflineMode(offlineMode);
353 void KNewsTicker::setUnderlineHighlighted(bool underlineHighlighted)
355 m_cfg->setUnderlineHighlighted(underlineHighlighted);
356 m_scroller->reset(true);
359 void KNewsTicker::setShowIcons(bool showIcons)
361 m_cfg->setShowIcons(showIcons);
362 m_scroller->reset(true);
365 void KNewsTicker::setSlowedScrolling(bool slowedScrolling)
367 m_cfg->setSlowedScrolling(slowedScrolling);
370 void KNewsTicker::setForegroundColor(const QColor &foregroundColor)
372 m_cfg->setForegroundColor(foregroundColor);
373 m_scroller->reset(false);
376 void KNewsTicker::setBackgroundColor(const QColor &backgroundColor)
378 m_cfg->setBackgroundColor(backgroundColor);
379 m_scroller->reset(false);
382 void KNewsTicker::setHighlightedColor(const QColor &highlightedColor)
384 m_cfg->setHighlightedColor(highlightedColor);
385 m_scroller->reset(false);
388 void KNewsTicker::setupArrowButton()
390 ArrowType at;
392 if (orientation() == Horizontal) {
393 m_arrowButton->setFixedWidth(12);
394 m_arrowButton->setMaximumHeight(128);
395 at = (position() == KPanelApplet::pTop ? DownArrow : UpArrow);
396 } else {
397 m_arrowButton->setMaximumWidth(128);
398 m_arrowButton->setFixedHeight(12);
399 at = (position() == KPanelApplet::pLeft ? RightArrow : LeftArrow);
401 m_arrowButton->setArrowType(at);
404 KNewsTickerMenu::KNewsTickerMenu(KNewsTicker *parent, const char *name)
405 : KPopupMenu(parent, name),
406 m_parent(parent),
407 m_fullMenu(false)
409 populateMenu();
412 void KNewsTickerMenu::populateMenu()
414 clear();
417 * Perhaps this hardcoded stuff should be replaced by some kind of
418 * themeing functionality?
420 const QPixmap lookIcon = SmallIcon(QString::fromLatin1("viewmag"));
421 const QPixmap newArticleIcon = SmallIcon(QString::fromLatin1("info"));
422 const QPixmap oldArticleIcon = SmallIcon(QString::fromLatin1("mime_empty"));
423 const QPixmap noArticlesIcon = SmallIcon(QString::fromLatin1("remove"));
425 unsigned int articleIdx = 0;
426 const NewsSourceBase::List sources = m_parent->m_newsSources;
427 NewsSourceBase::List::ConstIterator nIt = sources.begin();
428 for (; nIt != sources.end(); ++nIt) {
429 NewsSourceBase::Ptr ns = *nIt;
431 KPopupMenu *submenu = new KPopupMenu;
432 int checkNewsId = submenu->insertItem(lookIcon, i18n("Check News"), this, SLOT(slotCheckNews(int)), 0, sources.findIndex(ns) + 1000);
433 setItemParameter(checkNewsId, sources.findIndex(ns));
435 submenu->insertSeparator();
437 if (m_parent->m_pendingNewsUpdates.contains(ns->newsSourceName())) {
438 submenu->insertItem(noArticlesIcon, i18n("Currently Being Updated, No Articles Available"));
439 } else if (!ns->articles().isEmpty()) {
440 const Article::List articles = ns->articles();
441 Article::List::ConstIterator artIt = articles.begin();
442 for (; artIt != articles.end(); ++artIt) {
443 Article::Ptr a = *artIt;
444 QString headline = a->headline().replace('&', "&&");
445 int id;
446 if ( a->read() )
447 id = submenu->insertItem(oldArticleIcon, headline, this, SLOT(slotOpenArticle(int)), 0, articleIdx+2000);
448 else
449 id = submenu->insertItem(newArticleIcon, headline, this, SLOT(slotOpenArticle(int)), 0, articleIdx+2000);
450 kdDebug( 5005 ) << "Setting articles index for " << a->headline() << " to " << articleIdx << endl;
451 setItemParameter( id, articleIdx++ );
453 } else {
454 submenu->insertItem(noArticlesIcon, i18n("No Articles Available"));
457 insertItem(ns->icon(), ns->newsSourceName().replace('&', "&&"), submenu);
460 if (!m_parent->m_cfg->newsSources().isEmpty())
461 insertSeparator();
463 insertItem(lookIcon, i18n("Check News"), m_parent, SLOT(slotUpdateNews()));
464 int i = insertItem(i18n("Offline Mode"), this, SLOT(slotToggleOfflineMode()), 0, 4711 );
465 setItemChecked(i, m_parent->m_cfg->offlineMode());
467 if (m_fullMenu) {
468 insertSeparator();
470 const QPixmap logoIcon = SmallIcon(QString::fromLatin1("knewsticker"));
471 const QPixmap helpIcon = SmallIcon(QString::fromLatin1("help"));
472 const QPixmap confIcon = SmallIcon(QString::fromLatin1("configure"));
474 insertTitle(logoIcon, i18n("KNewsTicker"), 0, 0);
476 insertItem(helpIcon, i18n("Help"), this, SLOT(slotShowHelp()));
477 insertItem(logoIcon, i18n("About KNewsTicker"), this, SLOT(slotShowAbout()));
478 insertSeparator();
479 insertItem(confIcon, i18n("Configure KNewsTicker..."), this, SLOT(slotConfigure()));
483 void KNewsTickerMenu::slotShowHelp()
485 m_parent->help();
488 void KNewsTickerMenu::slotShowAbout()
490 m_parent->about();
493 void KNewsTickerMenu::slotConfigure()
495 m_parent->preferences();
498 void KNewsTickerMenu::slotToggleOfflineMode()
500 m_parent->setOfflineMode(!m_parent->m_cfg->offlineMode());
501 setItemChecked( indexOf( 4711 ), !m_parent->m_cfg->offlineMode() );
504 void KNewsTickerMenu::slotCheckNews(int idx)
506 m_parent->m_newsSources[ idx - 1000 ]->retrieveNews();
509 void KNewsTickerMenu::slotOpenArticle(int idx)
511 unsigned int i = idx - 2000;
512 const NewsSourceBase::List sources = m_parent->m_newsSources;
513 NewsSourceBase::List::ConstIterator it = sources.begin();
514 while ( it != sources.end() ) {
515 if ( ( *it )->articles().isEmpty() ) {
516 ++it;
517 continue;
520 if ( i <= ( *it )->articles().count() - 1 )
521 break;
523 i -= ( *it )->articles().count();
525 ++it;
528 if ( it == sources.end() )
529 return;
531 ( *it )->articles()[ i ]->open();
534 extern "C"
536 KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString &configFile)
538 KGlobal::locale()->insertCatalogue(QString::fromLatin1("knewsticker"));
539 return new KNewsTicker(configFile, KPanelApplet::Stretch,
540 KPanelApplet::Preferences | KPanelApplet::About |
541 KPanelApplet::Help | KPanelApplet::ReportBug,
542 parent, "knewsticker");
546 #include "knewsticker.moc"