korganizer

alarmdialog.cpp

00001 /*
00002     This file is part of the KOrganizer alarm daemon.
00003 
00004     Copyright (c) 2000,2003 Cornelius Schumacher <schumacher@kde.org>
00005     Copyright (c) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
00006 
00007     This program is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020 
00021     As a special exception, permission is given to link this program
00022     with any edition of Qt, and distribute the resulting executable,
00023     without including the source code for Qt in the source distribution.
00024 */
00025 
00026 #include <qhbox.h>
00027 #include <qvbox.h>
00028 #include <qlabel.h>
00029 #include <qfile.h>
00030 #include <qspinbox.h>
00031 #include <qlayout.h>
00032 #include <qpushbutton.h>
00033 #include <qcstring.h>
00034 #include <qdatastream.h>
00035 
00036 #include <dcopclient.h>
00037 #include <dcopref.h>
00038 #include <kapplication.h>
00039 #include <kconfig.h>
00040 #include <kdcopservicestarter.h>
00041 #include <kiconloader.h>
00042 #include <klocale.h>
00043 #include <kprocess.h>
00044 #include <kaudioplayer.h>
00045 #include <kdebug.h>
00046 #include <kmessagebox.h>
00047 #include <knotifyclient.h>
00048 #include <kcombobox.h>
00049 #include <klistview.h>
00050 #include <kwin.h>
00051 #include <klockfile.h>
00052 
00053 #include <libkcal/event.h>
00054 #include <libkcal/incidenceformatter.h>
00055 
00056 #include "koeventviewer.h"
00057 
00058 #include "alarmdialog.h"
00059 #include "alarmdialog.moc"
00060 
00061 static int defSuspendVal = 5;
00062 static int defSuspendUnit = 0; // 0=>minutes, 1=>hours, 2=>days, 3=>weeks
00063 static QDateTime defModDateTime = QDateTime( QDate( 1970, 1, 1 ), QTime( 0, 0, 0 ) );
00064 
00065 class AlarmListItem : public KListViewItem
00066 {
00067   public:
00068     AlarmListItem( const QString &uid, QListView *parent ) :
00069       KListViewItem( parent ),
00070       mUid( uid ),
00071       mNotified( false )
00072     {}
00073 
00074     ~AlarmListItem()
00075     {
00076     }
00077 
00078     int compare( KListViewItem *item, int iCol, bool bAscending ) const;
00079 
00080     QString mDisplayText;
00081 
00082     QString mUid;
00083     int mLastRevision;
00084     QDateTime mLastModified;
00085     QDateTime mRemindAt;
00086     QDateTime mHappening;
00087     bool mNotified;
00088 };
00089 
00090 int AlarmListItem::compare( KListViewItem *item, int iCol, bool bAscending ) const
00091 {
00092   if ( iCol == 1 ) {
00093     AlarmListItem *pItem = static_cast<AlarmListItem *>( item );
00094     return pItem->mHappening.secsTo( mHappening );
00095   } else {
00096     return KListViewItem::compare( item, iCol, bAscending );
00097   }
00098 }
00099 
00100 typedef QValueList<AlarmListItem*> ItemList;
00101 
00102 AlarmDialog::AlarmDialog( KCal::CalendarResources *calendar, QWidget *parent, const char *name )
00103   : KDialogBase( Plain, WType_TopLevel | WStyle_Customize | WStyle_StaysOnTop |
00104                  WStyle_DialogBorder,
00105                  parent, name, false, i18n("Reminder"),
00106                  Ok | User1 | User2 | User3, NoDefault,
00107                  false, i18n("Edit..."), i18n("Dismiss All"), i18n("Dismiss Reminder") ),
00108                  mCalendar( calendar ), mSuspendTimer(this)
00109 {
00110   // User1 => Edit...
00111   // User2 => Dismiss All
00112   // User3 => Dismiss Selected
00113   //    Ok => Suspend
00114 
00115   KGlobal::iconLoader()->addAppDir( "kdepim" );
00116   setButtonOK( i18n( "Suspend" ) );
00117 
00118   QWidget *topBox = plainPage();
00119   QBoxLayout *topLayout = new QVBoxLayout( topBox );
00120   topLayout->setSpacing( spacingHint() );
00121 
00122   QLabel *label = new QLabel( i18n("The following items triggered reminders:"),
00123                               topBox );
00124   topLayout->addWidget( label );
00125 
00126   mIncidenceListView = new KListView( topBox );
00127   mIncidenceListView->addColumn( i18n( "Summary" ) );
00128   mIncidenceListView->addColumn( i18n( "Due" ) );
00129   mIncidenceListView->setSorting( 0, true );
00130   mIncidenceListView->setSorting( 1, true );
00131   mIncidenceListView->setSortColumn( 1 );
00132   mIncidenceListView->setShowSortIndicator( true );
00133   mIncidenceListView->setAllColumnsShowFocus( true );
00134   mIncidenceListView->setSelectionMode( QListView::Extended );
00135   topLayout->addWidget( mIncidenceListView );
00136   connect( mIncidenceListView, SIGNAL(selectionChanged()), SLOT(updateButtons()) );
00137   connect( mIncidenceListView, SIGNAL(doubleClicked(QListViewItem*)), SLOT(edit()) );
00138   connect( mIncidenceListView, SIGNAL(currentChanged(QListViewItem*)), SLOT(showDetails()) );
00139   connect( mIncidenceListView, SIGNAL(selectionChanged()), SLOT(showDetails()) );
00140 
00141   mDetailView = new KOEventViewer( mCalendar, topBox );
00142   mDetailView->setFocus(); // set focus here to start with to make it harder
00143                            // to hit return by mistake and dismiss a reminder.
00144   topLayout->addWidget( mDetailView );
00145 
00146   QHBox *suspendBox = new QHBox( topBox );
00147   suspendBox->setSpacing( spacingHint() );
00148   topLayout->addWidget( suspendBox );
00149 
00150   QLabel *l = new QLabel( i18n("Suspend &duration:"), suspendBox );
00151   mSuspendSpin = new QSpinBox( 1, 9999, 1, suspendBox );
00152   mSuspendSpin->setValue( defSuspendVal );  // default suspend duration
00153   l->setBuddy( mSuspendSpin );
00154 
00155   mSuspendUnit = new KComboBox( suspendBox );
00156   mSuspendUnit->insertItem( i18n("minute(s)") );
00157   mSuspendUnit->insertItem( i18n("hour(s)") );
00158   mSuspendUnit->insertItem( i18n("day(s)") );
00159   mSuspendUnit->insertItem( i18n("week(s)") );
00160   mSuspendUnit->setCurrentItem( defSuspendUnit );
00161 
00162   connect( &mSuspendTimer, SIGNAL(timeout()), SLOT(wakeUp()) );
00163 
00164   setMinimumSize( 300, 200 );
00165 }
00166 
00167 AlarmDialog::~AlarmDialog()
00168 {
00169   mIncidenceListView->clear();
00170 }
00171 
00172 void AlarmDialog::addIncidence( Incidence *incidence,
00173                                 const QDateTime &reminderAt,
00174                                 const QString &displayText )
00175 {
00176   AlarmListItem *item = new AlarmListItem( incidence->uid(), mIncidenceListView );
00177   item->setText( 0, incidence->summary() );
00178   item->mRemindAt = reminderAt;
00179   item->mDisplayText = displayText;
00180   item->mLastRevision = incidence->revision();
00181   if ( incidence->lastModified().isValid() ) {
00182     item->mLastModified = incidence->lastModified();
00183   } else {
00184     item->mLastModified = defModDateTime;
00185   }
00186 
00187   Event *event;
00188   Todo *todo;
00189   Alarm *alarm = incidence->alarms().first();
00190   if ( incidence->type() == "Event" ) {
00191     item->setPixmap( 0, SmallIcon( "appointment" ) );
00192     event = static_cast<Event *>( incidence );
00193     if ( event->doesRecur() ) {
00194       QDateTime nextStart = event->recurrence()->getNextDateTime( reminderAt );
00195       if ( nextStart.isValid() ) {
00196         item->mHappening = nextStart;
00197         item->setText( 1, KGlobal::locale()->formatDateTime( nextStart ) );
00198       }
00199     }
00200     if ( item->text( 1 ).isEmpty() ) {
00201       QDateTime qdt;
00202       if ( alarm->hasStartOffset() ) {
00203         qdt = event->dtStart();
00204       } else {
00205         qdt = event->dtEnd();
00206       }
00207       item->mHappening = qdt;
00208       item->setText( 1, IncidenceFormatter::dateTimeToString( qdt, false, true ) );
00209     }
00210   } else if ( incidence->type() == "Todo" ) {
00211     item->setPixmap( 0, SmallIcon( "todo" ) );
00212     todo = static_cast<Todo *>( incidence );
00213     if ( todo->doesRecur() ) {
00214       QDateTime nextStart = todo->recurrence()->getNextDateTime( reminderAt );
00215       if ( nextStart.isValid() ) {
00216         item->mHappening = nextStart;
00217         item->setText( 1, KGlobal::locale()->formatDateTime( nextStart ) );
00218       }
00219     }
00220     if ( item->text( 1 ).isEmpty() ) {
00221       QDateTime qdt;
00222       if ( alarm->hasStartOffset() && todo->dtStart().isValid() ) {
00223         qdt = todo->dtStart();
00224       } else {
00225         qdt = todo->dtDue();
00226       }
00227       item->mHappening = qdt;
00228       item->setText( 1, IncidenceFormatter::dateTimeToString( qdt, false, true ) );
00229     }
00230   }
00231 
00232   if ( activeCount() == 1 ) {// previously empty
00233     mIncidenceListView->clearSelection();
00234     item->setSelected( true );
00235   }
00236   showDetails();
00237 }
00238 
00239 void AlarmDialog::slotOk()
00240 {
00241   suspend();
00242 }
00243 
00244 void AlarmDialog::slotUser1()
00245 {
00246   edit();
00247 }
00248 
00249 void AlarmDialog::slotUser2()
00250 {
00251   dismissAll();
00252 }
00253 
00254 void AlarmDialog::slotUser3()
00255 {
00256   dismissCurrent();
00257 }
00258 
00259 void AlarmDialog::dismissCurrent()
00260 {
00261   ItemList selection = selectedItems();
00262   for ( ItemList::Iterator it = selection.begin(); it != selection.end(); ++it ) {
00263     if ( (*it)->itemBelow() )
00264       (*it)->itemBelow()->setSelected( true );
00265     else if ( (*it)->itemAbove() )
00266       (*it)->itemAbove()->setSelected( true );
00267     delete *it;
00268   }
00269   if ( activeCount() == 0 ) {
00270     accept();
00271   } else {
00272     updateButtons();
00273     showDetails();
00274   }
00275   emit reminderCount( activeCount() );
00276 }
00277 
00278 void AlarmDialog::dismissAll()
00279 {
00280   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ) {
00281     AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00282     if ( !item->isVisible() ) {
00283       ++it;
00284       continue;
00285     }
00286     delete item;
00287   }
00288   setTimer();
00289   accept();
00290   emit reminderCount( activeCount() );
00291 }
00292 
00293 void AlarmDialog::edit()
00294 {
00295   ItemList selection = selectedItems();
00296   if ( selection.count() != 1 ) {
00297     return;
00298   }
00299   Incidence *incidence = mCalendar->incidence( selection.first()->mUid );
00300   if ( !incidence ) {
00301     return;
00302   }
00303   QDate dt = selection.first()->mRemindAt.date();
00304 
00305   if ( incidence->isReadOnly() ) {
00306     KMessageBox::sorry(
00307       this,
00308       i18n( "\"%1\" is a read-only item so modifications are not possible." ).
00309       arg( incidence->summary() ) );
00310     return;
00311   }
00312 
00313   if ( !ensureKorganizerRunning() ) {
00314     KMessageBox::error(
00315       this,
00316       i18n( "Could not start KOrganizer so editing is not possible." ) );
00317     return;
00318   }
00319 
00320   QByteArray data;
00321   QDataStream arg( data, IO_WriteOnly );
00322   arg << incidence->uid();
00323   arg << dt;
00324   //kdDebug(5890) << "editing incidence " << incidence->summary() << endl;
00325   if ( !kapp->dcopClient()->send( "korganizer", "KOrganizerIface",
00326                                   "editIncidence(QString,QDate)",
00327                                   data ) ) {
00328     KMessageBox::error(
00329       this,
00330       i18n( "An internal KOrganizer error occurred attempting to start the incidence editor" ) );
00331     return;
00332   }
00333 
00334   // get desktop # where korganizer (or kontact) runs
00335   QByteArray replyData;
00336   QCString object, replyType;
00337   object = kapp->dcopClient()->isApplicationRegistered( "kontact" ) ?
00338            "kontact-mainwindow#1" : "KOrganizer MainWindow";
00339   if (!kapp->dcopClient()->call( "korganizer", object,
00340                             "getWinID()", 0, replyType, replyData, true, -1 ) ) {
00341   }
00342 
00343   if ( replyType == "int" ) {
00344     int desktop, window;
00345     QDataStream ds( replyData, IO_ReadOnly );
00346     ds >> window;
00347     desktop = KWin::windowInfo( window ).desktop();
00348 
00349     if ( KWin::currentDesktop() == desktop ) {
00350       KWin::iconifyWindow( winId(), false );
00351     } else {
00352       KWin::setCurrentDesktop( desktop );
00353     }
00354     KWin::activateWindow( KWin::transientFor( window ) );
00355   }
00356 }
00357 
00358 void AlarmDialog::suspend()
00359 {
00360   if ( !isVisible() )
00361     return;
00362 
00363   int unit=1;
00364   switch (mSuspendUnit->currentItem()) {
00365     case 3: // weeks
00366       unit *=  7;
00367     case 2: // days
00368       unit *= 24;
00369     case 1: // hours
00370       unit *= 60;
00371     case 0: // minutes
00372       unit *= 60;
00373     default:
00374       break;
00375   }
00376 
00377   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00378     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00379     if ( item->isSelected() && item->isVisible() ) {
00380       item->setVisible( false );
00381       item->setSelected( false );
00382       item->mRemindAt = QDateTime::currentDateTime().addSecs( unit * mSuspendSpin->value() );
00383       item->mNotified = false;
00384     }
00385   }
00386 
00387   // save suspended alarms too so they can be restored on restart
00388   // kolab/issue4108
00389   slotSave();
00390 
00391   setTimer();
00392   if ( activeCount() == 0 ) {
00393     accept();
00394   } else {
00395     updateButtons();
00396     showDetails();
00397   }
00398   emit reminderCount( activeCount() );
00399 }
00400 
00401 void AlarmDialog::setTimer()
00402 {
00403   int nextReminderAt = -1;
00404   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00405     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00406     if ( item->mRemindAt > QDateTime::currentDateTime() ) {
00407       int secs = QDateTime::currentDateTime().secsTo( item->mRemindAt );
00408       nextReminderAt = nextReminderAt <= 0 ? secs : QMIN( nextReminderAt, secs );
00409     }
00410   }
00411 
00412   if ( nextReminderAt >= 0 ) {
00413     mSuspendTimer.stop();
00414     mSuspendTimer.start( 1000 * (nextReminderAt + 1), true );
00415   }
00416 }
00417 
00418 void AlarmDialog::show()
00419 {
00420   mIncidenceListView->sort();
00421 
00422   mIncidenceListView->clearSelection();
00423   if ( mIncidenceListView->firstChild() )
00424     mIncidenceListView->firstChild()->setSelected( true );
00425 
00426   updateButtons();
00427   showDetails();
00428 
00429   // reset the default suspend time
00430   mSuspendSpin->setValue( defSuspendVal );
00431   mSuspendUnit->setCurrentItem( defSuspendUnit );
00432 
00433   KDialogBase::show();
00434   KWin::deIconifyWindow( winId(), false );
00435   KWin::setState( winId(), NET::KeepAbove );
00436   KWin::setOnAllDesktops( winId(), true );
00437   eventNotification();
00438 }
00439 
00440 void AlarmDialog::eventNotification()
00441 {
00442   bool beeped = false, found = false;
00443 
00444   QValueList<AlarmListItem*> list;
00445   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00446     AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00447     if ( !item->isVisible() || item->mNotified ) {
00448       continue;
00449     }
00450     Incidence *incidence = mCalendar->incidence( item->mUid );
00451     if ( !incidence ) {
00452       continue;
00453     }
00454     found = true;
00455     item->mNotified = true;
00456     Alarm::List alarms = incidence->alarms();
00457     Alarm::List::ConstIterator it;
00458     for ( it = alarms.begin(); it != alarms.end(); ++it ) {
00459       Alarm *alarm = *it;
00460       // FIXME: Check whether this should be done for all multiple alarms
00461       if (alarm->type() == Alarm::Procedure) {
00462         // FIXME: Add a message box asking whether the procedure should really be executed
00463         kdDebug(5890) << "Starting program: '" << alarm->programFile() << "'" << endl;
00464         KProcess proc;
00465         proc << QFile::encodeName(alarm->programFile());
00466         proc.start(KProcess::DontCare);
00467       }
00468       else if (alarm->type() == Alarm::Audio) {
00469         beeped = true;
00470         KAudioPlayer::play(QFile::encodeName(alarm->audioFile()));
00471       }
00472     }
00473   }
00474 
00475   if ( !beeped && found ) {
00476     KNotifyClient::beep();
00477   }
00478 }
00479 
00480 void AlarmDialog::wakeUp()
00481 {
00482   bool activeReminders = false;
00483   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00484     AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00485     Incidence *incidence = mCalendar->incidence( item->mUid );
00486     if ( !incidence ) {
00487       delete item;
00488       continue;
00489     }
00490 
00491     QDateTime ilm;
00492     if ( incidence->lastModified().isValid() ) {
00493       ilm = incidence->lastModified();
00494     } else {
00495       ilm = defModDateTime;
00496     }
00497 
00498     // Check revision and last-modified to see if the incidence has been
00499     // edited since the last time we woke up.  If it was edited, then
00500     // remove it as the new version will have been added in addIncidence().
00501     if ( item->mRemindAt <= QDateTime::currentDateTime() &&
00502          incidence->revision() <= item->mLastRevision &&
00503          ilm <= item->mLastModified ) {
00504       if ( !item->isVisible() ) {
00505         item->setVisible( true );
00506         item->setSelected( false );
00507       }
00508       activeReminders = true;
00509     } else {
00510       item->setVisible( false );
00511     }
00512     item->mLastRevision = incidence->revision();
00513     item->mLastModified = incidence->lastModified();
00514   }
00515 
00516   if ( activeReminders )
00517     show();
00518   setTimer();
00519   showDetails();
00520   emit reminderCount( activeCount() );
00521 }
00522 
00523 void AlarmDialog::slotSave()
00524 {
00525   KConfig *config = kapp->config();
00526   KLockFile::Ptr lock = config->lockFile();
00527   if ( lock.data()->lock() != KLockFile::LockOK )
00528     return;
00529 
00530   config->setGroup( "General" );
00531   int numReminders = config->readNumEntry("Reminders", 0);
00532 
00533   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00534     AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00535     Incidence *incidence = mCalendar->incidence( item->mUid );
00536     if ( !incidence ) {
00537       continue;
00538     }
00539     config->setGroup( QString("Incidence-%1").arg(numReminders + 1) );
00540     config->writeEntry( "UID", incidence->uid() );
00541     config->writeEntry( "RemindAt", item->mRemindAt );
00542     ++numReminders;
00543   }
00544 
00545   config->setGroup( "General" );
00546   config->writeEntry( "Reminders", numReminders );
00547   config->sync();
00548   lock.data()->unlock();
00549 }
00550 
00551 void AlarmDialog::updateButtons()
00552 {
00553   ItemList selection = selectedItems();
00554   enableButton( User1, selection.count() == 1 ); // can only edit 1 at a time
00555   enableButton( User3, selection.count() > 0 );  // dismiss 1 or more
00556   enableButton( Ok, selection.count() > 0 );     // suspend 1 or more
00557 }
00558 
00559 QValueList< AlarmListItem * > AlarmDialog::selectedItems() const
00560 {
00561   QValueList<AlarmListItem*> list;
00562   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00563     if ( it.current()->isSelected() )
00564       list.append( static_cast<AlarmListItem*>( it.current() ) );
00565   }
00566   return list;
00567 }
00568 
00569 int AlarmDialog::activeCount()
00570 {
00571   int count = 0;
00572   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00573     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00574     if ( item->isVisible() )
00575       ++count;
00576   }
00577   return count;
00578 }
00579 
00580 void AlarmDialog::suspendAll()
00581 {
00582   mIncidenceListView->clearSelection();
00583   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00584     if ( it.current()->isVisible() )
00585       it.current()->setSelected( true );
00586   }
00587   suspend();
00588 }
00589 
00590 void AlarmDialog::showDetails()
00591 {
00592   mDetailView->clearEvents( true );
00593   mDetailView->clear();
00594   AlarmListItem *item = static_cast<AlarmListItem*>( mIncidenceListView->currentItem() );
00595   if ( !item || !item->isVisible() )
00596     return;
00597 
00598   Incidence *incidence = mCalendar->incidence( item->mUid );
00599   if ( !incidence ) {
00600     return;
00601   }
00602 
00603   if ( !item->mDisplayText.isEmpty() ) {
00604     QString txt = "<qt><p><b>" + item->mDisplayText + "</b></p></qt>";
00605     mDetailView->addText( txt );
00606   }
00607   item->setText( 0, incidence->summary() );
00608   mDetailView->appendIncidence( incidence, item->mRemindAt.date() );
00609 }
00610 
00611 bool AlarmDialog::ensureKorganizerRunning() const
00612 {
00613   QString error;
00614   QCString dcopService;
00615 
00616   int result = KDCOPServiceStarter::self()->findServiceFor(
00617     "DCOP/Organizer", QString::null, QString::null, &error, &dcopService );
00618 
00619   if ( result == 0 ) {
00620     // OK, so korganizer (or kontact) is running. Now ensure the object we
00621     // want is available [that's not the case when kontact was already running,
00622     // but korganizer not loaded into it...]
00623     static const char* const dcopObjectId = "KOrganizerIface";
00624     QCString dummy;
00625     if ( !kapp->dcopClient()->findObject(
00626            dcopService, dcopObjectId, "", QByteArray(), dummy, dummy ) ) {
00627       DCOPRef ref( dcopService, dcopService ); // talk to KUniqueApplication or its kontact wrapper
00628       DCOPReply reply = ref.call( "load()" );
00629       if ( reply.isValid() && (bool)reply ) {
00630         Q_ASSERT( kapp->dcopClient()->findObject(
00631                     dcopService, dcopObjectId, "", QByteArray(), dummy, dummy ) );
00632       } else {
00633         kdWarning() << "Error loading " << dcopService << endl;
00634       }
00635     }
00636 
00637     // We don't do anything with it we just need it to be running
00638     return true;
00639 
00640   } else {
00641     kdWarning() << "Couldn't start DCOP/Organizer: " << dcopService
00642                 << " " << error << endl;
00643   }
00644   return false;
00645 }
KDE Home | KDE Accessibility Home | Description of Access Keys