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 
00006     This program is free software; you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License as published by
00008     the Free Software Foundation; either version 2 of the License, or
00009     (at your option) any later version.
00010 
00011     This program is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014     GNU General Public License for more details.
00015 
00016     You should have received a copy of the GNU General Public License
00017     along with this program; if not, write to the Free Software
00018     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019 
00020     As a special exception, permission is given to link this program
00021     with any edition of Qt, and distribute the resulting executable,
00022     without including the source code for Qt in the source distribution.
00023 */
00024 
00025 #include <qhbox.h>
00026 #include <qvbox.h>
00027 #include <qlabel.h>
00028 #include <qfile.h>
00029 #include <qspinbox.h>
00030 #include <qlayout.h>
00031 #include <qpushbutton.h>
00032 #include <qcstring.h>
00033 #include <qdatastream.h>
00034 
00035 #include <kapplication.h>
00036 #include <kconfig.h>
00037 #include <kiconloader.h>
00038 #include <dcopclient.h>
00039 #include <klocale.h>
00040 #include <kprocess.h>
00041 #include <kaudioplayer.h>
00042 #include <kdebug.h>
00043 #include <kmessagebox.h>
00044 #include <knotifyclient.h>
00045 #include <kcombobox.h>
00046 #include <klistview.h>
00047 #include <kwin.h>
00048 #include <klockfile.h>
00049 
00050 #include <libkcal/event.h>
00051 
00052 #include "koeventviewer.h"
00053 
00054 #include "alarmdialog.h"
00055 #include "alarmdialog.moc"
00056 
00057 class AlarmListItem : public KListViewItem
00058 {
00059   public:
00060     AlarmListItem( Incidence *incidence, QListView *parent ) :
00061       KListViewItem( parent ),
00062       mIncidence( incidence->clone() ),
00063       mNotified( false )
00064     {}
00065 
00066     ~AlarmListItem()
00067     {
00068       delete mIncidence;
00069     }
00070 
00071     Incidence *mIncidence;
00072     QDateTime mRemindAt;
00073     bool mNotified;
00074 };
00075 
00076 typedef QValueList<AlarmListItem*> ItemList;
00077 
00078 AlarmDialog::AlarmDialog( QWidget *parent, const char *name )
00079   : KDialogBase( Plain, WType_TopLevel | WStyle_Customize | WStyle_StaysOnTop |
00080                  WStyle_DialogBorder,
00081                  parent, name, false, i18n("Reminder"), Ok | User1 | User2 | User3, User1/*3*/,
00082                  false, i18n("Dismiss All"), i18n("Edit..."), i18n("Suspend") ),
00083                  mSuspendTimer(this)
00084 {
00085   KGlobal::iconLoader()->addAppDir( "kdepim" );
00086   setButtonOK( i18n( "Dismiss Reminder" ) );
00087 
00088   QWidget *topBox = plainPage();
00089   QBoxLayout *topLayout = new QVBoxLayout( topBox );
00090   topLayout->setSpacing( spacingHint() );
00091 
00092   QLabel *label = new QLabel( i18n("The following events triggered reminders:"),
00093                               topBox );
00094   topLayout->addWidget( label );
00095 
00096   mIncidenceListView = new KListView( topBox );
00097   mIncidenceListView->addColumn( i18n( "Summary" ) );
00098   mIncidenceListView->addColumn( i18n( "Due" ) );
00099   mIncidenceListView->setAllColumnsShowFocus( true );
00100   mIncidenceListView->setSelectionMode( QListView::Extended );
00101   topLayout->addWidget( mIncidenceListView );
00102   connect( mIncidenceListView, SIGNAL(selectionChanged()), SLOT(updateButtons()) );
00103   connect( mIncidenceListView, SIGNAL(doubleClicked(QListViewItem*)), SLOT(slotUser2()) );
00104   connect( mIncidenceListView, SIGNAL(currentChanged(QListViewItem*)), SLOT(showDetails()) );
00105   connect( mIncidenceListView, SIGNAL(selectionChanged()), SLOT(showDetails()) );
00106 
00107   mDetailView = new KOEventViewer( topBox );
00108   topLayout->addWidget( mDetailView );
00109 
00110   QHBox *suspendBox = new QHBox( topBox );
00111   suspendBox->setSpacing( spacingHint() );
00112   topLayout->addWidget( suspendBox );
00113 
00114   QLabel *l = new QLabel( i18n("Suspend &duration:"), suspendBox );
00115   mSuspendSpin = new QSpinBox( 1, 9999, 1, suspendBox );
00116   mSuspendSpin->setValue( 5 );  // default suspend duration
00117   l->setBuddy( mSuspendSpin );
00118 
00119   mSuspendUnit = new KComboBox( suspendBox );
00120   mSuspendUnit->insertItem( i18n("minute(s)") );
00121   mSuspendUnit->insertItem( i18n("hour(s)") );
00122   mSuspendUnit->insertItem( i18n("day(s)") );
00123   mSuspendUnit->insertItem( i18n("week(s)") );
00124   connect( &mSuspendTimer, SIGNAL(timeout()), SLOT(wakeUp()) );
00125 
00126   // showButton( User2/*3*/, false );
00127 
00128   setMinimumSize( 300, 200 );
00129 }
00130 
00131 AlarmDialog::~AlarmDialog()
00132 {
00133   mIncidenceListView->clear();
00134 }
00135 
00136 void AlarmDialog::addIncidence( Incidence *incidence, const QDateTime &reminderAt )
00137 {
00138   AlarmListItem *item = new AlarmListItem( incidence, mIncidenceListView );
00139   item->setText( 0, incidence->summary() );
00140   item->mRemindAt = reminderAt;
00141   Todo *todo;
00142   if ( dynamic_cast<Event*>( incidence ) ) {
00143     item->setPixmap( 0, SmallIcon( "appointment" ) );
00144     if ( incidence->doesRecur() ) {
00145       QDateTime nextStart = incidence->recurrence()->getNextDateTime( reminderAt );
00146       if ( nextStart.isValid() )
00147         item->setText( 1, KGlobal::locale()->formatDateTime( nextStart ) );
00148     }
00149     if ( item->text( 1 ).isEmpty() )
00150       item->setText( 1, incidence->dtStartStr() );
00151   } else if ( (todo = dynamic_cast<Todo*>( incidence )) ) {
00152     item->setPixmap( 0, SmallIcon( "todo" ) );
00153     item->setText( 1, todo->dtDueStr() );
00154   }
00155   if ( activeCount() == 1 ) {// previously empty
00156     mIncidenceListView->clearSelection();
00157     item->setSelected( true );
00158   }
00159   showDetails();
00160 }
00161 
00162 void AlarmDialog::slotOk()
00163 {
00164   ItemList selection = selectedItems();
00165   for ( ItemList::Iterator it = selection.begin(); it != selection.end(); ++it ) {
00166     if ( (*it)->itemBelow() )
00167       (*it)->itemBelow()->setSelected( true );
00168     else if ( (*it)->itemAbove() )
00169       (*it)->itemAbove()->setSelected( true );
00170     delete *it;
00171   }
00172   if ( activeCount() == 0 )
00173     accept();
00174   else {
00175     updateButtons();
00176     showDetails();
00177   }
00178   emit reminderCount( activeCount() );
00179 }
00180 
00181 void AlarmDialog::slotUser1()
00182 {
00183   dismissAll();
00184 }
00185 
00186 void AlarmDialog::suspend()
00187 {
00188   if ( !isVisible() )
00189     return;
00190 
00191   int unit=1;
00192   switch (mSuspendUnit->currentItem()) {
00193     case 3: // weeks
00194       unit *=  7;
00195     case 2: // days
00196       unit *= 24;
00197     case 1: // hours
00198       unit *= 60;
00199     case 0: // minutes
00200       unit *= 60;
00201     default:
00202       break;
00203   }
00204 
00205   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00206     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00207     if ( item->isSelected() && item->isVisible() ) {
00208       item->setVisible( false );
00209       item->setSelected( false );
00210       item->mRemindAt = QDateTime::currentDateTime().addSecs( unit * mSuspendSpin->value() );
00211       item->mNotified = false;
00212     }
00213   }
00214 
00215   setTimer();
00216   if ( activeCount() == 0 )
00217     accept();
00218   else {
00219     updateButtons();
00220     showDetails();
00221   }
00222   emit reminderCount( activeCount() );
00223 }
00224 
00225 void AlarmDialog::setTimer()
00226 {
00227   int nextReminderAt = -1;
00228   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00229     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00230     if ( item->mRemindAt > QDateTime::currentDateTime() ) {
00231       int secs = QDateTime::currentDateTime().secsTo( item->mRemindAt );
00232       nextReminderAt = nextReminderAt <= 0 ? secs : QMIN( nextReminderAt, secs );
00233     }
00234   }
00235 
00236   if ( nextReminderAt >= 0 ) {
00237     mSuspendTimer.stop();
00238     mSuspendTimer.start( 1000 * (nextReminderAt + 1), true );
00239   }
00240 }
00241 
00242 void AlarmDialog::slotUser2()
00243 {
00244   ItemList selection = selectedItems();
00245   if ( selection.count() != 1 )
00246     return;
00247   Incidence *incidence = selection.first()->mIncidence;
00248 
00249   if ( !kapp->dcopClient()->isApplicationRegistered( "korganizer" ) ) {
00250     if ( kapp->startServiceByDesktopName( "korganizer", QString::null ) )
00251       KMessageBox::error( 0, i18n("Could not start KOrganizer.") );
00252   }
00253 
00254   kapp->dcopClient()->send( "korganizer", "KOrganizerIface",
00255                             "editIncidence(QString)",
00256                              incidence->uid() );
00257 
00258   // get desktop # where korganizer (or kontact) runs
00259   QByteArray replyData;
00260   QCString object, replyType;
00261   object = kapp->dcopClient()->isApplicationRegistered( "kontact" ) ?
00262            "kontact-mainwindow#1" : "KOrganizer MainWindow";
00263   if (!kapp->dcopClient()->call( "korganizer", object,
00264                             "getWinID()", 0, replyType, replyData, true, -1 ) ) {
00265   }
00266 
00267   if ( replyType == "int" ) {
00268     int desktop, window;
00269     QDataStream ds( replyData, IO_ReadOnly );
00270     ds >> window;
00271     desktop = KWin::windowInfo( window ).desktop();
00272 
00273     if ( KWin::currentDesktop() == desktop ) {
00274       KWin::iconifyWindow( winId(), false );
00275     }
00276     else
00277       KWin::setCurrentDesktop( desktop );
00278 
00279     KWin::activateWindow( KWin::transientFor( window ) );
00280   }
00281 }
00282 
00283 void AlarmDialog::slotUser3()
00284 {
00285   suspend();
00286 }
00287 
00288 void AlarmDialog::dismissAll()
00289 {
00290   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ) {
00291     AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00292     if ( !item->isVisible() ) {
00293       ++it;
00294       continue;
00295     }
00296     delete item;
00297   }
00298   setTimer();
00299   accept();
00300   emit reminderCount( activeCount() );
00301 }
00302 
00303 void AlarmDialog::show()
00304 {
00305   mIncidenceListView->clearSelection();
00306   if ( mIncidenceListView->firstChild() )
00307     mIncidenceListView->firstChild()->setSelected( true );
00308   updateButtons();
00309   KDialogBase::show();
00310   KWin::setState( winId(), NET::KeepAbove );
00311   KWin::setOnAllDesktops( winId(), true );
00312   eventNotification();
00313 }
00314 
00315 void AlarmDialog::eventNotification()
00316 {
00317   bool beeped = false, found = false;
00318 
00319   QValueList<AlarmListItem*> list;
00320   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00321     AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00322     if ( !item->isVisible() || item->mNotified )
00323       continue;
00324     found = true;
00325     item->mNotified = true;
00326     Alarm::List alarms = item->mIncidence->alarms();
00327     Alarm::List::ConstIterator it;
00328     for ( it = alarms.begin(); it != alarms.end(); ++it ) {
00329       Alarm *alarm = *it;
00330       // FIXME: Check whether this should be done for all multiple alarms
00331       if (alarm->type() == Alarm::Procedure) {
00332         // FIXME: Add a message box asking whether the procedure should really be executed
00333         kdDebug(5890) << "Starting program: '" << alarm->programFile() << "'" << endl;
00334         KProcess proc;
00335         proc << QFile::encodeName(alarm->programFile());
00336         proc.start(KProcess::DontCare);
00337       }
00338       else if (alarm->type() == Alarm::Audio) {
00339         beeped = true;
00340         KAudioPlayer::play(QFile::encodeName(alarm->audioFile()));
00341       }
00342     }
00343   }
00344 
00345   if ( !beeped && found ) {
00346     KNotifyClient::beep();
00347   }
00348 }
00349 
00350 void AlarmDialog::wakeUp()
00351 {
00352   bool activeReminders = false;
00353   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00354     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00355     if ( item->mRemindAt <= QDateTime::currentDateTime() ) {
00356       if ( !item->isVisible() ) {
00357         item->setVisible( true );
00358         item->setSelected( false );
00359       }
00360       activeReminders = true;
00361     } else {
00362       item->setVisible( false );
00363     }
00364   }
00365 
00366   if ( activeReminders )
00367     show();
00368   setTimer();
00369   showDetails();
00370   emit reminderCount( activeCount() );
00371 }
00372 
00373 void AlarmDialog::slotSave()
00374 {
00375   KConfig *config = kapp->config();
00376   KLockFile::Ptr lock = config->lockFile();
00377   if ( lock.data()->lock() != KLockFile::LockOK )
00378     return;
00379 
00380   config->setGroup( "General" );
00381   int numReminders = config->readNumEntry("Reminders", 0);
00382 
00383   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00384     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00385     config->setGroup( QString("Incidence-%1").arg(numReminders + 1) );
00386     config->writeEntry( "UID", item->mIncidence->uid() );
00387     config->writeEntry( "RemindAt", item->mRemindAt );
00388     ++numReminders;
00389   }
00390 
00391   config->setGroup( "General" );
00392   config->writeEntry( "Reminders", numReminders );
00393   config->sync();
00394   lock.data()->unlock();
00395 }
00396 
00397 void AlarmDialog::updateButtons()
00398 {
00399   ItemList selection = selectedItems();
00400   enableButton( User2, selection.count() == 1 );
00401   enableButton( Ok, selection.count() > 0 );
00402   enableButton( User3, selection.count() > 0 );
00403 }
00404 
00405 QValueList< AlarmListItem * > AlarmDialog::selectedItems() const
00406 {
00407   QValueList<AlarmListItem*> list;
00408   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00409     if ( it.current()->isSelected() )
00410       list.append( static_cast<AlarmListItem*>( it.current() ) );
00411   }
00412   return list;
00413 }
00414 
00415 int AlarmDialog::activeCount()
00416 {
00417   int count = 0;
00418   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00419     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00420     if ( item->isVisible() )
00421       ++count;
00422   }
00423   return count;
00424 }
00425 
00426 void AlarmDialog::suspendAll()
00427 {
00428   mIncidenceListView->clearSelection();
00429   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00430     if ( it.current()->isVisible() )
00431       it.current()->setSelected( true );
00432   }
00433   suspend();
00434 }
00435 
00436 void AlarmDialog::showDetails()
00437 {
00438   mDetailView->clearEvents( true );
00439   mDetailView->clear();
00440   AlarmListItem *item = static_cast<AlarmListItem*>( mIncidenceListView->currentItem() );
00441   if ( !item || !item->isVisible() )
00442     return;
00443   mDetailView->appendIncidence( item->mIncidence );
00444 }