00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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 <dcopclient.h>
00037 #include <dcopref.h>
00038 #include <kapplication.h>
00039 #include <kconfig.h>
00040 #include <kdcopservicestarter.h>
00041 #include <kiconloader.h>
00042 #include <klocale.h>
00043 #include <kprocess.h>
00044 #include <kaudioplayer.h>
00045 #include <kdebug.h>
00046 #include <kmessagebox.h>
00047 #include <knotifyclient.h>
00048 #include <kcombobox.h>
00049 #include <klistview.h>
00050 #include <kwin.h>
00051 #include <klockfile.h>
00052
00053 #include <libkcal/event.h>
00054 #include <libkcal/incidenceformatter.h>
00055
00056 #include "koeventviewer.h"
00057
00058 #include "alarmdialog.h"
00059 #include "alarmdialog.moc"
00060
00061 static int defSuspendVal = 5;
00062 static int defSuspendUnit = 0;
00063
00064 class AlarmListItem : public KListViewItem
00065 {
00066 public:
00067 AlarmListItem( const QString &uid, QListView *parent ) :
00068 KListViewItem( parent ),
00069 mUid( uid ),
00070 mNotified( false )
00071 {}
00072
00073 ~AlarmListItem()
00074 {
00075 }
00076
00077 int compare( KListViewItem *item, int iCol, bool bAscending ) const;
00078
00079 QString mDisplayText;
00080
00081 QString mUid;
00082 QDateTime mRemindAt;
00083 QDateTime mHappening;
00084 bool mNotified;
00085 };
00086
00087 int AlarmListItem::compare( KListViewItem *item, int iCol, bool bAscending ) const
00088 {
00089 if ( iCol == 1 ) {
00090 AlarmListItem *pItem = dynamic_cast<AlarmListItem *>( item );
00091 return mHappening < pItem->mHappening;
00092 } else {
00093 return KListViewItem::compare( item, iCol, bAscending );
00094 }
00095 }
00096
00097 typedef QValueList<AlarmListItem*> ItemList;
00098
00099 AlarmDialog::AlarmDialog( KCal::CalendarResources *calendar, QWidget *parent, const char *name )
00100 : KDialogBase( Plain, WType_TopLevel | WStyle_Customize | WStyle_StaysOnTop |
00101 WStyle_DialogBorder,
00102 parent, name, false, i18n("Reminder"),
00103 Ok | User1 | User2 | User3, NoDefault,
00104 false, i18n("Edit..."), i18n("Dismiss All"), i18n("Dismiss Reminder") ),
00105 mCalendar( calendar ), mSuspendTimer(this)
00106 {
00107
00108
00109
00110
00111
00112 KGlobal::iconLoader()->addAppDir( "kdepim" );
00113 setButtonOK( i18n( "Suspend" ) );
00114
00115 QWidget *topBox = plainPage();
00116 QBoxLayout *topLayout = new QVBoxLayout( topBox );
00117 topLayout->setSpacing( spacingHint() );
00118
00119 QLabel *label = new QLabel( i18n("The following items triggered reminders:"),
00120 topBox );
00121 topLayout->addWidget( label );
00122
00123 mIncidenceListView = new KListView( topBox );
00124 mIncidenceListView->addColumn( i18n( "Summary" ) );
00125 mIncidenceListView->addColumn( i18n( "Due" ) );
00126 mIncidenceListView->setSorting( 0, true );
00127 mIncidenceListView->setSorting( 1, true );
00128 mIncidenceListView->setSortColumn( 1 );
00129 mIncidenceListView->setShowSortIndicator( true );
00130 mIncidenceListView->setAllColumnsShowFocus( true );
00131 mIncidenceListView->setSelectionMode( QListView::Extended );
00132 topLayout->addWidget( mIncidenceListView );
00133 connect( mIncidenceListView, SIGNAL(selectionChanged()), SLOT(updateButtons()) );
00134 connect( mIncidenceListView, SIGNAL(doubleClicked(QListViewItem*)), SLOT(edit()) );
00135 connect( mIncidenceListView, SIGNAL(currentChanged(QListViewItem*)), SLOT(showDetails()) );
00136 connect( mIncidenceListView, SIGNAL(selectionChanged()), SLOT(showDetails()) );
00137
00138 mDetailView = new KOEventViewer( mCalendar, topBox );
00139 mDetailView->setFocus();
00140
00141 topLayout->addWidget( mDetailView );
00142
00143 QHBox *suspendBox = new QHBox( topBox );
00144 suspendBox->setSpacing( spacingHint() );
00145 topLayout->addWidget( suspendBox );
00146
00147 QLabel *l = new QLabel( i18n("Suspend &duration:"), suspendBox );
00148 mSuspendSpin = new QSpinBox( 1, 9999, 1, suspendBox );
00149 mSuspendSpin->setValue( defSuspendVal );
00150 l->setBuddy( mSuspendSpin );
00151
00152 mSuspendUnit = new KComboBox( suspendBox );
00153 mSuspendUnit->insertItem( i18n("minute(s)") );
00154 mSuspendUnit->insertItem( i18n("hour(s)") );
00155 mSuspendUnit->insertItem( i18n("day(s)") );
00156 mSuspendUnit->insertItem( i18n("week(s)") );
00157 mSuspendUnit->setCurrentItem( defSuspendUnit );
00158
00159 connect( &mSuspendTimer, SIGNAL(timeout()), SLOT(wakeUp()) );
00160
00161 setMinimumSize( 300, 200 );
00162 }
00163
00164 AlarmDialog::~AlarmDialog()
00165 {
00166 mIncidenceListView->clear();
00167 }
00168
00169 void AlarmDialog::addIncidence( Incidence *incidence,
00170 const QDateTime &reminderAt,
00171 const QString &displayText )
00172 {
00173 AlarmListItem *item = new AlarmListItem( incidence->uid(), mIncidenceListView );
00174 item->setText( 0, incidence->summary() );
00175 item->mRemindAt = reminderAt;
00176 item->mDisplayText = displayText;
00177
00178
00179
00180 Event *event;
00181 Todo *todo;
00182 Alarm *alarm = incidence->alarms().first();
00183 if ( ( event = dynamic_cast<Event *>( incidence ) ) ) {
00184 item->setPixmap( 0, SmallIcon( "appointment" ) );
00185 if ( event->doesRecur() ) {
00186 QDateTime nextStart = event->recurrence()->getNextDateTime( reminderAt );
00187 if ( nextStart.isValid() ) {
00188 item->mHappening = nextStart;
00189 item->setText( 1, KGlobal::locale()->formatDateTime( nextStart ) );
00190 }
00191 }
00192 if ( item->text( 1 ).isEmpty() ) {
00193 QDateTime qdt;
00194 if ( alarm->hasStartOffset() ) {
00195 qdt = event->dtStart();
00196 } else {
00197 qdt = event->dtEnd();
00198 }
00199 item->mHappening = qdt;
00200 item->setText( 1, IncidenceFormatter::dateTimeToString( qdt, false, true ) );
00201 }
00202 } else if ( ( todo = dynamic_cast<Todo *>( incidence ) ) ) {
00203 item->setPixmap( 0, SmallIcon( "todo" ) );
00204 if ( todo->doesRecur() ) {
00205 QDateTime nextStart = todo->recurrence()->getNextDateTime( reminderAt );
00206 if ( nextStart.isValid() ) {
00207 item->mHappening = nextStart;
00208 item->setText( 1, KGlobal::locale()->formatDateTime( nextStart ) );
00209 }
00210 }
00211 if ( item->text( 1 ).isEmpty() ) {
00212 QDateTime qdt;
00213 if ( alarm->hasStartOffset() && todo->dtStart().isValid() ) {
00214 qdt = todo->dtStart();
00215 } else {
00216 qdt = todo->dtDue();
00217 }
00218 item->mHappening = qdt;
00219 item->setText( 1, IncidenceFormatter::dateTimeToString( qdt, false, true ) );
00220 }
00221 }
00222
00223 if ( activeCount() == 1 ) {
00224 mIncidenceListView->clearSelection();
00225 item->setSelected( true );
00226 }
00227 showDetails();
00228 }
00229
00230 void AlarmDialog::slotOk()
00231 {
00232 suspend();
00233 }
00234
00235 void AlarmDialog::slotUser1()
00236 {
00237 edit();
00238 }
00239
00240 void AlarmDialog::slotUser2()
00241 {
00242 dismissAll();
00243 }
00244
00245 void AlarmDialog::slotUser3()
00246 {
00247 dismissCurrent();
00248 }
00249
00250 void AlarmDialog::dismissCurrent()
00251 {
00252 ItemList selection = selectedItems();
00253 for ( ItemList::Iterator it = selection.begin(); it != selection.end(); ++it ) {
00254 if ( (*it)->itemBelow() )
00255 (*it)->itemBelow()->setSelected( true );
00256 else if ( (*it)->itemAbove() )
00257 (*it)->itemAbove()->setSelected( true );
00258 delete *it;
00259 }
00260 if ( activeCount() == 0 )
00261 accept();
00262 else {
00263 updateButtons();
00264 showDetails();
00265 }
00266 emit reminderCount( activeCount() );
00267 }
00268
00269 void AlarmDialog::dismissAll()
00270 {
00271 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ) {
00272 AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00273 if ( !item->isVisible() ) {
00274 ++it;
00275 continue;
00276 }
00277 delete item;
00278 }
00279 setTimer();
00280 accept();
00281 emit reminderCount( activeCount() );
00282 }
00283
00284 void AlarmDialog::edit()
00285 {
00286 ItemList selection = selectedItems();
00287 if ( selection.count() != 1 ) {
00288 return;
00289 }
00290 Incidence *incidence = mCalendar->incidence( selection.first()->mUid );
00291 QDate dt = selection.first()->mRemindAt.date();
00292
00293 if ( incidence->isReadOnly() ) {
00294 KMessageBox::sorry(
00295 this,
00296 i18n( "\"%1\" is a read-only item so modifications are not possible." ).
00297 arg( incidence->summary() ) );
00298 return;
00299 }
00300
00301 if ( !ensureKorganizerRunning() ) {
00302 KMessageBox::error(
00303 this,
00304 i18n( "Could not start KOrganizer so editing is not possible." ) );
00305 return;
00306 }
00307
00308 QByteArray data;
00309 QDataStream arg( data, IO_WriteOnly );
00310 arg << incidence->uid();
00311 arg << dt;
00312
00313 if ( !kapp->dcopClient()->send( "korganizer", "KOrganizerIface",
00314 "editIncidence(QString,QDate)",
00315 data ) ) {
00316 KMessageBox::error(
00317 this,
00318 i18n( "An internal KOrganizer error occurred attempting to start the incidence editor" ) );
00319 return;
00320 }
00321
00322
00323 QByteArray replyData;
00324 QCString object, replyType;
00325 object = kapp->dcopClient()->isApplicationRegistered( "kontact" ) ?
00326 "kontact-mainwindow#1" : "KOrganizer MainWindow";
00327 if (!kapp->dcopClient()->call( "korganizer", object,
00328 "getWinID()", 0, replyType, replyData, true, -1 ) ) {
00329 }
00330
00331 if ( replyType == "int" ) {
00332 int desktop, window;
00333 QDataStream ds( replyData, IO_ReadOnly );
00334 ds >> window;
00335 desktop = KWin::windowInfo( window ).desktop();
00336
00337 if ( KWin::currentDesktop() == desktop ) {
00338 KWin::iconifyWindow( winId(), false );
00339 } else {
00340 KWin::setCurrentDesktop( desktop );
00341 }
00342 KWin::activateWindow( KWin::transientFor( window ) );
00343 }
00344 }
00345
00346 void AlarmDialog::suspend()
00347 {
00348 if ( !isVisible() )
00349 return;
00350
00351 int unit=1;
00352 switch (mSuspendUnit->currentItem()) {
00353 case 3:
00354 unit *= 7;
00355 case 2:
00356 unit *= 24;
00357 case 1:
00358 unit *= 60;
00359 case 0:
00360 unit *= 60;
00361 default:
00362 break;
00363 }
00364
00365 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00366 AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00367 if ( item->isSelected() && item->isVisible() ) {
00368 item->setVisible( false );
00369 item->setSelected( false );
00370 item->mRemindAt = QDateTime::currentDateTime().addSecs( unit * mSuspendSpin->value() );
00371 item->mNotified = false;
00372 }
00373 }
00374
00375
00376
00377 slotSave();
00378
00379 setTimer();
00380 if ( activeCount() == 0 ) {
00381 accept();
00382 } else {
00383 updateButtons();
00384 showDetails();
00385 }
00386 emit reminderCount( activeCount() );
00387 }
00388
00389 void AlarmDialog::setTimer()
00390 {
00391 int nextReminderAt = -1;
00392 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00393 AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00394 if ( item->mRemindAt > QDateTime::currentDateTime() ) {
00395 int secs = QDateTime::currentDateTime().secsTo( item->mRemindAt );
00396 nextReminderAt = nextReminderAt <= 0 ? secs : QMIN( nextReminderAt, secs );
00397 }
00398 }
00399
00400 if ( nextReminderAt >= 0 ) {
00401 mSuspendTimer.stop();
00402 mSuspendTimer.start( 1000 * (nextReminderAt + 1), true );
00403 }
00404 }
00405
00406 void AlarmDialog::show()
00407 {
00408 mIncidenceListView->sort();
00409
00410 mIncidenceListView->clearSelection();
00411 if ( mIncidenceListView->firstChild() )
00412 mIncidenceListView->firstChild()->setSelected( true );
00413
00414 updateButtons();
00415 showDetails();
00416
00417
00418 mSuspendSpin->setValue( defSuspendVal );
00419 mSuspendUnit->setCurrentItem( defSuspendUnit );
00420
00421 KDialogBase::show();
00422 KWin::setState( winId(), NET::KeepAbove );
00423 KWin::setOnAllDesktops( winId(), true );
00424 eventNotification();
00425 }
00426
00427 void AlarmDialog::eventNotification()
00428 {
00429 bool beeped = false, found = false;
00430
00431 QValueList<AlarmListItem*> list;
00432 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00433 AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00434 if ( !item->isVisible() || item->mNotified ) {
00435 continue;
00436 }
00437 Incidence *incidence = mCalendar->incidence( item->mUid );
00438 if ( !incidence ) {
00439 continue;
00440 }
00441 found = true;
00442 item->mNotified = true;
00443 Alarm::List alarms = incidence->alarms();
00444 Alarm::List::ConstIterator it;
00445 for ( it = alarms.begin(); it != alarms.end(); ++it ) {
00446 Alarm *alarm = *it;
00447
00448 if (alarm->type() == Alarm::Procedure) {
00449
00450 kdDebug(5890) << "Starting program: '" << alarm->programFile() << "'" << endl;
00451 KProcess proc;
00452 proc << QFile::encodeName(alarm->programFile());
00453 proc.start(KProcess::DontCare);
00454 }
00455 else if (alarm->type() == Alarm::Audio) {
00456 beeped = true;
00457 KAudioPlayer::play(QFile::encodeName(alarm->audioFile()));
00458 }
00459 }
00460 }
00461
00462 if ( !beeped && found ) {
00463 KNotifyClient::beep();
00464 }
00465 }
00466
00467 void AlarmDialog::wakeUp()
00468 {
00469 bool activeReminders = false;
00470 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00471 AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00472 Incidence *incidence = mCalendar->incidence( item->mUid );
00473 if ( !incidence ) {
00474 delete item;
00475 continue;
00476 }
00477
00478 if ( item->mRemindAt <= QDateTime::currentDateTime() ) {
00479 if ( !item->isVisible() ) {
00480 item->setVisible( true );
00481 item->setSelected( false );
00482 }
00483 activeReminders = true;
00484 } else {
00485 item->setVisible( false );
00486 }
00487 }
00488
00489 if ( activeReminders )
00490 show();
00491 setTimer();
00492 showDetails();
00493 emit reminderCount( activeCount() );
00494 }
00495
00496 void AlarmDialog::slotSave()
00497 {
00498 KConfig *config = kapp->config();
00499 KLockFile::Ptr lock = config->lockFile();
00500 if ( lock.data()->lock() != KLockFile::LockOK )
00501 return;
00502
00503 config->setGroup( "General" );
00504 int numReminders = config->readNumEntry("Reminders", 0);
00505
00506 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00507 AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00508 Incidence *incidence = mCalendar->incidence( item->mUid );
00509 if ( !incidence ) {
00510 continue;
00511 }
00512 config->setGroup( QString("Incidence-%1").arg(numReminders + 1) );
00513 config->writeEntry( "UID", incidence->uid() );
00514 config->writeEntry( "RemindAt", item->mRemindAt );
00515 ++numReminders;
00516 }
00517
00518 config->setGroup( "General" );
00519 config->writeEntry( "Reminders", numReminders );
00520 config->sync();
00521 lock.data()->unlock();
00522 }
00523
00524 void AlarmDialog::updateButtons()
00525 {
00526 ItemList selection = selectedItems();
00527 enableButton( User1, selection.count() == 1 );
00528 enableButton( User3, selection.count() > 0 );
00529 enableButton( Ok, selection.count() > 0 );
00530 }
00531
00532 QValueList< AlarmListItem * > AlarmDialog::selectedItems() const
00533 {
00534 QValueList<AlarmListItem*> list;
00535 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00536 if ( it.current()->isSelected() )
00537 list.append( static_cast<AlarmListItem*>( it.current() ) );
00538 }
00539 return list;
00540 }
00541
00542 int AlarmDialog::activeCount()
00543 {
00544 int count = 0;
00545 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00546 AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00547 if ( item->isVisible() )
00548 ++count;
00549 }
00550 return count;
00551 }
00552
00553 void AlarmDialog::suspendAll()
00554 {
00555 mIncidenceListView->clearSelection();
00556 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00557 if ( it.current()->isVisible() )
00558 it.current()->setSelected( true );
00559 }
00560 suspend();
00561 }
00562
00563 void AlarmDialog::showDetails()
00564 {
00565 mDetailView->clearEvents( true );
00566 mDetailView->clear();
00567 AlarmListItem *item = static_cast<AlarmListItem*>( mIncidenceListView->currentItem() );
00568 if ( !item || !item->isVisible() )
00569 return;
00570
00571 Incidence *incidence = mCalendar->incidence( item->mUid );
00572 if ( !incidence ) {
00573 return;
00574 }
00575
00576 if ( !item->mDisplayText.isEmpty() ) {
00577 QString txt = "<qt><p><b>" + item->mDisplayText + "</b></p></qt>";
00578 mDetailView->addText( txt );
00579 }
00580 item->setText( 0, incidence->summary() );
00581 mDetailView->appendIncidence( incidence, item->mRemindAt.date() );
00582 }
00583
00584 bool AlarmDialog::ensureKorganizerRunning() const
00585 {
00586 QString error;
00587 QCString dcopService;
00588
00589 int result = KDCOPServiceStarter::self()->findServiceFor(
00590 "DCOP/Organizer", QString::null, QString::null, &error, &dcopService );
00591
00592 if ( result == 0 ) {
00593
00594
00595
00596 static const char* const dcopObjectId = "KOrganizerIface";
00597 QCString dummy;
00598 if ( !kapp->dcopClient()->findObject(
00599 dcopService, dcopObjectId, "", QByteArray(), dummy, dummy ) ) {
00600 DCOPRef ref( dcopService, dcopService );
00601 DCOPReply reply = ref.call( "load()" );
00602 if ( reply.isValid() && (bool)reply ) {
00603 Q_ASSERT( kapp->dcopClient()->findObject(
00604 dcopService, dcopObjectId, "", QByteArray(), dummy, dummy ) );
00605 } else {
00606 kdWarning() << "Error loading " << dcopService << endl;
00607 }
00608 }
00609
00610
00611 return true;
00612
00613 } else {
00614 kdWarning() << "Couldn't start DCOP/Organizer: " << dcopService
00615 << " " << error << endl;
00616 }
00617 return false;
00618 }