2 * functions.cpp - miscellaneous functions
4 * Copyright © 2001-2011 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 "autoqpointer.h"
33 #include "alarmlistview.h"
35 #include "identities.h"
37 #include "kalarmapp.h"
39 #include "mainwindow.h"
40 #include "messagebox.h"
41 #include "messagewin.h"
42 #include "preferences.h"
43 #include "shellprocess.h"
44 #include "templatelistview.h"
45 #include "templatemenuaction.h"
48 #include <kcalcore/event.h>
49 #include <kcalcore/icalformat.h>
50 #include <kcalcore/person.h>
51 #include <kcalcore/duration.h>
52 using namespace KCalCore
;
54 #include <kcal/event.h>
55 #include <kcal/icalformat.h>
56 #include <kcal/person.h>
57 #include <kcal/duration.h>
60 #include <kpimidentities/identitymanager.h>
61 #include <kpimidentities/identity.h>
62 #include <kholidays/holidays.h>
64 #include <kconfiggroup.h>
66 #include <ktoggleaction.h>
67 #include <kactioncollection.h>
68 #include <kdbusservicestarter.h>
71 #include <kstandarddirs.h>
73 #include <ksystemtimezone.h>
74 #include <kstandardguiitem.h>
75 #include <kstandardshortcut.h>
76 #include <kfiledialog.h>
78 #include <kio/netaccess.h>
79 #include <kfileitem.h>
81 #include <ktoolinvocation.h>
84 #include <kwindowsystem.h>
85 #include <kxmessages.h>
86 #include <kstartupinfo.h>
93 #include <QDesktopWidget>
94 #include <QtDBus/QtDBus>
99 using namespace Akonadi
;
105 bool refreshAlarmsQueued
= false;
106 QString korganizerName
= "korganizer";
107 QString korgStartError
;
108 QDBusInterface
* korgInterface
= 0;
110 const char* KMAIL_DBUS_SERVICE
= "org.kde.kmail";
111 //const char* KMAIL_DBUS_IFACE = "org.kde.kmail.kmail";
112 //const char* KMAIL_DBUS_WINDOW_PATH = "/kmail/kmail_mainwindow_1";
113 const char* KORG_DBUS_SERVICE
= "org.kde.korganizer";
114 const char* KORG_DBUS_IFACE
= "org.kde.korganizer.Korganizer";
115 // D-Bus object path of KOrganizer's notification interface
116 #define KORG_DBUS_PATH "/Korganizer"
117 #define KORG_DBUS_LOAD_PATH "/korganizer_PimApplication"
118 //const char* KORG_DBUS_WINDOW_PATH = "/korganizer/MainWindow_1";
119 const QString KORGANIZER_UID
= QString::fromLatin1("-korg");
121 const char* ALARM_OPTS_FILE
= "alarmopts";
122 const char* DONT_SHOW_ERRORS_GROUP
= "DontShowErrors";
124 void editNewTemplate(EditAlarmDlg::Type
, const KAEvent
* preset
, QWidget
* parent
);
125 KAlarm::UpdateStatus
sendToKOrganizer(const KAEvent
*);
126 KAlarm::UpdateStatus
deleteFromKOrganizer(const QString
& eventID
);
127 KAlarm::UpdateStatus
runKOrganizer();
128 QString
uidKOrganizer(const QString
& eventID
);
135 Private
* Private::mInstance
= 0;
137 /******************************************************************************
138 * Display a main window with the specified event selected.
141 MainWindow
* displayMainWindowSelected(Akonadi::Item::Id eventId
)
143 MainWindow
* displayMainWindowSelected(const QString
& eventId
)
146 MainWindow
* win
= MainWindow::firstWindow();
149 if (theApp()->checkCalendar()) // ensure calendar is open
151 win
= MainWindow::create();
157 // There is already a main window, so make it the active window
158 win
->hide(); // in case it's on a different desktop
159 win
->setWindowState(win
->windowState() & ~Qt::WindowMinimized
);
162 win
->activateWindow();
165 if (win
&& eventId
>= 0)
166 win
->selectEvent(eventId
);
168 if (win
&& !eventId
.isEmpty())
169 win
->selectEvent(eventId
);
174 /******************************************************************************
175 * Create an "Alarms Enabled/Enable Alarms" action.
177 KToggleAction
* createAlarmEnableAction(QObject
* parent
)
179 KToggleAction
* action
= new KToggleAction(i18nc("@action", "Enable &Alarms"), parent
);
180 action
->setChecked(theApp()->alarmsEnabled());
181 QObject::connect(action
, SIGNAL(toggled(bool)), theApp(), SLOT(setAlarmsEnabled(bool)));
182 // The following line ensures that all instances are kept in the same state
183 QObject::connect(theApp(), SIGNAL(alarmEnabledToggled(bool)), action
, SLOT(setChecked(bool)));
187 /******************************************************************************
188 * Create a "Stop Play" action.
190 KAction
* createStopPlayAction(QObject
* parent
)
192 KAction
* action
= new KAction(KIcon("media-playback-stop"), i18nc("@action", "Stop Play"), parent
);
193 action
->setEnabled(MessageWin::isAudioPlaying());
194 QObject::connect(action
, SIGNAL(triggered(bool)), theApp(), SLOT(stopAudio()));
195 // The following line ensures that all instances are kept in the same state
196 QObject::connect(theApp(), SIGNAL(audioPlaying(bool)), action
, SLOT(setEnabled(bool)));
200 /******************************************************************************
201 * Create a "Spread Windows" action.
203 KToggleAction
* createSpreadWindowsAction(QObject
* parent
)
205 KToggleAction
* action
= new KToggleAction(i18nc("@action", "Spread Windows"), parent
);
206 QObject::connect(action
, SIGNAL(triggered(bool)), theApp(), SLOT(spreadWindows(bool)));
207 // The following line ensures that all instances are kept in the same state
208 QObject::connect(theApp(), SIGNAL(spreadWindowsToggled(bool)), action
, SLOT(setChecked(bool)));
212 /******************************************************************************
213 * Add a new active (non-archived) alarm.
214 * Save it in the calendar file and add it to every main window instance.
215 * Parameters: msgParent = parent widget for any calendar selection prompt or
217 * event - is updated with the actual event ID.
220 UpdateStatus
addEvent(KAEvent
& event
, Collection
* calendar
, QWidget
* msgParent
, int options
, bool showKOrgErr
)
222 UpdateStatus
addEvent(KAEvent
& event
, AlarmResource
* calendar
, QWidget
* msgParent
, int options
, bool showKOrgErr
)
225 kDebug() << event
.id();
226 bool cancelled
= false;
227 UpdateStatus status
= UPDATE_OK
;
228 if (!theApp()->checkCalendar()) // ensure calendar is open
229 status
= UPDATE_FAILED
;
232 // Save the event details in the calendar file, and get the new event ID
233 AlarmCalendar
* cal
= AlarmCalendar::resources();
234 KAEvent
* newev
= new KAEvent(event
);
236 if (!cal
->addEvent(*newev
, msgParent
, (options
& USE_EVENT_ID
), calendar
, (options
& NO_RESOURCE_PROMPT
), &cancelled
))
238 if (!cal
->addEvent(newev
, msgParent
, (options
& USE_EVENT_ID
), calendar
, (options
& NO_RESOURCE_PROMPT
), &cancelled
))
242 status
= UPDATE_FAILED
;
246 event
= *newev
; // update event ID etc.
248 status
= SAVE_FAILED
;
250 if (status
== UPDATE_OK
)
252 if ((options
& ALLOW_KORG_UPDATE
) && event
.copyToKOrganizer())
254 UpdateStatus st
= sendToKOrganizer(newev
); // tell KOrganizer to show the event
260 // Update the window lists
261 EventListModel::alarms()->addEvent(newev
);
266 if (status
!= UPDATE_OK
&& !cancelled
&& msgParent
)
267 displayUpdateError(msgParent
, status
, ERR_ADD
, 1, 1, showKOrgErr
);
271 /******************************************************************************
272 * Add a list of new active (non-archived) alarms.
273 * Save them in the calendar file and add them to every main window instance.
274 * The events are updated with their actual event IDs.
276 UpdateStatus
addEvents(QVector
<KAEvent
>& events
, QWidget
* msgParent
, bool allowKOrgUpdate
, bool showKOrgErr
)
278 kDebug() << events
.count();
279 if (events
.isEmpty())
283 UpdateStatus status
= UPDATE_OK
;
285 Collection collection
;
287 AlarmResource
* resource
;
289 if (!theApp()->checkCalendar()) // ensure calendar is open
290 status
= UPDATE_FAILED
;
294 collection
= CollectionControlModel::instance()->destination(KAlarm::CalEvent::ACTIVE
, msgParent
);
295 if (!collection
.isValid())
297 resource
= AlarmResources::instance()->destination(KAlarm::CalEvent::ACTIVE
, msgParent
);
301 kDebug() << "No calendar";
302 status
= UPDATE_FAILED
;
305 if (status
== UPDATE_OK
)
308 AlarmCalendar
* cal
= AlarmCalendar::resources();
309 for (int i
= 0, end
= events
.count(); i
< end
; ++i
)
311 // Save the event details in the calendar file, and get the new event ID
313 KAEvent
* const newev
= &events
[i
];
314 if (!cal
->addEvent(events
[i
], msgParent
, false, &collection
))
316 KAEvent
* newev
= new KAEvent(events
[i
]);
317 if (!cal
->addEvent(newev
, msgParent
, false, resource
))
323 status
= UPDATE_ERROR
;
328 events
[i
] = *newev
; // update event ID etc.
330 if (allowKOrgUpdate
&& newev
->copyToKOrganizer())
332 UpdateStatus st
= sendToKOrganizer(newev
); // tell KOrganizer to show the event
342 // Update the window lists, but not yet which item is selected
343 EventListModel::alarms()->addEvent(newev
);
344 // selectID = newev->id();
347 if (warnErr
== events
.count())
348 status
= UPDATE_FAILED
;
349 else if (!cal
->save())
351 status
= SAVE_FAILED
;
352 warnErr
= 0; // everything failed
356 if (status
!= UPDATE_OK
&& msgParent
)
357 displayUpdateError(msgParent
, status
, ERR_ADD
, (warnErr
? warnErr
: events
.count()), warnKOrg
, showKOrgErr
);
361 /******************************************************************************
362 * Save the event in the archived calendar and adjust every main window instance.
363 * The event's ID is changed to an archived ID if necessary.
366 bool addArchivedEvent(KAEvent
& event
, Collection
* collection
)
368 bool addArchivedEvent(KAEvent
& event
, AlarmResource
* resource
)
371 kDebug() << event
.id();
372 QString oldid
= event
.id();
373 bool archiving
= (event
.category() == KAlarm::CalEvent::ACTIVE
);
374 if (archiving
&& !Preferences::archivedKeepDays())
375 return false; // expired alarms aren't being kept
376 AlarmCalendar
* cal
= AlarmCalendar::resources();
378 KAEvent
newevent(event
);
379 newevent
.setItemId(-1); // invalidate the Akonadi item ID since it's a new item
380 KAEvent
* const newev
= &newevent
;
382 KAEvent
* newev
= new KAEvent(event
);
386 newev
->setCategory(KAlarm::CalEvent::ARCHIVED
); // this changes the event ID
387 newev
->setCreatedDateTime(KDateTime::currentUtcDateTime()); // time stamp to control purging
389 // Note that archived resources are automatically saved after changes are made
391 if (!cal
->addEvent(newevent
, 0, false, collection
))
394 if (!cal
->addEvent(newev
, 0, false, resource
))
396 delete newev
; // failed to add to calendar - leave event in its original state
400 event
= *newev
; // update event ID etc.
403 // Update window lists.
404 // Note: updateEvent() is not used here since that doesn't trigger refiltering
405 // of the alarm list, resulting in the archived event still remaining visible
406 // even if archived events are supposed to be hidden.
408 EventListModel::alarms()->removeEvent(oldid
);
409 EventListModel::alarms()->addEvent(newev
);
414 /******************************************************************************
415 * Add a new template.
416 * Save it in the calendar file and add it to every template list view.
417 * 'event' is updated with the actual event ID.
418 * Parameters: promptParent = parent widget for any calendar selection prompt.
421 UpdateStatus
addTemplate(KAEvent
& event
, Collection
* collection
, QWidget
* msgParent
)
423 UpdateStatus
addTemplate(KAEvent
& event
, AlarmResource
* resource
, QWidget
* msgParent
)
426 kDebug() << event
.id();
427 UpdateStatus status
= UPDATE_OK
;
429 // Add the template to the calendar file
430 AlarmCalendar
* cal
= AlarmCalendar::resources();
432 KAEvent
newev(event
);
433 if (!cal
->addEvent(newev
, msgParent
, false, collection
))
434 status
= UPDATE_FAILED
;
436 KAEvent
* newev
= new KAEvent(event
);
437 if (!cal
->addEvent(newev
, msgParent
, false, resource
))
440 status
= UPDATE_FAILED
;
446 event
= newev
; // update event ID etc.
448 event
= *newev
; // update event ID etc.
451 status
= SAVE_FAILED
;
455 // Update the window lists
456 EventListModel::templates()->addEvent(newev
);
463 displayUpdateError(msgParent
, status
, ERR_TEMPLATE
, 1);
467 /******************************************************************************
468 * Modify an active (non-archived) alarm in the calendar file and in every main
470 * The new event must have a different event ID from the old one.
472 UpdateStatus
modifyEvent(KAEvent
& oldEvent
, KAEvent
& newEvent
, QWidget
* msgParent
, bool showKOrgErr
)
474 kDebug() << oldEvent
.id();
476 UpdateStatus status
= UPDATE_OK
;
477 if (!newEvent
.isValid())
479 deleteEvent(oldEvent
, true);
480 status
= UPDATE_FAILED
;
484 QString oldId
= oldEvent
.id();
485 if (oldEvent
.copyToKOrganizer())
487 // Tell KOrganizer to delete its old event.
488 // But ignore errors, because the user could have manually
489 // deleted it since KAlarm asked KOrganizer to set it up.
490 deleteFromKOrganizer(oldId
);
493 // Update the event in the calendar file, and get the new event ID
494 AlarmCalendar
* cal
= AlarmCalendar::resources();
495 if (!cal
->modifyEvent(oldId
, newEvent
))
496 status
= UPDATE_FAILED
;
498 // Delete from the window lists to prevent the event's invalid
499 // pointer being accessed.
500 EventListModel::alarms()->removeEvent(oldId
);
502 // Update the event in the calendar file, and get the new event ID
503 KAEvent
* newev
= new KAEvent(newEvent
);
504 AlarmCalendar
* cal
= AlarmCalendar::resources();
505 if (!cal
->modifyEvent(oldId
, newev
))
508 status
= UPDATE_FAILED
;
517 status
= SAVE_FAILED
;
518 if (status
== UPDATE_OK
)
520 if (newEvent
.copyToKOrganizer())
522 UpdateStatus st
= sendToKOrganizer(&newEvent
); // tell KOrganizer to show the new event
527 // Remove "Don't show error messages again" for the old alarm
528 setDontShowErrors(oldId
);
531 // Update the window lists
532 EventListModel::alarms()->addEvent(newev
);
538 if (status
!= UPDATE_OK
&& msgParent
)
539 displayUpdateError(msgParent
, status
, ERR_MODIFY
, 1, 1, showKOrgErr
);
543 /******************************************************************************
544 * Update an active (non-archived) alarm from the calendar file and from every
545 * main window instance.
546 * The new event will have the same event ID as the old one.
547 * The event is not updated in KOrganizer, since this function is called when an
548 * existing alarm is rescheduled (due to recurrence or deferral).
550 UpdateStatus
updateEvent(KAEvent
& event
, QWidget
* msgParent
, bool archiveOnDelete
)
552 kDebug() << event
.id();
554 if (!event
.isValid())
555 deleteEvent(event
, archiveOnDelete
);
558 // Update the event in the calendar file.
559 AlarmCalendar
* cal
= AlarmCalendar::resources();
561 cal
->updateEvent(event
);
563 KAEvent
* newEvent
= cal
->updateEvent(event
);
568 displayUpdateError(msgParent
, SAVE_FAILED
, ERR_ADD
, 1);
573 // Update the window lists
574 EventListModel::alarms()->updateEvent(newEvent
);
580 /******************************************************************************
581 * Update a template in the calendar file and in every template list view.
582 * If 'selectionView' is non-null, the selection highlight is moved to the
583 * updated event in that listView instance.
585 UpdateStatus
updateTemplate(KAEvent
& event
, QWidget
* msgParent
)
587 AlarmCalendar
* cal
= AlarmCalendar::resources();
588 KAEvent
* newEvent
= cal
->updateEvent(event
);
589 UpdateStatus status
= UPDATE_OK
;
591 status
= UPDATE_FAILED
;
592 else if (!cal
->save())
593 status
= SAVE_FAILED
;
594 if (status
!= UPDATE_OK
)
597 displayUpdateError(msgParent
, SAVE_FAILED
, ERR_TEMPLATE
, 1);
602 EventListModel::templates()->updateEvent(newEvent
);
607 /******************************************************************************
608 * Delete alarms from the calendar file and from every main window instance.
609 * If the events are archived, the events' IDs are changed to archived IDs if necessary.
611 UpdateStatus
deleteEvent(KAEvent
& event
, bool archive
, QWidget
* msgParent
, bool showKOrgErr
)
614 QVector
<KAEvent
> events(1, event
);
616 KAEvent::List events
;
619 return deleteEvents(events
, archive
, msgParent
, showKOrgErr
);
623 UpdateStatus
deleteEvents(QVector
<KAEvent
>& events
, bool archive
, QWidget
* msgParent
, bool showKOrgErr
)
625 UpdateStatus
deleteEvents(KAEvent::List
& events
, bool archive
, QWidget
* msgParent
, bool showKOrgErr
)
628 kDebug() << events
.count();
629 if (events
.isEmpty())
633 UpdateStatus status
= UPDATE_OK
;
634 AlarmCalendar
* cal
= AlarmCalendar::resources();
635 bool deleteWakeFromSuspendAlarm
= false;
636 QString wakeFromSuspendId
= checkRtcWakeConfig().value(0);
637 for (int i
= 0, end
= events
.count(); i
< end
; ++i
)
639 // Save the event details in the calendar file, and get the new event ID
641 KAEvent
* event
= &events
[i
];
643 KAEvent
* event
= events
[i
];
645 QString id
= event
->id();
648 // Update the window lists and clear stored command errors
649 EventListModel::alarms()->removeEvent(id
);
650 event
->setCommandError(KAEvent::CMD_NO_ERROR
);
653 // Delete the event from the calendar file
654 if (event
->category() != KAlarm::CalEvent::ARCHIVED
)
656 if (event
->copyToKOrganizer())
658 // The event was shown in KOrganizer, so tell KOrganizer to
659 // delete it. But ignore errors, because the user could have
660 // manually deleted it from KOrganizer since it was set up.
661 UpdateStatus st
= deleteFromKOrganizer(id
);
669 if (archive
&& event
->toBeArchived())
672 addArchivedEvent(ev
); // this changes the event ID to an archived ID
676 if (!cal
->deleteEvent(*event
, false)) // don't save calendar after deleting
678 if (!cal
->deleteEvent(id
, false)) // don't save calendar after deleting
681 status
= UPDATE_ERROR
;
685 if (id
== wakeFromSuspendId
)
686 deleteWakeFromSuspendAlarm
= true;
688 // Remove "Don't show error messages again" for this alarm
689 setDontShowErrors(id
);
692 if (warnErr
== events
.count())
693 status
= UPDATE_FAILED
;
694 else if (!cal
->save()) // save the calendars now
696 status
= SAVE_FAILED
;
697 warnErr
= events
.count();
699 if (status
!= UPDATE_OK
&& msgParent
)
700 displayUpdateError(msgParent
, status
, ERR_DELETE
, warnErr
, warnKOrg
, showKOrgErr
);
702 // Remove any wake-from-suspend scheduled for a deleted alarm
703 if (deleteWakeFromSuspendAlarm
&& !wakeFromSuspendId
.isEmpty())
704 cancelRtcWake(msgParent
, wakeFromSuspendId
);
709 /******************************************************************************
710 * Delete templates from the calendar file and from every template list view.
713 UpdateStatus
deleteTemplates(const KAEvent::List
& events
, QWidget
* msgParent
)
715 UpdateStatus
deleteTemplates(const QStringList
& eventIDs
, QWidget
* msgParent
)
719 int count
= events
.count();
721 int count
= eventIDs
.count();
727 UpdateStatus status
= UPDATE_OK
;
728 AlarmCalendar
* cal
= AlarmCalendar::resources();
729 for (int i
= 0, end
= count
; i
< end
; ++i
)
731 // Update the window lists
733 QString id
= eventIDs
[i
];
734 EventListModel::templates()->removeEvent(id
);
737 // Delete the template from the calendar file
738 AlarmCalendar
* cal
= AlarmCalendar::resources();
740 if (!cal
->deleteEvent(*events
[i
], false)) // don't save calendar after deleting
742 if (!cal
->deleteEvent(id
, false)) // don't save calendar after deleting
745 status
= UPDATE_ERROR
;
750 if (warnErr
== count
)
751 status
= UPDATE_FAILED
;
752 else if (!cal
->save()) // save the calendars now
754 status
= SAVE_FAILED
;
757 if (status
!= UPDATE_OK
&& msgParent
)
758 displayUpdateError(msgParent
, status
, ERR_TEMPLATE
, warnErr
);
762 /******************************************************************************
763 * Delete an alarm from the display calendar.
765 void deleteDisplayEvent(const QString
& eventID
)
768 AlarmCalendar
* cal
= AlarmCalendar::displayCalendarOpen();
771 cal
->deleteDisplayEvent(eventID
, true); // save calendar after deleting
773 cal
->deleteEvent(eventID
, true); // save calendar after deleting
777 /******************************************************************************
778 * Undelete archived alarms, and update every main window instance.
779 * The archive bit is set to ensure that they get re-archived if deleted again.
780 * 'ineligibleIDs' is filled in with the IDs of any ineligible events.
783 UpdateStatus
reactivateEvent(KAEvent
& event
, Collection
* calendar
, QWidget
* msgParent
, bool showKOrgErr
)
785 UpdateStatus
reactivateEvent(KAEvent
& event
, AlarmResource
* calendar
, QWidget
* msgParent
, bool showKOrgErr
)
790 QVector
<KAEvent
> events(1, event
);
792 KAEvent::List events
;
795 return reactivateEvents(events
, ids
, calendar
, msgParent
, showKOrgErr
);
799 UpdateStatus
reactivateEvents(QVector
<KAEvent
>& events
, QStringList
& ineligibleIDs
, Collection
* col
, QWidget
* msgParent
, bool showKOrgErr
)
801 UpdateStatus
reactivateEvents(KAEvent::List
& events
, QStringList
& ineligibleIDs
, AlarmResource
* resource
, QWidget
* msgParent
, bool showKOrgErr
)
804 kDebug() << events
.count();
805 ineligibleIDs
.clear();
806 if (events
.isEmpty())
810 UpdateStatus status
= UPDATE_OK
;
812 Collection collection
;
815 if (!collection
.isValid())
816 collection
= CollectionControlModel::instance()->destination(KAlarm::CalEvent::ACTIVE
, msgParent
);
817 if (!collection
.isValid())
820 resource
= AlarmResources::instance()->destination(KAlarm::CalEvent::ACTIVE
, msgParent
);
824 kDebug() << "No calendar";
825 status
= UPDATE_FAILED
;
826 warnErr
= events
.count();
832 AlarmCalendar
* cal
= AlarmCalendar::resources();
833 KDateTime now
= KDateTime::currentUtcDateTime();
834 for (int i
= 0, end
= events
.count(); i
< end
; ++i
)
836 // Delete the event from the archived resource
838 KAEvent
* event
= &events
[i
];
840 KAEvent
* event
= events
[i
];
842 if (event
->category() != KAlarm::CalEvent::ARCHIVED
843 || !event
->occursAfter(now
, true))
845 ineligibleIDs
+= event
->id();
851 KAEvent
newevent(*event
);
852 KAEvent
* const newev
= &newevent
;
854 KAEvent
* newev
= new KAEvent(*event
);
855 QString oldid
= event
->id();
857 newev
->setCategory(KAlarm::CalEvent::ACTIVE
); // this changes the event ID
858 if (newev
->recurs() || newev
->repetition())
859 newev
->setNextOccurrence(now
); // skip any recurrences in the past
860 newev
->setArchive(); // ensure that it gets re-archived if it is deleted
862 // Save the event details in the calendar file.
863 // This converts the event ID.
865 if (!cal
->addEvent(newevent
, msgParent
, true, &collection
))
867 if (!cal
->addEvent(newev
, msgParent
, true, resource
))
873 status
= UPDATE_ERROR
;
877 if (newev
->copyToKOrganizer())
879 UpdateStatus st
= sendToKOrganizer(newev
); // tell KOrganizer to show the event
889 // Update the window lists
890 EventListModel::alarms()->updateEvent(oldid
, newev
);
891 // selectID = newev->id();
895 if (cal
->event(event
->id()) // no error if event doesn't exist in archived resource
896 && !cal
->deleteEvent(*event
, false)) // don't save calendar after deleting
898 if (cal
->event(oldid
) // no error if event doesn't exist in archived resource
899 && !cal
->deleteEvent(oldid
, false)) // don't save calendar after deleting
902 status
= UPDATE_ERROR
;
906 events
[i
] = newevent
;
912 if (warnErr
== count
)
913 status
= UPDATE_FAILED
;
914 // Save the calendars, even if all events failed, since more than one calendar was updated
915 if (!cal
->save() && status
!= UPDATE_FAILED
)
917 status
= SAVE_FAILED
;
921 if (status
!= UPDATE_OK
&& msgParent
)
922 displayUpdateError(msgParent
, status
, ERR_REACTIVATE
, warnErr
, warnKOrg
, showKOrgErr
);
926 /******************************************************************************
927 * Enable or disable alarms in the calendar file and in every main window instance.
928 * The new events will have the same event IDs as the old ones.
931 UpdateStatus
enableEvents(QVector
<KAEvent
>& events
, bool enable
, QWidget
* msgParent
)
933 UpdateStatus
enableEvents(KAEvent::List
& events
, bool enable
, QWidget
* msgParent
)
936 kDebug() << events
.count();
937 if (events
.isEmpty())
939 UpdateStatus status
= UPDATE_OK
;
940 AlarmCalendar
* cal
= AlarmCalendar::resources();
941 bool deleteWakeFromSuspendAlarm
= false;
942 QString wakeFromSuspendId
= checkRtcWakeConfig().value(0);
943 for (int i
= 0, end
= events
.count(); i
< end
; ++i
)
946 KAEvent
* event
= &events
[i
];
948 KAEvent
* event
= events
[i
];
950 if (event
->category() == KAlarm::CalEvent::ACTIVE
951 && enable
!= event
->enabled())
953 event
->setEnabled(enable
);
955 if (!enable
&& event
->id() == wakeFromSuspendId
)
956 deleteWakeFromSuspendAlarm
= true;
958 // Update the event in the calendar file
959 KAEvent
* newev
= cal
->updateEvent(event
);
961 kError() << "Error updating event in calendar:" << event
->id();
964 cal
->disabledChanged(newev
);
966 // If we're disabling a display alarm, close any message window
967 if (!enable
&& (event
->actionTypes() & KAEvent::ACT_DISPLAY
))
969 MessageWin
* win
= MessageWin::findEvent(event
->id());
974 // Update the window lists
975 EventListModel::alarms()->updateEvent(newev
);
982 status
= SAVE_FAILED
;
983 if (status
!= UPDATE_OK
&& msgParent
)
984 displayUpdateError(msgParent
, status
, ERR_ADD
, events
.count(), 0);
986 // Remove any wake-from-suspend scheduled for a disabled alarm
987 if (deleteWakeFromSuspendAlarm
&& !wakeFromSuspendId
.isEmpty())
988 cancelRtcWake(msgParent
, wakeFromSuspendId
);
993 /******************************************************************************
994 * This method must only be called from the main KAlarm queue processing loop,
995 * to prevent asynchronous calendar operations interfering with one another.
997 * Purge all archived events from the default archived alarm resource whose end
998 * time is longer ago than 'purgeDays'. All events are deleted if 'purgeDays' is
1001 void purgeArchive(int purgeDays
)
1005 kDebug() << purgeDays
;
1006 QDate cutoff
= KDateTime::currentLocalDate().addDays(-purgeDays
);
1008 Collection collection
= CollectionControlModel::getStandard(KAlarm::CalEvent::ARCHIVED
);
1009 if (!collection
.isValid())
1011 KAEvent::List events
= AlarmCalendar::resources()->events(collection
);
1012 for (int i
= 0; i
< events
.count(); )
1014 if (purgeDays
&& events
[i
]->createdDateTime().date() >= cutoff
)
1020 AlarmResource
* resource
= AlarmResources::instance()->getStandardResource(KAlarm::CalEvent::ARCHIVED
);
1023 KAEvent::List events
= AlarmCalendar::resources()->events(resource
);
1024 for (int i
= 0; i
< events
.count(); )
1026 KAEvent
* event
= events
[i
];
1027 Incidence
* kcalIncidence
= resource
->incidence(event
->id());
1028 if (purgeDays
&& kcalIncidence
&& kcalIncidence
->created().date() >= cutoff
)
1031 EventListModel::alarms()->removeEvent(events
[i
++]); // update the window lists
1034 if (!events
.isEmpty())
1035 AlarmCalendar::resources()->purgeEvents(events
); // delete the events and save the calendar
1038 /******************************************************************************
1039 * Display an error message about an error when saving an event.
1041 void displayUpdateError(QWidget
* parent
, UpdateStatus status
, UpdateError code
, int nAlarms
, int nKOrgAlarms
, bool showKOrgError
)
1044 if (status
> UPDATE_KORG_ERR
)
1050 errmsg
= (nAlarms
> 1) ? i18nc("@info", "Error saving alarms")
1051 : i18nc("@info", "Error saving alarm");
1054 errmsg
= (nAlarms
> 1) ? i18nc("@info", "Error deleting alarms")
1055 : i18nc("@info", "Error deleting alarm");
1057 case ERR_REACTIVATE
:
1058 errmsg
= (nAlarms
> 1) ? i18nc("@info", "Error saving reactivated alarms")
1059 : i18nc("@info", "Error saving reactivated alarm");
1062 errmsg
= (nAlarms
> 1) ? i18nc("@info", "Error saving alarm templates")
1063 : i18nc("@info", "Error saving alarm template");
1066 KAMessageBox::error(parent
, errmsg
);
1068 else if (showKOrgError
)
1069 displayKOrgUpdateError(parent
, code
, status
, nKOrgAlarms
);
1072 /******************************************************************************
1073 * Display an error message corresponding to a specified alarm update error code.
1075 void displayKOrgUpdateError(QWidget
* parent
, UpdateError code
, UpdateStatus korgError
, int nAlarms
)
1081 case ERR_REACTIVATE
:
1082 errmsg
= (nAlarms
> 1) ? i18nc("@info", "Unable to show alarms in KOrganizer")
1083 : i18nc("@info", "Unable to show alarm in KOrganizer");
1086 errmsg
= i18nc("@info", "Unable to update alarm in KOrganizer");
1089 errmsg
= (nAlarms
> 1) ? i18nc("@info", "Unable to delete alarms from KOrganizer")
1090 : i18nc("@info", "Unable to delete alarm from KOrganizer");
1096 if (korgError
== UPDATE_KORG_ERRSTART
)
1097 msg
= i18nc("@info", "<para>%1</para><para>(KOrganizer not fully started)</para>", errmsg
);
1098 else if (korgError
== UPDATE_KORG_ERR
)
1099 msg
= i18nc("@info", "<para>%1</para><para>(Error communicating with KOrganizer)</para>", errmsg
);
1102 KAMessageBox::error(parent
, msg
);
1105 /******************************************************************************
1106 * Execute a New Alarm dialog for the specified alarm type.
1108 void editNewAlarm(EditAlarmDlg::Type type
, QWidget
* parent
)
1110 execNewAlarmDlg(EditAlarmDlg::create(false, type
, parent
));
1113 /******************************************************************************
1114 * Execute a New Alarm dialog for the specified alarm type.
1116 void editNewAlarm(KAEvent::SubAction action
, QWidget
* parent
, const AlarmText
* text
)
1118 bool setAction
= false;
1119 EditAlarmDlg::Type type
;
1122 case KAEvent::MESSAGE
:
1124 type
= EditAlarmDlg::DISPLAY
;
1127 case KAEvent::COMMAND
:
1128 type
= EditAlarmDlg::COMMAND
;
1130 case KAEvent::EMAIL
:
1131 type
= EditAlarmDlg::EMAIL
;
1133 case KAEvent::AUDIO
:
1134 type
= EditAlarmDlg::AUDIO
;
1139 EditAlarmDlg
* editDlg
= EditAlarmDlg::create(false, type
, parent
);
1140 if (setAction
|| text
)
1141 editDlg
->setAction(action
, *text
);
1142 execNewAlarmDlg(editDlg
);
1145 /******************************************************************************
1146 * Execute a New Alarm dialog, optionally either presetting it to the supplied
1147 * event, or setting the action and text.
1149 void editNewAlarm(const KAEvent
* preset
, QWidget
* parent
)
1151 execNewAlarmDlg(EditAlarmDlg::create(false, preset
, true, parent
));
1154 /******************************************************************************
1155 * Common code for editNewAlarm() variants.
1157 void execNewAlarmDlg(EditAlarmDlg
* editDlg
)
1159 // Create a PrivateNewAlarmDlg parented by editDlg.
1160 // It will be deleted when editDlg is closed.
1161 new PrivateNewAlarmDlg(editDlg
);
1164 editDlg
->activateWindow();
1167 PrivateNewAlarmDlg::PrivateNewAlarmDlg(EditAlarmDlg
* dlg
)
1170 connect(dlg
, SIGNAL(accepted()), SLOT(okClicked()));
1173 /******************************************************************************
1174 * Called when the dialogue is accepted (e.g. by clicking the OK button).
1175 * Creates the event specified in the instance's dialogue.
1177 void PrivateNewAlarmDlg::okClicked()
1179 accept(static_cast<EditAlarmDlg
*>(parent()));
1182 /******************************************************************************
1183 * Creates the event specified in a given dialogue.
1185 void PrivateNewAlarmDlg::accept(EditAlarmDlg
* editDlg
)
1189 Collection calendar
;
1191 AlarmResource
* calendar
;
1193 editDlg
->getEvent(event
, calendar
);
1195 // Add the alarm to the displayed lists and to the calendar file
1197 UpdateStatus status
= addEvent(event
, &calendar
, editDlg
);
1199 UpdateStatus status
= addEvent(event
, calendar
, editDlg
);
1205 case UPDATE_KORG_ERR
:
1206 case UPDATE_KORG_ERRSTART
:
1207 case UPDATE_KORG_FUNCERR
:
1208 displayKOrgUpdateError(editDlg
, ERR_ADD
, status
, 1);
1213 Undo::saveAdd(event
, calendar
);
1215 outputAlarmWarnings(editDlg
, &event
);
1218 /******************************************************************************
1219 * Display the alarm edit dialog to edit a new alarm, preset with a template.
1221 bool editNewAlarm(const QString
& templateName
, QWidget
* parent
)
1223 if (!templateName
.isEmpty())
1225 KAEvent
* templateEvent
= AlarmCalendar::resources()->templateEvent(templateName
);
1226 if (templateEvent
->isValid())
1228 editNewAlarm(templateEvent
, parent
);
1231 kWarning() << templateName
<< ": template not found";
1236 /******************************************************************************
1237 * Create a new template.
1239 void editNewTemplate(EditAlarmDlg::Type type
, QWidget
* parent
)
1241 ::editNewTemplate(type
, 0, parent
);
1244 /******************************************************************************
1245 * Create a new template, based on an existing event or template.
1247 void editNewTemplate(const KAEvent
* preset
, QWidget
* parent
)
1249 ::editNewTemplate(EditAlarmDlg::Type(0), preset
, parent
);
1252 /******************************************************************************
1253 * Check the config as to whether there is a wake-on-suspend alarm pending, and
1254 * if so, delete it from the config if it has expired.
1255 * If 'checkExists' is true, the config entry will only be returned if the
1257 * Reply = config entry: [0] = event ID, [1] = trigger time (time_t).
1258 * = empty list if none or expired.
1260 QStringList
checkRtcWakeConfig(bool checkEventExists
)
1262 KConfigGroup
config(KGlobal::config(), "General");
1263 QStringList params
= config
.readEntry("RtcWake", QStringList());
1264 if (params
.count() == 2 && params
[1].toUInt() > KDateTime::currentUtcDateTime().toTime_t())
1266 if (checkEventExists
&& !AlarmCalendar::getEvent(params
[0]))
1267 return QStringList();
1268 return params
; // config entry is valid
1270 if (!params
.isEmpty())
1272 config
.deleteEntry("RtcWake"); // delete the expired config entry
1275 return QStringList();
1278 /******************************************************************************
1279 * Delete any wake-on-suspend alarm from the config.
1281 void deleteRtcWakeConfig()
1283 KConfigGroup
config(KGlobal::config(), "General");
1284 config
.deleteEntry("RtcWake");
1288 /******************************************************************************
1289 * Delete any wake-on-suspend alarm, optionally only for a specified event.
1291 void cancelRtcWake(QWidget
* msgParent
, const QString
& eventId
)
1293 QStringList wakeup
= checkRtcWakeConfig();
1294 if (!wakeup
.isEmpty() && (eventId
.isEmpty() || wakeup
[0] == eventId
))
1296 Private::instance()->mMsgParent
= msgParent
? msgParent
: MainWindow::mainMainWindow();
1297 QTimer::singleShot(0, Private::instance(), SLOT(cancelRtcWake()));
1301 /******************************************************************************
1302 * Delete any wake-on-suspend alarm.
1304 void Private::cancelRtcWake()
1306 // setRtcWakeTime will only work with a parent window specified
1307 setRtcWakeTime(0, mMsgParent
);
1308 deleteRtcWakeConfig();
1309 KAMessageBox::information(mMsgParent
, i18nc("info", "The scheduled Wake from Suspend has been cancelled."));
1312 /******************************************************************************
1313 * Set the wakeup time for the system.
1314 * Set 'triggerTime' to zero to cancel the wakeup.
1315 * Reply = true if successful.
1317 bool setRtcWakeTime(unsigned triggerTime
, QWidget
* parent
)
1320 args
["time"] = triggerTime
;
1321 KAuth::Action
action("org.kde.kalarmrtcwake.settimer");
1322 action
.setHelperID("org.kde.kalarmrtcwake");
1323 action
.setParentWidget(parent
);
1324 action
.setArguments(args
);
1325 KAuth::ActionReply reply
= action
.execute();
1328 QString errmsg
= reply
.errorDescription();
1329 kDebug() << "Error code=" << reply
.errorCode() << errmsg
;
1330 if (errmsg
.isEmpty())
1332 int errcode
= reply
.errorCode();
1333 switch (reply
.type())
1335 case KAuth::ActionReply::KAuthError
:
1336 kDebug() << "Authorisation error:" << errcode
;
1339 case KAuth::ActionReply::AuthorizationDenied
:
1340 case KAuth::ActionReply::UserCancelled
:
1341 return false; // the user should already know about this
1346 case KAuth::ActionReply::HelperError
:
1347 kDebug() << "Helper error:" << errcode
;
1348 errcode
+= 100; // make code distinguishable from KAuthError type
1353 errmsg
= i18nc("@info", "Error obtaining authorization (%1)", errcode
);
1355 KAMessageBox::information(parent
, errmsg
);
1361 } // namespace KAlarm
1365 /******************************************************************************
1366 * Create a new template.
1367 * 'preset' is non-null to base it on an existing event or template; otherwise,
1368 * the alarm type is set to 'type'.
1370 void editNewTemplate(EditAlarmDlg::Type type
, const KAEvent
* preset
, QWidget
* parent
)
1373 if (CollectionControlModel::enabledCollections(KAlarm::CalEvent::TEMPLATE
, true).isEmpty())
1375 if (!AlarmResources::instance()->activeCount(KAlarm::CalEvent::TEMPLATE
, true))
1378 KAMessageBox::sorry(parent
, i18nc("@info", "You must enable a template calendar to save the template in"));
1381 // Use AutoQPointer to guard against crash on application exit while
1382 // the dialogue is still open. It prevents double deletion (both on
1383 // deletion of parent, and on return from this function).
1384 AutoQPointer
<EditAlarmDlg
> editDlg
;
1386 editDlg
= EditAlarmDlg::create(true, preset
, true, parent
);
1388 editDlg
= EditAlarmDlg::create(true, type
, parent
);
1389 if (editDlg
->exec() == QDialog::Accepted
)
1393 Akonadi::Collection calendar
;
1395 AlarmResource
* calendar
;
1397 editDlg
->getEvent(event
, calendar
);
1399 // Add the template to the displayed lists and to the calendar file
1401 KAlarm::addTemplate(event
, &calendar
, editDlg
);
1403 KAlarm::addTemplate(event
, calendar
, editDlg
);
1405 Undo::saveAdd(event
, calendar
);
1413 /******************************************************************************
1414 * Open the Edit Alarm dialog to edit the specified alarm.
1415 * If the alarm is read-only or archived, the dialog is opened read-only.
1417 void editAlarm(KAEvent
* event
, QWidget
* parent
)
1420 if (event
->expired() || AlarmCalendar::resources()->eventReadOnly(event
->itemId()))
1422 if (event
->expired() || AlarmCalendar::resources()->eventReadOnly(event
->id()))
1425 viewAlarm(event
, parent
);
1428 QString id
= event
->id();
1429 // Use AutoQPointer to guard against crash on application exit while
1430 // the dialogue is still open. It prevents double deletion (both on
1431 // deletion of parent, and on return from this function).
1432 AutoQPointer
<EditAlarmDlg
> editDlg
= EditAlarmDlg::create(false, event
, false, parent
, EditAlarmDlg::RES_USE_EVENT_ID
);
1433 if (editDlg
->exec() == QDialog::Accepted
)
1435 if (!AlarmCalendar::resources()->event(id
))
1437 // Event has been deleted while the user was editing the alarm,
1438 // so treat it as a new alarm.
1439 PrivateNewAlarmDlg().accept(editDlg
);
1444 Collection calendar
;
1446 AlarmResource
* calendar
;
1448 bool changeDeferral
= !editDlg
->getEvent(newEvent
, calendar
);
1450 // Update the event in the displays and in the calendar file
1451 Undo::Event
undo(*event
, calendar
);
1454 // The only change has been to an existing deferral
1455 if (updateEvent(newEvent
, editDlg
, true) != UPDATE_OK
) // keep the same event ID
1456 return; // failed to save event
1460 UpdateStatus status
= modifyEvent(*event
, newEvent
, editDlg
);
1461 if (status
!= UPDATE_OK
&& status
<= UPDATE_KORG_ERR
)
1462 displayKOrgUpdateError(editDlg
, ERR_MODIFY
, status
, 1);
1464 Undo::saveEdit(undo
, newEvent
);
1466 outputAlarmWarnings(editDlg
, &newEvent
);
1470 /******************************************************************************
1471 * Display the alarm edit dialog to edit a specified alarm.
1472 * An error occurs if the alarm is read-only or expired.
1474 bool editAlarm(const QString
& eventID
, QWidget
* parent
)
1476 KAEvent
* event
= AlarmCalendar::resources()->event(eventID
);
1479 kError() << eventID
<< ": event ID not found";
1483 if (AlarmCalendar::resources()->eventReadOnly(event
->itemId()))
1485 if (AlarmCalendar::resources()->eventReadOnly(eventID
))
1488 kError() << eventID
<< ": read-only";
1491 switch (event
->category())
1493 case KAlarm::CalEvent::ACTIVE
:
1494 case KAlarm::CalEvent::TEMPLATE
:
1497 kError() << eventID
<< ": event not active or template";
1500 editAlarm(event
, parent
);
1504 /******************************************************************************
1505 * Open the Edit Alarm dialog to edit the specified template.
1506 * If the template is read-only, the dialog is opened read-only.
1508 void editTemplate(KAEvent
* event
, QWidget
* parent
)
1511 if (AlarmCalendar::resources()->eventReadOnly(event
->itemId()))
1513 if (AlarmCalendar::resources()->eventReadOnly(event
->id()))
1516 // The template is read-only, so make the dialogue read-only.
1517 // Use AutoQPointer to guard against crash on application exit while
1518 // the dialogue is still open. It prevents double deletion (both on
1519 // deletion of parent, and on return from this function).
1520 AutoQPointer
<EditAlarmDlg
> editDlg
= EditAlarmDlg::create(true, event
, false, parent
, EditAlarmDlg::RES_PROMPT
, true);
1524 // Use AutoQPointer to guard against crash on application exit while
1525 // the dialogue is still open. It prevents double deletion (both on
1526 // deletion of parent, and on return from this function).
1527 AutoQPointer
<EditAlarmDlg
> editDlg
= EditAlarmDlg::create(true, event
, false, parent
, EditAlarmDlg::RES_USE_EVENT_ID
);
1528 if (editDlg
->exec() == QDialog::Accepted
)
1532 Akonadi::Collection calendar
;
1534 AlarmResource
* calendar
;
1536 editDlg
->getEvent(newEvent
, calendar
);
1537 QString id
= event
->id();
1538 newEvent
.setEventId(id
);
1540 // Update the event in the displays and in the calendar file
1541 Undo::Event
undo(*event
, calendar
);
1542 updateTemplate(newEvent
, editDlg
);
1543 Undo::saveEdit(undo
, newEvent
);
1547 /******************************************************************************
1548 * Open the Edit Alarm dialog to view the specified alarm (read-only).
1550 void viewAlarm(const KAEvent
* event
, QWidget
* parent
)
1552 // Use AutoQPointer to guard against crash on application exit while
1553 // the dialogue is still open. It prevents double deletion (both on
1554 // deletion of parent, and on return from this function).
1555 AutoQPointer
<EditAlarmDlg
> editDlg
= EditAlarmDlg::create(false, event
, false, parent
, EditAlarmDlg::RES_PROMPT
, true);
1559 /******************************************************************************
1560 * Called when OK is clicked in the alarm edit dialog invoked by the Edit button
1561 * in an alarm message window.
1562 * Updates the alarm calendar and closes the dialog.
1565 void updateEditedAlarm(EditAlarmDlg
* editDlg
, KAEvent
& event
, Collection
& calendar
)
1567 void updateEditedAlarm(EditAlarmDlg
* editDlg
, KAEvent
& event
, AlarmResource
* calendar
)
1573 Akonadi::Collection cal
;
1577 editDlg
->getEvent(newEvent
, cal
);
1579 // Update the displayed lists and the calendar file
1580 UpdateStatus status
;
1581 if (AlarmCalendar::resources()->event(event
.id()))
1583 // The old alarm hasn't expired yet, so replace it
1584 Undo::Event
undo(event
, calendar
);
1585 status
= modifyEvent(event
, newEvent
, editDlg
);
1586 Undo::saveEdit(undo
, newEvent
);
1590 // The old event has expired, so simply create a new one
1592 status
= addEvent(newEvent
, &calendar
, editDlg
);
1594 status
= addEvent(newEvent
, calendar
, editDlg
);
1596 Undo::saveAdd(newEvent
, calendar
);
1599 if (status
!= UPDATE_OK
&& status
<= UPDATE_KORG_ERR
)
1600 displayKOrgUpdateError(editDlg
, ERR_MODIFY
, status
, 1);
1601 outputAlarmWarnings(editDlg
, &newEvent
);
1606 /******************************************************************************
1607 * Returns a list of all alarm templates.
1608 * If shell commands are disabled, command alarm templates are omitted.
1610 KAEvent::List
templateList()
1612 KAEvent::List templates
;
1613 bool includeCmdAlarms
= ShellProcess::authorised();
1614 KAEvent::List events
= AlarmCalendar::resources()->events(KAlarm::CalEvent::TEMPLATE
);
1615 for (int i
= 0, end
= events
.count(); i
< end
; ++i
)
1617 KAEvent
* event
= events
[i
];
1618 if (includeCmdAlarms
|| !(event
->actionTypes() & KAEvent::ACT_COMMAND
))
1619 templates
.append(event
);
1624 /******************************************************************************
1625 * To be called after an alarm has been edited.
1626 * Prompt the user to re-enable alarms if they are currently disabled, and if
1627 * it's an email alarm, warn if no 'From' email address is configured.
1629 void outputAlarmWarnings(QWidget
* parent
, const KAEvent
* event
)
1631 if (event
&& event
->actionTypes() == KAEvent::ACT_EMAIL
1632 && Preferences::emailAddress().isEmpty())
1633 KAMessageBox::information(parent
, i18nc("@info Please set the 'From' email address...",
1634 "<para>%1</para><para>Please set it in the Configuration dialog.</para>", KAMail::i18n_NeedFromEmailAddress()));
1636 if (!theApp()->alarmsEnabled())
1638 if (KAMessageBox::warningYesNo(parent
, i18nc("@info", "<para>Alarms are currently disabled.</para><para>Do you want to enable alarms now?</para>"),
1639 QString(), KGuiItem(i18nc("@action:button", "Enable")), KGuiItem(i18nc("@action:button", "Keep Disabled")),
1640 QLatin1String("EditEnableAlarms"))
1641 == KMessageBox::Yes
)
1642 theApp()->setAlarmsEnabled(true);
1646 /******************************************************************************
1647 * Reload the calendar.
1649 void refreshAlarms()
1652 if (!refreshAlarmsQueued
)
1654 refreshAlarmsQueued
= true;
1655 theApp()->processQueue();
1659 /******************************************************************************
1660 * This method must only be called from the main KAlarm queue processing loop,
1661 * to prevent asynchronous calendar operations interfering with one another.
1663 * If refreshAlarms() has been called, reload the calendars.
1665 void refreshAlarmsIfQueued()
1667 if (refreshAlarmsQueued
)
1670 AlarmCalendar::resources()->reload();
1672 // Close any message windows for alarms which are now disabled
1673 KAEvent::List events
= AlarmCalendar::resources()->events(KAlarm::CalEvent::ACTIVE
);
1674 for (int i
= 0, end
= events
.count(); i
< end
; ++i
)
1676 KAEvent
* event
= events
[i
];
1677 if (!event
->enabled() && (event
->actionTypes() & KAEvent::ACT_DISPLAY
))
1679 MessageWin
* win
= MessageWin::findEvent(event
->id());
1684 MainWindow::refresh();
1685 refreshAlarmsQueued
= false;
1689 /******************************************************************************
1690 * Start KMail if it isn't already running, optionally minimised.
1691 * Reply = reason for failure to run KMail (which may be the empty string)
1692 * = null string if success.
1694 QString
runKMail(bool minimise
)
1696 QDBusReply
<bool> reply
= QDBusConnection::sessionBus().interface()->isServiceRegistered(KMAIL_DBUS_SERVICE
);
1697 if (!reply
.isValid() || !reply
.value())
1699 // Program is not already running, so start it
1701 if (minimise
&& Private::startKMailMinimised())
1703 if (KToolInvocation::startServiceByDesktopName(QLatin1String("kmail"), QString(), &errmsg
))
1705 kError() << "Couldn't start KMail (" << errmsg
<< ")";
1706 return i18nc("@info", "Unable to start <application>KMail</application><nl/>(<message>%1</message>)", errmsg
);
1712 /******************************************************************************
1713 * Start KMail, minimised.
1714 * This code is taken from kstart in kdebase.
1716 bool Private::startKMailMinimised()
1719 NETRootInfo
i(QX11Info::display(), NET::Supported
);
1720 if (i
.isSupported(NET::WM2KDETemporaryRules
))
1722 kDebug() << "using rules";
1724 QString message
= "wmclass=kmail\nwmclassmatch=1\n" // 1 = exact match
1725 "wmclasscomplete=false\n"
1726 "minimize=true\nminimizerule=3\n"
1727 "type=" + QString().setNum(NET::Normal
) + "\ntyperule=2";
1728 msg
.broadcastMessage("_KDE_NET_WM_TEMPORARY_RULES", message
, -1, false);
1733 // Connect to window add to get the NEW windows
1734 kDebug() << "connecting to window add";
1735 connect(KWindowSystem::self(), SIGNAL(windowAdded(WId
)), instance(), SLOT(windowAdded(WId
)));
1737 // Propagate the app startup notification info to the started app.
1738 // We are not using KApplication, so the env remained set.
1739 KStartupInfoId id
= KStartupInfo::currentStartupIdEnv();
1740 KProcess
* proc
= new KProcess
;
1742 int pid
= proc
->startDetached();
1745 KStartupInfo::sendFinish(id
); // failed to start
1748 KStartupInfoData data
;
1750 data
.setName("kmail");
1751 data
.setBin("kmail");
1752 KStartupInfo::sendChange(id
, data
);
1759 /******************************************************************************
1760 * Called when a window is created, to minimise it.
1761 * This code is taken from kstart in kdebase.
1763 void Private::windowAdded(WId w
)
1766 static const int SUPPORTED_TYPES
= NET::NormalMask
| NET::DesktopMask
| NET::DockMask
1767 | NET::ToolbarMask
| NET::MenuMask
| NET::DialogMask
1768 | NET::OverrideMask
| NET::TopMenuMask
| NET::UtilityMask
| NET::SplashMask
;
1769 KWindowInfo kwinfo
= KWindowSystem::windowInfo(w
, NET::WMWindowType
| NET::WMName
);
1770 if (kwinfo
.windowType(SUPPORTED_TYPES
) == NET::TopMenu
1771 || kwinfo
.windowType(SUPPORTED_TYPES
) == NET::Toolbar
1772 || kwinfo
.windowType(SUPPORTED_TYPES
) == NET::Desktop
)
1773 return; // always ignore these window types
1776 XWithdrawWindow(QX11Info::display(), w
, qxinfo
.screen());
1777 QApplication::flush();
1779 NETWinInfo
info(QX11Info::display(), w
, QX11Info::appRootWindow(), NET::WMState
);
1780 XWMHints
* hints
= XGetWMHints(QX11Info::display(), w
);
1783 hints
->flags
|= StateHint
;
1784 hints
->initial_state
= IconicState
;
1785 XSetWMHints(QX11Info::display(), w
, hints
);
1788 info
.setWindowType(NET::Normal
);
1790 XSync(QX11Info::display(), False
);
1791 XMapWindow(QX11Info::display(), w
);
1792 XSync(QX11Info::display(), False
);
1793 QApplication::flush();
1797 /******************************************************************************
1798 * The "Don't show again" option for error messages is personal to the user on a
1799 * particular computer. For example, he may want to inhibit error messages only
1800 * on his laptop. So the status is not stored in the alarm calendar, but in the
1801 * user's local KAlarm data directory.
1802 ******************************************************************************/
1804 /******************************************************************************
1805 * Return the Don't-show-again error message tags set for a specified alarm ID.
1807 QStringList
dontShowErrors(const QString
& eventId
)
1809 if (eventId
.isEmpty())
1810 return QStringList();
1811 KConfig
config(KStandardDirs::locateLocal("appdata", ALARM_OPTS_FILE
));
1812 KConfigGroup
group(&config
, DONT_SHOW_ERRORS_GROUP
);
1813 return group
.readEntry(eventId
, QStringList());
1816 /******************************************************************************
1817 * Check whether the specified Don't-show-again error message tag is set for an
1820 bool dontShowErrors(const QString
& eventId
, const QString
& tag
)
1822 if (tag
.isEmpty() || eventId
.isEmpty())
1824 QStringList tags
= dontShowErrors(eventId
);
1825 return tags
.indexOf(tag
) >= 0;
1828 /******************************************************************************
1829 * Reset the Don't-show-again error message tags for an alarm ID.
1830 * If 'tags' is empty, the config entry is deleted.
1832 void setDontShowErrors(const QString
& eventId
, const QStringList
& tags
)
1834 if (eventId
.isEmpty())
1836 KConfig
config(KStandardDirs::locateLocal("appdata", ALARM_OPTS_FILE
));
1837 KConfigGroup
group(&config
, DONT_SHOW_ERRORS_GROUP
);
1839 group
.deleteEntry(eventId
);
1841 group
.writeEntry(eventId
, tags
);
1845 /******************************************************************************
1846 * Set the specified Don't-show-again error message tag for an alarm ID.
1847 * Existing tags are unaffected.
1849 void setDontShowErrors(const QString
& eventId
, const QString
& tag
)
1851 if (tag
.isEmpty() || eventId
.isEmpty())
1853 KConfig
config(KStandardDirs::locateLocal("appdata", ALARM_OPTS_FILE
));
1854 KConfigGroup
group(&config
, DONT_SHOW_ERRORS_GROUP
);
1855 QStringList tags
= group
.readEntry(eventId
, QStringList());
1856 if (tags
.indexOf(tag
) < 0)
1859 group
.writeEntry(eventId
, tags
);
1864 /******************************************************************************
1865 * Read the size for the specified window from the config file, for the
1866 * current screen resolution.
1867 * Reply = true if size set in the config file, in which case 'result' is set
1868 * = false if no size is set, in which case 'result' is unchanged.
1870 bool readConfigWindowSize(const char* window
, QSize
& result
, int* splitterWidth
)
1872 KConfigGroup
config(KGlobal::config(), window
);
1873 QWidget
* desktop
= KApplication::desktop();
1874 QSize s
= QSize(config
.readEntry(QString::fromLatin1("Width %1").arg(desktop
->width()), (int)0),
1875 config
.readEntry(QString::fromLatin1("Height %1").arg(desktop
->height()), (int)0));
1880 *splitterWidth
= config
.readEntry(QString::fromLatin1("Splitter %1").arg(desktop
->width()), -1);
1884 /******************************************************************************
1885 * Write the size for the specified window to the config file, for the
1886 * current screen resolution.
1888 void writeConfigWindowSize(const char* window
, const QSize
& size
, int splitterWidth
)
1890 KConfigGroup
config(KGlobal::config(), window
);
1891 QWidget
* desktop
= KApplication::desktop();
1892 config
.writeEntry(QString::fromLatin1("Width %1").arg(desktop
->width()), size
.width());
1893 config
.writeEntry(QString::fromLatin1("Height %1").arg(desktop
->height()), size
.height());
1894 if (splitterWidth
>= 0)
1895 config
.writeEntry(QString::fromLatin1("Splitter %1").arg(desktop
->width()), splitterWidth
);
1899 /******************************************************************************
1900 * Check from its mime type whether a file appears to be a text or image file.
1901 * If a text file, its type is distinguished.
1902 * Reply = file type.
1904 FileType
fileType(const KMimeType::Ptr
& mimetype
)
1906 if (mimetype
->is("text/html"))
1907 return TextFormatted
;
1908 if (mimetype
->is("application/x-executable"))
1909 return TextApplication
;
1910 if (mimetype
->is("text/plain"))
1912 if (mimetype
->name().startsWith(QLatin1String("image/")))
1917 /******************************************************************************
1918 * Check that a file exists and is a plain readable file.
1919 * Updates 'filename' and 'url' even if an error occurs, since 'filename' may
1920 * be needed subsequently by showFileErrMessage().
1922 FileErr
checkFileExists(QString
& filename
, KUrl
& url
)
1925 FileErr err
= FileErr_None
;
1926 QString file
= filename
;
1927 QRegExp
f("^file:/+");
1928 if (f
.indexIn(file
) >= 0)
1929 file
= file
.mid(f
.matchedLength() - 1);
1930 // Convert any relative file path to absolute
1931 // (using home directory as the default)
1932 int i
= file
.indexOf(QLatin1Char('/'));
1933 if (i
> 0 && file
[i
- 1] == QLatin1Char(':'))
1937 filename
= url
.prettyUrl();
1939 if (!KIO::NetAccess::stat(url
, uds
, MainWindow::mainMainWindow()))
1940 err
= FileErr_Nonexistent
;
1943 KFileItem
fi(uds
, url
);
1944 if (fi
.isDir()) err
= FileErr_Directory
;
1945 else if (!fi
.isReadable()) err
= FileErr_Unreadable
;
1948 else if (file
.isEmpty())
1949 err
= FileErr_Blank
; // blank file name
1952 // It's a local file - convert to absolute path & check validity
1953 QFileInfo
info(file
);
1954 QDir::setCurrent(QDir::homePath());
1955 filename
= info
.absoluteFilePath();
1956 url
.setPath(filename
);
1957 if (info
.isDir()) err
= FileErr_Directory
;
1958 else if (!info
.exists()) err
= FileErr_Nonexistent
;
1959 else if (!info
.isReadable()) err
= FileErr_Unreadable
;
1964 /******************************************************************************
1965 * Display an error message appropriate to 'err'.
1966 * Display a Continue/Cancel error message if 'errmsgParent' non-null.
1967 * Reply = true to continue, false to cancel.
1969 bool showFileErrMessage(const QString
& filename
, FileErr err
, FileErr blankError
, QWidget
* errmsgParent
)
1971 if (err
!= FileErr_None
)
1973 // If file is a local file, remove "file://" from name
1974 QString file
= filename
;
1975 QRegExp
f("^file:/+");
1976 if (f
.indexIn(file
) >= 0)
1977 file
= file
.mid(f
.matchedLength() - 1);
1983 if (blankError
== FileErr_BlankDisplay
)
1984 errmsg
= i18nc("@info", "Please select a file to display");
1985 else if (blankError
== FileErr_BlankPlay
)
1986 errmsg
= i18nc("@info", "Please select a file to play");
1988 kFatal() << "Program error";
1989 KAMessageBox::sorry(errmsgParent
, errmsg
);
1991 case FileErr_Directory
:
1992 KAMessageBox::sorry(errmsgParent
, i18nc("@info", "<filename>%1</filename> is a folder", file
));
1994 case FileErr_Nonexistent
: errmsg
= i18nc("@info", "<filename>%1</filename> not found", file
); break;
1995 case FileErr_Unreadable
: errmsg
= i18nc("@info", "<filename>%1</filename> is not readable", file
); break;
1996 case FileErr_NotTextImage
: errmsg
= i18nc("@info", "<filename>%1</filename> appears not to be a text or image file", file
); break;
2001 if (KAMessageBox::warningContinueCancel(errmsgParent
, errmsg
)
2002 == KMessageBox::Cancel
)
2008 /******************************************************************************
2009 * If a url string is a local file, strip off the 'file:/' prefix.
2011 QString
pathOrUrl(const QString
& url
)
2013 static const QRegExp
localfile("^file:/+");
2014 return (localfile
.indexIn(url
) >= 0) ? url
.mid(localfile
.matchedLength() - 1) : url
;
2017 /******************************************************************************
2018 * Display a modal dialog to choose an existing file, initially highlighting
2019 * any specified file.
2020 * @param initialFile The file to initially highlight - must be a full path name or URL.
2021 * @param defaultDir The directory to start in if @p initialFile is empty. If empty,
2022 * the user's home directory will be used. Updated to the
2023 * directory containing the selected file, if a file is chosen.
2024 * @param mode OR of KFile::Mode values, e.g. ExistingOnly, LocalOnly.
2025 * Reply = URL selected.
2026 * = empty, non-null string if no file was selected.
2027 * = null string if dialogue was deleted while visible (indicating that
2028 * the parent widget was probably also deleted).
2030 QString
browseFile(const QString
& caption
, QString
& defaultDir
, const QString
& initialFile
,
2031 const QString
& filter
, KFile::Modes mode
, QWidget
* parent
)
2033 QString initialDir
= !initialFile
.isEmpty() ? QString(initialFile
).remove(QRegExp("/[^/]*$"))
2034 : !defaultDir
.isEmpty() ? defaultDir
2036 // Use AutoQPointer to guard against crash on application exit while
2037 // the dialogue is still open. It prevents double deletion (both on
2038 // deletion of parent, and on return from this function).
2039 AutoQPointer
<KFileDialog
> fileDlg
= new KFileDialog(initialDir
, filter
, parent
);
2040 fileDlg
->setOperationMode(mode
& KFile::ExistingOnly
? KFileDialog::Opening
: KFileDialog::Saving
);
2041 fileDlg
->setMode(KFile::File
| mode
);
2042 fileDlg
->setCaption(caption
);
2043 if (!initialFile
.isEmpty())
2044 fileDlg
->setSelection(initialFile
);
2045 if (fileDlg
->exec() != QDialog::Accepted
)
2046 return fileDlg
? QString("") : QString();
2047 KUrl url
= fileDlg
->selectedUrl();
2050 defaultDir
= url
.isLocalFile() ? url
.toLocalFile() : url
.path();
2051 return (mode
& KFile::LocalOnly
) ? url
.pathOrUrl() : url
.prettyUrl();
2054 /******************************************************************************
2055 * Convert a date/time specification string into a local date/time or date value.
2057 * timeString = in the form [[[yyyy-]mm-]dd-]hh:mm [TZ] or yyyy-mm-dd [TZ].
2058 * dateTime = receives converted date/time value.
2059 * defaultDt = default date/time used for missing parts of timeString, or null
2060 * to use current date/time.
2061 * allowTZ = whether to allow a time zone specifier in timeString.
2062 * Reply = true if successful.
2064 bool convertTimeString(const QByteArray
& timeString
, KDateTime
& dateTime
, const KDateTime
& defaultDt
, bool allowTZ
)
2066 #define MAX_DT_LEN 19
2067 int i
= timeString
.indexOf(' ');
2068 if (i
> MAX_DT_LEN
|| (i
>= 0 && !allowTZ
))
2070 QString zone
= (i
>= 0) ? QString::fromLatin1(timeString
.mid(i
)) : QString();
2071 char timeStr
[MAX_DT_LEN
+1];
2072 strcpy(timeStr
, timeString
.left(i
>= 0 ? i
: MAX_DT_LEN
));
2073 int dt
[5] = { -1, -1, -1, -1, -1 };
2077 // Get the minute value
2078 if ((s
= strchr(timeStr
, ':')) == 0)
2084 dt
[4] = strtoul(s
, &end
, 10);
2085 if (end
== s
|| *end
|| dt
[4] >= 60)
2087 // Get the hour value
2088 if ((s
= strrchr(timeStr
, '-')) == 0)
2092 dt
[3] = strtoul(s
, &end
, 10);
2093 if (end
== s
|| *end
|| dt
[3] >= 24)
2100 // Get the day value
2101 if ((s
= strrchr(timeStr
, '-')) == 0)
2105 dt
[2] = strtoul(s
, &end
, 10);
2106 if (end
== s
|| *end
|| dt
[2] == 0 || dt
[2] > 31)
2110 // Get the month value
2111 if ((s
= strrchr(timeStr
, '-')) == 0)
2115 dt
[1] = strtoul(s
, &end
, 10);
2116 if (end
== s
|| *end
|| dt
[1] == 0 || dt
[1] > 12)
2120 // Get the year value
2121 dt
[0] = strtoul(timeStr
, &end
, 10);
2122 if (end
== timeStr
|| *end
)
2130 date
= QDate(dt
[0], dt
[1], dt
[2]);
2131 QTime
time(0, 0, 0);
2134 // No time was specified, so the full date must have been specified
2135 if (dt
[0] < 0 || !date
.isValid())
2137 dateTime
= KAlarm::applyTimeZone(zone
, date
, time
, false, defaultDt
);
2141 // Compile the values into a date/time structure
2142 time
.setHMS(dt
[3], dt
[4], 0);
2145 // Some or all of the date was omitted.
2146 // Use the default date/time if provided.
2147 if (defaultDt
.isValid())
2149 dt
[0] = defaultDt
.date().year();
2151 (dt
[1] < 0 ? defaultDt
.date().month() : dt
[1]),
2152 (dt
[2] < 0 ? defaultDt
.date().day() : dt
[2]));
2155 date
.setYMD(2000, 1, 1); // temporary substitute for date
2157 dateTime
= KAlarm::applyTimeZone(zone
, date
, time
, true, defaultDt
);
2158 if (!dateTime
.isValid())
2162 // Some or all of the date was omitted.
2163 // Use the current date in the specified time zone as default.
2164 KDateTime now
= KDateTime::currentDateTime(dateTime
.timeSpec());
2165 date
= dateTime
.date();
2166 date
.setYMD(now
.date().year(),
2167 (dt
[1] < 0 ? now
.date().month() : dt
[1]),
2168 (dt
[2] < 0 ? now
.date().day() : dt
[2]));
2169 if (!date
.isValid())
2171 if (noDate
&& time
< now
.time())
2172 date
= date
.addDays(1);
2173 dateTime
.setDate(date
);
2176 return dateTime
.isValid();
2179 /******************************************************************************
2180 * Convert a time zone specifier string and apply it to a given date and/or time.
2181 * The time zone specifier is a system time zone name, e.g. "Europe/London",
2182 * "UTC" or "Clock". If no time zone is specified, it defaults to the local time
2184 * If 'defaultDt' is valid, it supplies the time spec and default date.
2186 KDateTime
applyTimeZone(const QString
& tzstring
, const QDate
& date
, const QTime
& time
,
2187 bool haveTime
, const KDateTime
& defaultDt
)
2190 KDateTime::Spec spec
= KDateTime::LocalZone
;
2191 QString zone
= tzstring
.trimmed();
2192 if (!zone
.isEmpty())
2194 if (zone
== QLatin1String("Clock"))
2195 spec
= KDateTime::ClockTime
;
2196 else if (zone
== QLatin1String("UTC"))
2197 spec
= KDateTime::UTC
;
2200 KTimeZone tz
= KSystemTimeZones::zone(zone
);
2201 error
= !tz
.isValid();
2206 else if (defaultDt
.isValid())
2207 spec
= defaultDt
.timeSpec();
2212 if (!date
.isValid())
2214 // It's a time without a date
2215 if (defaultDt
.isValid())
2216 result
= KDateTime(defaultDt
.date(), time
, spec
);
2217 else if (spec
== KDateTime::LocalZone
|| spec
== KDateTime::ClockTime
)
2218 result
= KDateTime(KDateTime::currentLocalDate(), time
, spec
);
2222 // It's a date and time
2223 result
= KDateTime(date
, time
, spec
);
2227 // It's a date without a time
2228 result
= KDateTime(date
, spec
);
2235 /******************************************************************************
2236 * Set up KAlarm test conditions based on environment variables.
2237 * KALARM_TIME: specifies current system time (format [[[yyyy-]mm-]dd-]hh:mm [TZ]).
2239 void setTestModeConditions()
2241 const QByteArray newTime
= qgetenv("KALARM_TIME");
2242 if (!newTime
.isEmpty())
2245 if (convertTimeString(newTime
, dt
, KDateTime::realCurrentLocalDateTime(), true))
2246 setSimulatedSystemTime(dt
);
2250 /******************************************************************************
2251 * Set the simulated system time.
2253 void setSimulatedSystemTime(const KDateTime
& dt
)
2255 KDateTime::setSimulatedSystemTime(dt
);
2256 kDebug() << "New time =" << qPrintable(KDateTime::currentLocalDateTime().toString("%Y-%m-%d %H:%M %:Z"));
2260 } // namespace KAlarm
2264 /******************************************************************************
2265 * Tell KOrganizer to put an alarm in its calendar.
2266 * It will be held by KOrganizer as a simple event, without alarms - KAlarm
2267 * is still responsible for alarming.
2269 KAlarm::UpdateStatus
sendToKOrganizer(const KAEvent
* event
)
2272 Event::Ptr
kcalEvent(new KCalCore::Event
);
2273 event
->updateKCalEvent(kcalEvent
, KAEvent::UID_IGNORE
);
2275 Event
* kcalEvent
= AlarmCalendar::resources()->createKCalEvent(event
);
2277 // Change the event ID to avoid duplicating the same unique ID as the original event
2278 QString uid
= uidKOrganizer(event
->id());
2279 kcalEvent
->setUid(uid
);
2280 kcalEvent
->clearAlarms();
2282 switch (event
->actionTypes())
2284 case KAEvent::ACT_DISPLAY
:
2285 case KAEvent::ACT_COMMAND
:
2286 case KAEvent::ACT_DISPLAY_COMMAND
:
2287 kcalEvent
->setSummary(event
->cleanText());
2288 userEmail
= Preferences::emailAddress();
2290 case KAEvent::ACT_EMAIL
:
2292 QString from
= event
->emailFromId()
2293 ? Identities::identityManager()->identityForUoid(event
->emailFromId()).fullEmailAddr()
2294 : Preferences::emailAddress();
2296 atext
.setEmail(event
->emailAddresses(", "), from
, QString(), QString(), event
->emailSubject(), QString());
2297 kcalEvent
->setSummary(atext
.displayText());
2301 case KAEvent::ACT_AUDIO
:
2302 kcalEvent
->setSummary(event
->audioFile());
2308 Person::Ptr
person(new Person(QString(), userEmail
));
2309 kcalEvent
->setOrganizer(person
);
2311 kcalEvent
->setOrganizer(KCal::Person(QString(), userEmail
));
2313 kcalEvent
->setDuration(Duration(Preferences::kOrgEventDuration() * 60, Duration::Seconds
));
2315 // Translate the event into string format
2317 format
.setTimeSpec(Preferences::timeZone(true));
2318 QString iCal
= format
.toICalString(kcalEvent
);
2323 // Send the event to KOrganizer
2324 KAlarm::UpdateStatus st
= runKOrganizer(); // start KOrganizer if it isn't already running, and create its D-Bus interface
2325 if (st
!= KAlarm::UPDATE_OK
)
2327 QList
<QVariant
> args
;
2329 QDBusReply
<bool> reply
= korgInterface
->callWithArgumentList(QDBus::Block
, QLatin1String("addIncidence"), args
);
2330 if (!reply
.isValid())
2332 if (reply
.error().type() == QDBusError::UnknownObject
)
2334 kError()<<"addIncidence() D-Bus error: still starting";
2335 return KAlarm::UPDATE_KORG_ERRSTART
;
2337 kError() << "addIncidence(" << uid
<< ") D-Bus call failed:" << reply
.error().message();
2338 return KAlarm::UPDATE_KORG_ERR
;
2342 kDebug() << "addIncidence(" << uid
<< ") D-Bus call returned false";
2343 return KAlarm::UPDATE_KORG_FUNCERR
;
2345 kDebug() << uid
<< ": success";
2346 return KAlarm::UPDATE_OK
;
2349 /******************************************************************************
2350 * Tell KOrganizer to delete an event from its calendar.
2352 KAlarm::UpdateStatus
deleteFromKOrganizer(const QString
& eventID
)
2354 KAlarm::UpdateStatus st
= runKOrganizer(); // start KOrganizer if it isn't already running, and create its D-Bus interface
2355 if (st
!= KAlarm::UPDATE_OK
)
2357 QString newID
= uidKOrganizer(eventID
);
2358 QList
<QVariant
> args
;
2359 args
<< newID
<< true;
2360 QDBusReply
<bool> reply
= korgInterface
->callWithArgumentList(QDBus::Block
, QLatin1String("deleteIncidence"), args
);
2361 if (!reply
.isValid())
2363 if (reply
.error().type() == QDBusError::UnknownObject
)
2365 kError()<<"deleteIncidence() D-Bus error: still starting";
2366 return KAlarm::UPDATE_KORG_ERRSTART
;
2368 kError() << "deleteIncidence(" << newID
<< ") D-Bus call failed:" << reply
.error().message();
2369 return KAlarm::UPDATE_KORG_ERR
;
2373 kDebug() << "deleteIncidence(" << newID
<< ") D-Bus call returned false";
2374 return KAlarm::UPDATE_KORG_FUNCERR
;
2376 kDebug() << newID
<< ": success";
2377 return KAlarm::UPDATE_OK
;
2380 /******************************************************************************
2381 * Start KOrganizer if not already running, and create its D-Bus interface.
2383 KAlarm::UpdateStatus
runKOrganizer()
2385 QString error
, dbusService
;
2386 int result
= KDBusServiceStarter::self()->findServiceFor("DBUS/Organizer", QString(), &error
, &dbusService
);
2389 kWarning() << "Unable to start DBUS/Organizer:" << dbusService
<< error
;
2390 return KAlarm::UPDATE_KORG_ERR
;
2392 // If Kontact is running, there is be a load() method which needs to be called
2393 // to load KOrganizer into Kontact. But if KOrganizer is running independently,
2394 // the load() method doesn't exist.
2395 QDBusInterface
iface(KORG_DBUS_SERVICE
, KORG_DBUS_LOAD_PATH
, "org.kde.KUniqueApplication");
2396 if (!iface
.isValid())
2398 kWarning() << "Unable to access "KORG_DBUS_LOAD_PATH
" D-Bus interface:" << iface
.lastError().message();
2399 return KAlarm::UPDATE_KORG_ERR
;
2401 QDBusReply
<bool> reply
= iface
.call("load");
2402 if ((!reply
.isValid() || !reply
.value())
2403 && iface
.lastError().type() != QDBusError::UnknownMethod
)
2405 kWarning() << "Loading KOrganizer failed:" << iface
.lastError().message();
2406 return KAlarm::UPDATE_KORG_ERR
;
2409 // KOrganizer has been started, but it may not have the necessary
2410 // D-Bus interface available yet.
2411 if (!korgInterface
|| !korgInterface
->isValid())
2413 delete korgInterface
;
2414 korgInterface
= new QDBusInterface(KORG_DBUS_SERVICE
, KORG_DBUS_PATH
, KORG_DBUS_IFACE
);
2415 if (!korgInterface
->isValid())
2417 kWarning() << "Unable to access "KORG_DBUS_PATH
" D-Bus interface:" << korgInterface
->lastError().message();
2418 delete korgInterface
;
2420 return KAlarm::UPDATE_KORG_ERRSTART
;
2423 return KAlarm::UPDATE_OK
;
2426 /******************************************************************************
2427 * Insert a KOrganizer string after the hyphen in the supplied event ID.
2429 QString
uidKOrganizer(const QString
& id
)
2431 QString result
= id
;
2432 int i
= result
.lastIndexOf('-');
2434 i
= result
.length();
2435 return result
.insert(i
, KORGANIZER_UID
);
2440 /******************************************************************************
2441 * Case insensitive comparison for use by qSort().
2443 bool caseInsensitiveLessThan(const QString
& s1
, const QString
& s2
)
2445 return s1
.toLower() < s2
.toLower();