You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/knewsticker/knewsticker.cpp

547 lines
15 KiB

/*
* 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 <tqcursor.h>
#include <tqlayout.h>
#include <tqtimer.h>
#include <tqtooltip.h>
#include <dcopclient.h>
KNewsTicker::KNewsTicker(const TQString &cfgFile, Type t, int actions, TQWidget *tqparent, const char *name)
: ConfigIface(), DCOPObject("KNewsTicker"),
KPanelApplet(cfgFile, t, actions, tqparent, name),
m_instance(new KInstance("knewsticker")),
m_dcopClient(new DCOPClient()),
m_cfg(new ConfigAccess(config())),
m_newsTimer(new TQTimer(this)),
m_updateTimer(new TQTimer(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")))
{
TQHBoxLayout *tqlayout = new TQHBoxLayout(this);
m_contextMenu = new KNewsTickerMenu(this);
connect(m_contextMenu, TQT_SIGNAL(aboutToHide()),
TQT_SLOT(slotContextMenuAboutToHide()));
setCustomMenu(m_contextMenu);
m_arrowButton = new KArrowButton(this);
TQToolTip::add(m_arrowButton, i18n("Show menu"));
connect(m_arrowButton, TQT_SIGNAL(clicked()), TQT_SLOT(slotArrowButtonPressed()));
m_arrowButton->setFocusPolicy(TQ_NoFocus);
setupArrowButton();
tqlayout->addWidget(m_arrowButton);
m_scroller = new NewsScroller(this, m_cfg);
tqlayout->addWidget(m_scroller);
m_dcopClient->registerAs("knewsticker", false);
TQToolTip::add(m_scroller, TQString());
connect(m_scroller, TQT_SIGNAL(contextMenu()), TQT_SLOT(slotOpenContextMenu()));
connect(m_newsTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotUpdateNews()));
connect(m_updateTimer, TQT_SIGNAL(timeout()), TQT_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->tqsizeHint().height() + m_arrowButton->height();
}
int KNewsTicker::widthForHeight(int) const
{
return m_scroller->tqsizeHint().width() + m_arrowButton->width();
}
void KNewsTicker::preferences()
{
KNewsTickerConfig dlg(m_cfg, this);
if (dlg.exec() == TQDialog::Accepted) {
reparseConfig();
}
}
void KNewsTicker::about()
{
KAboutApplication aboutDlg(m_aboutData);
aboutDlg.exec();
}
void KNewsTicker::help()
{
kapp->invokeHelp(TQString(), TQString::tqfromLatin1("knewsticker"));
}
void KNewsTicker::reportBug()
{
KBugReport bugReport(this, true, m_aboutData);
bugReport.exec();
}
void KNewsTicker::reparseConfig()
{
m_cfg->reparseConfiguration();
m_newsSources.clear();
TQStringList newsSources = m_cfg->newsSources();
TQStringList::ConstIterator it = newsSources.begin();
TQStringList::ConstIterator end = newsSources.end();
for (; it != end; ++it) {
NewsSourceBase::Ptr ns = m_cfg->newsSource((*it));
if (!ns->data().enabled)
continue;
connect(ns, TQT_SIGNAL(newNewsAvailable(const NewsSourceBase::Ptr &, bool)),
TQT_SLOT(slotNewsSourceUpdated(const NewsSourceBase::Ptr &, bool)));
connect(ns, TQT_SIGNAL(invalidInput(const NewsSourceBase::Ptr &)),
TQT_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(), TQString::tqfromLatin1("NewNews"));
}
}
void KNewsTicker::slotNewsSourceFailed(const NewsSourceBase::Ptr &ns)
{
m_failedNewsUpdates += ns->newsSourceName();
slotNewsSourceUpdated(ns);
}
void KNewsTicker::mousePressEvent(TQMouseEvent *e)
{
if (e->button() == Qt::RightButton)
slotOpenContextMenu();
}
void KNewsTicker::slotOpenContextMenu()
{
m_contextMenu->setFullMenu(true);
m_contextMenu->exec(TQCursor::pos());
}
void KNewsTicker::slotArrowButtonPressed()
{
TQPoint pos(m_arrowButton->mapToGlobal(TQPoint(0, 0)));
TQSize 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->tqsizeHint().height() - 2;
pos.setY(TQMAX(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->tqsizeHint().width() - 2;
pos.setX(TQMAX(0, x));
}
m_contextMenu->setFullMenu(true);
m_contextMenu->exec(pos);
}
void KNewsTicker::positionChange(Position)
{
delete tqlayout();
TQBoxLayout *tqlayout;
if (orientation() ==Qt::Horizontal)
tqlayout = new TQHBoxLayout(this);
else
tqlayout = new TQVBoxLayout(this);
if (m_arrowButton) {
tqlayout->addWidget(m_arrowButton);
setupArrowButton();
}
tqlayout->addWidget(m_scroller);
}
void KNewsTicker::slotContextMenuAboutToHide()
{
if (m_arrowButton)
m_arrowButton->setDown(false);
}
void KNewsTicker::slotNotifyOfFailures()
{
KNotifyClient::Instance instance(m_instance);
TQString notification = TQString();
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>").tqarg(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>");
TQStringList::ConstIterator it = m_failedNewsUpdates.begin();
TQStringList::ConstIterator end = m_failedNewsUpdates.end();
for (; it != end; ++it)
notification += TQString::tqfromLatin1("<li>%1</li>").tqarg(*it);
notification += TQString::tqfromLatin1("</ul></qt>");
} else
notification = i18n("Failed to update several news"
" sites. The Internet connection might be cut.");
KNotifyClient::event(winId(), TQString::tqfromLatin1("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 TQColor &foregroundColor)
{
m_cfg->setForegroundColor(foregroundColor);
m_scroller->reset(false);
}
void KNewsTicker::setBackgroundColor(const TQColor &backgroundColor)
{
m_cfg->setBackgroundColor(backgroundColor);
m_scroller->reset(false);
}
void KNewsTicker::setHighlightedColor(const TQColor &highlightedColor)
{
m_cfg->setHighlightedColor(highlightedColor);
m_scroller->reset(false);
}
void KNewsTicker::setupArrowButton()
{
Qt::ArrowType at;
if (orientation() ==Qt::Horizontal) {
m_arrowButton->setFixedWidth(12);
m_arrowButton->setMaximumHeight(128);
at = (position() == KPanelApplet::pTop ? Qt::DownArrow : Qt::UpArrow);
} else {
m_arrowButton->setMaximumWidth(128);
m_arrowButton->setFixedHeight(12);
at = (position() == KPanelApplet::pLeft ? Qt::RightArrow : Qt::LeftArrow);
}
m_arrowButton->setArrowType(at);
}
KNewsTickerMenu::KNewsTickerMenu(KNewsTicker *tqparent, const char *name)
: KPopupMenu(tqparent, name),
m_parent(tqparent),
m_fullMenu(false)
{
populateMenu();
}
void KNewsTickerMenu::populateMenu()
{
clear();
/*
* Perhaps this hardcoded stuff should be replaced by some kind of
* themeing functionality?
*/
const TQPixmap lookIcon = SmallIcon(TQString::tqfromLatin1("viewmag"));
const TQPixmap newArticleIcon = SmallIcon(TQString::tqfromLatin1("info"));
const TQPixmap oldArticleIcon = SmallIcon(TQString::tqfromLatin1("mime_empty"));
const TQPixmap noArticlesIcon = SmallIcon(TQString::tqfromLatin1("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"), TQT_TQOBJECT(this), TQT_SLOT(slotCheckNews(int)), 0, sources.tqfindIndex(ns) + 1000);
setItemParameter(checkNewsId, sources.tqfindIndex(ns));
submenu->insertSeparator();
if (m_parent->m_pendingNewsUpdates.tqcontains(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;
TQString headline = a->headline().tqreplace('&', "&&");
int id;
if ( a->read() )
id = submenu->insertItem(oldArticleIcon, headline, this, TQT_SLOT(slotOpenArticle(int)), 0, articleIdx+2000);
else
id = submenu->insertItem(newArticleIcon, headline, this, TQT_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().tqreplace('&', "&&"), submenu);
}
if (!m_parent->m_cfg->newsSources().isEmpty())
insertSeparator();
insertItem(lookIcon, i18n("Check News"), m_parent, TQT_SLOT(slotUpdateNews()));
int i = insertItem(i18n("Offline Mode"), this, TQT_SLOT(slotToggleOfflineMode()), 0, 4711 );
setItemChecked(i, m_parent->m_cfg->offlineMode());
if (m_fullMenu) {
insertSeparator();
const TQPixmap logoIcon = SmallIcon(TQString::tqfromLatin1("knewsticker"));
const TQPixmap helpIcon = SmallIcon(TQString::tqfromLatin1("help"));
const TQPixmap confIcon = SmallIcon(TQString::tqfromLatin1("configure"));
insertTitle(logoIcon, i18n("KNewsTicker"), 0, 0);
insertItem(helpIcon, i18n("Help"), this, TQT_SLOT(slotShowHelp()));
insertItem(logoIcon, i18n("About KNewsTicker"), this, TQT_SLOT(slotShowAbout()));
insertSeparator();
insertItem(confIcon, i18n("Configure KNewsTicker..."), this, TQT_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(TQWidget *tqparent, const TQString &configFile)
{
KGlobal::locale()->insertCatalogue(TQString::tqfromLatin1("knewsticker"));
return new KNewsTicker(configFile, KPanelApplet::Stretch,
KPanelApplet::Preferences | KPanelApplet::About |
KPanelApplet::Help | KPanelApplet::ReportBug,
tqparent, "knewsticker");
}
}
#include "knewsticker.moc"