• 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
functions.cpp
Go to the documentation of this file.
1 /*
2  * functions.cpp - miscellaneous functions
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" //krazy:exclude=includes (kalarm.h must be first)
22 #include "functions.h"
23 #include "functions_p.h"
24 
25 #ifdef USE_AKONADI
26 #include "collectionmodel.h"
27 #include "collectionsearch.h"
28 #else
29 #include "alarmresources.h"
30 #include "eventlistmodel.h"
31 #endif
32 #include "alarmcalendar.h"
33 #include "alarmtime.h"
34 #include "autoqpointer.h"
35 #include "alarmlistview.h"
36 #include "editdlg.h"
37 #include "kalarmapp.h"
38 #include "kamail.h"
39 #include "mainwindow.h"
40 #include "messagebox.h"
41 #include "messagewin.h"
42 #include "preferences.h"
43 #include "shellprocess.h"
44 #include "templatelistview.h"
45 #include "templatemenuaction.h"
46 
47 #include <kalarmcal/identities.h>
48 #include <kalarmcal/kaevent.h>
49 
50 #ifdef USE_AKONADI
51 #include <kcalcore/event.h>
52 #include <kcalcore/icalformat.h>
53 #include <kcalcore/person.h>
54 #include <kcalcore/duration.h>
55 using namespace KCalCore;
56 #else
57 #include <kcal/event.h>
58 #include <kcal/icalformat.h>
59 #include <kcal/person.h>
60 #include <kcal/duration.h>
61 using namespace KCal;
62 #endif
63 #include <kpimidentities/identitymanager.h>
64 #include <kpimidentities/identity.h>
65 #include <kholidays/holidays.h>
66 
67 #include <kconfiggroup.h>
68 #include <kaction.h>
69 #include <ktoggleaction.h>
70 #include <kactioncollection.h>
71 #include <kdbusservicestarter.h>
72 #include <kglobal.h>
73 #include <klocale.h>
74 #include <kstandarddirs.h>
75 #include <kauth.h>
76 #include <ksystemtimezone.h>
77 #include <kstandardguiitem.h>
78 #include <kstandardshortcut.h>
79 #include <kfiledialog.h>
80 #include <kicon.h>
81 #include <kio/netaccess.h>
82 #include <kfileitem.h>
83 #include <kdebug.h>
84 #include <ktoolinvocation.h>
85 
86 #ifdef Q_WS_X11
87 #include <kwindowsystem.h>
88 #include <kxmessages.h>
89 #include <kstartupinfo.h>
90 #include <netwm.h>
91 #include <QX11Info>
92 #endif
93 
94 #include <QDir>
95 #include <QRegExp>
96 #include <QDesktopWidget>
97 #include <QtDBus/QtDBus>
98 #include <QTimer>
99 #include <qglobal.h>
100 
101 #ifdef USE_AKONADI
102 using namespace Akonadi;
103 #endif
104 
105 
106 namespace
107 {
108 bool refreshAlarmsQueued = false;
109 QDBusInterface* korgInterface = 0;
110 
111 struct UpdateStatusData
112 {
113  KAlarm::UpdateResult status; // status code and KOrganizer error message if any
114  int warnErr;
115  int warnKOrg;
116 
117  explicit UpdateStatusData(KAlarm::UpdateStatus s = KAlarm::UPDATE_OK) : status(s), warnErr(0), warnKOrg(0) {}
118  // Set an error status and increment to number of errors to warn about
119  void setError(KAlarm::UpdateStatus st, int errorCount = -1)
120  {
121  status.set(st);
122  if (errorCount < 0)
123  ++warnErr;
124  else
125  warnErr = errorCount;
126  }
127  // Update the error status with a KOrganizer related status
128  void korgUpdate(KAlarm::UpdateResult result)
129  {
130  if (result.status != KAlarm::UPDATE_OK)
131  {
132  ++warnKOrg;
133  if (result.status > status.status)
134  status = result;
135  }
136  }
137 };
138 
139 const QLatin1String KMAIL_DBUS_SERVICE("org.kde.kmail");
140 //const QLatin1String KMAIL_DBUS_IFACE("org.kde.kmail.kmail");
141 //const QLatin1String KMAIL_DBUS_WINDOW_PATH("/kmail/kmail_mainwindow_1");
142 const QLatin1String KORG_DBUS_SERVICE("org.kde.korganizer");
143 const QLatin1String KORG_DBUS_IFACE("org.kde.korganizer.Korganizer");
144 // D-Bus object path of KOrganizer's notification interface
145 #define KORG_DBUS_PATH "/Korganizer"
146 #define KORG_DBUS_LOAD_PATH "/korganizer_PimApplication"
147 //const QLatin1String KORG_DBUS_WINDOW_PATH("/korganizer/MainWindow_1");
148 #ifdef USE_AKONADI
149 const QLatin1String KORG_MIME_TYPE("application/x-vnd.akonadi.calendar.event");
150 #endif
151 const QLatin1String KORGANIZER_UID("-korg");
152 
153 const QLatin1String ALARM_OPTS_FILE("alarmopts");
154 const char* DONT_SHOW_ERRORS_GROUP = "DontShowErrors";
155 
156 void editNewTemplate(EditAlarmDlg::Type, const KAEvent* preset, QWidget* parent);
157 void displayUpdateError(QWidget* parent, KAlarm::UpdateError, const UpdateStatusData&, bool showKOrgError = true);
158 KAlarm::UpdateResult sendToKOrganizer(const KAEvent&);
159 KAlarm::UpdateResult deleteFromKOrganizer(const QString& eventID);
160 KAlarm::UpdateResult runKOrganizer();
161 QString uidKOrganizer(const QString& eventID);
162 }
163 
164 
165 namespace KAlarm
166 {
167 
168 Private* Private::mInstance = 0;
169 
170 /******************************************************************************
171 * Display a main window with the specified event selected.
172 */
173 #ifdef USE_AKONADI
174 MainWindow* displayMainWindowSelected(Akonadi::Item::Id eventId)
175 #else
176 MainWindow* displayMainWindowSelected(const QString& eventId)
177 #endif
178 {
179  MainWindow* win = MainWindow::firstWindow();
180  if (!win)
181  {
182  if (theApp()->checkCalendar()) // ensure calendar is open
183  {
184  win = MainWindow::create();
185  win->show();
186  }
187  }
188  else
189  {
190  // There is already a main window, so make it the active window
191  win->hide(); // in case it's on a different desktop
192  win->setWindowState(win->windowState() & ~Qt::WindowMinimized);
193  win->show();
194  win->raise();
195  win->activateWindow();
196  }
197 #ifdef USE_AKONADI
198  if (win && eventId >= 0)
199  win->selectEvent(eventId);
200 #else
201  if (win && !eventId.isEmpty())
202  win->selectEvent(eventId);
203 #endif
204  return win;
205 }
206 
207 /******************************************************************************
208 * Create an "Alarms Enabled/Enable Alarms" action.
209 */
210 KToggleAction* createAlarmEnableAction(QObject* parent)
211 {
212  KToggleAction* action = new KToggleAction(i18nc("@action", "Enable &Alarms"), parent);
213  action->setChecked(theApp()->alarmsEnabled());
214  QObject::connect(action, SIGNAL(toggled(bool)), theApp(), SLOT(setAlarmsEnabled(bool)));
215  // The following line ensures that all instances are kept in the same state
216  QObject::connect(theApp(), SIGNAL(alarmEnabledToggled(bool)), action, SLOT(setChecked(bool)));
217  return action;
218 }
219 
220 /******************************************************************************
221 * Create a "Stop Play" action.
222 */
223 KAction* createStopPlayAction(QObject* parent)
224 {
225  KAction* action = new KAction(KIcon(QLatin1String("media-playback-stop")), i18nc("@action", "Stop Play"), parent);
226  action->setEnabled(MessageWin::isAudioPlaying());
227  QObject::connect(action, SIGNAL(triggered(bool)), theApp(), SLOT(stopAudio()));
228  // The following line ensures that all instances are kept in the same state
229  QObject::connect(theApp(), SIGNAL(audioPlaying(bool)), action, SLOT(setEnabled(bool)));
230  return action;
231 }
232 
233 /******************************************************************************
234 * Create a "Spread Windows" action.
235 */
236 KToggleAction* createSpreadWindowsAction(QObject* parent)
237 {
238  KToggleAction* action = new KToggleAction(i18nc("@action", "Spread Windows"), parent);
239  QObject::connect(action, SIGNAL(triggered(bool)), theApp(), SLOT(spreadWindows(bool)));
240  // The following line ensures that all instances are kept in the same state
241  QObject::connect(theApp(), SIGNAL(spreadWindowsToggled(bool)), action, SLOT(setChecked(bool)));
242  return action;
243 }
244 
245 /******************************************************************************
246 * Add a new active (non-archived) alarm.
247 * Save it in the calendar file and add it to every main window instance.
248 * Parameters: msgParent = parent widget for any calendar selection prompt or
249 * error message.
250 * event - is updated with the actual event ID.
251 */
252 #ifdef USE_AKONADI
253 UpdateResult addEvent(KAEvent& event, Collection* calendar, QWidget* msgParent, int options, bool showKOrgErr)
254 #else
255 UpdateResult addEvent(KAEvent& event, AlarmResource* calendar, QWidget* msgParent, int options, bool showKOrgErr)
256 #endif
257 {
258  kDebug() << event.id();
259  bool cancelled = false;
260  UpdateStatusData status;
261  if (!theApp()->checkCalendar()) // ensure calendar is open
262  status.status = UPDATE_FAILED;
263  else
264  {
265  // Save the event details in the calendar file, and get the new event ID
266  AlarmCalendar* cal = AlarmCalendar::resources();
267 #ifdef USE_AKONADI
268  // Note that AlarmCalendar::addEvent() updates 'event'.
269  if (!cal->addEvent(event, msgParent, (options & USE_EVENT_ID), calendar, (options & NO_RESOURCE_PROMPT), &cancelled))
270 #else
271  KAEvent* newev = new KAEvent(event);
272  if (!cal->addEvent(newev, msgParent, (options & USE_EVENT_ID), calendar, (options & NO_RESOURCE_PROMPT), &cancelled))
273 #endif
274  {
275 #ifndef USE_AKONADI
276  delete newev;
277 #endif
278  status.status = UPDATE_FAILED;
279  }
280  else
281  {
282 #ifndef USE_AKONADI
283  event = *newev; // update event ID etc.
284 #endif
285  if (!cal->save())
286  status.status = SAVE_FAILED;
287  }
288  if (status.status == UPDATE_OK)
289  {
290  if ((options & ALLOW_KORG_UPDATE) && event.copyToKOrganizer())
291  {
292  UpdateResult st = sendToKOrganizer(event); // tell KOrganizer to show the event
293  status.korgUpdate(st);
294  }
295 
296 #ifndef USE_AKONADI
297  // Update the window lists
298  EventListModel::alarms()->addEvent(newev);
299 #endif
300  }
301  }
302 
303  if (status.status != UPDATE_OK && !cancelled && msgParent)
304  displayUpdateError(msgParent, ERR_ADD, status, showKOrgErr);
305  return status.status;
306 }
307 
308 /******************************************************************************
309 * Add a list of new active (non-archived) alarms.
310 * Save them in the calendar file and add them to every main window instance.
311 * The events are updated with their actual event IDs.
312 */
313 UpdateResult addEvents(QVector<KAEvent>& events, QWidget* msgParent, bool allowKOrgUpdate, bool showKOrgErr)
314 {
315  kDebug() << events.count();
316  if (events.isEmpty())
317  return UpdateResult(UPDATE_OK);
318  UpdateStatusData status;
319 #ifdef USE_AKONADI
320  Collection collection;
321 #else
322  AlarmResource* resource;
323 #endif
324  if (!theApp()->checkCalendar()) // ensure calendar is open
325  status.status = UPDATE_FAILED;
326  else
327  {
328 #ifdef USE_AKONADI
329  collection = CollectionControlModel::instance()->destination(CalEvent::ACTIVE, msgParent);
330  if (!collection.isValid())
331 #else
332  resource = AlarmResources::instance()->destination(CalEvent::ACTIVE, msgParent);
333  if (!resource)
334 #endif
335  {
336  kDebug() << "No calendar";
337  status.status = UPDATE_FAILED;
338  }
339  }
340  if (status.status == UPDATE_OK)
341  {
342  QString selectID;
343  AlarmCalendar* cal = AlarmCalendar::resources();
344  for (int i = 0, end = events.count(); i < end; ++i)
345  {
346  // Save the event details in the calendar file, and get the new event ID
347 #ifdef USE_AKONADI
348  if (!cal->addEvent(events[i], msgParent, false, &collection))
349 #else
350  KAEvent* newev = new KAEvent(events[i]);
351  if (!cal->addEvent(newev, msgParent, false, resource))
352 #endif
353  {
354 #ifndef USE_AKONADI
355  delete newev;
356 #endif
357  status.setError(UPDATE_ERROR);
358  continue;
359  }
360 #ifndef USE_AKONADI
361  events[i] = *newev; // update event ID etc.
362 #endif
363  if (allowKOrgUpdate && events[i].copyToKOrganizer())
364  {
365  UpdateResult st = sendToKOrganizer(events[i]); // tell KOrganizer to show the event
366  status.korgUpdate(st);
367  }
368 
369 #ifndef USE_AKONADI
370  // Update the window lists, but not yet which item is selected
371  EventListModel::alarms()->addEvent(newev);
372 // selectID = newev->id();
373 #endif
374  }
375  if (status.warnErr == events.count())
376  status.status = UPDATE_FAILED;
377  else if (!cal->save())
378  status.setError(SAVE_FAILED, events.count()); // everything failed
379  }
380 
381  if (status.status != UPDATE_OK && msgParent)
382  displayUpdateError(msgParent, ERR_ADD, status, showKOrgErr);
383  return status.status;
384 }
385 
386 /******************************************************************************
387 * Save the event in the archived calendar and adjust every main window instance.
388 * The event's ID is changed to an archived ID if necessary.
389 */
390 #ifdef USE_AKONADI
391 bool addArchivedEvent(KAEvent& event, Collection* collection)
392 #else
393 bool addArchivedEvent(KAEvent& event, AlarmResource* resource)
394 #endif
395 {
396  kDebug() << event.id();
397  QString oldid = event.id();
398  bool archiving = (event.category() == CalEvent::ACTIVE);
399  if (archiving && !Preferences::archivedKeepDays())
400  return false; // expired alarms aren't being kept
401  AlarmCalendar* cal = AlarmCalendar::resources();
402 #ifdef USE_AKONADI
403  KAEvent newevent(event);
404  newevent.setItemId(-1); // invalidate the Akonadi item ID since it's a new item
405  KAEvent* const newev = &newevent;
406 #else
407  KAEvent* newev = new KAEvent(event);
408 #endif
409  if (archiving)
410  {
411  newev->setCategory(CalEvent::ARCHIVED); // this changes the event ID
412  newev->setCreatedDateTime(KDateTime::currentUtcDateTime()); // time stamp to control purging
413  }
414  // Note that archived resources are automatically saved after changes are made
415 #ifdef USE_AKONADI
416  if (!cal->addEvent(newevent, 0, false, collection))
417  return false;
418 #else
419  if (!cal->addEvent(newev, 0, false, resource))
420  {
421  delete newev; // failed to add to calendar - leave event in its original state
422  return false;
423  }
424 #endif
425  event = *newev; // update event ID etc.
426 
427 #ifndef USE_AKONADI
428  // Update window lists.
429  // Note: updateEvent() is not used here since that doesn't trigger refiltering
430  // of the alarm list, resulting in the archived event still remaining visible
431  // even if archived events are supposed to be hidden.
432  if (archiving)
433  EventListModel::alarms()->removeEvent(oldid);
434  EventListModel::alarms()->addEvent(newev);
435 #endif
436  return true;
437 }
438 
439 /******************************************************************************
440 * Add a new template.
441 * Save it in the calendar file and add it to every template list view.
442 * 'event' is updated with the actual event ID.
443 * Parameters: promptParent = parent widget for any calendar selection prompt.
444 */
445 #ifdef USE_AKONADI
446 UpdateResult addTemplate(KAEvent& event, Collection* collection, QWidget* msgParent)
447 #else
448 UpdateResult addTemplate(KAEvent& event, AlarmResource* resource, QWidget* msgParent)
449 #endif
450 {
451  kDebug() << event.id();
452  UpdateStatusData status;
453 
454  // Add the template to the calendar file
455  AlarmCalendar* cal = AlarmCalendar::resources();
456 #ifdef USE_AKONADI
457  KAEvent newev(event);
458  if (!cal->addEvent(newev, msgParent, false, collection))
459  status.status = UPDATE_FAILED;
460 #else
461  KAEvent* newev = new KAEvent(event);
462  if (!cal->addEvent(newev, msgParent, false, resource))
463  {
464  delete newev;
465  status.status = UPDATE_FAILED;
466  }
467 #endif
468  else
469  {
470 #ifdef USE_AKONADI
471  event = newev; // update event ID etc.
472 #else
473  event = *newev; // update event ID etc.
474 #endif
475  if (!cal->save())
476  status.status = SAVE_FAILED;
477  else
478  {
479 #ifndef USE_AKONADI
480  // Update the window lists
481  EventListModel::templates()->addEvent(newev);
482 #endif
483  return UpdateResult(UPDATE_OK);
484  }
485  }
486 
487  if (msgParent)
488  displayUpdateError(msgParent, ERR_TEMPLATE, status);
489  return status.status;
490 }
491 
492 /******************************************************************************
493 * Modify an active (non-archived) alarm in the calendar file and in every main
494 * window instance.
495 * The new event must have a different event ID from the old one.
496 */
497 UpdateResult modifyEvent(KAEvent& oldEvent, KAEvent& newEvent, QWidget* msgParent, bool showKOrgErr)
498 {
499  kDebug() << oldEvent.id();
500 
501  UpdateStatusData status;
502  if (!newEvent.isValid())
503  {
504  deleteEvent(oldEvent, true);
505  status.status = UPDATE_FAILED;
506  }
507  else
508  {
509 #ifdef USE_AKONADI
510  EventId oldId(oldEvent);
511 #else
512  QString oldId = oldEvent.id();
513 #endif
514  if (oldEvent.copyToKOrganizer())
515  {
516  // Tell KOrganizer to delete its old event.
517  // But ignore errors, because the user could have manually
518  // deleted it since KAlarm asked KOrganizer to set it up.
519 #ifdef USE_AKONADI
520  deleteFromKOrganizer(oldId.eventId());
521 #else
522  deleteFromKOrganizer(oldId);
523 #endif
524  }
525 #ifdef USE_AKONADI
526  // Update the event in the calendar file, and get the new event ID
527  AlarmCalendar* cal = AlarmCalendar::resources();
528  if (!cal->modifyEvent(oldId, newEvent))
529  status.status = UPDATE_FAILED;
530 #else
531  // Delete from the window lists to prevent the event's invalid
532  // pointer being accessed.
533  EventListModel::alarms()->removeEvent(oldId);
534 
535  // Update the event in the calendar file, and get the new event ID
536  KAEvent* newev = new KAEvent(newEvent);
537  AlarmCalendar* cal = AlarmCalendar::resources();
538  if (!cal->modifyEvent(oldId, newev))
539  {
540  delete newev;
541  status.status = UPDATE_FAILED;
542  }
543 #endif
544  else
545  {
546 #ifndef USE_AKONADI
547  newEvent = *newev;
548 #endif
549  if (!cal->save())
550  status.status = SAVE_FAILED;
551  if (status.status == UPDATE_OK)
552  {
553  if (newEvent.copyToKOrganizer())
554  {
555  UpdateResult st = sendToKOrganizer(newEvent); // tell KOrganizer to show the new event
556  status.korgUpdate(st);
557  }
558 
559  // Remove "Don't show error messages again" for the old alarm
560  setDontShowErrors(oldId);
561 
562 #ifndef USE_AKONADI
563  // Update the window lists
564  EventListModel::alarms()->addEvent(newev);
565 #endif
566  }
567  }
568  }
569 
570  if (status.status != UPDATE_OK && msgParent)
571  displayUpdateError(msgParent, ERR_MODIFY, status, showKOrgErr);
572  return status.status;
573 }
574 
575 /******************************************************************************
576 * Update an active (non-archived) alarm from the calendar file and from every
577 * main window instance.
578 * The new event will have the same event ID as the old one.
579 * The event is not updated in KOrganizer, since this function is called when an
580 * existing alarm is rescheduled (due to recurrence or deferral).
581 */
582 UpdateResult updateEvent(KAEvent& event, QWidget* msgParent, bool archiveOnDelete)
583 {
584  kDebug() << event.id();
585 
586  if (!event.isValid())
587  deleteEvent(event, archiveOnDelete);
588  else
589  {
590  // Update the event in the calendar file.
591  AlarmCalendar* cal = AlarmCalendar::resources();
592 #ifdef USE_AKONADI
593  cal->updateEvent(event);
594 #else
595  KAEvent* newEvent = cal->updateEvent(event);
596 #endif
597  if (!cal->save())
598  {
599  if (msgParent)
600  displayUpdateError(msgParent, ERR_ADD, UpdateStatusData(SAVE_FAILED));
601  return UpdateResult(SAVE_FAILED);
602  }
603 
604 #ifndef USE_AKONADI
605  // Update the window lists
606  EventListModel::alarms()->updateEvent(newEvent);
607 #endif
608  }
609  return UpdateResult(UPDATE_OK);
610 }
611 
612 /******************************************************************************
613 * Update a template in the calendar file and in every template list view.
614 * If 'selectionView' is non-null, the selection highlight is moved to the
615 * updated event in that listView instance.
616 */
617 UpdateResult updateTemplate(KAEvent& event, QWidget* msgParent)
618 {
619  AlarmCalendar* cal = AlarmCalendar::resources();
620  KAEvent* newEvent = cal->updateEvent(event);
621  UpdateStatus status = UPDATE_OK;
622  if (!newEvent)
623  status = UPDATE_FAILED;
624  else if (!cal->save())
625  status = SAVE_FAILED;
626  if (status != UPDATE_OK)
627  {
628  if (msgParent)
629  displayUpdateError(msgParent, ERR_TEMPLATE, UpdateStatusData(SAVE_FAILED));
630  return UpdateResult(status);
631  }
632 
633 #ifndef USE_AKONADI
634  EventListModel::templates()->updateEvent(newEvent);
635 #endif
636  return UpdateResult(UPDATE_OK);
637 }
638 
639 /******************************************************************************
640 * Delete alarms from the calendar file and from every main window instance.
641 * If the events are archived, the events' IDs are changed to archived IDs if necessary.
642 */
643 UpdateResult deleteEvent(KAEvent& event, bool archive, QWidget* msgParent, bool showKOrgErr)
644 {
645 #ifdef USE_AKONADI
646  QVector<KAEvent> events(1, event);
647 #else
648  KAEvent::List events;
649  events += &event;
650 #endif
651  return deleteEvents(events, archive, msgParent, showKOrgErr);
652 }
653 
654 #ifdef USE_AKONADI
655 UpdateResult deleteEvents(QVector<KAEvent>& events, bool archive, QWidget* msgParent, bool showKOrgErr)
656 #else
657 UpdateResult deleteEvents(KAEvent::List& events, bool archive, QWidget* msgParent, bool showKOrgErr)
658 #endif
659 {
660  kDebug() << events.count();
661  if (events.isEmpty())
662  return UpdateResult(UPDATE_OK);
663  UpdateStatusData status;
664  AlarmCalendar* cal = AlarmCalendar::resources();
665  bool deleteWakeFromSuspendAlarm = false;
666  QString wakeFromSuspendId = checkRtcWakeConfig().value(0);
667  for (int i = 0, end = events.count(); i < end; ++i)
668  {
669  // Save the event details in the calendar file, and get the new event ID
670 #ifdef USE_AKONADI
671  KAEvent* event = &events[i];
672 #else
673  KAEvent* event = events[i];
674 #endif
675  QString id = event->id();
676 
677 #ifndef USE_AKONADI
678  // Update the window lists and clear stored command errors
679  EventListModel::alarms()->removeEvent(id);
680  event->setCommandError(KAEvent::CMD_NO_ERROR);
681 #endif
682 
683  // Delete the event from the calendar file
684  if (event->category() != CalEvent::ARCHIVED)
685  {
686  if (event->copyToKOrganizer())
687  {
688  // The event was shown in KOrganizer, so tell KOrganizer to
689  // delete it. But ignore errors, because the user could have
690  // manually deleted it from KOrganizer since it was set up.
691  UpdateResult st = deleteFromKOrganizer(id);
692  status.korgUpdate(st);
693  }
694  if (archive && event->toBeArchived())
695  {
696  KAEvent ev(*event);
697  addArchivedEvent(ev); // this changes the event ID to an archived ID
698  }
699  }
700 #ifdef USE_AKONADI
701  if (!cal->deleteEvent(*event, false)) // don't save calendar after deleting
702 #else
703  if (!cal->deleteEvent(id, false)) // don't save calendar after deleting
704 #endif
705  status.setError(UPDATE_ERROR);
706 
707  if (id == wakeFromSuspendId)
708  deleteWakeFromSuspendAlarm = true;
709 
710  // Remove "Don't show error messages again" for this alarm
711 #ifdef USE_AKONADI
712  setDontShowErrors(EventId(*event));
713 #else
714  setDontShowErrors(id);
715 #endif
716  }
717 
718  if (status.warnErr == events.count())
719  status.status = UPDATE_FAILED;
720  else if (!cal->save()) // save the calendars now
721  status.setError(SAVE_FAILED, events.count());
722  if (status.status != UPDATE_OK && msgParent)
723  displayUpdateError(msgParent, ERR_DELETE, status, showKOrgErr);
724 
725  // Remove any wake-from-suspend scheduled for a deleted alarm
726  if (deleteWakeFromSuspendAlarm && !wakeFromSuspendId.isEmpty())
727  cancelRtcWake(msgParent, wakeFromSuspendId);
728 
729  return status.status;
730 }
731 
732 /******************************************************************************
733 * Delete templates from the calendar file and from every template list view.
734 */
735 #ifdef USE_AKONADI
736 UpdateResult deleteTemplates(const KAEvent::List& events, QWidget* msgParent)
737 #else
738 UpdateResult deleteTemplates(const QStringList& eventIDs, QWidget* msgParent)
739 #endif
740 {
741 #ifdef USE_AKONADI
742  int count = events.count();
743 #else
744  int count = eventIDs.count();
745 #endif
746  kDebug() << count;
747  if (!count)
748  return UpdateResult(UPDATE_OK);
749  UpdateStatusData status;
750  AlarmCalendar* cal = AlarmCalendar::resources();
751  for (int i = 0, end = count; i < end; ++i)
752  {
753  // Update the window lists
754 #ifndef USE_AKONADI
755  QString id = eventIDs[i];
756  EventListModel::templates()->removeEvent(id);
757 #endif
758 
759  // Delete the template from the calendar file
760  AlarmCalendar* cal = AlarmCalendar::resources();
761 #ifdef USE_AKONADI
762  if (!cal->deleteEvent(*events[i], false)) // don't save calendar after deleting
763 #else
764  if (!cal->deleteEvent(id, false)) // don't save calendar after deleting
765 #endif
766  status.setError(UPDATE_ERROR);
767  }
768 
769  if (status.warnErr == count)
770  status.status = UPDATE_FAILED;
771  else if (!cal->save()) // save the calendars now
772  status.setError(SAVE_FAILED, count);
773  if (status.status != UPDATE_OK && msgParent)
774  displayUpdateError(msgParent, ERR_TEMPLATE, status);
775  return status.status;
776 }
777 
778 /******************************************************************************
779 * Delete an alarm from the display calendar.
780 */
781 void deleteDisplayEvent(const QString& eventID)
782 {
783  kDebug() << eventID;
784  AlarmCalendar* cal = AlarmCalendar::displayCalendarOpen();
785  if (cal)
786 #ifdef USE_AKONADI
787  cal->deleteDisplayEvent(eventID, true); // save calendar after deleting
788 #else
789  cal->deleteEvent(eventID, true); // save calendar after deleting
790 #endif
791 }
792 
793 /******************************************************************************
794 * Undelete archived alarms, and update every main window instance.
795 * The archive bit is set to ensure that they get re-archived if deleted again.
796 * 'ineligibleIDs' is filled in with the IDs of any ineligible events.
797 */
798 #ifdef USE_AKONADI
799 UpdateResult reactivateEvent(KAEvent& event, Collection* calendar, QWidget* msgParent, bool showKOrgErr)
800 #else
801 UpdateResult reactivateEvent(KAEvent& event, AlarmResource* calendar, QWidget* msgParent, bool showKOrgErr)
802 #endif
803 {
804 #ifdef USE_AKONADI
805  QVector<EventId> ids;
806  QVector<KAEvent> events(1, event);
807 #else
808  QStringList ids;
809  KAEvent::List events;
810  events += &event;
811 #endif
812  return reactivateEvents(events, ids, calendar, msgParent, showKOrgErr);
813 }
814 
815 #ifdef USE_AKONADI
816 UpdateResult reactivateEvents(QVector<KAEvent>& events, QVector<EventId>& ineligibleIDs, Collection* col, QWidget* msgParent, bool showKOrgErr)
817 #else
818 UpdateResult reactivateEvents(KAEvent::List& events, QStringList& ineligibleIDs, AlarmResource* resource, QWidget* msgParent, bool showKOrgErr)
819 #endif
820 {
821  kDebug() << events.count();
822  ineligibleIDs.clear();
823  if (events.isEmpty())
824  return UpdateResult(UPDATE_OK);
825  UpdateStatusData status;
826 #ifdef USE_AKONADI
827  Collection collection;
828  if (col)
829  collection = *col;
830  if (!collection.isValid())
831  collection = CollectionControlModel::instance()->destination(CalEvent::ACTIVE, msgParent);
832  if (!collection.isValid())
833 #else
834  if (!resource)
835  resource = AlarmResources::instance()->destination(CalEvent::ACTIVE, msgParent);
836  if (!resource)
837 #endif
838  {
839  kDebug() << "No calendar";
840  status.setError(UPDATE_FAILED, events.count());
841  }
842  else
843  {
844  QString selectID;
845  int count = 0;
846  AlarmCalendar* cal = AlarmCalendar::resources();
847  KDateTime now = KDateTime::currentUtcDateTime();
848  for (int i = 0, end = events.count(); i < end; ++i)
849  {
850  // Delete the event from the archived resource
851 #ifdef USE_AKONADI
852  KAEvent* event = &events[i];
853 #else
854  KAEvent* event = events[i];
855 #endif
856  if (event->category() != CalEvent::ARCHIVED
857  || !event->occursAfter(now, true))
858  {
859 #ifdef USE_AKONADI
860  ineligibleIDs += EventId(*event);
861 #else
862  ineligibleIDs += event->id();
863 #endif
864  continue;
865  }
866  ++count;
867 
868 #ifdef USE_AKONADI
869  KAEvent newevent(*event);
870  KAEvent* const newev = &newevent;
871 #else
872  KAEvent* newev = new KAEvent(*event);
873  QString oldid = event->id();
874 #endif
875  newev->setCategory(CalEvent::ACTIVE); // this changes the event ID
876  if (newev->recurs() || newev->repetition())
877  newev->setNextOccurrence(now); // skip any recurrences in the past
878  newev->setArchive(); // ensure that it gets re-archived if it is deleted
879 
880  // Save the event details in the calendar file.
881  // This converts the event ID.
882 #ifdef USE_AKONADI
883  if (!cal->addEvent(newevent, msgParent, true, &collection))
884 #else
885  if (!cal->addEvent(newev, msgParent, true, resource))
886 #endif
887  {
888 #ifndef USE_AKONADI
889  delete newev;
890 #endif
891  status.setError(UPDATE_ERROR);
892  continue;
893  }
894  if (newev->copyToKOrganizer())
895  {
896  UpdateResult st = sendToKOrganizer(*newev); // tell KOrganizer to show the event
897  status.korgUpdate(st);
898  }
899 
900 #ifndef USE_AKONADI
901  // Update the window lists
902  EventListModel::alarms()->updateEvent(oldid, newev);
903 // selectID = newev->id();
904 #endif
905 
906 #ifdef USE_AKONADI
907  if (cal->event(EventId(*event)) // no error if event doesn't exist in archived resource
908  && !cal->deleteEvent(*event, false)) // don't save calendar after deleting
909 #else
910  if (cal->event(oldid) // no error if event doesn't exist in archived resource
911  && !cal->deleteEvent(oldid, false)) // don't save calendar after deleting
912 #endif
913  status.setError(UPDATE_ERROR);
914 #ifdef USE_AKONADI
915  events[i] = newevent;
916 #else
917  events[i] = newev;
918 #endif
919  }
920 
921  if (status.warnErr == count)
922  status.status = UPDATE_FAILED;
923  // Save the calendars, even if all events failed, since more than one calendar was updated
924  if (!cal->save() && status.status != UPDATE_FAILED)
925  status.setError(SAVE_FAILED, count);
926  }
927  if (status.status != UPDATE_OK && msgParent)
928  displayUpdateError(msgParent, ERR_REACTIVATE, status, showKOrgErr);
929  return status.status;
930 }
931 
932 /******************************************************************************
933 * Enable or disable alarms in the calendar file and in every main window instance.
934 * The new events will have the same event IDs as the old ones.
935 */
936 #ifdef USE_AKONADI
937 UpdateResult enableEvents(QVector<KAEvent>& events, bool enable, QWidget* msgParent)
938 #else
939 UpdateResult enableEvents(KAEvent::List& events, bool enable, QWidget* msgParent)
940 #endif
941 {
942  kDebug() << events.count();
943  if (events.isEmpty())
944  return UpdateResult(UPDATE_OK);
945  UpdateStatusData status;
946  AlarmCalendar* cal = AlarmCalendar::resources();
947  bool deleteWakeFromSuspendAlarm = false;
948  QString wakeFromSuspendId = checkRtcWakeConfig().value(0);
949  for (int i = 0, end = events.count(); i < end; ++i)
950  {
951 #ifdef USE_AKONADI
952  KAEvent* event = &events[i];
953 #else
954  KAEvent* event = events[i];
955 #endif
956  if (event->category() == CalEvent::ACTIVE
957  && enable != event->enabled())
958  {
959  event->setEnabled(enable);
960 
961  if (!enable && event->id() == wakeFromSuspendId)
962  deleteWakeFromSuspendAlarm = true;
963 
964  // Update the event in the calendar file
965  KAEvent* newev = cal->updateEvent(event);
966  if (!newev)
967  kError() << "Error updating event in calendar:" << event->id();
968  else
969  {
970  cal->disabledChanged(newev);
971 
972  // If we're disabling a display alarm, close any message window
973  if (!enable && (event->actionTypes() & KAEvent::ACT_DISPLAY))
974  {
975 #ifdef USE_AKONADI
976  MessageWin* win = MessageWin::findEvent(EventId(*event));
977 #else
978  MessageWin* win = MessageWin::findEvent(event->id());
979 #endif
980  delete win;
981  }
982 
983 #ifndef USE_AKONADI
984  // Update the window lists
985  EventListModel::alarms()->updateEvent(newev);
986 #endif
987  }
988  }
989  }
990 
991  if (!cal->save())
992  status.setError(SAVE_FAILED, events.count());
993  if (status.status != UPDATE_OK && msgParent)
994  displayUpdateError(msgParent, ERR_ADD, status);
995 
996  // Remove any wake-from-suspend scheduled for a disabled alarm
997  if (deleteWakeFromSuspendAlarm && !wakeFromSuspendId.isEmpty())
998  cancelRtcWake(msgParent, wakeFromSuspendId);
999 
1000  return status.status;
1001 }
1002 
1003 /******************************************************************************
1004 * This method must only be called from the main KAlarm queue processing loop,
1005 * to prevent asynchronous calendar operations interfering with one another.
1006 *
1007 * Purge all archived events from the default archived alarm resource whose end
1008 * time is longer ago than 'purgeDays'. All events are deleted if 'purgeDays' is
1009 * zero.
1010 */
1011 void purgeArchive(int purgeDays)
1012 {
1013  if (purgeDays < 0)
1014  return;
1015  kDebug() << purgeDays;
1016  QDate cutoff = KDateTime::currentLocalDate().addDays(-purgeDays);
1017 #ifdef USE_AKONADI
1018  Collection collection = CollectionControlModel::getStandard(CalEvent::ARCHIVED);
1019  if (!collection.isValid())
1020  return;
1021  KAEvent::List events = AlarmCalendar::resources()->events(collection);
1022  for (int i = 0; i < events.count(); )
1023  {
1024  if (purgeDays && events[i]->createdDateTime().date() >= cutoff)
1025  events.remove(i);
1026  else
1027  ++i;
1028  }
1029 #else
1030  AlarmResource* resource = AlarmResources::instance()->getStandardResource(CalEvent::ARCHIVED);
1031  if (!resource)
1032  return;
1033  KAEvent::List events = AlarmCalendar::resources()->events(resource);
1034  for (int i = 0; i < events.count(); )
1035  {
1036  KAEvent* event = events[i];
1037  Incidence* kcalIncidence = resource->incidence(event->id());
1038  if (purgeDays && kcalIncidence && kcalIncidence->created().date() >= cutoff)
1039  events.remove(i);
1040  else
1041  EventListModel::alarms()->removeEvent(events[i++]); // update the window lists
1042  }
1043 #endif
1044  if (!events.isEmpty())
1045  AlarmCalendar::resources()->purgeEvents(events); // delete the events and save the calendar
1046 }
1047 
1048 #ifdef USE_AKONADI
1049 /******************************************************************************
1050 * Display an error message about an error when saving an event.
1051 * If 'model' is non-null, the AlarmListModel* which it points to is used; if
1052 * that is null, it is created.
1053 */
1054 QVector<KAEvent> getSortedActiveEvents(QObject* parent, AlarmListModel** model)
1055 {
1056  AlarmListModel* mdl = 0;
1057  if (!model)
1058  model = &mdl;
1059  if (!*model)
1060  {
1061  *model = new AlarmListModel(parent);
1062  (*model)->setEventTypeFilter(CalEvent::ACTIVE);
1063  (*model)->sort(AlarmListModel::TimeColumn);
1064  }
1065  QVector<KAEvent> result;
1066  for (int i = 0, count = (*model)->rowCount(); i < count; ++i)
1067  {
1068  KAEvent event = (*model)->event(i);
1069  if (event.enabled() && !event.expired())
1070  result += event;
1071  }
1072  return result;
1073 }
1074 #else
1075 /******************************************************************************
1076 * Display an error message about an error when saving an event.
1077 */
1078 KAEvent::List getSortedActiveEvents(const KDateTime& startTime, const KDateTime& endTime)
1079 {
1080  KAEvent::List events;
1081  if (endTime.isValid())
1082  events = AlarmCalendar::resources()->events(startTime, endTime, CalEvent::ACTIVE);
1083  else
1084  events = AlarmCalendar::resources()->events(CalEvent::ACTIVE);
1085  KAEvent::List result;
1086  for (int i = 0, count = events.count(); i < count; ++i)
1087  {
1088  KAEvent* event = events[i];
1089  if (event->enabled() && !event->expired())
1090  result += event;
1091  }
1092  return result;
1093 }
1094 #endif
1095 
1096 /******************************************************************************
1097 * Display an error message corresponding to a specified alarm update error code.
1098 */
1099 void displayKOrgUpdateError(QWidget* parent, UpdateError code, UpdateResult korgError, int nAlarms)
1100 {
1101  QString errmsg;
1102  switch (code)
1103  {
1104  case ERR_ADD:
1105  case ERR_REACTIVATE:
1106  errmsg = (nAlarms > 1) ? i18nc("@info", "Unable to show alarms in KOrganizer")
1107  : i18nc("@info", "Unable to show alarm in KOrganizer");
1108  break;
1109  case ERR_MODIFY:
1110  errmsg = i18nc("@info", "Unable to update alarm in KOrganizer");
1111  break;
1112  case ERR_DELETE:
1113  errmsg = (nAlarms > 1) ? i18nc("@info", "Unable to delete alarms from KOrganizer")
1114  : i18nc("@info", "Unable to delete alarm from KOrganizer");
1115  break;
1116  case ERR_TEMPLATE:
1117  return;
1118  }
1119  bool showDetail = !korgError.message.isEmpty();
1120  QString msg;
1121  switch (korgError.status)
1122  {
1123  case UPDATE_KORG_ERRINIT:
1124  msg = i18nc("@info", "<para>%1</para><para>(Could not start KOrganizer)</para>", errmsg);
1125  break;
1126  case UPDATE_KORG_ERRSTART:
1127  msg = i18nc("@info", "<para>%1</para><para>(KOrganizer not fully started)</para>", errmsg);
1128  break;
1129  case UPDATE_KORG_ERR:
1130  msg = i18nc("@info", "<para>%1</para><para>(Error communicating with KOrganizer)</para>", errmsg);
1131  break;
1132  default:
1133  msg = errmsg;
1134  showDetail = false;
1135  break;
1136  }
1137  if (showDetail)
1138  KAMessageBox::detailedError(parent, msg, korgError.message);
1139  else
1140  KAMessageBox::error(parent, msg);
1141 }
1142 
1143 /******************************************************************************
1144 * Execute a New Alarm dialog for the specified alarm type.
1145 */
1146 void editNewAlarm(EditAlarmDlg::Type type, QWidget* parent)
1147 {
1148  execNewAlarmDlg(EditAlarmDlg::create(false, type, parent));
1149 }
1150 
1151 /******************************************************************************
1152 * Execute a New Alarm dialog for the specified alarm type.
1153 */
1154 void editNewAlarm(KAEvent::SubAction action, QWidget* parent, const AlarmText* text)
1155 {
1156  bool setAction = false;
1157  EditAlarmDlg::Type type;
1158  switch (action)
1159  {
1160  case KAEvent::MESSAGE:
1161  case KAEvent::FILE:
1162  type = EditAlarmDlg::DISPLAY;
1163  setAction = true;
1164  break;
1165  case KAEvent::COMMAND:
1166  type = EditAlarmDlg::COMMAND;
1167  break;
1168  case KAEvent::EMAIL:
1169  type = EditAlarmDlg::EMAIL;
1170  break;
1171  case KAEvent::AUDIO:
1172  type = EditAlarmDlg::AUDIO;
1173  break;
1174  default:
1175  return;
1176  }
1177  EditAlarmDlg* editDlg = EditAlarmDlg::create(false, type, parent);
1178  if (setAction || text)
1179  editDlg->setAction(action, *text);
1180  execNewAlarmDlg(editDlg);
1181 }
1182 
1183 /******************************************************************************
1184 * Execute a New Alarm dialog, optionally either presetting it to the supplied
1185 * event, or setting the action and text.
1186 */
1187 void editNewAlarm(const KAEvent* preset, QWidget* parent)
1188 {
1189  execNewAlarmDlg(EditAlarmDlg::create(false, preset, true, parent));
1190 }
1191 
1192 /******************************************************************************
1193 * Common code for editNewAlarm() variants.
1194 */
1195 void execNewAlarmDlg(EditAlarmDlg* editDlg)
1196 {
1197  // Create a PrivateNewAlarmDlg parented by editDlg.
1198  // It will be deleted when editDlg is closed.
1199  new PrivateNewAlarmDlg(editDlg);
1200  editDlg->show();
1201  editDlg->raise();
1202  editDlg->activateWindow();
1203 }
1204 
1205 PrivateNewAlarmDlg::PrivateNewAlarmDlg(EditAlarmDlg* dlg)
1206  : QObject(dlg)
1207 {
1208  connect(dlg, SIGNAL(accepted()), SLOT(okClicked()));
1209  connect(dlg, SIGNAL(rejected()), SLOT(cancelClicked()));
1210 }
1211 
1212 /******************************************************************************
1213 * Called when the dialogue is accepted (e.g. by clicking the OK button).
1214 * Creates the event specified in the instance's dialogue.
1215 */
1216 void PrivateNewAlarmDlg::okClicked()
1217 {
1218  accept(static_cast<EditAlarmDlg*>(parent()));
1219 }
1220 
1221 /******************************************************************************
1222 * Creates the event specified in a given dialogue.
1223 */
1224 void PrivateNewAlarmDlg::accept(EditAlarmDlg* editDlg)
1225 {
1226  KAEvent event;
1227 #ifdef USE_AKONADI
1228  Collection calendar;
1229 #else
1230  AlarmResource* calendar;
1231 #endif
1232  editDlg->getEvent(event, calendar);
1233 
1234  // Add the alarm to the displayed lists and to the calendar file
1235 #ifdef USE_AKONADI
1236  UpdateResult status = addEvent(event, &calendar, editDlg);
1237 #else
1238  UpdateResult status = addEvent(event, calendar, editDlg);
1239 #endif
1240  switch (status.status)
1241  {
1242  case UPDATE_FAILED:
1243  return;
1244  case UPDATE_KORG_ERR:
1245  case UPDATE_KORG_ERRINIT:
1246  case UPDATE_KORG_ERRSTART:
1247  case UPDATE_KORG_FUNCERR:
1248  displayKOrgUpdateError(editDlg, ERR_ADD, status);
1249  break;
1250  default:
1251  break;
1252  }
1253  Undo::saveAdd(event, calendar);
1254 
1255  outputAlarmWarnings(editDlg, &event);
1256 
1257  editDlg->deleteLater();
1258 }
1259 
1260 /******************************************************************************
1261 * Called when the dialogue is rejected (e.g. by clicking the Cancel button).
1262 */
1263 void PrivateNewAlarmDlg::cancelClicked()
1264 {
1265  static_cast<EditAlarmDlg*>(parent())->deleteLater();
1266 }
1267 
1268 /******************************************************************************
1269 * Display the alarm edit dialog to edit a new alarm, preset with a template.
1270 */
1271 bool editNewAlarm(const QString& templateName, QWidget* parent)
1272 {
1273  if (!templateName.isEmpty())
1274  {
1275  KAEvent* templateEvent = AlarmCalendar::resources()->templateEvent(templateName);
1276  if (templateEvent->isValid())
1277  {
1278  editNewAlarm(templateEvent, parent);
1279  return true;
1280  }
1281  kWarning() << templateName << ": template not found";
1282  }
1283  return false;
1284 }
1285 
1286 /******************************************************************************
1287 * Create a new template.
1288 */
1289 void editNewTemplate(EditAlarmDlg::Type type, QWidget* parent)
1290 {
1291  ::editNewTemplate(type, 0, parent);
1292 }
1293 
1294 /******************************************************************************
1295 * Create a new template, based on an existing event or template.
1296 */
1297 void editNewTemplate(const KAEvent* preset, QWidget* parent)
1298 {
1299  ::editNewTemplate(EditAlarmDlg::Type(0), preset, parent);
1300 }
1301 
1302 /******************************************************************************
1303 * Check the config as to whether there is a wake-on-suspend alarm pending, and
1304 * if so, delete it from the config if it has expired.
1305 * If 'checkExists' is true, the config entry will only be returned if the
1306 * event exists.
1307 * Reply = config entry: [0] = event's collection ID (Akonadi only),
1308 * [1] = event ID,
1309 * [2] = trigger time (time_t).
1310 * = empty list if none or expired.
1311 */
1312 QStringList checkRtcWakeConfig(bool checkEventExists)
1313 {
1314  KConfigGroup config(KGlobal::config(), "General");
1315  QStringList params = config.readEntry("RtcWake", QStringList());
1316 #ifdef USE_AKONADI
1317  if (params.count() == 3 && params[2].toUInt() > KDateTime::currentUtcDateTime().toTime_t())
1318 #else
1319  if (params.count() == 2 && params[1].toUInt() > KDateTime::currentUtcDateTime().toTime_t())
1320 #endif
1321  {
1322 #ifdef USE_AKONADI
1323  if (checkEventExists && !AlarmCalendar::getEvent(EventId(params[0].toLongLong(), params[1])))
1324 #else
1325  if (checkEventExists && !AlarmCalendar::getEvent(params[0]))
1326 #endif
1327  return QStringList();
1328  return params; // config entry is valid
1329  }
1330  if (!params.isEmpty())
1331  {
1332  config.deleteEntry("RtcWake"); // delete the expired config entry
1333  config.sync();
1334  }
1335  return QStringList();
1336 }
1337 
1338 /******************************************************************************
1339 * Delete any wake-on-suspend alarm from the config.
1340 */
1341 void deleteRtcWakeConfig()
1342 {
1343  KConfigGroup config(KGlobal::config(), "General");
1344  config.deleteEntry("RtcWake");
1345  config.sync();
1346 }
1347 
1348 /******************************************************************************
1349 * Delete any wake-on-suspend alarm, optionally only for a specified event.
1350 */
1351 void cancelRtcWake(QWidget* msgParent, const QString& eventId)
1352 {
1353  QStringList wakeup = checkRtcWakeConfig();
1354  if (!wakeup.isEmpty() && (eventId.isEmpty() || wakeup[0] == eventId))
1355  {
1356  Private::instance()->mMsgParent = msgParent ? msgParent : MainWindow::mainMainWindow();
1357  QTimer::singleShot(0, Private::instance(), SLOT(cancelRtcWake()));
1358  }
1359 }
1360 
1361 /******************************************************************************
1362 * Delete any wake-on-suspend alarm.
1363 */
1364 void Private::cancelRtcWake()
1365 {
1366  // setRtcWakeTime will only work with a parent window specified
1367  setRtcWakeTime(0, mMsgParent);
1368  deleteRtcWakeConfig();
1369  KAMessageBox::information(mMsgParent, i18nc("info", "The scheduled Wake from Suspend has been cancelled."));
1370 }
1371 
1372 /******************************************************************************
1373 * Set the wakeup time for the system.
1374 * Set 'triggerTime' to zero to cancel the wakeup.
1375 * Reply = true if successful.
1376 */
1377 bool setRtcWakeTime(unsigned triggerTime, QWidget* parent)
1378 {
1379  QVariantMap args;
1380  args[QLatin1String("time")] = triggerTime;
1381  KAuth::Action action(QLatin1String("org.kde.kalarmrtcwake.settimer"));
1382  action.setHelperID(QLatin1String("org.kde.kalarmrtcwake"));
1383  action.setParentWidget(parent);
1384  action.setArguments(args);
1385  KAuth::ActionReply reply = action.execute();
1386  if (reply.failed())
1387  {
1388  QString errmsg = reply.errorDescription();
1389  kDebug() << "Error code=" << reply.errorCode() << errmsg;
1390  if (errmsg.isEmpty())
1391  {
1392  int errcode = reply.errorCode();
1393  switch (reply.type())
1394  {
1395  case KAuth::ActionReply::KAuthError:
1396  kDebug() << "Authorisation error:" << errcode;
1397  switch (errcode)
1398  {
1399  case KAuth::ActionReply::AuthorizationDenied:
1400  case KAuth::ActionReply::UserCancelled:
1401  return false; // the user should already know about this
1402  default:
1403  break;
1404  }
1405  break;
1406  case KAuth::ActionReply::HelperError:
1407  kDebug() << "Helper error:" << errcode;
1408  errcode += 100; // make code distinguishable from KAuthError type
1409  break;
1410  default:
1411  break;
1412  }
1413  errmsg = i18nc("@info", "Error obtaining authorization (%1)", errcode);
1414  }
1415  KAMessageBox::information(parent, errmsg);
1416  return false;
1417  }
1418  return true;
1419 }
1420 
1421 } // namespace KAlarm
1422 namespace
1423 {
1424 
1425 /******************************************************************************
1426 * Create a new template.
1427 * 'preset' is non-null to base it on an existing event or template; otherwise,
1428 * the alarm type is set to 'type'.
1429 */
1430 void editNewTemplate(EditAlarmDlg::Type type, const KAEvent* preset, QWidget* parent)
1431 {
1432 #ifdef USE_AKONADI
1433  if (CollectionControlModel::enabledCollections(CalEvent::TEMPLATE, true).isEmpty())
1434 #else
1435  if (!AlarmResources::instance()->activeCount(CalEvent::TEMPLATE, true))
1436 #endif
1437  {
1438  KAMessageBox::sorry(parent, i18nc("@info", "You must enable a template calendar to save the template in"));
1439  return;
1440  }
1441  // Use AutoQPointer to guard against crash on application exit while
1442  // the dialogue is still open. It prevents double deletion (both on
1443  // deletion of parent, and on return from this function).
1444  AutoQPointer<EditAlarmDlg> editDlg;
1445  if (preset)
1446  editDlg = EditAlarmDlg::create(true, preset, true, parent);
1447  else
1448  editDlg = EditAlarmDlg::create(true, type, parent);
1449  if (editDlg->exec() == QDialog::Accepted)
1450  {
1451  KAEvent event;
1452 #ifdef USE_AKONADI
1453  Akonadi::Collection calendar;
1454 #else
1455  AlarmResource* calendar;
1456 #endif
1457  editDlg->getEvent(event, calendar);
1458 
1459  // Add the template to the displayed lists and to the calendar file
1460 #ifdef USE_AKONADI
1461  KAlarm::addTemplate(event, &calendar, editDlg);
1462 #else
1463  KAlarm::addTemplate(event, calendar, editDlg);
1464 #endif
1465  Undo::saveAdd(event, calendar);
1466  }
1467 }
1468 
1469 } // namespace
1470 namespace KAlarm
1471 {
1472 
1473 /******************************************************************************
1474 * Open the Edit Alarm dialog to edit the specified alarm.
1475 * If the alarm is read-only or archived, the dialog is opened read-only.
1476 */
1477 void editAlarm(KAEvent* event, QWidget* parent)
1478 {
1479 #ifdef USE_AKONADI
1480  if (event->expired() || AlarmCalendar::resources()->eventReadOnly(event->itemId()))
1481 #else
1482  if (event->expired() || AlarmCalendar::resources()->eventReadOnly(event->id()))
1483 #endif
1484  {
1485  viewAlarm(event, parent);
1486  return;
1487  }
1488 #ifdef USE_AKONADI
1489  EventId id(*event);
1490 #else
1491  QString id = event->id();
1492 #endif
1493  // Use AutoQPointer to guard against crash on application exit while
1494  // the dialogue is still open. It prevents double deletion (both on
1495  // deletion of parent, and on return from this function).
1496  AutoQPointer<EditAlarmDlg> editDlg = EditAlarmDlg::create(false, event, false, parent, EditAlarmDlg::RES_USE_EVENT_ID);
1497  if (editDlg->exec() == QDialog::Accepted)
1498  {
1499  if (!AlarmCalendar::resources()->event(id))
1500  {
1501  // Event has been deleted while the user was editing the alarm,
1502  // so treat it as a new alarm.
1503  PrivateNewAlarmDlg().accept(editDlg);
1504  return;
1505  }
1506  KAEvent newEvent;
1507 #ifdef USE_AKONADI
1508  Collection calendar;
1509 #else
1510  AlarmResource* calendar;
1511 #endif
1512  bool changeDeferral = !editDlg->getEvent(newEvent, calendar);
1513 
1514  // Update the event in the displays and in the calendar file
1515  Undo::Event undo(*event, calendar);
1516  if (changeDeferral)
1517  {
1518  // The only change has been to an existing deferral
1519  if (updateEvent(newEvent, editDlg, true) != UPDATE_OK) // keep the same event ID
1520  return; // failed to save event
1521  }
1522  else
1523  {
1524  UpdateResult status = modifyEvent(*event, newEvent, editDlg);
1525  if (status.status != UPDATE_OK && status.status <= UPDATE_KORG_ERR)
1526  displayKOrgUpdateError(editDlg, ERR_MODIFY, status);
1527  }
1528  Undo::saveEdit(undo, newEvent);
1529 
1530  outputAlarmWarnings(editDlg, &newEvent);
1531  }
1532 }
1533 
1534 /******************************************************************************
1535 * Display the alarm edit dialog to edit the alarm with the specified ID.
1536 * An error occurs if the alarm is not found, if there is more than one alarm
1537 * with the same ID, or if it is read-only or expired.
1538 */
1539 #ifdef USE_AKONADI
1540 bool editAlarmById(const EventId& id, QWidget* parent)
1541 #else
1542 bool editAlarmById(const QString& eventID, QWidget* parent)
1543 #endif
1544 {
1545 #ifdef USE_AKONADI
1546  const QString eventID(id.eventId());
1547  KAEvent* event = AlarmCalendar::resources()->event(id, true);
1548  if (!event)
1549  {
1550  if (id.collectionId() != -1)
1551  kWarning() << "Event ID not found, or duplicated:" << eventID;
1552  else
1553  kWarning() << "Event ID not found:" << eventID;
1554  return false;
1555  }
1556  if (AlarmCalendar::resources()->eventReadOnly(event->itemId()))
1557 #else
1558  KAEvent* event = AlarmCalendar::resources()->event(eventID);
1559  if (!event)
1560  {
1561  kError() << eventID << ": event ID not found";
1562  return false;
1563  }
1564  if (AlarmCalendar::resources()->eventReadOnly(eventID))
1565 #endif
1566  {
1567  kError() << eventID << ": read-only";
1568  return false;
1569  }
1570  switch (event->category())
1571  {
1572  case CalEvent::ACTIVE:
1573  case CalEvent::TEMPLATE:
1574  break;
1575  default:
1576  kError() << eventID << ": event not active or template";
1577  return false;
1578  }
1579  editAlarm(event, parent);
1580  return true;
1581 }
1582 
1583 /******************************************************************************
1584 * Open the Edit Alarm dialog to edit the specified template.
1585 * If the template is read-only, the dialog is opened read-only.
1586 */
1587 void editTemplate(KAEvent* event, QWidget* parent)
1588 {
1589 #ifdef USE_AKONADI
1590  if (AlarmCalendar::resources()->eventReadOnly(event->itemId()))
1591 #else
1592  if (AlarmCalendar::resources()->eventReadOnly(event->id()))
1593 #endif
1594  {
1595  // The template is read-only, so make the dialogue read-only.
1596  // Use AutoQPointer to guard against crash on application exit while
1597  // the dialogue is still open. It prevents double deletion (both on
1598  // deletion of parent, and on return from this function).
1599  AutoQPointer<EditAlarmDlg> editDlg = EditAlarmDlg::create(true, event, false, parent, EditAlarmDlg::RES_PROMPT, true);
1600  editDlg->exec();
1601  return;
1602  }
1603  // Use AutoQPointer to guard against crash on application exit while
1604  // the dialogue is still open. It prevents double deletion (both on
1605  // deletion of parent, and on return from this function).
1606  AutoQPointer<EditAlarmDlg> editDlg = EditAlarmDlg::create(true, event, false, parent, EditAlarmDlg::RES_USE_EVENT_ID);
1607  if (editDlg->exec() == QDialog::Accepted)
1608  {
1609  KAEvent newEvent;
1610 #ifdef USE_AKONADI
1611  Akonadi::Collection calendar;
1612 #else
1613  AlarmResource* calendar;
1614 #endif
1615  editDlg->getEvent(newEvent, calendar);
1616  QString id = event->id();
1617  newEvent.setEventId(id);
1618 #ifdef USE_AKONADI
1619  newEvent.setCollectionId(event->collectionId());
1620  newEvent.setItemId(event->itemId());
1621 #endif
1622 
1623  // Update the event in the displays and in the calendar file
1624  Undo::Event undo(*event, calendar);
1625  updateTemplate(newEvent, editDlg);
1626  Undo::saveEdit(undo, newEvent);
1627  }
1628 }
1629 
1630 /******************************************************************************
1631 * Open the Edit Alarm dialog to view the specified alarm (read-only).
1632 */
1633 void viewAlarm(const KAEvent* event, QWidget* parent)
1634 {
1635  // Use AutoQPointer to guard against crash on application exit while
1636  // the dialogue is still open. It prevents double deletion (both on
1637  // deletion of parent, and on return from this function).
1638  AutoQPointer<EditAlarmDlg> editDlg = EditAlarmDlg::create(false, event, false, parent, EditAlarmDlg::RES_PROMPT, true);
1639  editDlg->exec();
1640 }
1641 
1642 /******************************************************************************
1643 * Called when OK is clicked in the alarm edit dialog invoked by the Edit button
1644 * in an alarm message window.
1645 * Updates the alarm calendar and closes the dialog.
1646 */
1647 #ifdef USE_AKONADI
1648 void updateEditedAlarm(EditAlarmDlg* editDlg, KAEvent& event, Collection& calendar)
1649 #else
1650 void updateEditedAlarm(EditAlarmDlg* editDlg, KAEvent& event, AlarmResource* calendar)
1651 #endif
1652 {
1653  kDebug();
1654  KAEvent newEvent;
1655 #ifdef USE_AKONADI
1656  Akonadi::Collection cal;
1657 #else
1658  AlarmResource* cal;
1659 #endif
1660  editDlg->getEvent(newEvent, cal);
1661 
1662  // Update the displayed lists and the calendar file
1663  UpdateResult status;
1664 #ifdef USE_AKONADI
1665  if (AlarmCalendar::resources()->event(EventId(event)))
1666 #else
1667  if (AlarmCalendar::resources()->event(event.id()))
1668 #endif
1669  {
1670  // The old alarm hasn't expired yet, so replace it
1671  Undo::Event undo(event, calendar);
1672  status = modifyEvent(event, newEvent, editDlg);
1673  Undo::saveEdit(undo, newEvent);
1674  }
1675  else
1676  {
1677  // The old event has expired, so simply create a new one
1678 #ifdef USE_AKONADI
1679  status = addEvent(newEvent, &calendar, editDlg);
1680 #else
1681  status = addEvent(newEvent, calendar, editDlg);
1682 #endif
1683  Undo::saveAdd(newEvent, calendar);
1684  }
1685 
1686  if (status.status != UPDATE_OK && status.status <= UPDATE_KORG_ERR)
1687  displayKOrgUpdateError(editDlg, ERR_MODIFY, status);
1688  outputAlarmWarnings(editDlg, &newEvent);
1689 
1690  editDlg->close();
1691 }
1692 
1693 /******************************************************************************
1694 * Returns a list of all alarm templates.
1695 * If shell commands are disabled, command alarm templates are omitted.
1696 */
1697 KAEvent::List templateList()
1698 {
1699  KAEvent::List templates;
1700  bool includeCmdAlarms = ShellProcess::authorised();
1701  KAEvent::List events = AlarmCalendar::resources()->events(CalEvent::TEMPLATE);
1702  for (int i = 0, end = events.count(); i < end; ++i)
1703  {
1704  KAEvent* event = events[i];
1705  if (includeCmdAlarms || !(event->actionTypes() & KAEvent::ACT_COMMAND))
1706  templates.append(event);
1707  }
1708  return templates;
1709 }
1710 
1711 /******************************************************************************
1712 * To be called after an alarm has been edited.
1713 * Prompt the user to re-enable alarms if they are currently disabled, and if
1714 * it's an email alarm, warn if no 'From' email address is configured.
1715 */
1716 void outputAlarmWarnings(QWidget* parent, const KAEvent* event)
1717 {
1718  if (event && event->actionTypes() == KAEvent::ACT_EMAIL
1719  && Preferences::emailAddress().isEmpty())
1720  KAMessageBox::information(parent, i18nc("@info Please set the 'From' email address...",
1721  "<para>%1</para><para>Please set it in the Configuration dialog.</para>", KAMail::i18n_NeedFromEmailAddress()));
1722 
1723  if (!theApp()->alarmsEnabled())
1724  {
1725  if (KAMessageBox::warningYesNo(parent, i18nc("@info", "<para>Alarms are currently disabled.</para><para>Do you want to enable alarms now?</para>"),
1726  QString(), KGuiItem(i18nc("@action:button", "Enable")), KGuiItem(i18nc("@action:button", "Keep Disabled")),
1727  QLatin1String("EditEnableAlarms"))
1728  == KMessageBox::Yes)
1729  theApp()->setAlarmsEnabled(true);
1730  }
1731 }
1732 
1733 /******************************************************************************
1734 * Reload the calendar.
1735 */
1736 void refreshAlarms()
1737 {
1738  kDebug();
1739  if (!refreshAlarmsQueued)
1740  {
1741  refreshAlarmsQueued = true;
1742  theApp()->processQueue();
1743  }
1744 }
1745 
1746 /******************************************************************************
1747 * This method must only be called from the main KAlarm queue processing loop,
1748 * to prevent asynchronous calendar operations interfering with one another.
1749 *
1750 * If refreshAlarms() has been called, reload the calendars.
1751 */
1752 void refreshAlarmsIfQueued()
1753 {
1754  if (refreshAlarmsQueued)
1755  {
1756  kDebug();
1757  AlarmCalendar::resources()->reload();
1758 
1759  // Close any message windows for alarms which are now disabled
1760  KAEvent::List events = AlarmCalendar::resources()->events(CalEvent::ACTIVE);
1761  for (int i = 0, end = events.count(); i < end; ++i)
1762  {
1763  KAEvent* event = events[i];
1764  if (!event->enabled() && (event->actionTypes() & KAEvent::ACT_DISPLAY))
1765  {
1766 #ifdef USE_AKONADI
1767  MessageWin* win = MessageWin::findEvent(EventId(*event));
1768 #else
1769  MessageWin* win = MessageWin::findEvent(event->id());
1770 #endif
1771  delete win;
1772  }
1773  }
1774 
1775  MainWindow::refresh();
1776  refreshAlarmsQueued = false;
1777  }
1778 }
1779 
1780 /******************************************************************************
1781 * Start KMail if it isn't already running, optionally minimised.
1782 * Reply = reason for failure to run KMail (which may be the empty string)
1783 * = null string if success.
1784 */
1785 QString runKMail(bool minimise)
1786 {
1787  QDBusReply<bool> reply = QDBusConnection::sessionBus().interface()->isServiceRegistered(KMAIL_DBUS_SERVICE);
1788  if (!reply.isValid() || !reply.value())
1789  {
1790  // Program is not already running, so start it
1791  QString errmsg;
1792  if (minimise && Private::startKMailMinimised())
1793  return QString();
1794  if (KToolInvocation::startServiceByDesktopName(QLatin1String("kmail"), QString(), &errmsg))
1795  {
1796  kError() << "Couldn't start KMail (" << errmsg << ")";
1797  return i18nc("@info", "Unable to start <application>KMail</application><nl/>(<message>%1</message>)", errmsg);
1798  }
1799  }
1800  return QString();
1801 }
1802 
1803 /******************************************************************************
1804 * Start KMail, minimised.
1805 * This code is taken from kstart in kdebase.
1806 */
1807 bool Private::startKMailMinimised()
1808 {
1809 #ifdef Q_WS_X11
1810  NETRootInfo i(QX11Info::display(), NET::Supported);
1811  if (i.isSupported(NET::WM2KDETemporaryRules))
1812  {
1813  kDebug() << "using rules";
1814  KXMessages msg;
1815  QString message = QLatin1String("wmclass=kmail\nwmclassmatch=1\n" // 1 = exact match
1816  "wmclasscomplete=false\n"
1817  "minimize=true\nminimizerule=3\n"
1818  "type=") + QString().setNum(NET::Normal) + QLatin1String("\ntyperule=2");
1819  msg.broadcastMessage("_KDE_NET_WM_TEMPORARY_RULES", message, -1, false);
1820  qApp->flush();
1821  }
1822  else
1823  {
1824  // Connect to window add to get the NEW windows
1825  kDebug() << "connecting to window add";
1826  connect(KWindowSystem::self(), SIGNAL(windowAdded(WId)), instance(), SLOT(windowAdded(WId)));
1827  }
1828  // Propagate the app startup notification info to the started app.
1829  // We are not using KApplication, so the env remained set.
1830  KStartupInfoId id = KStartupInfo::currentStartupIdEnv();
1831  KProcess* proc = new KProcess;
1832  (*proc) << QLatin1String("kmail");
1833  int pid = proc->startDetached();
1834  if (!pid)
1835  {
1836  KStartupInfo::sendFinish(id); // failed to start
1837  return false;
1838  }
1839  KStartupInfoData data;
1840  data.addPid(pid);
1841  data.setName(QLatin1String("kmail"));
1842  data.setBin(QLatin1String("kmail"));
1843  KStartupInfo::sendChange(id, data);
1844  return true;
1845 #else
1846  return false;
1847 #endif
1848 }
1849 
1850 /******************************************************************************
1851 * Called when a window is created, to minimise it.
1852 * This code is taken from kstart in kdebase.
1853 */
1854 void Private::windowAdded(WId w)
1855 {
1856 #ifdef Q_WS_X11
1857  static const int SUPPORTED_TYPES = NET::NormalMask | NET::DesktopMask | NET::DockMask
1858  | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
1859  | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask;
1860  KWindowInfo kwinfo = KWindowSystem::windowInfo(w, NET::WMWindowType | NET::WMName);
1861  if (kwinfo.windowType(SUPPORTED_TYPES) == NET::TopMenu
1862  || kwinfo.windowType(SUPPORTED_TYPES) == NET::Toolbar
1863  || kwinfo.windowType(SUPPORTED_TYPES) == NET::Desktop)
1864  return; // always ignore these window types
1865 
1866  QX11Info qxinfo;
1867  XWithdrawWindow(QX11Info::display(), w, qxinfo.screen());
1868  QApplication::flush();
1869 
1870  NETWinInfo info(QX11Info::display(), w, QX11Info::appRootWindow(), NET::WMState);
1871  XWMHints* hints = XGetWMHints(QX11Info::display(), w);
1872  if (hints)
1873  {
1874  hints->flags |= StateHint;
1875  hints->initial_state = IconicState;
1876  XSetWMHints(QX11Info::display(), w, hints);
1877  XFree(hints);
1878  }
1879  info.setWindowType(NET::Normal);
1880 
1881  XSync(QX11Info::display(), False);
1882  XMapWindow(QX11Info::display(), w);
1883  XSync(QX11Info::display(), False);
1884  QApplication::flush();
1885 #endif
1886 }
1887 
1888 /******************************************************************************
1889 * The "Don't show again" option for error messages is personal to the user on a
1890 * particular computer. For example, he may want to inhibit error messages only
1891 * on his laptop. So the status is not stored in the alarm calendar, but in the
1892 * user's local KAlarm data directory.
1893 ******************************************************************************/
1894 
1895 /******************************************************************************
1896 * Return the Don't-show-again error message tags set for a specified alarm ID.
1897 */
1898 #ifdef USE_AKONADI
1899 QStringList dontShowErrors(const EventId& eventId)
1900 #else
1901 QStringList dontShowErrors(const QString& eventId)
1902 #endif
1903 {
1904  if (eventId.isEmpty())
1905  return QStringList();
1906  KConfig config(KStandardDirs::locateLocal("appdata", ALARM_OPTS_FILE));
1907  KConfigGroup group(&config, DONT_SHOW_ERRORS_GROUP);
1908 #ifdef USE_AKONADI
1909  const QString id = QString::fromLatin1("%1:%2").arg(eventId.collectionId()).arg(eventId.eventId());
1910 #else
1911  const QString id(eventId);
1912 #endif
1913  return group.readEntry(id, QStringList());
1914 }
1915 
1916 /******************************************************************************
1917 * Check whether the specified Don't-show-again error message tag is set for an
1918 * alarm ID.
1919 */
1920 #ifdef USE_AKONADI
1921 bool dontShowErrors(const EventId& eventId, const QString& tag)
1922 #else
1923 bool dontShowErrors(const QString& eventId, const QString& tag)
1924 #endif
1925 {
1926  if (tag.isEmpty())
1927  return false;
1928  QStringList tags = dontShowErrors(eventId);
1929  return tags.indexOf(tag) >= 0;
1930 }
1931 
1932 /******************************************************************************
1933 * Reset the Don't-show-again error message tags for an alarm ID.
1934 * If 'tags' is empty, the config entry is deleted.
1935 */
1936 #ifdef USE_AKONADI
1937 void setDontShowErrors(const EventId& eventId, const QStringList& tags)
1938 #else
1939 void setDontShowErrors(const QString& eventId, const QStringList& tags)
1940 #endif
1941 {
1942  if (eventId.isEmpty())
1943  return;
1944  KConfig config(KStandardDirs::locateLocal("appdata", ALARM_OPTS_FILE));
1945  KConfigGroup group(&config, DONT_SHOW_ERRORS_GROUP);
1946 #ifdef USE_AKONADI
1947  const QString id = QString::fromLatin1("%1:%2").arg(eventId.collectionId()).arg(eventId.eventId());
1948 #else
1949  const QString id(eventId);
1950 #endif
1951  if (tags.isEmpty())
1952  group.deleteEntry(id);
1953  else
1954  group.writeEntry(id, tags);
1955  group.sync();
1956 }
1957 
1958 /******************************************************************************
1959 * Set the specified Don't-show-again error message tag for an alarm ID.
1960 * Existing tags are unaffected.
1961 */
1962 #ifdef USE_AKONADI
1963 void setDontShowErrors(const EventId& eventId, const QString& tag)
1964 #else
1965 void setDontShowErrors(const QString& eventId, const QString& tag)
1966 #endif
1967 {
1968  if (eventId.isEmpty() || tag.isEmpty())
1969  return;
1970  KConfig config(KStandardDirs::locateLocal("appdata", ALARM_OPTS_FILE));
1971  KConfigGroup group(&config, DONT_SHOW_ERRORS_GROUP);
1972 #ifdef USE_AKONADI
1973  const QString id = QString::fromLatin1("%1:%2").arg(eventId.collectionId()).arg(eventId.eventId());
1974 #else
1975  const QString id(eventId);
1976 #endif
1977  QStringList tags = group.readEntry(id, QStringList());
1978  if (tags.indexOf(tag) < 0)
1979  {
1980  tags += tag;
1981  group.writeEntry(id, tags);
1982  group.sync();
1983  }
1984 }
1985 
1986 /******************************************************************************
1987 * Read the size for the specified window from the config file, for the
1988 * current screen resolution.
1989 * Reply = true if size set in the config file, in which case 'result' is set
1990 * = false if no size is set, in which case 'result' is unchanged.
1991 */
1992 bool readConfigWindowSize(const char* window, QSize& result, int* splitterWidth)
1993 {
1994  KConfigGroup config(KGlobal::config(), window);
1995  QWidget* desktop = KApplication::desktop();
1996  QSize s = QSize(config.readEntry(QString::fromLatin1("Width %1").arg(desktop->width()), (int)0),
1997  config.readEntry(QString::fromLatin1("Height %1").arg(desktop->height()), (int)0));
1998  if (s.isEmpty())
1999  return false;
2000  result = s;
2001  if (splitterWidth)
2002  *splitterWidth = config.readEntry(QString::fromLatin1("Splitter %1").arg(desktop->width()), -1);
2003  return true;
2004 }
2005 
2006 /******************************************************************************
2007 * Write the size for the specified window to the config file, for the
2008 * current screen resolution.
2009 */
2010 void writeConfigWindowSize(const char* window, const QSize& size, int splitterWidth)
2011 {
2012  KConfigGroup config(KGlobal::config(), window);
2013  QWidget* desktop = KApplication::desktop();
2014  config.writeEntry(QString::fromLatin1("Width %1").arg(desktop->width()), size.width());
2015  config.writeEntry(QString::fromLatin1("Height %1").arg(desktop->height()), size.height());
2016  if (splitterWidth >= 0)
2017  config.writeEntry(QString::fromLatin1("Splitter %1").arg(desktop->width()), splitterWidth);
2018  config.sync();
2019 }
2020 
2021 /******************************************************************************
2022 * Check from its mime type whether a file appears to be a text or image file.
2023 * If a text file, its type is distinguished.
2024 * Reply = file type.
2025 */
2026 FileType fileType(const KMimeType::Ptr& mimetype)
2027 {
2028  if (mimetype->is(QLatin1String("text/html")))
2029  return TextFormatted;
2030  if (mimetype->is(QLatin1String("application/x-executable")))
2031  return TextApplication;
2032  if (mimetype->is(QLatin1String("text/plain")))
2033  return TextPlain;
2034  if (mimetype->name().startsWith(QLatin1String("image/")))
2035  return Image;
2036  return Unknown;
2037 }
2038 
2039 /******************************************************************************
2040 * Check that a file exists and is a plain readable file.
2041 * Updates 'filename' and 'url' even if an error occurs, since 'filename' may
2042 * be needed subsequently by showFileErrMessage().
2043 */
2044 FileErr checkFileExists(QString& filename, KUrl& url)
2045 {
2046  url = KUrl();
2047  FileErr err = FileErr_None;
2048  QString file = filename;
2049  QRegExp f(QLatin1String("^file:/+"));
2050  if (f.indexIn(file) >= 0)
2051  file = file.mid(f.matchedLength() - 1);
2052  // Convert any relative file path to absolute
2053  // (using home directory as the default)
2054  int i = file.indexOf(QLatin1Char('/'));
2055  if (i > 0 && file[i - 1] == QLatin1Char(':'))
2056  {
2057  url = file;
2058  url.cleanPath();
2059  filename = url.prettyUrl();
2060  KIO::UDSEntry uds;
2061  if (!KIO::NetAccess::stat(url, uds, MainWindow::mainMainWindow()))
2062  err = FileErr_Nonexistent;
2063  else
2064  {
2065  KFileItem fi(uds, url);
2066  if (fi.isDir()) err = FileErr_Directory;
2067  else if (!fi.isReadable()) err = FileErr_Unreadable;
2068  }
2069  }
2070  else if (file.isEmpty())
2071  err = FileErr_Blank; // blank file name
2072  else
2073  {
2074  // It's a local file - convert to absolute path & check validity
2075  QFileInfo info(file);
2076  QDir::setCurrent(QDir::homePath());
2077  filename = info.absoluteFilePath();
2078  url.setPath(filename);
2079  if (info.isDir()) err = FileErr_Directory;
2080  else if (!info.exists()) err = FileErr_Nonexistent;
2081  else if (!info.isReadable()) err = FileErr_Unreadable;
2082  }
2083  return err;
2084 }
2085 
2086 /******************************************************************************
2087 * Display an error message appropriate to 'err'.
2088 * Display a Continue/Cancel error message if 'errmsgParent' non-null.
2089 * Reply = true to continue, false to cancel.
2090 */
2091 bool showFileErrMessage(const QString& filename, FileErr err, FileErr blankError, QWidget* errmsgParent)
2092 {
2093  if (err != FileErr_None)
2094  {
2095  // If file is a local file, remove "file://" from name
2096  QString file = filename;
2097  QRegExp f(QLatin1String("^file:/+"));
2098  if (f.indexIn(file) >= 0)
2099  file = file.mid(f.matchedLength() - 1);
2100 
2101  QString errmsg;
2102  switch (err)
2103  {
2104  case FileErr_Blank:
2105  if (blankError == FileErr_BlankDisplay)
2106  errmsg = i18nc("@info", "Please select a file to display");
2107  else if (blankError == FileErr_BlankPlay)
2108  errmsg = i18nc("@info", "Please select a file to play");
2109  else
2110  kFatal() << "Program error";
2111  KAMessageBox::sorry(errmsgParent, errmsg);
2112  return false;
2113  case FileErr_Directory:
2114  KAMessageBox::sorry(errmsgParent, i18nc("@info", "<filename>%1</filename> is a folder", file));
2115  return false;
2116  case FileErr_Nonexistent: errmsg = i18nc("@info", "<filename>%1</filename> not found", file); break;
2117  case FileErr_Unreadable: errmsg = i18nc("@info", "<filename>%1</filename> is not readable", file); break;
2118  case FileErr_NotTextImage: errmsg = i18nc("@info", "<filename>%1</filename> appears not to be a text or image file", file); break;
2119  default:
2120  break;
2121  }
2122  if (KAMessageBox::warningContinueCancel(errmsgParent, errmsg)
2123  == KMessageBox::Cancel)
2124  return false;
2125  }
2126  return true;
2127 }
2128 
2129 /******************************************************************************
2130 * If a url string is a local file, strip off the 'file:/' prefix.
2131 */
2132 QString pathOrUrl(const QString& url)
2133 {
2134  static const QRegExp localfile(QLatin1String("^file:/+"));
2135  return (localfile.indexIn(url) >= 0) ? url.mid(localfile.matchedLength() - 1) : url;
2136 }
2137 
2138 /******************************************************************************
2139 * Display a modal dialog to choose an existing file, initially highlighting
2140 * any specified file.
2141 * @param initialFile The file to initially highlight - must be a full path name or URL.
2142 * @param defaultDir The directory to start in if @p initialFile is empty. If empty,
2143 * the user's home directory will be used. Updated to the
2144 * directory containing the selected file, if a file is chosen.
2145 * @param mode OR of KFile::Mode values, e.g. ExistingOnly, LocalOnly.
2146 * Reply = URL selected.
2147 * = empty, non-null string if no file was selected.
2148 * = null string if dialogue was deleted while visible (indicating that
2149 * the parent widget was probably also deleted).
2150 */
2151 QString browseFile(const QString& caption, QString& defaultDir, const QString& initialFile,
2152  const QString& filter, KFile::Modes mode, QWidget* parent)
2153 {
2154  QString initialDir = !initialFile.isEmpty() ? QString(initialFile).remove(QRegExp(QLatin1String("/[^/]*$")))
2155  : !defaultDir.isEmpty() ? defaultDir
2156  : QDir::homePath();
2157  // Use AutoQPointer to guard against crash on application exit while
2158  // the dialogue is still open. It prevents double deletion (both on
2159  // deletion of parent, and on return from this function).
2160  AutoQPointer<KFileDialog> fileDlg = new KFileDialog(initialDir, filter, parent);
2161  fileDlg->setOperationMode(mode & KFile::ExistingOnly ? KFileDialog::Opening : KFileDialog::Saving);
2162  fileDlg->setMode(KFile::File | mode);
2163  fileDlg->setCaption(caption);
2164  if (!initialFile.isEmpty())
2165  fileDlg->setSelection(initialFile);
2166  if (fileDlg->exec() != QDialog::Accepted)
2167  return fileDlg ? QLatin1String("") : QString(); // return null only if dialog was deleted
2168  KUrl url = fileDlg->selectedUrl();
2169  if (url.isEmpty())
2170  return QLatin1String(""); // return empty, non-null string
2171  defaultDir = url.isLocalFile() ? url.upUrl().toLocalFile() : url.directory();
2172  return (mode & KFile::LocalOnly) ? url.pathOrUrl() : url.prettyUrl();
2173 }
2174 
2175 /******************************************************************************
2176 * Return a prompt string to ask the user whether to convert the calendar to the
2177 * current format.
2178 * If 'whole' is true, the whole calendar needs to be converted; else only some
2179 * alarms may need to be converted.
2180 *
2181 * Note: This method is defined here to avoid duplicating the i18n string
2182 * definition between the Akonadi and KResources code.
2183 */
2184 QString conversionPrompt(const QString& calendarName, const QString& calendarVersion, bool whole)
2185 {
2186  QString msg = whole
2187  ? i18nc("@info", "Calendar <resource>%1</resource> is in an old format (<application>KAlarm</application> version %2), "
2188  "and will be read-only unless you choose to update it to the current format.",
2189  calendarName, calendarVersion)
2190  : i18nc("@info", "Some or all of the alarms in calendar <resource>%1</resource> are in an old <application>KAlarm</application> format, "
2191  "and will be read-only unless you choose to update them to the current format.",
2192  calendarName);
2193  return i18nc("@info", "<para>%1</para><para>"
2194  "<warning>Do not update the calendar if it is also used with an older version of <application>KAlarm</application> "
2195  "(e.g. on another computer). If you do so, the calendar may become unusable there.</warning></para>"
2196  "<para>Do you wish to update the calendar?</para>", msg);
2197 }
2198 
2199 #ifndef NDEBUG
2200 /******************************************************************************
2201 * Set up KAlarm test conditions based on environment variables.
2202 * KALARM_TIME: specifies current system time (format [[[yyyy-]mm-]dd-]hh:mm [TZ]).
2203 */
2204 void setTestModeConditions()
2205 {
2206  const QByteArray newTime = qgetenv("KALARM_TIME");
2207  if (!newTime.isEmpty())
2208  {
2209  KDateTime dt;
2210  if (AlarmTime::convertTimeString(newTime, dt, KDateTime::realCurrentLocalDateTime(), true))
2211  setSimulatedSystemTime(dt);
2212  }
2213 }
2214 
2215 /******************************************************************************
2216 * Set the simulated system time.
2217 */
2218 void setSimulatedSystemTime(const KDateTime& dt)
2219 {
2220  KDateTime::setSimulatedSystemTime(dt);
2221  kDebug() << "New time =" << qPrintable(KDateTime::currentLocalDateTime().toString(QLatin1String("%Y-%m-%d %H:%M %:Z")));
2222 }
2223 #endif
2224 
2225 } // namespace KAlarm
2226 namespace
2227 {
2228 
2229 /******************************************************************************
2230 * Display an error message about an error when saving an event.
2231 */
2232 void displayUpdateError(QWidget* parent, KAlarm::UpdateError code, const UpdateStatusData& status, bool showKOrgError)
2233 {
2234  QString errmsg;
2235  if (status.status.status > KAlarm::UPDATE_KORG_ERR)
2236  {
2237  switch (code)
2238  {
2239  case KAlarm::ERR_ADD:
2240  case KAlarm::ERR_MODIFY:
2241  errmsg = (status.warnErr > 1) ? i18nc("@info", "Error saving alarms")
2242  : i18nc("@info", "Error saving alarm");
2243  break;
2244  case KAlarm::ERR_DELETE:
2245  errmsg = (status.warnErr > 1) ? i18nc("@info", "Error deleting alarms")
2246  : i18nc("@info", "Error deleting alarm");
2247  break;
2248  case KAlarm::ERR_REACTIVATE:
2249  errmsg = (status.warnErr > 1) ? i18nc("@info", "Error saving reactivated alarms")
2250  : i18nc("@info", "Error saving reactivated alarm");
2251  break;
2252  case KAlarm::ERR_TEMPLATE:
2253  errmsg = (status.warnErr > 1) ? i18nc("@info", "Error saving alarm templates")
2254  : i18nc("@info", "Error saving alarm template");
2255  break;
2256  }
2257  KAMessageBox::error(parent, errmsg);
2258  }
2259  else if (showKOrgError)
2260  displayKOrgUpdateError(parent, code, status.status, status.warnKOrg);
2261 }
2262 
2263 /******************************************************************************
2264 * Tell KOrganizer to put an alarm in its calendar.
2265 * It will be held by KOrganizer as a simple event, without alarms - KAlarm
2266 * is still responsible for alarming.
2267 */
2268 KAlarm::UpdateResult sendToKOrganizer(const KAEvent& event)
2269 {
2270 #ifdef USE_AKONADI
2271  Event::Ptr kcalEvent(new KCalCore::Event);
2272  event.updateKCalEvent(kcalEvent, KAEvent::UID_IGNORE);
2273 #else
2274  Event* kcalEvent = AlarmCalendar::resources()->createKCalEvent(&event);
2275 #endif
2276  // Change the event ID to avoid duplicating the same unique ID as the original event
2277  QString uid = uidKOrganizer(event.id());
2278  kcalEvent->setUid(uid);
2279  kcalEvent->clearAlarms();
2280  QString userEmail;
2281  switch (event.actionTypes())
2282  {
2283  case KAEvent::ACT_DISPLAY:
2284  case KAEvent::ACT_COMMAND:
2285  case KAEvent::ACT_DISPLAY_COMMAND:
2286  kcalEvent->setSummary(event.cleanText());
2287  userEmail = Preferences::emailAddress();
2288  break;
2289  case KAEvent::ACT_EMAIL:
2290  {
2291  QString from = event.emailFromId()
2292  ? Identities::identityManager()->identityForUoid(event.emailFromId()).fullEmailAddr()
2293  : Preferences::emailAddress();
2294  AlarmText atext;
2295  atext.setEmail(event.emailAddresses(QLatin1String(", ")), from, QString(), QString(), event.emailSubject(), QString());
2296  kcalEvent->setSummary(atext.displayText());
2297  userEmail = from;
2298  break;
2299  }
2300  case KAEvent::ACT_AUDIO:
2301  kcalEvent->setSummary(event.audioFile());
2302  break;
2303  default:
2304  break;
2305  }
2306 #ifdef USE_AKONADI
2307  Person::Ptr person(new Person(QString(), userEmail));
2308  kcalEvent->setOrganizer(person);
2309 #else
2310  kcalEvent->setOrganizer(KCal::Person(QString(), userEmail));
2311 #endif
2312  kcalEvent->setDuration(Duration(Preferences::kOrgEventDuration() * 60, Duration::Seconds));
2313 
2314  // Translate the event into string format
2315  ICalFormat format;
2316  format.setTimeSpec(Preferences::timeZone(true));
2317  QString iCal = format.toICalString(kcalEvent);
2318 #ifndef USE_AKONADI
2319  delete kcalEvent;
2320 #endif
2321 
2322  // Send the event to KOrganizer
2323  KAlarm::UpdateResult status = runKOrganizer(); // start KOrganizer if it isn't already running, and create its D-Bus interface
2324  if (status != KAlarm::UPDATE_OK)
2325  return status;
2326  QList<QVariant> args;
2327  args << iCal;
2328  QDBusReply<bool> reply = korgInterface->callWithArgumentList(QDBus::Block, QLatin1String("addIncidence"), args);
2329  if (!reply.isValid())
2330  {
2331  if (reply.error().type() == QDBusError::UnknownObject)
2332  {
2333  status = KAlarm::UPDATE_KORG_ERRSTART;
2334  kError() << "addIncidence() D-Bus error: still starting";
2335  }
2336  else
2337  {
2338  status.set(KAlarm::UPDATE_KORG_ERR, reply.error().message());
2339  kError() << "addIncidence(" << uid << ") D-Bus call failed:" << status.message;
2340  }
2341  }
2342  else if (!reply.value())
2343  {
2344  status = KAlarm::UPDATE_KORG_FUNCERR;
2345  kDebug() << "addIncidence(" << uid << ") D-Bus call returned false";
2346  }
2347  else
2348  kDebug() << uid << ": success";
2349  return status;
2350 }
2351 
2352 /******************************************************************************
2353 * Tell KOrganizer to delete an event from its calendar.
2354 */
2355 KAlarm::UpdateResult deleteFromKOrganizer(const QString& eventID)
2356 {
2357  const QString newID = uidKOrganizer(eventID);
2358 #if defined(USE_AKONADI) && KDE_IS_VERSION(4,12,4) // kdepimlibs/kdepim-runtime 4.12.4 are required
2359  new CollectionSearch(KORG_MIME_TYPE, newID, true); // this auto-deletes when complete
2360  // Ignore errors
2361  return KAlarm::UpdateResult(KAlarm::UPDATE_OK);
2362 #else
2363  KAlarm::UpdateResult status = runKOrganizer(); // start KOrganizer if it isn't already running, and create its D-Bus interface
2364  if (status != KAlarm::UPDATE_OK)
2365  return status;
2366  QList<QVariant> args;
2367  args << newID << true;
2368  QDBusReply<bool> reply = korgInterface->callWithArgumentList(QDBus::Block, QLatin1String("deleteIncidence"), args);
2369  if (!reply.isValid())
2370  {
2371  if (reply.error().type() == QDBusError::UnknownObject)
2372  {
2373  kError() << "deleteIncidence() D-Bus error: still starting";
2374  status = KAlarm::UPDATE_KORG_ERRSTART;
2375  }
2376  else
2377  {
2378  status.set(KAlarm::UPDATE_KORG_ERR, reply.error().message());
2379  kError() << "deleteIncidence(" << newID << ") D-Bus call failed:" << status.message;
2380  }
2381  }
2382  else if (!reply.value())
2383  {
2384  status = KAlarm::UPDATE_KORG_FUNCERR;
2385  kDebug() << "deleteIncidence(" << newID << ") D-Bus call returned false";
2386  }
2387  else
2388  kDebug() << newID << ": success";
2389  return status;
2390 #endif
2391 }
2392 
2393 /******************************************************************************
2394 * Start KOrganizer if not already running, and create its D-Bus interface.
2395 */
2396 KAlarm::UpdateResult runKOrganizer()
2397 {
2398  KAlarm::UpdateResult status;
2399  QString error, dbusService;
2400  int result = KDBusServiceStarter::self()->findServiceFor(QLatin1String("DBUS/Organizer"), QString(), &error, &dbusService);
2401  if (result)
2402  {
2403  status.set(KAlarm::UPDATE_KORG_ERRINIT, error);
2404  kWarning() << "Unable to start DBUS/Organizer:" << status.message;
2405  return status;
2406  }
2407  // If Kontact is running, there is a load() method which needs to be called to
2408  // load KOrganizer into Kontact. But if KOrganizer is running independently,
2409  // the load() method doesn't exist.
2410  QDBusInterface iface(KORG_DBUS_SERVICE, QLatin1String(KORG_DBUS_LOAD_PATH), QLatin1String("org.kde.KUniqueApplication"));
2411  if (!iface.isValid())
2412  {
2413  status.set(KAlarm::UPDATE_KORG_ERR, iface.lastError().message());
2414  kWarning() << "Unable to access " KORG_DBUS_LOAD_PATH " D-Bus interface:" << status.message;
2415  return status;
2416  }
2417  QDBusReply<bool> reply = iface.call(QLatin1String("load"));
2418  if ((!reply.isValid() || !reply.value())
2419  && iface.lastError().type() != QDBusError::UnknownMethod)
2420  {
2421  status.set(KAlarm::UPDATE_KORG_ERR, iface.lastError().message());
2422  kWarning() << "Loading KOrganizer failed:" << status.message;
2423  return status;
2424  }
2425 
2426  // KOrganizer has been started, but it may not have the necessary
2427  // D-Bus interface available yet.
2428  if (!korgInterface || !korgInterface->isValid())
2429  {
2430  delete korgInterface;
2431  korgInterface = new QDBusInterface(KORG_DBUS_SERVICE, QLatin1String(KORG_DBUS_PATH), KORG_DBUS_IFACE);
2432  if (!korgInterface->isValid())
2433  {
2434  status.set(KAlarm::UPDATE_KORG_ERRSTART, korgInterface->lastError().message());
2435  kWarning() << "Unable to access " KORG_DBUS_PATH " D-Bus interface:" << status.message;
2436  delete korgInterface;
2437  korgInterface = 0;
2438  }
2439  }
2440  return status;
2441 }
2442 
2443 /******************************************************************************
2444 * Insert a KOrganizer string after the hyphen in the supplied event ID.
2445 */
2446 QString uidKOrganizer(const QString& id)
2447 {
2448  QString result = id;
2449  int i = result.lastIndexOf(QLatin1Char('-'));
2450  if (i < 0)
2451  i = result.length();
2452  return result.insert(i, KORGANIZER_UID);
2453 }
2454 
2455 } // namespace
2456 
2457 /******************************************************************************
2458 * Case insensitive comparison for use by qSort().
2459 */
2460 bool caseInsensitiveLessThan(const QString& s1, const QString& s2)
2461 {
2462  return s1.toLower() < s2.toLower();
2463 }
2464 
2465 // vim: et sw=4:
QList::clear
void clear()
QString::indexOf
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
QWidget
QDBusAbstractInterface::isValid
bool isValid() const
CollectionControlModel::getStandard
static Akonadi::Collection getStandard(CalEvent::Type, bool useDefault=false)
Return the standard collection for a specified mime type.
Definition: collectionmodel.cpp:993
KAlarmApp::setAlarmsEnabled
void setAlarmsEnabled(bool)
Definition: kalarmapp.cpp:1319
KProcess
AlarmCalendar::deleteEvent
bool deleteEvent(const QString &eventID, bool save=false)
Definition: alarmcalendar.cpp:1653
EditAlarmDlg::RES_PROMPT
Definition: editdlg.h:66
QSize::width
int width() const
EventListModel::alarms
static EventListModel * alarms()
Definition: eventlistmodel.cpp:55
EditAlarmDlg::EMAIL
Definition: editdlg.h:64
AlarmCalendar::disabledChanged
void disabledChanged(const KAEvent *)
Definition: alarmcalendar.cpp:2128
KAlarmApp::alarmsEnabled
bool alarmsEnabled() const
Definition: kalarmapp.h:63
EditAlarmDlg::DISPLAY
Definition: editdlg.h:64
EditAlarmDlg::COMMAND
Definition: editdlg.h:64
KAlarm::Private::instance
static Private * instance()
Definition: functions_p.h:40
QByteArray
MessageWin::isAudioPlaying
static bool isAudioPlaying()
Definition: messagewin.cpp:1576
QSize::isEmpty
bool isEmpty() const
AlarmCalendar::eventReadOnly
bool eventReadOnly(const QString &uniqueID) const
Definition: alarmcalendar.cpp:2090
QDBusReply
text
virtual QByteArray text(quint32 serialNumber) const =0
caseInsensitiveLessThan
bool caseInsensitiveLessThan(const QString &s1, const QString &s2)
Definition: functions.cpp:2460
AlarmCalendar::updateEvent
KAEvent * updateEvent(const KAEvent &)
Definition: alarmcalendar.cpp:1582
QX11Info::appRootWindow
Qt::HANDLE appRootWindow(int screen)
QX11Info::display
Display * display()
QDBusConnection::interface
QDBusConnectionInterface * interface() const
KAlarm::UpdateResult
Result of calendar update.
Definition: functions.h:79
QDBusError::type
ErrorType type() const
AlarmCalendar::resources
static AlarmCalendar * resources()
Definition: alarmcalendar.h:130
alarmtime.h
QX11Info::screen
int screen() const
ShellProcess::authorised
static bool authorised()
KAMessageBox::error
static void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Options(Notify|WindowModal))
QByteArray::isEmpty
bool isEmpty() const
AlarmCalendar::reload
bool reload()
Definition: alarmcalendar.cpp:401
QDBusError::message
QString message() const
functions_p.h
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
Undo::saveEdit
static void saveEdit(const Event &oldEvent, const KAEvent &newEvent)
Definition: undo.cpp:327
QDBusConnection::sessionBus
QDBusConnection sessionBus()
alarmcalendar.h
EventListModel::removeEvent
void removeEvent(const KAEvent *event)
Definition: eventlistmodel.h:75
QString::remove
QString & remove(int position, int n)
from
QString from() const
QDBusConnectionInterface::isServiceRegistered
QDBusReply< bool > isServiceRegistered(const QString &serviceName) const
MainWindow::firstWindow
static MainWindow * firstWindow()
Definition: mainwindow.h:99
QDir::homePath
QString homePath()
AlarmCalendar::templateEvent
KAEvent * templateEvent(const QString &templateName)
Definition: alarmcalendar.cpp:1868
Undo::Event
Definition: undo.h:51
MessageWin::findEvent
static MessageWin * findEvent(const QString &eventId, MessageWin *exclude=0)
Definition: messagewin.cpp:1476
KAlarm::Private::startKMailMinimised
static bool startKMailMinimised()
Definition: functions.cpp:1807
KFileDialog
QString::lastIndexOf
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
KAlarm::UpdateResult::message
QString message
Definition: functions.h:82
QList::value
T value(int i) const
QObject::event
virtual bool event(QEvent *e)
QWidget::width
width
AlarmCalendar::getEvent
static KAEvent * getEvent(const QString &uniqueId)
Definition: alarmcalendar.cpp:150
kalarmapp.h
the KAlarm application object
QRegExp
AlarmTime::convertTimeString
static bool convertTimeString(const QByteArray &timeString, KDateTime &dateTime, const KDateTime &defaultDt=KDateTime(), bool allowTZ=true)
Definition: alarmtime.cpp:111
MessageWin
MessageWin: A window to display an alarm or error message.
Definition: messagewin.h:61
QList::count
int count(const T &value) const
AlarmCalendar::modifyEvent
bool modifyEvent(const QString &oldEventId, KAEvent *newEvent)
Definition: alarmcalendar.cpp:1504
QString::insert
QString & insert(int position, QChar ch)
CollectionControlModel::instance
static CollectionControlModel * instance()
Definition: collectionmodel.cpp:653
AlarmCalendar::purgeEvents
void purgeEvents(const KAEvent::List &)
Definition: alarmcalendar.cpp:1226
KAlarm::Private::cancelRtcWake
void cancelRtcWake()
Definition: functions.cpp:1364
autoqpointer.h
Preferences::timeZone
static KTimeZone timeZone(bool reload=false)
Definition: preferences.cpp:199
QObject
CollectionControlModel::destination
static Akonadi::Collection destination(CalEvent::Type, QWidget *promptParent=0, bool noPrompt=false, bool *cancelled=0)
Find the collection to be used to store an event of a given type.
Definition: collectionmodel.cpp:1180
QDBusReply::value
Type value() const
QList::isEmpty
bool isEmpty() const
MainWindow::selectEvent
void selectEvent(const QString &eventID)
Definition: mainwindow.cpp:702
KAlarm::Private::mMsgParent
QWidget * mMsgParent
Definition: functions_p.h:47
QString::isEmpty
bool isEmpty() const
EditAlarmDlg::setAction
virtual void setAction(KAEvent::SubAction, const AlarmText &=AlarmText())=0
messagewin.h
displays an alarm message
mainwindow.h
main application window
eventlistmodel.h
KAlarmApp::checkCalendar
bool checkCalendar()
Definition: kalarmapp.h:61
QDir::setCurrent
bool setCurrent(const QString &path)
EditAlarmDlg::create
static EditAlarmDlg * create(bool Template, Type, QWidget *parent=0, GetResourceType=RES_PROMPT)
Definition: editdlg.cpp:104
QDate
EditAlarmDlg::getEvent
bool getEvent(KAEvent &, AlarmResource *&)
Definition: editdlg.cpp:759
KAMail::i18n_NeedFromEmailAddress
static QString i18n_NeedFromEmailAddress()
Definition: kamail.cpp:88
QObject::deleteLater
void deleteLater()
MainWindow::create
static MainWindow * create(bool restored=false)
Definition: mainwindow.cpp:135
messagebox.h
QString
QList
KAMessageBox::detailedError
static void detailedError(QWidget *parent, const QString &text, const QString &details, const QString &caption=QString(), Options options=Options(Notify|WindowModal))
KAlarm::UpdateResult::set
void set(UpdateStatus s)
Definition: functions.h:88
theApp
KAlarmApp * theApp()
Definition: kalarmapp.h:263
QStringList
QDBusInterface
QFileInfo
QString::toLower
QString toLower() const
QSize
QLatin1Char
AlarmCalendar::createKCalEvent
KCal::Event * createKCalEvent(const KAEvent *e) const
Definition: alarmcalendar.h:85
AutoQPointer
preferences.h
QCoreApplication::flush
void flush()
CollectionControlModel::enabledCollections
static Akonadi::Collection::List enabledCollections(CalEvent::Type, bool writable)
Return the enabled collections which contain a specified mime type.
Definition: collectionmodel.cpp:1229
EventListModel::templates
static EventListModel * templates()
Definition: eventlistmodel.cpp:68
templatemenuaction.h
EventListModel::updateEvent
bool updateEvent(KAEvent *event)
Definition: eventlistmodel.h:72
KAlarm::PrivateNewAlarmDlg::accept
void accept(EditAlarmDlg *)
Definition: functions.cpp:1224
Undo::saveAdd
static void saveAdd(const KAEvent &, AlarmResource *, const QString &name=QString())
Definition: undo.cpp:309
collectionmodel.h
kamail.h
EventId
Unique event identifier for Akonadi.
Definition: eventid.h:38
QString::mid
QString mid(int position, int n) const
QVector
QTest::toString
char * toString(const T &value)
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))
EditAlarmDlg::Type
Type
Definition: editdlg.h:64
QLatin1String
functions.h
miscellaneous functions
KAMessageBox::sorry
static void sorry(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Options(Notify|WindowModal))
EditAlarmDlg
Definition: editdlg.h:60
QVector::isEmpty
bool isEmpty() const
QString::setNum
QString & setNum(short n, int base)
KORG_DBUS_LOAD_PATH
#define KORG_DBUS_LOAD_PATH
Definition: functions.cpp:146
Preferences::emailAddress
static QString emailAddress()
Definition: preferences.cpp:304
alarmlistview.h
EditAlarmDlg::RES_USE_EVENT_ID
Definition: editdlg.h:67
kalarm.h
KORG_DBUS_PATH
#define KORG_DBUS_PATH
Definition: functions.cpp:145
MainWindow::refresh
static void refresh()
Definition: mainwindow.cpp:674
QSize::height
int height() const
QVector::count
int count(const T &value) const
QString::length
int length() const
KAMessageBox::warningContinueCancel
static int warningContinueCancel(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Options(Notify|WindowModal))
QString::fromLatin1
QString fromLatin1(const char *str, int size)
AlarmCalendar
Provides read and write access to calendar files and resources.
Definition: alarmcalendar.h:58
QStringList::indexOf
int indexOf(const QRegExp &rx, int from) const
AlarmCalendar::displayCalendarOpen
static AlarmCalendar * displayCalendarOpen()
Definition: alarmcalendar.cpp:130
AlarmCalendar::events
KAEvent::List events(CalEvent::Types s=CalEvent::EMPTY) const
Definition: alarmcalendar.h:96
shellprocess.h
QDate::addDays
QDate addDays(int ndays) const
KAlarm::UpdateResult::status
UpdateStatus status
Definition: functions.h:81
EditAlarmDlg::AUDIO
Definition: editdlg.h:64
QDBusAbstractInterface::lastError
QDBusError lastError() const
AlarmCalendar::event
KAEvent * event(const QString &uniqueId)
Definition: alarmcalendar.cpp:1817
AlarmCalendar::save
bool save()
Definition: alarmcalendar.cpp:1209
MainWindow::show
virtual void show()
Definition: mainwindow.cpp:427
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)
QObject::parent
QObject * parent() const
QDBusReply::error
const QDBusError & error()
QDBusAbstractInterface::callWithArgumentList
QDBusMessage callWithArgumentList(QDBus::CallMode mode, const QString &method, const QList< QVariant > &args)
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
MainWindow
Definition: mainwindow.h:70
collectionsearch.h
AlarmListModel
Definition: itemlistmodel.h:87
KAlarm::Private::windowAdded
void windowAdded(WId)
Definition: functions.cpp:1854
QWidget::height
height
KAlarmApp::processQueue
void processQueue()
Definition: kalarmapp.cpp:891
AlarmListModel::TimeColumn
Definition: itemlistmodel.h:92
EventListModel::addEvent
void addEvent(KAEvent *)
Definition: eventlistmodel.cpp:725
templatelistview.h
QX11Info
QTimer::singleShot
singleShot
KAMessageBox::information
static void information(QWidget *parent, const QString &text, const QString &caption=QString(), const QString &dontShowAgainName=QString(), Options options=Options(Notify|WindowModal))
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