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: