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   }
00227   Incidence *incidence = selection.first()->mIncidence;
00228   if ( incidence->isReadOnly() ) {
00229     KMessageBox::sorry(
00230       this,
00231       i18n( "\"%1\" is a read-only item so modifications are not possible." ).
00232       arg( incidence->summary() ) );
00233     return;
00234   }
00235 
00236   if ( !kapp->dcopClient()->isApplicationRegistered( "korganizer" ) ) {
00237     if ( kapp->startServiceByDesktopName( "korganizer", QString::null ) ) {
00238       KMessageBox::error(
00239         this,
00240         i18n( "Could not start KOrganizer so editing is not possible.") );
00241       return;
00242     }
00243   }
00244 
00245   kdDebug(5890) << "editing incidence " << incidence->summary() << endl;
00246   if ( !kapp->dcopClient()->send( "korganizer", "KOrganizerIface",
00247                                   "editIncidence(QString)",
00248                                   incidence->uid() ) ) {
00249     KMessageBox::error(
00250       this,
00251       i18n( "An internal KOrganizer error occurred attempting to modify \"%1\"" ).
00252       arg( incidence->summary() ) );
00253   }
00254 
00255   // get desktop # where korganizer (or kontact) runs
00256   QByteArray replyData;
00257   QCString object, replyType;
00258   object = kapp->dcopClient()->isApplicationRegistered( "kontact" ) ?
00259            "kontact-mainwindow#1" : "KOrganizer MainWindow";
00260   if (!kapp->dcopClient()->call( "korganizer", object,
00261                             "getWinID()", 0, replyType, replyData, true, -1 ) ) {
00262   }
00263 
00264   if ( replyType == "int" ) {
00265     int desktop, window;
00266     QDataStream ds( replyData, IO_ReadOnly );
00267     ds >> window;
00268     desktop = KWin::windowInfo( window ).desktop();
00269 
00270     if ( KWin::currentDesktop() == desktop ) {
00271       KWin::iconifyWindow( winId(), false );
00272     } else {
00273       KWin::setCurrentDesktop( desktop );
00274     }
00275     KWin::activateWindow( KWin::transientFor( window ) );
00276   }
00277 }
00278 
00279 void AlarmDialog::suspend()
00280 {
00281   if ( !isVisible() )
00282     return;
00283 
00284   int unit=1;
00285   switch (mSuspendUnit->currentItem()) {
00286     case 3: // weeks
00287       unit *=  7;
00288     case 2: // days
00289       unit *= 24;
00290     case 1: // hours
00291       unit *= 60;
00292     case 0: // minutes
00293       unit *= 60;
00294     default:
00295       break;
00296   }
00297 
00298   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00299     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00300     if ( item->isSelected() && item->isVisible() ) {
00301       item->setVisible( false );
00302       item->setSelected( false );
00303       item->mRemindAt = QDateTime::currentDateTime().addSecs( unit * mSuspendSpin->value() );
00304       item->mNotified = false;
00305     }
00306   }
00307 
00308   setTimer();
00309   if ( activeCount() == 0 )
00310     accept();
00311   else {
00312     updateButtons();
00313     showDetails();
00314   }
00315   emit reminderCount( activeCount() );
00316 }
00317 
00318 void AlarmDialog::setTimer()
00319 {
00320   int nextReminderAt = -1;
00321   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00322     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00323     if ( item->mRemindAt > QDateTime::currentDateTime() ) {
00324       int secs = QDateTime::currentDateTime().secsTo( item->mRemindAt );
00325       nextReminderAt = nextReminderAt <= 0 ? secs : QMIN( nextReminderAt, secs );
00326     }
00327   }
00328 
00329   if ( nextReminderAt >= 0 ) {
00330     mSuspendTimer.stop();
00331     mSuspendTimer.start( 1000 * (nextReminderAt + 1), true );
00332   }
00333 }
00334 
00335 void AlarmDialog::show()
00336 {
00337   mIncidenceListView->clearSelection();
00338   if ( mIncidenceListView->firstChild() )
00339     mIncidenceListView->firstChild()->setSelected( true );
00340   updateButtons();
00341   KDialogBase::show();
00342   KWin::setState( winId(), NET::KeepAbove );
00343   KWin::setOnAllDesktops( winId(), true );
00344   eventNotification();
00345 }
00346 
00347 void AlarmDialog::eventNotification()
00348 {
00349   bool beeped = false, found = false;
00350 
00351   QValueList<AlarmListItem*> list;
00352   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00353     AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00354     if ( !item->isVisible() || item->mNotified )
00355       continue;
00356     found = true;
00357     item->mNotified = true;
00358     Alarm::List alarms = item->mIncidence->alarms();
00359     Alarm::List::ConstIterator it;
00360     for ( it = alarms.begin(); it != alarms.end(); ++it ) {
00361       Alarm *alarm = *it;
00362       // FIXME: Check whether this should be done for all multiple alarms
00363       if (alarm->type() == Alarm::Procedure) {
00364         // FIXME: Add a message box asking whether the procedure should really be executed
00365         kdDebug(5890) << "Starting program: '" << alarm->programFile() << "'" << endl;
00366         KProcess proc;
00367         proc << QFile::encodeName(alarm->programFile());
00368         proc.start(KProcess::DontCare);
00369       }
00370       else if (alarm->type() == Alarm::Audio) {
00371         beeped = true;
00372         KAudioPlayer::play(QFile::encodeName(alarm->audioFile()));
00373       }
00374     }
00375   }
00376 
00377   if ( !beeped && found ) {
00378     KNotifyClient::beep();
00379   }
00380 }
00381 
00382 void AlarmDialog::wakeUp()
00383 {
00384   bool activeReminders = false;
00385   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00386     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00387     if ( item->mRemindAt <= QDateTime::currentDateTime() ) {
00388       if ( !item->isVisible() ) {
00389         item->setVisible( true );
00390         item->setSelected( false );
00391       }
00392       activeReminders = true;
00393     } else {
00394       item->setVisible( false );
00395     }
00396   }
00397 
00398   if ( activeReminders )
00399     show();
00400   setTimer();
00401   showDetails();
00402   emit reminderCount( activeCount() );
00403 }
00404 
00405 void AlarmDialog::slotSave()
00406 {
00407   KConfig *config = kapp->config();
00408   KLockFile::Ptr lock = config->lockFile();
00409   if ( lock.data()->lock() != KLockFile::LockOK )
00410     return;
00411 
00412   config->setGroup( "General" );
00413   int numReminders = config->readNumEntry("Reminders", 0);
00414 
00415   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00416     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00417     config->setGroup( QString("Incidence-%1").arg(numReminders + 1) );
00418     config->writeEntry( "UID", item->mIncidence->uid() );
00419     config->writeEntry( "RemindAt", item->mRemindAt );
00420     ++numReminders;
00421   }
00422 
00423   config->setGroup( "General" );
00424   config->writeEntry( "Reminders", numReminders );
00425   config->sync();
00426   lock.data()->unlock();
00427 }
00428 
00429 void AlarmDialog::updateButtons()
00430 {
00431   ItemList selection = selectedItems();
00432   enableButton( User1, selection.count() == 1 ); // can only edit 1 at a time
00433   enableButton( User3, selection.count() > 0 );  // dismiss 1 or more
00434   enableButton( Ok, selection.count() > 0 );     // suspend 1 or more
00435 }
00436 
00437 QValueList< AlarmListItem * > AlarmDialog::selectedItems() const
00438 {
00439   QValueList<AlarmListItem*> list;
00440   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00441     if ( it.current()->isSelected() )
00442       list.append( static_cast<AlarmListItem*>( it.current() ) );
00443   }
00444   return list;
00445 }
00446 
00447 int AlarmDialog::activeCount()
00448 {
00449   int count = 0;
00450   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00451     AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00452     if ( item->isVisible() )
00453       ++count;
00454   }
00455   return count;
00456 }
00457 
00458 void AlarmDialog::suspendAll()
00459 {
00460   mIncidenceListView->clearSelection();
00461   for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00462     if ( it.current()->isVisible() )
00463       it.current()->setSelected( true );
00464   }
00465   suspend();
00466 }
00467 
00468 void AlarmDialog::showDetails()
00469 {
00470   mDetailView->clearEvents( true );
00471   mDetailView->clear();
00472   AlarmListItem *item = static_cast<AlarmListItem*>( mIncidenceListView->currentItem() );
00473   if ( !item || !item->isVisible() )
00474     return;
00475   mDetailView->appendIncidence( item->mIncidence );
00476 }
KDE Home | KDE Accessibility Home | Description of Access Keys