SVN_SILENT made messages (.desktop file) - always resolve ours
[kdepim.git] / kalarm / mainwindow.cpp
blobe8e11bfed1b7a1cf5b4aa1ec6b090c9649bb19bb
1 /*
2 * mainwindow.cpp - main application window
3 * Program: kalarm
4 * Copyright © 2001-2015 by David Jarvie <djarvie@kde.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "kalarm.h"
22 #include "mainwindow.h"
24 #include "alarmcalendar.h"
25 #include "alarmlistdelegate.h"
26 #include "autoqpointer.h"
27 #include "alarmlistview.h"
28 #include "birthdaydlg.h"
29 #include "functions.h"
30 #include "kalarmapp.h"
31 #include "kamail.h"
32 #include "messagebox.h"
33 #include "newalarmaction.h"
34 #include "prefdlg.h"
35 #include "preferences.h"
36 #include "resourceselector.h"
37 #include "synchtimer.h"
38 #include "templatedlg.h"
39 #include "templatemenuaction.h"
40 #include "templatepickdlg.h"
41 #include "traywindow.h"
42 #include "wakedlg.h"
44 #include <kalarmcal/alarmtext.h>
45 #include <kalarmcal/kaevent.h>
47 #include <Libkdepim/MaillistDrag>
48 #include <kmime/kmime_content.h>
49 #include <KCalCore/MemoryCalendar>
50 #include <KCalUtils/kcalutils/icaldrag.h>
51 #include <AkonadiWidgets/controlgui.h>
52 using namespace KCalCore;
53 using namespace KCalUtils;
54 #include <KLocale>
55 #include <K4AboutData>
56 #include <ktoolbar.h>
57 #include <kactioncollection.h>
58 #include <ksystemtrayicon.h>
59 #include <kstandardaction.h>
60 #include <kiconloader.h>
61 #include <KLocalizedString>
62 #include <KSharedConfig>
63 #include <kconfiggroup.h>
64 #include <kshortcutsdialog.h>
65 #include <kedittoolbar.h>
66 #include <kxmlguifactory.h>
67 #include <ktoggleaction.h>
68 #include <ktoolbarpopupaction.h>
69 #include <KTimeZone>
71 #include <QAction>
72 #include <QSplitter>
73 #include <QByteArray>
74 #include <QDragEnterEvent>
75 #include <QDropEvent>
76 #include <QResizeEvent>
77 #include <QCloseEvent>
78 #include <QDesktopWidget>
79 #include <QMenu>
80 #include <QMimeDatabase>
81 #include <qinputdialog.h>
82 #include <QUrl>
83 #include "kalarm_debug.h"
85 using namespace KAlarmCal;
87 namespace
89 const QString UI_FILE(QStringLiteral("kalarmui.rc"));
90 const char* WINDOW_NAME = "MainWindow";
92 const char* VIEW_GROUP = "View";
93 const char* SHOW_TIME_KEY = "ShowAlarmTime";
94 const char* SHOW_TIME_TO_KEY = "ShowTimeToAlarm";
95 const char* SHOW_ARCHIVED_KEY = "ShowArchivedAlarms";
96 const char* SHOW_RESOURCES_KEY = "ShowResources";
98 QString undoText;
99 QString undoTextStripped;
100 QList<QKeySequence> undoShortcut;
101 QString redoText;
102 QString redoTextStripped;
103 QList<QKeySequence> redoShortcut;
107 /*=============================================================================
108 = Class: MainWindow
109 =============================================================================*/
111 MainWindow::WindowList MainWindow::mWindowList;
112 TemplateDlg* MainWindow::mTemplateDlg = Q_NULLPTR;
114 // Collect these widget labels together to ensure consistent wording and
115 // translations across different modules.
116 QString MainWindow::i18n_a_ShowAlarmTimes() { return i18nc("@action", "Show &Alarm Times"); }
117 QString MainWindow::i18n_chk_ShowAlarmTime() { return i18nc("@option:check", "Show alarm time"); }
118 QString MainWindow::i18n_o_ShowTimeToAlarms() { return i18nc("@action", "Show Time t&o Alarms"); }
119 QString MainWindow::i18n_chk_ShowTimeToAlarm() { return i18nc("@option:check", "Show time until alarm"); }
122 /******************************************************************************
123 * Construct an instance.
124 * To avoid resize() events occurring while still opening the calendar (and
125 * resultant crashes), the calendar is opened before constructing the instance.
127 MainWindow* MainWindow::create(bool restored)
129 theApp()->checkCalendar(); // ensure calendar is open
130 return new MainWindow(restored);
133 MainWindow::MainWindow(bool restored)
134 : MainWindowBase(Q_NULLPTR, Qt::WindowContextHelpButtonHint),
135 mResourcesWidth(-1),
136 mHiddenTrayParent(false),
137 mShown(false),
138 mResizing(false)
140 qCDebug(KALARM_LOG);
141 setAttribute(Qt::WA_DeleteOnClose);
142 setWindowModality(Qt::WindowModal);
143 setObjectName(QStringLiteral("MainWin")); // used by LikeBack
144 setPlainCaption(KComponentData::mainComponent().aboutData()->programName());
145 KConfigGroup config(KSharedConfig::openConfig(), VIEW_GROUP);
146 mShowResources = config.readEntry(SHOW_RESOURCES_KEY, false);
147 mShowArchived = config.readEntry(SHOW_ARCHIVED_KEY, false);
148 mShowTime = config.readEntry(SHOW_TIME_KEY, true);
149 mShowTimeTo = config.readEntry(SHOW_TIME_TO_KEY, false);
150 if (!restored)
152 KConfigGroup wconfig(KSharedConfig::openConfig(), WINDOW_NAME);
153 mResourcesWidth = wconfig.readEntry(QStringLiteral("Splitter %1").arg(KApplication::desktop()->width()), (int)0);
156 setAcceptDrops(true); // allow drag-and-drop onto this window
157 if (!mShowTimeTo)
158 mShowTime = true; // ensure at least one time column is visible
160 mSplitter = new QSplitter(Qt::Horizontal, this);
161 mSplitter->setChildrenCollapsible(false);
162 mSplitter->installEventFilter(this);
163 setCentralWidget(mSplitter);
165 // Create the calendar resource selector widget
166 Akonadi::ControlGui::widgetNeedsAkonadi(this);
167 mResourceSelector = new ResourceSelector(mSplitter);
168 mSplitter->setStretchFactor(0, 0); // don't resize resource selector when window is resized
169 mSplitter->setStretchFactor(1, 1);
171 // Create the alarm list widget
172 mListFilterModel = new AlarmListModel(this);
173 mListFilterModel->setEventTypeFilter(mShowArchived ? CalEvent::ACTIVE | CalEvent::ARCHIVED : CalEvent::ACTIVE);
174 mListView = new AlarmListView(WINDOW_NAME, mSplitter);
175 mListView->setModel(mListFilterModel);
176 mListView->selectTimeColumns(mShowTime, mShowTimeTo);
177 mListView->sortByColumn(mShowTime ? AlarmListModel::TimeColumn : AlarmListModel::TimeToColumn, Qt::AscendingOrder);
178 mListView->setItemDelegate(new AlarmListDelegate(mListView));
179 connect(mListView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(slotSelection()));
180 connect(mListView, &AlarmListView::contextMenuRequested, this, &MainWindow::slotContextMenuRequested);
181 connect(AkonadiModel::instance(), SIGNAL(collectionStatusChanged(Akonadi::Collection,AkonadiModel::Change,QVariant,bool)),
182 SLOT(slotCalendarStatusChanged()));
183 connect(mResourceSelector, &ResourceSelector::resized, this, &MainWindow::resourcesResized);
184 mListView->installEventFilter(this);
185 initActions();
187 setAutoSaveSettings(QLatin1String(WINDOW_NAME), true); // save toolbars, window sizes etc.
188 mWindowList.append(this);
189 if (mWindowList.count() == 1)
191 // It's the first main window
192 if (theApp()->wantShowInSystemTray())
193 theApp()->displayTrayIcon(true, this); // create system tray icon for run-in-system-tray mode
194 else if (theApp()->trayWindow())
195 theApp()->trayWindow()->setAssocMainWindow(this); // associate this window with the system tray icon
197 slotCalendarStatusChanged(); // initialise action states now that window is registered
200 MainWindow::~MainWindow()
202 qCDebug(KALARM_LOG);
203 bool trayParent = isTrayParent(); // must call before removing from window list
204 mWindowList.removeAt(mWindowList.indexOf(this));
206 // Prevent view updates during window destruction
207 delete mResourceSelector;
208 mResourceSelector = Q_NULLPTR;
209 delete mListView;
210 mListView = Q_NULLPTR;
212 if (theApp()->trayWindow())
214 if (trayParent)
215 delete theApp()->trayWindow();
216 else
217 theApp()->trayWindow()->removeWindow(this);
219 KSharedConfig::openConfig()->sync(); // save any new window size to disc
220 theApp()->quitIf();
223 /******************************************************************************
224 * Save settings to the session managed config file, for restoration
225 * when the program is restored.
227 void MainWindow::saveProperties(KConfigGroup& config)
229 config.writeEntry("HiddenTrayParent", isTrayParent() && isHidden());
230 config.writeEntry("ShowArchived", mShowArchived);
231 config.writeEntry("ShowTime", mShowTime);
232 config.writeEntry("ShowTimeTo", mShowTimeTo);
233 config.writeEntry("ResourcesWidth", mResourceSelector->isHidden() ? 0 : mResourceSelector->width());
236 /******************************************************************************
237 * Read settings from the session managed config file.
238 * This function is automatically called whenever the app is being
239 * restored. Read in whatever was saved in saveProperties().
241 void MainWindow::readProperties(const KConfigGroup& config)
243 mHiddenTrayParent = config.readEntry("HiddenTrayParent", true);
244 mShowArchived = config.readEntry("ShowArchived", false);
245 mShowTime = config.readEntry("ShowTime", true);
246 mShowTimeTo = config.readEntry("ShowTimeTo", false);
247 mResourcesWidth = config.readEntry("ResourcesWidth", (int)0);
248 mShowResources = (mResourcesWidth > 0);
251 /******************************************************************************
252 * Get the main main window, i.e. the parent of the system tray icon, or if
253 * none, the first main window to be created. Visible windows take precedence
254 * over hidden ones.
256 MainWindow* MainWindow::mainMainWindow()
258 MainWindow* tray = theApp()->trayWindow() ? theApp()->trayWindow()->assocMainWindow() : Q_NULLPTR;
259 if (tray && tray->isVisible())
260 return tray;
261 for (int i = 0, end = mWindowList.count(); i < end; ++i)
262 if (mWindowList[i]->isVisible())
263 return mWindowList[i];
264 if (tray)
265 return tray;
266 if (mWindowList.isEmpty())
267 return Q_NULLPTR;
268 return mWindowList[0];
271 /******************************************************************************
272 * Check whether this main window is effectively the parent of the system tray icon.
274 bool MainWindow::isTrayParent() const
276 TrayWindow* tray = theApp()->trayWindow();
277 if (!tray || !KSystemTrayIcon::isSystemTrayAvailable())
278 return false;
279 if (tray->assocMainWindow() == this)
280 return true;
281 return mWindowList.count() == 1;
284 /******************************************************************************
285 * Close all main windows.
287 void MainWindow::closeAll()
289 while (!mWindowList.isEmpty())
290 delete mWindowList[0]; // N.B. the destructor removes the window from the list
293 /******************************************************************************
294 * Intercept events for the splitter widget.
296 bool MainWindow::eventFilter(QObject* obj, QEvent* e)
298 if (obj == mSplitter)
300 switch (e->type())
302 case QEvent::Resize:
303 // Don't change resources size while WINDOW is being resized.
304 // Resize event always occurs before Paint.
305 mResizing = true;
306 break;
307 case QEvent::Paint:
308 // Allow resources to be resized again
309 mResizing = false;
310 break;
311 default:
312 break;
315 else if (obj == mListView)
317 switch (e->type())
319 case QEvent::KeyPress:
321 QKeyEvent* ke = static_cast<QKeyEvent*>(e);
322 if (ke->key() == Qt::Key_Delete && ke->modifiers() == Qt::ShiftModifier)
324 // Prevent Shift-Delete being processed by EventListDelegate
325 mActionDeleteForce->trigger();
326 return true;
328 break;
330 default:
331 break;
334 return false;
337 /******************************************************************************
338 * Called when the window's size has changed (before it is painted).
339 * Sets the last column in the list view to extend at least to the right hand
340 * edge of the list view.
341 * Records the new size in the config file.
343 void MainWindow::resizeEvent(QResizeEvent* re)
345 // Save the window's new size only if it's the first main window
346 MainWindowBase::resizeEvent(re);
347 if (mResourcesWidth > 0)
349 QList<int> widths;
350 widths.append(mResourcesWidth);
351 widths.append(width() - mResourcesWidth - mSplitter->handleWidth());
352 mSplitter->setSizes(widths);
356 void MainWindow::resourcesResized()
358 if (!mShown || mResizing)
359 return;
360 QList<int> widths = mSplitter->sizes();
361 if (widths.count() > 1)
363 mResourcesWidth = widths[0];
364 // Width is reported as non-zero when resource selector is
365 // actually invisible, so note a zero width in these circumstances.
366 if (mResourcesWidth <= 5)
367 mResourcesWidth = 0;
368 else if (mainMainWindow() == this)
370 KConfigGroup config(KSharedConfig::openConfig(), WINDOW_NAME);
371 config.writeEntry(QStringLiteral("Splitter %1").arg(KApplication::desktop()->width()), mResourcesWidth);
376 /******************************************************************************
377 * Called when the window is first displayed.
378 * Sets the last column in the list view to extend at least to the right hand
379 * edge of the list view.
381 void MainWindow::showEvent(QShowEvent* se)
383 if (mResourcesWidth > 0)
385 QList<int> widths;
386 widths.append(mResourcesWidth);
387 widths.append(width() - mResourcesWidth - mSplitter->handleWidth());
388 mSplitter->setSizes(widths);
390 MainWindowBase::showEvent(se);
391 mShown = true;
394 /******************************************************************************
395 * Display the window.
397 void MainWindow::show()
399 MainWindowBase::show();
400 if (mMenuError)
402 // Show error message now that the main window has been displayed.
403 // Waiting until now lets the user easily associate the message with
404 // the main window which is faulty.
405 KAMessageBox::error(this, xi18nc("@info", "Failure to create menus (perhaps <filename>%1</filename> missing or corrupted)", UI_FILE));
406 mMenuError = false;
410 /******************************************************************************
411 * Called after the window is hidden.
413 void MainWindow::hideEvent(QHideEvent* he)
415 MainWindowBase::hideEvent(he);
418 /******************************************************************************
419 * Initialise the menu, toolbar and main window actions.
421 void MainWindow::initActions()
423 //KShortcut dummy;
424 KActionCollection* actions = actionCollection();
426 mActionTemplates = new QAction(i18nc("@action", "&Templates..."), this);
427 actions->addAction(QStringLiteral("templates"), mActionTemplates);
428 connect(mActionTemplates, &QAction::triggered, this, &MainWindow::slotTemplates);
430 mActionNew = new NewAlarmAction(false, i18nc("@action", "&New"), this);
431 actions->addAction(QStringLiteral("new"), mActionNew);
433 QAction* action = mActionNew->displayAlarmAction();
434 actions->addAction(QStringLiteral("newDisplay"), action);
435 #pragma message("port QT5")
436 //QT5 action->setGlobalShortcut(dummy); // actions->addAction() must be called first!
437 connect(action, &QAction::triggered, this, &MainWindow::slotNewDisplay);
439 action = mActionNew->commandAlarmAction();
440 actions->addAction(QStringLiteral("newCommand"), action);
441 #pragma message("port QT5")
442 //QT5 action->setGlobalShortcut(dummy); // actions->addAction() must be called first!
443 connect(action, &QAction::triggered, this, &MainWindow::slotNewCommand);
445 action = mActionNew->emailAlarmAction();
446 actions->addAction(QStringLiteral("newEmail"), action);
447 #pragma message("port QT5")
448 //QT5 action->setGlobalShortcut(dummy); // actions->addAction() must be called first!
449 connect(action, &QAction::triggered, this, &MainWindow::slotNewEmail);
451 action = mActionNew->audioAlarmAction();
452 actions->addAction(QStringLiteral("newAudio"), action);
453 #pragma message("port QT5")
454 //QT5 action->setGlobalShortcut(dummy); // actions->addAction() must be called first!
455 connect(action, &QAction::triggered, this, &MainWindow::slotNewAudio);
457 TemplateMenuAction *templateMenuAction = mActionNew->fromTemplateAlarmAction();
458 actions->addAction(QStringLiteral("newFromTemplate"), templateMenuAction);
459 connect(templateMenuAction, &TemplateMenuAction::selected, this, &MainWindow::slotNewFromTemplate);
461 mActionCreateTemplate = new QAction(i18nc("@action", "Create Tem&plate..."), this);
462 actions->addAction(QStringLiteral("createTemplate"), mActionCreateTemplate);
463 connect(mActionCreateTemplate, &QAction::triggered, this, &MainWindow::slotNewTemplate);
465 mActionCopy = new QAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18nc("@action", "&Copy..."), this);
466 actions->addAction(QStringLiteral("copy"), mActionCopy);
467 actions->setDefaultShortcut(mActionCopy, QKeySequence(Qt::SHIFT + Qt::Key_Insert));
468 connect(mActionCopy, &QAction::triggered, this, &MainWindow::slotCopy);
470 mActionModify = new QAction(QIcon::fromTheme(QStringLiteral("document-properties")), i18nc("@action", "&Edit..."), this);
471 actions->addAction(QStringLiteral("modify"), mActionModify);
472 actions->setDefaultShortcut(mActionModify, QKeySequence(Qt::CTRL + Qt::Key_E));
473 connect(mActionModify, &QAction::triggered, this, &MainWindow::slotModify);
475 mActionDelete = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action", "&Delete"), this);
476 actions->addAction(QStringLiteral("delete"), mActionDelete);
477 actions->setDefaultShortcut(mActionDelete, QKeySequence::Delete);
478 connect(mActionDelete, &QAction::triggered, this, &MainWindow::slotDeleteIf);
480 // Set up Shift-Delete as a shortcut to delete without confirmation
481 mActionDeleteForce = new QAction(i18nc("@action", "Delete Without Confirmation"), this);
482 actions->addAction(QStringLiteral("delete-force"), mActionDeleteForce);
483 actions->setDefaultShortcut(mActionDeleteForce, QKeySequence::Delete + Qt::SHIFT);
484 connect(mActionDeleteForce, &QAction::triggered, this, &MainWindow::slotDeleteForce);
486 mActionReactivate = new QAction(i18nc("@action", "Reac&tivate"), this);
487 actions->addAction(QStringLiteral("undelete"), mActionReactivate);
488 actions->setDefaultShortcut(mActionReactivate, QKeySequence(Qt::CTRL + Qt::Key_R));
489 connect(mActionReactivate, &QAction::triggered, this, &MainWindow::slotReactivate);
491 mActionEnable = new QAction(this);
492 actions->addAction(QStringLiteral("disable"), mActionEnable);
493 actions->setDefaultShortcut(mActionEnable, QKeySequence(Qt::CTRL + Qt::Key_B));
494 connect(mActionEnable, &QAction::triggered, this, &MainWindow::slotEnable);
496 action = new QAction(i18nc("@action", "Wake From Suspend..."), this);
497 actions->addAction(QStringLiteral("wakeSuspend"), action);
498 connect(action, &QAction::triggered, this, &MainWindow::slotWakeFromSuspend);
500 action = KAlarm::createStopPlayAction(this);
501 actions->addAction(QStringLiteral("stopAudio"), action);
502 //QT5 action->setGlobalShortcut(dummy); // actions->addAction() must be called first!
504 mActionShowTime = new KToggleAction(i18n_a_ShowAlarmTimes(), this);
505 actions->addAction(QStringLiteral("showAlarmTimes"), mActionShowTime);
506 connect(mActionShowTime, &KToggleAction::triggered, this, &MainWindow::slotShowTime);
508 mActionShowTimeTo = new KToggleAction(i18n_o_ShowTimeToAlarms(), this);
509 actions->addAction(QStringLiteral("showTimeToAlarms"), mActionShowTimeTo);
510 actions->setDefaultShortcut(mActionShowTimeTo, QKeySequence(Qt::CTRL + Qt::Key_I));
511 connect(mActionShowTimeTo, &KToggleAction::triggered, this, &MainWindow::slotShowTimeTo);
513 mActionShowArchived = new KToggleAction(i18nc("@action", "Show Archi&ved Alarms"), this);
514 actions->addAction(QStringLiteral("showArchivedAlarms"), mActionShowArchived);
515 actions->setDefaultShortcut(mActionShowArchived, QKeySequence(Qt::CTRL + Qt::Key_P));
516 connect(mActionShowArchived, &KToggleAction::triggered, this, &MainWindow::slotShowArchived);
518 mActionToggleTrayIcon = new KToggleAction(i18nc("@action", "Show in System &Tray"), this);
519 actions->addAction(QStringLiteral("showInSystemTray"), mActionToggleTrayIcon);
520 connect(mActionToggleTrayIcon, &KToggleAction::triggered, this, &MainWindow::slotToggleTrayIcon);
522 mActionToggleResourceSel = new KToggleAction(QIcon::fromTheme(QStringLiteral("view-choose")), i18nc("@action", "Show &Calendars"), this);
523 actions->addAction(QStringLiteral("showResources"), mActionToggleResourceSel);
524 connect(mActionToggleResourceSel, &KToggleAction::triggered, this, &MainWindow::slotToggleResourceSelector);
526 mActionSpreadWindows = KAlarm::createSpreadWindowsAction(this);
527 actions->addAction(QStringLiteral("spread"), mActionSpreadWindows);
528 #pragma message("port QT5")
530 //QT5 mActionSpreadWindows->setGlobalShortcut(dummy); // actions->addAction() must be called first!
532 mActionImportAlarms = new QAction(i18nc("@action", "Import &Alarms..."), this);
533 actions->addAction(QStringLiteral("importAlarms"), mActionImportAlarms);
534 connect(mActionImportAlarms, &QAction::triggered, this, &MainWindow::slotImportAlarms);
536 mActionImportBirthdays = new QAction(i18nc("@action", "Import &Birthdays..."), this);
537 actions->addAction(QStringLiteral("importBirthdays"), mActionImportBirthdays);
538 connect(mActionImportBirthdays, &QAction::triggered, this, &MainWindow::slotBirthdays);
540 mActionExportAlarms = new QAction(i18nc("@action", "E&xport Selected Alarms..."), this);
541 actions->addAction(QStringLiteral("exportAlarms"), mActionExportAlarms);
542 connect(mActionExportAlarms, &QAction::triggered, this, &MainWindow::slotExportAlarms);
544 mActionExport = new QAction(i18nc("@action", "E&xport..."), this);
545 actions->addAction(QStringLiteral("export"), mActionExport);
546 connect(mActionExport, &QAction::triggered, this, &MainWindow::slotExportAlarms);
548 action = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18nc("@action", "&Refresh Alarms"), this);
549 actions->addAction(QStringLiteral("refreshAlarms"), action);
550 connect(action, &QAction::triggered, this, &MainWindow::slotRefreshAlarms);
552 KToggleAction* toggleAction = KAlarm::createAlarmEnableAction(this);
553 actions->addAction(QStringLiteral("alarmsEnable"), toggleAction);
554 if (undoText.isNull())
556 // Get standard texts, etc., for Undo and Redo actions
557 QAction* act = KStandardAction::undo(this, Q_NULLPTR, actions);
558 undoShortcut = act->shortcuts();
559 undoText = act->text();
560 undoTextStripped = KLocalizedString::removeAcceleratorMarker(undoText);
561 delete act;
562 act = KStandardAction::redo(this, Q_NULLPTR, actions);
563 redoShortcut = act->shortcuts();
564 redoText = act->text();
565 redoTextStripped = KLocalizedString::removeAcceleratorMarker(redoText);
566 delete act;
568 mActionUndo = new KToolBarPopupAction(QIcon::fromTheme(QStringLiteral("edit-undo")), undoText, this);
569 actions->addAction(QStringLiteral("edit_undo"), mActionUndo);
570 mActionUndo->setShortcuts(undoShortcut);
571 connect(mActionUndo, &KToolBarPopupAction::triggered, this, &MainWindow::slotUndo);
573 mActionRedo = new KToolBarPopupAction(QIcon::fromTheme(QStringLiteral("edit-redo")), redoText, this);
574 actions->addAction(QStringLiteral("edit_redo"), mActionRedo);
575 mActionRedo->setShortcuts(redoShortcut);
576 connect(mActionRedo, &KToolBarPopupAction::triggered, this, &MainWindow::slotRedo);
578 KStandardAction::find(mListView, SLOT(slotFind()), actions);
579 mActionFindNext = KStandardAction::findNext(mListView, SLOT(slotFindNext()), actions);
580 mActionFindPrev = KStandardAction::findPrev(mListView, SLOT(slotFindPrev()), actions);
581 KStandardAction::selectAll(mListView, SLOT(selectAll()), actions);
582 KStandardAction::deselect(mListView, SLOT(clearSelection()), actions);
583 // Quit only once the event loop is called; otherwise, the parent window will
584 // be deleted while still processing the action, resulting in a crash.
585 QAction* act = KStandardAction::quit(Q_NULLPTR, Q_NULLPTR, actions);
586 connect(act, &QAction::triggered, this, &MainWindow::slotQuit, Qt::QueuedConnection);
587 KStandardAction::keyBindings(this, SLOT(slotConfigureKeys()), actions);
588 KStandardAction::configureToolbars(this, SLOT(slotConfigureToolbar()), actions);
589 KStandardAction::preferences(this, SLOT(slotPreferences()), actions);
590 mResourceSelector->initActions(actions);
591 setStandardToolBarMenuEnabled(true);
592 createGUI(UI_FILE);
593 // Load menu and toolbar settings
594 applyMainWindowSettings(KSharedConfig::openConfig()->group(WINDOW_NAME));
596 mContextMenu = static_cast<QMenu*>(factory()->container(QStringLiteral("listContext"), this));
597 mActionsMenu = static_cast<QMenu*>(factory()->container(QStringLiteral("actions"), this));
598 QMenu* resourceMenu = static_cast<QMenu*>(factory()->container(QStringLiteral("resourceContext"), this));
599 mResourceSelector->setContextMenu(resourceMenu);
600 mMenuError = (!mContextMenu || !mActionsMenu || !resourceMenu);
601 connect(mActionUndo->menu(), SIGNAL(aboutToShow()), SLOT(slotInitUndoMenu()));
602 connect(mActionUndo->menu(), SIGNAL(triggered(QAction*)), SLOT(slotUndoItem(QAction*)));
603 connect(mActionRedo->menu(), SIGNAL(aboutToShow()), SLOT(slotInitRedoMenu()));
604 connect(mActionRedo->menu(), SIGNAL(triggered(QAction*)), SLOT(slotRedoItem(QAction*)));
605 connect(Undo::instance(), SIGNAL(changed(QString,QString)), SLOT(slotUndoStatus(QString,QString)));
606 connect(mListView, &AlarmListView::findActive, this, &MainWindow::slotFindActive);
607 Preferences::connect(SIGNAL(archivedKeepDaysChanged(int)), this, SLOT(updateKeepArchived(int)));
608 Preferences::connect(SIGNAL(showInSystemTrayChanged(bool)), this, SLOT(updateTrayIconAction()));
609 connect(theApp(), SIGNAL(trayIconToggled()), SLOT(updateTrayIconAction()));
611 // Set menu item states
612 setEnableText(true);
613 mActionShowTime->setChecked(mShowTime);
614 mActionShowTimeTo->setChecked(mShowTimeTo);
615 mActionShowArchived->setChecked(mShowArchived);
616 if (!Preferences::archivedKeepDays())
617 mActionShowArchived->setEnabled(false);
618 mActionToggleResourceSel->setChecked(mShowResources);
619 slotToggleResourceSelector();
620 updateTrayIconAction(); // set the correct text for this action
621 mActionUndo->setEnabled(Undo::haveUndo());
622 mActionRedo->setEnabled(Undo::haveRedo());
623 mActionFindNext->setEnabled(false);
624 mActionFindPrev->setEnabled(false);
626 mActionCopy->setEnabled(false);
627 mActionModify->setEnabled(false);
628 mActionDelete->setEnabled(false);
629 mActionReactivate->setEnabled(false);
630 mActionEnable->setEnabled(false);
631 mActionCreateTemplate->setEnabled(false);
632 mActionExport->setEnabled(false);
634 Undo::emitChanged(); // set the Undo/Redo menu texts
635 // Daemon::monitoringAlarms();
638 /******************************************************************************
639 * Enable or disable the Templates menu item in every main window instance.
641 void MainWindow::enableTemplateMenuItem(bool enable)
643 for (int i = 0, end = mWindowList.count(); i < end; ++i)
644 mWindowList[i]->mActionTemplates->setEnabled(enable);
647 /******************************************************************************
648 * Refresh the alarm list in every main window instance.
650 void MainWindow::refresh()
652 qCDebug(KALARM_LOG);
653 AkonadiModel::instance()->reload();
656 /******************************************************************************
657 * Called when the keep archived alarm setting changes in the user preferences.
658 * Enable/disable Show archived alarms option.
660 void MainWindow::updateKeepArchived(int days)
662 qCDebug(KALARM_LOG) << (bool)days;
663 if (mShowArchived && !days)
664 slotShowArchived(); // toggle Show Archived option setting
665 mActionShowArchived->setEnabled(days);
668 /******************************************************************************
669 * Select an alarm in the displayed list.
671 void MainWindow::selectEvent(Akonadi::Item::Id eventId)
673 mListView->clearSelection();
674 QModelIndex index = mListFilterModel->eventIndex(eventId);
675 if (index.isValid())
677 mListView->select(index);
678 mListView->scrollTo(index);
682 /******************************************************************************
683 * Return the single selected alarm in the displayed list.
685 KAEvent MainWindow::selectedEvent() const
687 return mListView->selectedEvent();
690 /******************************************************************************
691 * Deselect all alarms in the displayed list.
693 void MainWindow::clearSelection()
695 mListView->clearSelection();
698 /******************************************************************************
699 * Called when the New button is clicked to edit a new alarm to add to the list.
701 void MainWindow::slotNew(EditAlarmDlg::Type type)
703 KAlarm::editNewAlarm(type, mListView);
706 /******************************************************************************
707 * Called when a template is selected from the New From Template popup menu.
708 * Executes a New Alarm dialog, preset from the selected template.
710 void MainWindow::slotNewFromTemplate(const KAEvent* tmplate)
712 KAlarm::editNewAlarm(tmplate, mListView);
715 /******************************************************************************
716 * Called when the New Template button is clicked to create a new template
717 * based on the currently selected alarm.
719 void MainWindow::slotNewTemplate()
721 KAEvent event = mListView->selectedEvent();
722 if (event.isValid())
723 KAlarm::editNewTemplate(&event, this);
726 /******************************************************************************
727 * Called when the Copy button is clicked to edit a copy of an existing alarm,
728 * to add to the list.
730 void MainWindow::slotCopy()
732 KAEvent event = mListView->selectedEvent();
733 if (event.isValid())
734 KAlarm::editNewAlarm(&event, this);
737 /******************************************************************************
738 * Called when the Modify button is clicked to edit the currently highlighted
739 * alarm in the list.
741 void MainWindow::slotModify()
743 KAEvent event = mListView->selectedEvent();
744 if (event.isValid())
745 KAlarm::editAlarm(&event, this); // edit alarm (view-only mode if archived or read-only)
748 /******************************************************************************
749 * Called when the Delete button is clicked to delete the currently highlighted
750 * alarms in the list.
752 void MainWindow::slotDelete(bool force)
754 QVector<KAEvent> events = mListView->selectedEvents();
755 if (!force && Preferences::confirmAlarmDeletion())
757 int n = events.count();
758 if (KAMessageBox::warningContinueCancel(this, i18ncp("@info", "Do you really want to delete the selected alarm?",
759 "Do you really want to delete the %1 selected alarms?", n),
760 i18ncp("@title:window", "Delete Alarm", "Delete Alarms", n),
761 KGuiItem(i18nc("@action:button", "&Delete"), QStringLiteral("edit-delete")),
762 KStandardGuiItem::cancel(),
763 Preferences::CONFIRM_ALARM_DELETION)
764 != KMessageBox::Continue)
765 return;
768 // Remove any events which have just triggered, from the list to delete.
769 Undo::EventList undos;
770 AlarmCalendar* resources = AlarmCalendar::resources();
771 for (int i = 0; i < events.count(); )
773 Akonadi::Collection c = resources->collectionForEvent(events[i].itemId());
774 if (!c.isValid())
775 events.remove(i);
776 else
777 undos.append(events[i++], c);
780 if (events.isEmpty())
781 qCDebug(KALARM_LOG) << "No alarms left to delete";
782 else
784 // Delete the events from the calendar and displays
785 KAlarm::deleteEvents(events, true, this);
786 Undo::saveDeletes(undos);
790 /******************************************************************************
791 * Called when the Reactivate button is clicked to reinstate the currently
792 * highlighted archived alarms in the list.
794 void MainWindow::slotReactivate()
796 QVector<KAEvent> events = mListView->selectedEvents();
797 mListView->clearSelection();
799 // Add the alarms to the displayed lists and to the calendar file
800 Undo::EventList undos;
801 QVector<EventId> ineligibleIDs;
802 KAlarm::reactivateEvents(events, ineligibleIDs, Q_NULLPTR, this);
804 // Create the undo list, excluding ineligible events
805 AlarmCalendar* resources = AlarmCalendar::resources();
806 for (int i = 0, end = events.count(); i < end; ++i)
808 if (!ineligibleIDs.contains(EventId(events[i])))
810 undos.append(events[i], resources->collectionForEvent(events[i].itemId()));
813 Undo::saveReactivates(undos);
816 /******************************************************************************
817 * Called when the Enable/Disable button is clicked to enable or disable the
818 * currently highlighted alarms in the list.
820 void MainWindow::slotEnable()
822 bool enable = mActionEnableEnable; // save since changed in response to KAlarm::enableEvent()
823 QVector<KAEvent> events = mListView->selectedEvents();
824 QVector<KAEvent> eventCopies;
825 for (int i = 0, end = events.count(); i < end; ++i)
826 eventCopies += events[i];
827 KAlarm::enableEvents(eventCopies, enable, this);
828 slotSelection(); // update Enable/Disable action text
831 /******************************************************************************
832 * Called when the Show Alarm Times menu item is selected or deselected.
834 void MainWindow::slotShowTime()
836 mShowTime = !mShowTime;
837 mActionShowTime->setChecked(mShowTime);
838 if (!mShowTime && !mShowTimeTo)
839 slotShowTimeTo(); // at least one time column must be displayed
840 else
842 mListView->selectTimeColumns(mShowTime, mShowTimeTo);
843 KConfigGroup config(KSharedConfig::openConfig(), VIEW_GROUP);
844 config.writeEntry(SHOW_TIME_KEY, mShowTime);
845 config.writeEntry(SHOW_TIME_TO_KEY, mShowTimeTo);
849 /******************************************************************************
850 * Called when the Show Time To Alarms menu item is selected or deselected.
852 void MainWindow::slotShowTimeTo()
854 mShowTimeTo = !mShowTimeTo;
855 mActionShowTimeTo->setChecked(mShowTimeTo);
856 if (!mShowTimeTo && !mShowTime)
857 slotShowTime(); // at least one time column must be displayed
858 else
860 mListView->selectTimeColumns(mShowTime, mShowTimeTo);
861 KConfigGroup config(KSharedConfig::openConfig(), VIEW_GROUP);
862 config.writeEntry(SHOW_TIME_KEY, mShowTime);
863 config.writeEntry(SHOW_TIME_TO_KEY, mShowTimeTo);
867 /******************************************************************************
868 * Called when the Show Archived Alarms menu item is selected or deselected.
870 void MainWindow::slotShowArchived()
872 mShowArchived = !mShowArchived;
873 mActionShowArchived->setChecked(mShowArchived);
874 mActionShowArchived->setToolTip(mShowArchived ? i18nc("@info:tooltip", "Hide Archived Alarms")
875 : i18nc("@info:tooltip", "Show Archived Alarms"));
876 mListFilterModel->setEventTypeFilter(mShowArchived ? CalEvent::ACTIVE | CalEvent::ARCHIVED : CalEvent::ACTIVE);
877 mListView->reset();
878 KConfigGroup config(KSharedConfig::openConfig(), VIEW_GROUP);
879 config.writeEntry(SHOW_ARCHIVED_KEY, mShowArchived);
882 /******************************************************************************
883 * Called when the Spread Windows global shortcut is selected, to spread alarm
884 * windows so that they are all visible.
886 void MainWindow::slotSpreadWindowsShortcut()
888 mActionSpreadWindows->trigger();
891 /******************************************************************************
892 * Called when the Wake From Suspend menu option is selected.
894 void MainWindow::slotWakeFromSuspend()
896 (WakeFromSuspendDlg::create(this))->show();
899 /******************************************************************************
900 * Called when the Import Alarms menu item is selected, to merge alarms from an
901 * external calendar into the current calendars.
903 void MainWindow::slotImportAlarms()
905 AlarmCalendar::importAlarms(this);
908 /******************************************************************************
909 * Called when the Export Alarms menu item is selected, to export the selected
910 * alarms to an external calendar.
912 void MainWindow::slotExportAlarms()
914 QVector<KAEvent> events = mListView->selectedEvents();
915 if (!events.isEmpty())
917 KAEvent::List evts = KAEvent::ptrList(events);
918 AlarmCalendar::exportAlarms(evts, this);
922 /******************************************************************************
923 * Called when the Import Birthdays menu item is selected, to display birthdays
924 * from the address book for selection as alarms.
926 void MainWindow::slotBirthdays()
928 // Use AutoQPointer to guard against crash on application exit while
929 // the dialogue is still open. It prevents double deletion (both on
930 // deletion of MainWindow, and on return from this function).
931 AutoQPointer<BirthdayDlg> dlg = new BirthdayDlg(this);
932 if (dlg->exec() == QDialog::Accepted)
934 QVector<KAEvent> events = dlg->events();
935 if (!events.isEmpty())
937 mListView->clearSelection();
938 // Add alarm to the displayed lists and to the calendar file
939 KAlarm::UpdateResult status = KAlarm::addEvents(events, dlg, true, true);
941 Undo::EventList undos;
942 AlarmCalendar* resources = AlarmCalendar::resources();
943 for (int i = 0, end = events.count(); i < end; ++i)
945 Akonadi::Collection c = resources->collectionForEvent(events[i].itemId());
946 undos.append(events[i], c);
948 Undo::saveAdds(undos, i18nc("@info", "Import birthdays"));
950 if (status != KAlarm::UPDATE_FAILED)
951 KAlarm::outputAlarmWarnings(dlg);
956 /******************************************************************************
957 * Called when the Templates menu item is selected, to display the alarm
958 * template editing dialog.
960 void MainWindow::slotTemplates()
962 if (!mTemplateDlg)
964 mTemplateDlg = TemplateDlg::create(this);
965 enableTemplateMenuItem(false); // disable menu item in all windows
966 connect(mTemplateDlg, SIGNAL(finished()), SLOT(slotTemplatesEnd()));
967 mTemplateDlg->show();
971 /******************************************************************************
972 * Called when the alarm template editing dialog has exited.
974 void MainWindow::slotTemplatesEnd()
976 if (mTemplateDlg)
978 mTemplateDlg->deleteLater(); // this deletes the dialog once it is safe to do so
979 mTemplateDlg = Q_NULLPTR;
980 enableTemplateMenuItem(true); // re-enable menu item in all windows
984 /******************************************************************************
985 * Called when the Display System Tray Icon menu item is selected.
987 void MainWindow::slotToggleTrayIcon()
989 theApp()->displayTrayIcon(!theApp()->trayIconDisplayed(), this);
992 /******************************************************************************
993 * Called when the Show Resource Selector menu item is selected.
995 void MainWindow::slotToggleResourceSelector()
997 mShowResources = mActionToggleResourceSel->isChecked();
998 if (mShowResources)
1000 if (mResourcesWidth <= 0)
1002 mResourcesWidth = mResourceSelector->sizeHint().width();
1003 mResourceSelector->resize(mResourcesWidth, mResourceSelector->height());
1004 QList<int> widths = mSplitter->sizes();
1005 if (widths.count() == 1)
1007 int listwidth = widths[0] - mSplitter->handleWidth() - mResourcesWidth;
1008 mListView->resize(listwidth, mListView->height());
1009 widths.append(listwidth);
1010 widths[0] = mResourcesWidth;
1012 mSplitter->setSizes(widths);
1014 mResourceSelector->show();
1016 else
1017 mResourceSelector->hide();
1019 KConfigGroup config(KSharedConfig::openConfig(), VIEW_GROUP);
1020 config.writeEntry(SHOW_RESOURCES_KEY, mShowResources);
1023 /******************************************************************************
1024 * Called when an error occurs in the resource calendar, to display a message.
1026 void MainWindow::showErrorMessage(const QString& msg)
1028 KAMessageBox::error(this, msg);
1031 /******************************************************************************
1032 * Called when the system tray icon is created or destroyed.
1033 * Set the system tray icon menu text according to whether or not the system
1034 * tray icon is currently visible.
1036 void MainWindow::updateTrayIconAction()
1038 mActionToggleTrayIcon->setEnabled(KSystemTrayIcon::isSystemTrayAvailable());
1039 mActionToggleTrayIcon->setChecked(theApp()->trayIconDisplayed());
1042 /******************************************************************************
1043 * Called when the active status of Find changes.
1045 void MainWindow::slotFindActive(bool active)
1047 mActionFindNext->setEnabled(active);
1048 mActionFindPrev->setEnabled(active);
1051 /******************************************************************************
1052 * Called when the Undo action is selected.
1054 void MainWindow::slotUndo()
1056 Undo::undo(this, KLocalizedString::removeAcceleratorMarker(mActionUndo->text()));
1059 /******************************************************************************
1060 * Called when the Redo action is selected.
1062 void MainWindow::slotRedo()
1064 Undo::redo(this, KLocalizedString::removeAcceleratorMarker(mActionRedo->text()));
1067 /******************************************************************************
1068 * Called when an Undo item is selected.
1070 void MainWindow::slotUndoItem(QAction* action)
1072 int id = mUndoMenuIds[action];
1073 Undo::undo(id, this, Undo::actionText(Undo::UNDO, id));
1076 /******************************************************************************
1077 * Called when a Redo item is selected.
1079 void MainWindow::slotRedoItem(QAction* action)
1081 int id = mUndoMenuIds[action];
1082 Undo::redo(id, this, Undo::actionText(Undo::REDO, id));
1085 /******************************************************************************
1086 * Called when the Undo menu is about to show.
1087 * Populates the menu.
1089 void MainWindow::slotInitUndoMenu()
1091 initUndoMenu(mActionUndo->menu(), Undo::UNDO);
1094 /******************************************************************************
1095 * Called when the Redo menu is about to show.
1096 * Populates the menu.
1098 void MainWindow::slotInitRedoMenu()
1100 initUndoMenu(mActionRedo->menu(), Undo::REDO);
1103 /******************************************************************************
1104 * Populate the undo or redo menu.
1106 void MainWindow::initUndoMenu(QMenu* menu, Undo::Type type)
1108 menu->clear();
1109 mUndoMenuIds.clear();
1110 const QString& action = (type == Undo::UNDO) ? undoTextStripped : redoTextStripped;
1111 QList<int> ids = Undo::ids(type);
1112 for (int i = 0, end = ids.count(); i < end; ++i)
1114 int id = ids[i];
1115 QString actText = Undo::actionText(type, id);
1116 QString descrip = Undo::description(type, id);
1117 QString text = descrip.isEmpty()
1118 ? i18nc("@action Undo/Redo [action]", "%1 %2", action, actText)
1119 : i18nc("@action Undo [action]: message", "%1 %2: %3", action, actText, descrip);
1120 QAction* act = menu->addAction(text);
1121 mUndoMenuIds[act] = id;
1125 /******************************************************************************
1126 * Called when the status of the Undo or Redo list changes.
1127 * Change the Undo or Redo text to include the action which would be undone/redone.
1129 void MainWindow::slotUndoStatus(const QString& undo, const QString& redo)
1131 if (undo.isNull())
1133 mActionUndo->setEnabled(false);
1134 mActionUndo->setText(undoText);
1136 else
1138 mActionUndo->setEnabled(true);
1139 mActionUndo->setText(QStringLiteral("%1 %2").arg(undoText).arg(undo));
1141 if (redo.isNull())
1143 mActionRedo->setEnabled(false);
1144 mActionRedo->setText(redoText);
1146 else
1148 mActionRedo->setEnabled(true);
1149 mActionRedo->setText(QStringLiteral("%1 %2").arg(redoText).arg(redo));
1153 /******************************************************************************
1154 * Called when the Refresh Alarms menu item is selected.
1156 void MainWindow::slotRefreshAlarms()
1158 KAlarm::refreshAlarms();
1161 /******************************************************************************
1162 * Called when the "Configure KAlarm" menu item is selected.
1164 void MainWindow::slotPreferences()
1166 KAlarmPrefDlg::display();
1169 /******************************************************************************
1170 * Called when the Configure Keys menu item is selected.
1172 void MainWindow::slotConfigureKeys()
1174 KShortcutsDialog::configure(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this);
1177 /******************************************************************************
1178 * Called when the Configure Toolbars menu item is selected.
1180 void MainWindow::slotConfigureToolbar()
1182 KConfigGroup grp(KSharedConfig::openConfig()->group(WINDOW_NAME));
1183 saveMainWindowSettings(grp);
1184 KEditToolBar dlg(factory());
1185 connect(&dlg, &KEditToolBar::newToolBarConfig, this, &MainWindow::slotNewToolbarConfig);
1186 dlg.exec();
1189 /******************************************************************************
1190 * Called when OK or Apply is clicked in the Configure Toolbars dialog, to save
1191 * the new configuration.
1193 void MainWindow::slotNewToolbarConfig()
1195 createGUI(UI_FILE);
1196 applyMainWindowSettings(KSharedConfig::openConfig()->group(WINDOW_NAME));
1199 /******************************************************************************
1200 * Called when the Quit menu item is selected.
1201 * Note that this must be called by the event loop, not directly from the menu
1202 * item, since otherwise the window will be deleted while still processing the
1203 * menu, resulting in a crash.
1205 void MainWindow::slotQuit()
1207 theApp()->doQuit(this);
1210 /******************************************************************************
1211 * Called when the user or the session manager attempts to close the window.
1213 void MainWindow::closeEvent(QCloseEvent* ce)
1215 if (!theApp()->sessionClosingDown())
1217 // The user (not the session manager) wants to close the window.
1218 if (isTrayParent())
1220 // It's the parent window of the system tray icon, so just hide
1221 // it to prevent the system tray icon closing.
1222 hide();
1223 theApp()->quitIf();
1224 ce->ignore();
1225 return;
1228 ce->accept();
1231 /******************************************************************************
1232 * Called when the drag cursor enters a main or system tray window, to accept
1233 * or reject the dragged object.
1235 void MainWindow::executeDragEnterEvent(QDragEnterEvent* e)
1237 const QMimeData* data = e->mimeData();
1238 bool accept = ICalDrag::canDecode(data) ? !e->source() // don't accept "text/calendar" objects from this application
1239 : data->hasText()
1240 || data->hasUrls()
1241 || KPIM::MailList::canDecode(data);
1242 if (accept)
1243 e->acceptProposedAction();
1246 /******************************************************************************
1247 * Called when an object is dropped on the window.
1248 * If the object is recognised, the edit alarm dialog is opened appropriately.
1250 void MainWindow::dropEvent(QDropEvent* e)
1252 executeDropEvent(this, e);
1255 static QString getMailHeader(const char* header, KMime::Content& content)
1257 KMime::Headers::Base* hd = content.headerByType(header);
1258 return hd ? hd->asUnicodeString() : QString();
1261 /******************************************************************************
1262 * Called when an object is dropped on a main or system tray window, to
1263 * evaluate the action required and extract the text.
1265 void MainWindow::executeDropEvent(MainWindow* win, QDropEvent* e)
1267 qCDebug(KALARM_LOG) << "Formats:" << e->mimeData()->formats();
1268 const QMimeData* data = e->mimeData();
1269 KAEvent::SubAction action = KAEvent::MESSAGE;
1270 QByteArray bytes;
1271 AlarmText alarmText;
1272 KPIM::MailList mailList;
1273 QList<QUrl> files;
1274 MemoryCalendar::Ptr calendar(new MemoryCalendar(Preferences::timeZone(true)));
1275 #ifndef NDEBUG
1276 QString fmts = data->formats().join(QStringLiteral(", "));
1277 qCDebug(KALARM_LOG) << fmts;
1278 #endif
1280 /* The order of the tests below matters, since some dropped objects
1281 * provide more than one mime type.
1282 * Don't change them without careful thought !!
1284 if (!(bytes = data->data(QStringLiteral("message/rfc822"))).isEmpty())
1286 // Email message(s). Ignore all but the first.
1287 qCDebug(KALARM_LOG) << "email";
1288 KMime::Content content;
1289 content.setContent(bytes);
1290 content.parse();
1291 QString body;
1292 if (content.textContent())
1293 body = content.textContent()->decodedText(true, true); // strip trailing newlines & spaces
1294 unsigned long sernum = 0;
1295 if (KPIM::MailList::canDecode(data))
1297 // Get its KMail serial number to allow the KMail message
1298 // to be called up from the alarm message window.
1299 mailList = KPIM::MailList::fromMimeData(data);
1300 if (!mailList.isEmpty())
1301 sernum = mailList[0].serialNumber();
1303 alarmText.setEmail(getMailHeader("To", content),
1304 getMailHeader("From", content),
1305 getMailHeader("Cc", content),
1306 getMailHeader("Date", content),
1307 getMailHeader("Subject", content),
1308 body, sernum);
1310 else if (KPIM::MailList::canDecode(data))
1312 mailList = KPIM::MailList::fromMimeData(data);
1313 // KMail message(s). Ignore all but the first.
1314 qCDebug(KALARM_LOG) << "KMail_list";
1315 if (mailList.isEmpty())
1316 return;
1317 KPIM::MailSummary& summary = mailList[0];
1318 QDateTime dt;
1319 dt.setTime_t(summary.date());
1320 QString body = KAMail::getMailBody(summary.serialNumber());
1321 alarmText.setEmail(summary.to(), summary.from(), QString(),
1322 KLocale::global()->formatDateTime(dt), summary.subject(),
1323 body, summary.serialNumber());
1325 else if (ICalDrag::fromMimeData(data, calendar))
1327 // iCalendar - If events are included, use the first event
1328 qCDebug(KALARM_LOG) << "iCalendar";
1329 Event::List events = calendar->rawEvents();
1330 if (!events.isEmpty())
1332 KAEvent ev(events[0]);
1333 KAlarm::editNewAlarm(&ev, win);
1334 return;
1336 // If todos are included, use the first todo
1337 Todo::List todos = calendar->rawTodos();
1338 if (todos.isEmpty())
1339 return;
1340 Todo::Ptr todo = todos[0];
1341 alarmText.setTodo(todo);
1342 KDateTime start = todo->dtStart(true);
1343 if (!start.isValid() && todo->hasDueDate())
1344 start = todo->dtDue(true);
1345 KAEvent::Flags flags = KAEvent::DEFAULT_FONT;
1346 if (start.isDateOnly())
1347 flags |= KAEvent::ANY_TIME;
1348 KAEvent ev(start, alarmText.displayText(), Preferences::defaultBgColour(), Preferences::defaultFgColour(),
1349 QFont(), KAEvent::MESSAGE, 0, flags, true);
1350 if (todo->recurs())
1352 ev.setRecurrence(*todo->recurrence());
1353 ev.setNextOccurrence(KDateTime::currentUtcDateTime());
1355 ev.endChanges();
1356 KAlarm::editNewAlarm(&ev, win);
1357 return;
1359 else if (!(files = data->urls()).isEmpty())
1361 qCDebug(KALARM_LOG) << "URL";
1362 // Try to find the mime type of the file, without downloading a remote file
1363 QMimeDatabase mimeDb;
1364 const QString mimeTypeName = mimeDb.mimeTypeForUrl(files[0]).name();
1365 action = mimeTypeName.startsWith(QStringLiteral("audio/")) ? KAEvent::AUDIO : KAEvent::FILE;
1366 alarmText.setText(files[0].toDisplayString());
1368 else if (data->hasText())
1370 QString text = data->text();
1371 qCDebug(KALARM_LOG) << "text";
1372 alarmText.setText(text);
1374 else
1375 return;
1377 if (!alarmText.isEmpty())
1379 if (action == KAEvent::MESSAGE
1380 && (alarmText.isEmail() || alarmText.isScript()))
1382 // If the alarm text could be interpreted as an email or command script,
1383 // prompt for which type of alarm to create.
1384 QStringList types;
1385 types += i18nc("@item:inlistbox", "Display Alarm");
1386 if (alarmText.isEmail())
1387 types += i18nc("@item:inlistbox", "Email Alarm");
1388 else if (alarmText.isScript())
1389 types += i18nc("@item:inlistbox", "Command Alarm");
1390 bool ok = false;
1391 QString type = QInputDialog::getItem(mainMainWindow(), i18nc("@title:window", "Alarm Type"),
1392 i18nc("@info", "Choose alarm type to create:"), types, 0, false, &ok);
1393 if (!ok)
1394 return; // user didn't press OK
1395 int i = types.indexOf(type);
1396 if (i == 1)
1397 action = alarmText.isEmail() ? KAEvent::EMAIL : KAEvent::COMMAND;
1399 KAlarm::editNewAlarm(action, win, &alarmText);
1403 /******************************************************************************
1404 * Called when the status of a calendar has changed.
1405 * Enable or disable actions appropriately.
1407 void MainWindow::slotCalendarStatusChanged()
1409 // Find whether there are any writable calendars
1410 bool active = !CollectionControlModel::enabledCollections(CalEvent::ACTIVE, true).isEmpty();
1411 bool templat = !CollectionControlModel::enabledCollections(CalEvent::TEMPLATE, true).isEmpty();
1412 for (int i = 0, end = mWindowList.count(); i < end; ++i)
1414 MainWindow* w = mWindowList[i];
1415 w->mActionImportAlarms->setEnabled(active || templat);
1416 w->mActionImportBirthdays->setEnabled(active);
1417 w->mActionCreateTemplate->setEnabled(templat);
1418 // Note: w->mActionNew enabled status is set in the NewAlarmAction class.
1419 w->slotSelection();
1423 /******************************************************************************
1424 * Called when the selected items in the ListView change.
1425 * Enables the actions appropriately.
1427 void MainWindow::slotSelection()
1429 // Find which events have been selected
1430 QVector<KAEvent> events = mListView->selectedEvents();
1431 int count = events.count();
1432 if (!count)
1434 selectionCleared(); // disable actions
1435 Q_EMIT selectionChanged();
1436 return;
1439 // Find whether there are any writable resources
1440 bool active = mActionNew->isEnabled();
1442 bool readOnly = false;
1443 bool allArchived = true;
1444 bool enableReactivate = true;
1445 bool enableEnableDisable = true;
1446 bool enableEnable = false;
1447 bool enableDisable = false;
1448 AlarmCalendar* resources = AlarmCalendar::resources();
1449 KDateTime now = KDateTime::currentUtcDateTime();
1450 for (int i = 0; i < count; ++i)
1452 KAEvent* ev = resources->event(EventId(events[i])); // get up-to-date status
1453 KAEvent* event = ev ? ev : &events[i];
1454 bool expired = event->expired();
1455 if (!expired)
1456 allArchived = false;
1457 if (resources->eventReadOnly(event->itemId()))
1458 readOnly = true;
1459 if (enableReactivate
1460 && (!expired || !event->occursAfter(now, true)))
1461 enableReactivate = false;
1462 if (enableEnableDisable)
1464 if (expired)
1465 enableEnableDisable = enableEnable = enableDisable = false;
1466 else
1468 if (!enableEnable && !event->enabled())
1469 enableEnable = true;
1470 if (!enableDisable && event->enabled())
1471 enableDisable = true;
1476 qCDebug(KALARM_LOG) << "true";
1477 mActionCreateTemplate->setEnabled((count == 1) && !CollectionControlModel::enabledCollections(CalEvent::TEMPLATE, true).isEmpty());
1478 mActionExportAlarms->setEnabled(true);
1479 mActionExport->setEnabled(true);
1480 mActionCopy->setEnabled(active && count == 1);
1481 mActionModify->setEnabled(count == 1);
1482 mActionDelete->setEnabled(!readOnly && (active || allArchived));
1483 mActionReactivate->setEnabled(active && enableReactivate);
1484 mActionEnable->setEnabled(active && !readOnly && (enableEnable || enableDisable));
1485 if (enableEnable || enableDisable)
1486 setEnableText(enableEnable);
1488 Q_EMIT selectionChanged();
1491 /******************************************************************************
1492 * Called when a context menu is requested in the ListView.
1493 * Displays a context menu to modify or delete the selected item.
1495 void MainWindow::slotContextMenuRequested(const QPoint& globalPos)
1497 qCDebug(KALARM_LOG);
1498 if (mContextMenu)
1499 mContextMenu->popup(globalPos);
1502 /******************************************************************************
1503 * Disables actions when no item is selected.
1505 void MainWindow::selectionCleared()
1507 mActionCreateTemplate->setEnabled(false);
1508 mActionExportAlarms->setEnabled(false);
1509 mActionExport->setEnabled(false);
1510 mActionCopy->setEnabled(false);
1511 mActionModify->setEnabled(false);
1512 mActionDelete->setEnabled(false);
1513 mActionReactivate->setEnabled(false);
1514 mActionEnable->setEnabled(false);
1517 /******************************************************************************
1518 * Set the text of the Enable/Disable menu action.
1520 void MainWindow::setEnableText(bool enable)
1522 mActionEnableEnable = enable;
1523 mActionEnable->setText(enable ? i18nc("@action", "Ena&ble") : i18nc("@action", "Disa&ble"));
1526 /******************************************************************************
1527 * Display or hide the specified main window.
1528 * This should only be called when the application doesn't run in the system tray.
1530 MainWindow* MainWindow::toggleWindow(MainWindow* win)
1532 if (win && mWindowList.indexOf(win) != -1)
1534 // A window is specified (and it exists)
1535 if (win->isVisible())
1537 // The window is visible, so close it
1538 win->close();
1539 return Q_NULLPTR;
1541 else
1543 // The window is hidden, so display it
1544 win->hide(); // in case it's on a different desktop
1545 win->setWindowState(win->windowState() & ~Qt::WindowMinimized);
1546 win->raise();
1547 win->activateWindow();
1548 return win;
1552 // No window is specified, or the window doesn't exist. Open a new one.
1553 win = create();
1554 win->show();
1555 return win;
1558 /******************************************************************************
1559 * Called when the Edit button is clicked in an alarm message window.
1560 * This controls the alarm edit dialog created by the alarm window, and allows
1561 * it to remain unaffected by the alarm window closing.
1562 * See MessageWin::slotEdit() for more information.
1564 void MainWindow::editAlarm(EditAlarmDlg* dlg, const KAEvent& event)
1566 mEditAlarmMap[dlg] = event;
1567 connect(dlg, &KEditToolBar::accepted, this, &MainWindow::editAlarmOk);
1568 connect(dlg, &KEditToolBar::destroyed, this, &MainWindow::editAlarmDeleted);
1569 dlg->setAttribute(Qt::WA_DeleteOnClose, true); // ensure no memory leaks
1570 dlg->show();
1573 /******************************************************************************
1574 * Called when OK is clicked in the alarm edit dialog shown by editAlarm().
1575 * Updates the event which has been edited.
1577 void MainWindow::editAlarmOk()
1579 EditAlarmDlg* dlg = qobject_cast<EditAlarmDlg*>(sender());
1580 if (!dlg)
1581 return;
1582 QMap<EditAlarmDlg*, KAEvent>::Iterator it = mEditAlarmMap.find(dlg);
1583 if (it == mEditAlarmMap.end())
1584 return;
1585 KAEvent event = it.value();
1586 mEditAlarmMap.erase(it);
1587 if (!event.isValid())
1588 return;
1589 if (dlg->result() != QDialog::Accepted)
1590 return;
1591 Akonadi::Collection c = AkonadiModel::instance()->collection(event);
1592 KAlarm::updateEditedAlarm(dlg, event, c);
1595 /******************************************************************************
1596 * Called when the alarm edit dialog shown by editAlarm() is deleted.
1597 * Removes the dialog from the pending list.
1599 void MainWindow::editAlarmDeleted(QObject* obj)
1601 mEditAlarmMap.remove(static_cast<EditAlarmDlg*>(obj));
1604 // vim: et sw=4: