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