kontact

summarywidget.cpp

00001 /*
00002     This file is part of Kontact.
00003     Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>
00004 
00005     This program is free software; you can redistribute it and/or modify
00006     it under the terms of the GNU General Public License as published by
00007     the Free Software Foundation; either version 2 of the License, or
00008     (at your option) any later version.
00009 
00010     This program is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00013     GNU General Public License for more details.
00014 
00015     You should have received a copy of the GNU General Public License
00016     along with this program; if not, write to the Free Software
00017     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018 
00019     As a special exception, permission is given to link this program
00020     with any edition of Qt, and distribute the resulting executable,
00021     without including the source code for Qt in the source distribution.
00022 */
00023 
00024 #include <qclipboard.h>
00025 #include <qeventloop.h>
00026 #include <qhbox.h>
00027 #include <qlayout.h>
00028 #include <qpixmap.h>
00029 #include <qpopupmenu.h>
00030 #include <qcursor.h>
00031 
00032 #include <dcopclient.h>
00033 #include <kapplication.h>
00034 #include <kcharsets.h>
00035 #include <kconfig.h>
00036 #include <kdebug.h>
00037 #include <kglobal.h>
00038 #include <kiconloader.h>
00039 #include <klocale.h>
00040 #include <kurllabel.h>
00041 
00042 #include "summarywidget.h"
00043 
00044 SummaryWidget::SummaryWidget( QWidget *parent, const char *name )
00045   : Kontact::Summary( parent, name ),
00046     DCOPObject( "NewsTickerPlugin" ), mLayout( 0 ), mFeedCounter( 0 )
00047 {
00048   QVBoxLayout *vlay = new QVBoxLayout( this, 3, 3 );
00049 
00050   QPixmap icon = KGlobal::iconLoader()->loadIcon( "kontact_news",
00051                                                   KIcon::Desktop, KIcon::SizeMedium );
00052 
00053   QWidget *header = createHeader( this, icon, i18n( "News Feeds" ) );
00054   vlay->addWidget( header );
00055 
00056   QString error;
00057   QCString appID;
00058 
00059   bool dcopAvailable = true;
00060   if ( !kapp->dcopClient()->isApplicationRegistered( "rssservice" ) ) {
00061     if ( KApplication::startServiceByDesktopName( "rssservice", QStringList(), &error, &appID ) ) {
00062       QLabel *label = new QLabel( i18n( "No rss dcop service available.\nYou need rssservice to use this plugin." ), this );
00063       vlay->addWidget( label, Qt::AlignHCenter );
00064       dcopAvailable = false;
00065     }
00066   }
00067 
00068   mBaseWidget = new QWidget( this, "baseWidget" );
00069   vlay->addWidget( mBaseWidget );
00070 
00071   connect( &mTimer, SIGNAL( timeout() ), this, SLOT( updateDocuments() ) );
00072 
00073   readConfig();
00074 
00075   connectDCOPSignal( 0, 0, "documentUpdateError(DCOPRef,int)", "documentUpdateError(DCOPRef, int)", false );
00076 
00077   if ( dcopAvailable )
00078     initDocuments();
00079 
00080   connectDCOPSignal( 0, 0, "added(QString)", "documentAdded(QString)", false );
00081   connectDCOPSignal( 0, 0, "removed(QString)", "documentRemoved(QString)", false );
00082 }
00083 
00084 int SummaryWidget::summaryHeight() const
00085 {
00086   return ( mFeeds.count() == 0 ? 1 : mFeeds.count() );
00087 }
00088 
00089 void SummaryWidget::documentAdded( QString )
00090 {
00091   initDocuments();
00092 }
00093 
00094 void SummaryWidget::documentRemoved( QString )
00095 {
00096   initDocuments();
00097 }
00098 
00099 void SummaryWidget::configChanged()
00100 {
00101   readConfig();
00102 
00103   updateView();
00104 }
00105 
00106 void SummaryWidget::readConfig()
00107 {
00108   KConfig config( "kcmkontactkntrc" );
00109   config.setGroup( "General" );
00110 
00111   mUpdateInterval = config.readNumEntry( "UpdateInterval", 600 );
00112   mArticleCount = config.readNumEntry( "ArticleCount", 4 );
00113 }
00114 
00115 void SummaryWidget::initDocuments()
00116 {
00117   mFeeds.clear();
00118 
00119   DCOPRef dcopCall( "rssservice", "RSSService" );
00120   QStringList urls;
00121   dcopCall.call( "list()" ).get( urls );
00122 
00123   if ( urls.isEmpty() ) { // add default
00124     urls.append( "http://www.kde.org/dotkdeorg.rdf" );
00125     dcopCall.send( "add(QString)", urls[ 0 ] );
00126   }
00127 
00128   QStringList::Iterator it;
00129   for ( it = urls.begin(); it != urls.end(); ++it ) {
00130     DCOPRef feedRef = dcopCall.call( "document(QString)", *it );
00131 
00132     Feed feed;
00133     feed.ref = feedRef;
00134     feedRef.call( "title()" ).get( feed.title );
00135     feedRef.call( "link()" ).get( feed.url );
00136     feedRef.call( "pixmap()" ).get( feed.logo );
00137     mFeeds.append( feed );
00138 
00139     disconnectDCOPSignal( "rssservice", feedRef.obj(), "documentUpdated(DCOPRef)", 0 );
00140     connectDCOPSignal( "rssservice", feedRef.obj(), "documentUpdated(DCOPRef)",
00141                        "documentUpdated(DCOPRef)", false );
00142 
00143     if ( qApp )
00144       qApp->eventLoop()->processEvents( QEventLoop::ExcludeUserInput |
00145                                     QEventLoop::ExcludeSocketNotifiers );
00146   }
00147 
00148   updateDocuments();
00149 }
00150 
00151 void SummaryWidget::updateDocuments()
00152 {
00153   mTimer.stop();
00154 
00155   FeedList::Iterator it;
00156   for ( it = mFeeds.begin(); it != mFeeds.end(); ++it )
00157     (*it).ref.send( "refresh()" );
00158 
00159   mTimer.start( 1000 * mUpdateInterval );
00160 }
00161 
00162 void SummaryWidget::documentUpdated( DCOPRef feedRef )
00163 {
00164   ArticleMap map;
00165 
00166   int numArticles = feedRef.call( "count()" );
00167   for ( int i = 0; i < numArticles; ++i ) {
00168     DCOPRef artRef = feedRef.call( "article(int)", i );
00169     QString title, url;
00170 
00171     if ( qApp )
00172       qApp->eventLoop()->processEvents( QEventLoop::ExcludeUserInput |
00173                                         QEventLoop::ExcludeSocketNotifiers );
00174 
00175     artRef.call( "title()" ).get( title );
00176     artRef.call( "link()" ).get( url );
00177 
00178     QPair<QString, KURL> article(title, KURL( url ));
00179     map.append( article );
00180   }
00181 
00182   FeedList::Iterator it;
00183   for ( it = mFeeds.begin(); it != mFeeds.end(); ++it )
00184     if ( (*it).ref.obj() == feedRef.obj() ) {
00185       (*it).map = map;
00186       if ( (*it).title.isEmpty() )
00187         feedRef.call( "title()" ).get( (*it).title );
00188       if ( (*it).url.isEmpty() )
00189         feedRef.call( "link()" ).get( (*it).url );
00190       if ( (*it).logo.isNull() )
00191         feedRef.call( "pixmap()" ).get( (*it).logo );
00192     }
00193 
00194   mFeedCounter++;
00195   if ( mFeedCounter == mFeeds.count() ) {
00196     mFeedCounter = 0;
00197     updateView();
00198   }
00199 }
00200 
00201 void SummaryWidget::updateView()
00202 {
00203   mLabels.setAutoDelete( true );
00204   mLabels.clear();
00205   mLabels.setAutoDelete( false );
00206 
00207   delete mLayout;
00208   mLayout = new QVBoxLayout( mBaseWidget, 3 );
00209 
00210   QFont boldFont;
00211   boldFont.setBold( true );
00212   boldFont.setPointSize( boldFont.pointSize() + 2 );
00213 
00214   FeedList::Iterator it;
00215   for ( it = mFeeds.begin(); it != mFeeds.end(); ++it ) {
00216     QHBox *hbox = new QHBox( mBaseWidget );
00217     mLayout->addWidget( hbox );
00218 
00219     // icon
00220     KURLLabel *urlLabel = new KURLLabel( hbox );
00221     urlLabel->setURL( (*it).url );
00222     urlLabel->setPixmap( (*it).logo );
00223     urlLabel->setMaximumSize( urlLabel->minimumSizeHint() );
00224     mLabels.append( urlLabel );
00225 
00226     connect( urlLabel, SIGNAL( leftClickedURL( const QString& ) ),
00227              kapp, SLOT( invokeBrowser( const QString& ) ) );
00228     connect( urlLabel, SIGNAL( rightClickedURL( const QString& ) ),
00229              this, SLOT( rmbMenu( const QString& ) ) );
00230 
00231     // header
00232     QLabel *label = new QLabel( hbox );
00233     label->setText( KCharsets::resolveEntities( (*it).title ) );
00234     label->setAlignment( AlignLeft|AlignVCenter );
00235     label->setFont( boldFont );
00236     label->setIndent( 6 );
00237     label->setMaximumSize( label->minimumSizeHint() );
00238     mLabels.append( label );
00239 
00240     hbox->setMaximumWidth( hbox->minimumSizeHint().width() );
00241     hbox->show();
00242 
00243     // articles
00244     ArticleMap articles = (*it).map;
00245     ArticleMap::Iterator artIt;
00246     int numArticles = 0;
00247     for ( artIt = articles.begin(); artIt != articles.end() && numArticles < mArticleCount; ++artIt ) {
00248       urlLabel = new KURLLabel( (*artIt).second.url(), (*artIt).first, mBaseWidget );
00249       urlLabel->installEventFilter( this );
00250       //TODO: RichText causes too much horizontal space between articles
00251       //urlLabel->setTextFormat( RichText );
00252       mLabels.append( urlLabel );
00253       mLayout->addWidget( urlLabel );
00254 
00255       connect( urlLabel, SIGNAL( leftClickedURL( const QString& ) ),
00256                kapp, SLOT( invokeBrowser( const QString& ) ) );
00257       connect( urlLabel, SIGNAL( rightClickedURL( const QString& ) ),
00258                this, SLOT( rmbMenu( const QString& ) ) );
00259 
00260 
00261       numArticles++;
00262     }
00263   }
00264 
00265   for ( QLabel *label = mLabels.first(); label; label = mLabels.next() )
00266     label->show();
00267 }
00268 
00269 void SummaryWidget::documentUpdateError( DCOPRef feedRef, int errorCode )
00270 {
00271   kdDebug() << " error while updating document, error code: " << errorCode << endl;
00272   FeedList::Iterator it;
00273   for ( it = mFeeds.begin(); it != mFeeds.end(); ++it ) {
00274     if ( (*it).ref.obj() == feedRef.obj() ) {
00275       mFeeds.remove( it );
00276       break;
00277     }
00278   }
00279 
00280   if ( mFeedCounter == mFeeds.count() ) {
00281     mFeedCounter = 0;
00282     updateView();
00283   }
00284 
00285 }
00286 
00287 QStringList SummaryWidget::configModules() const
00288 {
00289   return "kcmkontactknt.desktop";
00290 }
00291 
00292 void SummaryWidget::updateSummary( bool )
00293 {
00294   updateDocuments();
00295 }
00296 
00297 void SummaryWidget::rmbMenu( const QString& url )
00298 {
00299   QPopupMenu menu;
00300   menu.insertItem( i18n( "Copy URL to Clipboard" ) );
00301   int id = menu.exec( QCursor::pos() );
00302   if ( id != -1 )
00303     kapp->clipboard()->setText( url, QClipboard::Clipboard );
00304 }
00305 
00306 bool SummaryWidget::eventFilter( QObject *obj, QEvent* e )
00307 {
00308   if ( obj->inherits( "KURLLabel" ) ) {
00309     KURLLabel* label = static_cast<KURLLabel*>( obj );
00310     if ( e->type() == QEvent::Enter )
00311       emit message( label->url() );
00312     if ( e->type() == QEvent::Leave )
00313       emit message( QString::null );
00314   }
00315 
00316   return Kontact::Summary::eventFilter( obj, e );
00317 }
00318 
00319 #include "summarywidget.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys