kaddressbook

viewmanager.cpp
1 /*
2  This file is part of KAddressBook.
3  Copyright (c) 2002 Mike Pilone <mpilone@slac.com>
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 <tqfile.h>
25 #include <tqlayout.h>
26 #include <tqwidgetstack.h>
27 
28 #include <libtdepim/kvcarddrag.h>
29 #include <tdeabc/addressbook.h>
30 #include <tdeabc/vcardconverter.h>
31 #include <tdeactionclasses.h>
32 #include <tdeconfig.h>
33 #include <kdebug.h>
34 #include <tdeversion.h>
35 #include <kiconloader.h>
36 #include <tdelocale.h>
37 #include <tdemessagebox.h>
38 #include <tdemultipledrag.h>
39 #include <ktempdir.h>
40 #include <ktrader.h>
41 #include <kurldrag.h>
42 
43 #include "addviewdialog.h"
44 #include "addresseeutil.h"
45 #include "core.h"
46 #include "filtereditdialog.h"
47 #include "filterselectionwidget.h"
48 #include "kabprefs.h"
49 
50 #include "viewmanager.h"
51 
52 ViewManager::ViewManager( KAB::Core *core, TQWidget *parent, const char *name )
53  : TQWidget( parent, name ), mCore( core ), mActiveView( 0 ),
54  mFilterSelectionWidget( 0 )
55 {
56  initGUI();
57  initActions();
58 
59  mViewDict.setAutoDelete( true );
60 
61  createViewFactories();
62 }
63 
64 ViewManager::~ViewManager()
65 {
66  unloadViews();
67  mViewFactoryDict.clear();
68 }
69 
70 void ViewManager::restoreSettings()
71 {
72  mViewNameList = KABPrefs::instance()->viewNames();
73  TQString activeViewName = KABPrefs::instance()->currentView();
74 
75  mActionSelectView->setItems( mViewNameList );
76 
77  // Filter
78  mFilterList = Filter::restore( mCore->config(), "Filter" );
79  mFilterSelectionWidget->setItems( filterNames() );
80  mFilterSelectionWidget->setCurrentItem( KABPrefs::instance()->currentFilter() );
81 
82  // Tell the views to reread their config, since they may have
83  // been modified by global settings
84  TQDictIterator<KAddressBookView> it( mViewDict );
85  for ( it.toFirst(); it.current(); ++it ) {
86  TDEConfigGroupSaver saver( mCore->config(), it.currentKey() );
87  it.current()->readConfig( mCore->config() );
88  }
89 
90  setActiveView( activeViewName );
91 
92  mActionDeleteView->setEnabled( mViewNameList.count() > 1 );
93 }
94 
95 void ViewManager::saveSettings()
96 {
97  TQDictIterator<KAddressBookView> it( mViewDict );
98  for ( it.toFirst(); it.current(); ++it ) {
99  TDEConfigGroupSaver saver( mCore->config(), it.currentKey() );
100  (*it)->writeConfig( mCore->config() );
101  }
102 
103  Filter::save( mCore->config(), "Filter", mFilterList );
104  KABPrefs::instance()->setCurrentFilter( mFilterSelectionWidget->currentItem() );
105 
106  // write the view name list
107  KABPrefs::instance()->setViewNames( mViewNameList );
108 
109  if ( mActiveView )
110  KABPrefs::instance()->setCurrentView( mActiveView->caption() );
111 }
112 
113 TQStringList ViewManager::selectedUids() const
114 {
115  if ( mActiveView ) {
116  return mActiveView->selectedUids();
117  } else
118  return TQStringList();
119 }
120 
121 TQStringList ViewManager::selectedEmails() const
122 {
123  if ( mActiveView )
124  return mActiveView->selectedEmails();
125  else
126  return TQStringList();
127 }
128 
129 TDEABC::Addressee::List ViewManager::selectedAddressees() const
130 {
131  TDEABC::Addressee::List list;
132 
133  const TQStringList uids = selectedUids();
134  TQStringList::ConstIterator it;
135  for ( it = uids.begin(); it != uids.end(); ++it ) {
136  TDEABC::Addressee addr = mCore->addressBook()->findByUid( *it );
137  if ( !addr.isEmpty() )
138  list.append( addr );
139  }
140 
141  return list;
142 }
143 
144 void ViewManager::setFilterSelectionWidget( FilterSelectionWidget *wdg )
145 {
146  mFilterSelectionWidget = wdg;
147 }
148 
149 TDEABC::Field *ViewManager::currentSortField() const
150 {
151  if ( mActiveView )
152  return mActiveView->sortField();
153  else
154  return 0;
155 }
156 
157 TDEABC::Field::List ViewManager::viewFields() const
158 {
159 /*
160  if ( mActiveView )
161  return mActiveView->fields();
162  else
163 */
164  return TDEABC::Field::List();
165 }
166 
167 void ViewManager::setSelected( const TQString &uid, bool selected )
168 {
169  if ( mActiveView )
170  mActiveView->setSelected( uid, selected );
171 }
172 
173 void ViewManager::setFirstSelected( bool selected )
174 {
175  if ( mActiveView )
176  mActiveView->setFirstSelected( selected );
177 }
178 
179 void ViewManager::unloadViews()
180 {
181  mViewDict.clear();
182  mActiveView = 0;
183 }
184 
185 void ViewManager::setActiveView( const TQString &name )
186 {
187  KAddressBookView *view = 0;
188 
189  // Check that this isn't the same as the current active view
190  if ( mActiveView && ( mActiveView->caption() == name ) )
191  return;
192 
193  // At this point we know the view that should be active is not
194  // currently active. We will try to find the new on in the list. If
195  // we can't find it, it means it hasn't been instantiated, so we will
196  // create it on demand.
197 
198  view = mViewDict.find( name );
199 
200  // Check if we found the view. If we didn't, then we need to create it
201  if ( view == 0 ) {
202  TDEConfig *config = mCore->config();
203  TDEConfigGroupSaver saver( config, name );
204  TQString type = config->readEntry( "Type", "Table" );
205 
206  kdDebug(5720) << "ViewManager::setActiveView: creating view - " << name << endl;
207 
208  ViewFactory *factory = mViewFactoryDict.find( type );
209  if ( factory )
210  view = factory->view( mCore, mViewWidgetStack );
211 
212  if ( view ) {
213  view->setCaption( name );
214  mViewDict.insert( name, view );
215  mViewWidgetStack->addWidget( view );
216  view->readConfig( config );
217 
218  // The manager just relays the signals
219  connect( view, TQ_SIGNAL( selected( const TQString& ) ),
220  TQ_SIGNAL( selected( const TQString & ) ) );
221  connect( view, TQ_SIGNAL( executed( const TQString& ) ),
222  TQ_SIGNAL( executed( const TQString& ) ) );
223  connect( view, TQ_SIGNAL( modified() ), TQ_SIGNAL( modified() ) );
224  connect( view, TQ_SIGNAL( dropped( TQDropEvent* ) ),
225  TQ_SLOT( dropped( TQDropEvent* ) ) );
226  connect( view, TQ_SIGNAL( startDrag() ), TQ_SLOT( startDrag() ) );
227  connect( view, TQ_SIGNAL( sortFieldChanged() ), TQ_SIGNAL( sortFieldChanged() ) );
228  }
229  }
230 
231  // If we found or created the view, raise it and refresh it
232  if ( view ) {
233  mActiveView = view;
234  mViewWidgetStack->raiseWidget( view );
235  // Set the proper filter in the view. By setting the combo
236  // box, the activated slot will be called, which will push
237  // the filter to the view and refresh it.
238  if ( view->defaultFilterType() == KAddressBookView::None ) {
239  mFilterSelectionWidget->setCurrentItem( 0 );
240  setActiveFilter( 0 );
241  } else if ( view->defaultFilterType() == KAddressBookView::Active ) {
242  setActiveFilter( mFilterSelectionWidget->currentItem() );
243  } else {
244  uint pos = filterPosition( view->defaultFilterName() );
245  mFilterSelectionWidget->setCurrentItem( pos );
246  setActiveFilter( pos );
247  }
248 
249  // Update the inc search widget to show the fields in the new active
250  // view.
251  mActiveView->refresh();
252 
253  } else
254  kdDebug(5720) << "ViewManager::setActiveView: unable to find view\n";
255 }
256 
257 void ViewManager::refreshView( const TQString &uid )
258 {
259  if ( mActiveView )
260  mActiveView->refresh( uid );
261 }
262 
263 void ViewManager::editView()
264 {
265  if ( !mActiveView )
266  return;
267 
268  ViewFactory *factory = mViewFactoryDict.find( mActiveView->type() );
269  ViewConfigureWidget *wdg = 0;
270 
271  if ( factory ) {
272  // Save the filters so the dialog has the latest set
273  Filter::save( mCore->config(), "Filter", mFilterList );
274 
275  wdg = factory->configureWidget( mCore->addressBook(), 0 );
276  }
277 
278  if ( wdg ) {
279  ViewConfigureDialog dlg( wdg, mActiveView->caption(), this );
280 
281  TDEConfigGroupSaver saver( mCore->config(), mActiveView->caption() );
282  dlg.restoreSettings( mCore->config() );
283 
284  if ( dlg.exec() ) {
285  dlg.saveSettings( mCore->config() );
286  mActiveView->readConfig( mCore->config() );
287  // Set the proper filter in the view. By setting the combo
288  // box, the activated slot will be called, which will push
289  // the filter to the view and refresh it.
290  if ( mActiveView->defaultFilterType() == KAddressBookView::None ) {
291  mFilterSelectionWidget->setCurrentItem( 0 );
292  setActiveFilter( 0 );
293  } else if ( mActiveView->defaultFilterType() == KAddressBookView::Active ) {
294  setActiveFilter( mFilterSelectionWidget->currentItem() );
295  } else {
296  uint pos = filterPosition( mActiveView->defaultFilterName() );
297  mFilterSelectionWidget->setCurrentItem( pos );
298  setActiveFilter( pos );
299  }
300 
301  mActiveView->refresh();
302  emit viewFieldsChanged();
303  }
304  }
305 }
306 
307 void ViewManager::deleteView()
308 {
309  TQString text = i18n( "<qt>Are you sure that you want to delete the view <b>%1</b>?</qt>" )
310  .arg( mActiveView->caption() );
311  TQString caption = i18n( "Confirm Delete" );
312 
313  if ( KMessageBox::warningContinueCancel( this, text, caption, KGuiItem( i18n("&Delete"), "edit-delete") ) == KMessageBox::Continue ) {
314  mViewNameList.remove( mActiveView->caption() );
315 
316  // remove the view from the config file
317  TDEConfig *config = mCore->config();
318  config->deleteGroup( mActiveView->caption() );
319 
320  mViewDict.remove( mActiveView->caption() );
321  mActiveView = 0;
322 
323  // we are in an invalid state now, but that should be fixed after
324  // we emit the signal
325  mActionSelectView->setItems( mViewNameList );
326  if ( mViewNameList.count() > 0 ) {
327  mActionSelectView->setCurrentItem( 0 );
328  setActiveView( mViewNameList[ 0 ] );
329  }
330  mActionDeleteView->setEnabled( mViewNameList.count() > 1 );
331  }
332 }
333 
334 void ViewManager::addView()
335 {
336  AddViewDialog dialog( &mViewFactoryDict, this );
337 
338  if ( dialog.exec() ) {
339  TQString newName = dialog.viewName();
340  TQString type = dialog.viewType();
341 
342  // Check for name conflicts
343  bool firstConflict = true;
344  int numTries = 1;
345  while ( mViewNameList.contains( newName ) > 0 ) {
346  if ( !firstConflict ) {
347  newName = newName.left( newName.length() - 4 );
348  firstConflict = false;
349  }
350 
351  newName = TQString( "%1 <%2>" ).arg( newName ).arg( numTries );
352  numTries++;
353  }
354 
355  // Add the new one to the list
356  mViewNameList.append( newName );
357 
358  // write the view to the config file,
359  TDEConfig *config = mCore->config();
360  config->deleteGroup( newName );
361  TDEConfigGroupSaver saver( config, newName );
362  config->writeEntry( "Type", type );
363 
364  // try to set the active view
365  mActionSelectView->setItems( mViewNameList );
366  mActionSelectView->setCurrentItem( mViewNameList.findIndex( newName ) );
367  setActiveView( newName );
368 
369  editView();
370 
371  mActionDeleteView->setEnabled( mViewNameList.count() > 1 );
372  }
373 }
374 
375 void ViewManager::scrollUp()
376 {
377  if ( mActiveView )
378  mActiveView->scrollUp();
379 }
380 
381 void ViewManager::scrollDown()
382 {
383  if ( mActiveView )
384  mActiveView->scrollDown();
385 }
386 
387 void ViewManager::createViewFactories()
388 {
389  const TDETrader::OfferList plugins = TDETrader::self()->query( "KAddressBook/View",
390  TQString( "[X-TDE-KAddressBook-ViewPluginVersion] == %1" ).arg( KAB_VIEW_PLUGIN_VERSION ) );
391  TDETrader::OfferList::ConstIterator it;
392  for ( it = plugins.begin(); it != plugins.end(); ++it ) {
393  if ( !(*it)->hasServiceType( "KAddressBook/View" ) )
394  continue;
395 
396  KLibFactory *factory = KLibLoader::self()->factory( (*it)->library().latin1() );
397 
398  if ( !factory ) {
399  kdDebug(5720) << "ViewManager::createViewFactories(): Factory creation failed" << endl;
400  continue;
401  }
402 
403  ViewFactory *viewFactory = static_cast<ViewFactory*>( factory );
404 
405  if ( !viewFactory ) {
406  kdDebug(5720) << "ViewManager::createViewFactories(): Cast failed" << endl;
407  continue;
408  }
409 
410  mViewFactoryDict.insert( viewFactory->type(), viewFactory );
411  }
412 }
413 
414 void ViewManager::dropped( TQDropEvent *e )
415 {
416  kdDebug(5720) << "ViewManager::dropped: got a drop event" << endl;
417 
418  // don't allow drops from our own drags
419  if ( e->source() == this )
420  return;
421 
422  TDEABC::Addressee::List list;
423  KURL::List urls;
424 
425  if ( KURLDrag::decode( e, urls) ) {
426  KURL::List::ConstIterator it = urls.begin();
427  int c = urls.count();
428  if ( c > 1 ) {
429  TQString questionString = i18n( "Import one contact into your addressbook?", "Import %n contacts into your addressbook?", c );
430  if ( KMessageBox::questionYesNo( this, questionString, i18n( "Import Contacts?" ), i18n("Import"), i18n("Do Not Import") ) == KMessageBox::Yes ) {
431  for ( ; it != urls.end(); ++it )
432  emit urlDropped( *it );
433  }
434  } else if ( c == 1 )
435  emit urlDropped( *it );
436  } else if ( KVCardDrag::decode( e, list ) ) {
437  TDEABC::Addressee::List::ConstIterator it;
438  for ( it = list.begin(); it != list.end(); ++it ) {
439  TDEABC::Addressee a = mCore->addressBook()->findByUid( (*it).uid() );
440  if ( a.isEmpty() ) { // not yet in address book
441  mCore->addressBook()->insertAddressee( *it );
442  emit modified();
443  }
444  }
445 
446  mActiveView->refresh();
447  }
448 }
449 
451 {
452  // Get the list of all the selected addressees
453  TDEABC::Addressee::List addrList;
454  const TQStringList uidList = selectedUids();
455  if ( uidList.isEmpty() )
456  return;
457 
458  kdDebug(5720) << "ViewManager::startDrag: starting to drag" << endl;
459 
460  TQStringList::ConstIterator it;
461  for ( it = uidList.begin(); it != uidList.end(); ++it )
462  addrList.append( mCore->addressBook()->findByUid( *it ) );
463 
464  KMultipleDrag *drag = new KMultipleDrag( this );
465 
466  TDEABC::VCardConverter converter;
467 #if defined(KABC_VCARD_ENCODING_FIX)
468  TQCString vcards = converter.createVCardsRaw( addrList );
469 #else
470  TQString vcards = converter.createVCards( addrList );
471 #endif
472 
473  // Best text representation is given by textdrag, so it must be first
474  drag->addDragObject( new TQTextDrag( AddresseeUtil::addresseesToEmails( addrList ), this ) );
475  drag->addDragObject( new KVCardDrag( vcards, this ) );
476 
477  KTempDir tempDir;
478  // can't set tempDir to autoDelete, in case of dropping on the desktop, the copy is async...
479  if ( tempDir.status() == 0 ) {
480  TQString fileName;
481  if ( addrList.count() == 1 )
482  fileName = addrList[ 0 ].givenName() + "_" + addrList[ 0 ].familyName() + ".vcf";
483  else
484  fileName = "contacts.vcf";
485 
486  TQFile tempFile( tempDir.name() + "/" + fileName );
487  if ( tempFile.open( IO_WriteOnly ) ) {
488 #if defined(KABC_VCARD_ENCODING_FIX)
489  tempFile.writeBlock( vcards, vcards.length() );
490 #else
491  tempFile.writeBlock( vcards.utf8() );
492 #endif
493  tempFile.close();
494 
495  KURLDrag *urlDrag = new KURLDrag( KURL( tempFile.name() ), this );
496  drag->addDragObject( urlDrag );
497  }
498  }
499 
500  drag->setPixmap( TDEGlobal::iconLoader()->loadIcon( "x-office-address-book", TDEIcon::Desktop ) );
501  drag->dragCopy();
502 }
503 
504 void ViewManager::setActiveFilter( int index )
505 {
506  Filter currentFilter;
507 
508  if ( ( index - 1 ) < 0 )
509  currentFilter = Filter();
510  else if ( ( index - 1 ) < 1 ) {
511  currentFilter = Filter();
512  currentFilter.setMatchRule(Filter::NotMatching);
513  }
514  else
515  currentFilter = mFilterList[ index - 2 ];
516 
517  // Check if we have a view. Since the filter combo is created before
518  // the view, this slot could be called before there is a valid view.
519  if ( mActiveView ) {
520  mActiveView->setFilter( currentFilter );
521  mActiveView->refresh();
522  emit selected( TQString() );
523  }
524 }
525 
526 void ViewManager::configureFilters()
527 {
528  FilterDialog dlg( this );
529 
530  dlg.setFilters( mFilterList );
531 
532  if ( dlg.exec() )
533  mFilterList = dlg.filters();
534 
535  uint pos = mFilterSelectionWidget->currentItem();
536  mFilterSelectionWidget->setItems( filterNames() );
537  mFilterSelectionWidget->setCurrentItem( pos );
538  setActiveFilter( pos );
539 }
540 
541 TQStringList ViewManager::filterNames() const
542 {
543  TQStringList names( i18n( "None" ) );
544  names.append( i18n( "Unfiled" ) );
545 
546  Filter::List::ConstIterator it;
547  for ( it = mFilterList.begin(); it != mFilterList.end(); ++it )
548  names.append( (*it).name() );
549 
550  return names;
551 }
552 
553 int ViewManager::filterPosition( const TQString &name ) const
554 {
555  int pos = 0;
556 
557  Filter::List::ConstIterator it;
558  for ( it = mFilterList.begin(); it != mFilterList.end(); ++it, ++pos )
559  if ( name == (*it).name() )
560  return pos + 2;
561 
562  return 0;
563 }
564 
565 void ViewManager::initActions()
566 {
567  mActionSelectView = new TDESelectAction( i18n( "Select View" ), 0, mCore->actionCollection(), "select_view" );
568 #if TDE_VERSION >= 309
569  mActionSelectView->setMenuAccelsEnabled( false );
570 #endif
571  connect( mActionSelectView, TQ_SIGNAL( activated( const TQString& ) ),
572  TQ_SLOT( setActiveView( const TQString& ) ) );
573 
574  TDEAction *action;
575 
576  action = new TDEAction( i18n( "Modify View..." ), "configure", 0, this,
577  TQ_SLOT( editView() ), mCore->actionCollection(),
578  "view_modify" );
579  action->setWhatsThis( i18n( "By pressing this button a dialog opens that allows you to modify the view of the addressbook. There you can add or remove fields that you want to be shown or hidden in the addressbook like the name for example." ) );
580 
581  action = new TDEAction( i18n( "Add View..." ), "window-new", 0, this,
582  TQ_SLOT( addView() ), mCore->actionCollection(),
583  "view_add" );
584  action->setWhatsThis( i18n( "You can add a new view by choosing one from the dialog that appears after pressing the button. You have to give the view a name, so that you can distinguish between the different views." ) );
585 
586  mActionDeleteView = new TDEAction( i18n( "Delete View" ), "view_remove", 0,
587  this, TQ_SLOT( deleteView() ),
588  mCore->actionCollection(), "view_delete" );
589  mActionDeleteView->setWhatsThis( i18n( "By pressing this button you can delete the actual view, which you have added before." ) );
590 
591  action = new TDEAction( i18n( "Refresh View" ), "reload", 0, this,
592  TQ_SLOT( refreshView() ), mCore->actionCollection(),
593  "view_refresh" );
594  action->setWhatsThis( i18n( "The view will be refreshed by pressing this button." ) );
595 
596  action = new TDEAction( i18n( "Edit &Filters..." ), "filter", 0, this,
597  TQ_SLOT( configureFilters() ), mCore->actionCollection(),
598  "options_edit_filters" );
599  action->setWhatsThis( i18n( "Edit the contact filters<p>You will be presented with a dialog, where you can add, remove and edit filters." ) );
600 }
601 
602 void ViewManager::initGUI()
603 {
604  TQHBoxLayout *layout = new TQHBoxLayout( this );
605  mViewWidgetStack = new TQWidgetStack( this );
606  layout->addWidget( mViewWidgetStack );
607 }
608 
609 #include "viewmanager.moc"
Modal dialog used for adding a new view.
Definition: addviewdialog.h:42
static TQString addresseesToEmails(const TDEABC::Addressee::List &addrList)
Converts the list of addressee objects into a list of email addresses.
A simple widget which consists of a label and a combo box in a horizontal line.
Filter for AddressBook related objects (Addressees)
Definition: filter.h:40
void restore(TDEConfig *config)
Loads the filter from the config file.
Definition: filter.cpp:132
void save(TDEConfig *config)
Saves the filter to the config file.
Definition: filter.cpp:124
void setMatchRule(MatchRule rule)
Sets the filter rule.
Definition: filter.cpp:204
Base class for all views in kaddressbook.
virtual TQStringList selectedUids()=0
Must be overloaded in subclasses.
virtual TQString type() const =0
Return the type of the view: Icon, Table, etc.
const TQString & defaultFilterName() const
void setFilter(const Filter &)
Sets the active filter.
DefaultFilterType defaultFilterType() const
virtual void refresh(const TQString &uid=TQString())=0
Must be overloaded in subclasses to refresh the view.
virtual TQString selectedEmails()
Returns a TQString with all the selected email addresses concatenated together with a ',...
virtual void setSelected(const TQString &uid=TQString(), bool selected=true)=0
This method must be overloaded in subclasses.
virtual void setFirstSelected(bool selected=true)=0
Selects the first contact in the view.
virtual void readConfig(TDEConfig *config)
Called whenever this view should read the config.
virtual TDEABC::Field * sortField() const =0
This widget is the base class for all view configuration widgets.
void viewFieldsChanged()
Emitted whenever the view fields changed.
void startDrag()
Called whenever the user attempts to start a drag in the view.
void urlDropped(const KURL &)
Emitted whenever a url is dragged on a view.
void dropped(TQDropEvent *)
Called whenever the user drops something in the active view.
void modified()
Emitted whenever the address book is modified in some way.
void selected(const TQString &uid)
Emitted whenever the user selects an entry in the view.
void sortFieldChanged()
Emitted whenever the sort field of a view has changed.
void executed(const TQString &uid)
Emitted whenever the user activates an entry in the view.