newsticker/summarywidget.cpp
1/*
2 This file is part of Kontact.
3 Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19 As a special exception, permission is given to link this program
20 with any edition of TQt, and distribute the resulting executable,
21 without including the source code for TQt in the source distribution.
22*/
23
24#include <tqclipboard.h>
25#include <tqeventloop.h>
26#include <tqhbox.h>
27#include <tqlayout.h>
28#include <tqpixmap.h>
29#include <tqpopupmenu.h>
30#include <tqcursor.h>
31
32#include <dcopclient.h>
33#include <tdeapplication.h>
34#include <kcharsets.h>
35#include <tdeconfig.h>
36#include <kdebug.h>
37#include <tdeglobal.h>
38#include <kiconloader.h>
39#include <tdelocale.h>
40#include <kurllabel.h>
41
42#include "summarywidget.h"
43
44SummaryWidget::SummaryWidget( TQWidget *parent, const char *name )
45 : Kontact::Summary( parent, name ),
46 DCOPObject( "NewsTickerPlugin" ), mLayout( 0 ), mFeedCounter( 0 )
47{
48 TQVBoxLayout *vlay = new TQVBoxLayout( this, 3, 3 );
49
50 TQPixmap icon = TDEGlobal::iconLoader()->loadIcon( "kontact_news",
51 TDEIcon::Desktop, TDEIcon::SizeMedium );
52
53 TQWidget *header = createHeader( this, icon, i18n( "News Feeds" ) );
54 vlay->addWidget( header );
55
56 TQString error;
57 TQCString appID;
58
59 bool dcopAvailable = true;
60 if ( !tdeApp->dcopClient()->isApplicationRegistered( "rssservice" ) ) {
61 if ( TDEApplication::startServiceByDesktopName( "rssservice", TQStringList(), &error, &appID ) ) {
62 TQLabel *label = new TQLabel( i18n( "No rss dcop service available.\nYou need rssservice to use this plugin." ), this );
63 vlay->addWidget( label, TQt::AlignHCenter );
64 dcopAvailable = false;
65 }
66 }
67
68 mBaseWidget = new TQWidget( this, "baseWidget" );
69 vlay->addWidget( mBaseWidget );
70
71 connect( &mTimer, TQ_SIGNAL( timeout() ), this, TQ_SLOT( updateDocuments() ) );
72
73 readConfig();
74
75 connectDCOPSignal( 0, 0, "documentUpdateError(DCOPRef,int)", "documentUpdateError(DCOPRef, int)", false );
76
77 if ( dcopAvailable )
78 initDocuments();
79
80 connectDCOPSignal( 0, 0, "added(TQString)", "documentAdded(TQString)", false );
81 connectDCOPSignal( 0, 0, "removed(TQString)", "documentRemoved(TQString)", false );
82}
83
84int SummaryWidget::summaryHeight() const
85{
86 return ( mFeeds.count() == 0 ? 1 : mFeeds.count() );
87}
88
89void SummaryWidget::documentAdded( TQString )
90{
91 initDocuments();
92}
93
94void SummaryWidget::documentRemoved( TQString )
95{
96 initDocuments();
97}
98
99void SummaryWidget::configChanged()
100{
101 readConfig();
102
103 updateView();
104}
105
106void SummaryWidget::readConfig()
107{
108 TDEConfig config( "kcmkontactkntrc" );
109 config.setGroup( "General" );
110
111 mUpdateInterval = config.readNumEntry( "UpdateInterval", 600 );
112 mArticleCount = config.readNumEntry( "ArticleCount", 4 );
113}
114
115void SummaryWidget::initDocuments()
116{
117 mFeeds.clear();
118
119 DCOPRef dcopCall( "rssservice", "RSSService" );
120 TQStringList urls;
121 dcopCall.call( "list()" ).get( urls );
122
123 if ( urls.isEmpty() ) { // add default
124 urls.append( "http://www.kde.org/dotkdeorg.rdf" );
125 dcopCall.send( "add(TQString)", urls[ 0 ] );
126 }
127
128 TQStringList::Iterator it;
129 for ( it = urls.begin(); it != urls.end(); ++it ) {
130 DCOPRef feedRef = dcopCall.call( "document(TQString)", *it );
131
132 Feed feed;
133 feed.ref = feedRef;
134 feedRef.call( "title()" ).get( feed.title );
135 feedRef.call( "link()" ).get( feed.url );
136 feedRef.call( "pixmap()" ).get( feed.logo );
137 mFeeds.append( feed );
138
139 disconnectDCOPSignal( "rssservice", feedRef.obj(), "documentUpdated(DCOPRef)", 0 );
140 connectDCOPSignal( "rssservice", feedRef.obj(), "documentUpdated(DCOPRef)",
141 "documentUpdated(DCOPRef)", false );
142
143 if ( tqApp )
144 tqApp->eventLoop()->processEvents( TQEventLoop::ExcludeUserInput |
145 TQEventLoop::ExcludeSocketNotifiers );
146 }
147
148 updateDocuments();
149}
150
151void SummaryWidget::updateDocuments()
152{
153 mTimer.stop();
154
155 FeedList::Iterator it;
156 for ( it = mFeeds.begin(); it != mFeeds.end(); ++it )
157 (*it).ref.send( "refresh()" );
158
159 mTimer.start( 1000 * mUpdateInterval );
160}
161
162void SummaryWidget::documentUpdated( DCOPRef feedRef )
163{
164 ArticleMap map;
165
166 int numArticles = feedRef.call( "count()" );
167 for ( int i = 0; i < numArticles; ++i ) {
168 DCOPRef artRef = feedRef.call( "article(int)", i );
169 TQString title, url;
170
171 if ( tqApp )
172 tqApp->eventLoop()->processEvents( TQEventLoop::ExcludeUserInput |
173 TQEventLoop::ExcludeSocketNotifiers );
174
175 artRef.call( "title()" ).get( title );
176 artRef.call( "link()" ).get( url );
177
178 TQPair<TQString, KURL> article(title, KURL( url ));
179 map.append( article );
180 }
181
182 FeedList::Iterator it;
183 for ( it = mFeeds.begin(); it != mFeeds.end(); ++it )
184 if ( (*it).ref.obj() == feedRef.obj() ) {
185 (*it).map = map;
186 if ( (*it).title.isEmpty() )
187 feedRef.call( "title()" ).get( (*it).title );
188 if ( (*it).url.isEmpty() )
189 feedRef.call( "link()" ).get( (*it).url );
190 if ( (*it).logo.isNull() )
191 feedRef.call( "pixmap()" ).get( (*it).logo );
192 }
193
194 mFeedCounter++;
195 if ( mFeedCounter == mFeeds.count() ) {
196 mFeedCounter = 0;
197 updateView();
198 }
199}
200
201void SummaryWidget::updateView()
202{
203 mLabels.setAutoDelete( true );
204 mLabels.clear();
205 mLabels.setAutoDelete( false );
206
207 delete mLayout;
208 mLayout = new TQVBoxLayout( mBaseWidget, 3 );
209
210 TQFont boldFont;
211 boldFont.setBold( true );
212 boldFont.setPointSize( boldFont.pointSize() + 2 );
213
214 FeedList::Iterator it;
215 for ( it = mFeeds.begin(); it != mFeeds.end(); ++it ) {
216 TQHBox *hbox = new TQHBox( mBaseWidget );
217 mLayout->addWidget( hbox );
218
219 // icon
220 KURLLabel *urlLabel = new KURLLabel( hbox );
221 urlLabel->setURL( (*it).url );
222 urlLabel->setPixmap( (*it).logo );
223 urlLabel->setMaximumSize( urlLabel->minimumSizeHint() );
224 mLabels.append( urlLabel );
225
226 connect( urlLabel, TQ_SIGNAL( leftClickedURL( const TQString& ) ),
227 tdeApp, TQ_SLOT( invokeBrowser( const TQString& ) ) );
228 connect( urlLabel, TQ_SIGNAL( rightClickedURL( const TQString& ) ),
229 this, TQ_SLOT( rmbMenu( const TQString& ) ) );
230
231 // header
232 TQLabel *label = new TQLabel( hbox );
233 label->setText( KCharsets::resolveEntities( (*it).title ) );
234 label->setAlignment( AlignLeft|AlignVCenter );
235 label->setFont( boldFont );
236 label->setIndent( 6 );
237 label->setMaximumSize( label->minimumSizeHint() );
238 mLabels.append( label );
239
240 hbox->setMaximumWidth( hbox->minimumSizeHint().width() );
241 hbox->show();
242
243 // articles
244 ArticleMap articles = (*it).map;
245 ArticleMap::Iterator artIt;
246 int numArticles = 0;
247 for ( artIt = articles.begin(); artIt != articles.end() && numArticles < mArticleCount; ++artIt ) {
248 urlLabel = new KURLLabel( (*artIt).second.url(), (*artIt).first, mBaseWidget );
249 urlLabel->installEventFilter( this );
250 //TODO: RichText causes too much horizontal space between articles
251 //urlLabel->setTextFormat( RichText );
252 mLabels.append( urlLabel );
253 mLayout->addWidget( urlLabel );
254
255 connect( urlLabel, TQ_SIGNAL( leftClickedURL( const TQString& ) ),
256 tdeApp, TQ_SLOT( invokeBrowser( const TQString& ) ) );
257 connect( urlLabel, TQ_SIGNAL( rightClickedURL( const TQString& ) ),
258 this, TQ_SLOT( rmbMenu( const TQString& ) ) );
259
260
261 numArticles++;
262 }
263 }
264
265 for ( TQLabel *label = mLabels.first(); label; label = mLabels.next() )
266 label->show();
267}
268
269void SummaryWidget::documentUpdateError( DCOPRef feedRef, int errorCode )
270{
271 kdDebug() << " error while updating document, error code: " << errorCode << endl;
272 FeedList::Iterator it;
273 for ( it = mFeeds.begin(); it != mFeeds.end(); ++it ) {
274 if ( (*it).ref.obj() == feedRef.obj() ) {
275 mFeeds.remove( it );
276 break;
277 }
278 }
279
280 if ( mFeedCounter == mFeeds.count() ) {
281 mFeedCounter = 0;
282 updateView();
283 }
284
285}
286
287TQStringList SummaryWidget::configModules() const
288{
289 return "kcmkontactknt.desktop";
290}
291
292void SummaryWidget::updateSummary( bool )
293{
294 updateDocuments();
295}
296
297void SummaryWidget::rmbMenu( const TQString& url )
298{
299 TQPopupMenu menu;
300 menu.insertItem( i18n( "Copy URL to Clipboard" ) );
301 int id = menu.exec( TQCursor::pos() );
302 if ( id != -1 )
303 tdeApp->clipboard()->setText( url, TQClipboard::Clipboard );
304}
305
306bool SummaryWidget::eventFilter( TQObject *obj, TQEvent* e )
307{
308 if ( obj->inherits( "KURLLabel" ) ) {
309 KURLLabel* label = static_cast<KURLLabel*>( obj );
310 if ( e->type() == TQEvent::Enter )
311 emit message( label->url() );
312 if ( e->type() == TQEvent::Leave )
313 emit message( TQString() );
314 }
315
316 return Kontact::Summary::eventFilter( obj, e );
317}
318
319#include "summarywidget.moc"