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