Need to store config when changing units, if not very bad things happen
[kdepim.git] / kaddressbook / viewmanager.cpp
blob9d2f632ed56418b5978744ca1a3a785cdb92511b
1 /*
2 This file is part of KAddressBook.
3 Copyright (c) 2002 Mike Pilone <mpilone@slac.com>
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.
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.
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.
19 As a special exception, permission is given to link this program
20 with any edition of Qt, and distribute the resulting executable,
21 without including the source code for Qt in the source distribution.
24 #include <qfile.h>
25 #include <qlayout.h>
26 #include <qwidgetstack.h>
28 #include <libkdepim/kvcarddrag.h>
29 #include <kabc/addressbook.h>
30 #include <kabc/vcardconverter.h>
31 #include <kactionclasses.h>
32 #include <kconfig.h>
33 #include <kdebug.h>
34 #include <kdeversion.h>
35 #include <kiconloader.h>
36 #include <klocale.h>
37 #include <kmessagebox.h>
38 #include <kmultipledrag.h>
39 #include <ktempdir.h>
40 #include <ktrader.h>
41 #include <kurldrag.h>
43 #include "addviewdialog.h"
44 #include "addresseeutil.h"
45 #include "core.h"
46 #include "filtereditdialog.h"
47 #include "filterselectionwidget.h"
48 #include "kabprefs.h"
50 #include "viewmanager.h"
52 ViewManager::ViewManager( KAB::Core *core, QWidget *parent, const char *name )
53 : QWidget( parent, name ), mCore( core ), mActiveView( 0 ),
54 mFilterSelectionWidget( 0 )
56 initGUI();
57 initActions();
59 mViewDict.setAutoDelete( true );
61 createViewFactories();
64 ViewManager::~ViewManager()
66 unloadViews();
67 mViewFactoryDict.clear();
70 void ViewManager::restoreSettings()
72 mViewNameList = KABPrefs::instance()->viewNames();
73 QString activeViewName = KABPrefs::instance()->currentView();
75 mActionSelectView->setItems( mViewNameList );
77 // Filter
78 mFilterList = Filter::restore( mCore->config(), "Filter" );
79 mFilterSelectionWidget->setItems( filterNames() );
80 mFilterSelectionWidget->setCurrentItem( KABPrefs::instance()->currentFilter() );
82 // Tell the views to reread their config, since they may have
83 // been modified by global settings
84 QDictIterator<KAddressBookView> it( mViewDict );
85 for ( it.toFirst(); it.current(); ++it ) {
86 KConfigGroupSaver saver( mCore->config(), it.currentKey() );
87 it.current()->readConfig( mCore->config() );
90 setActiveView( activeViewName );
92 mActionDeleteView->setEnabled( mViewNameList.count() > 1 );
95 void ViewManager::saveSettings()
97 QDictIterator<KAddressBookView> it( mViewDict );
98 for ( it.toFirst(); it.current(); ++it ) {
99 KConfigGroupSaver saver( mCore->config(), it.currentKey() );
100 (*it)->writeConfig( mCore->config() );
103 Filter::save( mCore->config(), "Filter", mFilterList );
104 KABPrefs::instance()->setCurrentFilter( mFilterSelectionWidget->currentItem() );
106 // write the view name list
107 KABPrefs::instance()->setViewNames( mViewNameList );
109 if ( mActiveView )
110 KABPrefs::instance()->setCurrentView( mActiveView->caption() );
113 QStringList ViewManager::selectedUids() const
115 if ( mActiveView ) {
116 return mActiveView->selectedUids();
117 } else
118 return QStringList();
121 QStringList ViewManager::selectedEmails() const
123 if ( mActiveView )
124 return mActiveView->selectedEmails();
125 else
126 return QStringList();
129 KABC::Addressee::List ViewManager::selectedAddressees() const
131 KABC::Addressee::List list;
133 const QStringList uids = selectedUids();
134 QStringList::ConstIterator it;
135 for ( it = uids.begin(); it != uids.end(); ++it ) {
136 KABC::Addressee addr = mCore->addressBook()->findByUid( *it );
137 if ( !addr.isEmpty() )
138 list.append( addr );
141 return list;
144 void ViewManager::setFilterSelectionWidget( FilterSelectionWidget *wdg )
146 mFilterSelectionWidget = wdg;
149 KABC::Field *ViewManager::currentSortField() const
151 if ( mActiveView )
152 return mActiveView->sortField();
153 else
154 return 0;
157 KABC::Field::List ViewManager::viewFields() const
160 if ( mActiveView )
161 return mActiveView->fields();
162 else
164 return KABC::Field::List();
167 void ViewManager::setSelected( const QString &uid, bool selected )
169 if ( mActiveView )
170 mActiveView->setSelected( uid, selected );
173 void ViewManager::setFirstSelected( bool selected )
175 if ( mActiveView )
176 mActiveView->setFirstSelected( selected );
179 void ViewManager::unloadViews()
181 mViewDict.clear();
182 mActiveView = 0;
185 void ViewManager::setActiveView( const QString &name )
187 KAddressBookView *view = 0;
189 // Check that this isn't the same as the current active view
190 if ( mActiveView && ( mActiveView->caption() == name ) )
191 return;
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.
198 view = mViewDict.find( name );
200 // Check if we found the view. If we didn't, then we need to create it
201 if ( view == 0 ) {
202 KConfig *config = mCore->config();
203 KConfigGroupSaver saver( config, name );
204 QString type = config->readEntry( "Type", "Table" );
206 kdDebug(5720) << "ViewManager::setActiveView: creating view - " << name << endl;
208 ViewFactory *factory = mViewFactoryDict.find( type );
209 if ( factory )
210 view = factory->view( mCore, mViewWidgetStack );
212 if ( view ) {
213 view->setCaption( name );
214 mViewDict.insert( name, view );
215 mViewWidgetStack->addWidget( view );
216 view->readConfig( config );
218 // The manager just relays the signals
219 connect( view, SIGNAL( selected( const QString& ) ),
220 SIGNAL( selected( const QString & ) ) );
221 connect( view, SIGNAL( executed( const QString& ) ),
222 SIGNAL( executed( const QString& ) ) );
223 connect( view, SIGNAL( modified() ), SIGNAL( modified() ) );
224 connect( view, SIGNAL( dropped( QDropEvent* ) ),
225 SLOT( dropped( QDropEvent* ) ) );
226 connect( view, SIGNAL( startDrag() ), SLOT( startDrag() ) );
227 connect( view, SIGNAL( sortFieldChanged() ), SIGNAL( sortFieldChanged() ) );
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 );
249 // Update the inc search widget to show the fields in the new active
250 // view.
251 mActiveView->refresh();
253 } else
254 kdDebug(5720) << "ViewManager::setActiveView: unable to find view\n";
257 void ViewManager::refreshView( const QString &uid )
259 if ( mActiveView )
260 mActiveView->refresh( uid );
263 void ViewManager::editView()
265 if ( !mActiveView )
266 return;
268 ViewFactory *factory = mViewFactoryDict.find( mActiveView->type() );
269 ViewConfigureWidget *wdg = 0;
271 if ( factory ) {
272 // Save the filters so the dialog has the latest set
273 Filter::save( mCore->config(), "Filter", mFilterList );
275 wdg = factory->configureWidget( mCore->addressBook(), 0 );
278 if ( wdg ) {
279 ViewConfigureDialog dlg( wdg, mActiveView->caption(), this );
281 KConfigGroupSaver saver( mCore->config(), mActiveView->caption() );
282 dlg.restoreSettings( mCore->config() );
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 );
301 mActiveView->refresh();
302 emit viewFieldsChanged();
307 void ViewManager::deleteView()
309 QString text = i18n( "<qt>Are you sure that you want to delete the view <b>%1</b>?</qt>" )
310 .arg( mActiveView->caption() );
311 QString caption = i18n( "Confirm Delete" );
313 if ( KMessageBox::warningContinueCancel( this, text, caption, KGuiItem( i18n("&Delete"), "editdelete") ) == KMessageBox::Continue ) {
314 mViewNameList.remove( mActiveView->caption() );
316 // remove the view from the config file
317 KConfig *config = mCore->config();
318 config->deleteGroup( mActiveView->caption() );
320 mViewDict.remove( mActiveView->caption() );
321 mActiveView = 0;
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 ] );
330 mActionDeleteView->setEnabled( mViewNameList.count() > 1 );
334 void ViewManager::addView()
336 AddViewDialog dialog( &mViewFactoryDict, this );
338 if ( dialog.exec() ) {
339 QString newName = dialog.viewName();
340 QString type = dialog.viewType();
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;
351 newName = QString( "%1 <%2>" ).arg( newName ).arg( numTries );
352 numTries++;
355 // Add the new one to the list
356 mViewNameList.append( newName );
358 // write the view to the config file,
359 KConfig *config = mCore->config();
360 config->deleteGroup( newName );
361 KConfigGroupSaver saver( config, newName );
362 config->writeEntry( "Type", type );
364 // try to set the active view
365 mActionSelectView->setItems( mViewNameList );
366 mActionSelectView->setCurrentItem( mViewNameList.findIndex( newName ) );
367 setActiveView( newName );
369 editView();
371 mActionDeleteView->setEnabled( mViewNameList.count() > 1 );
375 void ViewManager::scrollUp()
377 if ( mActiveView )
378 mActiveView->scrollUp();
381 void ViewManager::scrollDown()
383 if ( mActiveView )
384 mActiveView->scrollDown();
387 void ViewManager::createViewFactories()
389 const KTrader::OfferList plugins = KTrader::self()->query( "KAddressBook/View",
390 QString( "[X-KDE-KAddressBook-ViewPluginVersion] == %1" ).arg( KAB_VIEW_PLUGIN_VERSION ) );
391 KTrader::OfferList::ConstIterator it;
392 for ( it = plugins.begin(); it != plugins.end(); ++it ) {
393 if ( !(*it)->hasServiceType( "KAddressBook/View" ) )
394 continue;
396 KLibFactory *factory = KLibLoader::self()->factory( (*it)->library().latin1() );
398 if ( !factory ) {
399 kdDebug(5720) << "ViewManager::createViewFactories(): Factory creation failed" << endl;
400 continue;
403 ViewFactory *viewFactory = static_cast<ViewFactory*>( factory );
405 if ( !viewFactory ) {
406 kdDebug(5720) << "ViewManager::createViewFactories(): Cast failed" << endl;
407 continue;
410 mViewFactoryDict.insert( viewFactory->type(), viewFactory );
414 void ViewManager::dropped( QDropEvent *e )
416 kdDebug(5720) << "ViewManager::dropped: got a drop event" << endl;
418 // don't allow drops from our own drags
419 if ( e->source() == this )
420 return;
422 QString clipText, vcards;
423 KURL::List urls;
425 if ( KURLDrag::decode( e, urls) ) {
426 KURL::List::ConstIterator it = urls.begin();
427 int c = urls.count();
428 if ( c > 1 ) {
429 QString 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 );
434 } else if ( c == 1 )
435 emit urlDropped( *it );
436 } else if ( KVCardDrag::decode( e, vcards ) ) {
437 KABC::VCardConverter converter;
439 const KABC::Addressee::List list = converter.parseVCards( vcards );
440 KABC::Addressee::List::ConstIterator it;
441 for ( it = list.begin(); it != list.end(); ++it ) {
442 KABC::Addressee a = mCore->addressBook()->findByUid( (*it).uid() );
443 if ( a.isEmpty() ) { // not yet in address book
444 mCore->addressBook()->insertAddressee( *it );
445 emit modified();
449 mActiveView->refresh();
453 void ViewManager::startDrag()
455 // Get the list of all the selected addressees
456 KABC::Addressee::List addrList;
457 const QStringList uidList = selectedUids();
458 if ( uidList.isEmpty() )
459 return;
461 kdDebug(5720) << "ViewManager::startDrag: starting to drag" << endl;
463 QStringList::ConstIterator it;
464 for ( it = uidList.begin(); it != uidList.end(); ++it )
465 addrList.append( mCore->addressBook()->findByUid( *it ) );
467 KMultipleDrag *drag = new KMultipleDrag( this );
469 KABC::VCardConverter converter;
470 QString vcards = converter.createVCards( addrList );
472 // Best text representation is given by textdrag, so it must be first
473 drag->addDragObject( new QTextDrag( AddresseeUtil::addresseesToEmails( addrList ), this ) );
474 drag->addDragObject( new KVCardDrag( vcards, this ) );
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 QString fileName;
480 if ( addrList.count() == 1 )
481 fileName = addrList[ 0 ].givenName() + "_" + addrList[ 0 ].familyName() + ".vcf";
482 else
483 fileName = "contacts.vcf";
485 QFile tempFile( tempDir.name() + "/" + fileName );
486 if ( tempFile.open( IO_WriteOnly ) ) {
487 tempFile.writeBlock( vcards.utf8() );
488 tempFile.close();
490 KURLDrag *urlDrag = new KURLDrag( KURL( tempFile.name() ), this );
491 drag->addDragObject( urlDrag );
495 drag->setPixmap( KGlobal::iconLoader()->loadIcon( "vcard", KIcon::Desktop ) );
496 drag->dragCopy();
499 void ViewManager::setActiveFilter( int index )
501 Filter currentFilter;
503 if ( ( index - 1 ) < 0 )
504 currentFilter = Filter();
505 else if ( ( index - 1 ) < 1 ) {
506 currentFilter = Filter();
507 currentFilter.setMatchRule(Filter::NotMatching);
509 else
510 currentFilter = mFilterList[ index - 2 ];
512 // Check if we have a view. Since the filter combo is created before
513 // the view, this slot could be called before there is a valid view.
514 if ( mActiveView ) {
515 mActiveView->setFilter( currentFilter );
516 mActiveView->refresh();
517 emit selected( QString::null );
521 void ViewManager::configureFilters()
523 FilterDialog dlg( this );
525 dlg.setFilters( mFilterList );
527 if ( dlg.exec() )
528 mFilterList = dlg.filters();
530 uint pos = mFilterSelectionWidget->currentItem();
531 mFilterSelectionWidget->setItems( filterNames() );
532 mFilterSelectionWidget->setCurrentItem( pos );
533 setActiveFilter( pos );
536 QStringList ViewManager::filterNames() const
538 QStringList names( i18n( "None" ) );
539 names.append( i18n( "Unfiled" ) );
541 Filter::List::ConstIterator it;
542 for ( it = mFilterList.begin(); it != mFilterList.end(); ++it )
543 names.append( (*it).name() );
545 return names;
548 int ViewManager::filterPosition( const QString &name ) const
550 int pos = 0;
552 Filter::List::ConstIterator it;
553 for ( it = mFilterList.begin(); it != mFilterList.end(); ++it, ++pos )
554 if ( name == (*it).name() )
555 return pos + 2;
557 return 0;
560 void ViewManager::initActions()
562 mActionSelectView = new KSelectAction( i18n( "Select View" ), 0, mCore->actionCollection(), "select_view" );
563 #if KDE_VERSION >= 309
564 mActionSelectView->setMenuAccelsEnabled( false );
565 #endif
566 connect( mActionSelectView, SIGNAL( activated( const QString& ) ),
567 SLOT( setActiveView( const QString& ) ) );
569 KAction *action;
571 action = new KAction( i18n( "Modify View..." ), "configure", 0, this,
572 SLOT( editView() ), mCore->actionCollection(),
573 "view_modify" );
574 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." ) );
576 action = new KAction( i18n( "Add View..." ), "window_new", 0, this,
577 SLOT( addView() ), mCore->actionCollection(),
578 "view_add" );
579 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." ) );
581 mActionDeleteView = new KAction( i18n( "Delete View" ), "view_remove", 0,
582 this, SLOT( deleteView() ),
583 mCore->actionCollection(), "view_delete" );
584 mActionDeleteView->setWhatsThis( i18n( "By pressing this button you can delete the actual view, which you have added before." ) );
586 action = new KAction( i18n( "Refresh View" ), "reload", 0, this,
587 SLOT( refreshView() ), mCore->actionCollection(),
588 "view_refresh" );
589 action->setWhatsThis( i18n( "The view will be refreshed by pressing this button." ) );
591 action = new KAction( i18n( "Edit &Filters..." ), "filter", 0, this,
592 SLOT( configureFilters() ), mCore->actionCollection(),
593 "options_edit_filters" );
594 action->setWhatsThis( i18n( "Edit the contact filters<p>You will be presented with a dialog, where you can add, remove and edit filters." ) );
597 void ViewManager::initGUI()
599 QHBoxLayout *layout = new QHBoxLayout( this );
600 mViewWidgetStack = new QWidgetStack( this );
601 layout->addWidget( mViewWidgetStack );
604 #include "viewmanager.moc"