Warning, file /pim/kalarm/src/mainwindow.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  *  mainwindow.cpp  -  main application window
0003  *  Program:  kalarm
0004  *  SPDX-FileCopyrightText: 2001-2024 David Jarvie <djarvie@kde.org>
0005  *
0006  *  SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "mainwindow.h"
0010 
0011 #include "alarmlistdelegate.h"
0012 #include "alarmlistview.h"
0013 #include "birthdaydlg.h"
0014 #include "datepicker.h"
0015 #include "deferdlg.h"
0016 #include "functions.h"
0017 #include "kalarmapp.h"
0018 #include "kernelwakealarm.h"
0019 #include "newalarmaction.h"
0020 #include "prefdlg.h"
0021 #include "preferences.h"
0022 #include "resourcescalendar.h"
0023 #include "resourceselector.h"
0024 #include "templatedlg.h"
0025 #include "traywindow.h"
0026 #include "wakedlg.h"
0027 #include "resources/datamodel.h"
0028 #include "resources/eventmodel.h"
0029 #include "resources/resources.h"
0030 #include "lib/autoqpointer.h"
0031 #include "lib/dragdrop.h"
0032 #include "lib/messagebox.h"
0033 #include "kalarmcalendar/alarmtext.h"
0034 #include "config-kalarm.h"
0035 #include "kalarm_debug.h"
0036 
0037 #include <KCalUtils/ICalDrag>
0038 #include <KCalendarCore/MemoryCalendar>
0039 using namespace KCalendarCore;
0040 using namespace KCalUtils;
0041 
0042 #include <KAboutData>
0043 #include <KToolBar>
0044 #include <KActionCollection>
0045 #include <KGlobalAccel>
0046 #include <KStandardAction>
0047 #include <KLocalizedString>
0048 #include <KSharedConfig>
0049 #include <KConfigGroup>
0050 #include <KShortcutsDialog>
0051 #include <KEditToolBar>
0052 #include <KXMLGUIFactory>
0053 #include <KToggleAction>
0054 #include <KNotifyConfigWidget>
0055 #include <KToolBarPopupAction>
0056 #include <KWindowSystem>
0057 
0058 #include <QAction>
0059 #include <QSplitter>
0060 #include <QVBoxLayout>
0061 #include <QDragEnterEvent>
0062 #include <QDropEvent>
0063 #include <QResizeEvent>
0064 #include <QCloseEvent>
0065 #include <QMenu>
0066 #include <QMimeDatabase>
0067 #include <QInputDialog>
0068 #include <QScrollArea>
0069 #include <QScrollBar>
0070 #include <QTimer>
0071 #include <QUrl>
0072 #include <QMenuBar>
0073 #include <QSystemTrayIcon>
0074 #include <QMimeData>
0075 
0076 using namespace KAlarmCal;
0077 
0078 //clazy:excludeall=non-pod-global-static
0079 
0080 namespace
0081 {
0082 const QString UI_FILE(QStringLiteral("kalarmui.rc"));
0083 const QLatin1String WINDOW_NAME("MainWindow");
0084 const QLatin1String VIEW_GROUP("View");
0085 
0086 const char*   SHOW_COLUMNS        = "ShowColumns";
0087 const char*   SHOW_ARCHIVED_KEY   = "ShowArchivedAlarms";
0088 const char*   SHOW_RESOURCES_KEY  = "ShowResources";
0089 const char*   RESOURCES_WIDTH_KEY = "ResourcesWidth";
0090 const char*   SHOW_DATE_NAVIGATOR = "ShowDateNavigator";
0091 const char*   DATE_NAVIGATOR_TOP  = "DateNavigatorTop";
0092 const char*   HIDDEN_TRAY_PARENT  = "HiddenTrayParent";
0093 
0094 QString             undoText;
0095 QString             undoTextStripped;
0096 QList<QKeySequence> undoShortcut;
0097 QString             redoText;
0098 QString             redoTextStripped;
0099 QList<QKeySequence> redoShortcut;
0100 }
0101 
0102 
0103 /*=============================================================================
0104 =  Class: MainWindow
0105 =============================================================================*/
0106 
0107 MainWindow::WindowList   MainWindow::mWindowList;
0108 TemplateDlg*             MainWindow::mTemplateDlg = nullptr;
0109 
0110 
0111 /******************************************************************************
0112 * Construct an instance.
0113 * To avoid resize() events occurring while still opening the calendar (and
0114 * resultant crashes), the calendar is opened before constructing the instance.
0115 */
0116 MainWindow* MainWindow::create(bool restored)
0117 {
0118     theApp()->checkCalendar();    // ensure calendar is open
0119     return new MainWindow(restored);
0120 }
0121 
0122 MainWindow::MainWindow(bool restored)
0123     : MainWindowBase(nullptr, Qt::WindowContextHelpButtonHint)
0124 {
0125     Q_UNUSED(restored)
0126     qCDebug(KALARM_LOG) << "MainWindow:";
0127     setAttribute(Qt::WA_DeleteOnClose);
0128     setWindowModality(Qt::WindowModal);
0129     setObjectName(QLatin1StringView("MainWin"));    // used by LikeBack
0130     setPlainCaption(KAboutData::applicationData().displayName());
0131     KConfigGroup config(KSharedConfig::openConfig(), VIEW_GROUP);
0132     mShowResources     = config.readEntry(SHOW_RESOURCES_KEY, false);
0133     mShowDateNavigator = config.readEntry(SHOW_DATE_NAVIGATOR, false);
0134     mDateNavigatorTop  = config.readEntry(DATE_NAVIGATOR_TOP, true);
0135     mShowArchived      = config.readEntry(SHOW_ARCHIVED_KEY, false);
0136     mResourcesWidth    = config.readEntry(RESOURCES_WIDTH_KEY, (int)0);
0137     const QList<bool> showColumns = config.readEntry(SHOW_COLUMNS, QList<bool>());
0138 
0139     setAcceptDrops(true);         // allow drag-and-drop onto this window
0140 
0141     mSplitter = new QSplitter(Qt::Horizontal, this);
0142     mSplitter->setChildrenCollapsible(false);
0143     mSplitter->installEventFilter(this);
0144     setCentralWidget(mSplitter);
0145     mSplitter->setStretchFactor(0, 0);   // don't resize side panel when window is resized
0146     mSplitter->setStretchFactor(1, 1);
0147 
0148     // Create the side panel, containing the calendar resource selector and
0149     // date picker widgets.
0150     mPanel = new QScrollArea(mSplitter);
0151     QWidget* panelContents = new QWidget(mPanel);
0152     mPanel->setWidget(panelContents);
0153     mPanel->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0154     mPanel->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
0155     mPanel->setWidgetResizable(true);
0156     mPanel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
0157     mPanel->verticalScrollBar()->installEventFilter(this);
0158     mPanelLayout = new QVBoxLayout(panelContents);
0159     mPanelLayout->setContentsMargins(0, 0, 0, 0);
0160 
0161     DataModel::widgetNeedsDatabase(this);
0162     mResourceSelector = new ResourceSelector(this, panelContents);
0163     mResourceSelector->setResizeToList();
0164     mPanelLayout->addWidget(mResourceSelector);
0165     mPanelLayout->addStretch();
0166 
0167     mDatePicker = new DatePicker(nullptr);
0168     mPanelLayout->addWidget(mDatePicker);
0169 
0170     // Create the alarm list widget
0171     mListFilterModel = DataModel::createAlarmListModel(this);
0172     mListFilterModel->setEventTypeFilter(mShowArchived ? CalEvent::ACTIVE | CalEvent::ARCHIVED : CalEvent::ACTIVE);
0173     mListView = new AlarmListView(WINDOW_NAME, mSplitter);
0174     mListView->setModel(mListFilterModel);
0175     mListView->setColumnsVisible(showColumns);
0176     mListView->setItemDelegate(new AlarmListDelegate(mListView));
0177     connect(mListView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::slotSelection);
0178     connect(mListView, &AlarmListView::contextMenuRequested, this, &MainWindow::slotContextMenuRequested);
0179     connect(mListView, &AlarmListView::columnsVisibleChanged, this, &MainWindow::slotAlarmListColumnsChanged);
0180     connect(Resources::instance(), &Resources::settingsChanged,
0181                              this, &MainWindow::slotCalendarStatusChanged);
0182     connect(mResourceSelector, &ResourceSelector::resized, this, &MainWindow::resourcesResized);
0183     connect(mDatePicker, &DatePicker::datesSelected, this, &MainWindow::datesSelected);
0184     mListView->installEventFilter(this);
0185     initActions();
0186 
0187     setAutoSaveSettings(WINDOW_NAME, true);    // save toolbars, window sizes etc.
0188     mWindowList.append(this);
0189     if (mWindowList.count() == 1)
0190     {
0191         // It's the first main window
0192         if (theApp()->wantShowInSystemTray())
0193             theApp()->displayTrayIcon(true, this);     // create system tray icon for run-in-system-tray mode
0194         else if (theApp()->trayWindow())
0195             theApp()->trayWindow()->setAssocMainWindow(this);    // associate this window with the system tray icon
0196     }
0197     slotCalendarStatusChanged();   // initialise action states now that window is registered
0198 }
0199 
0200 MainWindow::~MainWindow()
0201 {
0202     qCDebug(KALARM_LOG) << "~MainWindow";
0203     bool trayParent = isTrayParent();   // must call before removing from window list
0204     mWindowList.removeAt(mWindowList.indexOf(this));
0205 
0206     // Prevent view updates during window destruction
0207     delete mResourceSelector;
0208     mResourceSelector = nullptr;
0209     delete mDatePicker;
0210     mDatePicker = nullptr;
0211     delete mListView;
0212     mListView = nullptr;
0213 
0214     if (theApp()->trayWindow())
0215     {
0216         if (trayParent)
0217             delete theApp()->trayWindow();
0218         else
0219             theApp()->trayWindow()->removeWindow(this);
0220     }
0221     KSharedConfig::openConfig()->sync();    // save any new window size to disc
0222     theApp()->quitIf();
0223 }
0224 
0225 /******************************************************************************
0226 * Called when the QApplication::saveStateRequest() signal has been emitted.
0227 * Save settings to the session managed config file, for restoration
0228 * when the program is restored.
0229 */
0230 void MainWindow::saveProperties(KConfigGroup& config)
0231 {
0232     config.writeEntry(HIDDEN_TRAY_PARENT, isTrayParent() && isHidden());
0233     config.writeEntry(SHOW_ARCHIVED_KEY, mShowArchived);
0234     config.writeEntry(SHOW_RESOURCES_KEY, mShowResources);
0235     config.writeEntry(SHOW_DATE_NAVIGATOR, mShowDateNavigator);
0236     config.writeEntry(DATE_NAVIGATOR_TOP, mDateNavigatorTop);
0237     config.writeEntry(SHOW_COLUMNS, mListView->columnsVisible());
0238     config.writeEntry(RESOURCES_WIDTH_KEY, mResourceSelector->isVisible() ? mResourceSelector->width() : 0);
0239 }
0240 
0241 /******************************************************************************
0242 * Read settings from the session managed config file.
0243 * This function is automatically called whenever the app is being
0244 * restored. Read in whatever was saved in saveProperties().
0245 */
0246 void MainWindow::readProperties(const KConfigGroup& config)
0247 {
0248     mHiddenTrayParent  = config.readEntry(HIDDEN_TRAY_PARENT, true);
0249     mShowArchived      = config.readEntry(SHOW_ARCHIVED_KEY, false);
0250     mShowResources     = config.readEntry(SHOW_RESOURCES_KEY, false);
0251     mResourcesWidth    = config.readEntry(RESOURCES_WIDTH_KEY, (int)0);
0252     if (mResourcesWidth <= 0)
0253         mShowResources = false;
0254     mShowDateNavigator = config.readEntry(SHOW_DATE_NAVIGATOR, false);
0255     mDateNavigatorTop  = config.readEntry(DATE_NAVIGATOR_TOP, true);
0256     mListView->setColumnsVisible(config.readEntry(SHOW_COLUMNS, QList<bool>()));
0257 }
0258 
0259 /******************************************************************************
0260 * Get the main main window, i.e. the parent of the system tray icon, or if
0261 * none, the first main window to be created. Visible windows take precedence
0262 * over hidden ones.
0263 */
0264 MainWindow* MainWindow::mainMainWindow()
0265 {
0266     MainWindow* tray = (theApp() && theApp()->trayWindow()) ? theApp()->trayWindow()->assocMainWindow() : nullptr;
0267     if (tray  &&  tray->isVisible())
0268         return tray;
0269     for (int i = 0, end = mWindowList.count();  i < end;  ++i)
0270         if (mWindowList.at(i)->isVisible())
0271             return mWindowList.at(i);
0272     if (tray)
0273         return tray;
0274     if (mWindowList.isEmpty())
0275         return nullptr;
0276     return mWindowList.at(0);
0277 }
0278 
0279 /******************************************************************************
0280 * Check whether this main window is effectively the parent of the system tray icon.
0281 */
0282 bool MainWindow::isTrayParent() const
0283 {
0284     TrayWindow* tray = theApp()->trayWindow();
0285     if (!tray  ||  !QSystemTrayIcon::isSystemTrayAvailable())
0286         return false;
0287     if (tray->assocMainWindow() == this)
0288         return true;
0289     return mWindowList.count() == 1;
0290 }
0291 
0292 /******************************************************************************
0293 * Close all main windows.
0294 */
0295 void MainWindow::closeAll()
0296 {
0297     while (!mWindowList.isEmpty())
0298         delete mWindowList[0];    // N.B. the destructor removes the window from the list
0299 }
0300 
0301 /******************************************************************************
0302 * Intercept events for the splitter widget.
0303 */
0304 bool MainWindow::eventFilter(QObject* obj, QEvent* e)
0305 {
0306     if (obj == mSplitter)
0307     {
0308         switch (e->type())
0309         {
0310             case QEvent::Resize:
0311                 // Don't change resources size while WINDOW is being resized.
0312                 // Resize event always occurs before Paint.
0313                 mResizing = true;
0314                 break;
0315             case QEvent::Paint:
0316                 // Allow resources to be resized again
0317                 mResizing = false;
0318                 break;
0319             default:
0320                 break;
0321         }
0322     }
0323     else if (obj == mListView)
0324     {
0325         switch (e->type())
0326         {
0327             case QEvent::KeyPress:
0328             {
0329                 auto ke = static_cast<QKeyEvent*>(e);
0330                 if (ke->key() == Qt::Key_Delete  &&  ke->modifiers() == Qt::ShiftModifier)
0331                 {
0332                     // Prevent Shift-Delete being processed by EventListDelegate
0333                     mActionDeleteForce->trigger();
0334                     return true;
0335                 }
0336                 break;
0337             }
0338             default:
0339                 break;
0340         }
0341     }
0342     else if (obj == mPanel->verticalScrollBar())
0343     {
0344         // The scroll bar width can't be determined correctly until it has been shown.
0345         if (e->type() == QEvent::Show)
0346         {
0347             mPanelScrollBarWidth = mPanel->verticalScrollBar()->width();
0348             arrangePanel();
0349         }
0350     }
0351     return false;
0352 }
0353 
0354 /******************************************************************************
0355 * Called when the window's size has changed (before it is painted).
0356 * Sets the last column in the list view to extend at least to the right hand
0357 * edge of the list view.
0358 * Records the new size in the config file.
0359 */
0360 void MainWindow::resizeEvent(QResizeEvent* re)
0361 {
0362     // Save the window's new size only if it's the first main window
0363     MainWindowBase::resizeEvent(re);
0364     setSplitterSizes();
0365 }
0366 
0367 /******************************************************************************
0368 * Emitted when the date selection changes in the date picker.
0369 */
0370 void MainWindow::datesSelected(const QList<QDate>& dates, bool workChange)
0371 {
0372     mListFilterModel->setDateFilter(dates, workChange);
0373 }
0374 
0375 /******************************************************************************
0376 * Called when the resources panel has been resized.
0377 * Records the new size in the config file.
0378 */
0379 void MainWindow::resourcesResized()
0380 {
0381     if (!mShown  ||  mResizing)
0382         return;
0383     const QList<int> widths = mSplitter->sizes();
0384     if (widths.count() > 1  &&  mResourceSelector->isVisible())
0385     {
0386         mResourcesWidth = widths[0];
0387         // Width is reported as non-zero when resource selector is
0388         // actually invisible, so note a zero width in these circumstances.
0389         if (mResourcesWidth <= 5)
0390             mResourcesWidth = 0;
0391         else if (mainMainWindow() == this)
0392         {
0393             KConfigGroup config(KSharedConfig::openConfig(), VIEW_GROUP);
0394             config.writeEntry(SHOW_RESOURCES_KEY, mShowResources);
0395             if (mShowResources)
0396                 config.writeEntry(RESOURCES_WIDTH_KEY, mResourcesWidth);
0397             config.sync();
0398         }
0399     }
0400 }
0401 
0402 /******************************************************************************
0403 * Called when the window is first displayed.
0404 * Sets the last column in the list view to extend at least to the right hand
0405 * edge of the list view.
0406 */
0407 void MainWindow::showEvent(QShowEvent* se)
0408 {
0409     setSplitterSizes();
0410     MainWindowBase::showEvent(se);
0411     mShown = true;
0412 
0413     if (mMenuError)
0414         QTimer::singleShot(0, this, &MainWindow::showMenuErrorMessage);    //NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
0415 }
0416 
0417 /******************************************************************************
0418 * Set the sizes of the splitter panels.
0419 */
0420 void MainWindow::setSplitterSizes()
0421 {
0422     if (mShowResources  &&  mResourcesWidth > 0)
0423     {
0424         const QList<int> widths{ mResourcesWidth,
0425                                  width() - mResourcesWidth - mSplitter->handleWidth()
0426                                };
0427         mSplitter->setSizes(widths);
0428     }
0429 }
0430 
0431 /******************************************************************************
0432 * Show the menu error message now that the main window has been displayed.
0433 * Waiting until now lets the user easily associate the message with the main
0434 * window which is faulty.
0435 */
0436 void MainWindow::showMenuErrorMessage()
0437 {
0438     if (mMenuError)
0439     {
0440         KAMessageBox::error(this, xi18nc("@info", "Failure to create menus (perhaps <filename>%1</filename> missing or corrupted)", UI_FILE));
0441         mMenuError = false;
0442     }
0443 }
0444 
0445 /******************************************************************************
0446 * Called after the window is hidden.
0447 */
0448 void MainWindow::hideEvent(QHideEvent* he)
0449 {
0450     MainWindowBase::hideEvent(he);
0451 }
0452 
0453 /******************************************************************************
0454 * Initialise the menu, toolbar and main window actions.
0455 */
0456 void MainWindow::initActions()
0457 {
0458     KActionCollection* actions = actionCollection();
0459 
0460     mActionTemplates = new QAction(i18nc("@action", "Templates..."), this);
0461     actions->addAction(QStringLiteral("templates"), mActionTemplates);
0462     connect(mActionTemplates, &QAction::triggered, this, &MainWindow::slotTemplates);
0463 
0464     mActionNew = new NewAlarmAction(false, i18nc("@action", "New"), this, actions);
0465     mActionNew->setActionNames(QStringLiteral("newDisplay"), QStringLiteral("newCommand"), QStringLiteral("newEmail"), QStringLiteral("newAudio"), QStringLiteral("newFromTemplate"));
0466     actions->addAction(QStringLiteral("new"), mActionNew);
0467     connect(mActionNew, &NewAlarmAction::selected, this, &MainWindow::slotNew);
0468     connect(mActionNew, &NewAlarmAction::selectedTemplate, this, &MainWindow::slotNewFromTemplate);
0469 
0470     mActionCreateTemplate = new QAction(i18nc("@action", "Create Template..."), this);
0471     actions->addAction(QStringLiteral("createTemplate"), mActionCreateTemplate);
0472     connect(mActionCreateTemplate, &QAction::triggered, this, &MainWindow::slotNewTemplate);
0473 
0474     mActionCopy = new QAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18nc("@action", "Copy..."), this);
0475     actions->addAction(QStringLiteral("copy"), mActionCopy);
0476     actions->setDefaultShortcut(mActionCopy, QKeySequence(Qt::SHIFT | Qt::Key_Insert));
0477     connect(mActionCopy, &QAction::triggered, this, &MainWindow::slotCopy);
0478 
0479     mActionModify = new QAction(QIcon::fromTheme(QStringLiteral("document-properties")), i18nc("@action", "Edit..."), this);
0480     actions->addAction(QStringLiteral("modify"), mActionModify);
0481     actions->setDefaultShortcut(mActionModify, QKeySequence(Qt::CTRL | Qt::Key_E));
0482     connect(mActionModify, &QAction::triggered, this, &MainWindow::slotModify);
0483 
0484     mActionDelete = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action", "Delete"), this);
0485     actions->addAction(QStringLiteral("delete"), mActionDelete);
0486     actions->setDefaultShortcut(mActionDelete, QKeySequence::Delete);
0487     connect(mActionDelete, &QAction::triggered, this, &MainWindow::slotDeleteIf);
0488 
0489     // Set up Shift-Delete as a shortcut to delete without confirmation
0490     mActionDeleteForce = new QAction(i18nc("@action", "Delete Without Confirmation"), this);
0491     actions->addAction(QStringLiteral("delete-force"), mActionDeleteForce);
0492     actions->setDefaultShortcut(mActionDeleteForce, QKeySequence::Delete | Qt::SHIFT);
0493     connect(mActionDeleteForce, &QAction::triggered, this, &MainWindow::slotDeleteForce);
0494 
0495     mActionReactivate = new QAction(i18nc("@action", "Reactivate"), this);
0496     actions->addAction(QStringLiteral("undelete"), mActionReactivate);
0497     actions->setDefaultShortcut(mActionReactivate, QKeySequence(Qt::CTRL | Qt::Key_R));
0498     connect(mActionReactivate, &QAction::triggered, this, &MainWindow::slotReactivate);
0499 
0500     mActionEnable = new QAction(this);
0501     actions->addAction(QStringLiteral("disable"), mActionEnable);
0502     actions->setDefaultShortcut(mActionEnable, QKeySequence(Qt::CTRL | Qt::Key_B));
0503     connect(mActionEnable, &QAction::triggered, this, &MainWindow::slotEnable);
0504 
0505 #if ENABLE_RTC_WAKE_FROM_SUSPEND
0506     if (!KernelWakeAlarm::isAvailable())
0507     {
0508         // Only enable RTC wake from suspend if kernel wake alarms are not available
0509         QAction* waction = new QAction(i18nc("@action", "Wake From Suspend..."), this);
0510         actions->addAction(QStringLiteral("wakeSuspend"), waction);
0511         connect(waction, &QAction::triggered, this, &MainWindow::slotWakeFromSuspend);
0512     }
0513 #endif
0514 
0515     QAction* action = KAlarm::createStopPlayAction(this);
0516     actions->addAction(QStringLiteral("stopAudio"), action);
0517     KGlobalAccel::setGlobalShortcut(action, QList<QKeySequence>());  // allow user to set a global shortcut
0518 
0519     mActionShowArchived = new KToggleAction(i18nc("@action", "Show Archived Alarms"), this);
0520     actions->addAction(QStringLiteral("showArchivedAlarms"), mActionShowArchived);
0521     actions->setDefaultShortcut(mActionShowArchived, QKeySequence(Qt::CTRL | Qt::Key_P));
0522     connect(mActionShowArchived, &KToggleAction::triggered, this, &MainWindow::slotShowArchived);
0523 
0524     mActionToggleTrayIcon = new KToggleAction(i18nc("@action", "Show in System Tray"), this);
0525     actions->addAction(QStringLiteral("showInSystemTray"), mActionToggleTrayIcon);
0526     connect(mActionToggleTrayIcon, &KToggleAction::triggered, this, &MainWindow::slotToggleTrayIcon);
0527 
0528     mActionToggleResourceSel = new KToggleAction(QIcon::fromTheme(QStringLiteral("view-choose")), i18nc("@action", "Show Calendars"), this);
0529     actions->addAction(QStringLiteral("showResources"), mActionToggleResourceSel);
0530     connect(mActionToggleResourceSel, &KToggleAction::triggered, this, &MainWindow::slotToggleResourceSelector);
0531 
0532     mActionToggleDateNavigator = new KToggleAction(QIcon::fromTheme(QStringLiteral("view-calendar-month")), i18nc("@action", "Show Date Selector"), this);
0533     actions->addAction(QStringLiteral("showDateSelector"), mActionToggleDateNavigator);
0534     connect(mActionToggleDateNavigator, &KToggleAction::triggered, this, &MainWindow::slotToggleDateNavigator);
0535 
0536     if (!KWindowSystem::isPlatformWayland())  // Wayland doesn't allow positioning of windows
0537     {
0538         mActionSpreadWindows = KAlarm::createSpreadWindowsAction(this);
0539         actions->addAction(QStringLiteral("spread"), mActionSpreadWindows);
0540         KGlobalAccel::setGlobalShortcut(mActionSpreadWindows, QList<QKeySequence>());  // allow user to set a global shortcut
0541     }
0542 
0543     mActionImportAlarms = new QAction(i18nc("@action", "Import Alarms..."), this);
0544     actions->addAction(QStringLiteral("importAlarms"), mActionImportAlarms);
0545     connect(mActionImportAlarms, &QAction::triggered, this, &MainWindow::slotImportAlarms);
0546 
0547     if (Preferences::useAkonadi())
0548     {
0549         mActionImportBirthdays = new QAction(i18nc("@action", "Import Birthdays..."), this);
0550         actions->addAction(QStringLiteral("importBirthdays"), mActionImportBirthdays);
0551         connect(mActionImportBirthdays, &QAction::triggered, this, &MainWindow::slotBirthdays);
0552     }
0553 
0554     mActionExportAlarms = new QAction(i18nc("@action", "Export Selected Alarms..."), this);
0555     actions->addAction(QStringLiteral("exportAlarms"), mActionExportAlarms);
0556     connect(mActionExportAlarms, &QAction::triggered, this, &MainWindow::slotExportAlarms);
0557 
0558     mActionExport = new QAction(i18nc("@action", "Export..."), this);
0559     actions->addAction(QStringLiteral("export"), mActionExport);
0560     connect(mActionExport, &QAction::triggered, this, &MainWindow::slotExportAlarms);
0561 
0562     action = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18nc("@action", "Refresh Alarms"), this);
0563     actions->addAction(QStringLiteral("refreshAlarms"), action);
0564     connect(action, &QAction::triggered, this, &MainWindow::slotRefreshAlarms);
0565 
0566     KToggleAction* toggleAction = KAlarm::createAlarmEnableAction(this);
0567     actions->addAction(QStringLiteral("alarmsEnable"), toggleAction);
0568     if (undoText.isNull())
0569     {
0570         // Get standard texts, etc., for Undo and Redo actions
0571         QAction* act = KStandardAction::undo(this, nullptr, actions);
0572         undoShortcut     = act->shortcuts();
0573         undoText         = act->text();
0574         undoTextStripped = KLocalizedString::removeAcceleratorMarker(undoText);
0575         delete act;
0576         act = KStandardAction::redo(this, nullptr, actions);
0577         redoShortcut     = act->shortcuts();
0578         redoText         = act->text();
0579         redoTextStripped = KLocalizedString::removeAcceleratorMarker(redoText);
0580         delete act;
0581     }
0582     mActionUndo = new KToolBarPopupAction(QIcon::fromTheme(QStringLiteral("edit-undo")), undoText, this);
0583     actions->addAction(QStringLiteral("edit_undo"), mActionUndo);
0584     actions->setDefaultShortcuts(mActionUndo, undoShortcut);
0585     connect(mActionUndo, &KToolBarPopupAction::triggered, this, &MainWindow::slotUndo);
0586 
0587     mActionRedo = new KToolBarPopupAction(QIcon::fromTheme(QStringLiteral("edit-redo")), redoText, this);
0588     actions->addAction(QStringLiteral("edit_redo"), mActionRedo);
0589     actions->setDefaultShortcuts(mActionRedo, redoShortcut);
0590     connect(mActionRedo, &KToolBarPopupAction::triggered, this, &MainWindow::slotRedo);
0591 
0592     KStandardAction::find(mListView, &EventListView::slotFind, actions);
0593     mActionFindNext = KStandardAction::findNext(mListView, &EventListView::slotFindNext, actions);
0594     mActionFindPrev = KStandardAction::findPrev(mListView, &EventListView::slotFindPrev, actions);
0595     KStandardAction::selectAll(mListView, &QTreeView::selectAll, actions);
0596     KStandardAction::deselect(mListView, &QAbstractItemView::clearSelection, actions);
0597     // Quit only once the event loop is called; otherwise, the parent window will
0598     // be deleted while still processing the action, resulting in a crash.
0599     QAction* act = KStandardAction::quit(nullptr, nullptr, actions);
0600     connect(act, &QAction::triggered, this, &MainWindow::slotQuit, Qt::QueuedConnection);
0601     KStandardAction::keyBindings(this, &MainWindow::slotConfigureKeys, actions);
0602     KStandardAction::configureNotifications(this, &MainWindow::slotConfigureNotifications, actions);
0603     KStandardAction::configureToolbars(this, &MainWindow::slotConfigureToolbar, actions);
0604     KStandardAction::preferences(this, &MainWindow::slotPreferences, actions);
0605     mActionShowMenuBar = KStandardAction::showMenubar(this, &MainWindow::slotToggleMenubar, actions);
0606     mHamburgerMenu = KStandardAction::hamburgerMenu(nullptr, nullptr, actions);
0607     mHamburgerMenu->setShowMenuBarAction(mActionShowMenuBar);
0608     mHamburgerMenu->setMenuBar(menuBar());
0609     connect(mHamburgerMenu, &KHamburgerMenu::aboutToShowMenu, this, [this]() {
0610         slotInitHamburgerMenu();
0611         // Needs to be run on demand, but the contents won't change, so disconnect now.
0612         disconnect(mHamburgerMenu, &KHamburgerMenu::aboutToShowMenu, this, nullptr);
0613     });
0614     mResourceSelector->initActions(actions);
0615     setStandardToolBarMenuEnabled(true);
0616     createGUI(UI_FILE);
0617     // Load menu and toolbar settings
0618     applyMainWindowSettings(KSharedConfig::openConfig()->group(WINDOW_NAME));
0619 
0620     mContextMenu = static_cast<QMenu*>(factory()->container(QStringLiteral("listContext"), this));
0621     QMenu* actionsMenu = static_cast<QMenu*>(factory()->container(QStringLiteral("actions"), this));
0622     mMenuError = (!mContextMenu  ||  !actionsMenu  ||  !resourceContextMenu());
0623     connect(mActionUndo->popupMenu(), &QMenu::aboutToShow, this, &MainWindow::slotInitUndoMenu);
0624     connect(mActionUndo->popupMenu(), &QMenu::triggered, this, &MainWindow::slotUndoItem);
0625     connect(mActionRedo->popupMenu(), &QMenu::aboutToShow, this, &MainWindow::slotInitRedoMenu);
0626     connect(mActionRedo->popupMenu(), &QMenu::triggered, this, &MainWindow::slotRedoItem);
0627     connect(Undo::instance(), &Undo::changed, this, &MainWindow::slotUndoStatus);
0628     connect(mListView, &AlarmListView::findActive, this, &MainWindow::slotFindActive);
0629     Preferences::connect(&Preferences::archivedKeepDaysChanged, this, &MainWindow::updateKeepArchived);
0630     Preferences::connect(&Preferences::showInSystemTrayChanged, this, &MainWindow::updateTrayIconAction);
0631     connect(theApp(), &KAlarmApp::trayIconToggled, this, &MainWindow::updateTrayIconAction);
0632 
0633     // Set menu item states
0634     setEnableText(true);
0635     mActionShowArchived->setChecked(mShowArchived);
0636     if (!Preferences::archivedKeepDays())
0637         mActionShowArchived->setEnabled(false);
0638     mActionToggleResourceSel->setChecked(mShowResources);
0639     mActionToggleDateNavigator->setChecked(mShowDateNavigator);
0640     slotToggleResourceSelector();   // give priority to resource selector over date navigator
0641     slotToggleDateNavigator();
0642     updateTrayIconAction();         // set the correct text for this action
0643     mActionUndo->setEnabled(Undo::haveUndo());
0644     mActionRedo->setEnabled(Undo::haveRedo());
0645     mActionFindNext->setEnabled(false);
0646     mActionFindPrev->setEnabled(false);
0647 
0648     mActionCopy->setEnabled(false);
0649     mActionModify->setEnabled(false);
0650     mActionDelete->setEnabled(false);
0651     mActionReactivate->setEnabled(false);
0652     mActionEnable->setEnabled(false);
0653     mActionCreateTemplate->setEnabled(false);
0654     mActionExport->setEnabled(false);
0655 
0656     mActionShowMenuBar->setChecked(Preferences::showMenuBar());
0657     slotToggleMenubar(true);
0658 
0659     Undo::emitChanged();     // set the Undo/Redo menu texts
0660 }
0661 
0662 /******************************************************************************
0663 * Set up the Hamburger menu, which allows the menu bar to be easily reinstated
0664 * after it has been hidden.
0665 */
0666 void MainWindow::slotInitHamburgerMenu()
0667 {
0668     QMenu* menu = new QMenu(this);
0669     KActionCollection* actions = actionCollection();
0670     menu->addAction(actions->action(QStringLiteral("new")));
0671     menu->addAction(actions->action(QStringLiteral("templates")));
0672     menu->addSeparator();
0673     menu->addAction(actions->action(QStringLiteral("edit_find")));
0674     menu->addSeparator();
0675     menu->addAction(actions->action(QStringLiteral("showArchivedAlarms")));
0676     menu->addAction(actions->action(QStringLiteral("showDateSelector")));
0677     menu->addSeparator();
0678     menu->addAction(actions->action(KStandardAction::name(KStandardAction::Quit)));
0679     mHamburgerMenu->setMenu(menu);
0680 }
0681 
0682 /******************************************************************************
0683 * Enable or disable the Templates menu item in every main window instance.
0684 */
0685 void MainWindow::enableTemplateMenuItem(bool enable)
0686 {
0687     for (int i = 0, end = mWindowList.count();  i < end;  ++i)
0688         mWindowList[i]->mActionTemplates->setEnabled(enable);
0689 }
0690 
0691 /******************************************************************************
0692 * Refresh the alarm list in every main window instance.
0693 */
0694 void MainWindow::refresh()
0695 {
0696     qCDebug(KALARM_LOG) << "MainWindow::refresh";
0697     DataModel::reload();
0698 }
0699 
0700 /******************************************************************************
0701 * Called when the keep archived alarm setting changes in the user preferences.
0702 * Enable/disable Show archived alarms option.
0703 */
0704 void MainWindow::updateKeepArchived(int days)
0705 {
0706     qCDebug(KALARM_LOG) << "MainWindow::updateKeepArchived:" << (bool)days;
0707     if (mShowArchived  &&  !days)
0708         slotShowArchived();   // toggle Show Archived option setting
0709     mActionShowArchived->setEnabled(days);
0710 }
0711 
0712 /******************************************************************************
0713 * Select an alarm in the displayed list.
0714 */
0715 void MainWindow::selectEvent(const QString& eventId)
0716 {
0717     mListView->clearSelection();
0718     const QModelIndex index = mListFilterModel->eventIndex(eventId);
0719     if (index.isValid())
0720     {
0721         mListView->select(index);
0722         mListView->scrollTo(index);
0723     }
0724 }
0725 
0726 /******************************************************************************
0727 * Return the single selected alarm in the displayed list.
0728 */
0729 KAEvent MainWindow::selectedEvent() const
0730 {
0731     return mListView->selectedEvent();
0732 }
0733 
0734 /******************************************************************************
0735 * Deselect all alarms in the displayed list.
0736 */
0737 void MainWindow::clearSelection()
0738 {
0739     mListView->clearSelection();
0740 }
0741 
0742 /******************************************************************************
0743 * Provide the context menu for the resource selector to use.
0744 */
0745 QMenu* MainWindow::resourceContextMenu()
0746 {
0747     // Recreate the resource selector context menu if it has been deleted
0748     // (which happens if the toolbar is edited).
0749     if (!mResourceContextMenu)
0750         mResourceContextMenu = static_cast<QMenu*>(factory()->container(QStringLiteral("resourceContext"), this));
0751     return mResourceContextMenu;
0752 }
0753 
0754 /******************************************************************************
0755 * Called when the New button is clicked to edit a new alarm to add to the list.
0756 */
0757 void MainWindow::slotNew(EditAlarmDlg::Type type)
0758 {
0759     KAlarm::editNewAlarm(type, mListView);
0760 }
0761 
0762 /******************************************************************************
0763 * Called when a template is selected from the New From Template popup menu.
0764 * Executes a New Alarm dialog, preset from the selected template.
0765 */
0766 void MainWindow::slotNewFromTemplate(const KAEvent& tmplate)
0767 {
0768     KAlarm::editNewAlarm(tmplate, mListView);
0769 }
0770 
0771 /******************************************************************************
0772 * Called when the New Template button is clicked to create a new template
0773 * based on the currently selected alarm.
0774 */
0775 void MainWindow::slotNewTemplate()
0776 {
0777     KAEvent event = mListView->selectedEvent();
0778     if (event.isValid())
0779         KAlarm::editNewTemplate(event, this);
0780 }
0781 
0782 /******************************************************************************
0783 * Called when the Copy button is clicked to edit a copy of an existing alarm,
0784 * to add to the list.
0785 */
0786 void MainWindow::slotCopy()
0787 {
0788     KAEvent event = mListView->selectedEvent();
0789     if (event.isValid())
0790         KAlarm::editNewAlarm(event, this);
0791 }
0792 
0793 /******************************************************************************
0794 * Called when the Modify button is clicked to edit the currently highlighted
0795 * alarm in the list.
0796 */
0797 void MainWindow::slotModify()
0798 {
0799     KAEvent event = mListView->selectedEvent();
0800     if (event.isValid())
0801         KAlarm::editAlarm(event, this);   // edit alarm (view-only mode if archived or read-only)
0802 }
0803 
0804 /******************************************************************************
0805 * Called when the Delete button is clicked to delete the currently highlighted
0806 * alarms in the list.
0807 */
0808 void MainWindow::slotDelete(bool force)
0809 {
0810     QList<KAEvent> events = mListView->selectedEvents();
0811     if (!force  &&  Preferences::confirmAlarmDeletion())
0812     {
0813         int n = events.count();
0814         if (KAMessageBox::warningContinueCancel(this, i18ncp("@info", "Do you really want to delete the selected alarm?",
0815                                                              "Do you really want to delete the %1 selected alarms?", n),
0816                                                 i18ncp("@title:window", "Delete Alarm", "Delete Alarms", n),
0817                                                 KGuiItem(i18nc("@action:button", "Delete"), QStringLiteral("edit-delete")),
0818                                                 KStandardGuiItem::cancel(),
0819                                                 Preferences::CONFIRM_ALARM_DELETION)
0820             != KMessageBox::Continue)
0821             return;
0822     }
0823 
0824     // Remove any events which have just triggered, from the list to delete.
0825     Undo::EventList undos;
0826     for (int i = 0;  i < events.count();  )
0827     {
0828         Resource res = Resources::resourceForEvent(events[i].id());
0829         if (!res.isValid())
0830             events.remove(i);
0831         else
0832             undos.append(events[i++], res);
0833     }
0834 
0835     if (events.isEmpty())
0836         qCDebug(KALARM_LOG) << "MainWindow::slotDelete: No alarms left to delete";
0837     else
0838     {
0839         // Delete the events from the calendar and displays
0840         Resource resource;
0841         const KAlarm::UpdateResult status = KAlarm::deleteEvents(events, resource, true, this);
0842         if (status.status < KAlarm::UPDATE_FAILED)
0843         {
0844             // Create the undo list
0845             for (int i = status.failed.count();  --i >= 0; )
0846                 undos.removeAt(status.failed.at(i));
0847             Undo::saveDeletes(undos);
0848         }
0849     }
0850 }
0851 
0852 /******************************************************************************
0853 * Called when the Reactivate button is clicked to reinstate the currently
0854 * highlighted archived alarms in the list.
0855 */
0856 void MainWindow::slotReactivate()
0857 {
0858     QList<KAEvent> events = mListView->selectedEvents();
0859     mListView->clearSelection();
0860 
0861     // Add the alarms to the displayed lists and to the calendar file
0862     Resource resource;   // active alarms resource which alarms are restored to
0863     QList<int> ineligibleIndexes;
0864     const KAlarm::UpdateResult status = KAlarm::reactivateEvents(events, ineligibleIndexes, resource, this);
0865     if (status.status < KAlarm::UPDATE_FAILED)
0866     {
0867         // Create the undo list, excluding ineligible events
0868         Undo::EventList undos;
0869         for (int i = 0, end = events.count();  i < end;  ++i)
0870         {
0871             if (!ineligibleIndexes.contains(i)  &&  !status.failed.contains(i))
0872                 undos.append(events[i], resource);
0873         }
0874         Undo::saveReactivates(undos);
0875     }
0876 }
0877 
0878 /******************************************************************************
0879 * Called when the Enable/Disable button is clicked to enable or disable the
0880 * currently highlighted alarms in the list.
0881 */
0882 void MainWindow::slotEnable()
0883 {
0884     bool enable = mActionEnableEnable;    // save since changed in response to KAlarm::enableEvent()
0885     const QList<KAEvent> events = mListView->selectedEvents();
0886     QList<KAEvent> eventCopies;
0887     eventCopies.reserve(events.count());
0888     for (const KAEvent& event : events)
0889         eventCopies += event;
0890     KAlarm::enableEvents(eventCopies, enable, this);
0891     slotSelection();   // update Enable/Disable action text
0892 }
0893 
0894 /******************************************************************************
0895 * Called when the columns visible in the alarm list view have changed.
0896 */
0897 void MainWindow::slotAlarmListColumnsChanged()
0898 {
0899     KConfigGroup config(KSharedConfig::openConfig(), VIEW_GROUP);
0900     config.writeEntry(SHOW_COLUMNS, mListView->columnsVisible());
0901     config.sync();
0902 }
0903 
0904 /******************************************************************************
0905 * Called when the Show Archived Alarms menu item is selected or deselected.
0906 */
0907 void MainWindow::slotShowArchived()
0908 {
0909     mShowArchived = !mShowArchived;
0910     mActionShowArchived->setChecked(mShowArchived);
0911     mActionShowArchived->setToolTip(mShowArchived ? i18nc("@info:tooltip", "Hide Archived Alarms")
0912                                                   : i18nc("@info:tooltip", "Show Archived Alarms"));
0913     mListFilterModel->setEventTypeFilter(mShowArchived ? CalEvent::ACTIVE | CalEvent::ARCHIVED : CalEvent::ACTIVE);
0914     mListView->reset();
0915     KConfigGroup config(KSharedConfig::openConfig(), VIEW_GROUP);
0916     config.writeEntry(SHOW_ARCHIVED_KEY, mShowArchived);
0917     config.sync();
0918 }
0919 
0920 /******************************************************************************
0921 * Called when the Spread Windows global shortcut is selected, to spread alarm
0922 * windows so that they are all visible.
0923 */
0924 void MainWindow::slotSpreadWindowsShortcut()
0925 {
0926     mActionSpreadWindows->trigger();
0927 }
0928 
0929 /******************************************************************************
0930 * Called when the Wake From Suspend menu option is selected.
0931 */
0932 void MainWindow::slotWakeFromSuspend()
0933 {
0934 #if ENABLE_RTC_WAKE_FROM_SUSPEND
0935     (WakeFromSuspendDlg::create(this))->show();
0936 #endif
0937 }
0938 
0939 /******************************************************************************
0940 * Called when the Import Alarms menu item is selected, to merge alarms from an
0941 * external calendar into the current calendars.
0942 */
0943 void MainWindow::slotImportAlarms()
0944 {
0945     Resource resource;
0946     KAlarm::importAlarms(resource, this);
0947 }
0948 
0949 /******************************************************************************
0950 * Called when the Export Alarms menu item is selected, to export the selected
0951 * alarms to an external calendar.
0952 */
0953 void MainWindow::slotExportAlarms()
0954 {
0955     const QList<KAEvent> events = mListView->selectedEvents();
0956     if (!events.isEmpty())
0957         KAlarm::exportAlarms(events, this);
0958 }
0959 
0960 /******************************************************************************
0961 * Called when the Import Birthdays menu item is selected, to display birthdays
0962 * from the address book for selection as alarms.
0963 */
0964 void MainWindow::slotBirthdays()
0965 {
0966     if (Preferences::useAkonadi())
0967     {
0968         // Use AutoQPointer to guard against crash on application exit while
0969         // the dialogue is still open. It prevents double deletion (both on
0970         // deletion of MainWindow, and on return from this function).
0971         AutoQPointer<BirthdayDlg> dlg = new BirthdayDlg(this);
0972         if (dlg->exec() == QDialog::Accepted)
0973         {
0974             QList<KAEvent> events = dlg->events();
0975             if (!events.isEmpty())
0976             {
0977                 mListView->clearSelection();
0978                 // Add alarm to the displayed lists and to the calendar file
0979                 Resource resource;
0980                 const KAlarm::UpdateResult status = KAlarm::addEvents(events, resource, dlg, true, true);
0981                 if (status.status < KAlarm::UPDATE_FAILED)
0982                 {
0983                     // Create the undo list
0984                     Undo::EventList undos;
0985                     for (int i = 0, end = events.count();  i < end;  ++i)
0986                         if (!status.failed.contains(i))
0987                             undos.append(events[i], resource);
0988                     Undo::saveAdds(undos, i18nc("@info", "Import birthdays"));
0989 
0990                     KAlarm::outputAlarmWarnings(dlg);
0991                 }
0992             }
0993         }
0994     }
0995 }
0996 
0997 /******************************************************************************
0998 * Called when the Templates menu item is selected, to display the alarm
0999 * template editing dialog.
1000 */
1001 void MainWindow::slotTemplates()
1002 {
1003     if (!mTemplateDlg)
1004     {
1005         mTemplateDlg = TemplateDlg::create(this);
1006         enableTemplateMenuItem(false);     // disable menu item in all windows
1007         connect(mTemplateDlg, &QDialog::finished, this, &MainWindow::slotTemplatesEnd);
1008         mTemplateDlg->show();
1009     }
1010 }
1011 
1012 /******************************************************************************
1013 * Called when the alarm template editing dialog has exited.
1014 */
1015 void MainWindow::slotTemplatesEnd()
1016 {
1017     if (mTemplateDlg)
1018     {
1019         mTemplateDlg->deleteLater();   // this deletes the dialog once it is safe to do so
1020         mTemplateDlg = nullptr;
1021         enableTemplateMenuItem(true);      // re-enable menu item in all windows
1022     }
1023 }
1024 
1025 /******************************************************************************
1026 * Called when the Display System Tray Icon menu item is selected.
1027 */
1028 void MainWindow::slotToggleTrayIcon()
1029 {
1030     theApp()->displayTrayIcon(!theApp()->trayIconDisplayed(), this);
1031 }
1032 
1033 /******************************************************************************
1034 * Called when the Show Resource Selector menu item is selected.
1035 */
1036 void MainWindow::slotToggleResourceSelector()
1037 {
1038     mShowResources = mActionToggleResourceSel->isChecked();
1039     if (mShowResources)
1040     {
1041         if (mResourcesWidth <= 0)
1042             mResourcesWidth = mResourceSelector->sizeHint().width();
1043         mResourceSelector->resize(mResourcesWidth, mResourceSelector->height());
1044         mDateNavigatorTop = mShowDateNavigator;
1045     }
1046     else
1047         mResourceSelector->hide();
1048     arrangePanel();
1049 
1050     KConfigGroup config(KSharedConfig::openConfig(), VIEW_GROUP);
1051     config.writeEntry(SHOW_RESOURCES_KEY, mShowResources);
1052     if (mShowResources)
1053         config.writeEntry(RESOURCES_WIDTH_KEY, mResourcesWidth);
1054     config.writeEntry(DATE_NAVIGATOR_TOP, mDateNavigatorTop);
1055     config.sync();
1056 }
1057 
1058 /******************************************************************************
1059 * Called when the Show Date Navigator menu item is selected.
1060 */
1061 void MainWindow::slotToggleDateNavigator()
1062 {
1063     mShowDateNavigator = mActionToggleDateNavigator->isChecked();
1064     if (mShowDateNavigator)
1065     {
1066         const int panelWidth = mDatePicker->sizeHint().width();
1067         mDatePicker->resize(panelWidth, mDatePicker->height());
1068         mDateNavigatorTop = !mShowResources;
1069     }
1070     else
1071     {
1072         // When the date navigator is not visible, prevent it from filtering alarms.
1073         mDatePicker->clearSelection();
1074         mDatePicker->hide();
1075     }
1076     arrangePanel();
1077 
1078     KConfigGroup config(KSharedConfig::openConfig(), VIEW_GROUP);
1079     config.writeEntry(SHOW_DATE_NAVIGATOR, mShowDateNavigator);
1080     config.writeEntry(DATE_NAVIGATOR_TOP, mDateNavigatorTop);
1081     config.sync();
1082 }
1083 
1084 /******************************************************************************
1085 * Arrange the contents and set the width of the side panel containing the
1086 * resource selector or date navigator.
1087 */
1088 void MainWindow::arrangePanel()
1089 {
1090     // Clear the panel layout before recreating it.
1091     QLayoutItem* item;
1092     while ((item = mPanelLayout->takeAt(0)) != nullptr)
1093         delete item;
1094 
1095     QWidget* date = mShowDateNavigator ? mDatePicker : nullptr;
1096     QWidget* res  = mShowResources ? mResourceSelector : nullptr;
1097     QWidget* top    = mDateNavigatorTop ? date : res;
1098     QWidget* bottom = mDateNavigatorTop ? res : date;
1099     if (bottom  &&  !top)
1100     {
1101         top = bottom;
1102         bottom = nullptr;
1103     }
1104     if (!top)
1105         mPanel->hide();
1106     else
1107     {
1108         int minWidth = 0;
1109         top->show();
1110         mPanelLayout->addWidget(top);
1111         mPanelLayout->addStretch();
1112         mPanel->show();
1113         minWidth = top->minimumSizeHint().width();
1114         if (bottom)
1115         {
1116             bottom->show();
1117             if (!mPanelDivider)
1118             {
1119                 mPanelDivider = new QFrame(mPanel);
1120                 mPanelDivider->setFrameShape(QFrame::HLine);
1121             }
1122             mPanelDivider->show();
1123             mPanelLayout->addWidget(mPanelDivider);
1124             mPanelLayout->addWidget(bottom);
1125             minWidth = std::max(minWidth, bottom->minimumSizeHint().width());
1126         }
1127         else if (mPanelDivider)
1128             mPanelDivider->hide();
1129         // Warning: mPanel->vericalScrollBar()->width() returns a silly value
1130         //          until AFTER showEvent() has been executed.
1131         mPanel->setMinimumWidth(minWidth + mPanelScrollBarWidth);
1132 
1133         int panelWidth = 0;
1134         if (mShowDateNavigator)
1135             panelWidth = mDatePicker->sizeHint().width();
1136         if (mShowResources)
1137             panelWidth = std::max(panelWidth, mResourcesWidth);
1138         panelWidth += mPanelScrollBarWidth;
1139 
1140         const int listWidth = width() - mSplitter->handleWidth() - panelWidth;
1141         mListView->resize(listWidth, mListView->height());
1142         mSplitter->setSizes({ panelWidth, listWidth });
1143     }
1144 }
1145 
1146 /******************************************************************************
1147 * Called when an error occurs in the resource calendar, to display a message.
1148 */
1149 void MainWindow::showErrorMessage(const QString& msg)
1150 {
1151     KAMessageBox::error(this, msg);
1152 }
1153 
1154 /******************************************************************************
1155 * Called when the system tray icon is created or destroyed.
1156 * Set the system tray icon menu text according to whether or not the system
1157 * tray icon is currently visible.
1158 */
1159 void MainWindow::updateTrayIconAction()
1160 {
1161     mActionToggleTrayIcon->setEnabled(QSystemTrayIcon::isSystemTrayAvailable());
1162     mActionToggleTrayIcon->setChecked(theApp()->trayIconDisplayed());
1163 }
1164 
1165 /******************************************************************************
1166 * Called when the active status of Find changes.
1167 */
1168 void MainWindow::slotFindActive(bool active)
1169 {
1170     mActionFindNext->setEnabled(active);
1171     mActionFindPrev->setEnabled(active);
1172 }
1173 
1174 /******************************************************************************
1175 * Called when the Undo action is selected.
1176 */
1177 void MainWindow::slotUndo()
1178 {
1179     Undo::undo(this, KLocalizedString::removeAcceleratorMarker(mActionUndo->text()));
1180 }
1181 
1182 /******************************************************************************
1183 * Called when the Redo action is selected.
1184 */
1185 void MainWindow::slotRedo()
1186 {
1187     Undo::redo(this, KLocalizedString::removeAcceleratorMarker(mActionRedo->text()));
1188 }
1189 
1190 /******************************************************************************
1191 * Called when an Undo item is selected.
1192 */
1193 void MainWindow::slotUndoItem(QAction* action)
1194 {
1195     int id = mUndoMenuIds.value(action);
1196     Undo::undo(id, this, Undo::actionText(Undo::UNDO, id));
1197 }
1198 
1199 /******************************************************************************
1200 * Called when a Redo item is selected.
1201 */
1202 void MainWindow::slotRedoItem(QAction* action)
1203 {
1204     int id = mUndoMenuIds.value(action);
1205     Undo::redo(id, this, Undo::actionText(Undo::REDO, id));
1206 }
1207 
1208 /******************************************************************************
1209 * Called when the Undo menu is about to show.
1210 * Populates the menu.
1211 */
1212 void MainWindow::slotInitUndoMenu()
1213 {
1214     initUndoMenu(mActionUndo->popupMenu(), Undo::UNDO);
1215 }
1216 
1217 /******************************************************************************
1218 * Called when the Redo menu is about to show.
1219 * Populates the menu.
1220 */
1221 void MainWindow::slotInitRedoMenu()
1222 {
1223     initUndoMenu(mActionRedo->popupMenu(), Undo::REDO);
1224 }
1225 
1226 /******************************************************************************
1227 * Populate the undo or redo menu.
1228 */
1229 void MainWindow::initUndoMenu(QMenu* menu, Undo::Type type)
1230 {
1231     menu->clear();
1232     mUndoMenuIds.clear();
1233     const QString& action = (type == Undo::UNDO) ? undoTextStripped : redoTextStripped;
1234     const QList<int> ids = Undo::ids(type);
1235     for (const int id : ids)
1236     {
1237         const QString actText = Undo::actionText(type, id);
1238         const QString descrip = Undo::description(type, id);
1239         const QString text = descrip.isEmpty()
1240                            ? i18nc("@action Undo/Redo [action]", "%1 %2", action, actText)
1241                            : i18nc("@action Undo [action]: message", "%1 %2: %3", action, actText, descrip);
1242         QAction* act = menu->addAction(text);
1243         mUndoMenuIds[act] = id;
1244     }
1245 }
1246 
1247 /******************************************************************************
1248 * Called when the status of the Undo or Redo list changes.
1249 * Change the Undo or Redo text to include the action which would be undone/redone.
1250 */
1251 void MainWindow::slotUndoStatus(const QString& undo, const QString& redo)
1252 {
1253     if (undo.isNull())
1254     {
1255         mActionUndo->setEnabled(false);
1256         mActionUndo->setText(undoText);
1257     }
1258     else
1259     {
1260         mActionUndo->setEnabled(true);
1261         mActionUndo->setText(QStringLiteral("%1 %2").arg(undoText, undo));
1262     }
1263     if (redo.isNull())
1264     {
1265         mActionRedo->setEnabled(false);
1266         mActionRedo->setText(redoText);
1267     }
1268     else
1269     {
1270         mActionRedo->setEnabled(true);
1271         mActionRedo->setText(QStringLiteral("%1 %2").arg(redoText, redo));
1272     }
1273 }
1274 
1275 /******************************************************************************
1276 * Called when the Refresh Alarms menu item is selected.
1277 */
1278 void MainWindow::slotRefreshAlarms()
1279 {
1280     KAlarm::refreshAlarms();
1281 }
1282 
1283 /******************************************************************************
1284 * Called when the "Configure KAlarm" menu item is selected.
1285 */
1286 void MainWindow::slotPreferences()
1287 {
1288     KAlarmPrefDlg::display();
1289 }
1290 
1291 /******************************************************************************
1292 * Called when the Show Menubar menu item is selected.
1293 */
1294 void MainWindow::slotToggleMenubar(bool dontShowWarning)
1295 {
1296     if (menuBar())
1297     {
1298         if (mActionShowMenuBar->isChecked())
1299             menuBar()->show();
1300         else
1301         {
1302             if (!dontShowWarning
1303             &&  (!toolBar()->isVisible() || !toolBar()->actions().contains(mHamburgerMenu)))
1304             {
1305                 const QString accel = mActionShowMenuBar->shortcut().toString();
1306                 KMessageBox::information(this,
1307                                          i18n("<qt>This will hide the menu bar completely."
1308                                               " You can show it again by typing %1.</qt>", accel),
1309                                          i18n("Hide menu bar"), QStringLiteral("HideMenuBarWarning"));
1310             }
1311             menuBar()->hide();
1312         }
1313         Preferences::setShowMenuBar(mActionShowMenuBar->isChecked());
1314         Preferences::self()->save();
1315     }
1316 }
1317 
1318 /******************************************************************************
1319 * Called when the Configure Keys menu item is selected.
1320 */
1321 void MainWindow::slotConfigureKeys()
1322 {
1323     KShortcutsDialog::showDialog(actionCollection(),  KShortcutsEditor::LetterShortcutsAllowed, this);
1324 }
1325 
1326 /******************************************************************************
1327 * Called when the Configure Notifications menu item is selected.
1328 */
1329 void MainWindow::slotConfigureNotifications()
1330 {
1331     KNotifyConfigWidget::configure(this);
1332 }
1333 
1334 /******************************************************************************
1335 * Called when the Configure Toolbars menu item is selected.
1336 */
1337 void MainWindow::slotConfigureToolbar()
1338 {
1339     KConfigGroup grp(KSharedConfig::openConfig()->group(WINDOW_NAME));
1340     saveMainWindowSettings(grp);
1341     KEditToolBar dlg(factory());
1342     connect(&dlg, &KEditToolBar::newToolBarConfig, this, &MainWindow::slotNewToolbarConfig);
1343     dlg.exec();
1344 }
1345 
1346 /******************************************************************************
1347 * Called when OK or Apply is clicked in the Configure Toolbars dialog, to save
1348 * the new configuration.
1349 */
1350 void MainWindow::slotNewToolbarConfig()
1351 {
1352     createGUI(UI_FILE);
1353     applyMainWindowSettings(KSharedConfig::openConfig()->group(WINDOW_NAME));
1354 }
1355 
1356 /******************************************************************************
1357 * Called when the Quit menu item is selected.
1358 * Note that this must be called by the event loop, not directly from the menu
1359 * item, since otherwise the window will be deleted while still processing the
1360 * menu, resulting in a crash.
1361 */
1362 void MainWindow::slotQuit()
1363 {
1364     theApp()->doQuit(this);
1365 }
1366 
1367 /******************************************************************************
1368 * Called when the user or the session manager attempts to close the window.
1369 */
1370 void MainWindow::closeEvent(QCloseEvent* ce)
1371 {
1372     if (!qApp->isSavingSession())
1373     {
1374         // The user (not the session manager) wants to close the window.
1375         if (isTrayParent())
1376         {
1377             // It's the parent window of the system tray icon, so just hide
1378             // it to prevent the system tray icon closing.
1379             hide();
1380             theApp()->quitIf();
1381             ce->ignore();
1382             return;
1383         }
1384     }
1385     ce->accept();
1386 }
1387 
1388 /******************************************************************************
1389 * Called when the drag cursor enters a main or system tray window, to accept
1390 * or reject the dragged object.
1391 */
1392 void MainWindow::executeDragEnterEvent(QDragEnterEvent* e)
1393 {
1394     const QMimeData* data = e->mimeData();
1395     bool accept = ICalDrag::canDecode(data) ? !e->source()   // don't accept "text/calendar" objects from this application
1396                                             : data->hasText() || data->hasUrls();
1397     if (accept)
1398         e->acceptProposedAction();
1399 }
1400 
1401 /******************************************************************************
1402 * Called when an object is dropped on the window.
1403 * If the object is recognised, the edit alarm dialog is opened appropriately.
1404 */
1405 void MainWindow::dropEvent(QDropEvent* e)
1406 {
1407     executeDropEvent(this, e);
1408 }
1409 
1410 /******************************************************************************
1411 * Called when an object is dropped on a main or system tray window, to
1412 * evaluate the action required and extract the text.
1413 */
1414 void MainWindow::executeDropEvent(MainWindow* win, QDropEvent* e)
1415 {
1416     qCDebug(KALARM_LOG) << "MainWindow::executeDropEvent: Formats:" << e->mimeData()->formats();
1417     const QMimeData* data = e->mimeData();
1418     KAEvent::SubAction action = KAEvent::SubAction::Message;
1419     AlarmText          alarmText;
1420     MemoryCalendar::Ptr calendar(new MemoryCalendar(Preferences::timeSpecAsZone()));
1421 #ifndef NDEBUG
1422     const QString fmts = data->formats().join(QLatin1String(", "));
1423     qCDebug(KALARM_LOG) << "MainWindow::executeDropEvent:" << fmts;
1424 #endif
1425 
1426     /* The order of the tests below matters, since some dropped objects
1427      * provide more than one mime type.
1428      * Don't change them without careful thought !!
1429      */
1430     if (DragDrop::dropRFC822(data, alarmText))
1431     {
1432         // Email message(s). Ignore all but the first.
1433         qCDebug(KALARM_LOG) << "MainWindow::executeDropEvent: email";
1434 //TODO: Fetch attachments if an email alarm is created below
1435     }
1436     else if (ICalDrag::fromMimeData(data, calendar))
1437     {
1438         // iCalendar - If events are included, use the first event
1439         qCDebug(KALARM_LOG) << "MainWindow::executeDropEvent: iCalendar";
1440         const Event::List events = calendar->rawEvents();
1441         if (!events.isEmpty())
1442         {
1443             Event::Ptr event = events[0];
1444             if (event->alarms().isEmpty())
1445             {
1446                 Alarm::Ptr alarm = event->newAlarm();
1447                 alarm->setEnabled(true);
1448                 alarm->setTime(event->dtStart());
1449                 alarm->setDisplayAlarm(event->summary().isEmpty() ? event->description() : event->summary());
1450                 event->addAlarm(alarm);
1451             }
1452             KAEvent ev(event);
1453             KAlarm::editNewAlarm(ev, win);
1454             return;
1455         }
1456         // If todos are included, use the first todo
1457         const Todo::List todos = calendar->rawTodos();
1458         if (!todos.isEmpty())
1459         {
1460             Todo::Ptr todo = todos[0];
1461             alarmText.setTodo(todo);
1462             KADateTime start(todo->dtStart(true));
1463             KADateTime due(todo->dtDue(true));
1464             bool haveBothTimes = false;
1465             if (todo->hasDueDate())
1466             {
1467                 if (start.isValid())
1468                     haveBothTimes = true;
1469                 else
1470                     start = due;
1471             }
1472             if (todo->allDay())
1473                 start.setDateOnly(true);
1474             KAEvent::Flags flags = KAEvent::DEFAULT_FONT;
1475             if (start.isDateOnly())
1476                 flags |= KAEvent::ANY_TIME;
1477             KAEvent ev(start, todo->summary(), alarmText.displayText(), Preferences::defaultBgColour(), Preferences::defaultFgColour(),
1478                        QFont(), KAEvent::SubAction::Message, 0, flags, true);
1479             ev.startChanges();
1480             if (todo->recurs())
1481             {
1482                 ev.setRecurrence(*todo->recurrence());
1483                 ev.setNextOccurrence(KADateTime::currentUtcDateTime());
1484             }
1485             const Alarm::List alarms = todo->alarms();
1486             if (!alarms.isEmpty()  &&  alarms[0]->type() == Alarm::Display)
1487             {
1488                 // A display alarm represents a reminder
1489                 int offset = 0;
1490                 if (alarms[0]->hasStartOffset())
1491                     offset = alarms[0]->startOffset().asSeconds();
1492                 else if (alarms[0]->hasEndOffset())
1493                 {
1494                     offset = alarms[0]->endOffset().asSeconds();
1495                     if (haveBothTimes)
1496                     {
1497                         // Get offset relative to start time instead of due time
1498                         offset += start.secsTo(due);
1499                     }
1500                 }
1501                 if (offset / 60)
1502                     ev.setReminder(-offset / 60, false);
1503             }
1504             ev.endChanges();
1505             KAlarm::editNewAlarm(ev, win);
1506         }
1507         return;
1508     }
1509     else
1510     {
1511         QUrl url;
1512         if (KAlarm::dropAkonadiEmail(data, url, alarmText))
1513         {
1514             // It's an email held in Akonadi
1515             qCDebug(KALARM_LOG) << "MainWindow::executeDropEvent: Akonadi email";
1516 //TODO: Fetch attachments if an email alarm is created below
1517         }
1518         else if (!url.isEmpty())
1519         {
1520             // The data provides a URL, but it isn't an Akonadi email URL.
1521             qCDebug(KALARM_LOG) << "MainWindow::executeDropEvent: URL";
1522             // Try to find the mime type of the file, without downloading a remote file
1523             QMimeDatabase mimeDb;
1524             const QString mimeTypeName = mimeDb.mimeTypeForUrl(url).name();
1525             action = mimeTypeName.startsWith(QLatin1String("audio/")) ? KAEvent::SubAction::Audio : KAEvent::SubAction::File;
1526             alarmText.setText(url.toDisplayString());
1527         }
1528     }
1529     if (alarmText.isEmpty())
1530     {
1531         if (data->hasText())
1532         {
1533             const QString text = data->text();
1534             qCDebug(KALARM_LOG) << "MainWindow::executeDropEvent: text";
1535             alarmText.setText(text);
1536         }
1537         else
1538             return;
1539     }
1540 
1541     if (!alarmText.isEmpty())
1542     {
1543         if (action == KAEvent::SubAction::Message
1544         &&  (alarmText.isEmail() || alarmText.isScript()))
1545         {
1546             // If the alarm text could be interpreted as an email or command script,
1547             // prompt for which type of alarm to create.
1548             QStringList types;
1549             types += i18nc("@item:inlistbox", "Display Alarm");
1550             if (alarmText.isEmail())
1551                 types += i18nc("@item:inlistbox", "Email Alarm");
1552             else if (alarmText.isScript())
1553                 types += i18nc("@item:inlistbox", "Command Alarm");
1554             bool ok = false;
1555             const QString type = QInputDialog::getItem(mainMainWindow(), i18nc("@title:window", "Alarm Type"),
1556                                                        i18nc("@info", "Choose alarm type to create:"), types, 0, false, &ok);
1557             if (!ok)
1558                 return;   // user didn't press OK
1559             int i = types.indexOf(type);
1560             if (i == 1)
1561                 action = alarmText.isEmail() ? KAEvent::SubAction::Email : KAEvent::SubAction::Command;
1562         }
1563         KAlarm::editNewAlarm(action, win, &alarmText);
1564     }
1565 }
1566 
1567 /******************************************************************************
1568 * Called when the status of a calendar has changed.
1569 * Enable or disable actions appropriately.
1570 */
1571 void MainWindow::slotCalendarStatusChanged()
1572 {
1573     // Find whether there are any writable calendars
1574     bool active  = !Resources::enabledResources(CalEvent::ACTIVE, true).isEmpty();
1575     bool templat = !Resources::enabledResources(CalEvent::TEMPLATE, true).isEmpty();
1576     for (MainWindow* w : std::as_const(mWindowList))
1577     {
1578         w->mActionImportAlarms->setEnabled(active || templat);
1579         if (w->mActionImportBirthdays)
1580             w->mActionImportBirthdays->setEnabled(active);
1581         w->mActionCreateTemplate->setEnabled(templat);
1582         // Note: w->mActionNew enabled status is set in the NewAlarmAction class.
1583         w->slotSelection();
1584     }
1585 }
1586 
1587 /******************************************************************************
1588 * Called when the selected items in the ListView change.
1589 * Enables the actions appropriately.
1590 */
1591 void MainWindow::slotSelection()
1592 {
1593     // Find which events have been selected
1594     QList<KAEvent> events = mListView->selectedEvents();
1595     int count = events.count();
1596     if (!count)
1597     {
1598         selectionCleared();    // disable actions
1599         Q_EMIT selectionChanged();
1600         return;
1601     }
1602 
1603     // Find whether there are any writable resources
1604     bool active = mActionNew->isEnabled();
1605 
1606     bool readOnly = false;
1607     bool allArchived = true;
1608     bool enableReactivate = true;
1609     bool enableEnableDisable = true;
1610     bool enableEnable = false;
1611     bool enableDisable = false;
1612     const KADateTime now = KADateTime::currentUtcDateTime();
1613     for (int i = 0;  i < count;  ++i)
1614     {
1615         const KAEvent ev = ResourcesCalendar::event(EventId(events.at(i)));   // get up-to-date status
1616         const KAEvent& event = ev.isValid() ? ev : events[i];
1617         bool expired = event.expired();
1618         if (!expired)
1619             allArchived = false;
1620         if (KAlarm::eventReadOnly(event.id()))
1621             readOnly = true;
1622         if (enableReactivate
1623         &&  (!expired  ||  !event.occursAfter(now, true)))
1624             enableReactivate = false;
1625         if (enableEnableDisable)
1626         {
1627             if (expired)
1628                 enableEnableDisable = enableEnable = enableDisable = false;
1629             else
1630             {
1631                 if (!enableEnable  &&  !event.enabled())
1632                     enableEnable = true;
1633                 if (!enableDisable  &&  event.enabled())
1634                     enableDisable = true;
1635             }
1636         }
1637     }
1638 
1639     qCDebug(KALARM_LOG) << "MainWindow::slotSelection: true";
1640     mActionCreateTemplate->setEnabled((count == 1) && !Resources::enabledResources(CalEvent::TEMPLATE, true).isEmpty());
1641     mActionExportAlarms->setEnabled(true);
1642     mActionExport->setEnabled(true);
1643     mActionCopy->setEnabled(active && count == 1);
1644     mActionModify->setEnabled(count == 1);
1645     mActionDelete->setEnabled(!readOnly && (active || allArchived));
1646     mActionReactivate->setEnabled(active && enableReactivate);
1647     mActionEnable->setEnabled(active && !readOnly && (enableEnable || enableDisable));
1648     if (enableEnable || enableDisable)
1649         setEnableText(enableEnable);
1650 
1651     Q_EMIT selectionChanged();
1652 }
1653 
1654 /******************************************************************************
1655 * Called when a context menu is requested in the ListView.
1656 * Displays a context menu to modify or delete the selected item.
1657 */
1658 void MainWindow::slotContextMenuRequested(const QPoint& globalPos)
1659 {
1660     qCDebug(KALARM_LOG) << "MainWindow::slotContextMenuRequested";
1661     // Recreate the context menu if it has been deleted (which happens if the
1662     // toolbar is edited).
1663     if (!mContextMenu)
1664         mContextMenu = static_cast<QMenu*>(factory()->container(QStringLiteral("listContext"), this));
1665     mContextMenu->popup(globalPos);
1666 }
1667 
1668 /******************************************************************************
1669 * Disables actions when no item is selected.
1670 */
1671 void MainWindow::selectionCleared()
1672 {
1673     mActionCreateTemplate->setEnabled(false);
1674     mActionExportAlarms->setEnabled(false);
1675     mActionExport->setEnabled(false);
1676     mActionCopy->setEnabled(false);
1677     mActionModify->setEnabled(false);
1678     mActionDelete->setEnabled(false);
1679     mActionReactivate->setEnabled(false);
1680     mActionEnable->setEnabled(false);
1681 }
1682 
1683 /******************************************************************************
1684 * Set the text of the Enable/Disable menu action.
1685 */
1686 void MainWindow::setEnableText(bool enable)
1687 {
1688     mActionEnableEnable = enable;
1689     mActionEnable->setText(enable ? i18nc("@action", "Enable") : i18nc("@action", "Disable"));
1690 }
1691 
1692 /******************************************************************************
1693 * Display or hide the specified main window.
1694 * This should only be called when the application doesn't run in the system tray.
1695 */
1696 MainWindow* MainWindow::toggleWindow(MainWindow* win)
1697 {
1698     if (win  &&  mWindowList.indexOf(win) != -1)
1699     {
1700         // A window is specified (and it exists)
1701         if (win->isVisible())
1702         {
1703             // The window is visible, so close it
1704             win->close();
1705             return nullptr;
1706         }
1707         else
1708         {
1709             // The window is hidden, so display it
1710             win->hide();        // in case it's on a different desktop
1711             win->setWindowState(win->windowState() & ~Qt::WindowMinimized);
1712             win->raise();
1713             win->activateWindow();
1714             return win;
1715         }
1716     }
1717 
1718     // No window is specified, or the window doesn't exist. Open a new one.
1719     win = create();
1720     win->show();
1721     return win;
1722 }
1723 
1724 /******************************************************************************
1725 * Called when the Edit button is clicked in an alarm message window.
1726 * This controls the alarm edit dialog created by the alarm window, and allows
1727 * it to remain unaffected by the alarm window closing.
1728 * See MessageWindow::slotEdit() for more information.
1729 */
1730 void MainWindow::editAlarm(EditAlarmDlg* dlg, const KAEvent& event)
1731 {
1732     mEditAlarmMap[dlg] = event;
1733     connect(dlg, &KEditToolBar::accepted, this, &MainWindow::editAlarmOk);
1734     connect(dlg, &KEditToolBar::destroyed, this, &MainWindow::editAlarmDeleted);
1735     dlg->setAttribute(Qt::WA_DeleteOnClose, true);   // ensure no memory leaks
1736     dlg->show();
1737 }
1738 
1739 /******************************************************************************
1740 * Called when OK is clicked in the alarm edit dialog shown by editAlarm().
1741 * Updates the event which has been edited.
1742 */
1743 void MainWindow::editAlarmOk()
1744 {
1745     auto dlg = qobject_cast<EditAlarmDlg*>(sender());
1746     if (!dlg)
1747         return;
1748     QMap<EditAlarmDlg*, KAEvent>::Iterator it = mEditAlarmMap.find(dlg);
1749     if (it == mEditAlarmMap.end())
1750         return;
1751     KAEvent event = it.value();
1752     mEditAlarmMap.erase(it);
1753     if (!event.isValid())
1754         return;
1755     if (dlg->result() != QDialog::Accepted)
1756         return;
1757     Resource res = Resources::resourceForEvent(event.id());
1758     KAlarm::updateEditedAlarm(dlg, event, res);
1759 }
1760 
1761 /******************************************************************************
1762 * Called when the alarm edit dialog shown by editAlarm() is deleted.
1763 * Removes the dialog from the pending list.
1764 */
1765 void MainWindow::editAlarmDeleted(QObject* obj)
1766 {
1767     mEditAlarmMap.remove(static_cast<EditAlarmDlg*>(obj));
1768 }
1769 
1770 /******************************************************************************
1771 * Called when the Defer button is clicked in an alarm window or notification.
1772 * This controls the defer dialog created by the alarm display, and allows it to
1773 * remain unaffected by the alarm display closing.
1774 * See MessageWindow::slotEdit() for more information.
1775 */
1776 void MainWindow::showDeferAlarmDlg(MessageDisplay::DeferDlgData* data)
1777 {
1778     DeferAlarmDlg* dlg = data->dlg;
1779     mDeferAlarmMap[dlg] = data;
1780     connect(dlg, &KEditToolBar::finished, this, &MainWindow::deferAlarmDlgDone);
1781     connect(dlg, &KEditToolBar::destroyed, this, &MainWindow::deferAlarmDlgDeleted);
1782     dlg->setAttribute(Qt::WA_DeleteOnClose, true);   // ensure no memory leaks
1783     dlg->show();
1784 }
1785 
1786 /******************************************************************************
1787 * Called when OK is clicked in the defer alarm dialog shown by
1788 * showDeferAlarmDlg().
1789 */
1790 void MainWindow::deferAlarmDlgDone(int result)
1791 {
1792     processDeferAlarmDlg(sender(), result);
1793 }
1794 
1795 /******************************************************************************
1796 * Called when the defer alarm dialog shown by showDeferAlarmDlg() is complete.
1797 * Removes the dialog from the pending list, and processes the result.
1798 */
1799 void MainWindow::processDeferAlarmDlg(QObject* obj, int result)
1800 {
1801     auto dlg = qobject_cast<DeferAlarmDlg*>(obj);
1802     if (!dlg)
1803         return;
1804     QMap<DeferAlarmDlg*, MessageDisplay::DeferDlgData*>::Iterator it = mDeferAlarmMap.find(dlg);
1805     if (it == mDeferAlarmMap.end())
1806         return;
1807     MessageDisplay::DeferDlgData* data = it.value();
1808     mDeferAlarmMap.erase(it);
1809     MessageDisplay::processDeferDlg(data, result);
1810 }
1811 
1812 #include "moc_mainwindow.cpp"
1813 
1814 // vim: et sw=4: