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