viewmanager.cpp
00001 /* 00002 This file is part of KAddressBook. 00003 Copyright (c) 2002 Mike Pilone <mpilone@slac.com> 00004 00005 This program is free software; you can redistribute it and/or modify 00006 it under the terms of the GNU General Public License as published by 00007 the Free Software Foundation; either version 2 of the License, or 00008 (at your option) any later version. 00009 00010 This program is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 GNU General Public License for more details. 00014 00015 You should have received a copy of the GNU General Public License 00016 along with this program; if not, write to the Free Software 00017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00018 00019 As a special exception, permission is given to link this program 00020 with any edition of TQt, and distribute the resulting executable, 00021 without including the source code for TQt in the source distribution. 00022 */ 00023 00024 #include <tqfile.h> 00025 #include <tqlayout.h> 00026 #include <tqwidgetstack.h> 00027 00028 #include <libkdepim/kvcarddrag.h> 00029 #include <kabc/addressbook.h> 00030 #include <kabc/vcardconverter.h> 00031 #include <kactionclasses.h> 00032 #include <kconfig.h> 00033 #include <kdebug.h> 00034 #include <kdeversion.h> 00035 #include <kiconloader.h> 00036 #include <klocale.h> 00037 #include <kmessagebox.h> 00038 #include <kmultipledrag.h> 00039 #include <ktempdir.h> 00040 #include <ktrader.h> 00041 #include <kurldrag.h> 00042 00043 #include "addviewdialog.h" 00044 #include "addresseeutil.h" 00045 #include "core.h" 00046 #include "filtereditdialog.h" 00047 #include "filterselectionwidget.h" 00048 #include "kabprefs.h" 00049 00050 #include "viewmanager.h" 00051 00052 ViewManager::ViewManager( KAB::Core *core, TQWidget *parent, const char *name ) 00053 : TQWidget( parent, name ), mCore( core ), mActiveView( 0 ), 00054 mFilterSelectionWidget( 0 ) 00055 { 00056 initGUI(); 00057 initActions(); 00058 00059 mViewDict.setAutoDelete( true ); 00060 00061 createViewFactories(); 00062 } 00063 00064 ViewManager::~ViewManager() 00065 { 00066 unloadViews(); 00067 mViewFactoryDict.clear(); 00068 } 00069 00070 void ViewManager::restoreSettings() 00071 { 00072 mViewNameList = KABPrefs::instance()->viewNames(); 00073 TQString activeViewName = KABPrefs::instance()->currentView(); 00074 00075 mActionSelectView->setItems( mViewNameList ); 00076 00077 // Filter 00078 mFilterList = Filter::restore( mCore->config(), "Filter" ); 00079 mFilterSelectionWidget->setItems( filterNames() ); 00080 mFilterSelectionWidget->setCurrentItem( KABPrefs::instance()->currentFilter() ); 00081 00082 // Tell the views to reread their config, since they may have 00083 // been modified by global settings 00084 TQDictIterator<KAddressBookView> it( mViewDict ); 00085 for ( it.toFirst(); it.current(); ++it ) { 00086 KConfigGroupSaver saver( mCore->config(), it.currentKey() ); 00087 it.current()->readConfig( mCore->config() ); 00088 } 00089 00090 setActiveView( activeViewName ); 00091 00092 mActionDeleteView->setEnabled( mViewNameList.count() > 1 ); 00093 } 00094 00095 void ViewManager::saveSettings() 00096 { 00097 TQDictIterator<KAddressBookView> it( mViewDict ); 00098 for ( it.toFirst(); it.current(); ++it ) { 00099 KConfigGroupSaver saver( mCore->config(), it.currentKey() ); 00100 (*it)->writeConfig( mCore->config() ); 00101 } 00102 00103 Filter::save( mCore->config(), "Filter", mFilterList ); 00104 KABPrefs::instance()->setCurrentFilter( mFilterSelectionWidget->currentItem() ); 00105 00106 // write the view name list 00107 KABPrefs::instance()->setViewNames( mViewNameList ); 00108 00109 if ( mActiveView ) 00110 KABPrefs::instance()->setCurrentView( mActiveView->caption() ); 00111 } 00112 00113 TQStringList ViewManager::selectedUids() const 00114 { 00115 if ( mActiveView ) { 00116 return mActiveView->selectedUids(); 00117 } else 00118 return TQStringList(); 00119 } 00120 00121 TQStringList ViewManager::selectedEmails() const 00122 { 00123 if ( mActiveView ) 00124 return mActiveView->selectedEmails(); 00125 else 00126 return TQStringList(); 00127 } 00128 00129 KABC::Addressee::List ViewManager::selectedAddressees() const 00130 { 00131 KABC::Addressee::List list; 00132 00133 const TQStringList uids = selectedUids(); 00134 TQStringList::ConstIterator it; 00135 for ( it = uids.begin(); it != uids.end(); ++it ) { 00136 KABC::Addressee addr = mCore->addressBook()->findByUid( *it ); 00137 if ( !addr.isEmpty() ) 00138 list.append( addr ); 00139 } 00140 00141 return list; 00142 } 00143 00144 void ViewManager::setFilterSelectionWidget( FilterSelectionWidget *wdg ) 00145 { 00146 mFilterSelectionWidget = wdg; 00147 } 00148 00149 KABC::Field *ViewManager::currentSortField() const 00150 { 00151 if ( mActiveView ) 00152 return mActiveView->sortField(); 00153 else 00154 return 0; 00155 } 00156 00157 KABC::Field::List ViewManager::viewFields() const 00158 { 00159 /* 00160 if ( mActiveView ) 00161 return mActiveView->fields(); 00162 else 00163 */ 00164 return KABC::Field::List(); 00165 } 00166 00167 void ViewManager::setSelected( const TQString &uid, bool selected ) 00168 { 00169 if ( mActiveView ) 00170 mActiveView->setSelected( uid, selected ); 00171 } 00172 00173 void ViewManager::setFirstSelected( bool selected ) 00174 { 00175 if ( mActiveView ) 00176 mActiveView->setFirstSelected( selected ); 00177 } 00178 00179 void ViewManager::unloadViews() 00180 { 00181 mViewDict.clear(); 00182 mActiveView = 0; 00183 } 00184 00185 void ViewManager::setActiveView( const TQString &name ) 00186 { 00187 KAddressBookView *view = 0; 00188 00189 // Check that this isn't the same as the current active view 00190 if ( mActiveView && ( mActiveView->caption() == name ) ) 00191 return; 00192 00193 // At this point we know the view that should be active is not 00194 // currently active. We will try to find the new on in the list. If 00195 // we can't find it, it means it hasn't been instantiated, so we will 00196 // create it on demand. 00197 00198 view = mViewDict.find( name ); 00199 00200 // Check if we found the view. If we didn't, then we need to create it 00201 if ( view == 0 ) { 00202 KConfig *config = mCore->config(); 00203 KConfigGroupSaver saver( config, name ); 00204 TQString type = config->readEntry( "Type", "Table" ); 00205 00206 kdDebug(5720) << "ViewManager::setActiveView: creating view - " << name << endl; 00207 00208 ViewFactory *factory = mViewFactoryDict.find( type ); 00209 if ( factory ) 00210 view = factory->view( mCore, mViewWidgetStack ); 00211 00212 if ( view ) { 00213 view->setCaption( name ); 00214 mViewDict.insert( name, view ); 00215 mViewWidgetStack->addWidget( view ); 00216 view->readConfig( config ); 00217 00218 // The manager just relays the signals 00219 connect( view, TQT_SIGNAL( selected( const TQString& ) ), 00220 TQT_SIGNAL( selected( const TQString & ) ) ); 00221 connect( view, TQT_SIGNAL( executed( const TQString& ) ), 00222 TQT_SIGNAL( executed( const TQString& ) ) ); 00223 connect( view, TQT_SIGNAL( modified() ), TQT_SIGNAL( modified() ) ); 00224 connect( view, TQT_SIGNAL( dropped( TQDropEvent* ) ), 00225 TQT_SLOT( dropped( TQDropEvent* ) ) ); 00226 connect( view, TQT_SIGNAL( startDrag() ), TQT_SLOT( startDrag() ) ); 00227 connect( view, TQT_SIGNAL( sortFieldChanged() ), TQT_SIGNAL( sortFieldChanged() ) ); 00228 } 00229 } 00230 00231 // If we found or created the view, raise it and refresh it 00232 if ( view ) { 00233 mActiveView = view; 00234 mViewWidgetStack->raiseWidget( view ); 00235 // Set the proper filter in the view. By setting the combo 00236 // box, the activated slot will be called, which will push 00237 // the filter to the view and refresh it. 00238 if ( view->defaultFilterType() == KAddressBookView::None ) { 00239 mFilterSelectionWidget->setCurrentItem( 0 ); 00240 setActiveFilter( 0 ); 00241 } else if ( view->defaultFilterType() == KAddressBookView::Active ) { 00242 setActiveFilter( mFilterSelectionWidget->currentItem() ); 00243 } else { 00244 uint pos = filterPosition( view->defaultFilterName() ); 00245 mFilterSelectionWidget->setCurrentItem( pos ); 00246 setActiveFilter( pos ); 00247 } 00248 00249 // Update the inc search widget to show the fields in the new active 00250 // view. 00251 mActiveView->refresh(); 00252 00253 } else 00254 kdDebug(5720) << "ViewManager::setActiveView: unable to find view\n"; 00255 } 00256 00257 void ViewManager::refreshView( const TQString &uid ) 00258 { 00259 if ( mActiveView ) 00260 mActiveView->refresh( uid ); 00261 } 00262 00263 void ViewManager::editView() 00264 { 00265 if ( !mActiveView ) 00266 return; 00267 00268 ViewFactory *factory = mViewFactoryDict.find( mActiveView->type() ); 00269 ViewConfigureWidget *wdg = 0; 00270 00271 if ( factory ) { 00272 // Save the filters so the dialog has the latest set 00273 Filter::save( mCore->config(), "Filter", mFilterList ); 00274 00275 wdg = factory->configureWidget( mCore->addressBook(), 0 ); 00276 } 00277 00278 if ( wdg ) { 00279 ViewConfigureDialog dlg( wdg, mActiveView->caption(), this ); 00280 00281 KConfigGroupSaver saver( mCore->config(), mActiveView->caption() ); 00282 dlg.restoreSettings( mCore->config() ); 00283 00284 if ( dlg.exec() ) { 00285 dlg.saveSettings( mCore->config() ); 00286 mActiveView->readConfig( mCore->config() ); 00287 // Set the proper filter in the view. By setting the combo 00288 // box, the activated slot will be called, which will push 00289 // the filter to the view and refresh it. 00290 if ( mActiveView->defaultFilterType() == KAddressBookView::None ) { 00291 mFilterSelectionWidget->setCurrentItem( 0 ); 00292 setActiveFilter( 0 ); 00293 } else if ( mActiveView->defaultFilterType() == KAddressBookView::Active ) { 00294 setActiveFilter( mFilterSelectionWidget->currentItem() ); 00295 } else { 00296 uint pos = filterPosition( mActiveView->defaultFilterName() ); 00297 mFilterSelectionWidget->setCurrentItem( pos ); 00298 setActiveFilter( pos ); 00299 } 00300 00301 mActiveView->refresh(); 00302 emit viewFieldsChanged(); 00303 } 00304 } 00305 } 00306 00307 void ViewManager::deleteView() 00308 { 00309 TQString text = i18n( "<qt>Are you sure that you want to delete the view <b>%1</b>?</qt>" ) 00310 .arg( mActiveView->caption() ); 00311 TQString caption = i18n( "Confirm Delete" ); 00312 00313 if ( KMessageBox::warningContinueCancel( this, text, caption, KGuiItem( i18n("&Delete"), "editdelete") ) == KMessageBox::Continue ) { 00314 mViewNameList.remove( mActiveView->caption() ); 00315 00316 // remove the view from the config file 00317 KConfig *config = mCore->config(); 00318 config->deleteGroup( mActiveView->caption() ); 00319 00320 mViewDict.remove( mActiveView->caption() ); 00321 mActiveView = 0; 00322 00323 // we are in an invalid state now, but that should be fixed after 00324 // we emit the signal 00325 mActionSelectView->setItems( mViewNameList ); 00326 if ( mViewNameList.count() > 0 ) { 00327 mActionSelectView->setCurrentItem( 0 ); 00328 setActiveView( mViewNameList[ 0 ] ); 00329 } 00330 mActionDeleteView->setEnabled( mViewNameList.count() > 1 ); 00331 } 00332 } 00333 00334 void ViewManager::addView() 00335 { 00336 AddViewDialog dialog( &mViewFactoryDict, this ); 00337 00338 if ( dialog.exec() ) { 00339 TQString newName = dialog.viewName(); 00340 TQString type = dialog.viewType(); 00341 00342 // Check for name conflicts 00343 bool firstConflict = true; 00344 int numTries = 1; 00345 while ( mViewNameList.contains( newName ) > 0 ) { 00346 if ( !firstConflict ) { 00347 newName = newName.left( newName.length() - 4 ); 00348 firstConflict = false; 00349 } 00350 00351 newName = TQString( "%1 <%2>" ).arg( newName ).arg( numTries ); 00352 numTries++; 00353 } 00354 00355 // Add the new one to the list 00356 mViewNameList.append( newName ); 00357 00358 // write the view to the config file, 00359 KConfig *config = mCore->config(); 00360 config->deleteGroup( newName ); 00361 KConfigGroupSaver saver( config, newName ); 00362 config->writeEntry( "Type", type ); 00363 00364 // try to set the active view 00365 mActionSelectView->setItems( mViewNameList ); 00366 mActionSelectView->setCurrentItem( mViewNameList.findIndex( newName ) ); 00367 setActiveView( newName ); 00368 00369 editView(); 00370 00371 mActionDeleteView->setEnabled( mViewNameList.count() > 1 ); 00372 } 00373 } 00374 00375 void ViewManager::scrollUp() 00376 { 00377 if ( mActiveView ) 00378 mActiveView->scrollUp(); 00379 } 00380 00381 void ViewManager::scrollDown() 00382 { 00383 if ( mActiveView ) 00384 mActiveView->scrollDown(); 00385 } 00386 00387 void ViewManager::createViewFactories() 00388 { 00389 const KTrader::OfferList plugins = KTrader::self()->query( "KAddressBook/View", 00390 TQString( "[X-KDE-KAddressBook-ViewPluginVersion] == %1" ).arg( KAB_VIEW_PLUGIN_VERSION ) ); 00391 KTrader::OfferList::ConstIterator it; 00392 for ( it = plugins.begin(); it != plugins.end(); ++it ) { 00393 if ( !(*it)->hasServiceType( "KAddressBook/View" ) ) 00394 continue; 00395 00396 KLibFactory *factory = KLibLoader::self()->factory( (*it)->library().latin1() ); 00397 00398 if ( !factory ) { 00399 kdDebug(5720) << "ViewManager::createViewFactories(): Factory creation failed" << endl; 00400 continue; 00401 } 00402 00403 ViewFactory *viewFactory = static_cast<ViewFactory*>( factory ); 00404 00405 if ( !viewFactory ) { 00406 kdDebug(5720) << "ViewManager::createViewFactories(): Cast failed" << endl; 00407 continue; 00408 } 00409 00410 mViewFactoryDict.insert( viewFactory->type(), viewFactory ); 00411 } 00412 } 00413 00414 void ViewManager::dropped( TQDropEvent *e ) 00415 { 00416 kdDebug(5720) << "ViewManager::dropped: got a drop event" << endl; 00417 00418 // don't allow drops from our own drags 00419 if ( e->source() == this ) 00420 return; 00421 00422 KABC::Addressee::List list; 00423 KURL::List urls; 00424 00425 if ( KURLDrag::decode( e, urls) ) { 00426 KURL::List::ConstIterator it = urls.begin(); 00427 int c = urls.count(); 00428 if ( c > 1 ) { 00429 TQString questionString = i18n( "Import one contact into your addressbook?", "Import %n contacts into your addressbook?", c ); 00430 if ( KMessageBox::questionYesNo( this, questionString, i18n( "Import Contacts?" ), i18n("Import"), i18n("Do Not Import") ) == KMessageBox::Yes ) { 00431 for ( ; it != urls.end(); ++it ) 00432 emit urlDropped( *it ); 00433 } 00434 } else if ( c == 1 ) 00435 emit urlDropped( *it ); 00436 } else if ( KVCardDrag::decode( e, list ) ) { 00437 KABC::Addressee::List::ConstIterator it; 00438 for ( it = list.begin(); it != list.end(); ++it ) { 00439 KABC::Addressee a = mCore->addressBook()->findByUid( (*it).uid() ); 00440 if ( a.isEmpty() ) { // not yet in address book 00441 mCore->addressBook()->insertAddressee( *it ); 00442 emit modified(); 00443 } 00444 } 00445 00446 mActiveView->refresh(); 00447 } 00448 } 00449 00450 void ViewManager::startDrag() 00451 { 00452 // Get the list of all the selected addressees 00453 KABC::Addressee::List addrList; 00454 const TQStringList uidList = selectedUids(); 00455 if ( uidList.isEmpty() ) 00456 return; 00457 00458 kdDebug(5720) << "ViewManager::startDrag: starting to drag" << endl; 00459 00460 TQStringList::ConstIterator it; 00461 for ( it = uidList.begin(); it != uidList.end(); ++it ) 00462 addrList.append( mCore->addressBook()->findByUid( *it ) ); 00463 00464 KMultipleDrag *drag = new KMultipleDrag( this ); 00465 00466 KABC::VCardConverter converter; 00467 #if defined(KABC_VCARD_ENCODING_FIX) 00468 TQCString vcards = converter.createVCardsRaw( addrList ); 00469 #else 00470 TQString vcards = converter.createVCards( addrList ); 00471 #endif 00472 00473 // Best text representation is given by textdrag, so it must be first 00474 drag->addDragObject( new TQTextDrag( AddresseeUtil::addresseesToEmails( addrList ), this ) ); 00475 drag->addDragObject( new KVCardDrag( vcards, this ) ); 00476 00477 KTempDir tempDir; 00478 // can't set tempDir to autoDelete, in case of dropping on the desktop, the copy is async... 00479 if ( tempDir.status() == 0 ) { 00480 TQString fileName; 00481 if ( addrList.count() == 1 ) 00482 fileName = addrList[ 0 ].givenName() + "_" + addrList[ 0 ].familyName() + ".vcf"; 00483 else 00484 fileName = "contacts.vcf"; 00485 00486 TQFile tempFile( tempDir.name() + "/" + fileName ); 00487 if ( tempFile.open( IO_WriteOnly ) ) { 00488 #if defined(KABC_VCARD_ENCODING_FIX) 00489 tempFile.writeBlock( vcards, vcards.length() ); 00490 #else 00491 tempFile.writeBlock( vcards.utf8() ); 00492 #endif 00493 tempFile.close(); 00494 00495 KURLDrag *urlDrag = new KURLDrag( KURL( tempFile.name() ), this ); 00496 drag->addDragObject( urlDrag ); 00497 } 00498 } 00499 00500 drag->setPixmap( KGlobal::iconLoader()->loadIcon( "vcard", KIcon::Desktop ) ); 00501 drag->dragCopy(); 00502 } 00503 00504 void ViewManager::setActiveFilter( int index ) 00505 { 00506 Filter currentFilter; 00507 00508 if ( ( index - 1 ) < 0 ) 00509 currentFilter = Filter(); 00510 else if ( ( index - 1 ) < 1 ) { 00511 currentFilter = Filter(); 00512 currentFilter.setMatchRule(Filter::NotMatching); 00513 } 00514 else 00515 currentFilter = mFilterList[ index - 2 ]; 00516 00517 // Check if we have a view. Since the filter combo is created before 00518 // the view, this slot could be called before there is a valid view. 00519 if ( mActiveView ) { 00520 mActiveView->setFilter( currentFilter ); 00521 mActiveView->refresh(); 00522 emit selected( TQString() ); 00523 } 00524 } 00525 00526 void ViewManager::configureFilters() 00527 { 00528 FilterDialog dlg( this ); 00529 00530 dlg.setFilters( mFilterList ); 00531 00532 if ( dlg.exec() ) 00533 mFilterList = dlg.filters(); 00534 00535 uint pos = mFilterSelectionWidget->currentItem(); 00536 mFilterSelectionWidget->setItems( filterNames() ); 00537 mFilterSelectionWidget->setCurrentItem( pos ); 00538 setActiveFilter( pos ); 00539 } 00540 00541 TQStringList ViewManager::filterNames() const 00542 { 00543 TQStringList names( i18n( "None" ) ); 00544 names.append( i18n( "Unfiled" ) ); 00545 00546 Filter::List::ConstIterator it; 00547 for ( it = mFilterList.begin(); it != mFilterList.end(); ++it ) 00548 names.append( (*it).name() ); 00549 00550 return names; 00551 } 00552 00553 int ViewManager::filterPosition( const TQString &name ) const 00554 { 00555 int pos = 0; 00556 00557 Filter::List::ConstIterator it; 00558 for ( it = mFilterList.begin(); it != mFilterList.end(); ++it, ++pos ) 00559 if ( name == (*it).name() ) 00560 return pos + 2; 00561 00562 return 0; 00563 } 00564 00565 void ViewManager::initActions() 00566 { 00567 mActionSelectView = new KSelectAction( i18n( "Select View" ), 0, mCore->actionCollection(), "select_view" ); 00568 #if KDE_VERSION >= 309 00569 mActionSelectView->setMenuAccelsEnabled( false ); 00570 #endif 00571 connect( mActionSelectView, TQT_SIGNAL( activated( const TQString& ) ), 00572 TQT_SLOT( setActiveView( const TQString& ) ) ); 00573 00574 KAction *action; 00575 00576 action = new KAction( i18n( "Modify View..." ), "configure", 0, TQT_TQOBJECT(this), 00577 TQT_SLOT( editView() ), mCore->actionCollection(), 00578 "view_modify" ); 00579 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." ) ); 00580 00581 action = new KAction( i18n( "Add View..." ), "window_new", 0, TQT_TQOBJECT(this), 00582 TQT_SLOT( addView() ), mCore->actionCollection(), 00583 "view_add" ); 00584 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." ) ); 00585 00586 mActionDeleteView = new KAction( i18n( "Delete View" ), "view_remove", 0, 00587 TQT_TQOBJECT(this), TQT_SLOT( deleteView() ), 00588 mCore->actionCollection(), "view_delete" ); 00589 mActionDeleteView->setWhatsThis( i18n( "By pressing this button you can delete the actual view, which you have added before." ) ); 00590 00591 action = new KAction( i18n( "Refresh View" ), "reload", 0, TQT_TQOBJECT(this), 00592 TQT_SLOT( refreshView() ), mCore->actionCollection(), 00593 "view_refresh" ); 00594 action->setWhatsThis( i18n( "The view will be refreshed by pressing this button." ) ); 00595 00596 action = new KAction( i18n( "Edit &Filters..." ), "filter", 0, TQT_TQOBJECT(this), 00597 TQT_SLOT( configureFilters() ), mCore->actionCollection(), 00598 "options_edit_filters" ); 00599 action->setWhatsThis( i18n( "Edit the contact filters<p>You will be presented with a dialog, where you can add, remove and edit filters." ) ); 00600 } 00601 00602 void ViewManager::initGUI() 00603 { 00604 TQHBoxLayout *layout = new TQHBoxLayout( this ); 00605 mViewWidgetStack = new TQWidgetStack( this ); 00606 layout->addWidget( mViewWidgetStack ); 00607 } 00608 00609 #include "viewmanager.moc"