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
52ViewManager::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
64ViewManager::~ViewManager()
65{
66 unloadViews();
67 mViewFactoryDict.clear();
68}
69
70void 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
95void 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
113TQStringList ViewManager::selectedUids() const
114{
115 if ( mActiveView ) {
116 return mActiveView->selectedUids();
117 } else
118 return TQStringList();
119}
120
121TQStringList ViewManager::selectedEmails() const
122{
123 if ( mActiveView )
124 return mActiveView->selectedEmails();
125 else
126 return TQStringList();
127}
128
129TDEABC::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
144void ViewManager::setFilterSelectionWidget( FilterSelectionWidget *wdg )
145{
146 mFilterSelectionWidget = wdg;
147}
148
149TDEABC::Field *ViewManager::currentSortField() const
150{
151 if ( mActiveView )
152 return mActiveView->sortField();
153 else
154 return 0;
155}
156
157TDEABC::Field::List ViewManager::viewFields() const
158{
159/*
160 if ( mActiveView )
161 return mActiveView->fields();
162 else
163*/
164 return TDEABC::Field::List();
165}
166
167void ViewManager::setSelected( const TQString &uid, bool selected )
168{
169 if ( mActiveView )
170 mActiveView->setSelected( uid, selected );
171}
172
173void ViewManager::setFirstSelected( bool selected )
174{
175 if ( mActiveView )
176 mActiveView->setFirstSelected( selected );
177}
178
179void ViewManager::unloadViews()
180{
181 mViewDict.clear();
182 mActiveView = 0;
183}
184
185void 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
257void ViewManager::refreshView( const TQString &uid )
258{
259 if ( mActiveView )
260 mActiveView->refresh( uid );
261}
262
263void 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
307void 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
334void 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
375void ViewManager::scrollUp()
376{
377 if ( mActiveView )
378 mActiveView->scrollUp();
379}
380
381void ViewManager::scrollDown()
382{
383 if ( mActiveView )
384 mActiveView->scrollDown();
385}
386
387void 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
414void 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
504void 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
526void 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
541TQStringList 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
553int 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
565void 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
602void 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 TDEABC::Field * sortField() const =0
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.
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.