Fix the clang build.
[kdepim.git] / kalarm / functions.cpp
blobbaffbe1ca8a6eedd29869ce12c03b0da9c7d3e81
1 /*
2 * functions.cpp - miscellaneous functions
3 * Program: kalarm
4 * Copyright © 2001-2012 by David Jarvie <djarvie@kde.org>
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.
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.
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.
21 #include "kalarm.h" //krazy:exclude=includes (kalarm.h must be first)
22 #include "functions.h"
23 #include "functions_p.h"
25 #ifdef USE_AKONADI
26 #include "collectionmodel.h"
27 #else
28 #include "alarmresources.h"
29 #include "eventlistmodel.h"
30 #endif
31 #include "alarmcalendar.h"
32 #include "alarmtime.h"
33 #include "autoqpointer.h"
34 #include "alarmlistview.h"
35 #include "editdlg.h"
36 #include "kalarmapp.h"
37 #include "kamail.h"
38 #include "mainwindow.h"
39 #include "messagebox.h"
40 #include "messagewin.h"
41 #include "preferences.h"
42 #include "shellprocess.h"
43 #include "templatelistview.h"
44 #include "templatemenuaction.h"
46 #include <kalarmcal/identities.h>
47 #include <kalarmcal/kaevent.h>
49 #ifdef USE_AKONADI
50 #include <kcalcore/event.h>
51 #include <kcalcore/icalformat.h>
52 #include <kcalcore/person.h>
53 #include <kcalcore/duration.h>
54 using namespace KCalCore;
55 #else
56 #include <kcal/event.h>
57 #include <kcal/icalformat.h>
58 #include <kcal/person.h>
59 #include <kcal/duration.h>
60 using namespace KCal;
61 #endif
62 #include <kpimidentities/identitymanager.h>
63 #include <kpimidentities/identity.h>
64 #include <kholidays/holidays.h>
66 #include <kconfiggroup.h>
67 #include <kaction.h>
68 #include <ktoggleaction.h>
69 #include <kactioncollection.h>
70 #include <kdbusservicestarter.h>
71 #include <kglobal.h>
72 #include <klocale.h>
73 #include <kstandarddirs.h>
74 #include <kauth.h>
75 #include <ksystemtimezone.h>
76 #include <kstandardguiitem.h>
77 #include <kstandardshortcut.h>
78 #include <kfiledialog.h>
79 #include <kicon.h>
80 #include <kio/netaccess.h>
81 #include <kfileitem.h>
82 #include <kdebug.h>
83 #include <ktoolinvocation.h>
85 #ifdef Q_WS_X11
86 #include <kwindowsystem.h>
87 #include <kxmessages.h>
88 #include <kstartupinfo.h>
89 #include <netwm.h>
90 #include <QX11Info>
91 #endif
93 #include <QDir>
94 #include <QRegExp>
95 #include <QDesktopWidget>
96 #include <QtDBus/QtDBus>
97 #include <QTimer>
98 #include <qglobal.h>
100 #ifdef USE_AKONADI
101 using namespace Akonadi;
102 #endif
105 namespace
107 bool refreshAlarmsQueued = false;
108 QString korganizerName = "korganizer";
109 QString korgStartError;
110 QDBusInterface* korgInterface = 0;
112 const char* KMAIL_DBUS_SERVICE = "org.kde.kmail";
113 //const char* KMAIL_DBUS_IFACE = "org.kde.kmail.kmail";
114 //const char* KMAIL_DBUS_WINDOW_PATH = "/kmail/kmail_mainwindow_1";
115 const char* KORG_DBUS_SERVICE = "org.kde.korganizer";
116 const char* KORG_DBUS_IFACE = "org.kde.korganizer.Korganizer";
117 // D-Bus object path of KOrganizer's notification interface
118 #define KORG_DBUS_PATH "/Korganizer"
119 #define KORG_DBUS_LOAD_PATH "/korganizer_PimApplication"
120 //const char* KORG_DBUS_WINDOW_PATH = "/korganizer/MainWindow_1";
121 const QString KORGANIZER_UID = QString::fromLatin1("-korg");
123 const char* ALARM_OPTS_FILE = "alarmopts";
124 const char* DONT_SHOW_ERRORS_GROUP = "DontShowErrors";
126 void editNewTemplate(EditAlarmDlg::Type, const KAEvent* preset, QWidget* parent);
127 KAlarm::UpdateStatus sendToKOrganizer(const KAEvent*);
128 KAlarm::UpdateStatus deleteFromKOrganizer(const QString& eventID);
129 KAlarm::UpdateStatus runKOrganizer();
130 QString uidKOrganizer(const QString& eventID);
134 namespace KAlarm
137 Private* Private::mInstance = 0;
139 /******************************************************************************
140 * Display a main window with the specified event selected.
142 #ifdef USE_AKONADI
143 MainWindow* displayMainWindowSelected(Akonadi::Item::Id eventId)
144 #else
145 MainWindow* displayMainWindowSelected(const QString& eventId)
146 #endif
148 MainWindow* win = MainWindow::firstWindow();
149 if (!win)
151 if (theApp()->checkCalendar()) // ensure calendar is open
153 win = MainWindow::create();
154 win->show();
157 else
159 // There is already a main window, so make it the active window
160 win->hide(); // in case it's on a different desktop
161 win->setWindowState(win->windowState() & ~Qt::WindowMinimized);
162 win->show();
163 win->raise();
164 win->activateWindow();
166 #ifdef USE_AKONADI
167 if (win && eventId >= 0)
168 win->selectEvent(eventId);
169 #else
170 if (win && !eventId.isEmpty())
171 win->selectEvent(eventId);
172 #endif
173 return win;
176 /******************************************************************************
177 * Create an "Alarms Enabled/Enable Alarms" action.
179 KToggleAction* createAlarmEnableAction(QObject* parent)
181 KToggleAction* action = new KToggleAction(i18nc("@action", "Enable &Alarms"), parent);
182 action->setChecked(theApp()->alarmsEnabled());
183 QObject::connect(action, SIGNAL(toggled(bool)), theApp(), SLOT(setAlarmsEnabled(bool)));
184 // The following line ensures that all instances are kept in the same state
185 QObject::connect(theApp(), SIGNAL(alarmEnabledToggled(bool)), action, SLOT(setChecked(bool)));
186 return action;
189 /******************************************************************************
190 * Create a "Stop Play" action.
192 KAction* createStopPlayAction(QObject* parent)
194 KAction* action = new KAction(KIcon("media-playback-stop"), i18nc("@action", "Stop Play"), parent);
195 action->setEnabled(MessageWin::isAudioPlaying());
196 QObject::connect(action, SIGNAL(triggered(bool)), theApp(), SLOT(stopAudio()));
197 // The following line ensures that all instances are kept in the same state
198 QObject::connect(theApp(), SIGNAL(audioPlaying(bool)), action, SLOT(setEnabled(bool)));
199 return action;
202 /******************************************************************************
203 * Create a "Spread Windows" action.
205 KToggleAction* createSpreadWindowsAction(QObject* parent)
207 KToggleAction* action = new KToggleAction(i18nc("@action", "Spread Windows"), parent);
208 QObject::connect(action, SIGNAL(triggered(bool)), theApp(), SLOT(spreadWindows(bool)));
209 // The following line ensures that all instances are kept in the same state
210 QObject::connect(theApp(), SIGNAL(spreadWindowsToggled(bool)), action, SLOT(setChecked(bool)));
211 return action;
214 /******************************************************************************
215 * Add a new active (non-archived) alarm.
216 * Save it in the calendar file and add it to every main window instance.
217 * Parameters: msgParent = parent widget for any calendar selection prompt or
218 * error message.
219 * event - is updated with the actual event ID.
221 #ifdef USE_AKONADI
222 UpdateStatus addEvent(KAEvent& event, Collection* calendar, QWidget* msgParent, int options, bool showKOrgErr)
223 #else
224 UpdateStatus addEvent(KAEvent& event, AlarmResource* calendar, QWidget* msgParent, int options, bool showKOrgErr)
225 #endif
227 kDebug() << event.id();
228 bool cancelled = false;
229 UpdateStatus status = UPDATE_OK;
230 if (!theApp()->checkCalendar()) // ensure calendar is open
231 status = UPDATE_FAILED;
232 else
234 // Save the event details in the calendar file, and get the new event ID
235 AlarmCalendar* cal = AlarmCalendar::resources();
236 KAEvent* newev = new KAEvent(event);
237 #ifdef USE_AKONADI
238 if (!cal->addEvent(*newev, msgParent, (options & USE_EVENT_ID), calendar, (options & NO_RESOURCE_PROMPT), &cancelled))
239 #else
240 if (!cal->addEvent(newev, msgParent, (options & USE_EVENT_ID), calendar, (options & NO_RESOURCE_PROMPT), &cancelled))
241 #endif
243 delete newev;
244 status = UPDATE_FAILED;
246 else
248 event = *newev; // update event ID etc.
249 if (!cal->save())
250 status = SAVE_FAILED;
252 if (status == UPDATE_OK)
254 if ((options & ALLOW_KORG_UPDATE) && event.copyToKOrganizer())
256 UpdateStatus st = sendToKOrganizer(newev); // tell KOrganizer to show the event
257 if (st > status)
258 status = st;
261 #ifndef USE_AKONADI
262 // Update the window lists
263 EventListModel::alarms()->addEvent(newev);
264 #endif
268 if (status != UPDATE_OK && !cancelled && msgParent)
269 displayUpdateError(msgParent, status, ERR_ADD, 1, 1, showKOrgErr);
270 return status;
273 /******************************************************************************
274 * Add a list of new active (non-archived) alarms.
275 * Save them in the calendar file and add them to every main window instance.
276 * The events are updated with their actual event IDs.
278 UpdateStatus addEvents(QVector<KAEvent>& events, QWidget* msgParent, bool allowKOrgUpdate, bool showKOrgErr)
280 kDebug() << events.count();
281 if (events.isEmpty())
282 return UPDATE_OK;
283 int warnErr = 0;
284 int warnKOrg = 0;
285 UpdateStatus status = UPDATE_OK;
286 #ifdef USE_AKONADI
287 Collection collection;
288 #else
289 AlarmResource* resource;
290 #endif
291 if (!theApp()->checkCalendar()) // ensure calendar is open
292 status = UPDATE_FAILED;
293 else
295 #ifdef USE_AKONADI
296 collection = CollectionControlModel::instance()->destination(CalEvent::ACTIVE, msgParent);
297 if (!collection.isValid())
298 #else
299 resource = AlarmResources::instance()->destination(CalEvent::ACTIVE, msgParent);
300 if (!resource)
301 #endif
303 kDebug() << "No calendar";
304 status = UPDATE_FAILED;
307 if (status == UPDATE_OK)
309 QString selectID;
310 AlarmCalendar* cal = AlarmCalendar::resources();
311 for (int i = 0, end = events.count(); i < end; ++i)
313 // Save the event details in the calendar file, and get the new event ID
314 #ifdef USE_AKONADI
315 KAEvent* const newev = &events[i];
316 if (!cal->addEvent(events[i], msgParent, false, &collection))
317 #else
318 KAEvent* newev = new KAEvent(events[i]);
319 if (!cal->addEvent(newev, msgParent, false, resource))
320 #endif
322 #ifndef USE_AKONADI
323 delete newev;
324 #endif
325 status = UPDATE_ERROR;
326 ++warnErr;
327 continue;
329 #ifndef USE_AKONADI
330 events[i] = *newev; // update event ID etc.
331 #endif
332 if (allowKOrgUpdate && newev->copyToKOrganizer())
334 UpdateStatus st = sendToKOrganizer(newev); // tell KOrganizer to show the event
335 if (st != UPDATE_OK)
337 ++warnKOrg;
338 if (st > status)
339 status = st;
343 #ifndef USE_AKONADI
344 // Update the window lists, but not yet which item is selected
345 EventListModel::alarms()->addEvent(newev);
346 // selectID = newev->id();
347 #endif
349 if (warnErr == events.count())
350 status = UPDATE_FAILED;
351 else if (!cal->save())
353 status = SAVE_FAILED;
354 warnErr = 0; // everything failed
358 if (status != UPDATE_OK && msgParent)
359 displayUpdateError(msgParent, status, ERR_ADD, (warnErr ? warnErr : events.count()), warnKOrg, showKOrgErr);
360 return status;
363 /******************************************************************************
364 * Save the event in the archived calendar and adjust every main window instance.
365 * The event's ID is changed to an archived ID if necessary.
367 #ifdef USE_AKONADI
368 bool addArchivedEvent(KAEvent& event, Collection* collection)
369 #else
370 bool addArchivedEvent(KAEvent& event, AlarmResource* resource)
371 #endif
373 kDebug() << event.id();
374 QString oldid = event.id();
375 bool archiving = (event.category() == CalEvent::ACTIVE);
376 if (archiving && !Preferences::archivedKeepDays())
377 return false; // expired alarms aren't being kept
378 AlarmCalendar* cal = AlarmCalendar::resources();
379 #ifdef USE_AKONADI
380 KAEvent newevent(event);
381 newevent.setItemId(-1); // invalidate the Akonadi item ID since it's a new item
382 KAEvent* const newev = &newevent;
383 #else
384 KAEvent* newev = new KAEvent(event);
385 #endif
386 if (archiving)
388 newev->setCategory(CalEvent::ARCHIVED); // this changes the event ID
389 newev->setCreatedDateTime(KDateTime::currentUtcDateTime()); // time stamp to control purging
391 // Note that archived resources are automatically saved after changes are made
392 #ifdef USE_AKONADI
393 if (!cal->addEvent(newevent, 0, false, collection))
394 return false;
395 #else
396 if (!cal->addEvent(newev, 0, false, resource))
398 delete newev; // failed to add to calendar - leave event in its original state
399 return false;
401 #endif
402 event = *newev; // update event ID etc.
404 #ifndef USE_AKONADI
405 // Update window lists.
406 // Note: updateEvent() is not used here since that doesn't trigger refiltering
407 // of the alarm list, resulting in the archived event still remaining visible
408 // even if archived events are supposed to be hidden.
409 if (archiving)
410 EventListModel::alarms()->removeEvent(oldid);
411 EventListModel::alarms()->addEvent(newev);
412 #endif
413 return true;
416 /******************************************************************************
417 * Add a new template.
418 * Save it in the calendar file and add it to every template list view.
419 * 'event' is updated with the actual event ID.
420 * Parameters: promptParent = parent widget for any calendar selection prompt.
422 #ifdef USE_AKONADI
423 UpdateStatus addTemplate(KAEvent& event, Collection* collection, QWidget* msgParent)
424 #else
425 UpdateStatus addTemplate(KAEvent& event, AlarmResource* resource, QWidget* msgParent)
426 #endif
428 kDebug() << event.id();
429 UpdateStatus status = UPDATE_OK;
431 // Add the template to the calendar file
432 AlarmCalendar* cal = AlarmCalendar::resources();
433 #ifdef USE_AKONADI
434 KAEvent newev(event);
435 if (!cal->addEvent(newev, msgParent, false, collection))
436 status = UPDATE_FAILED;
437 #else
438 KAEvent* newev = new KAEvent(event);
439 if (!cal->addEvent(newev, msgParent, false, resource))
441 delete newev;
442 status = UPDATE_FAILED;
444 #endif
445 else
447 #ifdef USE_AKONADI
448 event = newev; // update event ID etc.
449 #else
450 event = *newev; // update event ID etc.
451 #endif
452 if (!cal->save())
453 status = SAVE_FAILED;
454 else
456 #ifndef USE_AKONADI
457 // Update the window lists
458 EventListModel::templates()->addEvent(newev);
459 #endif
460 return UPDATE_OK;
464 if (msgParent)
465 displayUpdateError(msgParent, status, ERR_TEMPLATE, 1);
466 return status;
469 /******************************************************************************
470 * Modify an active (non-archived) alarm in the calendar file and in every main
471 * window instance.
472 * The new event must have a different event ID from the old one.
474 UpdateStatus modifyEvent(KAEvent& oldEvent, KAEvent& newEvent, QWidget* msgParent, bool showKOrgErr)
476 kDebug() << oldEvent.id();
478 UpdateStatus status = UPDATE_OK;
479 if (!newEvent.isValid())
481 deleteEvent(oldEvent, true);
482 status = UPDATE_FAILED;
484 else
486 #ifdef USE_AKONADI
487 EventId oldId(oldEvent);
488 #else
489 QString oldId = oldEvent.id();
490 #endif
491 if (oldEvent.copyToKOrganizer())
493 // Tell KOrganizer to delete its old event.
494 // But ignore errors, because the user could have manually
495 // deleted it since KAlarm asked KOrganizer to set it up.
496 #ifdef USE_AKONADI
497 deleteFromKOrganizer(oldId.eventId());
498 #else
499 deleteFromKOrganizer(oldId);
500 #endif
502 #ifdef USE_AKONADI
503 // Update the event in the calendar file, and get the new event ID
504 AlarmCalendar* cal = AlarmCalendar::resources();
505 if (!cal->modifyEvent(oldId, newEvent))
506 status = UPDATE_FAILED;
507 #else
508 // Delete from the window lists to prevent the event's invalid
509 // pointer being accessed.
510 EventListModel::alarms()->removeEvent(oldId);
512 // Update the event in the calendar file, and get the new event ID
513 KAEvent* newev = new KAEvent(newEvent);
514 AlarmCalendar* cal = AlarmCalendar::resources();
515 if (!cal->modifyEvent(oldId, newev))
517 delete newev;
518 status = UPDATE_FAILED;
520 #endif
521 else
523 #ifndef USE_AKONADI
524 newEvent = *newev;
525 #endif
526 if (!cal->save())
527 status = SAVE_FAILED;
528 if (status == UPDATE_OK)
530 if (newEvent.copyToKOrganizer())
532 UpdateStatus st = sendToKOrganizer(&newEvent); // tell KOrganizer to show the new event
533 if (st > status)
534 status = st;
537 // Remove "Don't show error messages again" for the old alarm
538 setDontShowErrors(oldId);
540 #ifndef USE_AKONADI
541 // Update the window lists
542 EventListModel::alarms()->addEvent(newev);
543 #endif
548 if (status != UPDATE_OK && msgParent)
549 displayUpdateError(msgParent, status, ERR_MODIFY, 1, 1, showKOrgErr);
550 return status;
553 /******************************************************************************
554 * Update an active (non-archived) alarm from the calendar file and from every
555 * main window instance.
556 * The new event will have the same event ID as the old one.
557 * The event is not updated in KOrganizer, since this function is called when an
558 * existing alarm is rescheduled (due to recurrence or deferral).
560 UpdateStatus updateEvent(KAEvent& event, QWidget* msgParent, bool archiveOnDelete)
562 kDebug() << event.id();
564 if (!event.isValid())
565 deleteEvent(event, archiveOnDelete);
566 else
568 // Update the event in the calendar file.
569 AlarmCalendar* cal = AlarmCalendar::resources();
570 #ifdef USE_AKONADI
571 cal->updateEvent(event);
572 #else
573 KAEvent* newEvent = cal->updateEvent(event);
574 #endif
575 if (!cal->save())
577 if (msgParent)
578 displayUpdateError(msgParent, SAVE_FAILED, ERR_ADD, 1);
579 return SAVE_FAILED;
582 #ifndef USE_AKONADI
583 // Update the window lists
584 EventListModel::alarms()->updateEvent(newEvent);
585 #endif
587 return UPDATE_OK;
590 /******************************************************************************
591 * Update a template in the calendar file and in every template list view.
592 * If 'selectionView' is non-null, the selection highlight is moved to the
593 * updated event in that listView instance.
595 UpdateStatus updateTemplate(KAEvent& event, QWidget* msgParent)
597 AlarmCalendar* cal = AlarmCalendar::resources();
598 KAEvent* newEvent = cal->updateEvent(event);
599 UpdateStatus status = UPDATE_OK;
600 if (!newEvent)
601 status = UPDATE_FAILED;
602 else if (!cal->save())
603 status = SAVE_FAILED;
604 if (status != UPDATE_OK)
606 if (msgParent)
607 displayUpdateError(msgParent, SAVE_FAILED, ERR_TEMPLATE, 1);
608 return status;
611 #ifndef USE_AKONADI
612 EventListModel::templates()->updateEvent(newEvent);
613 #endif
614 return UPDATE_OK;
617 /******************************************************************************
618 * Delete alarms from the calendar file and from every main window instance.
619 * If the events are archived, the events' IDs are changed to archived IDs if necessary.
621 UpdateStatus deleteEvent(KAEvent& event, bool archive, QWidget* msgParent, bool showKOrgErr)
623 #ifdef USE_AKONADI
624 QVector<KAEvent> events(1, event);
625 #else
626 KAEvent::List events;
627 events += &event;
628 #endif
629 return deleteEvents(events, archive, msgParent, showKOrgErr);
632 #ifdef USE_AKONADI
633 UpdateStatus deleteEvents(QVector<KAEvent>& events, bool archive, QWidget* msgParent, bool showKOrgErr)
634 #else
635 UpdateStatus deleteEvents(KAEvent::List& events, bool archive, QWidget* msgParent, bool showKOrgErr)
636 #endif
638 kDebug() << events.count();
639 if (events.isEmpty())
640 return UPDATE_OK;
641 int warnErr = 0;
642 int warnKOrg = 0;
643 UpdateStatus status = UPDATE_OK;
644 AlarmCalendar* cal = AlarmCalendar::resources();
645 bool deleteWakeFromSuspendAlarm = false;
646 QString wakeFromSuspendId = checkRtcWakeConfig().value(0);
647 for (int i = 0, end = events.count(); i < end; ++i)
649 // Save the event details in the calendar file, and get the new event ID
650 #ifdef USE_AKONADI
651 KAEvent* event = &events[i];
652 #else
653 KAEvent* event = events[i];
654 #endif
655 QString id = event->id();
657 #ifndef USE_AKONADI
658 // Update the window lists and clear stored command errors
659 EventListModel::alarms()->removeEvent(id);
660 event->setCommandError(KAEvent::CMD_NO_ERROR);
661 #endif
663 // Delete the event from the calendar file
664 if (event->category() != CalEvent::ARCHIVED)
666 if (event->copyToKOrganizer())
668 // The event was shown in KOrganizer, so tell KOrganizer to
669 // delete it. But ignore errors, because the user could have
670 // manually deleted it from KOrganizer since it was set up.
671 UpdateStatus st = deleteFromKOrganizer(id);
672 if (st != UPDATE_OK)
674 ++warnKOrg;
675 if (st > status)
676 status = st;
679 if (archive && event->toBeArchived())
681 KAEvent ev(*event);
682 addArchivedEvent(ev); // this changes the event ID to an archived ID
685 #ifdef USE_AKONADI
686 if (!cal->deleteEvent(*event, false)) // don't save calendar after deleting
687 #else
688 if (!cal->deleteEvent(id, false)) // don't save calendar after deleting
689 #endif
691 status = UPDATE_ERROR;
692 ++warnErr;
695 if (id == wakeFromSuspendId)
696 deleteWakeFromSuspendAlarm = true;
698 // Remove "Don't show error messages again" for this alarm
699 #ifdef USE_AKONADI
700 setDontShowErrors(EventId(*event));
701 #else
702 setDontShowErrors(id);
703 #endif
706 if (warnErr == events.count())
707 status = UPDATE_FAILED;
708 else if (!cal->save()) // save the calendars now
710 status = SAVE_FAILED;
711 warnErr = events.count();
713 if (status != UPDATE_OK && msgParent)
714 displayUpdateError(msgParent, status, ERR_DELETE, warnErr, warnKOrg, showKOrgErr);
716 // Remove any wake-from-suspend scheduled for a deleted alarm
717 if (deleteWakeFromSuspendAlarm && !wakeFromSuspendId.isEmpty())
718 cancelRtcWake(msgParent, wakeFromSuspendId);
720 return status;
723 /******************************************************************************
724 * Delete templates from the calendar file and from every template list view.
726 #ifdef USE_AKONADI
727 UpdateStatus deleteTemplates(const KAEvent::List& events, QWidget* msgParent)
728 #else
729 UpdateStatus deleteTemplates(const QStringList& eventIDs, QWidget* msgParent)
730 #endif
732 #ifdef USE_AKONADI
733 int count = events.count();
734 #else
735 int count = eventIDs.count();
736 #endif
737 kDebug() << count;
738 if (!count)
739 return UPDATE_OK;
740 int warnErr = 0;
741 UpdateStatus status = UPDATE_OK;
742 AlarmCalendar* cal = AlarmCalendar::resources();
743 for (int i = 0, end = count; i < end; ++i)
745 // Update the window lists
746 #ifndef USE_AKONADI
747 QString id = eventIDs[i];
748 EventListModel::templates()->removeEvent(id);
749 #endif
751 // Delete the template from the calendar file
752 AlarmCalendar* cal = AlarmCalendar::resources();
753 #ifdef USE_AKONADI
754 if (!cal->deleteEvent(*events[i], false)) // don't save calendar after deleting
755 #else
756 if (!cal->deleteEvent(id, false)) // don't save calendar after deleting
757 #endif
759 status = UPDATE_ERROR;
760 ++warnErr;
764 if (warnErr == count)
765 status = UPDATE_FAILED;
766 else if (!cal->save()) // save the calendars now
768 status = SAVE_FAILED;
769 warnErr = count;
771 if (status != UPDATE_OK && msgParent)
772 displayUpdateError(msgParent, status, ERR_TEMPLATE, warnErr);
773 return status;
776 /******************************************************************************
777 * Delete an alarm from the display calendar.
779 void deleteDisplayEvent(const QString& eventID)
781 kDebug() << eventID;
782 AlarmCalendar* cal = AlarmCalendar::displayCalendarOpen();
783 if (cal)
784 #ifdef USE_AKONADI
785 cal->deleteDisplayEvent(eventID, true); // save calendar after deleting
786 #else
787 cal->deleteEvent(eventID, true); // save calendar after deleting
788 #endif
791 /******************************************************************************
792 * Undelete archived alarms, and update every main window instance.
793 * The archive bit is set to ensure that they get re-archived if deleted again.
794 * 'ineligibleIDs' is filled in with the IDs of any ineligible events.
796 #ifdef USE_AKONADI
797 UpdateStatus reactivateEvent(KAEvent& event, Collection* calendar, QWidget* msgParent, bool showKOrgErr)
798 #else
799 UpdateStatus reactivateEvent(KAEvent& event, AlarmResource* calendar, QWidget* msgParent, bool showKOrgErr)
800 #endif
802 #ifdef USE_AKONADI
803 QVector<EventId> ids;
804 QVector<KAEvent> events(1, event);
805 #else
806 QStringList ids;
807 KAEvent::List events;
808 events += &event;
809 #endif
810 return reactivateEvents(events, ids, calendar, msgParent, showKOrgErr);
813 #ifdef USE_AKONADI
814 UpdateStatus reactivateEvents(QVector<KAEvent>& events, QVector<EventId>& ineligibleIDs, Collection* col, QWidget* msgParent, bool showKOrgErr)
815 #else
816 UpdateStatus reactivateEvents(KAEvent::List& events, QStringList& ineligibleIDs, AlarmResource* resource, QWidget* msgParent, bool showKOrgErr)
817 #endif
819 kDebug() << events.count();
820 ineligibleIDs.clear();
821 if (events.isEmpty())
822 return UPDATE_OK;
823 int warnErr = 0;
824 int warnKOrg = 0;
825 UpdateStatus status = UPDATE_OK;
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
839 kDebug() << "No calendar";
840 status = UPDATE_FAILED;
841 warnErr = events.count();
843 else
845 QString selectID;
846 int count = 0;
847 AlarmCalendar* cal = AlarmCalendar::resources();
848 KDateTime now = KDateTime::currentUtcDateTime();
849 for (int i = 0, end = events.count(); i < end; ++i)
851 // Delete the event from the archived resource
852 #ifdef USE_AKONADI
853 KAEvent* event = &events[i];
854 #else
855 KAEvent* event = events[i];
856 #endif
857 if (event->category() != CalEvent::ARCHIVED
858 || !event->occursAfter(now, true))
860 #ifdef USE_AKONADI
861 ineligibleIDs += EventId(*event);
862 #else
863 ineligibleIDs += event->id();
864 #endif
865 continue;
867 ++count;
869 #ifdef USE_AKONADI
870 KAEvent newevent(*event);
871 KAEvent* const newev = &newevent;
872 #else
873 KAEvent* newev = new KAEvent(*event);
874 QString oldid = event->id();
875 #endif
876 newev->setCategory(CalEvent::ACTIVE); // this changes the event ID
877 if (newev->recurs() || newev->repetition())
878 newev->setNextOccurrence(now); // skip any recurrences in the past
879 newev->setArchive(); // ensure that it gets re-archived if it is deleted
881 // Save the event details in the calendar file.
882 // This converts the event ID.
883 #ifdef USE_AKONADI
884 if (!cal->addEvent(newevent, msgParent, true, &collection))
885 #else
886 if (!cal->addEvent(newev, msgParent, true, resource))
887 #endif
889 #ifndef USE_AKONADI
890 delete newev;
891 #endif
892 status = UPDATE_ERROR;
893 ++warnErr;
894 continue;
896 if (newev->copyToKOrganizer())
898 UpdateStatus st = sendToKOrganizer(newev); // tell KOrganizer to show the event
899 if (st != UPDATE_OK)
901 ++warnKOrg;
902 if (st > status)
903 status = st;
907 #ifndef USE_AKONADI
908 // Update the window lists
909 EventListModel::alarms()->updateEvent(oldid, newev);
910 // selectID = newev->id();
911 #endif
913 #ifdef USE_AKONADI
914 if (cal->event(EventId(*event)) // no error if event doesn't exist in archived resource
915 && !cal->deleteEvent(*event, false)) // don't save calendar after deleting
916 #else
917 if (cal->event(oldid) // no error if event doesn't exist in archived resource
918 && !cal->deleteEvent(oldid, false)) // don't save calendar after deleting
919 #endif
921 status = UPDATE_ERROR;
922 ++warnErr;
924 #ifdef USE_AKONADI
925 events[i] = newevent;
926 #else
927 events[i] = newev;
928 #endif
931 if (warnErr == count)
932 status = UPDATE_FAILED;
933 // Save the calendars, even if all events failed, since more than one calendar was updated
934 if (!cal->save() && status != UPDATE_FAILED)
936 status = SAVE_FAILED;
937 warnErr = count;
940 if (status != UPDATE_OK && msgParent)
941 displayUpdateError(msgParent, status, ERR_REACTIVATE, warnErr, warnKOrg, showKOrgErr);
942 return status;
945 /******************************************************************************
946 * Enable or disable alarms in the calendar file and in every main window instance.
947 * The new events will have the same event IDs as the old ones.
949 #ifdef USE_AKONADI
950 UpdateStatus enableEvents(QVector<KAEvent>& events, bool enable, QWidget* msgParent)
951 #else
952 UpdateStatus enableEvents(KAEvent::List& events, bool enable, QWidget* msgParent)
953 #endif
955 kDebug() << events.count();
956 if (events.isEmpty())
957 return UPDATE_OK;
958 UpdateStatus status = UPDATE_OK;
959 AlarmCalendar* cal = AlarmCalendar::resources();
960 bool deleteWakeFromSuspendAlarm = false;
961 QString wakeFromSuspendId = checkRtcWakeConfig().value(0);
962 for (int i = 0, end = events.count(); i < end; ++i)
964 #ifdef USE_AKONADI
965 KAEvent* event = &events[i];
966 #else
967 KAEvent* event = events[i];
968 #endif
969 if (event->category() == CalEvent::ACTIVE
970 && enable != event->enabled())
972 event->setEnabled(enable);
974 if (!enable && event->id() == wakeFromSuspendId)
975 deleteWakeFromSuspendAlarm = true;
977 // Update the event in the calendar file
978 KAEvent* newev = cal->updateEvent(event);
979 if (!newev)
980 kError() << "Error updating event in calendar:" << event->id();
981 else
983 cal->disabledChanged(newev);
985 // If we're disabling a display alarm, close any message window
986 if (!enable && (event->actionTypes() & KAEvent::ACT_DISPLAY))
988 #ifdef USE_AKONADI
989 MessageWin* win = MessageWin::findEvent(EventId(*event));
990 #else
991 MessageWin* win = MessageWin::findEvent(event->id());
992 #endif
993 delete win;
996 #ifndef USE_AKONADI
997 // Update the window lists
998 EventListModel::alarms()->updateEvent(newev);
999 #endif
1004 if (!cal->save())
1005 status = SAVE_FAILED;
1006 if (status != UPDATE_OK && msgParent)
1007 displayUpdateError(msgParent, status, ERR_ADD, events.count(), 0);
1009 // Remove any wake-from-suspend scheduled for a disabled alarm
1010 if (deleteWakeFromSuspendAlarm && !wakeFromSuspendId.isEmpty())
1011 cancelRtcWake(msgParent, wakeFromSuspendId);
1013 return status;
1016 /******************************************************************************
1017 * This method must only be called from the main KAlarm queue processing loop,
1018 * to prevent asynchronous calendar operations interfering with one another.
1020 * Purge all archived events from the default archived alarm resource whose end
1021 * time is longer ago than 'purgeDays'. All events are deleted if 'purgeDays' is
1022 * zero.
1024 void purgeArchive(int purgeDays)
1026 if (purgeDays < 0)
1027 return;
1028 kDebug() << purgeDays;
1029 QDate cutoff = KDateTime::currentLocalDate().addDays(-purgeDays);
1030 #ifdef USE_AKONADI
1031 Collection collection = CollectionControlModel::getStandard(CalEvent::ARCHIVED);
1032 if (!collection.isValid())
1033 return;
1034 KAEvent::List events = AlarmCalendar::resources()->events(collection);
1035 for (int i = 0; i < events.count(); )
1037 if (purgeDays && events[i]->createdDateTime().date() >= cutoff)
1038 events.remove(i);
1039 else
1040 ++i;
1042 #else
1043 AlarmResource* resource = AlarmResources::instance()->getStandardResource(CalEvent::ARCHIVED);
1044 if (!resource)
1045 return;
1046 KAEvent::List events = AlarmCalendar::resources()->events(resource);
1047 for (int i = 0; i < events.count(); )
1049 KAEvent* event = events[i];
1050 Incidence* kcalIncidence = resource->incidence(event->id());
1051 if (purgeDays && kcalIncidence && kcalIncidence->created().date() >= cutoff)
1052 events.remove(i);
1053 else
1054 EventListModel::alarms()->removeEvent(events[i++]); // update the window lists
1056 #endif
1057 if (!events.isEmpty())
1058 AlarmCalendar::resources()->purgeEvents(events); // delete the events and save the calendar
1061 #ifdef USE_AKONADI
1062 /******************************************************************************
1063 * Display an error message about an error when saving an event.
1064 * If 'model' is non-null, the AlarmListModel* which it points to is used; if
1065 * that is null, it is created.
1067 QVector<KAEvent> getSortedActiveEvents(QObject* parent, AlarmListModel** model)
1069 AlarmListModel* mdl = 0;
1070 if (!model)
1071 model = &mdl;
1072 if (!*model)
1074 *model = new AlarmListModel(parent);
1075 (*model)->setEventTypeFilter(CalEvent::ACTIVE);
1076 (*model)->sort(AlarmListModel::TimeColumn);
1078 QVector<KAEvent> result;
1079 for (int i = 0, count = (*model)->rowCount(); i < count; ++i)
1081 KAEvent event = (*model)->event(i);
1082 if (event.enabled() && !event.expired())
1083 result += event;
1085 return result;
1087 #else
1088 /******************************************************************************
1089 * Display an error message about an error when saving an event.
1091 KAEvent::List getSortedActiveEvents(const KDateTime& startTime, const KDateTime& endTime)
1093 KAEvent::List events;
1094 if (endTime.isValid())
1095 events = AlarmCalendar::resources()->events(startTime, endTime, CalEvent::ACTIVE);
1096 else
1097 events = AlarmCalendar::resources()->events(CalEvent::ACTIVE);
1098 KAEvent::List result;
1099 for (i = 0, count = events.count(); i < count; ++i)
1101 if (event->enabled() && !event->expired())
1102 result += event;
1104 return result;
1106 #endif
1108 /******************************************************************************
1109 * Display an error message about an error when saving an event.
1111 void displayUpdateError(QWidget* parent, UpdateStatus status, UpdateError code, int nAlarms, int nKOrgAlarms, bool showKOrgError)
1113 QString errmsg;
1114 if (status > UPDATE_KORG_ERR)
1116 switch (code)
1118 case ERR_ADD:
1119 case ERR_MODIFY:
1120 errmsg = (nAlarms > 1) ? i18nc("@info", "Error saving alarms")
1121 : i18nc("@info", "Error saving alarm");
1122 break;
1123 case ERR_DELETE:
1124 errmsg = (nAlarms > 1) ? i18nc("@info", "Error deleting alarms")
1125 : i18nc("@info", "Error deleting alarm");
1126 break;
1127 case ERR_REACTIVATE:
1128 errmsg = (nAlarms > 1) ? i18nc("@info", "Error saving reactivated alarms")
1129 : i18nc("@info", "Error saving reactivated alarm");
1130 break;
1131 case ERR_TEMPLATE:
1132 errmsg = (nAlarms > 1) ? i18nc("@info", "Error saving alarm templates")
1133 : i18nc("@info", "Error saving alarm template");
1134 break;
1136 KAMessageBox::error(parent, errmsg);
1138 else if (showKOrgError)
1139 displayKOrgUpdateError(parent, code, status, nKOrgAlarms);
1142 /******************************************************************************
1143 * Display an error message corresponding to a specified alarm update error code.
1145 void displayKOrgUpdateError(QWidget* parent, UpdateError code, UpdateStatus korgError, int nAlarms)
1147 QString errmsg;
1148 switch (code)
1150 case ERR_ADD:
1151 case ERR_REACTIVATE:
1152 errmsg = (nAlarms > 1) ? i18nc("@info", "Unable to show alarms in KOrganizer")
1153 : i18nc("@info", "Unable to show alarm in KOrganizer");
1154 break;
1155 case ERR_MODIFY:
1156 errmsg = i18nc("@info", "Unable to update alarm in KOrganizer");
1157 break;
1158 case ERR_DELETE:
1159 errmsg = (nAlarms > 1) ? i18nc("@info", "Unable to delete alarms from KOrganizer")
1160 : i18nc("@info", "Unable to delete alarm from KOrganizer");
1161 break;
1162 case ERR_TEMPLATE:
1163 return;
1165 QString msg;
1166 if (korgError == UPDATE_KORG_ERRSTART)
1167 msg = i18nc("@info", "<para>%1</para><para>(KOrganizer not fully started)</para>", errmsg);
1168 else if (korgError == UPDATE_KORG_ERR)
1169 msg = i18nc("@info", "<para>%1</para><para>(Error communicating with KOrganizer)</para>", errmsg);
1170 else
1171 msg = errmsg;
1172 KAMessageBox::error(parent, msg);
1175 /******************************************************************************
1176 * Execute a New Alarm dialog for the specified alarm type.
1178 void editNewAlarm(EditAlarmDlg::Type type, QWidget* parent)
1180 execNewAlarmDlg(EditAlarmDlg::create(false, type, parent));
1183 /******************************************************************************
1184 * Execute a New Alarm dialog for the specified alarm type.
1186 void editNewAlarm(KAEvent::SubAction action, QWidget* parent, const AlarmText* text)
1188 bool setAction = false;
1189 EditAlarmDlg::Type type;
1190 switch (action)
1192 case KAEvent::MESSAGE:
1193 case KAEvent::FILE:
1194 type = EditAlarmDlg::DISPLAY;
1195 setAction = true;
1196 break;
1197 case KAEvent::COMMAND:
1198 type = EditAlarmDlg::COMMAND;
1199 break;
1200 case KAEvent::EMAIL:
1201 type = EditAlarmDlg::EMAIL;
1202 break;
1203 case KAEvent::AUDIO:
1204 type = EditAlarmDlg::AUDIO;
1205 break;
1206 default:
1207 return;
1209 EditAlarmDlg* editDlg = EditAlarmDlg::create(false, type, parent);
1210 if (setAction || text)
1211 editDlg->setAction(action, *text);
1212 execNewAlarmDlg(editDlg);
1215 /******************************************************************************
1216 * Execute a New Alarm dialog, optionally either presetting it to the supplied
1217 * event, or setting the action and text.
1219 void editNewAlarm(const KAEvent* preset, QWidget* parent)
1221 execNewAlarmDlg(EditAlarmDlg::create(false, preset, true, parent));
1224 /******************************************************************************
1225 * Common code for editNewAlarm() variants.
1227 void execNewAlarmDlg(EditAlarmDlg* editDlg)
1229 // Create a PrivateNewAlarmDlg parented by editDlg.
1230 // It will be deleted when editDlg is closed.
1231 new PrivateNewAlarmDlg(editDlg);
1232 editDlg->show();
1233 editDlg->raise();
1234 editDlg->activateWindow();
1237 PrivateNewAlarmDlg::PrivateNewAlarmDlg(EditAlarmDlg* dlg)
1238 : QObject(dlg)
1240 connect(dlg, SIGNAL(accepted()), SLOT(okClicked()));
1243 /******************************************************************************
1244 * Called when the dialogue is accepted (e.g. by clicking the OK button).
1245 * Creates the event specified in the instance's dialogue.
1247 void PrivateNewAlarmDlg::okClicked()
1249 accept(static_cast<EditAlarmDlg*>(parent()));
1252 /******************************************************************************
1253 * Creates the event specified in a given dialogue.
1255 void PrivateNewAlarmDlg::accept(EditAlarmDlg* editDlg)
1257 KAEvent event;
1258 #ifdef USE_AKONADI
1259 Collection calendar;
1260 #else
1261 AlarmResource* calendar;
1262 #endif
1263 editDlg->getEvent(event, calendar);
1265 // Add the alarm to the displayed lists and to the calendar file
1266 #ifdef USE_AKONADI
1267 UpdateStatus status = addEvent(event, &calendar, editDlg);
1268 #else
1269 UpdateStatus status = addEvent(event, calendar, editDlg);
1270 #endif
1271 switch (status)
1273 case UPDATE_FAILED:
1274 return;
1275 case UPDATE_KORG_ERR:
1276 case UPDATE_KORG_ERRSTART:
1277 case UPDATE_KORG_FUNCERR:
1278 displayKOrgUpdateError(editDlg, ERR_ADD, status, 1);
1279 break;
1280 default:
1281 break;
1283 Undo::saveAdd(event, calendar);
1285 outputAlarmWarnings(editDlg, &event);
1288 /******************************************************************************
1289 * Display the alarm edit dialog to edit a new alarm, preset with a template.
1291 bool editNewAlarm(const QString& templateName, QWidget* parent)
1293 if (!templateName.isEmpty())
1295 KAEvent* templateEvent = AlarmCalendar::resources()->templateEvent(templateName);
1296 if (templateEvent->isValid())
1298 editNewAlarm(templateEvent, parent);
1299 return true;
1301 kWarning() << templateName << ": template not found";
1303 return false;
1306 /******************************************************************************
1307 * Create a new template.
1309 void editNewTemplate(EditAlarmDlg::Type type, QWidget* parent)
1311 ::editNewTemplate(type, 0, parent);
1314 /******************************************************************************
1315 * Create a new template, based on an existing event or template.
1317 void editNewTemplate(const KAEvent* preset, QWidget* parent)
1319 ::editNewTemplate(EditAlarmDlg::Type(0), preset, parent);
1322 /******************************************************************************
1323 * Check the config as to whether there is a wake-on-suspend alarm pending, and
1324 * if so, delete it from the config if it has expired.
1325 * If 'checkExists' is true, the config entry will only be returned if the
1326 * event exists.
1327 * Reply = config entry: [0] = event's collection ID (Akonadi only),
1328 * [1] = event ID,
1329 * [2] = trigger time (time_t).
1330 * = empty list if none or expired.
1332 QStringList checkRtcWakeConfig(bool checkEventExists)
1334 KConfigGroup config(KGlobal::config(), "General");
1335 QStringList params = config.readEntry("RtcWake", QStringList());
1336 #ifdef USE_AKONADI
1337 if (params.count() == 3 && params[2].toUInt() > KDateTime::currentUtcDateTime().toTime_t())
1338 #else
1339 if (params.count() == 2 && params[1].toUInt() > KDateTime::currentUtcDateTime().toTime_t())
1340 #endif
1342 #ifdef USE_AKONADI
1343 if (checkEventExists && !AlarmCalendar::getEvent(EventId(params[0].toLongLong(), params[1])))
1344 #else
1345 if (checkEventExists && !AlarmCalendar::getEvent(params[0]))
1346 #endif
1347 return QStringList();
1348 return params; // config entry is valid
1350 if (!params.isEmpty())
1352 config.deleteEntry("RtcWake"); // delete the expired config entry
1353 config.sync();
1355 return QStringList();
1358 /******************************************************************************
1359 * Delete any wake-on-suspend alarm from the config.
1361 void deleteRtcWakeConfig()
1363 KConfigGroup config(KGlobal::config(), "General");
1364 config.deleteEntry("RtcWake");
1365 config.sync();
1368 /******************************************************************************
1369 * Delete any wake-on-suspend alarm, optionally only for a specified event.
1371 void cancelRtcWake(QWidget* msgParent, const QString& eventId)
1373 QStringList wakeup = checkRtcWakeConfig();
1374 if (!wakeup.isEmpty() && (eventId.isEmpty() || wakeup[0] == eventId))
1376 Private::instance()->mMsgParent = msgParent ? msgParent : MainWindow::mainMainWindow();
1377 QTimer::singleShot(0, Private::instance(), SLOT(cancelRtcWake()));
1381 /******************************************************************************
1382 * Delete any wake-on-suspend alarm.
1384 void Private::cancelRtcWake()
1386 // setRtcWakeTime will only work with a parent window specified
1387 setRtcWakeTime(0, mMsgParent);
1388 deleteRtcWakeConfig();
1389 KAMessageBox::information(mMsgParent, i18nc("info", "The scheduled Wake from Suspend has been cancelled."));
1392 /******************************************************************************
1393 * Set the wakeup time for the system.
1394 * Set 'triggerTime' to zero to cancel the wakeup.
1395 * Reply = true if successful.
1397 bool setRtcWakeTime(unsigned triggerTime, QWidget* parent)
1399 QVariantMap args;
1400 args["time"] = triggerTime;
1401 KAuth::Action action("org.kde.kalarmrtcwake.settimer");
1402 action.setHelperID("org.kde.kalarmrtcwake");
1403 action.setParentWidget(parent);
1404 action.setArguments(args);
1405 KAuth::ActionReply reply = action.execute();
1406 if (reply.failed())
1408 QString errmsg = reply.errorDescription();
1409 kDebug() << "Error code=" << reply.errorCode() << errmsg;
1410 if (errmsg.isEmpty())
1412 int errcode = reply.errorCode();
1413 switch (reply.type())
1415 case KAuth::ActionReply::KAuthError:
1416 kDebug() << "Authorisation error:" << errcode;
1417 switch (errcode)
1419 case KAuth::ActionReply::AuthorizationDenied:
1420 case KAuth::ActionReply::UserCancelled:
1421 return false; // the user should already know about this
1422 default:
1423 break;
1425 break;
1426 case KAuth::ActionReply::HelperError:
1427 kDebug() << "Helper error:" << errcode;
1428 errcode += 100; // make code distinguishable from KAuthError type
1429 break;
1430 default:
1431 break;
1433 errmsg = i18nc("@info", "Error obtaining authorization (%1)", errcode);
1435 KAMessageBox::information(parent, errmsg);
1436 return false;
1438 return true;
1441 } // namespace KAlarm
1442 namespace
1445 /******************************************************************************
1446 * Create a new template.
1447 * 'preset' is non-null to base it on an existing event or template; otherwise,
1448 * the alarm type is set to 'type'.
1450 void editNewTemplate(EditAlarmDlg::Type type, const KAEvent* preset, QWidget* parent)
1452 #ifdef USE_AKONADI
1453 if (CollectionControlModel::enabledCollections(CalEvent::TEMPLATE, true).isEmpty())
1454 #else
1455 if (!AlarmResources::instance()->activeCount(CalEvent::TEMPLATE, true))
1456 #endif
1458 KAMessageBox::sorry(parent, i18nc("@info", "You must enable a template calendar to save the template in"));
1459 return;
1461 // Use AutoQPointer to guard against crash on application exit while
1462 // the dialogue is still open. It prevents double deletion (both on
1463 // deletion of parent, and on return from this function).
1464 AutoQPointer<EditAlarmDlg> editDlg;
1465 if (preset)
1466 editDlg = EditAlarmDlg::create(true, preset, true, parent);
1467 else
1468 editDlg = EditAlarmDlg::create(true, type, parent);
1469 if (editDlg->exec() == QDialog::Accepted)
1471 KAEvent event;
1472 #ifdef USE_AKONADI
1473 Akonadi::Collection calendar;
1474 #else
1475 AlarmResource* calendar;
1476 #endif
1477 editDlg->getEvent(event, calendar);
1479 // Add the template to the displayed lists and to the calendar file
1480 #ifdef USE_AKONADI
1481 KAlarm::addTemplate(event, &calendar, editDlg);
1482 #else
1483 KAlarm::addTemplate(event, calendar, editDlg);
1484 #endif
1485 Undo::saveAdd(event, calendar);
1489 } // namespace
1490 namespace KAlarm
1493 /******************************************************************************
1494 * Open the Edit Alarm dialog to edit the specified alarm.
1495 * If the alarm is read-only or archived, the dialog is opened read-only.
1497 void editAlarm(KAEvent* event, QWidget* parent)
1499 #ifdef USE_AKONADI
1500 if (event->expired() || AlarmCalendar::resources()->eventReadOnly(event->itemId()))
1501 #else
1502 if (event->expired() || AlarmCalendar::resources()->eventReadOnly(event->id()))
1503 #endif
1505 viewAlarm(event, parent);
1506 return;
1508 #ifdef USE_AKONADI
1509 EventId id(*event);
1510 #else
1511 QString id = event->id();
1512 #endif
1513 // Use AutoQPointer to guard against crash on application exit while
1514 // the dialogue is still open. It prevents double deletion (both on
1515 // deletion of parent, and on return from this function).
1516 AutoQPointer<EditAlarmDlg> editDlg = EditAlarmDlg::create(false, event, false, parent, EditAlarmDlg::RES_USE_EVENT_ID);
1517 if (editDlg->exec() == QDialog::Accepted)
1519 if (!AlarmCalendar::resources()->event(id))
1521 // Event has been deleted while the user was editing the alarm,
1522 // so treat it as a new alarm.
1523 PrivateNewAlarmDlg().accept(editDlg);
1524 return;
1526 KAEvent newEvent;
1527 #ifdef USE_AKONADI
1528 Collection calendar;
1529 #else
1530 AlarmResource* calendar;
1531 #endif
1532 bool changeDeferral = !editDlg->getEvent(newEvent, calendar);
1534 // Update the event in the displays and in the calendar file
1535 Undo::Event undo(*event, calendar);
1536 if (changeDeferral)
1538 // The only change has been to an existing deferral
1539 if (updateEvent(newEvent, editDlg, true) != UPDATE_OK) // keep the same event ID
1540 return; // failed to save event
1542 else
1544 UpdateStatus status = modifyEvent(*event, newEvent, editDlg);
1545 if (status != UPDATE_OK && status <= UPDATE_KORG_ERR)
1546 displayKOrgUpdateError(editDlg, ERR_MODIFY, status, 1);
1548 Undo::saveEdit(undo, newEvent);
1550 outputAlarmWarnings(editDlg, &newEvent);
1554 /******************************************************************************
1555 * Display the alarm edit dialog to edit the alarm with the specified ID.
1556 * An error occurs if the alarm is not found, if there is more than one alarm
1557 * with the same ID, or if it is read-only or expired.
1559 bool editAlarmById(const QString& eventID, QWidget* parent)
1561 #ifdef USE_AKONADI
1562 KAEvent::List events = AlarmCalendar::resources()->events(eventID);
1563 if (events.count() > 1)
1565 kWarning() << eventID << ": multiple events found";
1566 return false;
1568 if (events.isEmpty())
1569 #else
1570 KAEvent* event = AlarmCalendar::resources()->event(eventID);
1571 if (!event)
1572 #endif
1574 kError() << eventID << ": event ID not found";
1575 return false;
1577 #ifdef USE_AKONADI
1578 KAEvent* event = events[0];
1579 if (AlarmCalendar::resources()->eventReadOnly(event->itemId()))
1580 #else
1581 if (AlarmCalendar::resources()->eventReadOnly(eventID))
1582 #endif
1584 kError() << eventID << ": read-only";
1585 return false;
1587 switch (event->category())
1589 case CalEvent::ACTIVE:
1590 case CalEvent::TEMPLATE:
1591 break;
1592 default:
1593 kError() << eventID << ": event not active or template";
1594 return false;
1596 editAlarm(event, parent);
1597 return true;
1600 /******************************************************************************
1601 * Open the Edit Alarm dialog to edit the specified template.
1602 * If the template is read-only, the dialog is opened read-only.
1604 void editTemplate(KAEvent* event, QWidget* parent)
1606 #ifdef USE_AKONADI
1607 if (AlarmCalendar::resources()->eventReadOnly(event->itemId()))
1608 #else
1609 if (AlarmCalendar::resources()->eventReadOnly(event->id()))
1610 #endif
1612 // The template is read-only, so make the dialogue read-only.
1613 // Use AutoQPointer to guard against crash on application exit while
1614 // the dialogue is still open. It prevents double deletion (both on
1615 // deletion of parent, and on return from this function).
1616 AutoQPointer<EditAlarmDlg> editDlg = EditAlarmDlg::create(true, event, false, parent, EditAlarmDlg::RES_PROMPT, true);
1617 editDlg->exec();
1618 return;
1620 // Use AutoQPointer to guard against crash on application exit while
1621 // the dialogue is still open. It prevents double deletion (both on
1622 // deletion of parent, and on return from this function).
1623 AutoQPointer<EditAlarmDlg> editDlg = EditAlarmDlg::create(true, event, false, parent, EditAlarmDlg::RES_USE_EVENT_ID);
1624 if (editDlg->exec() == QDialog::Accepted)
1626 KAEvent newEvent;
1627 #ifdef USE_AKONADI
1628 Akonadi::Collection calendar;
1629 #else
1630 AlarmResource* calendar;
1631 #endif
1632 editDlg->getEvent(newEvent, calendar);
1633 QString id = event->id();
1634 newEvent.setEventId(id);
1636 // Update the event in the displays and in the calendar file
1637 Undo::Event undo(*event, calendar);
1638 updateTemplate(newEvent, editDlg);
1639 Undo::saveEdit(undo, newEvent);
1643 /******************************************************************************
1644 * Open the Edit Alarm dialog to view the specified alarm (read-only).
1646 void viewAlarm(const KAEvent* event, QWidget* parent)
1648 // Use AutoQPointer to guard against crash on application exit while
1649 // the dialogue is still open. It prevents double deletion (both on
1650 // deletion of parent, and on return from this function).
1651 AutoQPointer<EditAlarmDlg> editDlg = EditAlarmDlg::create(false, event, false, parent, EditAlarmDlg::RES_PROMPT, true);
1652 editDlg->exec();
1655 /******************************************************************************
1656 * Called when OK is clicked in the alarm edit dialog invoked by the Edit button
1657 * in an alarm message window.
1658 * Updates the alarm calendar and closes the dialog.
1660 #ifdef USE_AKONADI
1661 void updateEditedAlarm(EditAlarmDlg* editDlg, KAEvent& event, Collection& calendar)
1662 #else
1663 void updateEditedAlarm(EditAlarmDlg* editDlg, KAEvent& event, AlarmResource* calendar)
1664 #endif
1666 kDebug();
1667 KAEvent newEvent;
1668 #ifdef USE_AKONADI
1669 Akonadi::Collection cal;
1670 #else
1671 AlarmResource* cal;
1672 #endif
1673 editDlg->getEvent(newEvent, cal);
1675 // Update the displayed lists and the calendar file
1676 UpdateStatus status;
1677 #ifdef USE_AKONADI
1678 if (AlarmCalendar::resources()->event(EventId(event)))
1679 #else
1680 if (AlarmCalendar::resources()->event(event.id()))
1681 #endif
1683 // The old alarm hasn't expired yet, so replace it
1684 Undo::Event undo(event, calendar);
1685 status = modifyEvent(event, newEvent, editDlg);
1686 Undo::saveEdit(undo, newEvent);
1688 else
1690 // The old event has expired, so simply create a new one
1691 #ifdef USE_AKONADI
1692 status = addEvent(newEvent, &calendar, editDlg);
1693 #else
1694 status = addEvent(newEvent, calendar, editDlg);
1695 #endif
1696 Undo::saveAdd(newEvent, calendar);
1699 if (status != UPDATE_OK && status <= UPDATE_KORG_ERR)
1700 displayKOrgUpdateError(editDlg, ERR_MODIFY, status, 1);
1701 outputAlarmWarnings(editDlg, &newEvent);
1703 editDlg->close();
1706 /******************************************************************************
1707 * Returns a list of all alarm templates.
1708 * If shell commands are disabled, command alarm templates are omitted.
1710 KAEvent::List templateList()
1712 KAEvent::List templates;
1713 bool includeCmdAlarms = ShellProcess::authorised();
1714 KAEvent::List events = AlarmCalendar::resources()->events(CalEvent::TEMPLATE);
1715 for (int i = 0, end = events.count(); i < end; ++i)
1717 KAEvent* event = events[i];
1718 if (includeCmdAlarms || !(event->actionTypes() & KAEvent::ACT_COMMAND))
1719 templates.append(event);
1721 return templates;
1724 /******************************************************************************
1725 * To be called after an alarm has been edited.
1726 * Prompt the user to re-enable alarms if they are currently disabled, and if
1727 * it's an email alarm, warn if no 'From' email address is configured.
1729 void outputAlarmWarnings(QWidget* parent, const KAEvent* event)
1731 if (event && event->actionTypes() == KAEvent::ACT_EMAIL
1732 && Preferences::emailAddress().isEmpty())
1733 KAMessageBox::information(parent, i18nc("@info Please set the 'From' email address...",
1734 "<para>%1</para><para>Please set it in the Configuration dialog.</para>", KAMail::i18n_NeedFromEmailAddress()));
1736 if (!theApp()->alarmsEnabled())
1738 if (KAMessageBox::warningYesNo(parent, i18nc("@info", "<para>Alarms are currently disabled.</para><para>Do you want to enable alarms now?</para>"),
1739 QString(), KGuiItem(i18nc("@action:button", "Enable")), KGuiItem(i18nc("@action:button", "Keep Disabled")),
1740 QLatin1String("EditEnableAlarms"))
1741 == KMessageBox::Yes)
1742 theApp()->setAlarmsEnabled(true);
1746 /******************************************************************************
1747 * Reload the calendar.
1749 void refreshAlarms()
1751 kDebug();
1752 if (!refreshAlarmsQueued)
1754 refreshAlarmsQueued = true;
1755 theApp()->processQueue();
1759 /******************************************************************************
1760 * This method must only be called from the main KAlarm queue processing loop,
1761 * to prevent asynchronous calendar operations interfering with one another.
1763 * If refreshAlarms() has been called, reload the calendars.
1765 void refreshAlarmsIfQueued()
1767 if (refreshAlarmsQueued)
1769 kDebug();
1770 AlarmCalendar::resources()->reload();
1772 // Close any message windows for alarms which are now disabled
1773 KAEvent::List events = AlarmCalendar::resources()->events(CalEvent::ACTIVE);
1774 for (int i = 0, end = events.count(); i < end; ++i)
1776 KAEvent* event = events[i];
1777 if (!event->enabled() && (event->actionTypes() & KAEvent::ACT_DISPLAY))
1779 #ifdef USE_AKONADI
1780 MessageWin* win = MessageWin::findEvent(EventId(*event));
1781 #else
1782 MessageWin* win = MessageWin::findEvent(event->id());
1783 #endif
1784 delete win;
1788 MainWindow::refresh();
1789 refreshAlarmsQueued = false;
1793 /******************************************************************************
1794 * Start KMail if it isn't already running, optionally minimised.
1795 * Reply = reason for failure to run KMail (which may be the empty string)
1796 * = null string if success.
1798 QString runKMail(bool minimise)
1800 QDBusReply<bool> reply = QDBusConnection::sessionBus().interface()->isServiceRegistered(KMAIL_DBUS_SERVICE);
1801 if (!reply.isValid() || !reply.value())
1803 // Program is not already running, so start it
1804 QString errmsg;
1805 if (minimise && Private::startKMailMinimised())
1806 return QString();
1807 if (KToolInvocation::startServiceByDesktopName(QLatin1String("kmail"), QString(), &errmsg))
1809 kError() << "Couldn't start KMail (" << errmsg << ")";
1810 return i18nc("@info", "Unable to start <application>KMail</application><nl/>(<message>%1</message>)", errmsg);
1813 return QString();
1816 /******************************************************************************
1817 * Start KMail, minimised.
1818 * This code is taken from kstart in kdebase.
1820 bool Private::startKMailMinimised()
1822 #ifdef Q_WS_X11
1823 NETRootInfo i(QX11Info::display(), NET::Supported);
1824 if (i.isSupported(NET::WM2KDETemporaryRules))
1826 kDebug() << "using rules";
1827 KXMessages msg;
1828 QString message = "wmclass=kmail\nwmclassmatch=1\n" // 1 = exact match
1829 "wmclasscomplete=false\n"
1830 "minimize=true\nminimizerule=3\n"
1831 "type=" + QString().setNum(NET::Normal) + "\ntyperule=2";
1832 msg.broadcastMessage("_KDE_NET_WM_TEMPORARY_RULES", message, -1, false);
1833 qApp->flush();
1835 else
1837 // Connect to window add to get the NEW windows
1838 kDebug() << "connecting to window add";
1839 connect(KWindowSystem::self(), SIGNAL(windowAdded(WId)), instance(), SLOT(windowAdded(WId)));
1841 // Propagate the app startup notification info to the started app.
1842 // We are not using KApplication, so the env remained set.
1843 KStartupInfoId id = KStartupInfo::currentStartupIdEnv();
1844 KProcess* proc = new KProcess;
1845 (*proc) << "kmail";
1846 int pid = proc->startDetached();
1847 if (!pid)
1849 KStartupInfo::sendFinish(id); // failed to start
1850 return false;
1852 KStartupInfoData data;
1853 data.addPid(pid);
1854 data.setName("kmail");
1855 data.setBin("kmail");
1856 KStartupInfo::sendChange(id, data);
1857 return true;
1858 #else
1859 return false;
1860 #endif
1863 /******************************************************************************
1864 * Called when a window is created, to minimise it.
1865 * This code is taken from kstart in kdebase.
1867 void Private::windowAdded(WId w)
1869 #ifdef Q_WS_X11
1870 static const int SUPPORTED_TYPES = NET::NormalMask | NET::DesktopMask | NET::DockMask
1871 | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
1872 | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask;
1873 KWindowInfo kwinfo = KWindowSystem::windowInfo(w, NET::WMWindowType | NET::WMName);
1874 if (kwinfo.windowType(SUPPORTED_TYPES) == NET::TopMenu
1875 || kwinfo.windowType(SUPPORTED_TYPES) == NET::Toolbar
1876 || kwinfo.windowType(SUPPORTED_TYPES) == NET::Desktop)
1877 return; // always ignore these window types
1879 QX11Info qxinfo;
1880 XWithdrawWindow(QX11Info::display(), w, qxinfo.screen());
1881 QApplication::flush();
1883 NETWinInfo info(QX11Info::display(), w, QX11Info::appRootWindow(), NET::WMState);
1884 XWMHints* hints = XGetWMHints(QX11Info::display(), w);
1885 if (hints)
1887 hints->flags |= StateHint;
1888 hints->initial_state = IconicState;
1889 XSetWMHints(QX11Info::display(), w, hints);
1890 XFree(hints);
1892 info.setWindowType(NET::Normal);
1894 XSync(QX11Info::display(), False);
1895 XMapWindow(QX11Info::display(), w);
1896 XSync(QX11Info::display(), False);
1897 QApplication::flush();
1898 #endif
1901 /******************************************************************************
1902 * The "Don't show again" option for error messages is personal to the user on a
1903 * particular computer. For example, he may want to inhibit error messages only
1904 * on his laptop. So the status is not stored in the alarm calendar, but in the
1905 * user's local KAlarm data directory.
1906 ******************************************************************************/
1908 /******************************************************************************
1909 * Return the Don't-show-again error message tags set for a specified alarm ID.
1911 #ifdef USE_AKONADI
1912 QStringList dontShowErrors(const EventId& eventId)
1913 #else
1914 QStringList dontShowErrors(const QString& eventId)
1915 #endif
1917 if (eventId.isEmpty())
1918 return QStringList();
1919 KConfig config(KStandardDirs::locateLocal("appdata", ALARM_OPTS_FILE));
1920 KConfigGroup group(&config, DONT_SHOW_ERRORS_GROUP);
1921 #ifdef USE_AKONADI
1922 const QString id = QString("%1:%2").arg(eventId.collectionId()).arg(eventId.eventId());
1923 #else
1924 const QString id(eventId);
1925 #endif
1926 return group.readEntry(id, QStringList());
1929 /******************************************************************************
1930 * Check whether the specified Don't-show-again error message tag is set for an
1931 * alarm ID.
1933 #ifdef USE_AKONADI
1934 bool dontShowErrors(const EventId& eventId, const QString& tag)
1935 #else
1936 bool dontShowErrors(const QString& eventId, const QString& tag)
1937 #endif
1939 if (tag.isEmpty())
1940 return false;
1941 QStringList tags = dontShowErrors(eventId);
1942 return tags.indexOf(tag) >= 0;
1945 /******************************************************************************
1946 * Reset the Don't-show-again error message tags for an alarm ID.
1947 * If 'tags' is empty, the config entry is deleted.
1949 #ifdef USE_AKONADI
1950 void setDontShowErrors(const EventId& eventId, const QStringList& tags)
1951 #else
1952 void setDontShowErrors(const QString& eventId, const QStringList& tags)
1953 #endif
1955 if (eventId.isEmpty())
1956 return;
1957 KConfig config(KStandardDirs::locateLocal("appdata", ALARM_OPTS_FILE));
1958 KConfigGroup group(&config, DONT_SHOW_ERRORS_GROUP);
1959 #ifdef USE_AKONADI
1960 const QString id = QString("%1:%2").arg(eventId.collectionId()).arg(eventId.eventId());
1961 #else
1962 const QString id(eventId);
1963 #endif
1964 if (tags.isEmpty())
1965 group.deleteEntry(id);
1966 else
1967 group.writeEntry(id, tags);
1968 group.sync();
1971 /******************************************************************************
1972 * Set the specified Don't-show-again error message tag for an alarm ID.
1973 * Existing tags are unaffected.
1975 #ifdef USE_AKONADI
1976 void setDontShowErrors(const EventId& eventId, const QString& tag)
1977 #else
1978 void setDontShowErrors(const QString& eventId, const QString& tag)
1979 #endif
1981 if (eventId.isEmpty() || tag.isEmpty())
1982 return;
1983 KConfig config(KStandardDirs::locateLocal("appdata", ALARM_OPTS_FILE));
1984 KConfigGroup group(&config, DONT_SHOW_ERRORS_GROUP);
1985 #ifdef USE_AKONADI
1986 const QString id = QString("%1:%2").arg(eventId.collectionId()).arg(eventId.eventId());
1987 #else
1988 const QString id(eventId);
1989 #endif
1990 QStringList tags = group.readEntry(id, QStringList());
1991 if (tags.indexOf(tag) < 0)
1993 tags += tag;
1994 group.writeEntry(id, tags);
1995 group.sync();
1999 /******************************************************************************
2000 * Read the size for the specified window from the config file, for the
2001 * current screen resolution.
2002 * Reply = true if size set in the config file, in which case 'result' is set
2003 * = false if no size is set, in which case 'result' is unchanged.
2005 bool readConfigWindowSize(const char* window, QSize& result, int* splitterWidth)
2007 KConfigGroup config(KGlobal::config(), window);
2008 QWidget* desktop = KApplication::desktop();
2009 QSize s = QSize(config.readEntry(QString::fromLatin1("Width %1").arg(desktop->width()), (int)0),
2010 config.readEntry(QString::fromLatin1("Height %1").arg(desktop->height()), (int)0));
2011 if (s.isEmpty())
2012 return false;
2013 result = s;
2014 if (splitterWidth)
2015 *splitterWidth = config.readEntry(QString::fromLatin1("Splitter %1").arg(desktop->width()), -1);
2016 return true;
2019 /******************************************************************************
2020 * Write the size for the specified window to the config file, for the
2021 * current screen resolution.
2023 void writeConfigWindowSize(const char* window, const QSize& size, int splitterWidth)
2025 KConfigGroup config(KGlobal::config(), window);
2026 QWidget* desktop = KApplication::desktop();
2027 config.writeEntry(QString::fromLatin1("Width %1").arg(desktop->width()), size.width());
2028 config.writeEntry(QString::fromLatin1("Height %1").arg(desktop->height()), size.height());
2029 if (splitterWidth >= 0)
2030 config.writeEntry(QString::fromLatin1("Splitter %1").arg(desktop->width()), splitterWidth);
2031 config.sync();
2034 /******************************************************************************
2035 * Check from its mime type whether a file appears to be a text or image file.
2036 * If a text file, its type is distinguished.
2037 * Reply = file type.
2039 FileType fileType(const KMimeType::Ptr& mimetype)
2041 if (mimetype->is("text/html"))
2042 return TextFormatted;
2043 if (mimetype->is("application/x-executable"))
2044 return TextApplication;
2045 if (mimetype->is("text/plain"))
2046 return TextPlain;
2047 if (mimetype->name().startsWith(QLatin1String("image/")))
2048 return Image;
2049 return Unknown;
2052 /******************************************************************************
2053 * Check that a file exists and is a plain readable file.
2054 * Updates 'filename' and 'url' even if an error occurs, since 'filename' may
2055 * be needed subsequently by showFileErrMessage().
2057 FileErr checkFileExists(QString& filename, KUrl& url)
2059 url = KUrl();
2060 FileErr err = FileErr_None;
2061 QString file = filename;
2062 QRegExp f("^file:/+");
2063 if (f.indexIn(file) >= 0)
2064 file = file.mid(f.matchedLength() - 1);
2065 // Convert any relative file path to absolute
2066 // (using home directory as the default)
2067 int i = file.indexOf(QLatin1Char('/'));
2068 if (i > 0 && file[i - 1] == QLatin1Char(':'))
2070 url = file;
2071 url.cleanPath();
2072 filename = url.prettyUrl();
2073 KIO::UDSEntry uds;
2074 if (!KIO::NetAccess::stat(url, uds, MainWindow::mainMainWindow()))
2075 err = FileErr_Nonexistent;
2076 else
2078 KFileItem fi(uds, url);
2079 if (fi.isDir()) err = FileErr_Directory;
2080 else if (!fi.isReadable()) err = FileErr_Unreadable;
2083 else if (file.isEmpty())
2084 err = FileErr_Blank; // blank file name
2085 else
2087 // It's a local file - convert to absolute path & check validity
2088 QFileInfo info(file);
2089 QDir::setCurrent(QDir::homePath());
2090 filename = info.absoluteFilePath();
2091 url.setPath(filename);
2092 if (info.isDir()) err = FileErr_Directory;
2093 else if (!info.exists()) err = FileErr_Nonexistent;
2094 else if (!info.isReadable()) err = FileErr_Unreadable;
2096 return err;
2099 /******************************************************************************
2100 * Display an error message appropriate to 'err'.
2101 * Display a Continue/Cancel error message if 'errmsgParent' non-null.
2102 * Reply = true to continue, false to cancel.
2104 bool showFileErrMessage(const QString& filename, FileErr err, FileErr blankError, QWidget* errmsgParent)
2106 if (err != FileErr_None)
2108 // If file is a local file, remove "file://" from name
2109 QString file = filename;
2110 QRegExp f("^file:/+");
2111 if (f.indexIn(file) >= 0)
2112 file = file.mid(f.matchedLength() - 1);
2114 QString errmsg;
2115 switch (err)
2117 case FileErr_Blank:
2118 if (blankError == FileErr_BlankDisplay)
2119 errmsg = i18nc("@info", "Please select a file to display");
2120 else if (blankError == FileErr_BlankPlay)
2121 errmsg = i18nc("@info", "Please select a file to play");
2122 else
2123 kFatal() << "Program error";
2124 KAMessageBox::sorry(errmsgParent, errmsg);
2125 return false;
2126 case FileErr_Directory:
2127 KAMessageBox::sorry(errmsgParent, i18nc("@info", "<filename>%1</filename> is a folder", file));
2128 return false;
2129 case FileErr_Nonexistent: errmsg = i18nc("@info", "<filename>%1</filename> not found", file); break;
2130 case FileErr_Unreadable: errmsg = i18nc("@info", "<filename>%1</filename> is not readable", file); break;
2131 case FileErr_NotTextImage: errmsg = i18nc("@info", "<filename>%1</filename> appears not to be a text or image file", file); break;
2132 case FileErr_None:
2133 default:
2134 break;
2136 if (KAMessageBox::warningContinueCancel(errmsgParent, errmsg)
2137 == KMessageBox::Cancel)
2138 return false;
2140 return true;
2143 /******************************************************************************
2144 * If a url string is a local file, strip off the 'file:/' prefix.
2146 QString pathOrUrl(const QString& url)
2148 static const QRegExp localfile("^file:/+");
2149 return (localfile.indexIn(url) >= 0) ? url.mid(localfile.matchedLength() - 1) : url;
2152 /******************************************************************************
2153 * Display a modal dialog to choose an existing file, initially highlighting
2154 * any specified file.
2155 * @param initialFile The file to initially highlight - must be a full path name or URL.
2156 * @param defaultDir The directory to start in if @p initialFile is empty. If empty,
2157 * the user's home directory will be used. Updated to the
2158 * directory containing the selected file, if a file is chosen.
2159 * @param mode OR of KFile::Mode values, e.g. ExistingOnly, LocalOnly.
2160 * Reply = URL selected.
2161 * = empty, non-null string if no file was selected.
2162 * = null string if dialogue was deleted while visible (indicating that
2163 * the parent widget was probably also deleted).
2165 QString browseFile(const QString& caption, QString& defaultDir, const QString& initialFile,
2166 const QString& filter, KFile::Modes mode, QWidget* parent)
2168 QString initialDir = !initialFile.isEmpty() ? QString(initialFile).remove(QRegExp("/[^/]*$"))
2169 : !defaultDir.isEmpty() ? defaultDir
2170 : QDir::homePath();
2171 // Use AutoQPointer to guard against crash on application exit while
2172 // the dialogue is still open. It prevents double deletion (both on
2173 // deletion of parent, and on return from this function).
2174 AutoQPointer<KFileDialog> fileDlg = new KFileDialog(initialDir, filter, parent);
2175 fileDlg->setOperationMode(mode & KFile::ExistingOnly ? KFileDialog::Opening : KFileDialog::Saving);
2176 fileDlg->setMode(KFile::File | mode);
2177 fileDlg->setCaption(caption);
2178 if (!initialFile.isEmpty())
2179 fileDlg->setSelection(initialFile);
2180 if (fileDlg->exec() != QDialog::Accepted)
2181 return fileDlg ? QString("") : QString(); // return null only if dialog was deleted
2182 KUrl url = fileDlg->selectedUrl();
2183 if (url.isEmpty())
2184 return QString(""); // return empty, non-null string
2185 defaultDir = url.isLocalFile() ? url.upUrl().toLocalFile() : url.directory();
2186 return (mode & KFile::LocalOnly) ? url.pathOrUrl() : url.prettyUrl();
2189 /******************************************************************************
2190 * Return a prompt string to ask the user whether to convert the calendar to the
2191 * current format.
2192 * If 'whole' is true, the whole calendar needs to be converted; else only some
2193 * alarms may need to be converted.
2195 * Note: This method is defined here to avoid duplicating the i18n string
2196 * definition between the Akonadi and KResources code.
2198 QString conversionPrompt(const QString& calendarName, const QString& calendarVersion, bool whole)
2200 QString msg = whole
2201 ? i18nc("@info", "Calendar <resource>%1</resource> is in an old format (<application>KAlarm</application> version %2), "
2202 "and will be read-only unless you choose to update it to the current format.",
2203 calendarName, calendarVersion)
2204 : i18nc("@info", "Some or all of the alarms in calendar <resource>%1</resource> are in an old <application>KAlarm</application> format, "
2205 "and will be read-only unless you choose to update them to the current format.",
2206 calendarName);
2207 return i18nc("@info", "<para>%1</para><para>"
2208 "<warning>Do not update the calendar if it is also used with an older version of <application>KAlarm</application> "
2209 "(e.g. on another computer). If you do so, the calendar may become unusable there.</warning></para>"
2210 "<para>Do you wish to update the calendar?</para>", msg);
2213 #ifndef NDEBUG
2214 /******************************************************************************
2215 * Set up KAlarm test conditions based on environment variables.
2216 * KALARM_TIME: specifies current system time (format [[[yyyy-]mm-]dd-]hh:mm [TZ]).
2218 void setTestModeConditions()
2220 const QByteArray newTime = qgetenv("KALARM_TIME");
2221 if (!newTime.isEmpty())
2223 KDateTime dt;
2224 if (AlarmTime::convertTimeString(newTime, dt, KDateTime::realCurrentLocalDateTime(), true))
2225 setSimulatedSystemTime(dt);
2229 /******************************************************************************
2230 * Set the simulated system time.
2232 void setSimulatedSystemTime(const KDateTime& dt)
2234 KDateTime::setSimulatedSystemTime(dt);
2235 kDebug() << "New time =" << qPrintable(KDateTime::currentLocalDateTime().toString("%Y-%m-%d %H:%M %:Z"));
2237 #endif
2239 } // namespace KAlarm
2240 namespace
2243 /******************************************************************************
2244 * Tell KOrganizer to put an alarm in its calendar.
2245 * It will be held by KOrganizer as a simple event, without alarms - KAlarm
2246 * is still responsible for alarming.
2248 KAlarm::UpdateStatus sendToKOrganizer(const KAEvent* event)
2250 #ifdef USE_AKONADI
2251 Event::Ptr kcalEvent(new KCalCore::Event);
2252 event->updateKCalEvent(kcalEvent, KAEvent::UID_IGNORE);
2253 #else
2254 Event* kcalEvent = AlarmCalendar::resources()->createKCalEvent(event);
2255 #endif
2256 // Change the event ID to avoid duplicating the same unique ID as the original event
2257 QString uid = uidKOrganizer(event->id());
2258 kcalEvent->setUid(uid);
2259 kcalEvent->clearAlarms();
2260 QString userEmail;
2261 switch (event->actionTypes())
2263 case KAEvent::ACT_DISPLAY:
2264 case KAEvent::ACT_COMMAND:
2265 case KAEvent::ACT_DISPLAY_COMMAND:
2266 kcalEvent->setSummary(event->cleanText());
2267 userEmail = Preferences::emailAddress();
2268 break;
2269 case KAEvent::ACT_EMAIL:
2271 QString from = event->emailFromId()
2272 ? Identities::identityManager()->identityForUoid(event->emailFromId()).fullEmailAddr()
2273 : Preferences::emailAddress();
2274 AlarmText atext;
2275 atext.setEmail(event->emailAddresses(", "), from, QString(), QString(), event->emailSubject(), QString());
2276 kcalEvent->setSummary(atext.displayText());
2277 userEmail = from;
2278 break;
2280 case KAEvent::ACT_AUDIO:
2281 kcalEvent->setSummary(event->audioFile());
2282 break;
2283 default:
2284 break;
2286 #ifdef USE_AKONADI
2287 Person::Ptr person(new Person(QString(), userEmail));
2288 kcalEvent->setOrganizer(person);
2289 #else
2290 kcalEvent->setOrganizer(KCal::Person(QString(), userEmail));
2291 #endif
2292 kcalEvent->setDuration(Duration(Preferences::kOrgEventDuration() * 60, Duration::Seconds));
2294 // Translate the event into string format
2295 ICalFormat format;
2296 format.setTimeSpec(Preferences::timeZone(true));
2297 QString iCal = format.toICalString(kcalEvent);
2298 #ifndef USE_AKONADI
2299 delete kcalEvent;
2300 #endif
2302 // Send the event to KOrganizer
2303 KAlarm::UpdateStatus st = runKOrganizer(); // start KOrganizer if it isn't already running, and create its D-Bus interface
2304 if (st != KAlarm::UPDATE_OK)
2305 return st;
2306 QList<QVariant> args;
2307 args << iCal;
2308 QDBusReply<bool> reply = korgInterface->callWithArgumentList(QDBus::Block, QLatin1String("addIncidence"), args);
2309 if (!reply.isValid())
2311 if (reply.error().type() == QDBusError::UnknownObject)
2313 kError()<<"addIncidence() D-Bus error: still starting";
2314 return KAlarm::UPDATE_KORG_ERRSTART;
2316 kError() << "addIncidence(" << uid << ") D-Bus call failed:" << reply.error().message();
2317 return KAlarm::UPDATE_KORG_ERR;
2319 if (!reply.value())
2321 kDebug() << "addIncidence(" << uid << ") D-Bus call returned false";
2322 return KAlarm::UPDATE_KORG_FUNCERR;
2324 kDebug() << uid << ": success";
2325 return KAlarm::UPDATE_OK;
2328 /******************************************************************************
2329 * Tell KOrganizer to delete an event from its calendar.
2331 KAlarm::UpdateStatus deleteFromKOrganizer(const QString& eventID)
2333 KAlarm::UpdateStatus st = runKOrganizer(); // start KOrganizer if it isn't already running, and create its D-Bus interface
2334 if (st != KAlarm::UPDATE_OK)
2335 return st;
2336 QString newID = uidKOrganizer(eventID);
2337 QList<QVariant> args;
2338 args << newID << true;
2339 QDBusReply<bool> reply = korgInterface->callWithArgumentList(QDBus::Block, QLatin1String("deleteIncidence"), args);
2340 if (!reply.isValid())
2342 if (reply.error().type() == QDBusError::UnknownObject)
2344 kError()<<"deleteIncidence() D-Bus error: still starting";
2345 return KAlarm::UPDATE_KORG_ERRSTART;
2347 kError() << "deleteIncidence(" << newID << ") D-Bus call failed:" << reply.error().message();
2348 return KAlarm::UPDATE_KORG_ERR;
2350 if (!reply.value())
2352 kDebug() << "deleteIncidence(" << newID << ") D-Bus call returned false";
2353 return KAlarm::UPDATE_KORG_FUNCERR;
2355 kDebug() << newID << ": success";
2356 return KAlarm::UPDATE_OK;
2359 /******************************************************************************
2360 * Start KOrganizer if not already running, and create its D-Bus interface.
2362 KAlarm::UpdateStatus runKOrganizer()
2364 QString error, dbusService;
2365 int result = KDBusServiceStarter::self()->findServiceFor("DBUS/Organizer", QString(), &error, &dbusService);
2366 if (result)
2368 kWarning() << "Unable to start DBUS/Organizer:" << dbusService << error;
2369 return KAlarm::UPDATE_KORG_ERR;
2371 // If Kontact is running, there is be a load() method which needs to be called
2372 // to load KOrganizer into Kontact. But if KOrganizer is running independently,
2373 // the load() method doesn't exist.
2374 QDBusInterface iface(KORG_DBUS_SERVICE, KORG_DBUS_LOAD_PATH, "org.kde.KUniqueApplication");
2375 if (!iface.isValid())
2377 kWarning() << "Unable to access "KORG_DBUS_LOAD_PATH" D-Bus interface:" << iface.lastError().message();
2378 return KAlarm::UPDATE_KORG_ERR;
2380 QDBusReply<bool> reply = iface.call("load");
2381 if ((!reply.isValid() || !reply.value())
2382 && iface.lastError().type() != QDBusError::UnknownMethod)
2384 kWarning() << "Loading KOrganizer failed:" << iface.lastError().message();
2385 return KAlarm::UPDATE_KORG_ERR;
2388 // KOrganizer has been started, but it may not have the necessary
2389 // D-Bus interface available yet.
2390 if (!korgInterface || !korgInterface->isValid())
2392 delete korgInterface;
2393 korgInterface = new QDBusInterface(KORG_DBUS_SERVICE, KORG_DBUS_PATH, KORG_DBUS_IFACE);
2394 if (!korgInterface->isValid())
2396 kWarning() << "Unable to access "KORG_DBUS_PATH" D-Bus interface:" << korgInterface->lastError().message();
2397 delete korgInterface;
2398 korgInterface = 0;
2399 return KAlarm::UPDATE_KORG_ERRSTART;
2402 return KAlarm::UPDATE_OK;
2405 /******************************************************************************
2406 * Insert a KOrganizer string after the hyphen in the supplied event ID.
2408 QString uidKOrganizer(const QString& id)
2410 QString result = id;
2411 int i = result.lastIndexOf('-');
2412 if (i < 0)
2413 i = result.length();
2414 return result.insert(i, KORGANIZER_UID);
2417 } // namespace
2419 /******************************************************************************
2420 * Case insensitive comparison for use by qSort().
2422 bool caseInsensitiveLessThan(const QString& s1, const QString& s2)
2424 return s1.toLower() < s2.toLower();
2427 // vim: et sw=4: