File indexing completed on 2024-05-12 05:14:50
0001 /* 0002 * messagedisplay.cpp - base class to display an alarm or error message 0003 * Program: kalarm 0004 * SPDX-FileCopyrightText: 2001-2023 David Jarvie <djarvie@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "messagewindow.h" 0010 #include "messagenotification.h" 0011 0012 #include "deferdlg.h" 0013 #include "displaycalendar.h" 0014 #include "functions.h" 0015 #include "kalarmapp.h" 0016 #include "mainwindow.h" 0017 #include "resourcescalendar.h" 0018 #include "resources/resources.h" 0019 #include "lib/messagebox.h" 0020 0021 #include <KLocalizedString> 0022 0023 using namespace KAlarmCal; 0024 using namespace KCalendarCore; 0025 0026 0027 bool MessageDisplay::mRedisplayed = false; 0028 0029 /****************************************************************************** 0030 * Create a new instance of a MessageDisplay, the derived class being dependent 0031 * on 'event.notify()'. 0032 */ 0033 MessageDisplay* MessageDisplay::create(const KAEvent& event, const KAAlarm& alarm, int flags) 0034 { 0035 if (event.notify()) 0036 return new MessageNotification(event, alarm, flags & ~AlwaysHide); 0037 else 0038 return new MessageWindow(event, alarm, flags); 0039 } 0040 0041 /****************************************************************************** 0042 * Show an error message about the execution of an alarm. 0043 * If 'dontShowAgain' is non-null, a "Don't show again" option is displayed. Note 0044 * that the option is specific to 'event'. 0045 */ 0046 void MessageDisplay::showError(const KAEvent& event, const DateTime& alarmDateTime, 0047 const QStringList& errmsgs, const QString& dontShowAgain) 0048 { 0049 if (MessageDisplayHelper::shouldShowError(event, errmsgs, dontShowAgain)) 0050 { 0051 MessageDisplay* disp; 0052 if (event.notify()) 0053 disp = new MessageNotification(event, alarmDateTime, errmsgs, dontShowAgain); 0054 else 0055 disp = new MessageWindow(event, alarmDateTime, errmsgs, dontShowAgain); 0056 disp->showDisplay(); 0057 } 0058 } 0059 0060 /****************************************************************************** 0061 * Constructors. 0062 */ 0063 MessageDisplay::MessageDisplay() 0064 : mHelper(new MessageDisplayHelper(this)) 0065 { 0066 } 0067 0068 MessageDisplay::MessageDisplay(const KAEvent& event, const KAAlarm& alarm, int flags) 0069 : mHelper(new MessageDisplayHelper(this, event, alarm, flags)) 0070 { 0071 } 0072 0073 MessageDisplay::MessageDisplay(const KAEvent& event, const DateTime& alarmDateTime, 0074 const QStringList& errmsgs, const QString& dontShowAgain) 0075 : mHelper(new MessageDisplayHelper(this, event, alarmDateTime, errmsgs, dontShowAgain)) 0076 { 0077 } 0078 0079 MessageDisplay::MessageDisplay(MessageDisplayHelper* helper) 0080 : mHelper(helper) 0081 { 0082 mHelper->setParent(this); 0083 } 0084 0085 MessageDisplay::~MessageDisplay() 0086 { 0087 delete mHelper; 0088 if (!instanceCount(true)) 0089 theApp()->quitIf(); // no visible displays remain - check whether to quit 0090 } 0091 0092 /****************************************************************************** 0093 * Redisplay alarms which were being shown when the program last exited. 0094 * Normally, these alarms will have been displayed by session restoration, but 0095 * if the program crashed or was killed, we can redisplay them here so that 0096 * they won't be lost. 0097 */ 0098 void MessageDisplay::redisplayAlarms() 0099 { 0100 if (mRedisplayed) 0101 return; 0102 qCDebug(KALARM_LOG) << "MessageDisplay::redisplayAlarms"; 0103 mRedisplayed = true; 0104 if (DisplayCalendar::isOpen()) 0105 { 0106 KAEvent event; 0107 Resource resource; 0108 const Event::List kcalEvents = DisplayCalendar::kcalEvents(); 0109 for (const Event::Ptr& kcalEvent : kcalEvents) 0110 { 0111 bool showDefer, showEdit; 0112 if (!reinstateFromDisplaying(kcalEvent, event, resource, showEdit, showDefer)) 0113 continue; 0114 const EventId eventId(event); 0115 if (findEvent(eventId)) 0116 qCDebug(KALARM_LOG) << "MessageDisplay::redisplayAlarms: Message display already exists:" << eventId; 0117 else 0118 { 0119 // This event should be displayed, but currently isn't being 0120 const KAAlarm alarm = event.convertDisplayingAlarm(); 0121 if (alarm.type() == KAAlarm::Type::Invalid) 0122 { 0123 qCCritical(KALARM_LOG) << "MessageDisplay::redisplayAlarms: Invalid alarm: id=" << eventId; 0124 continue; 0125 } 0126 qCDebug(KALARM_LOG) << "MessageDisplay::redisplayAlarms:" << eventId; 0127 const bool login = alarm.repeatAtLogin(); 0128 const int flags = NoReschedule | (login ? NoDefer : 0) | NoInitView; 0129 MessageDisplay* d = create(event, alarm, flags); 0130 MessageDisplayHelper* h = d->mHelper; 0131 h->mResource = resource; 0132 const bool rw = resource.isWritable(event.category()); 0133 h->mShowEdit = rw ? showEdit : false; 0134 h->mNoDefer = (rw && !login) ? !showDefer : true; 0135 d->setUpDisplay(); 0136 d->showDisplay(); 0137 } 0138 } 0139 } 0140 } 0141 0142 /****************************************************************************** 0143 * Retrieves the event with the current ID from the displaying calendar file, 0144 * or if not found there, from the archive calendar. 'resource' is set to the 0145 * resource which originally contained the event, or invalid if not known. 0146 */ 0147 bool MessageDisplay::retrieveEvent(const EventId& evntId, KAEvent& event, Resource& resource, bool& showEdit, bool& showDefer) 0148 { 0149 const QString eventId = evntId.eventId(); 0150 const Event::Ptr kcalEvent = DisplayCalendar::kcalEvent(CalEvent::uid(eventId, CalEvent::DISPLAYING)); 0151 if (!reinstateFromDisplaying(kcalEvent, event, resource, showEdit, showDefer)) 0152 { 0153 // The event isn't in the displaying calendar. 0154 // Try to retrieve it from the archive calendar. 0155 KAEvent ev; 0156 Resource archiveRes = Resources::getStandard(CalEvent::ARCHIVED, true); 0157 if (archiveRes.isValid()) 0158 ev = ResourcesCalendar::event(EventId(archiveRes.id(), CalEvent::uid(eventId, CalEvent::ARCHIVED))); 0159 if (!ev.isValid()) 0160 return false; 0161 event = ev; 0162 event.setArchive(); // ensure that it gets re-archived if it's saved 0163 event.setCategory(CalEvent::ACTIVE); 0164 if (eventId != event.id()) 0165 qCCritical(KALARM_LOG) << "MessageDisplay::retrieveEvent: Wrong event ID"; 0166 event.setEventId(eventId); 0167 resource = Resource(); 0168 showEdit = false; 0169 showDefer = false; 0170 qCDebug(KALARM_LOG) << "MessageDisplay::retrieveEvent:" << event.id() << ": success"; 0171 } 0172 return true; 0173 } 0174 0175 /****************************************************************************** 0176 * Retrieves the displayed event from the calendar file, or if not found there, 0177 * from the displaying calendar. 'resource' is set to the resource which 0178 * originally contained the event. 0179 */ 0180 bool MessageDisplay::reinstateFromDisplaying(const Event::Ptr& kcalEvent, KAEvent& event, Resource& resource, bool& showEdit, bool& showDefer) 0181 { 0182 if (!kcalEvent) 0183 return false; 0184 ResourceId resourceId; 0185 event.reinstateFromDisplaying(kcalEvent, resourceId, showEdit, showDefer); 0186 event.setResourceId(resourceId); 0187 resource = Resources::resource(resourceId); 0188 qCDebug(KALARM_LOG) << "MessageDisplay::reinstateFromDisplaying:" << EventId(event) << ": success"; 0189 return true; 0190 } 0191 0192 /****************************************************************************** 0193 * Display the main window, with the appropriate alarm selected. 0194 */ 0195 void MessageDisplay::displayMainWindow() 0196 { 0197 KAlarm::displayMainWindowSelected(mEventId().eventId()); 0198 } 0199 0200 MessageDisplay::DeferDlgData::~DeferDlgData() 0201 { 0202 delete dlg; 0203 } 0204 0205 /****************************************************************************** 0206 * Create a defer message dialog. 0207 */ 0208 MessageDisplay::DeferDlgData* MessageDisplay::createDeferDlg(QObject* thisObject, bool displayClosing) 0209 { 0210 DeferAlarmDlg* dlg = new DeferAlarmDlg(KADateTime::currentDateTime(Preferences::timeSpec()).addSecs(60), mDateTime().isDateOnly(), false, MainWindow::mainMainWindow()); 0211 dlg->setObjectName(QLatin1StringView("DeferDlg")); // used by LikeBack 0212 dlg->setDeferMinutes(mDefaultDeferMinutes() > 0 ? mDefaultDeferMinutes() : Preferences::defaultDeferTime()); 0213 dlg->setLimit(mEvent()); 0214 auto data = new DeferDlgData(this, dlg); 0215 if (!displayClosing) 0216 data->displayObj = thisObject; 0217 data->eventId = mEventId(); 0218 data->alarmType = mAlarmType(); 0219 data->commandError = mCommandError(); 0220 return data; 0221 } 0222 0223 /****************************************************************************** 0224 * Display a defer message dialog. 0225 */ 0226 void MessageDisplay::executeDeferDlg(DeferDlgData* data) 0227 { 0228 MainWindow::mainMainWindow()->showDeferAlarmDlg(data); 0229 } 0230 0231 /****************************************************************************** 0232 * Process the result of a defer message dialog. 0233 */ 0234 void MessageDisplay::processDeferDlg(DeferDlgData* data, int result) 0235 { 0236 MessageDisplay* display = data->displayObj ? data->display : nullptr; 0237 if (result == QDialog::Accepted) 0238 { 0239 const DateTime dateTime = data->dlg->getDateTime(); 0240 const int delayMins = data->dlg->deferMinutes(); 0241 // Fetch the up-to-date alarm from the calendar. Note that it could have 0242 // changed since it was displayed. 0243 KAEvent event; 0244 if (!data->eventId.isEmpty()) 0245 event = ResourcesCalendar::event(data->eventId); 0246 if (event.isValid()) 0247 { 0248 // The event still exists in the active calendar 0249 qCDebug(KALARM_LOG) << "MessageDisplay::executeDeferDlg: Deferring event" << data->eventId; 0250 KAEvent newev(event); 0251 newev.defer(dateTime, (static_cast<int>(data->alarmType) & static_cast<int>(KAAlarm::Type::Reminder)), true); 0252 newev.setDeferDefaultMinutes(delayMins); 0253 KAlarm::updateEvent(newev, data->dlg, true); 0254 if (display) 0255 { 0256 if (newev.deferred()) 0257 display->mNoPostAction() = true; 0258 } 0259 } 0260 else 0261 { 0262 // Try to retrieve the event from the displaying or archive calendars 0263 Resource resource; // receives the event's original resource, if known 0264 KAEvent event2; 0265 bool showEdit, showDefer; 0266 if (!retrieveEvent(data->eventId, event2, resource, showEdit, showDefer)) 0267 { 0268 // The event doesn't exist any more !?!, so recurrence data, 0269 // flags, and more, have been lost. 0270 QWidget* par = display ? display->displayParent() : MainWindow::mainMainWindow(); 0271 KAMessageBox::error(par, xi18nc("@info", "<para>Cannot defer alarm:</para><para>Alarm not found.</para>")); 0272 if (display) 0273 { 0274 display->raiseDisplay(); 0275 display->enableDeferButton(false); 0276 display->enableEditButton(false); 0277 } 0278 delete data; 0279 return; 0280 } 0281 qCDebug(KALARM_LOG) << "MessageDisplay::executeDeferDlg: Deferring retrieved event" << data->eventId; 0282 event2.defer(dateTime, (static_cast<int>(data->alarmType) & static_cast<int>(KAAlarm::Type::Reminder)), true); 0283 event2.setDeferDefaultMinutes(delayMins); 0284 event2.setCommandError(data->commandError); 0285 // Add the event back into the calendar file, retaining its ID 0286 // and not updating KOrganizer. 0287 KAlarm::addEvent(event2, resource, data->dlg, KAlarm::USE_EVENT_ID); 0288 if (display) 0289 { 0290 if (event2.deferred()) 0291 display->mNoPostAction() = true; 0292 } 0293 // Finally delete it from the archived calendar now that it has 0294 // been reactivated. 0295 KAEvent eventX; 0296 Resource res = Resources::resourceForEvent(CalEvent::uid(event2.id(), CalEvent::ARCHIVED), eventX); 0297 KAlarm::deleteEvent(eventX, res, false); 0298 } 0299 if (theApp()->wantShowInSystemTray()) 0300 { 0301 // Alarms are to be displayed only if the system tray icon is running, 0302 // so start it if necessary so that the deferred alarm will be shown. 0303 theApp()->displayTrayIcon(true); 0304 } 0305 if (display) 0306 { 0307 display->mHelper->mNoCloseConfirm = true; // allow window to close without confirmation prompt 0308 display->closeDisplay(); 0309 } 0310 } 0311 else 0312 { 0313 if (display) 0314 display->raiseDisplay(); 0315 } 0316 delete data; 0317 } 0318 0319 // vim: et sw=4: