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>
23 #include <kiconloader.h>
24 #include <knotifyclient.h>
26 #include <kprotocolmanager.h>
27 #include <kstandarddirs.h>
28 #include <kstartupinfo.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
);
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"),
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"),
82 m_aboutData
->addAuthor("Adriaan de Groot", I18N_NOOP("Rotated scrolling text"
83 " modes"), "adridg@sci.kun.nl");
87 KStartupInfo::appStarted();
90 KNewsTicker::~KNewsTicker()
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
) {
114 void KNewsTicker::about()
116 KAboutApplication
aboutDlg(m_aboutData
);
120 void KNewsTicker::help()
122 kapp
->invokeHelp(QString::null
, QString::fromLatin1("knewsticker"));
125 void KNewsTicker::reportBug()
127 KBugReport
bugReport(this, true, m_aboutData
);
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
)
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())
156 void KNewsTicker::slotUpdateNews()
158 kdDebug(5005) << "slotUpdateNews()" << endl
;
161 m_updateTimer
->start(KProtocolManager::responseTimeout(), true);
163 m_failedNewsUpdates
.clear();
164 m_pendingNewsUpdates
.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();
175 kdDebug(5005) << "m_pendingNewsUpdates = " << m_pendingNewsUpdates
.join(",")
179 void KNewsTicker::slotNewsSourceUpdated(const NewsSourceBase::Ptr
&ns
,
182 kdDebug(5005) << "slotNewsSourceUpdate()" << endl
;
186 if (!ns
->articles().isEmpty())
187 if (m_cfg
->scrollMostRecentOnly())
188 m_scroller
->addHeadline(ns
->articles().first());
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(",")
205 if (!m_pendingNewsUpdates
.isEmpty())
208 m_updateTimer
->stop();
210 if (!m_failedNewsUpdates
.isEmpty())
211 slotNotifyOfFailures();
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
)
264 if (orientation() == Horizontal
)
265 layout
= new QHBoxLayout(this);
267 layout
= new QVBoxLayout(this);
270 layout
->addWidget(m_arrowButton
);
274 layout
->addWidget(m_scroller
);
277 void KNewsTicker::slotContextMenuAboutToHide()
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>");
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
);
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
)
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()
392 if (orientation() == Horizontal
) {
393 m_arrowButton
->setFixedWidth(12);
394 m_arrowButton
->setMaximumHeight(128);
395 at
= (position() == KPanelApplet::pTop
? DownArrow
: UpArrow
);
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
),
412 void KNewsTickerMenu::populateMenu()
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('&', "&&");
447 id
= submenu
->insertItem(oldArticleIcon
, headline
, this, SLOT(slotOpenArticle(int)), 0, articleIdx
+2000);
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
++ );
454 submenu
->insertItem(noArticlesIcon
, i18n("No Articles Available"));
457 insertItem(ns
->icon(), ns
->newsSourceName().replace('&', "&&"), submenu
);
460 if (!m_parent
->m_cfg
->newsSources().isEmpty())
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());
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()));
479 insertItem(confIcon
, i18n("Configure KNewsTicker..."), this, SLOT(slotConfigure()));
483 void KNewsTickerMenu::slotShowHelp()
488 void KNewsTickerMenu::slotShowAbout()
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() ) {
520 if ( i
<= ( *it
)->articles().count() - 1 )
523 i
-= ( *it
)->articles().count();
528 if ( it
== sources
.end() )
531 ( *it
)->articles()[ i
]->open();
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"