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 class AlarmListItem : public KListViewItem
00062 {
00063 public:
00064 AlarmListItem( Incidence *incidence, QListView *parent ) :
00065 KListViewItem( parent ),
00066 mIncidence( incidence->clone() ),
00067 mNotified( false )
00068 {}
00069
00070 ~AlarmListItem()
00071 {
00072 delete mIncidence;
00073 }
00074
00075 int compare( KListViewItem *item, int iCol, bool bAscending ) const;
00076
00077 QString mDisplayText;
00078 Incidence *mIncidence;
00079 QDateTime mRemindAt;
00080 QDateTime mHappening;
00081 bool mNotified;
00082 };
00083
00084 int AlarmListItem::compare( KListViewItem *item, int iCol, bool bAscending ) const
00085 {
00086 if ( iCol == 1 ) {
00087 AlarmListItem *pItem = dynamic_cast<AlarmListItem *>( item );
00088 return mHappening < pItem->mHappening;
00089 } else {
00090 return KListViewItem::compare( item, iCol, bAscending );
00091 }
00092 }
00093
00094 typedef QValueList<AlarmListItem*> ItemList;
00095
00096 AlarmDialog::AlarmDialog( KCal::Calendar *calendar, QWidget *parent, const char *name )
00097 : KDialogBase( Plain, WType_TopLevel | WStyle_Customize | WStyle_StaysOnTop |
00098 WStyle_DialogBorder,
00099 parent, name, false, i18n("Reminder"),
00100 Ok | User1 | User2 | User3, NoDefault,
00101 false, i18n("Edit..."), i18n("Dismiss All"), i18n("Dismiss Reminder") ),
00102 mCalendar( calendar ), mSuspendTimer(this)
00103 {
00104
00105
00106
00107
00108
00109 KGlobal::iconLoader()->addAppDir( "kdepim" );
00110 setButtonOK( i18n( "Suspend" ) );
00111
00112 QWidget *topBox = plainPage();
00113 QBoxLayout *topLayout = new QVBoxLayout( topBox );
00114 topLayout->setSpacing( spacingHint() );
00115
00116 QLabel *label = new QLabel( i18n("The following items triggered reminders:"),
00117 topBox );
00118 topLayout->addWidget( label );
00119
00120 mIncidenceListView = new KListView( topBox );
00121 mIncidenceListView->addColumn( i18n( "Summary" ) );
00122 mIncidenceListView->addColumn( i18n( "Due" ) );
00123 mIncidenceListView->setSorting( 0, true );
00124 mIncidenceListView->setSorting( 1, true );
00125 mIncidenceListView->setSortColumn( 1 );
00126 mIncidenceListView->setShowSortIndicator( true );
00127 mIncidenceListView->setAllColumnsShowFocus( true );
00128 mIncidenceListView->setSelectionMode( QListView::Extended );
00129 topLayout->addWidget( mIncidenceListView );
00130 connect( mIncidenceListView, SIGNAL(selectionChanged()), SLOT(updateButtons()) );
00131 connect( mIncidenceListView, SIGNAL(doubleClicked(QListViewItem*)), SLOT(edit()) );
00132 connect( mIncidenceListView, SIGNAL(currentChanged(QListViewItem*)), SLOT(showDetails()) );
00133 connect( mIncidenceListView, SIGNAL(selectionChanged()), SLOT(showDetails()) );
00134
00135 mDetailView = new KOEventViewer( mCalendar, topBox );
00136 mDetailView->setFocus();
00137
00138 topLayout->addWidget( mDetailView );
00139
00140 QHBox *suspendBox = new QHBox( topBox );
00141 suspendBox->setSpacing( spacingHint() );
00142 topLayout->addWidget( suspendBox );
00143
00144 QLabel *l = new QLabel( i18n("Suspend &duration:"), suspendBox );
00145 mSuspendSpin = new QSpinBox( 1, 9999, 1, suspendBox );
00146 mSuspendSpin->setValue( 5 );
00147 l->setBuddy( mSuspendSpin );
00148
00149 mSuspendUnit = new KComboBox( suspendBox );
00150 mSuspendUnit->insertItem( i18n("minute(s)") );
00151 mSuspendUnit->insertItem( i18n("hour(s)") );
00152 mSuspendUnit->insertItem( i18n("day(s)") );
00153 mSuspendUnit->insertItem( i18n("week(s)") );
00154 connect( &mSuspendTimer, SIGNAL(timeout()), SLOT(wakeUp()) );
00155
00156 setMinimumSize( 300, 200 );
00157 }
00158
00159 AlarmDialog::~AlarmDialog()
00160 {
00161 mIncidenceListView->clear();
00162 }
00163
00164 void AlarmDialog::addIncidence( Incidence *incidence,
00165 const QDateTime &reminderAt,
00166 const QString &displayText )
00167 {
00168 AlarmListItem *item = new AlarmListItem( incidence, mIncidenceListView );
00169 item->setText( 0, incidence->summary() );
00170 item->mRemindAt = reminderAt;
00171 item->mDisplayText = displayText;
00172
00173
00174
00175 Event *event;
00176 Todo *todo;
00177 Alarm *alarm = incidence->alarms().first();
00178 if ( ( event = dynamic_cast<Event *>( incidence ) ) ) {
00179 item->setPixmap( 0, SmallIcon( "appointment" ) );
00180 if ( event->doesRecur() ) {
00181 QDateTime nextStart = event->recurrence()->getNextDateTime( reminderAt );
00182 if ( nextStart.isValid() ) {
00183 item->mHappening = nextStart;
00184 item->setText( 1, KGlobal::locale()->formatDateTime( nextStart ) );
00185 }
00186 }
00187 if ( item->text( 1 ).isEmpty() ) {
00188 QDateTime qdt;
00189 if ( alarm->hasStartOffset() ) {
00190 qdt = event->dtStart();
00191 } else {
00192 qdt = event->dtEnd();
00193 }
00194 item->mHappening = qdt;
00195 item->setText( 1, IncidenceFormatter::dateTimeToString( qdt, false, true ) );
00196 }
00197 } else if ( ( todo = dynamic_cast<Todo *>( incidence ) ) ) {
00198 item->setPixmap( 0, SmallIcon( "todo" ) );
00199 if ( todo->doesRecur() ) {
00200 QDateTime nextStart = todo->recurrence()->getNextDateTime( reminderAt );
00201 if ( nextStart.isValid() ) {
00202 item->mHappening = nextStart;
00203 item->setText( 1, KGlobal::locale()->formatDateTime( nextStart ) );
00204 }
00205 }
00206 if ( item->text( 1 ).isEmpty() ) {
00207 QDateTime qdt;
00208 if ( alarm->hasStartOffset() && todo->dtStart().isValid() ) {
00209 qdt = todo->dtStart();
00210 } else {
00211 qdt = todo->dtDue();
00212 }
00213 item->mHappening = qdt;
00214 item->setText( 1, IncidenceFormatter::dateTimeToString( qdt, false, true ) );
00215 }
00216 }
00217
00218 if ( activeCount() == 1 ) {
00219 mIncidenceListView->clearSelection();
00220 item->setSelected( true );
00221 }
00222 showDetails();
00223 }
00224
00225 void AlarmDialog::slotOk()
00226 {
00227 suspend();
00228 }
00229
00230 void AlarmDialog::slotUser1()
00231 {
00232 edit();
00233 }
00234
00235 void AlarmDialog::slotUser2()
00236 {
00237 dismissAll();
00238 }
00239
00240 void AlarmDialog::slotUser3()
00241 {
00242 dismissCurrent();
00243 }
00244
00245 void AlarmDialog::dismissCurrent()
00246 {
00247 ItemList selection = selectedItems();
00248 for ( ItemList::Iterator it = selection.begin(); it != selection.end(); ++it ) {
00249 if ( (*it)->itemBelow() )
00250 (*it)->itemBelow()->setSelected( true );
00251 else if ( (*it)->itemAbove() )
00252 (*it)->itemAbove()->setSelected( true );
00253 delete *it;
00254 }
00255 if ( activeCount() == 0 )
00256 accept();
00257 else {
00258 updateButtons();
00259 showDetails();
00260 }
00261 emit reminderCount( activeCount() );
00262 }
00263
00264 void AlarmDialog::dismissAll()
00265 {
00266 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ) {
00267 AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00268 if ( !item->isVisible() ) {
00269 ++it;
00270 continue;
00271 }
00272 delete item;
00273 }
00274 setTimer();
00275 accept();
00276 emit reminderCount( activeCount() );
00277 }
00278
00279 void AlarmDialog::edit()
00280 {
00281 ItemList selection = selectedItems();
00282 if ( selection.count() != 1 ) {
00283 return;
00284 }
00285 Incidence *incidence = selection.first()->mIncidence;
00286 if ( incidence->isReadOnly() ) {
00287 KMessageBox::sorry(
00288 this,
00289 i18n( "\"%1\" is a read-only item so modifications are not possible." ).
00290 arg( incidence->summary() ) );
00291 return;
00292 }
00293
00294 if ( !ensureKorganizerRunning() ) {
00295 KMessageBox::error(
00296 this,
00297 i18n( "Could not start KOrganizer so editing is not possible." ) );
00298 return;
00299 }
00300
00301
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 start the incidence editor" ) );
00308 return;
00309 }
00310
00311
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:
00343 unit *= 7;
00344 case 2:
00345 unit *= 24;
00346 case 1:
00347 unit *= 60;
00348 case 0:
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
00424 if (alarm->type() == Alarm::Procedure) {
00425
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 );
00494 enableButton( User3, selection.count() > 0 );
00495 enableButton( Ok, selection.count() > 0 );
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 }
00543
00544 bool AlarmDialog::ensureKorganizerRunning() const
00545 {
00546 QString error;
00547 QCString dcopService;
00548
00549 int result = KDCOPServiceStarter::self()->findServiceFor(
00550 "DCOP/Organizer", QString::null, QString::null, &error, &dcopService );
00551
00552 if ( result == 0 ) {
00553
00554
00555
00556 static const char* const dcopObjectId = "KOrganizerIface";
00557 QCString dummy;
00558 if ( !kapp->dcopClient()->findObject(
00559 dcopService, dcopObjectId, "", QByteArray(), dummy, dummy ) ) {
00560 DCOPRef ref( dcopService, dcopService );
00561 DCOPReply reply = ref.call( "load()" );
00562 if ( reply.isValid() && (bool)reply ) {
00563 Q_ASSERT( kapp->dcopClient()->findObject(
00564 dcopService, dcopObjectId, "", QByteArray(), dummy, dummy ) );
00565 } else {
00566 kdWarning() << "Error loading " << dcopService << endl;
00567 }
00568 }
00569
00570
00571 return true;
00572
00573 } else {
00574 kdWarning() << "Couldn't start DCOP/Organizer: " << dcopService
00575 << " " << error << endl;
00576 }
00577 return false;
00578 }