2 * functions.cpp - miscellaneous functions
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"
26 #include "collectionmodel.h"
28 #include "alarmresources.h"
29 #include "eventlistmodel.h"
31 #include "alarmcalendar.h"
32 #include "alarmtime.h"
33 #include "autoqpointer.h"
34 #include "alarmlistview.h"
36 #include "kalarmapp.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>
50 #include <kcalcore/event.h>
51 #include <kcalcore/icalformat.h>
52 #include <kcalcore/person.h>
53 #include <kcalcore/duration.h>
54 using namespace KCalCore
;
56 #include <kcal/event.h>
57 #include <kcal/icalformat.h>
58 #include <kcal/person.h>
59 #include <kcal/duration.h>
62 #include <kpimidentities/identitymanager.h>
63 #include <kpimidentities/identity.h>
64 #include <kholidays/holidays.h>
66 #include <kconfiggroup.h>
68 #include <ktoggleaction.h>
69 #include <kactioncollection.h>
70 #include <kdbusservicestarter.h>
73 #include <kstandarddirs.h>
75 #include <ksystemtimezone.h>
76 #include <kstandardguiitem.h>
77 #include <kstandardshortcut.h>
78 #include <kfiledialog.h>
80 #include <kio/netaccess.h>
81 #include <kfileitem.h>
83 #include <ktoolinvocation.h>
86 #include <kwindowsystem.h>
87 #include <kxmessages.h>
88 #include <kstartupinfo.h>
95 #include <QDesktopWidget>
96 #include <QtDBus/QtDBus>
101 using namespace Akonadi
;
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
);
137 Private
* Private::mInstance
= 0;
139 /******************************************************************************
140 * Display a main window with the specified event selected.
143 MainWindow
* displayMainWindowSelected(Akonadi::Item::Id eventId
)
145 MainWindow
* displayMainWindowSelected(const QString
& eventId
)
148 MainWindow
* win
= MainWindow::firstWindow();
151 if (theApp()->checkCalendar()) // ensure calendar is open
153 win
= MainWindow::create();
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
);
164 win
->activateWindow();
167 if (win
&& eventId
>= 0)
168 win
->selectEvent(eventId
);
170 if (win
&& !eventId
.isEmpty())
171 win
->selectEvent(eventId
);
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)));
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)));
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)));
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
219 * event - is updated with the actual event ID.
222 UpdateStatus
addEvent(KAEvent
& event
, Collection
* calendar
, QWidget
* msgParent
, int options
, bool showKOrgErr
)
224 UpdateStatus
addEvent(KAEvent
& event
, AlarmResource
* calendar
, QWidget
* msgParent
, int options
, bool showKOrgErr
)
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
;
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
);
238 if (!cal
->addEvent(*newev
, msgParent
, (options
& USE_EVENT_ID
), calendar
, (options
& NO_RESOURCE_PROMPT
), &cancelled
))
240 if (!cal
->addEvent(newev
, msgParent
, (options
& USE_EVENT_ID
), calendar
, (options
& NO_RESOURCE_PROMPT
), &cancelled
))
244 status
= UPDATE_FAILED
;
248 event
= *newev
; // update event ID etc.
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
262 // Update the window lists
263 EventListModel::alarms()->addEvent(newev
);
268 if (status
!= UPDATE_OK
&& !cancelled
&& msgParent
)
269 displayUpdateError(msgParent
, status
, ERR_ADD
, 1, 1, showKOrgErr
);
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())
285 UpdateStatus status
= UPDATE_OK
;
287 Collection collection
;
289 AlarmResource
* resource
;
291 if (!theApp()->checkCalendar()) // ensure calendar is open
292 status
= UPDATE_FAILED
;
296 collection
= CollectionControlModel::instance()->destination(CalEvent::ACTIVE
, msgParent
);
297 if (!collection
.isValid())
299 resource
= AlarmResources::instance()->destination(CalEvent::ACTIVE
, msgParent
);
303 kDebug() << "No calendar";
304 status
= UPDATE_FAILED
;
307 if (status
== UPDATE_OK
)
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
315 KAEvent
* const newev
= &events
[i
];
316 if (!cal
->addEvent(events
[i
], msgParent
, false, &collection
))
318 KAEvent
* newev
= new KAEvent(events
[i
]);
319 if (!cal
->addEvent(newev
, msgParent
, false, resource
))
325 status
= UPDATE_ERROR
;
330 events
[i
] = *newev
; // update event ID etc.
332 if (allowKOrgUpdate
&& newev
->copyToKOrganizer())
334 UpdateStatus st
= sendToKOrganizer(newev
); // tell KOrganizer to show the event
344 // Update the window lists, but not yet which item is selected
345 EventListModel::alarms()->addEvent(newev
);
346 // selectID = newev->id();
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
);
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.
368 bool addArchivedEvent(KAEvent
& event
, Collection
* collection
)
370 bool addArchivedEvent(KAEvent
& event
, AlarmResource
* resource
)
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();
380 KAEvent
newevent(event
);
381 newevent
.setItemId(-1); // invalidate the Akonadi item ID since it's a new item
382 KAEvent
* const newev
= &newevent
;
384 KAEvent
* newev
= new KAEvent(event
);
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
393 if (!cal
->addEvent(newevent
, 0, false, collection
))
396 if (!cal
->addEvent(newev
, 0, false, resource
))
398 delete newev
; // failed to add to calendar - leave event in its original state
402 event
= *newev
; // update event ID etc.
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.
410 EventListModel::alarms()->removeEvent(oldid
);
411 EventListModel::alarms()->addEvent(newev
);
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.
423 UpdateStatus
addTemplate(KAEvent
& event
, Collection
* collection
, QWidget
* msgParent
)
425 UpdateStatus
addTemplate(KAEvent
& event
, AlarmResource
* resource
, QWidget
* msgParent
)
428 kDebug() << event
.id();
429 UpdateStatus status
= UPDATE_OK
;
431 // Add the template to the calendar file
432 AlarmCalendar
* cal
= AlarmCalendar::resources();
434 KAEvent
newev(event
);
435 if (!cal
->addEvent(newev
, msgParent
, false, collection
))
436 status
= UPDATE_FAILED
;
438 KAEvent
* newev
= new KAEvent(event
);
439 if (!cal
->addEvent(newev
, msgParent
, false, resource
))
442 status
= UPDATE_FAILED
;
448 event
= newev
; // update event ID etc.
450 event
= *newev
; // update event ID etc.
453 status
= SAVE_FAILED
;
457 // Update the window lists
458 EventListModel::templates()->addEvent(newev
);
465 displayUpdateError(msgParent
, status
, ERR_TEMPLATE
, 1);
469 /******************************************************************************
470 * Modify an active (non-archived) alarm in the calendar file and in every main
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
;
487 EventId
oldId(oldEvent
);
489 QString oldId
= oldEvent
.id();
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.
497 deleteFromKOrganizer(oldId
.eventId());
499 deleteFromKOrganizer(oldId
);
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
;
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
))
518 status
= UPDATE_FAILED
;
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
537 // Remove "Don't show error messages again" for the old alarm
538 setDontShowErrors(oldId
);
541 // Update the window lists
542 EventListModel::alarms()->addEvent(newev
);
548 if (status
!= UPDATE_OK
&& msgParent
)
549 displayUpdateError(msgParent
, status
, ERR_MODIFY
, 1, 1, showKOrgErr
);
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
);
568 // Update the event in the calendar file.
569 AlarmCalendar
* cal
= AlarmCalendar::resources();
571 cal
->updateEvent(event
);
573 KAEvent
* newEvent
= cal
->updateEvent(event
);
578 displayUpdateError(msgParent
, SAVE_FAILED
, ERR_ADD
, 1);
583 // Update the window lists
584 EventListModel::alarms()->updateEvent(newEvent
);
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
;
601 status
= UPDATE_FAILED
;
602 else if (!cal
->save())
603 status
= SAVE_FAILED
;
604 if (status
!= UPDATE_OK
)
607 displayUpdateError(msgParent
, SAVE_FAILED
, ERR_TEMPLATE
, 1);
612 EventListModel::templates()->updateEvent(newEvent
);
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
)
624 QVector
<KAEvent
> events(1, event
);
626 KAEvent::List events
;
629 return deleteEvents(events
, archive
, msgParent
, showKOrgErr
);
633 UpdateStatus
deleteEvents(QVector
<KAEvent
>& events
, bool archive
, QWidget
* msgParent
, bool showKOrgErr
)
635 UpdateStatus
deleteEvents(KAEvent::List
& events
, bool archive
, QWidget
* msgParent
, bool showKOrgErr
)
638 kDebug() << events
.count();
639 if (events
.isEmpty())
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
651 KAEvent
* event
= &events
[i
];
653 KAEvent
* event
= events
[i
];
655 QString id
= event
->id();
658 // Update the window lists and clear stored command errors
659 EventListModel::alarms()->removeEvent(id
);
660 event
->setCommandError(KAEvent::CMD_NO_ERROR
);
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
);
679 if (archive
&& event
->toBeArchived())
682 addArchivedEvent(ev
); // this changes the event ID to an archived ID
686 if (!cal
->deleteEvent(*event
, false)) // don't save calendar after deleting
688 if (!cal
->deleteEvent(id
, false)) // don't save calendar after deleting
691 status
= UPDATE_ERROR
;
695 if (id
== wakeFromSuspendId
)
696 deleteWakeFromSuspendAlarm
= true;
698 // Remove "Don't show error messages again" for this alarm
700 setDontShowErrors(EventId(*event
));
702 setDontShowErrors(id
);
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
);
723 /******************************************************************************
724 * Delete templates from the calendar file and from every template list view.
727 UpdateStatus
deleteTemplates(const KAEvent::List
& events
, QWidget
* msgParent
)
729 UpdateStatus
deleteTemplates(const QStringList
& eventIDs
, QWidget
* msgParent
)
733 int count
= events
.count();
735 int count
= eventIDs
.count();
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
747 QString id
= eventIDs
[i
];
748 EventListModel::templates()->removeEvent(id
);
751 // Delete the template from the calendar file
752 AlarmCalendar
* cal
= AlarmCalendar::resources();
754 if (!cal
->deleteEvent(*events
[i
], false)) // don't save calendar after deleting
756 if (!cal
->deleteEvent(id
, false)) // don't save calendar after deleting
759 status
= UPDATE_ERROR
;
764 if (warnErr
== count
)
765 status
= UPDATE_FAILED
;
766 else if (!cal
->save()) // save the calendars now
768 status
= SAVE_FAILED
;
771 if (status
!= UPDATE_OK
&& msgParent
)
772 displayUpdateError(msgParent
, status
, ERR_TEMPLATE
, warnErr
);
776 /******************************************************************************
777 * Delete an alarm from the display calendar.
779 void deleteDisplayEvent(const QString
& eventID
)
782 AlarmCalendar
* cal
= AlarmCalendar::displayCalendarOpen();
785 cal
->deleteDisplayEvent(eventID
, true); // save calendar after deleting
787 cal
->deleteEvent(eventID
, true); // save calendar after deleting
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.
797 UpdateStatus
reactivateEvent(KAEvent
& event
, Collection
* calendar
, QWidget
* msgParent
, bool showKOrgErr
)
799 UpdateStatus
reactivateEvent(KAEvent
& event
, AlarmResource
* calendar
, QWidget
* msgParent
, bool showKOrgErr
)
803 QVector
<EventId
> ids
;
804 QVector
<KAEvent
> events(1, event
);
807 KAEvent::List events
;
810 return reactivateEvents(events
, ids
, calendar
, msgParent
, showKOrgErr
);
814 UpdateStatus
reactivateEvents(QVector
<KAEvent
>& events
, QVector
<EventId
>& ineligibleIDs
, Collection
* col
, QWidget
* msgParent
, bool showKOrgErr
)
816 UpdateStatus
reactivateEvents(KAEvent::List
& events
, QStringList
& ineligibleIDs
, AlarmResource
* resource
, QWidget
* msgParent
, bool showKOrgErr
)
819 kDebug() << events
.count();
820 ineligibleIDs
.clear();
821 if (events
.isEmpty())
825 UpdateStatus status
= UPDATE_OK
;
827 Collection collection
;
830 if (!collection
.isValid())
831 collection
= CollectionControlModel::instance()->destination(CalEvent::ACTIVE
, msgParent
);
832 if (!collection
.isValid())
835 resource
= AlarmResources::instance()->destination(CalEvent::ACTIVE
, msgParent
);
839 kDebug() << "No calendar";
840 status
= UPDATE_FAILED
;
841 warnErr
= events
.count();
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
853 KAEvent
* event
= &events
[i
];
855 KAEvent
* event
= events
[i
];
857 if (event
->category() != CalEvent::ARCHIVED
858 || !event
->occursAfter(now
, true))
861 ineligibleIDs
+= EventId(*event
);
863 ineligibleIDs
+= event
->id();
870 KAEvent
newevent(*event
);
871 KAEvent
* const newev
= &newevent
;
873 KAEvent
* newev
= new KAEvent(*event
);
874 QString oldid
= event
->id();
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.
884 if (!cal
->addEvent(newevent
, msgParent
, true, &collection
))
886 if (!cal
->addEvent(newev
, msgParent
, true, resource
))
892 status
= UPDATE_ERROR
;
896 if (newev
->copyToKOrganizer())
898 UpdateStatus st
= sendToKOrganizer(newev
); // tell KOrganizer to show the event
908 // Update the window lists
909 EventListModel::alarms()->updateEvent(oldid
, newev
);
910 // selectID = newev->id();
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
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
921 status
= UPDATE_ERROR
;
925 events
[i
] = newevent
;
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
;
940 if (status
!= UPDATE_OK
&& msgParent
)
941 displayUpdateError(msgParent
, status
, ERR_REACTIVATE
, warnErr
, warnKOrg
, showKOrgErr
);
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.
950 UpdateStatus
enableEvents(QVector
<KAEvent
>& events
, bool enable
, QWidget
* msgParent
)
952 UpdateStatus
enableEvents(KAEvent::List
& events
, bool enable
, QWidget
* msgParent
)
955 kDebug() << events
.count();
956 if (events
.isEmpty())
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
)
965 KAEvent
* event
= &events
[i
];
967 KAEvent
* event
= events
[i
];
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
);
980 kError() << "Error updating event in calendar:" << event
->id();
983 cal
->disabledChanged(newev
);
985 // If we're disabling a display alarm, close any message window
986 if (!enable
&& (event
->actionTypes() & KAEvent::ACT_DISPLAY
))
989 MessageWin
* win
= MessageWin::findEvent(EventId(*event
));
991 MessageWin
* win
= MessageWin::findEvent(event
->id());
997 // Update the window lists
998 EventListModel::alarms()->updateEvent(newev
);
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
);
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
1024 void purgeArchive(int purgeDays
)
1028 kDebug() << purgeDays
;
1029 QDate cutoff
= KDateTime::currentLocalDate().addDays(-purgeDays
);
1031 Collection collection
= CollectionControlModel::getStandard(CalEvent::ARCHIVED
);
1032 if (!collection
.isValid())
1034 KAEvent::List events
= AlarmCalendar::resources()->events(collection
);
1035 for (int i
= 0; i
< events
.count(); )
1037 if (purgeDays
&& events
[i
]->createdDateTime().date() >= cutoff
)
1043 AlarmResource
* resource
= AlarmResources::instance()->getStandardResource(CalEvent::ARCHIVED
);
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
)
1054 EventListModel::alarms()->removeEvent(events
[i
++]); // update the window lists
1057 if (!events
.isEmpty())
1058 AlarmCalendar::resources()->purgeEvents(events
); // delete the events and save the calendar
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;
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())
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
);
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())
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
)
1114 if (status
> UPDATE_KORG_ERR
)
1120 errmsg
= (nAlarms
> 1) ? i18nc("@info", "Error saving alarms")
1121 : i18nc("@info", "Error saving alarm");
1124 errmsg
= (nAlarms
> 1) ? i18nc("@info", "Error deleting alarms")
1125 : i18nc("@info", "Error deleting alarm");
1127 case ERR_REACTIVATE
:
1128 errmsg
= (nAlarms
> 1) ? i18nc("@info", "Error saving reactivated alarms")
1129 : i18nc("@info", "Error saving reactivated alarm");
1132 errmsg
= (nAlarms
> 1) ? i18nc("@info", "Error saving alarm templates")
1133 : i18nc("@info", "Error saving alarm template");
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
)
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");
1156 errmsg
= i18nc("@info", "Unable to update alarm in KOrganizer");
1159 errmsg
= (nAlarms
> 1) ? i18nc("@info", "Unable to delete alarms from KOrganizer")
1160 : i18nc("@info", "Unable to delete alarm from KOrganizer");
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
);
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
;
1192 case KAEvent::MESSAGE
:
1194 type
= EditAlarmDlg::DISPLAY
;
1197 case KAEvent::COMMAND
:
1198 type
= EditAlarmDlg::COMMAND
;
1200 case KAEvent::EMAIL
:
1201 type
= EditAlarmDlg::EMAIL
;
1203 case KAEvent::AUDIO
:
1204 type
= EditAlarmDlg::AUDIO
;
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
);
1234 editDlg
->activateWindow();
1237 PrivateNewAlarmDlg::PrivateNewAlarmDlg(EditAlarmDlg
* 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
)
1259 Collection calendar
;
1261 AlarmResource
* calendar
;
1263 editDlg
->getEvent(event
, calendar
);
1265 // Add the alarm to the displayed lists and to the calendar file
1267 UpdateStatus status
= addEvent(event
, &calendar
, editDlg
);
1269 UpdateStatus status
= addEvent(event
, calendar
, editDlg
);
1275 case UPDATE_KORG_ERR
:
1276 case UPDATE_KORG_ERRSTART
:
1277 case UPDATE_KORG_FUNCERR
:
1278 displayKOrgUpdateError(editDlg
, ERR_ADD
, status
, 1);
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
);
1301 kWarning() << templateName
<< ": template not found";
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
1327 * Reply = config entry: [0] = event's collection ID (Akonadi only),
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());
1337 if (params
.count() == 3 && params
[2].toUInt() > KDateTime::currentUtcDateTime().toTime_t())
1339 if (params
.count() == 2 && params
[1].toUInt() > KDateTime::currentUtcDateTime().toTime_t())
1343 if (checkEventExists
&& !AlarmCalendar::getEvent(EventId(params
[0].toLongLong(), params
[1])))
1345 if (checkEventExists
&& !AlarmCalendar::getEvent(params
[0]))
1347 return QStringList();
1348 return params
; // config entry is valid
1350 if (!params
.isEmpty())
1352 config
.deleteEntry("RtcWake"); // delete the expired config entry
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");
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
)
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();
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
;
1419 case KAuth::ActionReply::AuthorizationDenied
:
1420 case KAuth::ActionReply::UserCancelled
:
1421 return false; // the user should already know about this
1426 case KAuth::ActionReply::HelperError
:
1427 kDebug() << "Helper error:" << errcode
;
1428 errcode
+= 100; // make code distinguishable from KAuthError type
1433 errmsg
= i18nc("@info", "Error obtaining authorization (%1)", errcode
);
1435 KAMessageBox::information(parent
, errmsg
);
1441 } // namespace KAlarm
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
)
1453 if (CollectionControlModel::enabledCollections(CalEvent::TEMPLATE
, true).isEmpty())
1455 if (!AlarmResources::instance()->activeCount(CalEvent::TEMPLATE
, true))
1458 KAMessageBox::sorry(parent
, i18nc("@info", "You must enable a template calendar to save the template in"));
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
;
1466 editDlg
= EditAlarmDlg::create(true, preset
, true, parent
);
1468 editDlg
= EditAlarmDlg::create(true, type
, parent
);
1469 if (editDlg
->exec() == QDialog::Accepted
)
1473 Akonadi::Collection calendar
;
1475 AlarmResource
* calendar
;
1477 editDlg
->getEvent(event
, calendar
);
1479 // Add the template to the displayed lists and to the calendar file
1481 KAlarm::addTemplate(event
, &calendar
, editDlg
);
1483 KAlarm::addTemplate(event
, calendar
, editDlg
);
1485 Undo::saveAdd(event
, calendar
);
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
)
1500 if (event
->expired() || AlarmCalendar::resources()->eventReadOnly(event
->itemId()))
1502 if (event
->expired() || AlarmCalendar::resources()->eventReadOnly(event
->id()))
1505 viewAlarm(event
, parent
);
1511 QString id
= event
->id();
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
);
1528 Collection calendar
;
1530 AlarmResource
* calendar
;
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
);
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
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
)
1562 KAEvent::List events
= AlarmCalendar::resources()->events(eventID
);
1563 if (events
.count() > 1)
1565 kWarning() << eventID
<< ": multiple events found";
1568 if (events
.isEmpty())
1570 KAEvent
* event
= AlarmCalendar::resources()->event(eventID
);
1574 kError() << eventID
<< ": event ID not found";
1578 KAEvent
* event
= events
[0];
1579 if (AlarmCalendar::resources()->eventReadOnly(event
->itemId()))
1581 if (AlarmCalendar::resources()->eventReadOnly(eventID
))
1584 kError() << eventID
<< ": read-only";
1587 switch (event
->category())
1589 case CalEvent::ACTIVE
:
1590 case CalEvent::TEMPLATE
:
1593 kError() << eventID
<< ": event not active or template";
1596 editAlarm(event
, parent
);
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
)
1607 if (AlarmCalendar::resources()->eventReadOnly(event
->itemId()))
1609 if (AlarmCalendar::resources()->eventReadOnly(event
->id()))
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);
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
)
1628 Akonadi::Collection calendar
;
1630 AlarmResource
* calendar
;
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);
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.
1661 void updateEditedAlarm(EditAlarmDlg
* editDlg
, KAEvent
& event
, Collection
& calendar
)
1663 void updateEditedAlarm(EditAlarmDlg
* editDlg
, KAEvent
& event
, AlarmResource
* calendar
)
1669 Akonadi::Collection cal
;
1673 editDlg
->getEvent(newEvent
, cal
);
1675 // Update the displayed lists and the calendar file
1676 UpdateStatus status
;
1678 if (AlarmCalendar::resources()->event(EventId(event
)))
1680 if (AlarmCalendar::resources()->event(event
.id()))
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
);
1690 // The old event has expired, so simply create a new one
1692 status
= addEvent(newEvent
, &calendar
, editDlg
);
1694 status
= addEvent(newEvent
, calendar
, editDlg
);
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
);
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
);
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()
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
)
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
))
1780 MessageWin
* win
= MessageWin::findEvent(EventId(*event
));
1782 MessageWin
* win
= MessageWin::findEvent(event
->id());
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
1805 if (minimise
&& Private::startKMailMinimised())
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
);
1816 /******************************************************************************
1817 * Start KMail, minimised.
1818 * This code is taken from kstart in kdebase.
1820 bool Private::startKMailMinimised()
1823 NETRootInfo
i(QX11Info::display(), NET::Supported
);
1824 if (i
.isSupported(NET::WM2KDETemporaryRules
))
1826 kDebug() << "using rules";
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);
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
;
1846 int pid
= proc
->startDetached();
1849 KStartupInfo::sendFinish(id
); // failed to start
1852 KStartupInfoData data
;
1854 data
.setName("kmail");
1855 data
.setBin("kmail");
1856 KStartupInfo::sendChange(id
, data
);
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
)
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
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
);
1887 hints
->flags
|= StateHint
;
1888 hints
->initial_state
= IconicState
;
1889 XSetWMHints(QX11Info::display(), w
, hints
);
1892 info
.setWindowType(NET::Normal
);
1894 XSync(QX11Info::display(), False
);
1895 XMapWindow(QX11Info::display(), w
);
1896 XSync(QX11Info::display(), False
);
1897 QApplication::flush();
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.
1912 QStringList
dontShowErrors(const EventId
& eventId
)
1914 QStringList
dontShowErrors(const QString
& eventId
)
1917 if (eventId
.isEmpty())
1918 return QStringList();
1919 KConfig
config(KStandardDirs::locateLocal("appdata", ALARM_OPTS_FILE
));
1920 KConfigGroup
group(&config
, DONT_SHOW_ERRORS_GROUP
);
1922 const QString id
= QString("%1:%2").arg(eventId
.collectionId()).arg(eventId
.eventId());
1924 const QString
id(eventId
);
1926 return group
.readEntry(id
, QStringList());
1929 /******************************************************************************
1930 * Check whether the specified Don't-show-again error message tag is set for an
1934 bool dontShowErrors(const EventId
& eventId
, const QString
& tag
)
1936 bool dontShowErrors(const QString
& eventId
, const QString
& tag
)
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.
1950 void setDontShowErrors(const EventId
& eventId
, const QStringList
& tags
)
1952 void setDontShowErrors(const QString
& eventId
, const QStringList
& tags
)
1955 if (eventId
.isEmpty())
1957 KConfig
config(KStandardDirs::locateLocal("appdata", ALARM_OPTS_FILE
));
1958 KConfigGroup
group(&config
, DONT_SHOW_ERRORS_GROUP
);
1960 const QString id
= QString("%1:%2").arg(eventId
.collectionId()).arg(eventId
.eventId());
1962 const QString
id(eventId
);
1965 group
.deleteEntry(id
);
1967 group
.writeEntry(id
, tags
);
1971 /******************************************************************************
1972 * Set the specified Don't-show-again error message tag for an alarm ID.
1973 * Existing tags are unaffected.
1976 void setDontShowErrors(const EventId
& eventId
, const QString
& tag
)
1978 void setDontShowErrors(const QString
& eventId
, const QString
& tag
)
1981 if (eventId
.isEmpty() || tag
.isEmpty())
1983 KConfig
config(KStandardDirs::locateLocal("appdata", ALARM_OPTS_FILE
));
1984 KConfigGroup
group(&config
, DONT_SHOW_ERRORS_GROUP
);
1986 const QString id
= QString("%1:%2").arg(eventId
.collectionId()).arg(eventId
.eventId());
1988 const QString
id(eventId
);
1990 QStringList tags
= group
.readEntry(id
, QStringList());
1991 if (tags
.indexOf(tag
) < 0)
1994 group
.writeEntry(id
, tags
);
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));
2015 *splitterWidth
= config
.readEntry(QString::fromLatin1("Splitter %1").arg(desktop
->width()), -1);
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
);
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"))
2047 if (mimetype
->name().startsWith(QLatin1String("image/")))
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
)
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(':'))
2072 filename
= url
.prettyUrl();
2074 if (!KIO::NetAccess::stat(url
, uds
, MainWindow::mainMainWindow()))
2075 err
= FileErr_Nonexistent
;
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
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
;
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);
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");
2123 kFatal() << "Program error";
2124 KAMessageBox::sorry(errmsgParent
, errmsg
);
2126 case FileErr_Directory
:
2127 KAMessageBox::sorry(errmsgParent
, i18nc("@info", "<filename>%1</filename> is a folder", file
));
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;
2136 if (KAMessageBox::warningContinueCancel(errmsgParent
, errmsg
)
2137 == KMessageBox::Cancel
)
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
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();
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
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
)
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.",
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
);
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())
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"));
2239 } // namespace KAlarm
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
)
2251 Event::Ptr
kcalEvent(new KCalCore::Event
);
2252 event
->updateKCalEvent(kcalEvent
, KAEvent::UID_IGNORE
);
2254 Event
* kcalEvent
= AlarmCalendar::resources()->createKCalEvent(event
);
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();
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();
2269 case KAEvent::ACT_EMAIL
:
2271 QString from
= event
->emailFromId()
2272 ? Identities::identityManager()->identityForUoid(event
->emailFromId()).fullEmailAddr()
2273 : Preferences::emailAddress();
2275 atext
.setEmail(event
->emailAddresses(", "), from
, QString(), QString(), event
->emailSubject(), QString());
2276 kcalEvent
->setSummary(atext
.displayText());
2280 case KAEvent::ACT_AUDIO
:
2281 kcalEvent
->setSummary(event
->audioFile());
2287 Person::Ptr
person(new Person(QString(), userEmail
));
2288 kcalEvent
->setOrganizer(person
);
2290 kcalEvent
->setOrganizer(KCal::Person(QString(), userEmail
));
2292 kcalEvent
->setDuration(Duration(Preferences::kOrgEventDuration() * 60, Duration::Seconds
));
2294 // Translate the event into string format
2296 format
.setTimeSpec(Preferences::timeZone(true));
2297 QString iCal
= format
.toICalString(kcalEvent
);
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
)
2306 QList
<QVariant
> args
;
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
;
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
)
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
;
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
);
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
;
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('-');
2413 i
= result
.length();
2414 return result
.insert(i
, KORGANIZER_UID
);
2419 /******************************************************************************
2420 * Case insensitive comparison for use by qSort().
2422 bool caseInsensitiveLessThan(const QString
& s1
, const QString
& s2
)
2424 return s1
.toLower() < s2
.toLower();