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 #include <libkcal/incidenceformatter.h>
00052 
00053 #include "koeventviewer.h"
00054 
00055 #include "alarmdialog.h"
00056 #include "alarmdialog.moc"
00057 
00058 class AlarmListItem : public KListViewItem
00059 {
00060   public:
00061     AlarmListItem( Incidence *incidence, QListView *parent ) :
00062       KListViewItem( parent ),
00063       mIncidence( incidence->clone() ),
00064       mNotified( false )
00065     {}
00066 
00067     ~AlarmListItem()
00068     {
00069       delete mIncidence;
00070     }
00071 
00072     Incidence *mIncidence;
00073     QDateTime mRemindAt;
00074     bool mNotified;
00075 };
00076 
00077 typedef QValueList<AlarmListItem*> ItemList;
00078 
00079 AlarmDialog::AlarmDialog( KCal::Calendar *calendar, QWidget *parent, const char *name )
00080   : KDialogBase( Plain, WType_TopLevel | WStyle_Customize | WStyle_StaysOnTop |
00081                  WStyle_DialogBorder,
00082                  parent, name, false, i18n("Reminder"),
00083                  Ok | User1 | User2 | User3, User3,
00084                  false, i18n("Edit..."), i18n("Dismiss All"), i18n("Dismiss Reminder") ),
00085                  mCalendar( calendar ), mSuspendTimer(this)
00086 {
00087   // User1 => Edit...
00088   // User2 => Dismiss All
00089   // User3 => Dismiss Selected
00090   //    Ok => Suspend
00091 
00092   KGlobal::iconLoader()->addAppDir( "kdepim" );
00093   setButtonOK( i18n( "Suspend" ) );
00094 
00095   QWidget *topBox = plainPage();
00096   QBoxLayout *topLayout = new QVBoxLayout( topBox );
00097   topLayout->setSpacing( spacingHint() );
00098 
00099   QLabel *label = new QLabel( i18n("The following events triggered reminders:"),
00100                               topBox );
00101   topLayout->addWidget( label );
00102 
00103   mIncidenceListView = new KListView( topBox );
00104   mIncidenceListView->addColumn( i18n( "Summary" ) );
00105   mIncidenceListView->addColumn( i18n( "Due" ) );
00106   mIncidenceListView->setAllColumnsShowFocus( true );
00107   mIncidenceListView->setSelectionMode( QListView::Extended );
00108   topLayout->addWidget( mIncidenceListView );
00109   connect( mIncidenceListView, SIGNAL(selectionChanged()), SLOT(updateButtons()) );
00110   connect( mIncidenceListView, SIGNAL(doubleClicked(QListViewItem*)), SLOT(edit()) );
00111   connect( mIncidenceListView, SIGNAL(currentChanged(QListViewItem*)), SLOT(showDetails()) );
00112   connect( mIncidenceListView, SIGNAL(selectionChanged()), SLOT(showDetails()) );
00113 
00114   mDetailView = new KOEventViewer( mCalendar, topBox );
00115   topLayout->addWidget( mDetailView );
00116 
00117   QHBox *suspendBox = new QHBox( topBox );
00118   suspendBox->setSpacing( spacingHint() );
00119   topLayout->addWidget( suspendBox );
00120 
00121   QLabel *l = new QLabel( i18n("Suspend &duration:"), suspendBox );
00122   mSuspendSpin = new QSpinBox( 1, 9999, 1, suspendBox );
00123   mSuspendSpin->setValue( 5 );  // default suspend duration
00124   l->setBuddy( mSuspendSpin );
00125 
00126   mSuspendUnit = new KComboBox( suspendBox );
00127   mSuspendUnit->insertItem( i18n("minute(s)") );
00128   mSuspendUnit->insertItem( i18n("hour(s)") );
00129   mSuspendUnit->insertItem( i18n("day(s)") );
00130   mSuspendUnit->insertItem( i18n("week(s)") );
00131   connect( &mSuspendTimer, SIGNAL(timeout()), SLOT(wakeUp()) );
00132 
00133   setMinimumSize( 300, 200 );
00134 }
00135 
00136 AlarmDialog::~AlarmDialog()
00137 {
00138   mIncidenceListView->clear();
00139 }
00140 
00141 void AlarmDialog::addIncidence( Incidence *incidence, const QDateTime &reminderAt )
00142 {
00143   AlarmListItem *item = new AlarmListItem( incidence, mIncidenceListView );
00144   item->setText( 0, incidence->summary() );
00145   item->mRemindAt = reminderAt;
00146   Todo *todo;
00147   if ( dynamic_cast<Event*>( incidence ) ) {
00148     item->setPixmap( 0, SmallIcon( "appointment" ) );
00149     if ( incidence->doesRecur() ) {
00150       QDateTime nextStart = incidence->recurrence()->getNextDateTime( reminderAt );
00151       if ( nextStart.isValid() )
00152         item->setText( 1, KGlobal::locale()->formatDateTime( nextStart ) );
00153     }
00154     if ( item->text( 1 ).isEmpty() )
00155       item->setText( 1, IncidenceFormatter::dateTimeToString( incidence->dtStart(), false, true ) );
00156   } else if ( (todo = dynamic_cast<Todo*>( incidence )) ) {
00157     item->setPixmap( 0, SmallIcon( "todo" ) );
00158     item->setText( 1, IncidenceFormatter::dateTimeToString( todo->dtDue(), false, true ) );
00159   }
00160   if ( activeCount() == 1 ) {// previously empty
00161     mIncidenceListView->clearSelection();
00162     item->setSelected( true );
00163   }
00164   showDetails();
00165 }
00166 
00167 void AlarmDialog::slotOk()
00168 {
00169   suspend();
00170 }
00171 
00172 void AlarmDialog::slotUser1()
00173 {
00174   edit();
00175 }
00176 
00177 void AlarmDialog::slotUser2()
00178 {
00179   dismissAll();
00180 }
00181 
00182 void AlarmDialog::slotUser3()
00183 {
00184   dismissCurrent();
00185 }
00186 
00187 void AlarmDialog::dismissCurrent()
00188 {
00189   ItemList selection = selectedItems();
00190   for ( ItemList::Iterator it = selection.begin(); it != selection.end(); ++it ) {
00191     if ( (*it)->itemBelow() )
00192       (*it)->itemBelow()->setSelected( true );
00193     else if ( (*it)->itemAbove() )
00194       (*it)->itemAbove()->setSelected( true );
00195     delete *it;
00196   }
00197   if ( activeCount() == 0 )
00198     accept();
00199   else {
00200     updateButtons();
00201     showDetails();
00202   }
00203   emit reminderCount( activeCount() );
00204 }
00205 
00206 void AlarmDialog::dismissAll()
00207 {
00208   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ) {
00209     AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00210     if ( !item->isVisible() ) {
00211       ++it;
00212       continue;
00213     }
00214     delete item;
00215   }
00216   setTimer();
00217   accept();
00218   emit reminderCount( activeCount() );
00219 }
00220 
00221 void AlarmDialog::edit()
00222 {
00223   ItemList selection = selectedItems();
00224   if ( selection.count() != 1 )
00225     return;
00226   Incidence *incidence = selection.first()->mIncidence;
00227 
00228   if ( !kapp->dcopClient()->isApplicationRegistered( "korganizer" ) ) {
00229     if ( kapp->startServiceByDesktopName( "korganizer", QString::null ) )
00230       KMessageBox::error( 0, i18n("Could not start KOrganizer.") );
00231   }
00232 
00233   kapp->dcopClient()->send( "korganizer", "KOrganizerIface",
00234                             "editIncidence(QString)",
00235                              incidence->uid() );
00236 
00237   // get desktop # where korganizer (or kontact) runs
00238   QByteArray replyData;
00239   QCString object, replyType;
00240   object = kapp->dcopClient()->isApplicationRegistered( "kontact" ) ?
00241            "kontact-mainwindow#1" : "KOrganizer MainWindow";
00242   if (!kapp->dcopClient()->call( "korganizer", object,
00243                             "getWinID()", 0, replyType, replyData, true, -1 ) ) {
00244   }
00245 
00246   if ( replyType == "int" ) {
00247     int desktop, window;
00248     QDataStream ds( replyData, IO_ReadOnly );
00249     ds >> window;
00250     desktop = KWin::windowInfo( window ).desktop();
00251 
00252     if ( KWin::currentDesktop() == desktop ) {
00253       KWin::iconifyWindow( winId(), false );
00254     } else {
00255       KWin::setCurrentDesktop( desktop );
00256     }
00257     KWin::activateWindow( KWin::transientFor( window ) );
00258   }
00259 }
00260 
00261 void AlarmDialog::suspend()
00262 {
00263   if ( !isVisible() )
00264     return;
00265 
00266   int unit=1;
00267   switch (mSuspendUnit->currentItem()) {
00268     case 3: // weeks
00269       unit *=  7;
00270     case 2: // days
00271       unit *= 24;
00272     case 1: // hours
00273       unit *= 60;
00274     case 0: // minutes
00275       unit *= 60;
00276     default:
00277       break;
00278   }
00279 
00280   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00281     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00282     if ( item->isSelected() && item->isVisible() ) {
00283       item->setVisible( false );
00284       item->setSelected( false );
00285       item->mRemindAt = QDateTime::currentDateTime().addSecs( unit * mSuspendSpin->value() );
00286       item->mNotified = false;
00287     }
00288   }
00289 
00290   setTimer();
00291   if ( activeCount() == 0 )
00292     accept();
00293   else {
00294     updateButtons();
00295     showDetails();
00296   }
00297   emit reminderCount( activeCount() );
00298 }
00299 
00300 void AlarmDialog::setTimer()
00301 {
00302   int nextReminderAt = -1;
00303   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00304     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00305     if ( item->mRemindAt > QDateTime::currentDateTime() ) {
00306       int secs = QDateTime::currentDateTime().secsTo( item->mRemindAt );
00307       nextReminderAt = nextReminderAt <= 0 ? secs : QMIN( nextReminderAt, secs );
00308     }
00309   }
00310 
00311   if ( nextReminderAt >= 0 ) {
00312     mSuspendTimer.stop();
00313     mSuspendTimer.start( 1000 * (nextReminderAt + 1), true );
00314   }
00315 }
00316 
00317 void AlarmDialog::show()
00318 {
00319   mIncidenceListView->clearSelection();
00320   if ( mIncidenceListView->firstChild() )
00321     mIncidenceListView->firstChild()->setSelected( true );
00322   updateButtons();
00323   KDialogBase::show();
00324   KWin::setState( winId(), NET::KeepAbove );
00325   KWin::setOnAllDesktops( winId(), true );
00326   eventNotification();
00327 }
00328 
00329 void AlarmDialog::eventNotification()
00330 {
00331   bool beeped = false, found = false;
00332 
00333   QValueList<AlarmListItem*> list;
00334   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00335     AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00336     if ( !item->isVisible() || item->mNotified )
00337       continue;
00338     found = true;
00339     item->mNotified = true;
00340     Alarm::List alarms = item->mIncidence->alarms();
00341     Alarm::List::ConstIterator it;
00342     for ( it = alarms.begin(); it != alarms.end(); ++it ) {
00343       Alarm *alarm = *it;
00344       // FIXME: Check whether this should be done for all multiple alarms
00345       if (alarm->type() == Alarm::Procedure) {
00346         // FIXME: Add a message box asking whether the procedure should really be executed
00347         kdDebug(5890) << "Starting program: '" << alarm->programFile() << "'" << endl;
00348         KProcess proc;
00349         proc << QFile::encodeName(alarm->programFile());
00350         proc.start(KProcess::DontCare);
00351       }
00352       else if (alarm->type() == Alarm::Audio) {
00353         beeped = true;
00354         KAudioPlayer::play(QFile::encodeName(alarm->audioFile()));
00355       }
00356     }
00357   }
00358 
00359   if ( !beeped && found ) {
00360     KNotifyClient::beep();
00361   }
00362 }
00363 
00364 void AlarmDialog::wakeUp()
00365 {
00366   bool activeReminders = false;
00367   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00368     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00369     if ( item->mRemindAt <= QDateTime::currentDateTime() ) {
00370       if ( !item->isVisible() ) {
00371         item->setVisible( true );
00372         item->setSelected( false );
00373       }
00374       activeReminders = true;
00375     } else {
00376       item->setVisible( false );
00377     }
00378   }
00379 
00380   if ( activeReminders )
00381     show();
00382   setTimer();
00383   showDetails();
00384   emit reminderCount( activeCount() );
00385 }
00386 
00387 void AlarmDialog::slotSave()
00388 {
00389   KConfig *config = kapp->config();
00390   KLockFile::Ptr lock = config->lockFile();
00391   if ( lock.data()->lock() != KLockFile::LockOK )
00392     return;
00393 
00394   config->setGroup( "General" );
00395   int numReminders = config->readNumEntry("Reminders", 0);
00396 
00397   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00398     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00399     config->setGroup( QString("Incidence-%1").arg(numReminders + 1) );
00400     config->writeEntry( "UID", item->mIncidence->uid() );
00401     config->writeEntry( "RemindAt", item->mRemindAt );
00402     ++numReminders;
00403   }
00404 
00405   config->setGroup( "General" );
00406   config->writeEntry( "Reminders", numReminders );
00407   config->sync();
00408   lock.data()->unlock();
00409 }
00410 
00411 void AlarmDialog::updateButtons()
00412 {
00413   ItemList selection = selectedItems();
00414   enableButton( User1, selection.count() == 1 ); // can only edit 1 at a time
00415   enableButton( User3, selection.count() > 0 );  // dismiss 1 or more
00416   enableButton( Ok, selection.count() > 0 );     // suspend 1 or more
00417 }
00418 
00419 QValueList< AlarmListItem * > AlarmDialog::selectedItems() const
00420 {
00421   QValueList<AlarmListItem*> list;
00422   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00423     if ( it.current()->isSelected() )
00424       list.append( static_cast<AlarmListItem*>( it.current() ) );
00425   }
00426   return list;
00427 }
00428 
00429 int AlarmDialog::activeCount()
00430 {
00431   int count = 0;
00432   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00433     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00434     if ( item->isVisible() )
00435       ++count;
00436   }
00437   return count;
00438 }
00439 
00440 void AlarmDialog::suspendAll()
00441 {
00442   mIncidenceListView->clearSelection();
00443   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00444     if ( it.current()->isVisible() )
00445       it.current()->setSelected( true );
00446   }
00447   suspend();
00448 }
00449 
00450 void AlarmDialog::showDetails()
00451 {
00452   mDetailView->clearEvents( true );
00453   mDetailView->clear();
00454   AlarmListItem *item = static_cast<AlarmListItem*>( mIncidenceListView->currentItem() );
00455   if ( !item || !item->isVisible() )
00456     return;
00457   mDetailView->appendIncidence( item->mIncidence );
00458 }
KDE Home | KDE Accessibility Home | Description of Access Keys