• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepim API Reference
  • KDE Home
  • Contact Us
 

kalarm

  • sources
  • kde-4.14
  • kdepim
  • kalarm
messagewin.cpp
Go to the documentation of this file.
1 /*
2  * messagewin.cpp - displays an alarm message
3  * Program: kalarm
4  * Copyright © 2001-2014 by David Jarvie <djarvie@kde.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include "kalarm.h"
22 #include "messagewin_p.h"
23 #include "messagewin.h"
24 
25 #include "alarmcalendar.h"
26 #include "autoqpointer.h"
27 #ifdef USE_AKONADI
28 #include "collectionmodel.h"
29 #endif
30 #include "deferdlg.h"
31 #include "desktop.h"
32 #include "editdlg.h"
33 #include "functions.h"
34 #include "kalarmapp.h"
35 #include "mainwindow.h"
36 #include "messagebox.h"
37 #include "preferences.h"
38 #include "pushbutton.h"
39 #include "shellprocess.h"
40 #include "synchtimer.h"
41 
42 #include "kspeechinterface.h"
43 
44 #include <kstandarddirs.h>
45 #include <kaction.h>
46 #include <kstandardguiitem.h>
47 #include <kaboutdata.h>
48 #include <klocale.h>
49 #include <kconfig.h>
50 #include <kiconloader.h>
51 #include <kdialog.h>
52 #include <ktextbrowser.h>
53 #include <ksystemtimezone.h>
54 #include <kglobalsettings.h>
55 #include <kmimetype.h>
56 #include <ktextedit.h>
57 #include <kwindowsystem.h>
58 #include <kio/netaccess.h>
59 #include <knotification.h>
60 #include <kpushbutton.h>
61 #include <ksqueezedtextlabel.h>
62 #include <phonon/mediaobject.h>
63 #include <phonon/audiooutput.h>
64 #include <phonon/volumefadereffect.h>
65 #include <kdebug.h>
66 #include <ktoolinvocation.h>
67 #ifdef Q_WS_X11
68 #include <netwm.h>
69 #include <QX11Info>
70 #endif
71 
72 #include <QScrollBar>
73 #include <QtDBus/QtDBus>
74 #include <QFile>
75 #include <QFileInfo>
76 #include <QCheckBox>
77 #include <QLabel>
78 #include <QPalette>
79 #include <QTimer>
80 #include <QPixmap>
81 #include <QByteArray>
82 #include <QFrame>
83 #include <QGridLayout>
84 #include <QVBoxLayout>
85 #include <QHBoxLayout>
86 #include <QResizeEvent>
87 #include <QCloseEvent>
88 #include <QDesktopWidget>
89 #include <QMutexLocker>
90 
91 #include <stdlib.h>
92 #include <string.h>
93 
94 #ifdef USE_AKONADI
95 using namespace KCalCore;
96 #else
97 using namespace KCal;
98 #endif
99 using namespace KAlarmCal;
100 
101 #ifdef Q_WS_X11
102 enum FullScreenType { NoFullScreen = 0, FullScreen = 1, FullScreenActive = 2 };
103 static FullScreenType haveFullScreenWindow(int screen);
104 static FullScreenType findFullScreenWindows(const QVector<QRect>& screenRects, QVector<FullScreenType>& screenTypes);
105 #endif
106 
107 #ifdef KMAIL_SUPPORTED
108 #include "kmailinterface.h"
109 static const QLatin1String KMAIL_DBUS_SERVICE("org.kde.kmail");
110 static const QLatin1String KMAIL_DBUS_PATH("/KMail");
111 #endif
112 
113 // The delay for enabling message window buttons if a zero delay is
114 // configured, i.e. the windows are placed far from the cursor.
115 static const int proximityButtonDelay = 1000; // (milliseconds)
116 static const int proximityMultiple = 10; // multiple of button height distance from cursor for proximity
117 
118 // A text label widget which can be scrolled and copied with the mouse
119 class MessageText : public KTextEdit
120 {
121  public:
122  MessageText(QWidget* parent = 0)
123  : KTextEdit(parent),
124  mNewLine(false)
125  {
126  setReadOnly(true);
127  setFrameStyle(NoFrame);
128  setLineWrapMode(NoWrap);
129  }
130  int scrollBarHeight() const { return horizontalScrollBar()->height(); }
131  int scrollBarWidth() const { return verticalScrollBar()->width(); }
132  void setBackgroundColour(const QColor& c)
133  {
134  QPalette pal = viewport()->palette();
135  pal.setColor(viewport()->backgroundRole(), c);
136  viewport()->setPalette(pal);
137  }
138  virtual QSize sizeHint() const
139  {
140  const QSizeF docsize = document()->size();
141  return QSize(static_cast<int>(docsize.width() + 0.99) + verticalScrollBar()->width(),
142  static_cast<int>(docsize.height() + 0.99) + horizontalScrollBar()->height());
143  }
144  bool newLine() const { return mNewLine; }
145  void setNewLine(bool nl) { mNewLine = nl; }
146  private:
147  bool mNewLine;
148 };
149 
150 
151 // Basic flags for the window
152 static const Qt::WindowFlags WFLAGS = Qt::WindowStaysOnTopHint;
153 static const Qt::WindowFlags WFLAGS2 = Qt::WindowContextHelpButtonHint;
154 static const Qt::WidgetAttribute WidgetFlags = Qt::WA_DeleteOnClose;
155 
156 // Error message bit masks
157 enum {
158  ErrMsg_Speak = 0x01,
159  ErrMsg_AudioFile = 0x02
160 };
161 
162 
163 QList<MessageWin*> MessageWin::mWindowList;
164 #ifdef USE_AKONADI
165 QMap<EventId, unsigned> MessageWin::mErrorMessages;
166 #else
167 QMap<QString, unsigned> MessageWin::mErrorMessages;
168 #endif
169 bool MessageWin::mRedisplayed = false;
170 // There can only be one audio thread at a time: trying to play multiple
171 // sound files simultaneously would result in a cacophony, and besides
172 // that, Phonon currently crashes...
173 QPointer<AudioThread> MessageWin::mAudioThread;
174 MessageWin* AudioThread::mAudioOwner = 0;
175 
176 /******************************************************************************
177 * Construct the message window for the specified alarm.
178 * Other alarms in the supplied event may have been updated by the caller, so
179 * the whole event needs to be stored for updating the calendar file when it is
180 * displayed.
181 */
182 MessageWin::MessageWin(const KAEvent* event, const KAAlarm& alarm, int flags)
183  : MainWindowBase(0, static_cast<Qt::WindowFlags>(WFLAGS | WFLAGS2 | ((flags & ALWAYS_HIDE) || getWorkAreaAndModal() ? Qt::WindowType(0) : Qt::X11BypassWindowManagerHint))),
184  mMessage(event->cleanText()),
185  mFont(event->font()),
186  mBgColour(event->bgColour()),
187  mFgColour(event->fgColour()),
188 #ifdef USE_AKONADI
189  mEventItemId(event->itemId()),
190  mEventId(*event),
191 #else
192  mEventId(event->id()),
193 #endif
194  mAudioFile(event->audioFile()),
195  mVolume(event->soundVolume()),
196  mFadeVolume(event->fadeVolume()),
197  mFadeSeconds(qMin(event->fadeSeconds(), 86400)),
198  mDefaultDeferMinutes(event->deferDefaultMinutes()),
199  mAlarmType(alarm.type()),
200  mAction(event->actionSubType()),
201 #ifdef KMAIL_SUPPORTED
202  mKMailSerialNumber(event->kmailSerialNumber()),
203 #else
204  mKMailSerialNumber(0),
205 #endif
206  mCommandError(event->commandError()),
207  mRestoreHeight(0),
208  mAudioRepeatPause(event->repeatSoundPause()),
209  mConfirmAck(event->confirmAck()),
210  mNoDefer(true),
211  mInvalid(false),
212  mEvent(*event),
213  mOriginalEvent(*event),
214 #ifdef USE_AKONADI
215  mCollection(AlarmCalendar::resources()->collectionForEvent(mEventItemId)),
216 #else
217  mResource(AlarmCalendar::resources()->resourceForEvent(mEventId)),
218 #endif
219  mTimeLabel(0),
220  mRemainingText(0),
221  mEditButton(0),
222  mDeferButton(0),
223  mSilenceButton(0),
224  mKMailButton(0),
225  mCommandText(0),
226  mDontShowAgainCheck(0),
227  mEditDlg(0),
228  mDeferDlg(0),
229  mAlwaysHide(flags & ALWAYS_HIDE),
230  mErrorWindow(false),
231  mInitialised(false),
232  mNoPostAction(alarm.type() & KAAlarm::REMINDER_ALARM),
233  mRecreating(false),
234  mBeep(event->beep()),
235  mSpeak(event->speak()),
236  mRescheduleEvent(!(flags & NO_RESCHEDULE)),
237  mShown(false),
238  mPositioning(false),
239  mNoCloseConfirm(false),
240  mDisableDeferral(false)
241 {
242  kDebug() << (void*)this << "event" << mEventId;
243  setAttribute(static_cast<Qt::WidgetAttribute>(WidgetFlags));
244  setWindowModality(Qt::WindowModal);
245  setObjectName(QLatin1String("MessageWin")); // used by LikeBack
246  if (alarm.type() & KAAlarm::REMINDER_ALARM)
247  {
248  if (event->reminderMinutes() < 0)
249  {
250  event->previousOccurrence(alarm.dateTime(false).effectiveKDateTime(), mDateTime, false);
251  if (!mDateTime.isValid() && event->repeatAtLogin())
252  mDateTime = alarm.dateTime().addSecs(event->reminderMinutes() * 60);
253  }
254  else
255  mDateTime = event->mainDateTime(true);
256  }
257  else
258  mDateTime = alarm.dateTime(true);
259  if (!(flags & (NO_INIT_VIEW | ALWAYS_HIDE)))
260  {
261 #ifdef USE_AKONADI
262  const bool readonly = AlarmCalendar::resources()->eventReadOnly(mEventItemId);
263 #else
264  const bool readonly = AlarmCalendar::resources()->eventReadOnly(mEventId);
265 #endif
266  mShowEdit = !mEventId.isEmpty() && !readonly;
267  mNoDefer = readonly || (flags & NO_DEFER) || alarm.repeatAtLogin();
268  initView();
269  }
270  // Set to save settings automatically, but don't save window size.
271  // File alarm window size is saved elsewhere.
272  setAutoSaveSettings(QLatin1String("MessageWin"), false);
273  mWindowList.append(this);
274  if (event->autoClose())
275  mCloseTime = alarm.dateTime().effectiveKDateTime().toUtc().dateTime().addSecs(event->lateCancel() * 60);
276  if (mAlwaysHide)
277  {
278  hide();
279  displayComplete(); // play audio, etc.
280  }
281 }
282 
283 /******************************************************************************
284 * Display an error message window.
285 * If 'dontShowAgain' is non-null, a "Don't show again" option is displayed. Note
286 * that the option is specific to 'event'.
287 */
288 void MessageWin::showError(const KAEvent& event, const DateTime& alarmDateTime,
289  const QStringList& errmsgs, const QString& dontShowAgain)
290 {
291 #ifdef USE_AKONADI
292  if (!dontShowAgain.isEmpty()
293  && KAlarm::dontShowErrors(EventId(event), dontShowAgain))
294 #else
295  if (!dontShowAgain.isEmpty()
296  && KAlarm::dontShowErrors(event.id(), dontShowAgain))
297 #endif
298  return;
299 
300  // Don't pile up duplicate error messages for the same alarm
301  for (int i = 0, end = mWindowList.count(); i < end; ++i)
302  {
303  const MessageWin* w = mWindowList[i];
304 #ifdef USE_AKONADI
305  if (w->mErrorWindow && w->mEventId == EventId(event)
306  && w->mErrorMsgs == errmsgs && w->mDontShowAgain == dontShowAgain)
307 #else
308  if (w->mErrorWindow && w->mEventId == event.id()
309  && w->mErrorMsgs == errmsgs && w->mDontShowAgain == dontShowAgain)
310 #endif
311  return;
312  }
313 
314  (new MessageWin(&event, alarmDateTime, errmsgs, dontShowAgain))->show();
315 }
316 
317 /******************************************************************************
318 * Construct the message window for a specified error message.
319 * If 'dontShowAgain' is non-null, a "Don't show again" option is displayed. Note
320 * that the option is specific to 'event'.
321 */
322 MessageWin::MessageWin(const KAEvent* event, const DateTime& alarmDateTime,
323  const QStringList& errmsgs, const QString& dontShowAgain)
324  : MainWindowBase(0, WFLAGS | WFLAGS2),
325  mMessage(event->cleanText()),
326  mDateTime(alarmDateTime),
327 #ifdef USE_AKONADI
328  mEventItemId(event->itemId()),
329  mEventId(*event),
330 #else
331  mEventId(event->id()),
332 #endif
333  mAlarmType(KAAlarm::MAIN_ALARM),
334  mAction(event->actionSubType()),
335  mKMailSerialNumber(0),
336  mCommandError(KAEvent::CMD_NO_ERROR),
337  mErrorMsgs(errmsgs),
338  mDontShowAgain(dontShowAgain),
339  mRestoreHeight(0),
340  mConfirmAck(false),
341  mShowEdit(false),
342  mNoDefer(true),
343  mInvalid(false),
344  mEvent(*event),
345  mOriginalEvent(*event),
346 #ifndef USE_AKONADI
347  mResource(0),
348 #endif
349  mTimeLabel(0),
350  mRemainingText(0),
351  mEditButton(0),
352  mDeferButton(0),
353  mSilenceButton(0),
354  mKMailButton(0),
355  mCommandText(0),
356  mDontShowAgainCheck(0),
357  mEditDlg(0),
358  mDeferDlg(0),
359  mAlwaysHide(false),
360  mErrorWindow(true),
361  mInitialised(false),
362  mNoPostAction(true),
363  mRecreating(false),
364  mRescheduleEvent(false),
365  mShown(false),
366  mPositioning(false),
367  mNoCloseConfirm(false),
368  mDisableDeferral(false)
369 {
370  kDebug() << "errmsg";
371  setAttribute(static_cast<Qt::WidgetAttribute>(WidgetFlags));
372  setWindowModality(Qt::WindowModal);
373  setObjectName(QLatin1String("ErrorWin")); // used by LikeBack
374  getWorkAreaAndModal();
375  initView();
376  mWindowList.append(this);
377 }
378 
379 /******************************************************************************
380 * Construct the message window for restoration by session management.
381 * The window is initialised by readProperties().
382 */
383 MessageWin::MessageWin()
384  : MainWindowBase(0, WFLAGS),
385  mTimeLabel(0),
386  mRemainingText(0),
387  mEditButton(0),
388  mDeferButton(0),
389  mSilenceButton(0),
390  mKMailButton(0),
391  mCommandText(0),
392  mDontShowAgainCheck(0),
393  mEditDlg(0),
394  mDeferDlg(0),
395  mAlwaysHide(false),
396  mErrorWindow(false),
397  mInitialised(false),
398  mRecreating(false),
399  mRescheduleEvent(false),
400  mShown(false),
401  mPositioning(false),
402  mNoCloseConfirm(false),
403  mDisableDeferral(false)
404 {
405  kDebug() << (void*)this << "restore";
406  setAttribute(WidgetFlags);
407  setWindowModality(Qt::WindowModal);
408  setObjectName(QLatin1String("RestoredMsgWin")); // used by LikeBack
409  getWorkAreaAndModal();
410  mWindowList.append(this);
411 }
412 
413 /******************************************************************************
414 * Destructor. Perform any post-alarm actions before tidying up.
415 */
416 MessageWin::~MessageWin()
417 {
418  kDebug() << (void*)this << mEventId;
419  if (AudioThread::mAudioOwner == this && !mAudioThread.isNull())
420  mAudioThread->quit();
421  mErrorMessages.remove(mEventId);
422  mWindowList.removeAll(this);
423  if (!mRecreating)
424  {
425  if (!mNoPostAction && !mEvent.postAction().isEmpty())
426  theApp()->alarmCompleted(mEvent);
427  if (!instanceCount(true))
428  theApp()->quitIf(); // no visible windows remain - check whether to quit
429  }
430 }
431 
432 /******************************************************************************
433 * Construct the message window.
434 */
435 void MessageWin::initView()
436 {
437  const bool reminder = (!mErrorWindow && (mAlarmType & KAAlarm::REMINDER_ALARM));
438  const int leading = fontMetrics().leading();
439  setCaption((mAlarmType & KAAlarm::REMINDER_ALARM) ? i18nc("@title:window", "Reminder") : i18nc("@title:window", "Message"));
440  QWidget* topWidget = new QWidget(this);
441  setCentralWidget(topWidget);
442  QVBoxLayout* topLayout = new QVBoxLayout(topWidget);
443  topLayout->setMargin(KDialog::marginHint());
444  topLayout->setSpacing(KDialog::spacingHint());
445 
446  QPalette labelPalette = palette();
447  labelPalette.setColor(backgroundRole(), labelPalette.color(QPalette::Window));
448 
449  // Show the alarm date/time, together with a reminder text where appropriate.
450  // Alarm date/time: display time zone if not local time zone.
451  mTimeLabel = new QLabel(topWidget);
452  mTimeLabel->setText(dateTimeToDisplay());
453  mTimeLabel->setFrameStyle(QFrame::StyledPanel);
454  mTimeLabel->setPalette(labelPalette);
455  mTimeLabel->setAutoFillBackground(true);
456  topLayout->addWidget(mTimeLabel, 0, Qt::AlignHCenter);
457  mTimeLabel->setWhatsThis(i18nc("@info:whatsthis", "The scheduled date/time for the message (as opposed to the actual time of display)."));
458 
459  if (mDateTime.isValid())
460  {
461  // Reminder
462  if (reminder)
463  {
464  // Create a label "time\nReminder" by inserting the time at the
465  // start of the translated string, allowing for possible HTML tags
466  // enclosing "Reminder".
467  QString s = i18nc("@info", "Reminder");
468  QRegExp re(QLatin1String("^(<[^>]+>)*"));
469  re.indexIn(s);
470  s.insert(re.matchedLength(), mTimeLabel->text() + QLatin1String("<br/>"));
471  mTimeLabel->setText(s);
472  mTimeLabel->setAlignment(Qt::AlignHCenter);
473  }
474  }
475  else
476  mTimeLabel->hide();
477 
478  if (!mErrorWindow)
479  {
480  // It's a normal alarm message window
481  switch (mAction)
482  {
483  case KAEvent::FILE:
484  {
485  // Display the file name
486  KSqueezedTextLabel* label = new KSqueezedTextLabel(mMessage, topWidget);
487  label->setFrameStyle(QFrame::StyledPanel);
488  label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
489  label->setPalette(labelPalette);
490  label->setAutoFillBackground(true);
491  label->setWhatsThis(i18nc("@info:whatsthis", "The file whose contents are displayed below"));
492  topLayout->addWidget(label, 0, Qt::AlignHCenter);
493 
494  // Display contents of file
495  bool opened = false;
496  bool dir = false;
497  QString tmpFile;
498  const KUrl url(mMessage);
499  if (KIO::NetAccess::download(url, tmpFile, MainWindow::mainMainWindow()))
500  {
501  QFile qfile(tmpFile);
502  const QFileInfo info(qfile);
503  if (!(dir = info.isDir()))
504  {
505  opened = true;
506  KTextBrowser* view = new KTextBrowser(topWidget);
507  view->setFrameStyle(QFrame::NoFrame);
508  view->setWordWrapMode(QTextOption::NoWrap);
509  QPalette pal = view->viewport()->palette();
510  pal.setColor(view->viewport()->backgroundRole(), mBgColour);
511  view->viewport()->setPalette(pal);
512  view->setTextColor(mFgColour);
513  view->setCurrentFont(mFont);
514  KMimeType::Ptr mime = KMimeType::findByUrl(url);
515  if (mime->is(QLatin1String("application/octet-stream")))
516  mime = KMimeType::findByFileContent(tmpFile);
517  switch (KAlarm::fileType(mime))
518  {
519  case KAlarm::Image:
520  view->setHtml(QLatin1String("<img source=\"") + tmpFile + QLatin1String("\">"));
521  break;
522  case KAlarm::TextFormatted:
523  view->QTextBrowser::setSource(tmpFile); //krazy:exclude=qclasses
524  break;
525  default:
526  {
527  // Assume a plain text file
528  if (qfile.open(QIODevice::ReadOnly))
529  {
530  QTextStream str(&qfile);
531 
532  view->setPlainText(str.readAll());
533  qfile.close();
534  }
535  break;
536  }
537  }
538  view->setMinimumSize(view->sizeHint());
539  topLayout->addWidget(view);
540 
541  // Set the default size to 20 lines square.
542  // Note that after the first file has been displayed, this size
543  // is overridden by the user-set default stored in the config file.
544  // So there is no need to calculate an accurate size.
545  int h = 20*view->fontMetrics().lineSpacing() + 2*view->frameWidth();
546  view->resize(QSize(h, h).expandedTo(view->sizeHint()));
547  view->setWhatsThis(i18nc("@info:whatsthis", "The contents of the file to be displayed"));
548  }
549  KIO::NetAccess::removeTempFile(tmpFile);
550  }
551  if (!opened)
552  {
553  // File couldn't be opened
554  const bool exists = KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, MainWindow::mainMainWindow());
555  mErrorMsgs += dir ? i18nc("@info", "File is a folder") : exists ? i18nc("@info", "Failed to open file") : i18nc("@info", "File not found");
556  }
557  break;
558  }
559  case KAEvent::MESSAGE:
560  {
561  // Message label
562  // Using MessageText instead of QLabel allows scrolling and mouse copying
563  MessageText* text = new MessageText(topWidget);
564  text->setAutoFillBackground(true);
565  text->setBackgroundColour(mBgColour);
566  text->setTextColor(mFgColour);
567  text->setCurrentFont(mFont);
568  text->insertPlainText(mMessage);
569  const int lineSpacing = text->fontMetrics().lineSpacing();
570  const QSize s = text->sizeHint();
571  const int h = s.height();
572  text->setMaximumHeight(h + text->scrollBarHeight());
573  text->setMinimumHeight(qMin(h, lineSpacing*4));
574  text->setMaximumWidth(s.width() + text->scrollBarWidth());
575  text->setWhatsThis(i18nc("@info:whatsthis", "The alarm message"));
576  const int vspace = lineSpacing/2;
577  const int hspace = lineSpacing - KDialog::marginHint();
578  topLayout->addSpacing(vspace);
579  topLayout->addStretch();
580  // Don't include any horizontal margins if message is 2/3 screen width
581  if (text->sizeHint().width() >= KAlarm::desktopWorkArea(mScreenNumber).width()*2/3)
582  topLayout->addWidget(text, 1, Qt::AlignHCenter);
583  else
584  {
585  QHBoxLayout* layout = new QHBoxLayout();
586  layout->addSpacing(hspace);
587  layout->addWidget(text, 1, Qt::AlignHCenter);
588  layout->addSpacing(hspace);
589  topLayout->addLayout(layout);
590  }
591  if (!reminder)
592  topLayout->addStretch();
593  break;
594  }
595  case KAEvent::COMMAND:
596  {
597  mCommandText = new MessageText(topWidget);
598  mCommandText->setBackgroundColour(mBgColour);
599  mCommandText->setTextColor(mFgColour);
600  mCommandText->setCurrentFont(mFont);
601  topLayout->addWidget(mCommandText);
602  mCommandText->setWhatsThis(i18nc("@info:whatsthis", "The output of the alarm's command"));
603  theApp()->execCommandAlarm(mEvent, mEvent.alarm(mAlarmType), this, SLOT(readProcessOutput(ShellProcess*)));
604  break;
605  }
606  case KAEvent::EMAIL:
607  default:
608  break;
609  }
610 
611  if (reminder && mEvent.reminderMinutes() > 0)
612  {
613  // Advance reminder: show remaining time until the actual alarm
614  mRemainingText = new QLabel(topWidget);
615  mRemainingText->setFrameStyle(QFrame::Box | QFrame::Raised);
616  mRemainingText->setMargin(leading);
617  mRemainingText->setPalette(labelPalette);
618  mRemainingText->setAutoFillBackground(true);
619  if (mDateTime.isDateOnly() || KDateTime::currentLocalDate().daysTo(mDateTime.date()) > 0)
620  {
621  setRemainingTextDay();
622  MidnightTimer::connect(this, SLOT(setRemainingTextDay())); // update every day
623  }
624  else
625  {
626  setRemainingTextMinute();
627  MinuteTimer::connect(this, SLOT(setRemainingTextMinute())); // update every minute
628  }
629  topLayout->addWidget(mRemainingText, 0, Qt::AlignHCenter);
630  topLayout->addSpacing(KDialog::spacingHint());
631  topLayout->addStretch();
632  }
633  }
634  else
635  {
636  // It's an error message
637  switch (mAction)
638  {
639  case KAEvent::EMAIL:
640  {
641  // Display the email addresses and subject.
642  QFrame* frame = new QFrame(topWidget);
643  frame->setFrameStyle(QFrame::Box | QFrame::Raised);
644  frame->setWhatsThis(i18nc("@info:whatsthis", "The email to send"));
645  topLayout->addWidget(frame, 0, Qt::AlignHCenter);
646  QGridLayout* grid = new QGridLayout(frame);
647  grid->setMargin(KDialog::marginHint());
648  grid->setSpacing(KDialog::spacingHint());
649 
650  QLabel* label = new QLabel(i18nc("@info Email addressee", "To:"), frame);
651  label->setFixedSize(label->sizeHint());
652  grid->addWidget(label, 0, 0, Qt::AlignLeft);
653  label = new QLabel(mEvent.emailAddresses(QLatin1String("\n")), frame);
654  label->setFixedSize(label->sizeHint());
655  grid->addWidget(label, 0, 1, Qt::AlignLeft);
656 
657  label = new QLabel(i18nc("@info Email subject", "Subject:"), frame);
658  label->setFixedSize(label->sizeHint());
659  grid->addWidget(label, 1, 0, Qt::AlignLeft);
660  label = new QLabel(mEvent.emailSubject(), frame);
661  label->setFixedSize(label->sizeHint());
662  grid->addWidget(label, 1, 1, Qt::AlignLeft);
663  break;
664  }
665  case KAEvent::COMMAND:
666  case KAEvent::FILE:
667  case KAEvent::MESSAGE:
668  default:
669  // Just display the error message strings
670  break;
671  }
672  }
673 
674  if (!mErrorMsgs.count())
675  {
676  topWidget->setAutoFillBackground(true);
677  QPalette palette = topWidget->palette();
678  palette.setColor(topWidget->backgroundRole(), mBgColour);
679  topWidget->setPalette(palette);
680  }
681  else
682  {
683  setCaption(i18nc("@title:window", "Error"));
684  QHBoxLayout* layout = new QHBoxLayout();
685  layout->setMargin(2*KDialog::marginHint());
686  layout->addStretch();
687  topLayout->addLayout(layout);
688  QLabel* label = new QLabel(topWidget);
689  label->setPixmap(DesktopIcon(QLatin1String("dialog-error")));
690  label->setFixedSize(label->sizeHint());
691  layout->addWidget(label, 0, Qt::AlignRight);
692  QVBoxLayout* vlayout = new QVBoxLayout();
693  layout->addLayout(vlayout);
694  for (QStringList::ConstIterator it = mErrorMsgs.constBegin(); it != mErrorMsgs.constEnd(); ++it)
695  {
696  label = new QLabel(*it, topWidget);
697  label->setFixedSize(label->sizeHint());
698  vlayout->addWidget(label, 0, Qt::AlignLeft);
699  }
700  layout->addStretch();
701  if (!mDontShowAgain.isEmpty())
702  {
703  mDontShowAgainCheck = new QCheckBox(i18nc("@option:check", "Do not display this error message again for this alarm"), topWidget);
704  mDontShowAgainCheck->setFixedSize(mDontShowAgainCheck->sizeHint());
705  topLayout->addWidget(mDontShowAgainCheck, 0, Qt::AlignLeft);
706  }
707  }
708 
709  QGridLayout* grid = new QGridLayout();
710  grid->setColumnStretch(0, 1); // keep the buttons right-adjusted in the window
711  topLayout->addLayout(grid);
712  int gridIndex = 1;
713 
714  // Close button
715  mOkButton = new PushButton(KStandardGuiItem::close(), topWidget);
716  // Prevent accidental acknowledgement of the message if the user is typing when the window appears
717  mOkButton->clearFocus();
718  mOkButton->setFocusPolicy(Qt::ClickFocus); // don't allow keyboard selection
719  mOkButton->setFixedSize(mOkButton->sizeHint());
720  connect(mOkButton, SIGNAL(clicked()), SLOT(slotOk()));
721  grid->addWidget(mOkButton, 0, gridIndex++, Qt::AlignHCenter);
722  mOkButton->setWhatsThis(i18nc("@info:whatsthis", "Acknowledge the alarm"));
723 
724  if (mShowEdit)
725  {
726  // Edit button
727  mEditButton = new PushButton(i18nc("@action:button", "&Edit..."), topWidget);
728  mEditButton->setFocusPolicy(Qt::ClickFocus); // don't allow keyboard selection
729  mEditButton->setFixedSize(mEditButton->sizeHint());
730  connect(mEditButton, SIGNAL(clicked()), SLOT(slotEdit()));
731  grid->addWidget(mEditButton, 0, gridIndex++, Qt::AlignHCenter);
732  mEditButton->setWhatsThis(i18nc("@info:whatsthis", "Edit the alarm."));
733  }
734 
735  // Defer button
736  mDeferButton = new PushButton(i18nc("@action:button", "&Defer..."), topWidget);
737  mDeferButton->setFocusPolicy(Qt::ClickFocus); // don't allow keyboard selection
738  mDeferButton->setFixedSize(mDeferButton->sizeHint());
739  connect(mDeferButton, SIGNAL(clicked()), SLOT(slotDefer()));
740  grid->addWidget(mDeferButton, 0, gridIndex++, Qt::AlignHCenter);
741  mDeferButton->setWhatsThis(i18nc("@info:whatsthis", "<para>Defer the alarm until later.</para>"
742  "<para>You will be prompted to specify when the alarm should be redisplayed.</para>"));
743 
744  if (mNoDefer)
745  mDeferButton->hide();
746  else
747  setDeferralLimit(mEvent); // ensure that button is disabled when alarm can't be deferred any more
748 
749  if (!mAudioFile.isEmpty() && (mVolume || mFadeVolume > 0))
750  {
751  // Silence button to stop sound repetition
752  const QPixmap pixmap = MainBarIcon(QLatin1String("media-playback-stop"));
753  mSilenceButton = new PushButton(topWidget);
754  mSilenceButton->setIcon(KIcon(pixmap));
755  grid->addWidget(mSilenceButton, 0, gridIndex++, Qt::AlignHCenter);
756  mSilenceButton->setToolTip(i18nc("@info:tooltip", "Stop sound"));
757  mSilenceButton->setWhatsThis(i18nc("@info:whatsthis", "Stop playing the sound"));
758  // To avoid getting in a mess, disable the button until sound playing has been set up
759  mSilenceButton->setEnabled(false);
760  }
761 
762  KIconLoader iconLoader;
763  if (mKMailSerialNumber)
764  {
765  // KMail button
766  const QPixmap pixmap = iconLoader.loadIcon(QLatin1String("internet-mail"), KIconLoader::MainToolbar);
767  mKMailButton = new PushButton(topWidget);
768  mKMailButton->setIcon(KIcon(pixmap));
769  connect(mKMailButton, SIGNAL(clicked()), SLOT(slotShowKMailMessage()));
770  grid->addWidget(mKMailButton, 0, gridIndex++, Qt::AlignHCenter);
771  mKMailButton->setToolTip(i18nc("@info:tooltip Locate this email in KMail", "Locate in <application>KMail</application>"));
772  mKMailButton->setWhatsThis(i18nc("@info:whatsthis", "Locate and highlight this email in <application>KMail</application>"));
773  }
774 
775  // KAlarm button
776  const QPixmap pixmap = iconLoader.loadIcon(KGlobal::mainComponent().aboutData()->appName(), KIconLoader::MainToolbar);
777  mKAlarmButton = new PushButton(topWidget);
778  mKAlarmButton->setIcon(KIcon(pixmap));
779  connect(mKAlarmButton, SIGNAL(clicked()), SLOT(displayMainWindow()));
780  grid->addWidget(mKAlarmButton, 0, gridIndex++, Qt::AlignHCenter);
781  mKAlarmButton->setToolTip(i18nc("@info:tooltip", "Activate <application>KAlarm</application>"));
782  mKAlarmButton->setWhatsThis(i18nc("@info:whatsthis", "Activate <application>KAlarm</application>"));
783 
784  int butsize = mKAlarmButton->sizeHint().height();
785  if (mSilenceButton)
786  butsize = qMax(butsize, mSilenceButton->sizeHint().height());
787  if (mKMailButton)
788  butsize = qMax(butsize, mKMailButton->sizeHint().height());
789  mKAlarmButton->setFixedSize(butsize, butsize);
790  if (mSilenceButton)
791  mSilenceButton->setFixedSize(butsize, butsize);
792  if (mKMailButton)
793  mKMailButton->setFixedSize(butsize, butsize);
794 
795  // Disable all buttons initially, to prevent accidental clicking on if they happen to be
796  // under the mouse just as the window appears.
797  mOkButton->setEnabled(false);
798  if (mDeferButton->isVisible())
799  mDeferButton->setEnabled(false);
800  if (mEditButton)
801  mEditButton->setEnabled(false);
802  if (mKMailButton)
803  mKMailButton->setEnabled(false);
804  mKAlarmButton->setEnabled(false);
805 
806  topLayout->activate();
807  setMinimumSize(QSize(grid->sizeHint().width() + 2*KDialog::marginHint(), sizeHint().height()));
808  const bool modal = !(windowFlags() & Qt::X11BypassWindowManagerHint);
809  const unsigned long wstate = (modal ? NET::Modal : 0) | NET::Sticky | NET::StaysOnTop;
810  WId winid = winId();
811  KWindowSystem::setState(winid, wstate);
812  KWindowSystem::setOnAllDesktops(winid, true);
813 
814  mInitialised = true; // the window's widgets have been created
815 }
816 
817 /******************************************************************************
818 * Return the number of message windows, optionally excluding always-hidden ones.
819 */
820 int MessageWin::instanceCount(bool excludeAlwaysHidden)
821 {
822  int count = mWindowList.count();
823  if (excludeAlwaysHidden)
824  {
825  foreach (MessageWin* win, mWindowList)
826  {
827  if (win->mAlwaysHide)
828  --count;
829  }
830  }
831  return count;
832 }
833 
834 bool MessageWin::hasDefer() const
835 {
836  return mDeferButton && mDeferButton->isVisible();
837 }
838 
839 /******************************************************************************
840 * Show the Defer button when it was previously hidden.
841 */
842 void MessageWin::showDefer()
843 {
844  if (mDeferButton)
845  {
846  mNoDefer = false;
847  mDeferButton->show();
848  setDeferralLimit(mEvent); // ensure that button is disabled when alarm can't be deferred any more
849  resize(sizeHint());
850  }
851 }
852 
853 /******************************************************************************
854 * Convert a reminder window into a normal alarm window.
855 */
856 void MessageWin::cancelReminder(const KAEvent& event, const KAAlarm& alarm)
857 {
858  if (!mInitialised)
859  return;
860  mDateTime = alarm.dateTime(true);
861  mNoPostAction = false;
862  mAlarmType = alarm.type();
863  if (event.autoClose())
864  mCloseTime = alarm.dateTime().effectiveKDateTime().toUtc().dateTime().addSecs(event.lateCancel() * 60);
865  setCaption(i18nc("@title:window", "Message"));
866  mTimeLabel->setText(dateTimeToDisplay());
867  if (mRemainingText)
868  mRemainingText->hide();
869  MidnightTimer::disconnect(this, SLOT(setRemainingTextDay()));
870  MinuteTimer::disconnect(this, SLOT(setRemainingTextMinute()));
871  setMinimumHeight(0);
872  centralWidget()->layout()->activate();
873  setMinimumHeight(sizeHint().height());
874  resize(sizeHint());
875 }
876 
877 /******************************************************************************
878 * Show the alarm's trigger time.
879 * This is assumed to have previously been hidden.
880 */
881 void MessageWin::showDateTime(const KAEvent& event, const KAAlarm& alarm)
882 {
883  if (!mTimeLabel)
884  return;
885  mDateTime = (alarm.type() & KAAlarm::REMINDER_ALARM) ? event.mainDateTime(true) : alarm.dateTime(true);
886  if (mDateTime.isValid())
887  {
888  mTimeLabel->setText(dateTimeToDisplay());
889  mTimeLabel->show();
890  }
891 }
892 
893 /******************************************************************************
894 * Get the trigger time to display.
895 */
896 QString MessageWin::dateTimeToDisplay()
897 {
898  QString tm;
899  if (mDateTime.isValid())
900  {
901  if (mDateTime.isDateOnly())
902  tm = KGlobal::locale()->formatDate(mDateTime.date(), KLocale::ShortDate);
903  else
904  {
905  bool showZone = false;
906  if (mDateTime.timeType() == KDateTime::UTC
907  || (mDateTime.timeType() == KDateTime::TimeZone && !mDateTime.isLocalZone()))
908  {
909  // Display time zone abbreviation if it's different from the local
910  // zone. Note that the iCalendar time zone might represent the local
911  // time zone in a slightly different way from the system time zone,
912  // so the zone comparison above might not produce the desired result.
913  const QString tz = mDateTime.kDateTime().toString(QString::fromLatin1("%Z"));
914  KDateTime local = mDateTime.kDateTime();
915  local.setTimeSpec(KDateTime::Spec::LocalZone());
916  showZone = (local.toString(QString::fromLatin1("%Z")) != tz);
917  }
918  tm = KGlobal::locale()->formatDateTime(mDateTime.kDateTime(), KLocale::ShortDate, KLocale::DateTimeFormatOptions(showZone ? KLocale::TimeZone : 0));
919  }
920  }
921  return tm;
922 }
923 
924 /******************************************************************************
925 * Set the remaining time text in a reminder window.
926 * Called at the start of every day (at the user-defined start-of-day time).
927 */
928 void MessageWin::setRemainingTextDay()
929 {
930  QString text;
931  const int days = KDateTime::currentLocalDate().daysTo(mDateTime.date());
932  if (days <= 0 && !mDateTime.isDateOnly())
933  {
934  // The alarm is due today, so start refreshing every minute
935  MidnightTimer::disconnect(this, SLOT(setRemainingTextDay()));
936  setRemainingTextMinute();
937  MinuteTimer::connect(this, SLOT(setRemainingTextMinute())); // update every minute
938  }
939  else
940  {
941  if (days <= 0)
942  text = i18nc("@info", "Today");
943  else if (days % 7)
944  text = i18ncp("@info", "Tomorrow", "in %1 days' time", days);
945  else
946  text = i18ncp("@info", "in 1 week's time", "in %1 weeks' time", days/7);
947  }
948  mRemainingText->setText(text);
949 }
950 
951 /******************************************************************************
952 * Set the remaining time text in a reminder window.
953 * Called on every minute boundary.
954 */
955 void MessageWin::setRemainingTextMinute()
956 {
957  QString text;
958  const int mins = (KDateTime::currentUtcDateTime().secsTo(mDateTime.effectiveKDateTime()) + 59) / 60;
959  if (mins < 60)
960  text = i18ncp("@info", "in 1 minute's time", "in %1 minutes' time", (mins > 0 ? mins : 0));
961  else if (mins % 60 == 0)
962  text = i18ncp("@info", "in 1 hour's time", "in %1 hours' time", mins/60);
963  else
964  {
965  QString hourText = i18ncp("@item:intext inserted into 'in ... %1 minute's time' below", "1 hour", "%1 hours", mins/60);
966  text = i18ncp("@info '%2' is the previous message '1 hour'/'%1 hours'", "in %2 1 minute's time", "in %2 %1 minutes' time", mins%60, hourText);
967  }
968  mRemainingText->setText(text);
969 }
970 
971 /******************************************************************************
972 * Called when output is available from the command which is providing the text
973 * for this window. Add the output and resize the window to show it.
974 */
975 void MessageWin::readProcessOutput(ShellProcess* proc)
976 {
977  const QByteArray data = proc->readAll();
978  if (!data.isEmpty())
979  {
980  // Strip any trailing newline, to avoid showing trailing blank line
981  // in message window.
982  if (mCommandText->newLine())
983  mCommandText->append(QLatin1String("\n"));
984  const int nl = data.endsWith('\n') ? 1 : 0;
985  mCommandText->setNewLine(nl);
986  mCommandText->insertPlainText(QString::fromLocal8Bit(data.data(), data.length() - nl));
987  resize(sizeHint());
988  }
989 }
990 
991 /******************************************************************************
992 * Save settings to the session managed config file, for restoration
993 * when the program is restored.
994 */
995 void MessageWin::saveProperties(KConfigGroup& config)
996 {
997  if (mShown && !mErrorWindow && !mAlwaysHide)
998  {
999 #ifdef USE_AKONADI
1000  config.writeEntry("EventID", mEventId.eventId());
1001  config.writeEntry("EventItemID", mEventItemId);
1002 #else
1003  config.writeEntry("EventID", mEventId);
1004 #endif
1005  config.writeEntry("AlarmType", static_cast<int>(mAlarmType));
1006  if (mAlarmType == KAAlarm::INVALID_ALARM)
1007  kError() << "Invalid alarm: id=" << mEventId << ", alarm count=" << mEvent.alarmCount();
1008  config.writeEntry("Message", mMessage);
1009  config.writeEntry("Type", static_cast<int>(mAction));
1010  config.writeEntry("Font", mFont);
1011  config.writeEntry("BgColour", mBgColour);
1012  config.writeEntry("FgColour", mFgColour);
1013  config.writeEntry("ConfirmAck", mConfirmAck);
1014  if (mDateTime.isValid())
1015  {
1016 //TODO: Write KDateTime when it becomes possible
1017  config.writeEntry("Time", mDateTime.effectiveDateTime());
1018  config.writeEntry("DateOnly", mDateTime.isDateOnly());
1019  QString zone;
1020  if (mDateTime.isUtc())
1021  zone = QLatin1String("UTC");
1022  else
1023  {
1024  const KTimeZone tz = mDateTime.timeZone();
1025  if (tz.isValid())
1026  zone = tz.name();
1027  }
1028  config.writeEntry("TimeZone", zone);
1029  }
1030  if (mCloseTime.isValid())
1031  config.writeEntry("Expiry", mCloseTime);
1032  if (mAudioRepeatPause >= 0 && mSilenceButton && mSilenceButton->isEnabled())
1033  {
1034  // Only need to restart sound file playing if it's being repeated
1035  config.writePathEntry("AudioFile", mAudioFile);
1036  config.writeEntry("Volume", static_cast<int>(mVolume * 100));
1037  config.writeEntry("AudioPause", mAudioRepeatPause);
1038  }
1039  config.writeEntry("Speak", mSpeak);
1040  config.writeEntry("Height", height());
1041  config.writeEntry("DeferMins", mDefaultDeferMinutes);
1042  config.writeEntry("NoDefer", mNoDefer);
1043  config.writeEntry("NoPostAction", mNoPostAction);
1044  config.writeEntry("KMailSerial", static_cast<qulonglong>(mKMailSerialNumber));
1045  config.writeEntry("CmdErr", static_cast<int>(mCommandError));
1046  config.writeEntry("DontShowAgain", mDontShowAgain);
1047  }
1048  else
1049  config.writeEntry("Invalid", true);
1050 }
1051 
1052 /******************************************************************************
1053 * Read settings from the session managed config file.
1054 * This function is automatically called whenever the app is being restored.
1055 * Read in whatever was saved in saveProperties().
1056 */
1057 void MessageWin::readProperties(const KConfigGroup& config)
1058 {
1059  mInvalid = config.readEntry("Invalid", false);
1060  QString eventId = config.readEntry("EventID");
1061 #ifdef USE_AKONADI
1062  mEventItemId = config.readEntry("EventItemID", Akonadi::Item::Id(-1));
1063 #else
1064  mEventId = eventId;
1065 #endif
1066  mAlarmType = static_cast<KAAlarm::Type>(config.readEntry("AlarmType", 0));
1067  if (mAlarmType == KAAlarm::INVALID_ALARM)
1068  {
1069  mInvalid = true;
1070  kError() << "Invalid alarm: id=" << eventId;
1071  }
1072  mMessage = config.readEntry("Message");
1073  mAction = static_cast<KAEvent::SubAction>(config.readEntry("Type", 0));
1074  mFont = config.readEntry("Font", QFont());
1075  mBgColour = config.readEntry("BgColour", QColor(Qt::white));
1076  mFgColour = config.readEntry("FgColour", QColor(Qt::black));
1077  mConfirmAck = config.readEntry("ConfirmAck", false);
1078  QDateTime invalidDateTime;
1079  QDateTime dt = config.readEntry("Time", invalidDateTime);
1080  const QString zone = config.readEntry("TimeZone");
1081  if (zone.isEmpty())
1082  mDateTime = KDateTime(dt, KDateTime::ClockTime);
1083  else if (zone == QLatin1String("UTC"))
1084  {
1085  dt.setTimeSpec(Qt::UTC);
1086  mDateTime = KDateTime(dt, KDateTime::UTC);
1087  }
1088  else
1089  {
1090  KTimeZone tz = KSystemTimeZones::zone(zone);
1091  mDateTime = KDateTime(dt, (tz.isValid() ? tz : KSystemTimeZones::local()));
1092  }
1093  const bool dateOnly = config.readEntry("DateOnly", false);
1094  if (dateOnly)
1095  mDateTime.setDateOnly(true);
1096  mCloseTime = config.readEntry("Expiry", invalidDateTime);
1097  mCloseTime.setTimeSpec(Qt::UTC);
1098  mAudioFile = config.readPathEntry("AudioFile", QString());
1099  mVolume = static_cast<float>(config.readEntry("Volume", 0)) / 100;
1100  mFadeVolume = -1;
1101  mFadeSeconds = 0;
1102  if (!mAudioFile.isEmpty()) // audio file URL was only saved if it repeats
1103  mAudioRepeatPause = config.readEntry("AudioPause", 0);
1104  mBeep = false; // don't beep after restart (similar to not playing non-repeated sound file)
1105  mSpeak = config.readEntry("Speak", false);
1106  mRestoreHeight = config.readEntry("Height", 0);
1107  mDefaultDeferMinutes = config.readEntry("DeferMins", 0);
1108  mNoDefer = config.readEntry("NoDefer", false);
1109  mNoPostAction = config.readEntry("NoPostAction", true);
1110  mKMailSerialNumber = static_cast<unsigned long>(config.readEntry("KMailSerial", QVariant(QVariant::ULongLong)).toULongLong());
1111  mCommandError = KAEvent::CmdErrType(config.readEntry("CmdErr", static_cast<int>(KAEvent::CMD_NO_ERROR)));
1112  mDontShowAgain = config.readEntry("DontShowAgain", QString());
1113  mShowEdit = false;
1114 #ifdef USE_AKONADI
1115  // Temporarily initialise mCollection and mEventId - they will be set by redisplayAlarm()
1116  mCollection = Akonadi::Collection();
1117  mEventId = EventId(mCollection.id(), eventId);
1118 #else
1119  mResource = 0;
1120 #endif
1121  kDebug() << eventId;
1122  if (mAlarmType != KAAlarm::INVALID_ALARM)
1123  {
1124  // Recreate the event from the calendar file (if possible)
1125  if (eventId.isEmpty())
1126  initView();
1127  else
1128  {
1129  // Close any other window for this alarm which has already been restored by redisplayAlarms()
1130 #ifdef USE_AKONADI
1131  if (!AkonadiModel::instance()->isCollectionTreeFetched())
1132  {
1133  connect(AkonadiModel::instance(), SIGNAL(collectionTreeFetched(Akonadi::Collection::List)),
1134  SLOT(showRestoredAlarm()));
1135  return;
1136  }
1137 #endif
1138  redisplayAlarm();
1139  }
1140  }
1141 }
1142 
1143 #ifdef USE_AKONADI
1144 /******************************************************************************
1145 * Fetch the restored alarm from the calendar and redisplay it in this window.
1146 */
1147 void MessageWin::showRestoredAlarm()
1148 {
1149  kDebug() << mEventId;
1150  redisplayAlarm();
1151  show();
1152 }
1153 #endif
1154 
1155 /******************************************************************************
1156 * Fetch the restored alarm from the calendar and redisplay it in this window.
1157 */
1158 void MessageWin::redisplayAlarm()
1159 {
1160 #ifdef USE_AKONADI
1161  mCollection = AkonadiModel::instance()->collectionForItem(mEventItemId);
1162  mEventId.setCollectionId(mCollection.id());
1163 #endif
1164  kDebug() << mEventId;
1165  // Delete any already existing window for the same event
1166  MessageWin* duplicate = findEvent(mEventId, this);
1167  if (duplicate)
1168  kDebug() << "Deleting duplicate window:" << mEventId;
1169  delete duplicate;
1170 
1171  KAEvent* event = AlarmCalendar::resources()->event(mEventId);
1172  if (event)
1173  {
1174  mEvent = *event;
1175 #ifndef USE_AKONADI
1176  mResource = AlarmCalendar::resources()->resourceForEvent(mEventId);
1177 #endif
1178  mShowEdit = true;
1179  }
1180  else
1181  {
1182  // It's not in the active calendar, so try the displaying or archive calendars
1183 #ifdef USE_AKONADI
1184  retrieveEvent(mEvent, mCollection, mShowEdit, mNoDefer);
1185 #else
1186  retrieveEvent(mEvent, mResource, mShowEdit, mNoDefer);
1187 #endif
1188  mNoDefer = !mNoDefer;
1189  }
1190  initView();
1191 }
1192 
1193 /******************************************************************************
1194 * Redisplay alarms which were being shown when the program last exited.
1195 * Normally, these alarms will have been displayed by session restoration, but
1196 * if the program crashed or was killed, we can redisplay them here so that
1197 * they won't be lost.
1198 */
1199 void MessageWin::redisplayAlarms()
1200 {
1201  if (mRedisplayed)
1202  return;
1203  kDebug();
1204  mRedisplayed = true;
1205  AlarmCalendar* cal = AlarmCalendar::displayCalendar();
1206  if (cal->isOpen())
1207  {
1208  KAEvent event;
1209 #ifdef USE_AKONADI
1210  Akonadi::Collection collection;
1211 #else
1212  AlarmResource* resource;
1213 #endif
1214  const Event::List events = cal->kcalEvents();
1215  for (int i = 0, end = events.count(); i < end; ++i)
1216  {
1217  bool showDefer, showEdit;
1218 #ifdef USE_AKONADI
1219  reinstateFromDisplaying(events[i], event, collection, showEdit, showDefer);
1220  Akonadi::Item::Id id = AkonadiModel::instance()->findItemId(event);
1221  if (id >= 0)
1222  event.setItemId(id);
1223  const EventId eventId(event);
1224 #else
1225  reinstateFromDisplaying(events[i], event, resource, showEdit, showDefer);
1226  const QString eventId = event.id();
1227 #endif
1228  if (findEvent(eventId))
1229  kDebug() << "Message window already exists:" << eventId;
1230  else
1231  {
1232  // This event should be displayed, but currently isn't being
1233  const KAAlarm alarm = event.convertDisplayingAlarm();
1234  if (alarm.type() == KAAlarm::INVALID_ALARM)
1235  {
1236  kError() << "Invalid alarm: id=" << eventId;
1237  continue;
1238  }
1239  kDebug() << eventId;
1240  const bool login = alarm.repeatAtLogin();
1241  const int flags = NO_RESCHEDULE | (login ? NO_DEFER : 0) | NO_INIT_VIEW;
1242  MessageWin* win = new MessageWin(&event, alarm, flags);
1243 #ifdef USE_AKONADI
1244  win->mCollection = collection;
1245  const bool rw = CollectionControlModel::isWritableEnabled(collection, event.category()) > 0;
1246 #else
1247  win->mResource = resource;
1248  const bool rw = resource && resource->writable();
1249 #endif
1250  win->mShowEdit = rw ? showEdit : false;
1251  win->mNoDefer = (rw && !login) ? !showDefer : true;
1252  win->initView();
1253  win->show();
1254  }
1255  }
1256  }
1257 }
1258 
1259 /******************************************************************************
1260 * Retrieves the event with the current ID from the displaying calendar file,
1261 * or if not found there, from the archive calendar.
1262 */
1263 #ifdef USE_AKONADI
1264 bool MessageWin::retrieveEvent(KAEvent& event, Akonadi::Collection& resource, bool& showEdit, bool& showDefer)
1265 #else
1266 bool MessageWin::retrieveEvent(KAEvent& event, AlarmResource*& resource, bool& showEdit, bool& showDefer)
1267 #endif
1268 {
1269 #ifdef USE_AKONADI
1270  const Event::Ptr kcalEvent = AlarmCalendar::displayCalendar()->kcalEvent(CalEvent::uid(mEventId.eventId(), CalEvent::DISPLAYING));
1271 #else
1272  const Event* kcalEvent = AlarmCalendar::displayCalendar()->kcalEvent(CalEvent::uid(mEventId, CalEvent::DISPLAYING));
1273 #endif
1274  if (!reinstateFromDisplaying(kcalEvent, event, resource, showEdit, showDefer))
1275  {
1276  // The event isn't in the displaying calendar.
1277  // Try to retrieve it from the archive calendar.
1278 #ifdef USE_AKONADI
1279  KAEvent* ev = 0;
1280  Akonadi::Collection archiveCol = CollectionControlModel::getStandard(CalEvent::ARCHIVED);
1281  if (archiveCol.isValid())
1282  ev = AlarmCalendar::resources()->event(EventId(archiveCol.id(), CalEvent::uid(mEventId.eventId(), CalEvent::ARCHIVED)));
1283 #else
1284  const KAEvent* ev = AlarmCalendar::resources()->event(CalEvent::uid(mEventId, CalEvent::ARCHIVED));
1285 #endif
1286  if (!ev)
1287  return false;
1288  event = *ev;
1289  event.setArchive(); // ensure that it gets re-archived if it's saved
1290  event.setCategory(CalEvent::ACTIVE);
1291 #ifdef USE_AKONADI
1292  if (mEventId.eventId() != event.id())
1293  kError() << "Wrong event ID";
1294  event.setEventId(mEventId.eventId());
1295  resource = Akonadi::Collection();
1296 #else
1297  if (mEventId != event.id())
1298  kError() << "Wrong event ID";
1299  event.setEventId(mEventId);
1300  resource = 0;
1301 #endif
1302  showEdit = true;
1303  showDefer = true;
1304  kDebug() << event.id() << ": success";
1305  }
1306  return true;
1307 }
1308 
1309 /******************************************************************************
1310 * Retrieves the displayed event from the calendar file, or if not found there,
1311 * from the displaying calendar.
1312 */
1313 #ifdef USE_AKONADI
1314 bool MessageWin::reinstateFromDisplaying(const Event::Ptr& kcalEvent, KAEvent& event, Akonadi::Collection& collection, bool& showEdit, bool& showDefer)
1315 #else
1316 bool MessageWin::reinstateFromDisplaying(const Event* kcalEvent, KAEvent& event, AlarmResource*& resource, bool& showEdit, bool& showDefer)
1317 #endif
1318 {
1319  if (!kcalEvent)
1320  return false;
1321 #ifdef USE_AKONADI
1322  Akonadi::Collection::Id collectionId;
1323  event.reinstateFromDisplaying(kcalEvent, collectionId, showEdit, showDefer);
1324  event.setCollectionId(collectionId);
1325  collection = AkonadiModel::instance()->collectionById(collectionId);
1326  kDebug() << EventId(event) << ": success";
1327 #else
1328  QString resourceID;
1329  event.reinstateFromDisplaying(kcalEvent, resourceID, showEdit, showDefer);
1330  resource = AlarmResources::instance()->resourceWithId(resourceID);
1331  if (resource && !resource->isOpen())
1332  resource = 0;
1333  kDebug() << event.id() << ": success";
1334 #endif
1335  return true;
1336 }
1337 
1338 /******************************************************************************
1339 * Called when an alarm is currently being displayed, to store a copy of the
1340 * alarm in the displaying calendar, and to reschedule it for its next repetition.
1341 * If no repetitions remain, cancel it.
1342 */
1343 void MessageWin::alarmShowing(KAEvent& event)
1344 {
1345  kDebug() << event.id() << "," << KAAlarm::debugType(mAlarmType);
1346 #ifndef USE_AKONADI
1347  const KCal::Event* kcalEvent = AlarmCalendar::resources()->kcalEvent(event.id());
1348  if (!kcalEvent)
1349  {
1350  kError() << "Event ID not found:" << event.id();
1351  return;
1352  }
1353 #endif
1354  const KAAlarm alarm = event.alarm(mAlarmType);
1355  if (!alarm.isValid())
1356  {
1357  kError() << "Alarm type not found:" << event.id() << ":" << mAlarmType;
1358  return;
1359  }
1360  if (!mAlwaysHide)
1361  {
1362  // Copy the alarm to the displaying calendar in case of a crash, etc.
1363 #ifdef USE_AKONADI
1364  KAEvent dispEvent;
1365  const Akonadi::Collection collection = AkonadiModel::instance()->collectionForItem(event.itemId());
1366  dispEvent.setDisplaying(event, mAlarmType, collection.id(),
1367  mDateTime.effectiveKDateTime(), mShowEdit, !mNoDefer);
1368 #else
1369  KAEvent* dispEvent = new KAEvent;
1370  const AlarmResource* resource = AlarmResources::instance()->resource(kcalEvent);
1371  dispEvent->setDisplaying(event, mAlarmType, (resource ? resource->identifier() : QString()),
1372  mDateTime.effectiveKDateTime(), mShowEdit, !mNoDefer);
1373 #endif
1374  AlarmCalendar* cal = AlarmCalendar::displayCalendarOpen();
1375  if (cal)
1376  {
1377 #ifdef USE_AKONADI
1378  cal->deleteDisplayEvent(dispEvent.id()); // in case it already exists
1379  cal->addEvent(dispEvent);
1380 #else
1381  cal->deleteEvent(dispEvent->id()); // in case it already exists
1382  if (!cal->addEvent(dispEvent))
1383  delete dispEvent;
1384 #endif
1385  cal->save();
1386  }
1387 #ifndef USE_AKONADI
1388  else
1389  delete dispEvent;
1390 #endif
1391  }
1392  theApp()->rescheduleAlarm(event, alarm);
1393 }
1394 
1395 /******************************************************************************
1396 * Spread alarm windows over the screen so that they are all visible, or pile
1397 * them on top of each other again.
1398 * Reply = true if windows are now scattered, false if piled up.
1399 */
1400 bool MessageWin::spread(bool scatter)
1401 {
1402  if (instanceCount(true) <= 1) // ignore always-hidden windows
1403  return false;
1404 
1405  const QRect desk = KAlarm::desktopWorkArea(); // get the usable area of the desktop
1406  if (scatter == isSpread(desk.topLeft()))
1407  return scatter;
1408 
1409  if (scatter)
1410  {
1411  // Usually there won't be many windows, so a crude
1412  // scattering algorithm should suffice.
1413  int x = desk.left();
1414  int y = desk.top();
1415  int ynext = y;
1416  for (int errmsgs = 0; errmsgs < 2; ++errmsgs)
1417  {
1418  // Display alarm messages first, then error messages, since most
1419  // error messages tend to be the same height.
1420  for (int i = 0, end = mWindowList.count(); i < end; ++i)
1421  {
1422  MessageWin* w = mWindowList[i];
1423  if ((!errmsgs && w->mErrorWindow)
1424  || (errmsgs && !w->mErrorWindow))
1425  continue;
1426  const QSize sz = w->frameGeometry().size();
1427  if (x + sz.width() > desk.right())
1428  {
1429  x = desk.left();
1430  y = ynext;
1431  }
1432  int ytmp = y;
1433  if (y + sz.height() > desk.bottom())
1434  {
1435  ytmp = desk.bottom() - sz.height();
1436  if (ytmp < desk.top())
1437  ytmp = desk.top();
1438  }
1439  w->move(x, ytmp);
1440  x += sz.width();
1441  if (ytmp + sz.height() > ynext)
1442  ynext = ytmp + sz.height();
1443  }
1444  }
1445  }
1446  else
1447  {
1448  // Move all windows to the top left corner
1449  for (int i = 0, end = mWindowList.count(); i < end; ++i)
1450  mWindowList[i]->move(desk.topLeft());
1451  }
1452  return scatter;
1453 }
1454 
1455 /******************************************************************************
1456 * Check whether message windows are all piled up, or are spread out.
1457 * Reply = true if windows are currently spread, false if piled up.
1458 */
1459 bool MessageWin::isSpread(const QPoint& topLeft)
1460 {
1461  for (int i = 0, end = mWindowList.count(); i < end; ++i)
1462  {
1463  if (mWindowList[i]->pos() != topLeft)
1464  return true;
1465  }
1466  return false;
1467 }
1468 
1469 /******************************************************************************
1470 * Returns the existing message window (if any) which is displaying the event
1471 * with the specified ID.
1472 */
1473 #ifdef USE_AKONADI
1474 MessageWin* MessageWin::findEvent(const EventId& eventId, MessageWin* exclude)
1475 #else
1476 MessageWin* MessageWin::findEvent(const QString& eventId, MessageWin* exclude)
1477 #endif
1478 {
1479  if (!eventId.isEmpty())
1480  {
1481  for (int i = 0, end = mWindowList.count(); i < end; ++i)
1482  {
1483  MessageWin* w = mWindowList[i];
1484  if (w != exclude && w->mEventId == eventId && !w->mErrorWindow)
1485  return w;
1486  }
1487  }
1488  return 0;
1489 }
1490 
1491 /******************************************************************************
1492 * Beep and play the audio file, as appropriate.
1493 */
1494 void MessageWin::playAudio()
1495 {
1496  if (mBeep)
1497  {
1498  // Beep using two methods, in case the sound card/speakers are switched off or not working
1499  QApplication::beep(); // beep through the internal speaker
1500  KNotification::beep(); // beep through the sound card & speakers
1501  }
1502  if (!mAudioFile.isEmpty())
1503  {
1504  if (!mVolume && mFadeVolume <= 0)
1505  return; // ensure zero volume doesn't play anything
1506  startAudio(); // play the audio file
1507  }
1508  else if (mSpeak)
1509  {
1510  // The message is to be spoken. In case of error messges,
1511  // call it on a timer to allow the window to display first.
1512  QTimer::singleShot(0, this, SLOT(slotSpeak()));
1513  }
1514 }
1515 
1516 /******************************************************************************
1517 * Speak the message.
1518 * Called asynchronously to avoid delaying the display of the message.
1519 */
1520 void MessageWin::slotSpeak()
1521 {
1522  QString error;
1523  OrgKdeKSpeechInterface* kspeech = theApp()->kspeechInterface(error);
1524  if (!kspeech)
1525  {
1526  if (!haveErrorMessage(ErrMsg_Speak))
1527  {
1528  KAMessageBox::detailedError(MainWindow::mainMainWindow(), i18nc("@info", "Unable to speak message"), error);
1529  clearErrorMessage(ErrMsg_Speak);
1530  }
1531  return;
1532  }
1533  if (!kspeech->say(mMessage, 0))
1534  {
1535  kDebug() << "SayMessage() D-Bus error";
1536  if (!haveErrorMessage(ErrMsg_Speak))
1537  {
1538  KAMessageBox::detailedError(MainWindow::mainMainWindow(), i18nc("@info", "Unable to speak message"), i18nc("@info", "D-Bus call say() failed"));
1539  clearErrorMessage(ErrMsg_Speak);
1540  }
1541  }
1542 }
1543 
1544 /******************************************************************************
1545 * Called when another window's audio thread has been destructed.
1546 * Start playing this window's audio file. Because initialising the sound system
1547 * and loading the file may take some time, it is called in a separate thread to
1548 * allow the window to display first.
1549 */
1550 void MessageWin::startAudio()
1551 {
1552  if (mAudioThread)
1553  {
1554  // An audio file is already playing for another message
1555  // window, so wait until it has finished.
1556  connect(mAudioThread, SIGNAL(destroyed(QObject*)), SLOT(audioTerminating()));
1557  }
1558  else
1559  {
1560  kDebug() << QThread::currentThread();
1561  mAudioThread = new AudioThread(this, mAudioFile, mVolume, mFadeVolume, mFadeSeconds, mAudioRepeatPause);
1562  connect(mAudioThread, SIGNAL(readyToPlay()), SLOT(playReady()));
1563  connect(mAudioThread, SIGNAL(finished()), SLOT(playFinished()));
1564  if (mSilenceButton)
1565  connect(mSilenceButton, SIGNAL(clicked()), mAudioThread, SLOT(quit()));
1566  // Notify after creating mAudioThread, so that isAudioPlaying() will
1567  // return the correct value.
1568  theApp()->notifyAudioPlaying(true);
1569  mAudioThread->start();
1570  }
1571 }
1572 
1573 /******************************************************************************
1574 * Return whether audio playback is currently active.
1575 */
1576 bool MessageWin::isAudioPlaying()
1577 {
1578  return mAudioThread;
1579 }
1580 
1581 /******************************************************************************
1582 * Stop audio playback.
1583 */
1584 void MessageWin::stopAudio(bool wait)
1585 {
1586  kDebug();
1587  if (mAudioThread)
1588  mAudioThread->stop(wait);
1589 }
1590 
1591 /******************************************************************************
1592 * Called when another window's audio thread is being destructed.
1593 * Wait until the destructor has finished.
1594 */
1595 void MessageWin::audioTerminating()
1596 {
1597  QTimer::singleShot(0, this, SLOT(startAudio()));
1598 }
1599 
1600 /******************************************************************************
1601 * Called when the audio file is ready to start playing.
1602 */
1603 void MessageWin::playReady()
1604 {
1605  if (mSilenceButton)
1606  mSilenceButton->setEnabled(true);
1607 }
1608 
1609 /******************************************************************************
1610 * Called when the audio file thread finishes.
1611 */
1612 void MessageWin::playFinished()
1613 {
1614  if (mSilenceButton)
1615  mSilenceButton->setEnabled(false);
1616  if (mAudioThread) // mAudioThread can actually be null here!
1617  {
1618  const QString errmsg = mAudioThread->error();
1619  if (!errmsg.isEmpty() && !haveErrorMessage(ErrMsg_AudioFile))
1620  {
1621  KAMessageBox::error(this, errmsg);
1622  clearErrorMessage(ErrMsg_AudioFile);
1623  }
1624  }
1625  delete mAudioThread.data();
1626  if (mAlwaysHide)
1627  close();
1628 }
1629 
1630 /******************************************************************************
1631 * Constructor for audio thread.
1632 */
1633 AudioThread::AudioThread(MessageWin* parent, const QString& audioFile, float volume, float fadeVolume, int fadeSeconds, int repeatPause)
1634  : QThread(parent),
1635  mFile(audioFile),
1636  mVolume(volume),
1637  mFadeVolume(fadeVolume),
1638  mFadeSeconds(fadeSeconds),
1639  mRepeatPause(repeatPause),
1640  mAudioObject(0)
1641 {
1642  if (mAudioOwner)
1643  kError() << "mAudioOwner already set";
1644  mAudioOwner = parent;
1645 }
1646 
1647 /******************************************************************************
1648 * Destructor for audio thread. Waits for thread completion and tidies up.
1649 * Note that this destructor is executed in the parent thread.
1650 */
1651 AudioThread::~AudioThread()
1652 {
1653  kDebug();
1654  stop(true); // stop playing and tidy up (timeout 3 seconds)
1655  delete mAudioObject;
1656  mAudioObject = 0;
1657  if (mAudioOwner == parent())
1658  mAudioOwner = 0;
1659  // Notify after deleting mAudioThread, so that isAudioPlaying() will
1660  // return the correct value.
1661  QTimer::singleShot(0, theApp(), SLOT(notifyAudioStopped()));
1662 }
1663 
1664 /******************************************************************************
1665 * Quits the thread and waits for thread completion and tidies up.
1666 */
1667 void AudioThread::stop(bool waiT)
1668 {
1669  kDebug();
1670  quit(); // stop playing and tidy up
1671  wait(3000); // wait for run() to exit (timeout 3 seconds)
1672  if (!isFinished())
1673  {
1674  // Something has gone wrong - forcibly kill the thread
1675  terminate();
1676  if (waiT)
1677  wait();
1678  }
1679 }
1680 
1681 /******************************************************************************
1682 * Kick off the thread to play the audio file.
1683 */
1684 void AudioThread::run()
1685 {
1686  mMutex.lock();
1687  if (mAudioObject)
1688  {
1689  mMutex.unlock();
1690  return;
1691  }
1692  kDebug() << QThread::currentThread() << mFile;
1693  const QString audioFile = mFile;
1694  const QUrl url = QUrl::fromUserInput(mFile);
1695  mFile = url.isLocalFile() ? url.toLocalFile() : url.toString();
1696  Phonon::MediaSource source(url);
1697  if (source.type() == Phonon::MediaSource::Invalid)
1698  {
1699  mError = i18nc("@info", "Cannot open audio file: <filename>%1</filename>", audioFile);
1700  mMutex.unlock();
1701  kError() << "Open failure:" << audioFile;
1702  return;
1703  }
1704  mAudioObject = new Phonon::MediaObject();
1705  mAudioObject->setCurrentSource(source);
1706  mAudioObject->setTransitionTime(100); // workaround to prevent clipping of end of files in Xine backend
1707  Phonon::AudioOutput* output = new Phonon::AudioOutput(Phonon::NotificationCategory, mAudioObject);
1708  mPath = Phonon::createPath(mAudioObject, output);
1709  if (mVolume >= 0 || mFadeVolume >= 0)
1710  {
1711  const float vol = (mVolume >= 0) ? mVolume : output->volume();
1712  const float maxvol = qMax(vol, mFadeVolume);
1713  output->setVolume(maxvol);
1714  if (mFadeVolume >= 0 && mFadeSeconds > 0)
1715  {
1716  Phonon::VolumeFaderEffect* fader = new Phonon::VolumeFaderEffect(mAudioObject);
1717  fader->setVolume(mFadeVolume / maxvol);
1718  fader->fadeTo(mVolume / maxvol, mFadeSeconds * 1000);
1719  mPath.insertEffect(fader);
1720  }
1721  }
1722  connect(mAudioObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)), SLOT(playStateChanged(Phonon::State)), Qt::DirectConnection);
1723  connect(mAudioObject, SIGNAL(finished()), SLOT(checkAudioPlay()), Qt::DirectConnection);
1724  mPlayedOnce = false;
1725  mPausing = false;
1726  mMutex.unlock();
1727  emit readyToPlay();
1728  checkAudioPlay();
1729 
1730  // Start an event loop.
1731  // The function will exit once exit() or quit() is called.
1732  // First, ensure that the thread object is deleted once it has completed.
1733  connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
1734  exec();
1735  stopPlay();
1736 }
1737 
1738 /******************************************************************************
1739 * Called when the audio file has loaded and is ready to play, or when play
1740 * has completed.
1741 * If it is ready to play, start playing it (for the first time or repeated).
1742 * If play has not yet completed, wait a bit longer.
1743 */
1744 void AudioThread::checkAudioPlay()
1745 {
1746  mMutex.lock();
1747  if (!mAudioObject)
1748  {
1749  mMutex.unlock();
1750  return;
1751  }
1752  if (mPausing)
1753  mPausing = false;
1754  else
1755  {
1756  // The file has loaded and is ready to play, or play has completed
1757  if (mPlayedOnce)
1758  {
1759  if (mRepeatPause < 0)
1760  {
1761  // Play has completed
1762  mMutex.unlock();
1763  stopPlay();
1764  return;
1765  }
1766  if (mRepeatPause > 0)
1767  {
1768  // Pause before playing the file again
1769  mPausing = true;
1770  QTimer::singleShot(mRepeatPause * 1000, this, SLOT(checkAudioPlay()));
1771  mMutex.unlock();
1772  return;
1773  }
1774  }
1775  mPlayedOnce = true;
1776  }
1777 
1778  // Start playing the file, either for the first time or again
1779  kDebug() << "start";
1780  mAudioObject->play();
1781  mMutex.unlock();
1782 }
1783 
1784 /******************************************************************************
1785 * Called when the playback object changes state.
1786 * If an error has occurred, quit and return the error to the caller.
1787 */
1788 void AudioThread::playStateChanged(Phonon::State newState)
1789 {
1790  if (newState == Phonon::ErrorState)
1791  {
1792  QMutexLocker locker(&mMutex);
1793  const QString err = mAudioObject->errorString();
1794  if (!err.isEmpty())
1795  {
1796  kError() << "Play failure:" << mFile << ":" << err;
1797  mError = i18nc("@info", "<para>Error playing audio file: <filename>%1</filename></para><para>%2</para>", mFile, err);
1798  exit(1);
1799  }
1800  }
1801 }
1802 
1803 /******************************************************************************
1804 * Called when play completes, the Silence button is clicked, or the window is
1805 * closed, to terminate audio access.
1806 */
1807 void AudioThread::stopPlay()
1808 {
1809  mMutex.lock();
1810  if (mAudioObject)
1811  {
1812  mAudioObject->stop();
1813  const QList<Phonon::Effect*> effects = mPath.effects();
1814  for (int i = 0; i < effects.count(); ++i)
1815  {
1816  mPath.removeEffect(effects[i]);
1817  delete effects[i];
1818  }
1819  delete mAudioObject;
1820  mAudioObject = 0;
1821  }
1822  mMutex.unlock();
1823  quit(); // exit the event loop, if it's still running
1824 }
1825 
1826 QString AudioThread::error() const
1827 {
1828  QMutexLocker locker(&mMutex);
1829  return mError;
1830 }
1831 
1832 /******************************************************************************
1833 * Raise the alarm window, re-output any required audio notification, and
1834 * reschedule the alarm in the calendar file.
1835 */
1836 void MessageWin::repeat(const KAAlarm& alarm)
1837 {
1838  if (!mInitialised)
1839  return;
1840  if (mDeferDlg)
1841  {
1842  // Cancel any deferral dialog so that the user notices something's going on,
1843  // and also because the deferral time limit will have changed.
1844  delete mDeferDlg;
1845  mDeferDlg = 0;
1846  }
1847  KAEvent* event = mEventId.isEmpty() ? 0 : AlarmCalendar::resources()->event(mEventId);
1848  if (event)
1849  {
1850  mAlarmType = alarm.type(); // store new alarm type for use if it is later deferred
1851  if (mAlwaysHide)
1852  playAudio();
1853  else
1854  {
1855  if (!mDeferDlg || Preferences::modalMessages())
1856  {
1857  raise();
1858  playAudio();
1859  }
1860  if (mDeferButton->isVisible())
1861  {
1862  mDeferButton->setEnabled(true);
1863  setDeferralLimit(*event); // ensure that button is disabled when alarm can't be deferred any more
1864  }
1865  }
1866  alarmShowing(*event);
1867  }
1868 }
1869 
1870 /******************************************************************************
1871 * Display the window.
1872 * If windows are being positioned away from the mouse cursor, it is initially
1873 * positioned at the top left to slightly reduce the number of times the
1874 * windows need to be moved in showEvent().
1875 */
1876 void MessageWin::show()
1877 {
1878  if (mCloseTime.isValid())
1879  {
1880  // Set a timer to auto-close the window
1881  int delay = KDateTime::currentUtcDateTime().dateTime().secsTo(mCloseTime);
1882  if (delay < 0)
1883  delay = 0;
1884  QTimer::singleShot(delay * 1000, this, SLOT(close()));
1885  if (!delay)
1886  return; // don't show the window if auto-closing is already due
1887  }
1888  if (Preferences::messageButtonDelay() == 0)
1889  move(0, 0);
1890  MainWindowBase::show();
1891 }
1892 
1893 /******************************************************************************
1894 * Returns the window's recommended size exclusive of its frame.
1895 */
1896 QSize MessageWin::sizeHint() const
1897 {
1898  QSize desired;
1899  switch (mAction)
1900  {
1901  case KAEvent::MESSAGE:
1902  desired = MainWindowBase::sizeHint();
1903  break;
1904  case KAEvent::COMMAND:
1905  if (mShown)
1906  {
1907  // For command output, expand the window to accommodate the text
1908  const QSize texthint = mCommandText->sizeHint();
1909  int w = texthint.width() + 2*KDialog::marginHint();
1910  if (w < width())
1911  w = width();
1912  const int ypadding = height() - mCommandText->height();
1913  desired = QSize(w, texthint.height() + ypadding);
1914  break;
1915  }
1916  // fall through to default
1917  default:
1918  return MainWindowBase::sizeHint();
1919  }
1920 
1921  // Limit the size to fit inside the working area of the desktop
1922  const QSize desktop = KAlarm::desktopWorkArea(mScreenNumber).size();
1923  const QSize frameThickness = frameGeometry().size() - geometry().size(); // title bar & window frame
1924  return desired.boundedTo(desktop - frameThickness);
1925 }
1926 
1927 /******************************************************************************
1928 * Called when the window is shown.
1929 * The first time, output any required audio notification, and reschedule or
1930 * delete the event from the calendar file.
1931 */
1932 void MessageWin::showEvent(QShowEvent* se)
1933 {
1934  MainWindowBase::showEvent(se);
1935  if (mShown || !mInitialised)
1936  return;
1937  if (mErrorWindow || mAlarmType == KAAlarm::INVALID_ALARM)
1938  {
1939  // Don't bother repositioning error messages,
1940  // and invalid alarms should be deleted anyway.
1941  enableButtons();
1942  }
1943  else
1944  {
1945  /* Set the window size.
1946  * Note that the frame thickness is not yet known when this
1947  * method is called, so for large windows the size needs to be
1948  * set again later.
1949  */
1950  bool execComplete = true;
1951  QSize s = sizeHint(); // fit the window round the message
1952  if (mAction == KAEvent::FILE && !mErrorMsgs.count())
1953  KAlarm::readConfigWindowSize("FileMessage", s);
1954  resize(s);
1955 
1956  const QRect desk = KAlarm::desktopWorkArea(mScreenNumber);
1957  const QRect frame = frameGeometry();
1958 
1959  mButtonDelay = Preferences::messageButtonDelay() * 1000;
1960  if (mButtonDelay)
1961  {
1962  // Position the window in the middle of the screen, and
1963  // delay enabling the buttons.
1964  mPositioning = true;
1965  move((desk.width() - frame.width())/2, (desk.height() - frame.height())/2);
1966  execComplete = false;
1967  }
1968  else
1969  {
1970  /* Try to ensure that the window can't accidentally be acknowledged
1971  * by the user clicking the mouse just as it appears.
1972  * To achieve this, move the window so that the OK button is as far away
1973  * from the cursor as possible. If the buttons are still too close to the
1974  * cursor, disable the buttons for a short time.
1975  * N.B. This can't be done in show(), since the geometry of the window
1976  * is not known until it is displayed. Unfortunately by moving the
1977  * window in showEvent(), a flicker is unavoidable.
1978  * See the Qt documentation on window geometry for more details.
1979  */
1980  // PROBLEM: The frame size is not known yet!
1981  const QPoint cursor = QCursor::pos();
1982  const QRect rect = geometry();
1983  // Find the offsets from the outside of the frame to the edges of the OK button
1984  const QRect button(mOkButton->mapToParent(QPoint(0, 0)), mOkButton->mapToParent(mOkButton->rect().bottomRight()));
1985  const int buttonLeft = button.left() + rect.left() - frame.left();
1986  const int buttonRight = width() - button.right() + frame.right() - rect.right();
1987  const int buttonTop = button.top() + rect.top() - frame.top();
1988  const int buttonBottom = height() - button.bottom() + frame.bottom() - rect.bottom();
1989 
1990  const int centrex = (desk.width() + buttonLeft - buttonRight) / 2;
1991  const int centrey = (desk.height() + buttonTop - buttonBottom) / 2;
1992  const int x = (cursor.x() < centrex) ? desk.right() - frame.width() : desk.left();
1993  const int y = (cursor.y() < centrey) ? desk.bottom() - frame.height() : desk.top();
1994 
1995  // Find the enclosing rectangle for the new button positions
1996  // and check if the cursor is too near
1997  QRect buttons = mOkButton->geometry().unite(mKAlarmButton->geometry());
1998  buttons.translate(rect.left() + x - frame.left(), rect.top() + y - frame.top());
1999  const int minDistance = proximityMultiple * mOkButton->height();
2000  if ((abs(cursor.x() - buttons.left()) < minDistance
2001  || abs(cursor.x() - buttons.right()) < minDistance)
2002  && (abs(cursor.y() - buttons.top()) < minDistance
2003  || abs(cursor.y() - buttons.bottom()) < minDistance))
2004  mButtonDelay = proximityButtonDelay; // too near - disable buttons initially
2005 
2006  if (x != frame.left() || y != frame.top())
2007  {
2008  mPositioning = true;
2009  move(x, y);
2010  execComplete = false;
2011  }
2012  }
2013  if (execComplete)
2014  displayComplete(); // play audio, etc.
2015  }
2016 
2017  // Set the window size etc. once the frame size is known
2018  QTimer::singleShot(0, this, SLOT(frameDrawn()));
2019 
2020  mShown = true;
2021 }
2022 
2023 /******************************************************************************
2024 * Called when the window has been moved.
2025 */
2026 void MessageWin::moveEvent(QMoveEvent* e)
2027 {
2028  MainWindowBase::moveEvent(e);
2029  theApp()->setSpreadWindowsState(isSpread(KAlarm::desktopWorkArea(mScreenNumber).topLeft()));
2030  if (mPositioning)
2031  {
2032  // The window has just been initially positioned
2033  mPositioning = false;
2034  displayComplete(); // play audio, etc.
2035  }
2036 }
2037 
2038 /******************************************************************************
2039 * Called after (hopefully) the window frame size is known.
2040 * Reset the initial window size if it exceeds the working area of the desktop.
2041 * Set the 'spread windows' menu item status.
2042 */
2043 void MessageWin::frameDrawn()
2044 {
2045  if (!mErrorWindow && mAction == KAEvent::MESSAGE)
2046  {
2047  const QSize s = sizeHint();
2048  if (width() > s.width() || height() > s.height())
2049  resize(s);
2050  }
2051  theApp()->setSpreadWindowsState(isSpread(KAlarm::desktopWorkArea(mScreenNumber).topLeft()));
2052 }
2053 
2054 /******************************************************************************
2055 * Called when the window has been displayed properly (in its correct position),
2056 * to play sounds and reschedule the event.
2057 */
2058 void MessageWin::displayComplete()
2059 {
2060  playAudio();
2061  if (mRescheduleEvent)
2062  alarmShowing(mEvent);
2063 
2064  if (!mAlwaysHide)
2065  {
2066  // Enable the window's buttons either now or after the configured delay
2067  if (mButtonDelay > 0)
2068  QTimer::singleShot(mButtonDelay, this, SLOT(enableButtons()));
2069  else
2070  enableButtons();
2071  }
2072 }
2073 
2074 /******************************************************************************
2075 * Enable the window's buttons.
2076 */
2077 void MessageWin::enableButtons()
2078 {
2079  mOkButton->setEnabled(true);
2080  mKAlarmButton->setEnabled(true);
2081  if (mDeferButton->isVisible() && !mDisableDeferral)
2082  mDeferButton->setEnabled(true);
2083  if (mEditButton)
2084  mEditButton->setEnabled(true);
2085  if (mKMailButton)
2086  mKMailButton->setEnabled(true);
2087 }
2088 
2089 /******************************************************************************
2090 * Called when the window's size has changed (before it is painted).
2091 */
2092 void MessageWin::resizeEvent(QResizeEvent* re)
2093 {
2094  if (mRestoreHeight)
2095  {
2096  // Restore the window height on session restoration
2097  if (mRestoreHeight != re->size().height())
2098  {
2099  QSize size = re->size();
2100  size.setHeight(mRestoreHeight);
2101  resize(size);
2102  }
2103  else if (isVisible())
2104  mRestoreHeight = 0;
2105  }
2106  else
2107  {
2108  if (mShown && mAction == KAEvent::FILE && !mErrorMsgs.count())
2109  KAlarm::writeConfigWindowSize("FileMessage", re->size());
2110  MainWindowBase::resizeEvent(re);
2111  }
2112 }
2113 
2114 /******************************************************************************
2115 * Called when a close event is received.
2116 * Only quits the application if there is no system tray icon displayed.
2117 */
2118 void MessageWin::closeEvent(QCloseEvent* ce)
2119 {
2120  // Don't prompt or delete the alarm from the display calendar if the session is closing
2121  if (!mErrorWindow && !theApp()->sessionClosingDown())
2122  {
2123  if (mConfirmAck && !mNoCloseConfirm)
2124  {
2125  // Ask for confirmation of acknowledgement. Use warningYesNo() because its default is No.
2126  if (KAMessageBox::warningYesNo(this, i18nc("@info", "Do you really want to acknowledge this alarm?"),
2127  i18nc("@action:button", "Acknowledge Alarm"), KGuiItem(i18nc("@action:button", "Acknowledge")), KStandardGuiItem::cancel())
2128  != KMessageBox::Yes)
2129  {
2130  ce->ignore();
2131  return;
2132  }
2133  }
2134  if (!mEventId.isEmpty())
2135  {
2136  // Delete from the display calendar
2137 #ifdef USE_AKONADI
2138  KAlarm::deleteDisplayEvent(CalEvent::uid(mEventId.eventId(), CalEvent::DISPLAYING));
2139 #else
2140  KAlarm::deleteDisplayEvent(CalEvent::uid(mEventId, CalEvent::DISPLAYING));
2141 #endif
2142  }
2143  }
2144  MainWindowBase::closeEvent(ce);
2145 }
2146 
2147 /******************************************************************************
2148 * Called when the OK button is clicked.
2149 */
2150 void MessageWin::slotOk()
2151 {
2152  if (mDontShowAgainCheck && mDontShowAgainCheck->isChecked())
2153  KAlarm::setDontShowErrors(mEventId, mDontShowAgain);
2154  close();
2155 }
2156 
2157 #ifdef KMAIL_SUPPORTED
2158 /******************************************************************************
2159 * Called when the KMail button is clicked.
2160 * Tells KMail to display the email message displayed in this message window.
2161 */
2162 void MessageWin::slotShowKMailMessage()
2163 {
2164  kDebug();
2165  if (!mKMailSerialNumber)
2166  return;
2167  const QString err = KAlarm::runKMail(false);
2168  if (!err.isNull())
2169  {
2170  KAMessageBox::sorry(this, err);
2171  return;
2172  }
2173  org::kde::kmail::kmail kmail(KMAIL_DBUS_SERVICE, KMAIL_DBUS_PATH, QDBusConnection::sessionBus());
2174  QDBusReply<bool> reply = kmail.showMail((qint64)mKMailSerialNumber);
2175  if (!reply.isValid())
2176  kError() << "kmail D-Bus call failed:" << reply.error().message();
2177  else if (!reply.value())
2178  KAMessageBox::sorry(this, i18nc("@info", "Unable to locate this email in <application>KMail</application>"));
2179 }
2180 #endif
2181 
2182 /******************************************************************************
2183 * Called when the Edit... button is clicked.
2184 * Displays the alarm edit dialog.
2185 *
2186 * NOTE: The alarm edit dialog is made a child of the main window, not this
2187 * window, so that if this window closes before the dialog (e.g. on
2188 * auto-close), KAlarm doesn't crash. The dialog is set non-modal so that
2189 * the main window is unaffected, but modal mode is simulated so that
2190 * this window is inactive while the dialog is open.
2191 */
2192 void MessageWin::slotEdit()
2193 {
2194  kDebug();
2195  MainWindow* mainWin = MainWindow::mainMainWindow();
2196  mEditDlg = EditAlarmDlg::create(false, &mOriginalEvent, false, mainWin, EditAlarmDlg::RES_IGNORE);
2197  KWindowSystem::setMainWindow(mEditDlg, winId());
2198  KWindowSystem::setOnAllDesktops(mEditDlg->winId(), false);
2199  setButtonsReadOnly(true);
2200  connect(mEditDlg, SIGNAL(accepted()), SLOT(editCloseOk()));
2201  connect(mEditDlg, SIGNAL(rejected()), SLOT(editCloseCancel()));
2202  connect(mEditDlg, SIGNAL(destroyed(QObject*)), SLOT(editCloseCancel()));
2203  connect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)), SLOT(activeWindowChanged(WId)));
2204 #ifdef USE_AKONADI
2205  mainWin->editAlarm(mEditDlg, mOriginalEvent);
2206 #else
2207  mainWin->editAlarm(mEditDlg, mOriginalEvent, mResource);
2208 #endif
2209 }
2210 
2211 /******************************************************************************
2212 * Called when OK is clicked in the alarm edit dialog invoked by the Edit button.
2213 * Closes the window.
2214 */
2215 void MessageWin::editCloseOk()
2216 {
2217  mEditDlg = 0;
2218  mNoCloseConfirm = true; // allow window to close without confirmation prompt
2219  close();
2220 }
2221 
2222 /******************************************************************************
2223 * Called when Cancel is clicked in the alarm edit dialog invoked by the Edit
2224 * button, or when the dialog is deleted.
2225 */
2226 void MessageWin::editCloseCancel()
2227 {
2228  mEditDlg = 0;
2229  setButtonsReadOnly(false);
2230 }
2231 
2232 /******************************************************************************
2233 * Called when the active window has changed. If this window has become the
2234 * active window and there is an alarm edit dialog, simulate a modal dialog by
2235 * making the alarm edit dialog the active window instead.
2236 */
2237 void MessageWin::activeWindowChanged(WId win)
2238 {
2239  if (mEditDlg && win == winId())
2240  KWindowSystem::activateWindow(mEditDlg->winId());
2241 }
2242 
2243 /******************************************************************************
2244 * Set or clear the read-only state of the dialog buttons.
2245 */
2246 void MessageWin::setButtonsReadOnly(bool ro)
2247 {
2248  mOkButton->setReadOnly(ro, true);
2249  mDeferButton->setReadOnly(ro, true);
2250  mEditButton->setReadOnly(ro, true);
2251  if (mSilenceButton)
2252  mSilenceButton->setReadOnly(ro, true);
2253  if (mKMailButton)
2254  mKMailButton->setReadOnly(ro, true);
2255  mKAlarmButton->setReadOnly(ro, true);
2256 }
2257 
2258 /******************************************************************************
2259 * Set up to disable the defer button when the deferral limit is reached.
2260 */
2261 void MessageWin::setDeferralLimit(const KAEvent& event)
2262 {
2263  mDeferLimit = event.deferralLimit().effectiveKDateTime().toUtc().dateTime();
2264  MidnightTimer::connect(this, SLOT(checkDeferralLimit())); // check every day
2265  mDisableDeferral = false;
2266  checkDeferralLimit();
2267 }
2268 
2269 /******************************************************************************
2270 * Check whether the deferral limit has been reached.
2271 * If so, disable the Defer button.
2272 * N.B. Ideally, just a single QTimer::singleShot() call would be made to disable
2273 * the defer button at the corret time. But for a 32-bit integer, the
2274 * milliseconds parameter overflows in about 25 days, so instead a daily
2275 * check is done until the day when the deferral limit is reached, followed
2276 * by a non-overflowing QTimer::singleShot() call.
2277 */
2278 void MessageWin::checkDeferralLimit()
2279 {
2280  if (!mDeferButton->isEnabled() || !mDeferLimit.isValid())
2281  return;
2282  int n = KDateTime::currentLocalDate().daysTo(KDateTime(mDeferLimit, KDateTime::LocalZone).date());
2283  if (n > 0)
2284  return;
2285  MidnightTimer::disconnect(this, SLOT(checkDeferralLimit()));
2286  if (n == 0)
2287  {
2288  // The deferral limit will be reached today
2289  n = KDateTime::currentUtcDateTime().dateTime().secsTo(mDeferLimit);
2290  if (n > 0)
2291  {
2292  QTimer::singleShot(n * 1000, this, SLOT(checkDeferralLimit()));
2293  return;
2294  }
2295  }
2296  mDeferButton->setEnabled(false);
2297  mDisableDeferral = true;
2298 }
2299 
2300 /******************************************************************************
2301 * Called when the Defer... button is clicked.
2302 * Displays the defer message dialog.
2303 */
2304 void MessageWin::slotDefer()
2305 {
2306  mDeferDlg = new DeferAlarmDlg(KDateTime::currentDateTime(Preferences::timeZone()).addSecs(60), mDateTime.isDateOnly(), false, this);
2307  mDeferDlg->setObjectName(QLatin1String("DeferDlg")); // used by LikeBack
2308  mDeferDlg->setDeferMinutes(mDefaultDeferMinutes > 0 ? mDefaultDeferMinutes : Preferences::defaultDeferTime());
2309  mDeferDlg->setLimit(mEvent);
2310  if (!Preferences::modalMessages())
2311  lower();
2312  if (mDeferDlg->exec() == QDialog::Accepted)
2313  {
2314  const DateTime dateTime = mDeferDlg->getDateTime();
2315  const int delayMins = mDeferDlg->deferMinutes();
2316  // Fetch the up-to-date alarm from the calendar. Note that it could have
2317  // changed since it was displayed.
2318  const KAEvent* event = mEventId.isEmpty() ? 0 : AlarmCalendar::resources()->event(mEventId);
2319  if (event)
2320  {
2321  // The event still exists in the active calendar
2322  kDebug() << "Deferring event" << mEventId;
2323  KAEvent newev(*event);
2324  newev.defer(dateTime, (mAlarmType & KAAlarm::REMINDER_ALARM), true);
2325  newev.setDeferDefaultMinutes(delayMins);
2326  KAlarm::updateEvent(newev, mDeferDlg, true);
2327  if (newev.deferred())
2328  mNoPostAction = true;
2329  }
2330  else
2331  {
2332  // Try to retrieve the event from the displaying or archive calendars
2333 #ifdef USE_AKONADI
2334  Akonadi::Collection collection;
2335 #else
2336  AlarmResource* resource = 0;
2337 #endif
2338  KAEvent event;
2339  bool showEdit, showDefer;
2340 #ifdef USE_AKONADI
2341  if (!retrieveEvent(event, collection, showEdit, showDefer))
2342 #else
2343  if (!retrieveEvent(event, resource, showEdit, showDefer))
2344 #endif
2345  {
2346  // The event doesn't exist any more !?!, so recurrence data,
2347  // flags, and more, have been lost.
2348  KAMessageBox::error(this, i18nc("@info", "<para>Cannot defer alarm:</para><para>Alarm not found.</para>"));
2349  raise();
2350  delete mDeferDlg;
2351  mDeferDlg = 0;
2352  mDeferButton->setEnabled(false);
2353  mEditButton->setEnabled(false);
2354  return;
2355  }
2356  kDebug() << "Deferring retrieved event" << mEventId;
2357  event.defer(dateTime, (mAlarmType & KAAlarm::REMINDER_ALARM), true);
2358  event.setDeferDefaultMinutes(delayMins);
2359  event.setCommandError(mCommandError);
2360  // Add the event back into the calendar file, retaining its ID
2361  // and not updating KOrganizer.
2362 #ifdef USE_AKONADI
2363  KAlarm::addEvent(event, &collection, mDeferDlg, KAlarm::USE_EVENT_ID);
2364 #else
2365  KAlarm::addEvent(event, resource, mDeferDlg, KAlarm::USE_EVENT_ID);
2366 #endif
2367  if (event.deferred())
2368  mNoPostAction = true;
2369  // Finally delete it from the archived calendar now that it has
2370  // been reactivated.
2371  event.setCategory(CalEvent::ARCHIVED);
2372  KAlarm::deleteEvent(event, false);
2373  }
2374  if (theApp()->wantShowInSystemTray())
2375  {
2376  // Alarms are to be displayed only if the system tray icon is running,
2377  // so start it if necessary so that the deferred alarm will be shown.
2378  theApp()->displayTrayIcon(true);
2379  }
2380  mNoCloseConfirm = true; // allow window to close without confirmation prompt
2381  close();
2382  }
2383  else
2384  raise();
2385  delete mDeferDlg;
2386  mDeferDlg = 0;
2387 }
2388 
2389 /******************************************************************************
2390 * Called when the KAlarm icon button in the message window is clicked.
2391 * Displays the main window, with the appropriate alarm selected.
2392 */
2393 void MessageWin::displayMainWindow()
2394 {
2395 #ifdef USE_AKONADI
2396  KAlarm::displayMainWindowSelected(mEventItemId);
2397 #else
2398  KAlarm::displayMainWindowSelected(mEventId);
2399 #endif
2400 }
2401 
2402 /******************************************************************************
2403 * Check whether the specified error message is already displayed for this
2404 * alarm, and note that it will now be displayed.
2405 * Reply = true if message is already displayed.
2406 */
2407 bool MessageWin::haveErrorMessage(unsigned msg) const
2408 {
2409  if (!mErrorMessages.contains(mEventId))
2410  mErrorMessages.insert(mEventId, 0);
2411  const bool result = (mErrorMessages[mEventId] & msg);
2412  mErrorMessages[mEventId] |= msg;
2413  return result;
2414 }
2415 
2416 void MessageWin::clearErrorMessage(unsigned msg) const
2417 {
2418  if (mErrorMessages.contains(mEventId))
2419  {
2420  if (mErrorMessages[mEventId] == msg)
2421  mErrorMessages.remove(mEventId);
2422  else
2423  mErrorMessages[mEventId] &= ~msg;
2424  }
2425 }
2426 
2427 
2428 /******************************************************************************
2429 * Check whether the message window should be modal, i.e. with title bar etc.
2430 * Normally this follows the Preferences setting, but if there is a full screen
2431 * window displayed, on X11 the message window has to bypass the window manager
2432 * in order to display on top of it (which has the side effect that it will have
2433 * no window decoration).
2434 *
2435 * Also find the usable area of the desktop (excluding panel etc.), on the
2436 * appropriate screen if there are multiple screens.
2437 */
2438 bool MessageWin::getWorkAreaAndModal()
2439 {
2440  mScreenNumber = -1;
2441  const bool modal = Preferences::modalMessages();
2442 #ifdef Q_WS_X11
2443  const QDesktopWidget* desktop = qApp->desktop();
2444  const int numScreens = desktop->numScreens();
2445  if (numScreens > 1)
2446  {
2447  // There are multiple screens.
2448  // Check for any full screen windows, even if they are not the active
2449  // window, and try not to show the alarm message their screens.
2450  mScreenNumber = desktop->screenNumber(MainWindow::mainMainWindow()); // default = KAlarm's screen
2451  if (desktop->isVirtualDesktop())
2452  {
2453  // The screens form a single virtual desktop.
2454  // Xinerama, for example, uses this scheme.
2455  QVector<FullScreenType> screenTypes(numScreens);
2456  QVector<QRect> screenRects(numScreens);
2457  for (int s = 0; s < numScreens; ++s)
2458  screenRects[s] = desktop->screenGeometry(s);
2459  const FullScreenType full = findFullScreenWindows(screenRects, screenTypes);
2460  if (full == NoFullScreen || screenTypes[mScreenNumber] == NoFullScreen)
2461  return modal;
2462  for (int s = 0; s < numScreens; ++s)
2463  {
2464  if (screenTypes[s] == NoFullScreen)
2465 
2466  {
2467  // There is no full screen window on this screen
2468  mScreenNumber = s;
2469  return modal;
2470  }
2471  }
2472  // All screens contain a full screen window: use one without
2473  // an active full screen window.
2474  for (int s = 0; s < numScreens; ++s)
2475  {
2476  if (screenTypes[s] == FullScreen)
2477  {
2478  mScreenNumber = s;
2479  return modal;
2480  }
2481  }
2482  }
2483  else
2484  {
2485  // The screens are completely separate from each other.
2486  int inactiveScreen = -1;
2487  FullScreenType full = haveFullScreenWindow(mScreenNumber);
2488 kDebug()<<"full="<<full<<", screen="<<mScreenNumber;
2489  if (full == NoFullScreen)
2490  return modal; // KAlarm's screen doesn't contain a full screen window
2491  if (full == FullScreen)
2492  inactiveScreen = mScreenNumber;
2493  for (int s = 0; s < numScreens; ++s)
2494  {
2495  if (s != mScreenNumber)
2496  {
2497  full = haveFullScreenWindow(s);
2498  if (full == NoFullScreen)
2499  {
2500  // There is no full screen window on this screen
2501  mScreenNumber = s;
2502  return modal;
2503  }
2504  if (full == FullScreen && inactiveScreen < 0)
2505  inactiveScreen = s;
2506  }
2507  }
2508  if (inactiveScreen >= 0)
2509  {
2510  // All screens contain a full screen window: use one without
2511  // an active full screen window.
2512  mScreenNumber = inactiveScreen;
2513  return modal;
2514  }
2515  }
2516  return false; // can't logically get here, since there can only be one active window...
2517  }
2518 #endif
2519  if (modal)
2520  {
2521  const WId activeId = KWindowSystem::activeWindow();
2522  const KWindowInfo wi = KWindowSystem::windowInfo(activeId, NET::WMState);
2523  if (wi.valid() && wi.hasState(NET::FullScreen))
2524  return false; // the active window is full screen.
2525  }
2526  return modal;
2527 }
2528 
2529 #ifdef Q_WS_X11
2530 /******************************************************************************
2531 * In a multi-screen setup (not a single virtual desktop), find whether the
2532 * specified screen has a full screen window on it.
2533 */
2534 FullScreenType haveFullScreenWindow(int screen)
2535 {
2536  FullScreenType type = NoFullScreen;
2537  Display* display = QX11Info::display();
2538  const NETRootInfo rootInfo(display, NET::ClientList | NET::ActiveWindow, screen);
2539  const Window rootWindow = rootInfo.rootWindow();
2540  const Window activeWindow = rootInfo.activeWindow();
2541  const Window* windows = rootInfo.clientList();
2542  const int windowCount = rootInfo.clientListCount();
2543 kDebug()<<"Screen"<<screen<<": Window count="<<windowCount<<", active="<<activeWindow<<", geom="<<qApp->desktop()->screenGeometry(screen);
2544 NETRect geom;
2545 NETRect frame;
2546  for (int w = 0; w < windowCount; ++w)
2547  {
2548  NETWinInfo winInfo(display, windows[w], rootWindow, NET::WMState|NET::WMGeometry);
2549 winInfo.kdeGeometry(frame, geom);
2550 const QRect fr(frame.pos.x, frame.pos.y, frame.size.width, frame.size.height);
2551 const QRect gm(geom.pos.x, geom.pos.y, geom.size.width, geom.size.height);
2552  if (winInfo.state() & NET::FullScreen)
2553  {
2554 kDebug()<<"Found FULL SCREEN: "<<windows[w]<<", geom="<<gm<<", frame="<<fr;
2555  type = FullScreen;
2556  if (windows[w] == activeWindow)
2557  return FullScreenActive;
2558  }
2559 //else { kDebug()<<"Found normal: "<<windows[w]<<", geom="<<gm<<", frame="<<fr; }
2560  }
2561  return type;
2562 }
2563 
2564 /******************************************************************************
2565 * In a multi-screen setup (single virtual desktop, e.g. Xinerama), find which
2566 * screens have full screen windows on them.
2567 */
2568 FullScreenType findFullScreenWindows(const QVector<QRect>& screenRects, QVector<FullScreenType>& screenTypes)
2569 {
2570  FullScreenType result = NoFullScreen;
2571  screenTypes.fill(NoFullScreen);
2572  Display* display = QX11Info::display();
2573  const NETRootInfo rootInfo(display, NET::ClientList | NET::ActiveWindow, 0);
2574  const Window rootWindow = rootInfo.rootWindow();
2575  const Window activeWindow = rootInfo.activeWindow();
2576  const Window* windows = rootInfo.clientList();
2577  const int windowCount = rootInfo.clientListCount();
2578 kDebug()<<"Virtual desktops: Window count="<<windowCount<<", active="<<activeWindow<<", geom="<<qApp->desktop()->screenGeometry(0);
2579  NETRect netgeom;
2580  NETRect netframe;
2581  for (int w = 0; w < windowCount; ++w)
2582  {
2583  NETWinInfo winInfo(display, windows[w], rootWindow, NET::WMState | NET::WMGeometry);
2584  if (winInfo.state() & NET::FullScreen)
2585  {
2586  // Found a full screen window - find which screen it's on
2587  const bool active = (windows[w] == activeWindow);
2588  winInfo.kdeGeometry(netframe, netgeom);
2589  const QRect winRect(netgeom.pos.x, netgeom.pos.y, netgeom.size.width, netgeom.size.height);
2590 kDebug()<<"Found FULL SCREEN: "<<windows[w]<<", geom="<<winRect;
2591  for (int s = 0, count = screenRects.count(); s < count; ++s)
2592  {
2593  if (screenRects[s].contains(winRect))
2594  {
2595 kDebug()<<"FULL SCREEN on screen"<<s<<", active="<<active;
2596  if (active)
2597  screenTypes[s] = result = FullScreenActive;
2598  else
2599  {
2600  if (screenTypes[s] == NoFullScreen)
2601  screenTypes[s] = FullScreen;
2602  if (result == NoFullScreen)
2603  result = FullScreen;
2604  }
2605  break;
2606  }
2607  }
2608  }
2609  }
2610  return result;
2611 }
2612 #endif
2613 
2614 #include "moc_messagewin_p.cpp"
2615 #include "moc_messagewin.cpp"
2616 
2617 // vim: et sw=4:
QSize::boundedTo
QSize boundedTo(const QSize &otherSize) const
QResizeEvent
QWidget
synchtimer.h
MessageWin::ALWAYS_HIDE
Definition: messagewin.h:68
MessageWin::resizeEvent
virtual void resizeEvent(QResizeEvent *)
Definition: messagewin.cpp:2092
KAlarmApp::rescheduleAlarm
void rescheduleAlarm(KAEvent &e, const KAAlarm &a)
Definition: kalarmapp.h:84
CollectionControlModel::getStandard
static Akonadi::Collection getStandard(CalEvent::Type, bool useDefault=false)
Return the standard collection for a specified mime type.
Definition: collectionmodel.cpp:993
QSize::setHeight
void setHeight(int height)
QRect::size
QSize size() const
MessageWin::moveEvent
virtual void moveEvent(QMoveEvent *)
Definition: messagewin.cpp:2026
QDesktopWidget::screenGeometry
const QRect screenGeometry(int screen) const
QWidget::setPalette
void setPalette(const QPalette &)
AudioThread::readyToPlay
void readyToPlay()
AlarmCalendar::deleteEvent
bool deleteEvent(const QString &eventID, bool save=false)
Definition: alarmcalendar.cpp:1653
QSize::width
int width() const
QPointer::data
T * data() const
QGridLayout::sizeHint
virtual QSize sizeHint() const
MessageWin::~MessageWin
~MessageWin()
Definition: messagewin.cpp:416
ErrMsg_AudioFile
Definition: messagewin.cpp:159
Phonon::MediaSource::type
Type type() const
QMap::contains
bool contains(const Key &key) const
QMoveEvent
Phonon::MediaObject::errorString
QString errorString() const
ErrMsg_Speak
Definition: messagewin.cpp:158
QPalette::setColor
void setColor(ColorGroup group, ColorRole role, const QColor &color)
CollectionControlModel::isWritableEnabled
static int isWritableEnabled(const Akonadi::Collection &, CalEvent::Type)
Return whether a collection is both enabled and fully writable for a given alarm type, i.e.
Definition: collectionmodel.cpp:968
QGridLayout::addWidget
void addWidget(QWidget *widget, int row, int column, QFlags< Qt::AlignmentFlag > alignment)
QRect::right
int right() const
QByteArray
MessageWin::isAudioPlaying
static bool isAudioPlaying()
Definition: messagewin.cpp:1576
AudioThread::AudioThread
AudioThread(MessageWin *parent, const QString &audioFile, float volume, float fadeVolume, int fadeSeconds, int repeatPause)
Definition: messagewin.cpp:1633
MidnightTimer::connect
static void connect(QObject *receiver, const char *member)
AlarmCalendar::eventReadOnly
bool eventReadOnly(const QString &uniqueID) const
Definition: alarmcalendar.cpp:2090
QDBusReply
text
virtual QByteArray text(quint32 serialNumber) const =0
QApplication::beep
void beep()
MessageWin::spread
static bool spread(bool scatter)
Definition: messagewin.cpp:1400
QDesktopWidget::numScreens
int numScreens() const
Phonon::MediaObject::play
void play()
deferdlg.h
date
time_t date() const
QX11Info::display
Display * display()
AlarmCalendar::kcalEvent
KCal::Event * kcalEvent(const QString &uniqueId)
Definition: alarmcalendar.cpp:1858
QFont
MainWindowBase
The MainWindowBase class is a base class for KAlarm's main window and message window.
Definition: mainwindowbase.h:36
QVector::fill
QVector< T > & fill(const T &value, int size)
QLabel::setPixmap
void setPixmap(const QPixmap &)
AlarmCalendar::resources
static AlarmCalendar * resources()
Definition: alarmcalendar.h:130
QPalette::color
const QColor & color(ColorGroup group, ColorRole role) const
QMap
QCheckBox::sizeHint
virtual QSize sizeHint() const
QPointer< AudioThread >
KAMessageBox::error
static void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Options(Notify|WindowModal))
PushButton::setReadOnly
virtual void setReadOnly(bool readOnly, bool noHighlight=false)
QByteArray::isEmpty
bool isEmpty() const
AudioThread::~AudioThread
~AudioThread()
Definition: messagewin.cpp:1651
QRect::translate
void translate(int dx, int dy)
QDBusError::message
QString message() const
QHBoxLayout
QLabel::setAlignment
void setAlignment(QFlags< Qt::AlignmentFlag >)
editdlg.h
AlarmCalendar::addEvent
bool addEvent(KAEvent *, QWidget *promptParent=0, bool useEventID=false, AlarmResource *=0, bool noPrompt=false, bool *cancelled=0)
Definition: alarmcalendar.cpp:1258
QDBusReply::isValid
bool isValid() const
MessageWin::NO_DEFER
Definition: messagewin.h:67
QRect::height
int height() const
QDBusConnection::sessionBus
QDBusConnection sessionBus()
alarmcalendar.h
QGridLayout
QMutex::unlock
void unlock()
WidgetFlags
static const Qt::WidgetAttribute WidgetFlags
Definition: messagewin.cpp:154
QPoint
DeferAlarmDlg::setDeferMinutes
void setDeferMinutes(int mins)
Definition: deferdlg.cpp:125
QByteArray::length
int length() const
MessageWin::closeEvent
virtual void closeEvent(QCloseEvent *)
Definition: messagewin.cpp:2118
AlarmCalendar::resourceForEvent
AlarmResource * resourceForEvent(const QString &eventID) const
Definition: alarmcalendar.cpp:2117
QDesktopWidget::isVirtualDesktop
bool isVirtualDesktop() const
QFrame::setFrameStyle
void setFrameStyle(int style)
QBoxLayout::addSpacing
void addSpacing(int size)
DeferAlarmDlg
Definition: deferdlg.h:38
QUrl::fromUserInput
QUrl fromUserInput(const QString &userInput)
QUrl::toString
QString toString(QFlags< QUrl::FormattingOption > options) const
QDesktopWidget::screenNumber
int screenNumber(const QWidget *widget) const
AudioThread::run
virtual void run()
Definition: messagewin.cpp:1684
QFile
AkonadiModel::collectionById
Akonadi::Collection collectionById(Akonadi::Collection::Id) const
Definition: akonadimodel.cpp:1843
MessageWin::findEvent
static MessageWin * findEvent(const QString &eventId, MessageWin *exclude=0)
Definition: messagewin.cpp:1476
QPoint::x
int x() const
QPoint::y
int y() const
QTextStream
AlarmCalendar::isOpen
bool isOpen()
Definition: alarmcalendar.cpp:235
QGridLayout::setSpacing
void setSpacing(int spacing)
QString::isNull
bool isNull() const
PushButton
Phonon::AudioOutput::volume
volume
QCloseEvent
kalarmapp.h
the KAlarm application object
pushbutton.h
QRegExp
QDateTime::setTimeSpec
void setTimeSpec(Qt::TimeSpec spec)
QRect
MainWindow::editAlarm
void editAlarm(EditAlarmDlg *, const KAEvent &, AlarmResource *)
Definition: mainwindow.cpp:1723
KAlarmApp::quitIf
bool quitIf()
Definition: kalarmapp.h:69
QBoxLayout::addWidget
void addWidget(QWidget *widget, int stretch, QFlags< Qt::AlignmentFlag > alignment)
MessageWin
MessageWin: A window to display an alarm or error message.
Definition: messagewin.h:61
QList::count
int count(const T &value) const
QString::fromLocal8Bit
QString fromLocal8Bit(const char *str, int size)
Phonon::Path::removeEffect
bool removeEffect(Effect *effect)
DeferAlarmDlg::deferMinutes
int deferMinutes() const
Definition: deferdlg.h:47
QList::append
void append(const T &value)
QEvent::ignore
void ignore()
QString::insert
QString & insert(int position, QChar ch)
QThread::isFinished
bool isFinished() const
QDesktopWidget
WFLAGS2
static const Qt::WindowFlags WFLAGS2
Definition: messagewin.cpp:153
QRect::top
int top() const
MessageWin::saveProperties
virtual void saveProperties(KConfigGroup &)
Definition: messagewin.cpp:995
autoqpointer.h
KAlarmApp::execCommandAlarm
ShellProcess * execCommandAlarm(const KAEvent &, const KAAlarm &, const QObject *receiver=0, const char *slot=0)
Definition: kalarmapp.cpp:2071
QShowEvent
Preferences::timeZone
static KTimeZone timeZone(bool reload=false)
Definition: preferences.cpp:199
KAlarmApp::notifyAudioPlaying
void notifyAudioPlaying(bool playing)
Definition: kalarmapp.cpp:2495
QObject
Phonon::MediaObject
QThread::terminate
void terminate()
QPointer::isNull
bool isNull() const
QDBusReply::value
Type value() const
QCheckBox
QRect::left
int left() const
desktop.h
QWidget::backgroundRole
QPalette::ColorRole backgroundRole() const
AkonadiModel::instance
static AkonadiModel * instance()
Definition: akonadimodel.cpp:83
proximityMultiple
static const int proximityMultiple
Definition: messagewin.cpp:116
QString::isEmpty
bool isEmpty() const
QList::removeAll
int removeAll(const T &value)
MessageWin::stopAudio
static void stopAudio(bool wait=false)
Definition: messagewin.cpp:1584
messagewin.h
displays an alarm message
mainwindow.h
main application window
MessageWin::showDefer
void showDefer()
Definition: messagewin.cpp:842
Phonon::MediaObject::stop
void stop()
MessageWin::hasDefer
bool hasDefer() const
Definition: messagewin.cpp:834
QVBoxLayout
Phonon::MediaSource
EditAlarmDlg::create
static EditAlarmDlg * create(bool Template, Type, QWidget *parent=0, GetResourceType=RES_PROMPT)
Definition: editdlg.cpp:104
WFLAGS
static const Qt::WindowFlags WFLAGS
Definition: messagewin.cpp:152
MinuteTimer::disconnect
static void disconnect(QObject *receiver, const char *member=0)
MinuteTimer::connect
static void connect(QObject *receiver, const char *member)
QObject::deleteLater
void deleteLater()
QLabel::setText
void setText(const QString &)
messagebox.h
QString
QList< MessageWin * >
QWidget::hide
void hide()
QColor
MessageWin::dateTime
const DateTime & dateTime()
Definition: messagewin.h:77
KAMessageBox::detailedError
static void detailedError(QWidget *parent, const QString &text, const QString &details, const QString &caption=QString(), Options options=Options(Notify|WindowModal))
MessageWin::MessageWin
MessageWin()
Definition: messagewin.cpp:383
QLayout::setMargin
void setMargin(int margin)
theApp
KAlarmApp * theApp()
Definition: kalarmapp.h:263
QStringList
QPixmap
QUrl::toLocalFile
QString toLocalFile() const
MessageWin::instanceCount
static int instanceCount(bool excludeAlwaysHidden=false)
Definition: messagewin.cpp:820
MessageWin::redisplayAlarms
static void redisplayAlarms()
Definition: messagewin.cpp:1199
AkonadiModel::findItemId
Akonadi::Item::Id findItemId(const KAEvent &)
Search for an event's item ID.
Definition: akonadimodel.cpp:1199
QFileInfo
MessageWin::sizeHint
virtual QSize sizeHint() const
Definition: messagewin.cpp:1896
Phonon::createPath
Path createPath(MediaNode *source, MediaNode *sink)
QResizeEvent::size
const QSize & size() const
QSize
QUrl
QWidget::setFixedSize
void setFixedSize(const QSize &s)
Phonon::MediaObject::setCurrentSource
void setCurrentSource(const MediaSource &source)
QMutex::lock
void lock()
preferences.h
QFrame
Phonon::Path::effects
QList< Effect * > effects() const
QDateTime::isValid
bool isValid() const
QAbstractButton::isChecked
bool isChecked() const
Phonon::Path::insertEffect
Effect * insertEffect(const EffectDescription &desc, Effect *insertBefore)
collectionmodel.h
MessageWin::showDateTime
void showDateTime(const KAEvent &, const KAAlarm &)
Definition: messagewin.cpp:881
EventId
Unique event identifier for Akonadi.
Definition: eventid.h:38
AlarmCalendar::kcalEvents
KCal::Event::List kcalEvents(CalEvent::Type s=CalEvent::EMPTY)
Definition: alarmcalendar.h:107
DeferAlarmDlg::getDateTime
const DateTime & getDateTime() const
Definition: deferdlg.h:45
QWidget::setWhatsThis
void setWhatsThis(const QString &)
messagewin_p.h
QLayout::activate
bool activate()
QRect::width
int width() const
MessageWin::repeat
void repeat(const KAAlarm &)
Definition: messagewin.cpp:1836
QCursor::pos
QPoint pos()
QVector
QSizeF
AudioThread
Definition: messagewin_p.h:33
QThread::wait
bool wait(unsigned long time)
KAMessageBox::warningYesNo
static int warningYesNo(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const QString &dontAskAgainName=QString(), Options options=Options(Notify|Dangerous|WindowModal))
QLatin1String
functions.h
miscellaneous functions
QBoxLayout::addStretch
void addStretch(int stretch)
KAMessageBox::sorry
static void sorry(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Options(Notify|WindowModal))
QGridLayout::setColumnStretch
void setColumnStretch(int column, int stretch)
QMutexLocker
QThread::currentThread
QThread * currentThread()
DeferAlarmDlg::setLimit
void setLimit(const DateTime &)
Definition: deferdlg.cpp:142
MessageWin::showError
static void showError(const KAEvent &, const DateTime &alarmDateTime, const QStringList &errmsgs, const QString &dontShowAgain=QString())
Definition: messagewin.cpp:288
kalarm.h
MessageWin::cancelReminder
void cancelReminder(const KAEvent &, const KAAlarm &)
Definition: messagewin.cpp:856
QList::ConstIterator
typedef ConstIterator
MidnightTimer::disconnect
static void disconnect(QObject *receiver, const char *member=0)
QLabel::sizeHint
virtual QSize sizeHint() const
QSize::height
int height() const
QVector::count
int count(const T &value) const
MessageWin::show
virtual void show()
Definition: messagewin.cpp:1876
QRect::bottom
int bottom() const
MessageWin::readProperties
virtual void readProperties(const KConfigGroup &)
Definition: messagewin.cpp:1057
KAlarmApp::displayTrayIcon
bool displayTrayIcon(bool show, MainWindow *=0)
Definition: kalarmapp.cpp:997
QRect::topLeft
QPoint topLeft() const
QByteArray::data
char * data()
QString::fromLatin1
QString fromLatin1(const char *str, int size)
AlarmCalendar
Provides read and write access to calendar files and resources.
Definition: alarmcalendar.h:58
Qt::WindowFlags
typedef WindowFlags
AlarmCalendar::displayCalendarOpen
static AlarmCalendar * displayCalendarOpen()
Definition: alarmcalendar.cpp:130
QMap::insert
iterator insert(const Key &key, const T &value)
proximityButtonDelay
static const int proximityButtonDelay
Definition: messagewin.cpp:115
QWidget::setAutoFillBackground
void setAutoFillBackground(bool enabled)
Phonon::AudioOutput
QWidget::show
void show()
AudioThread::stop
void stop(bool wait=false)
Definition: messagewin.cpp:1667
KAlarmApp::kspeechInterface
OrgKdeKSpeechInterface * kspeechInterface(QString &error) const
Definition: kalarmapp.cpp:2402
shellprocess.h
QList::constEnd
const_iterator constEnd() const
QThread::quit
void quit()
QList::constBegin
const_iterator constBegin() const
MessageWin::NO_INIT_VIEW
Definition: messagewin.h:69
AlarmCalendar::event
KAEvent * event(const QString &uniqueId)
Definition: alarmcalendar.cpp:1817
AlarmCalendar::save
bool save()
Definition: alarmcalendar.cpp:1209
QDateTime::addSecs
QDateTime addSecs(int s) const
EditAlarmDlg::RES_IGNORE
Definition: editdlg.h:68
QSizeF::height
qreal height() const
QThread
MainWindow::mainMainWindow
static MainWindow * mainMainWindow()
Definition: mainwindow.cpp:286
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
KAlarm::desktopWorkArea
QRect desktopWorkArea(int screen)
MessageWin::NO_RESCHEDULE
Definition: messagewin.h:66
QLabel
QObject::parent
QObject * parent() const
QDBusReply::error
const QDBusError & error()
MessageWin::showEvent
virtual void showEvent(QShowEvent *)
Definition: messagewin.cpp:1932
AkonadiModel::collectionForItem
Akonadi::Collection collectionForItem(Akonadi::Item::Id) const
Definition: akonadimodel.cpp:1876
QLabel::setMargin
void setMargin(int)
AlarmCalendar::displayCalendar
static AlarmCalendar * displayCalendar()
Definition: alarmcalendar.h:131
QThread::exec
int exec()
MainWindow
Definition: mainwindow.h:70
QSizeF::width
qreal width() const
ShellProcess
QPalette
QBoxLayout::setSpacing
void setSpacing(int spacing)
KAlarmApp::setSpreadWindowsState
void setSpreadWindowsState(bool spread)
Definition: kalarmapp.cpp:1345
AudioThread::error
QString error() const
Definition: messagewin.cpp:1826
QByteArray::endsWith
bool endsWith(const QByteArray &ba) const
QThread::exit
void exit(int returnCode)
QDateTime
QThread::finished
void finished()
KTextEdit
QBoxLayout::addLayout
void addLayout(QLayout *layout, int stretch)
KAlarmApp::alarmCompleted
void alarmCompleted(const KAEvent &)
Definition: kalarmapp.cpp:1708
AudioThread::mAudioOwner
static MessageWin * mAudioOwner
Definition: messagewin_p.h:42
Phonon::MediaObject::setTransitionTime
void setTransitionTime(qint32 msec)
QMap::remove
int remove(const Key &key)
QTimer::singleShot
singleShot
QVariant
QUrl::isLocalFile
bool isLocalFile() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:34:51 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kalarm

Skip menu "kalarm"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer
  • pimprint

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal