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