kaddressbook

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"