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 
50 extern "C"
51 {
52  TDE_EXPORT TDECModule *create_kontactknt( TQWidget *parent, const char * )
53  {
54  return new KCMKontactKNT( parent, "kcmkontactknt" );
55  }
56 }
57 
58 NewsEditDialog::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 
91 void NewsEditDialog::modified()
92 {
93  enableButton( KDialogBase::Ok, !title().isEmpty() && !url().isEmpty() );
94 }
95 
96 TQString NewsEditDialog::title() const
97 {
98  return mTitle->text();
99 }
100 
101 TQString NewsEditDialog::url() const
102 {
103  return mURL->text();
104 }
105 
106 class 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 
131 KCMKontactKNT::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 
154 void 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 
176 void 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 
197 void 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 
216 void 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 
233 void 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 
250 void 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 
268 void 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 
287 void 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 
307 void KCMKontactKNT::selectedChanged( TQListViewItem *item )
308 {
309  mRemoveButton->setEnabled( item && item->isSelected() );
310 }
311 
312 void 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 
327 void KCMKontactKNT::modified()
328 {
329  emit changed( true );
330 }
331 
332 void 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 
390 bool KCMKontactKNT::dcopActive() const
391 {
392  TQString error;
393  TQCString appID;
394  bool isGood = true;
395  DCOPClient *client = kapp->dcopClient();
396  if ( !client->isApplicationRegistered( "rssservice" ) ) {
397  if ( TDEApplication::startServiceByDesktopName( "rssservice", TQStringList(), &error, &appID ) )
398  isGood = false;
399  }
400 
401  return isGood;
402 }
403 
404 void 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 
421 void 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 
436 void KCMKontactKNT::defaults()
437 {
438 }
439 
440 const 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"