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 Qt, and distribute the resulting executable,
00021     without including the source code for Qt in the source distribution.
00022 */
00023 
00024 #include <qfile.h>
00025 #include <qlayout.h>
00026 #include <qwidgetstack.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, QWidget *parent, const char *name )
00053   : QWidget( 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   QString 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   QDictIterator<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   QDictIterator<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 QStringList ViewManager::selectedUids() const
00114 {
00115   if ( mActiveView ) {
00116     return mActiveView->selectedUids();
00117   } else
00118     return QStringList();
00119 }
00120 
00121 QStringList ViewManager::selectedEmails() const
00122 {
00123   if ( mActiveView )
00124     return mActiveView->selectedEmails();
00125   else
00126     return QStringList();
00127 }
00128 
00129 KABC::Addressee::List ViewManager::selectedAddressees() const
00130 {
00131   KABC::Addressee::List list;
00132 
00133   const QStringList uids = selectedUids();
00134   QStringList::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 QString &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 QString &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     QString 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, SIGNAL( selected( const QString& ) ),
00220                SIGNAL( selected( const QString & ) ) );
00221       connect( view, SIGNAL( executed( const QString& ) ),
00222                SIGNAL( executed( const QString& ) ) );
00223       connect( view, SIGNAL( modified() ), SIGNAL( modified() ) );
00224       connect( view, SIGNAL( dropped( QDropEvent* ) ),
00225                SLOT( dropped( QDropEvent* ) ) );
00226       connect( view, SIGNAL( startDrag() ), SLOT( startDrag() ) );
00227       connect( view, SIGNAL( sortFieldChanged() ), 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 QString &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   QString text = i18n( "<qt>Are you sure that you want to delete the view <b>%1</b>?</qt>" )
00310                      .arg( mActiveView->caption() );
00311   QString 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     QString newName = dialog.viewName();
00340     QString 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 = QString( "%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     QString( "[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( QDropEvent *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   QString clipText, vcards;
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       QString 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, vcards ) ) {
00437     KABC::VCardConverter converter;
00438 
00439     const KABC::Addressee::List list = converter.parseVCards( vcards );
00440     KABC::Addressee::List::ConstIterator it;
00441     for ( it = list.begin(); it != list.end(); ++it ) {
00442       KABC::Addressee a = mCore->addressBook()->findByUid( (*it).uid() );
00443       if ( a.isEmpty() ) { // not yet in address book
00444         mCore->addressBook()->insertAddressee( *it );
00445         emit modified();
00446       }
00447     }
00448 
00449     mActiveView->refresh();
00450   }
00451 }
00452 
00453 void ViewManager::startDrag()
00454 {
00455   // Get the list of all the selected addressees
00456   KABC::Addressee::List addrList;
00457   const QStringList uidList = selectedUids();
00458   if (  uidList.isEmpty() )
00459     return;
00460 
00461   kdDebug(5720) << "ViewManager::startDrag: starting to drag" << endl;
00462 
00463   QStringList::ConstIterator it;
00464   for ( it = uidList.begin(); it != uidList.end(); ++it )
00465     addrList.append( mCore->addressBook()->findByUid( *it ) );
00466 
00467   KMultipleDrag *drag = new KMultipleDrag( this );
00468 
00469   KABC::VCardConverter converter;
00470   QString vcards = converter.createVCards( addrList );
00471 
00472   // Best text representation is given by textdrag, so it must be first
00473   drag->addDragObject( new QTextDrag( AddresseeUtil::addresseesToEmails( addrList ), this ) );
00474   drag->addDragObject( new KVCardDrag( vcards, this ) );
00475 
00476   KTempDir tempDir;
00477   // can't set tempDir to autoDelete, in case of dropping on the desktop, the copy is async...
00478   if ( tempDir.status() == 0 ) {
00479     QString fileName;
00480     if ( addrList.count() == 1 )
00481       fileName = addrList[ 0 ].givenName() + "_" + addrList[ 0 ].familyName() + ".vcf";
00482     else
00483       fileName = "contacts.vcf";
00484 
00485     QFile tempFile( tempDir.name() + "/" + fileName );
00486     if ( tempFile.open( IO_WriteOnly ) ) {
00487       tempFile.writeBlock( vcards.utf8() );
00488       tempFile.close();
00489 
00490       KURLDrag *urlDrag = new KURLDrag( KURL( tempFile.name() ), this );
00491       drag->addDragObject( urlDrag );
00492     }
00493   }
00494 
00495   drag->setPixmap( KGlobal::iconLoader()->loadIcon( "vcard", KIcon::Desktop ) );
00496   drag->dragCopy();
00497 }
00498 
00499 void ViewManager::setActiveFilter( int index )
00500 {
00501   Filter currentFilter;
00502 
00503   if ( ( index - 1 ) < 0 )
00504     currentFilter = Filter();
00505   else if ( ( index - 1 ) < 1 ) {
00506     currentFilter = Filter();
00507     currentFilter.setMatchRule(Filter::NotMatching);
00508   }
00509   else
00510     currentFilter = mFilterList[ index - 2 ];
00511 
00512   // Check if we have a view. Since the filter combo is created before
00513   // the view, this slot could be called before there is a valid view.
00514   if ( mActiveView ) {
00515     mActiveView->setFilter( currentFilter );
00516     mActiveView->refresh();
00517     emit selected( QString::null );
00518   }
00519 }
00520 
00521 void ViewManager::configureFilters()
00522 {
00523   FilterDialog dlg( this );
00524 
00525   dlg.setFilters( mFilterList );
00526 
00527   if ( dlg.exec() )
00528     mFilterList = dlg.filters();
00529 
00530   uint pos = mFilterSelectionWidget->currentItem();
00531   mFilterSelectionWidget->setItems( filterNames() );
00532   mFilterSelectionWidget->setCurrentItem( pos );
00533   setActiveFilter( pos );
00534 }
00535 
00536 QStringList ViewManager::filterNames() const
00537 {
00538   QStringList names( i18n( "None" ) );
00539   names.append( i18n( "Unfiled" ) );
00540 
00541   Filter::List::ConstIterator it;
00542   for ( it = mFilterList.begin(); it != mFilterList.end(); ++it )
00543     names.append( (*it).name() );
00544 
00545   return names;
00546 }
00547 
00548 int ViewManager::filterPosition( const QString &name ) const
00549 {
00550   int pos = 0;
00551 
00552   Filter::List::ConstIterator it;
00553   for ( it = mFilterList.begin(); it != mFilterList.end(); ++it, ++pos )
00554     if ( name == (*it).name() )
00555       return pos + 2;
00556 
00557   return 0;
00558 }
00559 
00560 void ViewManager::initActions()
00561 {
00562   mActionSelectView = new KSelectAction( i18n( "Select View" ), 0, mCore->actionCollection(), "select_view" );
00563 #if KDE_VERSION >= 309
00564   mActionSelectView->setMenuAccelsEnabled( false );
00565 #endif
00566   connect( mActionSelectView, SIGNAL( activated( const QString& ) ),
00567            SLOT( setActiveView( const QString& ) ) );
00568 
00569   KAction *action;
00570 
00571   action = new KAction( i18n( "Modify View..." ), "configure", 0, this,
00572                         SLOT( editView() ), mCore->actionCollection(),
00573                         "view_modify" );
00574   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." ) );
00575 
00576   action = new KAction( i18n( "Add View..." ), "window_new", 0, this,
00577                         SLOT( addView() ), mCore->actionCollection(),
00578                         "view_add" );
00579   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." ) );
00580 
00581   mActionDeleteView = new KAction( i18n( "Delete View" ), "view_remove", 0,
00582                                    this, SLOT( deleteView() ),
00583                                    mCore->actionCollection(), "view_delete" );
00584   mActionDeleteView->setWhatsThis( i18n( "By pressing this button you can delete the actual view, which you have added before." ) );
00585 
00586   action = new KAction( i18n( "Refresh View" ), "reload", 0, this,
00587                SLOT( refreshView() ), mCore->actionCollection(),
00588                "view_refresh" );
00589   action->setWhatsThis( i18n( "The view will be refreshed by pressing this button." ) );
00590 
00591   action = new KAction( i18n( "Edit &Filters..." ), "filter", 0, this,
00592                SLOT( configureFilters() ), mCore->actionCollection(),
00593                "options_edit_filters" );
00594   action->setWhatsThis( i18n( "Edit the contact filters<p>You will be presented with a dialog, where you can add, remove and edit filters." ) );
00595 }
00596 
00597 void ViewManager::initGUI()
00598 {
00599   QHBoxLayout *layout = new QHBoxLayout( this );
00600   mViewWidgetStack = new QWidgetStack( this );
00601   layout->addWidget( mViewWidgetStack );
00602 }
00603 
00604 #include "viewmanager.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys