kcmkontactknt.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 <tqgroupbox.h>
25#include <tqlabel.h>
26#include <tqlayout.h>
27#include <tqlineedit.h>
28#include <tqvaluevector.h>
29#include <tqspinbox.h>
30
31#include <dcopref.h>
32#include <dcopclient.h>
33
34#include <tdeaboutdata.h>
35#include <tdeapplication.h>
36#include <tdeaccelmanager.h>
37#include <tdeconfig.h>
38#include <kdebug.h>
39#include <kdialogbase.h>
40#include <tdelistview.h>
41#include <tdelocale.h>
42#include <kpushbutton.h>
43
44#include "kcmkontactknt.h"
45
46#include "newsfeeds.h"
47
48#include <tdemacros.h>
49
50extern "C"
51{
52 TDE_EXPORT TDECModule *create_kontactknt( TQWidget *parent, const char * )
53 {
54 return new KCMKontactKNT( parent, "kcmkontactknt" );
55 }
56}
57
58NewsEditDialog::NewsEditDialog( const TQString& title, const TQString& url, TQWidget *parent )
59 : KDialogBase( Plain, i18n( "New News Feed" ), Ok | Cancel,
60 Ok, parent, 0, true, true )
61{
62 TQWidget *page = plainPage();
63 TQGridLayout *layout = new TQGridLayout( page, 2, 3, marginHint(),
64 spacingHint() );
65
66 TQLabel *label = new TQLabel( i18n( "Name:" ), page );
67 layout->addWidget( label, 0, 0 );
68
69 mTitle = new TQLineEdit( page );
70 label->setBuddy( mTitle );
71 layout->addMultiCellWidget( mTitle, 0, 0, 1, 2 );
72
73 label = new TQLabel( i18n( "URL:" ), page );
74 layout->addWidget( label, 1, 0 );
75
76 mURL = new TQLineEdit( page );
77 label->setBuddy( mURL );
78 layout->addMultiCellWidget( mURL, 1, 1, 1, 2 );
79
80 mTitle->setText( title );
81 mURL->setText( url );
82 mTitle->setFocus();
83 connect( mTitle, TQ_SIGNAL( textChanged( const TQString& ) ),
84 this, TQ_SLOT( modified() ) );
85 connect( mURL, TQ_SIGNAL( textChanged( const TQString& ) ),
86 this, TQ_SLOT( modified() ) );
87
88 modified();
89}
90
91void NewsEditDialog::modified()
92{
93 enableButton( KDialogBase::Ok, !title().isEmpty() && !url().isEmpty() );
94}
95
96TQString NewsEditDialog::title() const
97{
98 return mTitle->text();
99}
100
101TQString NewsEditDialog::url() const
102{
103 return mURL->text();
104}
105
106class NewsItem : public TQListViewItem
107{
108 public:
109 NewsItem( TQListView *parent, const TQString& title, const TQString& url, bool custom )
110 : TQListViewItem( parent ), mTitle( title ), mUrl( url ), mCustom( custom )
111 {
112 setText( 0, mTitle );
113 }
114
115 NewsItem( TQListViewItem *parent, const TQString& title, const TQString& url, bool custom )
116 : TQListViewItem( parent ), mTitle( title ), mUrl( url ), mCustom( custom )
117 {
118 setText( 0, mTitle );
119 }
120
121 TQString title() const { return mTitle; }
122 TQString url() const { return mUrl; }
123 bool custom() const { return mCustom; }
124
125 private:
126 TQString mTitle;
127 TQString mUrl;
128 bool mCustom;
129};
130
131KCMKontactKNT::KCMKontactKNT( TQWidget *parent, const char *name )
132 : TDECModule( parent, name )
133{
134 initGUI();
135
136 connect( mAllNews, TQ_SIGNAL( currentChanged( TQListViewItem* ) ),
137 this, TQ_SLOT( allCurrentChanged( TQListViewItem* ) ) );
138 connect( mSelectedNews, TQ_SIGNAL( selectionChanged( TQListViewItem* ) ),
139 this, TQ_SLOT( selectedChanged( TQListViewItem* ) ) );
140
141 connect( mUpdateInterval, TQ_SIGNAL( valueChanged( int ) ), TQ_SLOT( modified() ) );
142 connect( mArticleCount, TQ_SIGNAL( valueChanged( int ) ), TQ_SLOT( modified() ) );
143
144 connect( mAddButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( addNews() ) );
145 connect( mRemoveButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( removeNews() ) );
146 connect( mNewButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( newFeed() ) );
147 connect( mDeleteButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( deleteFeed() ) );
148
149 TDEAcceleratorManager::manage( this );
150
151 load();
152}
153
154void KCMKontactKNT::loadNews()
155{
156 TQValueVector<TQListViewItem*> parents;
157 TQValueVector<TQListViewItem*>::Iterator it;
158
159 parents.append( new TQListViewItem( mAllNews, i18n( "Arts" ) ) );
160 parents.append( new TQListViewItem( mAllNews, i18n( "Business" ) ) );
161 parents.append( new TQListViewItem( mAllNews, i18n( "Computers" ) ) );
162 parents.append( new TQListViewItem( mAllNews, i18n( "Misc" ) ) );
163 parents.append( new TQListViewItem( mAllNews, i18n( "Recreation" ) ) );
164 parents.append( new TQListViewItem( mAllNews, i18n( "Society" ) ) );
165
166 for ( it = parents.begin(); it != parents.end(); ++it )
167 (*it)->setSelectable( false );
168
169 for ( int i = 0; i < DEFAULT_NEWSSOURCES; ++i ) {
170 NewsSourceData data = NewsSourceDefault[ i ];
171 new NewsItem( parents[ data.category() ], data.name(), data.url(), false );
172 mFeedMap.insert( data.url(), data.name() );
173 }
174}
175
176void KCMKontactKNT::loadCustomNews()
177{
178 TDEConfig config( "kcmkontactkntrc" );
179 TQMap<TQString, TQString> customFeeds = config.entryMap( "CustomFeeds" );
180 config.setGroup( "CustomFeeds" );
181
182 mCustomItem = new TQListViewItem( mAllNews, i18n( "Custom" ) );
183 mCustomItem->setSelectable( false );
184
185 if ( customFeeds.count() == 0 )
186 mCustomItem->setVisible( false );
187
188 TQMap<TQString, TQString>::Iterator it;
189 for ( it = customFeeds.begin(); it != customFeeds.end(); ++it ) {
190 TQStringList value = config.readListEntry( it.key() );
191 mCustomFeeds.append( new NewsItem( mCustomItem, value[ 0 ], value[ 1 ], true ) );
192 mFeedMap.insert( value[ 1 ], value[ 0 ] );
193 mCustomItem->setVisible( true );
194 }
195}
196
197void KCMKontactKNT::storeCustomNews()
198{
199 TDEConfig config( "kcmkontactkntrc" );
200 config.deleteGroup( "CustomFeeds" );
201 config.setGroup( "CustomFeeds" );
202
203 int counter = 0;
204 TQValueList<NewsItem*>::Iterator it;
205 for ( it = mCustomFeeds.begin(); it != mCustomFeeds.end(); ++it ) {
206 TQStringList value;
207 value << (*it)->title() << (*it)->url();
208 config.writeEntry( TQString::number( counter ), value );
209
210 ++counter;
211 }
212
213 config.sync();
214}
215
216void KCMKontactKNT::addNews()
217{
218 if ( !dcopActive() )
219 return;
220
221 NewsItem *item = dynamic_cast<NewsItem*>( mAllNews->selectedItem() );
222 if ( item == 0 )
223 return;
224
225 DCOPRef service( "rssservice", "RSSService" );
226 service.send( "add(TQString)", item->url() );
227
228 scanNews();
229
230 emit changed( true );
231}
232
233void KCMKontactKNT::removeNews()
234{
235 if ( !dcopActive() )
236 return;
237
238 NewsItem *item = dynamic_cast<NewsItem*>( mSelectedNews->selectedItem() );
239 if ( item == 0 )
240 return;
241
242 DCOPRef service( "rssservice", "RSSService" );
243 service.send( "remove(TQString)", item->url() );
244
245 scanNews();
246
247 emit changed( true );
248}
249
250void KCMKontactKNT::newFeed()
251{
252 NewsEditDialog dlg( "", "", this );
253
254 if ( dlg.exec() ) {
255 NewsItem *item = new NewsItem( mCustomItem, dlg.title(), dlg.url(), true );
256 mCustomFeeds.append( item );
257 mFeedMap.insert( dlg.url(), dlg.title() );
258
259 mCustomItem->setVisible( true );
260 mCustomItem->setOpen( true );
261 mAllNews->ensureItemVisible( item );
262 mAllNews->setSelected( item, true );
263
264 emit changed( true );
265 }
266}
267
268void KCMKontactKNT::deleteFeed()
269{
270 NewsItem *item = dynamic_cast<NewsItem*>( mAllNews->selectedItem() );
271 if ( !item )
272 return;
273
274 if ( mCustomFeeds.find( item ) == mCustomFeeds.end() )
275 return;
276
277 mCustomFeeds.remove( item );
278 mFeedMap.remove( item->url() );
279 delete item;
280
281 if ( mCustomFeeds.count() == 0 )
282 mCustomItem->setVisible( false );
283
284 emit changed( true );
285}
286
287void KCMKontactKNT::scanNews()
288{
289 if ( !dcopActive() )
290 return;
291
292 mSelectedNews->clear();
293
294 DCOPRef service( "rssservice", "RSSService" );
295 TQStringList urls = service.call( "list()" );
296
297 for ( uint i = 0; i < urls.count(); ++i )
298 {
299 TQString url = urls[ i ];
300 TQString feedName = mFeedMap[ url ];
301 if ( feedName.isEmpty() )
302 feedName = url;
303 new NewsItem( mSelectedNews, feedName, url, false );
304 }
305}
306
307void KCMKontactKNT::selectedChanged( TQListViewItem *item )
308{
309 mRemoveButton->setEnabled( item && item->isSelected() );
310}
311
312void KCMKontactKNT::allCurrentChanged( TQListViewItem *item )
313{
314 NewsItem *newsItem = dynamic_cast<NewsItem*>( item );
315
316 bool addState = false;
317 bool delState = false;
318 if ( newsItem && newsItem->isSelected() ) {
319 addState = true;
320 delState = (mCustomFeeds.find( newsItem ) != mCustomFeeds.end());
321 }
322
323 mAddButton->setEnabled( addState );
324 mDeleteButton->setEnabled( delState );
325}
326
327void KCMKontactKNT::modified()
328{
329 emit changed( true );
330}
331
332void KCMKontactKNT::initGUI()
333{
334 TQGridLayout *layout = new TQGridLayout( this, 2, 3, KDialog::marginHint(),
335 KDialog::spacingHint() );
336
337 mAllNews = new TDEListView( this );
338 mAllNews->addColumn( i18n( "All" ) );
339 mAllNews->setRootIsDecorated( true );
340 mAllNews->setFullWidth( true );
341 layout->addWidget( mAllNews, 0, 0 );
342
343 TQVBoxLayout *vbox = new TQVBoxLayout( layout, KDialog::spacingHint() );
344
345 vbox->addStretch();
346 mAddButton = new KPushButton( i18n( "Add" ), this );
347 mAddButton->setEnabled( false );
348 vbox->addWidget( mAddButton );
349 mRemoveButton = new KPushButton( i18n( "Remove" ), this );
350 mRemoveButton->setEnabled( false );
351 vbox->addWidget( mRemoveButton );
352 vbox->addStretch();
353
354 mSelectedNews = new TDEListView( this );
355 mSelectedNews->addColumn( i18n( "Selected" ) );
356 mSelectedNews->setFullWidth( true );
357 layout->addWidget( mSelectedNews, 0, 2 );
358
359 TQGroupBox *box = new TQGroupBox( 0, TQt::Vertical,
360 i18n( "News Feed Settings" ), this );
361
362 TQGridLayout *boxLayout = new TQGridLayout( box->layout(), 2, 3,
363 KDialog::spacingHint() );
364
365 TQLabel *label = new TQLabel( i18n( "Refresh time:" ), box );
366 boxLayout->addWidget( label, 0, 0 );
367
368 mUpdateInterval = new TQSpinBox( 1, 3600, 1, box );
369 mUpdateInterval->setSuffix( " sec." );
370 label->setBuddy( mUpdateInterval );
371 boxLayout->addWidget( mUpdateInterval, 0, 1 );
372
373 label = new TQLabel( i18n( "Number of items shown:" ), box );
374 boxLayout->addWidget( label, 1, 0 );
375
376 mArticleCount = new TQSpinBox( box );
377 label->setBuddy( mArticleCount );
378 boxLayout->addWidget( mArticleCount, 1, 1 );
379
380 mNewButton = new KPushButton( i18n( "New Feed..." ), box );
381 boxLayout->addWidget( mNewButton, 0, 2 );
382
383 mDeleteButton = new KPushButton( i18n( "Delete Feed" ), box );
384 mDeleteButton->setEnabled( false );
385 boxLayout->addWidget( mDeleteButton, 1, 2 );
386
387 layout->addMultiCellWidget( box, 1, 1, 0, 2 );
388}
389
390bool KCMKontactKNT::dcopActive() const
391{
392 TQString error;
393 TQCString appID;
394 bool isGood = true;
395 DCOPClient *client = tdeApp->dcopClient();
396 if ( !client->isApplicationRegistered( "rssservice" ) ) {
397 if ( TDEApplication::startServiceByDesktopName( "rssservice", TQStringList(), &error, &appID ) )
398 isGood = false;
399 }
400
401 return isGood;
402}
403
404void KCMKontactKNT::load()
405{
406 mAllNews->clear();
407
408 loadNews();
409 loadCustomNews();
410 scanNews();
411
412 TDEConfig config( "kcmkontactkntrc" );
413 config.setGroup( "General" );
414
415 mUpdateInterval->setValue( config.readNumEntry( "UpdateInterval", 600 ) );
416 mArticleCount->setValue( config.readNumEntry( "ArticleCount", 4 ) );
417
418 emit changed( false );
419}
420
421void KCMKontactKNT::save()
422{
423 storeCustomNews();
424
425 TDEConfig config( "kcmkontactkntrc" );
426 config.setGroup( "General" );
427
428 config.writeEntry( "UpdateInterval", mUpdateInterval->value() );
429 config.writeEntry( "ArticleCount", mArticleCount->value() );
430
431 config.sync();
432
433 emit changed( false );
434}
435
436void KCMKontactKNT::defaults()
437{
438}
439
440const TDEAboutData* KCMKontactKNT::aboutData() const
441{
442 TDEAboutData *about = new TDEAboutData( I18N_NOOP( "kcmkontactknt" ),
443 I18N_NOOP( "Newsticker Configuration Dialog" ),
444 0, 0, TDEAboutData::License_GPL,
445 I18N_NOOP( "(c) 2003 - 2004 Tobias Koenig" ) );
446
447 about->addAuthor( "Tobias Koenig", 0, "tokoe@kde.org" );
448
449 return about;
450}
451
452#include "kcmkontactknt.moc"