Factor out the shared parts of the agent action manager setup.
[kdepim.git] / kalarm / mainwindow.cpp
blob3befe6bf136de95a8d0c6e1e51895ad63ed0195b
1 /*
2 * mainwindow.cpp - main application window
3 * Program: kalarm
4 * Copyright © 2001-2011 by David Jarvie <djarvie@kde.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "kalarm.h"
22 #include "mainwindow.moc"
24 #include "alarmcalendar.h"
25 #include "alarmlistdelegate.h"
26 #include "autoqpointer.h"
27 #include "alarmlistview.h"
28 #ifndef USE_AKONADI
29 #include "alarmlistfiltermodel.h"
30 #include "alarmresources.h"
31 #include "eventlistmodel.h"
32 #endif
33 #include "birthdaydlg.h"
34 #include "functions.h"
35 #include "kalarmapp.h"
36 #include "kamail.h"
37 #include "messagebox.h"
38 #include "newalarmaction.h"
39 #include "prefdlg.h"
40 #include "preferences.h"
41 #include "resourceselector.h"
42 #include "synchtimer.h"
43 #include "templatedlg.h"
44 #include "templatemenuaction.h"
45 #include "templatepickdlg.h"
46 #include "traywindow.h"
47 #include "wakedlg.h"
49 #include <kalarmcal/alarmtext.h>
50 #include <kalarmcal/kaevent.h>
52 #include <libkdepim/maillistdrag.h>
53 #include <kmime/kmime_content.h>
54 #ifdef USE_AKONADI
55 #include <kcalcore/memorycalendar.h>
56 #include <kcalutils/icaldrag.h>
57 using namespace KCalCore;
58 using namespace KCalUtils;
59 #else
60 #include <kcal/calendarlocal.h>
61 #include <kcal/icaldrag.h>
62 using namespace KCal;
63 #endif
65 #include <kmenubar.h>
66 #include <ktoolbar.h>
67 #include <kmenu.h>
68 #include <kaction.h>
69 #include <kactioncollection.h>
70 #include <kinputdialog.h>
71 #include <ksystemtrayicon.h>
72 #include <kstandardaction.h>
73 #include <kiconloader.h>
74 #include <kurl.h>
75 #include <klocale.h>
76 #include <kglobalsettings.h>
77 #include <kconfiggroup.h>
78 #include <kshortcutsdialog.h>
79 #include <kedittoolbar.h>
80 #include <kxmlguifactory.h>
81 #include <kaboutdata.h>
82 #include <kdebug.h>
83 #include <ktoggleaction.h>
84 #include <ktoolbarpopupaction.h>
85 #include <kicon.h>
87 #include <QHeaderView>
88 #include <QSplitter>
89 #include <QByteArray>
90 #include <QDesktopWidget>
91 #include <QDragEnterEvent>
92 #include <QDropEvent>
93 #include <QResizeEvent>
94 #include <QCloseEvent>
95 #include <QTimer>
97 using namespace KAlarmCal;
99 static const char* UI_FILE = "kalarmui.rc";
100 static const char* WINDOW_NAME = "MainWindow";
102 static const char* VIEW_GROUP = "View";
103 static const char* SHOW_TIME_KEY = "ShowAlarmTime";
104 static const char* SHOW_TIME_TO_KEY = "ShowTimeToAlarm";
105 static const char* SHOW_ARCHIVED_KEY = "ShowArchivedAlarms";
106 static const char* SHOW_RESOURCES_KEY = "ShowResources";
108 static QString undoText;
109 static QString undoTextStripped;
110 static KShortcut undoShortcut;
111 static QString redoText;
112 static QString redoTextStripped;
113 static KShortcut redoShortcut;
116 /*=============================================================================
117 = Class: MainWindow
118 =============================================================================*/
120 MainWindow::WindowList MainWindow::mWindowList;
121 TemplateDlg* MainWindow::mTemplateDlg = 0;
123 // Collect these widget labels together to ensure consistent wording and
124 // translations across different modules.
125 QString MainWindow::i18n_a_ShowAlarmTimes() { return i18nc("@action", "Show &Alarm Times"); }
126 QString MainWindow::i18n_chk_ShowAlarmTime() { return i18nc("@option:check", "Show alarm time"); }
127 QString MainWindow::i18n_o_ShowTimeToAlarms() { return i18nc("@action", "Show Time t&o Alarms"); }
128 QString MainWindow::i18n_chk_ShowTimeToAlarm() { return i18nc("@option:check", "Show time until alarm"); }
131 /******************************************************************************
132 * Construct an instance.
133 * To avoid resize() events occurring while still opening the calendar (and
134 * resultant crashes), the calendar is opened before constructing the instance.
136 MainWindow* MainWindow::create(bool restored)
138 theApp()->checkCalendar(); // ensure calendar is open
139 return new MainWindow(restored);
142 MainWindow::MainWindow(bool restored)
143 : MainWindowBase(0, Qt::WindowContextHelpButtonHint),
144 mResourcesWidth(-1),
145 mHiddenTrayParent(false),
146 mShown(false),
147 mResizing(false)
149 kDebug();
150 setAttribute(Qt::WA_DeleteOnClose);
151 setWindowModality(Qt::WindowModal);
152 setObjectName("MainWin"); // used by LikeBack
153 setPlainCaption(KGlobal::mainComponent().aboutData()->programName());
154 KConfigGroup config(KGlobal::config(), VIEW_GROUP);
155 mShowResources = config.readEntry(SHOW_RESOURCES_KEY, false);
156 mShowArchived = config.readEntry(SHOW_ARCHIVED_KEY, false);
157 mShowTime = config.readEntry(SHOW_TIME_KEY, true);
158 mShowTimeTo = config.readEntry(SHOW_TIME_TO_KEY, false);
159 if (!restored)
161 KConfigGroup wconfig(KGlobal::config(), WINDOW_NAME);
162 mResourcesWidth = wconfig.readEntry(QString::fromLatin1("Splitter %1").arg(KApplication::desktop()->width()), (int)0);
165 setAcceptDrops(true); // allow drag-and-drop onto this window
166 if (!mShowTimeTo)
167 mShowTime = true; // ensure at least one time column is visible
169 mSplitter = new QSplitter(Qt::Horizontal, this);
170 mSplitter->setChildrenCollapsible(false);
171 mSplitter->installEventFilter(this);
172 setCentralWidget(mSplitter);
174 // Create the calendar resource selector widget
175 #ifdef USE_AKONADI
176 mResourceSelector = new ResourceSelector(mSplitter);
177 #else
178 AlarmResources* resources = AlarmResources::instance();
179 mResourceSelector = new ResourceSelector(resources, mSplitter);
180 #endif
181 mSplitter->setStretchFactor(0, 0); // don't resize resource selector when window is resized
182 mSplitter->setStretchFactor(1, 1);
183 #ifndef USE_AKONADI
184 connect(resources, SIGNAL(signalErrorMessage(QString)), SLOT(showErrorMessage(QString)));
185 #endif
187 // Create the alarm list widget
188 #ifdef USE_AKONADI
189 mListFilterModel = new AlarmListModel(this);
190 mListFilterModel->setEventTypeFilter(mShowArchived ? CalEvent::ACTIVE | CalEvent::ARCHIVED : CalEvent::ACTIVE);
191 #else
192 mListFilterModel = new AlarmListFilterModel(EventListModel::alarms(), this);
193 mListFilterModel->setStatusFilter(mShowArchived ? CalEvent::ACTIVE | CalEvent::ARCHIVED : CalEvent::ACTIVE);
194 #endif
195 mListView = new AlarmListView(WINDOW_NAME, mSplitter);
196 mListView->setModel(mListFilterModel);
197 mListView->selectTimeColumns(mShowTime, mShowTimeTo);
198 #ifdef USE_AKONADI
199 mListView->sortByColumn(mShowTime ? AlarmListModel::TimeColumn : AlarmListModel::TimeToColumn, Qt::AscendingOrder);
200 #else
201 mListView->sortByColumn(mShowTime ? EventListModel::TimeColumn : EventListModel::TimeToColumn, Qt::AscendingOrder);
202 #endif
203 mListView->setItemDelegate(new AlarmListDelegate(mListView));
204 connect(mListView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(slotSelection()));
205 connect(mListView, SIGNAL(contextMenuRequested(QPoint)), SLOT(slotContextMenuRequested(QPoint)));
206 #ifdef USE_AKONADI
207 connect(AkonadiModel::instance(), SIGNAL(collectionStatusChanged(Akonadi::Collection,AkonadiModel::Change,QVariant,bool)),
208 SLOT(slotCalendarStatusChanged()));
209 #else
210 connect(resources, SIGNAL(resourceStatusChanged(AlarmResource*,AlarmResources::Change)),
211 SLOT(slotCalendarStatusChanged()));
212 #endif
213 connect(mResourceSelector, SIGNAL(resized(QSize,QSize)), SLOT(resourcesResized()));
214 mListView->installEventFilter(this);
215 initActions();
217 setAutoSaveSettings(QLatin1String(WINDOW_NAME), true); // save toolbars, window sizes etc.
218 mWindowList.append(this);
219 if (mWindowList.count() == 1)
221 // It's the first main window
222 if (theApp()->wantShowInSystemTray())
223 theApp()->displayTrayIcon(true, this); // create system tray icon for run-in-system-tray mode
224 else if (theApp()->trayWindow())
225 theApp()->trayWindow()->setAssocMainWindow(this); // associate this window with the system tray icon
227 slotCalendarStatusChanged(); // initialise action states now that window is registered
230 MainWindow::~MainWindow()
232 kDebug();
233 bool trayParent = isTrayParent(); // must call before removing from window list
234 mWindowList.removeAt(mWindowList.indexOf(this));
236 // Prevent view updates during window destruction
237 delete mResourceSelector;
238 mResourceSelector = 0;
239 delete mListView;
240 mListView = 0;
242 if (theApp()->trayWindow())
244 if (trayParent)
245 delete theApp()->trayWindow();
246 else
247 theApp()->trayWindow()->removeWindow(this);
249 KGlobal::config()->sync(); // save any new window size to disc
250 theApp()->quitIf();
253 /******************************************************************************
254 * Save settings to the session managed config file, for restoration
255 * when the program is restored.
257 void MainWindow::saveProperties(KConfigGroup& config)
259 config.writeEntry("HiddenTrayParent", isTrayParent() && isHidden());
260 config.writeEntry("ShowArchived", mShowArchived);
261 config.writeEntry("ShowTime", mShowTime);
262 config.writeEntry("ShowTimeTo", mShowTimeTo);
263 config.writeEntry("ResourcesWidth", mResourceSelector->isHidden() ? 0 : mResourceSelector->width());
266 /******************************************************************************
267 * Read settings from the session managed config file.
268 * This function is automatically called whenever the app is being
269 * restored. Read in whatever was saved in saveProperties().
271 void MainWindow::readProperties(const KConfigGroup& config)
273 mHiddenTrayParent = config.readEntry("HiddenTrayParent", true);
274 mShowArchived = config.readEntry("ShowArchived", false);
275 mShowTime = config.readEntry("ShowTime", true);
276 mShowTimeTo = config.readEntry("ShowTimeTo", false);
277 mResourcesWidth = config.readEntry("ResourcesWidth", (int)0);
278 mShowResources = (mResourcesWidth > 0);
281 /******************************************************************************
282 * Get the main main window, i.e. the parent of the system tray icon, or if
283 * none, the first main window to be created. Visible windows take precedence
284 * over hidden ones.
286 MainWindow* MainWindow::mainMainWindow()
288 MainWindow* tray = theApp()->trayWindow() ? theApp()->trayWindow()->assocMainWindow() : 0;
289 if (tray && tray->isVisible())
290 return tray;
291 for (int i = 0, end = mWindowList.count(); i < end; ++i)
292 if (mWindowList[i]->isVisible())
293 return mWindowList[i];
294 if (tray)
295 return tray;
296 if (mWindowList.isEmpty())
297 return 0;
298 return mWindowList[0];
301 /******************************************************************************
302 * Check whether this main window is effectively the parent of the system tray icon.
304 bool MainWindow::isTrayParent() const
306 TrayWindow* tray = theApp()->trayWindow();
307 if (!tray || !KSystemTrayIcon::isSystemTrayAvailable())
308 return false;
309 if (tray->assocMainWindow() == this)
310 return true;
311 return mWindowList.count() == 1;
314 /******************************************************************************
315 * Close all main windows.
317 void MainWindow::closeAll()
319 while (!mWindowList.isEmpty())
320 delete mWindowList[0]; // N.B. the destructor removes the window from the list
323 /******************************************************************************
324 * Intercept events for the splitter widget.
326 bool MainWindow::eventFilter(QObject* obj, QEvent* e)
328 if (obj == mSplitter)
330 switch (e->type())
332 case QEvent::Resize:
333 // Don't change resources size while WINDOW is being resized.
334 // Resize event always occurs before Paint.
335 mResizing = true;
336 break;
337 case QEvent::Paint:
338 // Allow resources to be resized again
339 mResizing = false;
340 break;
341 default:
342 break;
345 else if (obj == mListView)
347 switch (e->type())
349 case QEvent::KeyPress:
351 QKeyEvent* ke = static_cast<QKeyEvent*>(e);
352 if (ke->key() == Qt::Key_Delete && ke->modifiers() == Qt::ShiftModifier)
354 // Prevent Shift-Delete being processed by EventListDelegate
355 mActionDeleteForce->trigger();
356 return true;
358 break;
360 default:
361 break;
364 return false;
367 /******************************************************************************
368 * Called when the window's size has changed (before it is painted).
369 * Sets the last column in the list view to extend at least to the right hand
370 * edge of the list view.
371 * Records the new size in the config file.
373 void MainWindow::resizeEvent(QResizeEvent* re)
375 // Save the window's new size only if it's the first main window
376 MainWindowBase::resizeEvent(re);
377 if (mResourcesWidth > 0)
379 QList<int> widths;
380 widths.append(mResourcesWidth);
381 widths.append(width() - mResourcesWidth - mSplitter->handleWidth());
382 mSplitter->setSizes(widths);
386 void MainWindow::resourcesResized()
388 if (!mShown || mResizing)
389 return;
390 QList<int> widths = mSplitter->sizes();
391 if (widths.count() > 1)
393 mResourcesWidth = widths[0];
394 // Width is reported as non-zero when resource selector is
395 // actually invisible, so note a zero width in these circumstances.
396 if (mResourcesWidth <= 5)
397 mResourcesWidth = 0;
398 else if (mainMainWindow() == this)
400 KConfigGroup config(KGlobal::config(), WINDOW_NAME);
401 config.writeEntry(QString::fromLatin1("Splitter %1").arg(KApplication::desktop()->width()), mResourcesWidth);
406 /******************************************************************************
407 * Called when the window is first displayed.
408 * Sets the last column in the list view to extend at least to the right hand
409 * edge of the list view.
411 void MainWindow::showEvent(QShowEvent* se)
413 if (mResourcesWidth > 0)
415 QList<int> widths;
416 widths.append(mResourcesWidth);
417 widths.append(width() - mResourcesWidth - mSplitter->handleWidth());
418 mSplitter->setSizes(widths);
420 MainWindowBase::showEvent(se);
421 mShown = true;
424 /******************************************************************************
425 * Display the window.
427 void MainWindow::show()
429 MainWindowBase::show();
430 if (mMenuError)
432 // Show error message now that the main window has been displayed.
433 // Waiting until now lets the user easily associate the message with
434 // the main window which is faulty.
435 KAMessageBox::error(this, i18nc("@info", "Failure to create menus (perhaps <filename>%1</filename> missing or corrupted)", QLatin1String(UI_FILE)));
436 mMenuError = false;
440 /******************************************************************************
441 * Called after the window is hidden.
443 void MainWindow::hideEvent(QHideEvent* he)
445 MainWindowBase::hideEvent(he);
448 /******************************************************************************
449 * Initialise the menu, toolbar and main window actions.
451 void MainWindow::initActions()
453 KShortcut dummy;
454 KActionCollection* actions = actionCollection();
456 mActionTemplates = new KAction(i18nc("@action", "&Templates..."), this);
457 actions->addAction(QLatin1String("templates"), mActionTemplates);
458 connect(mActionTemplates, SIGNAL(triggered(bool)), SLOT(slotTemplates()));
460 mActionNew = new NewAlarmAction(false, i18nc("@action", "&New"), this);
461 actions->addAction(QLatin1String("new"), mActionNew);
463 KAction* action = mActionNew->displayAlarmAction();
464 actions->addAction(QLatin1String("newDisplay"), action);
465 action->setGlobalShortcut(dummy); // actions->addAction() must be called first!
466 connect(action, SIGNAL(triggered(bool)), SLOT(slotNewDisplay()));
468 action = mActionNew->commandAlarmAction();
469 actions->addAction(QLatin1String("newCommand"), action);
470 action->setGlobalShortcut(dummy); // actions->addAction() must be called first!
471 connect(action, SIGNAL(triggered(bool)), SLOT(slotNewCommand()));
473 action = mActionNew->emailAlarmAction();
474 actions->addAction(QLatin1String("newEmail"), action);
475 action->setGlobalShortcut(dummy); // actions->addAction() must be called first!
476 connect(action, SIGNAL(triggered(bool)), SLOT(slotNewEmail()));
478 action = mActionNew->audioAlarmAction();
479 actions->addAction(QLatin1String("newAudio"), action);
480 action->setGlobalShortcut(dummy); // actions->addAction() must be called first!
481 connect(action, SIGNAL(triggered(bool)), SLOT(slotNewAudio()));
483 action = mActionNew->fromTemplateAlarmAction();
484 actions->addAction(QLatin1String("newFromTemplate"), action);
485 connect(action, SIGNAL(selected(const KAEvent*)), SLOT(slotNewFromTemplate(const KAEvent*)));
487 mActionCreateTemplate = new KAction(i18nc("@action", "Create Tem&plate..."), this);
488 actions->addAction(QLatin1String("createTemplate"), mActionCreateTemplate);
489 connect(mActionCreateTemplate, SIGNAL(triggered(bool)), SLOT(slotNewTemplate()));
491 mActionCopy = new KAction(KIcon("edit-copy"), i18nc("@action", "&Copy..."), this);
492 actions->addAction(QLatin1String("copy"), mActionCopy);
493 mActionCopy->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Insert));
494 connect(mActionCopy, SIGNAL(triggered(bool)), SLOT(slotCopy()));
496 mActionModify = new KAction(KIcon("document-properties"), i18nc("@action", "&Edit..."), this);
497 actions->addAction(QLatin1String("modify"), mActionModify);
498 mActionModify->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E));
499 connect(mActionModify, SIGNAL(triggered(bool)), SLOT(slotModify()));
501 mActionDelete = new KAction(KIcon("edit-delete"), i18nc("@action", "&Delete"), this);
502 actions->addAction(QLatin1String("delete"), mActionDelete);
503 mActionDelete->setShortcut(QKeySequence::Delete);
504 connect(mActionDelete, SIGNAL(triggered(bool)), SLOT(slotDeleteIf()));
506 // Set up Shift-Delete as a shortcut to delete without confirmation
507 mActionDeleteForce = new KAction(i18nc("@action", "Delete Without Confirmation"), this);
508 actions->addAction(QLatin1String("delete-force"), mActionDeleteForce);
509 mActionDeleteForce->setShortcut(QKeySequence::Delete + Qt::SHIFT);
510 connect(mActionDeleteForce, SIGNAL(triggered(bool)), SLOT(slotDeleteForce()));
512 mActionReactivate = new KAction(i18nc("@action", "Reac&tivate"), this);
513 actions->addAction(QLatin1String("undelete"), mActionReactivate);
514 mActionReactivate->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
515 connect(mActionReactivate, SIGNAL(triggered(bool)), SLOT(slotReactivate()));
517 mActionEnable = new KAction(this);
518 actions->addAction(QLatin1String("disable"), mActionEnable);
519 mActionEnable->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_B));
520 connect(mActionEnable, SIGNAL(triggered(bool)), SLOT(slotEnable()));
522 action = new KAction(i18nc("@action", "Wake From Suspend"), this);
523 actions->addAction(QLatin1String("wakeSuspend"), action);
524 connect(action, SIGNAL(triggered(bool)), SLOT(slotWakeFromSuspend()));
526 action = KAlarm::createStopPlayAction(this);
527 actions->addAction(QLatin1String("stopAudio"), action);
528 action->setGlobalShortcut(dummy); // actions->addAction() must be called first!
530 mActionShowTime = new KToggleAction(i18n_a_ShowAlarmTimes(), this);
531 actions->addAction(QLatin1String("showAlarmTimes"), mActionShowTime);
532 connect(mActionShowTime, SIGNAL(triggered(bool)), SLOT(slotShowTime()));
534 mActionShowTimeTo = new KToggleAction(i18n_o_ShowTimeToAlarms(), this);
535 actions->addAction(QLatin1String("showTimeToAlarms"), mActionShowTimeTo);
536 mActionShowTimeTo->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_I));
537 connect(mActionShowTimeTo, SIGNAL(triggered(bool)), SLOT(slotShowTimeTo()));
539 mActionShowArchived = new KToggleAction(i18nc("@action", "Show Archi&ved Alarms"), this);
540 actions->addAction(QLatin1String("showArchivedAlarms"), mActionShowArchived);
541 mActionShowArchived->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_P));
542 connect(mActionShowArchived, SIGNAL(triggered(bool)), SLOT(slotShowArchived()));
544 mActionToggleTrayIcon = new KToggleAction(i18nc("@action", "Show in System &Tray"), this);
545 actions->addAction(QLatin1String("showInSystemTray"), mActionToggleTrayIcon);
546 connect(mActionToggleTrayIcon, SIGNAL(triggered(bool)), SLOT(slotToggleTrayIcon()));
548 mActionToggleResourceSel = new KToggleAction(KIcon("view-choose"), i18nc("@action", "Show &Calendars"), this);
549 actions->addAction(QLatin1String("showResources"), mActionToggleResourceSel);
550 connect(mActionToggleResourceSel, SIGNAL(triggered(bool)), SLOT(slotToggleResourceSelector()));
552 mActionSpreadWindows = KAlarm::createSpreadWindowsAction(this);
553 actions->addAction(QLatin1String("spread"), mActionSpreadWindows);
554 mActionSpreadWindows->setGlobalShortcut(dummy); // actions->addAction() must be called first!
556 mActionImportAlarms = new KAction(i18nc("@action", "Import &Alarms..."), this);
557 actions->addAction(QLatin1String("importAlarms"), mActionImportAlarms);
558 connect(mActionImportAlarms, SIGNAL(triggered(bool)), SLOT(slotImportAlarms()));
560 mActionImportBirthdays = new KAction(i18nc("@action", "Import &Birthdays..."), this);
561 actions->addAction(QLatin1String("importBirthdays"), mActionImportBirthdays);
562 connect(mActionImportBirthdays, SIGNAL(triggered(bool)), SLOT(slotBirthdays()));
564 mActionExportAlarms = new KAction(i18nc("@action", "E&xport Selected Alarms..."), this);
565 actions->addAction(QLatin1String("exportAlarms"), mActionExportAlarms);
566 connect(mActionExportAlarms, SIGNAL(triggered(bool)), SLOT(slotExportAlarms()));
568 mActionExport = new KAction(i18nc("@action", "E&xport..."), this);
569 actions->addAction(QLatin1String("export"), mActionExport);
570 connect(mActionExport, SIGNAL(triggered(bool)), SLOT(slotExportAlarms()));
572 action = new KAction(KIcon("view-refresh"), i18nc("@action", "&Refresh Alarms"), this);
573 actions->addAction(QLatin1String("refreshAlarms"), action);
574 connect(action, SIGNAL(triggered(bool)), SLOT(slotRefreshAlarms()));
576 action = KAlarm::createAlarmEnableAction(this);
577 actions->addAction(QLatin1String("alarmsEnable"), action);
578 if (undoText.isNull())
580 // Get standard texts, etc., for Undo and Redo actions
581 QAction * act = KStandardAction::undo(this, 0, actions);
582 undoShortcut = KShortcut(act->shortcuts());
583 undoText = act->text();
584 undoTextStripped = KGlobal::locale()->removeAcceleratorMarker(undoText);
585 delete act;
586 act = KStandardAction::redo(this, 0, actions);
587 redoShortcut = KShortcut(act->shortcuts());
588 redoText = act->text();
589 redoTextStripped = KGlobal::locale()->removeAcceleratorMarker(redoText);
590 delete act;
592 mActionUndo = new KToolBarPopupAction(KIcon("edit-undo"), undoText, this);
593 actions->addAction(QLatin1String("edit_undo"), mActionUndo);
594 mActionUndo->setShortcut(undoShortcut);
595 connect(mActionUndo, SIGNAL(triggered(bool)), SLOT(slotUndo()));
597 mActionRedo = new KToolBarPopupAction(KIcon("edit-redo"), redoText, this);
598 actions->addAction(QLatin1String("edit_redo"), mActionRedo);
599 mActionRedo->setShortcut(redoShortcut);
600 connect(mActionRedo, SIGNAL(triggered(bool)), SLOT(slotRedo()));
602 KStandardAction::find(mListView, SLOT(slotFind()), actions);
603 mActionFindNext = KStandardAction::findNext(mListView, SLOT(slotFindNext()), actions);
604 mActionFindPrev = KStandardAction::findPrev(mListView, SLOT(slotFindPrev()), actions);
605 KStandardAction::selectAll(mListView, SLOT(selectAll()), actions);
606 KStandardAction::deselect(mListView, SLOT(clearSelection()), actions);
607 // Quit only once the event loop is called; otherwise, the parent window will
608 // be deleted while still processing the action, resulting in a crash.
609 KAction* act = KStandardAction::quit(0, 0, actions);
610 connect(act, SIGNAL(triggered(bool)), SLOT(slotQuit()), Qt::QueuedConnection);
611 KStandardAction::keyBindings(this, SLOT(slotConfigureKeys()), actions);
612 KStandardAction::configureToolbars(this, SLOT(slotConfigureToolbar()), actions);
613 KStandardAction::preferences(this, SLOT(slotPreferences()), actions);
614 mResourceSelector->initActions(actions);
615 setStandardToolBarMenuEnabled(true);
616 createGUI(UI_FILE);
617 // Load menu and toolbar settings
618 applyMainWindowSettings(KGlobal::config()->group(WINDOW_NAME));
620 mContextMenu = static_cast<KMenu*>(factory()->container("listContext", this));
621 mActionsMenu = static_cast<KMenu*>(factory()->container("actions", this));
622 KMenu* resourceMenu = static_cast<KMenu*>(factory()->container("resourceContext", this));
623 mResourceSelector->setContextMenu(resourceMenu);
624 mMenuError = (!mContextMenu || !mActionsMenu || !resourceMenu);
625 connect(mActionUndo->menu(), SIGNAL(aboutToShow()), SLOT(slotInitUndoMenu()));
626 connect(mActionUndo->menu(), SIGNAL(triggered(QAction*)), SLOT(slotUndoItem(QAction*)));
627 connect(mActionRedo->menu(), SIGNAL(aboutToShow()), SLOT(slotInitRedoMenu()));
628 connect(mActionRedo->menu(), SIGNAL(triggered(QAction*)), SLOT(slotRedoItem(QAction*)));
629 connect(Undo::instance(), SIGNAL(changed(QString,QString)), SLOT(slotUndoStatus(QString,QString)));
630 connect(mListView, SIGNAL(findActive(bool)), SLOT(slotFindActive(bool)));
631 Preferences::connect(SIGNAL(archivedKeepDaysChanged(int)), this, SLOT(updateKeepArchived(int)));
632 Preferences::connect(SIGNAL(showInSystemTrayChanged(bool)), this, SLOT(updateTrayIconAction()));
633 connect(theApp(), SIGNAL(trayIconToggled()), SLOT(updateTrayIconAction()));
635 // Set menu item states
636 setEnableText(true);
637 mActionShowTime->setChecked(mShowTime);
638 mActionShowTimeTo->setChecked(mShowTimeTo);
639 mActionShowArchived->setChecked(mShowArchived);
640 if (!Preferences::archivedKeepDays())
641 mActionShowArchived->setEnabled(false);
642 mActionToggleResourceSel->setChecked(mShowResources);
643 slotToggleResourceSelector();
644 updateTrayIconAction(); // set the correct text for this action
645 mActionUndo->setEnabled(Undo::haveUndo());
646 mActionRedo->setEnabled(Undo::haveRedo());
647 mActionFindNext->setEnabled(false);
648 mActionFindPrev->setEnabled(false);
650 mActionCopy->setEnabled(false);
651 mActionModify->setEnabled(false);
652 mActionDelete->setEnabled(false);
653 mActionReactivate->setEnabled(false);
654 mActionEnable->setEnabled(false);
655 mActionCreateTemplate->setEnabled(false);
656 mActionExport->setEnabled(false);
658 Undo::emitChanged(); // set the Undo/Redo menu texts
659 // Daemon::monitoringAlarms();
662 /******************************************************************************
663 * Enable or disable the Templates menu item in every main window instance.
665 void MainWindow::enableTemplateMenuItem(bool enable)
667 for (int i = 0, end = mWindowList.count(); i < end; ++i)
668 mWindowList[i]->mActionTemplates->setEnabled(enable);
671 /******************************************************************************
672 * Refresh the alarm list in every main window instance.
674 void MainWindow::refresh()
676 kDebug();
677 #ifdef USE_AKONADI
678 AkonadiModel::instance()->reload();
679 #else
680 EventListModel::alarms()->reload();
681 #endif
684 /******************************************************************************
685 * Called when the keep archived alarm setting changes in the user preferences.
686 * Enable/disable Show archived alarms option.
688 void MainWindow::updateKeepArchived(int days)
690 kDebug() << (bool)days;
691 if (mShowArchived && !days)
692 slotShowArchived(); // toggle Show Archived option setting
693 mActionShowArchived->setEnabled(days);
696 /******************************************************************************
697 * Select an alarm in the displayed list.
699 #ifdef USE_AKONADI
700 void MainWindow::selectEvent(Akonadi::Item::Id eventId)
701 #else
702 void MainWindow::selectEvent(const QString& eventId)
703 #endif
705 mListView->clearSelection();
706 #ifdef USE_AKONADI
707 QModelIndex index = mListFilterModel->eventIndex(eventId);
708 if (index.isValid())
710 mListView->select(index);
711 mListView->scrollTo(index);
713 #else
714 mListView->select(eventId, true);
715 #endif
718 /******************************************************************************
719 * Return the single selected alarm in the displayed list.
721 #ifdef USE_AKONADI
722 KAEvent MainWindow::selectedEvent() const
723 #else
724 KAEvent* MainWindow::selectedEvent() const
725 #endif
727 return mListView->selectedEvent();
730 /******************************************************************************
731 * Deselect all alarms in the displayed list.
733 void MainWindow::clearSelection()
735 mListView->clearSelection();
738 /******************************************************************************
739 * Called when the New button is clicked to edit a new alarm to add to the list.
741 void MainWindow::slotNew(EditAlarmDlg::Type type)
743 KAlarm::editNewAlarm(type, mListView);
746 /******************************************************************************
747 * Called when a template is selected from the New From Template popup menu.
748 * Executes a New Alarm dialog, preset from the selected template.
750 void MainWindow::slotNewFromTemplate(const KAEvent* tmplate)
752 KAlarm::editNewAlarm(tmplate, mListView);
755 /******************************************************************************
756 * Called when the New Template button is clicked to create a new template
757 * based on the currently selected alarm.
759 void MainWindow::slotNewTemplate()
761 #ifdef USE_AKONADI
762 KAEvent event = mListView->selectedEvent();
763 if (event.isValid())
764 KAlarm::editNewTemplate(&event, this);
765 #else
766 KAEvent* event = mListView->selectedEvent();
767 if (event)
768 KAlarm::editNewTemplate(event, this);
769 #endif
772 /******************************************************************************
773 * Called when the Copy button is clicked to edit a copy of an existing alarm,
774 * to add to the list.
776 void MainWindow::slotCopy()
778 #ifdef USE_AKONADI
779 KAEvent event = mListView->selectedEvent();
780 if (event.isValid())
781 KAlarm::editNewAlarm(&event, this);
782 #else
783 KAEvent* event = mListView->selectedEvent();
784 if (event)
785 KAlarm::editNewAlarm(event, this);
786 #endif
789 /******************************************************************************
790 * Called when the Modify button is clicked to edit the currently highlighted
791 * alarm in the list.
793 void MainWindow::slotModify()
795 #ifdef USE_AKONADI
796 KAEvent event = mListView->selectedEvent();
797 if (event.isValid())
798 KAlarm::editAlarm(&event, this); // edit alarm (view-only mode if archived or read-only)
799 #else
800 KAEvent* event = mListView->selectedEvent();
801 if (event)
802 KAlarm::editAlarm(event, this); // edit alarm (view-only mode if archived or read-only)
803 #endif
806 /******************************************************************************
807 * Called when the Delete button is clicked to delete the currently highlighted
808 * alarms in the list.
810 void MainWindow::slotDelete(bool force)
812 #ifdef USE_AKONADI
813 QVector<KAEvent> events = mListView->selectedEvents();
814 #else
815 KAEvent::List events = mListView->selectedEvents();
816 // Save the IDs of the events to be deleted, in case any events are
817 // deleted by being triggered while the confirmation prompt is displayed
818 // (in which case their pointers will become invalid).
819 QStringList ids;
820 for (int i = 0, end = events.count(); i < end; ++i)
821 ids.append(events[i]->id());
822 #endif
824 if (!force && Preferences::confirmAlarmDeletion())
826 int n = events.count();
827 if (KAMessageBox::warningContinueCancel(this, i18ncp("@info", "Do you really want to delete the selected alarm?",
828 "Do you really want to delete the %1 selected alarms?", n),
829 i18ncp("@title:window", "Delete Alarm", "Delete Alarms", n),
830 KGuiItem(i18nc("@action:button", "&Delete"), "edit-delete"),
831 KStandardGuiItem::cancel(),
832 Preferences::CONFIRM_ALARM_DELETION)
833 != KMessageBox::Continue)
834 return;
837 // Remove any events which have just triggered, from the list to delete.
838 Undo::EventList undos;
839 AlarmCalendar* resources = AlarmCalendar::resources();
840 #ifdef USE_AKONADI
841 for (int i = 0; i < events.count(); )
843 Akonadi::Collection c = resources->collectionForEvent(events[i].itemId());
844 if (!c.isValid())
845 events.remove(i);
846 else
847 undos.append(events[i++], c);
849 #else
850 for (int i = 0, e = 0, end = ids.count(); i < end; ++i)
852 AlarmResource* r = resources->resourceForEvent(ids[i]);
853 if (!r)
854 events.remove(e);
855 else
856 undos.append(*events[e++], r);
858 #endif
860 if (events.isEmpty())
861 kDebug() << "No alarms left to delete";
862 else
864 // Delete the events from the calendar and displays
865 KAlarm::deleteEvents(events, true, this);
866 Undo::saveDeletes(undos);
870 /******************************************************************************
871 * Called when the Reactivate button is clicked to reinstate the currently
872 * highlighted archived alarms in the list.
874 void MainWindow::slotReactivate()
876 #ifdef USE_AKONADI
877 QVector<KAEvent> events = mListView->selectedEvents();
878 #else
879 KAEvent::List events = mListView->selectedEvents();
880 #endif
881 mListView->clearSelection();
883 // Add the alarms to the displayed lists and to the calendar file
884 Undo::EventList undos;
885 QStringList ineligibleIDs;
886 KAlarm::reactivateEvents(events, ineligibleIDs, 0, this);
888 // Create the undo list, excluding ineligible events
889 AlarmCalendar* resources = AlarmCalendar::resources();
890 for (int i = 0, end = events.count(); i < end; ++i)
892 #ifdef USE_AKONADI
893 if (!ineligibleIDs.contains(events[i].id()))
895 Akonadi::Collection c = resources->collectionForEvent(events[i].itemId());
896 undos.append(events[i], c);
898 #else
899 QString id = events[i]->id();
900 if (!ineligibleIDs.contains(id))
901 undos.append(*events[i], resources->resourceForEvent(id));
902 #endif
904 Undo::saveReactivates(undos);
907 /******************************************************************************
908 * Called when the Enable/Disable button is clicked to enable or disable the
909 * currently highlighted alarms in the list.
911 void MainWindow::slotEnable()
913 bool enable = mActionEnableEnable; // save since changed in response to KAlarm::enableEvent()
914 #ifdef USE_AKONADI
915 QVector<KAEvent> events = mListView->selectedEvents();
916 QVector<KAEvent> eventCopies;
917 #else
918 KAEvent::List events = mListView->selectedEvents();
919 KAEvent::List eventCopies;
920 #endif
921 for (int i = 0, end = events.count(); i < end; ++i)
922 eventCopies += events[i];
923 KAlarm::enableEvents(eventCopies, enable, this);
924 slotSelection(); // update Enable/Disable action text
927 /******************************************************************************
928 * Called when the Show Alarm Times menu item is selected or deselected.
930 void MainWindow::slotShowTime()
932 mShowTime = !mShowTime;
933 mActionShowTime->setChecked(mShowTime);
934 if (!mShowTime && !mShowTimeTo)
935 slotShowTimeTo(); // at least one time column must be displayed
936 else
938 mListView->selectTimeColumns(mShowTime, mShowTimeTo);
939 KConfigGroup config(KGlobal::config(), VIEW_GROUP);
940 config.writeEntry(SHOW_TIME_KEY, mShowTime);
941 config.writeEntry(SHOW_TIME_TO_KEY, mShowTimeTo);
945 /******************************************************************************
946 * Called when the Show Time To Alarms menu item is selected or deselected.
948 void MainWindow::slotShowTimeTo()
950 mShowTimeTo = !mShowTimeTo;
951 mActionShowTimeTo->setChecked(mShowTimeTo);
952 if (!mShowTimeTo && !mShowTime)
953 slotShowTime(); // at least one time column must be displayed
954 else
956 mListView->selectTimeColumns(mShowTime, mShowTimeTo);
957 KConfigGroup config(KGlobal::config(), VIEW_GROUP);
958 config.writeEntry(SHOW_TIME_KEY, mShowTime);
959 config.writeEntry(SHOW_TIME_TO_KEY, mShowTimeTo);
963 /******************************************************************************
964 * Called when the Show Archived Alarms menu item is selected or deselected.
966 void MainWindow::slotShowArchived()
968 mShowArchived = !mShowArchived;
969 mActionShowArchived->setChecked(mShowArchived);
970 mActionShowArchived->setToolTip(mShowArchived ? i18nc("@info:tooltip", "Hide Archived Alarms")
971 : i18nc("@info:tooltip", "Show Archived Alarms"));
972 #ifdef USE_AKONADI
973 mListFilterModel->setEventTypeFilter(mShowArchived ? CalEvent::ACTIVE | CalEvent::ARCHIVED : CalEvent::ACTIVE);
974 #else
975 mListFilterModel->setStatusFilter(mShowArchived ? CalEvent::ACTIVE | CalEvent::ARCHIVED : CalEvent::ACTIVE);
976 #endif
977 mListView->reset();
978 KConfigGroup config(KGlobal::config(), VIEW_GROUP);
979 config.writeEntry(SHOW_ARCHIVED_KEY, mShowArchived);
982 /******************************************************************************
983 * Called when the Spread Windows global shortcut is selected, to spread alarm
984 * windows so that they are all visible.
986 void MainWindow::slotSpreadWindowsShortcut()
988 mActionSpreadWindows->trigger();
991 /******************************************************************************
992 * Called when the Wake From Suspend menu option is selected.
994 void MainWindow::slotWakeFromSuspend()
996 (WakeFromSuspendDlg::create(this))->show();
999 /******************************************************************************
1000 * Called when the Import Alarms menu item is selected, to merge alarms from an
1001 * external calendar into the current calendars.
1003 void MainWindow::slotImportAlarms()
1005 AlarmCalendar::importAlarms(this);
1008 /******************************************************************************
1009 * Called when the Export Alarms menu item is selected, to export the selected
1010 * alarms to an external calendar.
1012 void MainWindow::slotExportAlarms()
1014 #ifdef USE_AKONADI
1015 QVector<KAEvent> events = mListView->selectedEvents();
1016 if (!events.isEmpty())
1018 KAEvent::List evts = KAEvent::ptrList(events);
1019 AlarmCalendar::exportAlarms(evts, this);
1021 #else
1022 KAEvent::List events = mListView->selectedEvents();
1023 if (!events.isEmpty())
1024 AlarmCalendar::exportAlarms(events, this);
1025 #endif
1028 /******************************************************************************
1029 * Called when the Import Birthdays menu item is selected, to display birthdays
1030 * from the address book for selection as alarms.
1032 void MainWindow::slotBirthdays()
1034 // Use AutoQPointer to guard against crash on application exit while
1035 // the dialogue is still open. It prevents double deletion (both on
1036 // deletion of MainWindow, and on return from this function).
1037 AutoQPointer<BirthdayDlg> dlg = new BirthdayDlg(this);
1038 if (dlg->exec() == QDialog::Accepted)
1040 QVector<KAEvent> events = dlg->events();
1041 if (!events.isEmpty())
1043 mListView->clearSelection();
1044 // Add alarm to the displayed lists and to the calendar file
1045 KAlarm::UpdateStatus status = KAlarm::addEvents(events, dlg, true, true);
1047 Undo::EventList undos;
1048 AlarmCalendar* resources = AlarmCalendar::resources();
1049 for (int i = 0, end = events.count(); i < end; ++i)
1050 #ifdef USE_AKONADI
1052 Akonadi::Collection c = resources->collectionForEvent(events[i].itemId());
1053 undos.append(events[i], c);
1055 #else
1056 undos.append(events[i], resources->resourceForEvent(events[i].id()));
1057 #endif
1058 Undo::saveAdds(undos, i18nc("@info", "Import birthdays"));
1060 if (status != KAlarm::UPDATE_FAILED)
1061 KAlarm::outputAlarmWarnings(dlg);
1066 /******************************************************************************
1067 * Called when the Templates menu item is selected, to display the alarm
1068 * template editing dialog.
1070 void MainWindow::slotTemplates()
1072 if (!mTemplateDlg)
1074 mTemplateDlg = TemplateDlg::create(this);
1075 enableTemplateMenuItem(false); // disable menu item in all windows
1076 connect(mTemplateDlg, SIGNAL(finished()), SLOT(slotTemplatesEnd()));
1077 mTemplateDlg->show();
1081 /******************************************************************************
1082 * Called when the alarm template editing dialog has exited.
1084 void MainWindow::slotTemplatesEnd()
1086 if (mTemplateDlg)
1088 mTemplateDlg->delayedDestruct(); // this deletes the dialog once it is safe to do so
1089 mTemplateDlg = 0;
1090 enableTemplateMenuItem(true); // re-enable menu item in all windows
1094 /******************************************************************************
1095 * Called when the Display System Tray Icon menu item is selected.
1097 void MainWindow::slotToggleTrayIcon()
1099 theApp()->displayTrayIcon(!theApp()->trayIconDisplayed(), this);
1102 /******************************************************************************
1103 * Called when the Show Resource Selector menu item is selected.
1105 void MainWindow::slotToggleResourceSelector()
1107 mShowResources = mActionToggleResourceSel->isChecked();
1108 if (mShowResources)
1110 if (mResourcesWidth <= 0)
1112 mResourcesWidth = mResourceSelector->sizeHint().width();
1113 mResourceSelector->resize(mResourcesWidth, mResourceSelector->height());
1114 QList<int> widths = mSplitter->sizes();
1115 if (widths.count() == 1)
1117 int listwidth = widths[0] - mSplitter->handleWidth() - mResourcesWidth;
1118 mListView->resize(listwidth, mListView->height());
1119 widths.append(listwidth);
1120 widths[0] = mResourcesWidth;
1122 mSplitter->setSizes(widths);
1124 mResourceSelector->show();
1126 else
1127 mResourceSelector->hide();
1129 KConfigGroup config(KGlobal::config(), VIEW_GROUP);
1130 config.writeEntry(SHOW_RESOURCES_KEY, mShowResources);
1133 /******************************************************************************
1134 * Called when an error occurs in the resource calendar, to display a message.
1136 void MainWindow::showErrorMessage(const QString& msg)
1138 KAMessageBox::error(this, msg);
1141 /******************************************************************************
1142 * Called when the system tray icon is created or destroyed.
1143 * Set the system tray icon menu text according to whether or not the system
1144 * tray icon is currently visible.
1146 void MainWindow::updateTrayIconAction()
1148 mActionToggleTrayIcon->setEnabled(KSystemTrayIcon::isSystemTrayAvailable());
1149 mActionToggleTrayIcon->setChecked(theApp()->trayIconDisplayed());
1152 /******************************************************************************
1153 * Called when the active status of Find changes.
1155 void MainWindow::slotFindActive(bool active)
1157 mActionFindNext->setEnabled(active);
1158 mActionFindPrev->setEnabled(active);
1161 /******************************************************************************
1162 * Called when the Undo action is selected.
1164 void MainWindow::slotUndo()
1166 Undo::undo(this, KGlobal::locale()->removeAcceleratorMarker(mActionUndo->text()));
1169 /******************************************************************************
1170 * Called when the Redo action is selected.
1172 void MainWindow::slotRedo()
1174 Undo::redo(this, KGlobal::locale()->removeAcceleratorMarker(mActionRedo->text()));
1177 /******************************************************************************
1178 * Called when an Undo item is selected.
1180 void MainWindow::slotUndoItem(QAction* action)
1182 int id = mUndoMenuIds[action];
1183 Undo::undo(id, this, Undo::actionText(Undo::UNDO, id));
1186 /******************************************************************************
1187 * Called when a Redo item is selected.
1189 void MainWindow::slotRedoItem(QAction* action)
1191 int id = mUndoMenuIds[action];
1192 Undo::redo(id, this, Undo::actionText(Undo::REDO, id));
1195 /******************************************************************************
1196 * Called when the Undo menu is about to show.
1197 * Populates the menu.
1199 void MainWindow::slotInitUndoMenu()
1201 initUndoMenu(mActionUndo->menu(), Undo::UNDO);
1204 /******************************************************************************
1205 * Called when the Redo menu is about to show.
1206 * Populates the menu.
1208 void MainWindow::slotInitRedoMenu()
1210 initUndoMenu(mActionRedo->menu(), Undo::REDO);
1213 /******************************************************************************
1214 * Populate the undo or redo menu.
1216 void MainWindow::initUndoMenu(QMenu* menu, Undo::Type type)
1218 menu->clear();
1219 mUndoMenuIds.clear();
1220 const QString& action = (type == Undo::UNDO) ? undoTextStripped : redoTextStripped;
1221 QList<int> ids = Undo::ids(type);
1222 for (int i = 0, end = ids.count(); i < end; ++i)
1224 int id = ids[i];
1225 QString actText = Undo::actionText(type, id);
1226 QString descrip = Undo::description(type, id);
1227 QString text = descrip.isEmpty()
1228 ? i18nc("@action Undo/Redo [action]", "%1 %2", action, actText)
1229 : i18nc("@action Undo [action]: message", "%1 %2: %3", action, actText, descrip);
1230 QAction* act = menu->addAction(text);
1231 mUndoMenuIds[act] = id;
1235 /******************************************************************************
1236 * Called when the status of the Undo or Redo list changes.
1237 * Change the Undo or Redo text to include the action which would be undone/redone.
1239 void MainWindow::slotUndoStatus(const QString& undo, const QString& redo)
1241 if (undo.isNull())
1243 mActionUndo->setEnabled(false);
1244 mActionUndo->setText(undoText);
1246 else
1248 mActionUndo->setEnabled(true);
1249 mActionUndo->setText(QString("%1 %2").arg(undoText).arg(undo));
1251 if (redo.isNull())
1253 mActionRedo->setEnabled(false);
1254 mActionRedo->setText(redoText);
1256 else
1258 mActionRedo->setEnabled(true);
1259 mActionRedo->setText(QString("%1 %2").arg(redoText).arg(redo));
1263 /******************************************************************************
1264 * Called when the Refresh Alarms menu item is selected.
1266 void MainWindow::slotRefreshAlarms()
1268 KAlarm::refreshAlarms();
1271 /******************************************************************************
1272 * Called when the "Configure KAlarm" menu item is selected.
1274 void MainWindow::slotPreferences()
1276 KAlarmPrefDlg::display();
1279 /******************************************************************************
1280 * Called when the Configure Keys menu item is selected.
1282 void MainWindow::slotConfigureKeys()
1284 KShortcutsDialog::configure(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this);
1287 /******************************************************************************
1288 * Called when the Configure Toolbars menu item is selected.
1290 void MainWindow::slotConfigureToolbar()
1292 saveMainWindowSettings(KGlobal::config()->group(WINDOW_NAME));
1293 KEditToolBar dlg(factory());
1294 connect(&dlg, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig()));
1295 dlg.exec();
1298 /******************************************************************************
1299 * Called when OK or Apply is clicked in the Configure Toolbars dialog, to save
1300 * the new configuration.
1302 void MainWindow::slotNewToolbarConfig()
1304 createGUI(UI_FILE);
1305 applyMainWindowSettings(KGlobal::config()->group(WINDOW_NAME));
1308 /******************************************************************************
1309 * Called when the Quit menu item is selected.
1310 * Note that this must be called by the event loop, not directly from the menu
1311 * item, since otherwise the window will be deleted while still processing the
1312 * menu, resulting in a crash.
1314 void MainWindow::slotQuit()
1316 theApp()->doQuit(this);
1319 /******************************************************************************
1320 * Called when the user or the session manager attempts to close the window.
1322 void MainWindow::closeEvent(QCloseEvent* ce)
1324 if (!theApp()->sessionClosingDown())
1326 // The user (not the session manager) wants to close the window.
1327 if (isTrayParent())
1329 // It's the parent window of the system tray icon, so just hide
1330 // it to prevent the system tray icon closing.
1331 hide();
1332 theApp()->quitIf();
1333 ce->ignore();
1334 return;
1337 ce->accept();
1340 /******************************************************************************
1341 * Called when the drag cursor enters a main or system tray window, to accept
1342 * or reject the dragged object.
1344 void MainWindow::executeDragEnterEvent(QDragEnterEvent* e)
1346 const QMimeData* data = e->mimeData();
1347 bool accept = ICalDrag::canDecode(data) ? !e->source() // don't accept "text/calendar" objects from this application
1348 : data->hasText()
1349 || KUrl::List::canDecode(data)
1350 || KPIM::MailList::canDecode(data);
1351 if (accept)
1352 e->acceptProposedAction();
1355 /******************************************************************************
1356 * Called when an object is dropped on the window.
1357 * If the object is recognised, the edit alarm dialog is opened appropriately.
1359 void MainWindow::dropEvent(QDropEvent* e)
1361 executeDropEvent(this, e);
1364 static QString getMailHeader(const char* header, KMime::Content& content)
1366 KMime::Headers::Base* hd = content.headerByType(header);
1367 return hd ? hd->asUnicodeString() : QString();
1370 /******************************************************************************
1371 * Called when an object is dropped on a main or system tray window, to
1372 * evaluate the action required and extract the text.
1374 void MainWindow::executeDropEvent(MainWindow* win, QDropEvent* e)
1376 kDebug() << "Formats:" << e->mimeData()->formats();
1377 const QMimeData* data = e->mimeData();
1378 KAEvent::SubAction action = KAEvent::MESSAGE;
1379 QByteArray bytes;
1380 AlarmText alarmText;
1381 KPIM::MailList mailList;
1382 KUrl::List files;
1383 #ifdef USE_AKONADI
1384 MemoryCalendar::Ptr calendar(new MemoryCalendar(Preferences::timeZone(true)));
1385 #else
1386 CalendarLocal calendar(Preferences::timeZone(true));
1387 #endif
1388 #ifndef NDEBUG
1389 QString fmts = data->formats().join(", ");
1390 kDebug() << fmts;
1391 #endif
1393 /* The order of the tests below matters, since some dropped objects
1394 * provide more than one mime type.
1395 * Don't change them without careful thought !!
1397 if (!(bytes = data->data("message/rfc822")).isEmpty())
1399 // Email message(s). Ignore all but the first.
1400 kDebug() << "email";
1401 KMime::Content content;
1402 content.setContent(bytes);
1403 content.parse();
1404 QString body;
1405 if (content.textContent())
1406 body = content.textContent()->decodedText(true, true); // strip trailing newlines & spaces
1407 unsigned long sernum = 0;
1408 if (KPIM::MailList::canDecode(data))
1410 // Get its KMail serial number to allow the KMail message
1411 // to be called up from the alarm message window.
1412 mailList = KPIM::MailList::fromMimeData(data);
1413 if (!mailList.isEmpty())
1414 sernum = mailList[0].serialNumber();
1416 alarmText.setEmail(getMailHeader("To", content),
1417 getMailHeader("From", content),
1418 getMailHeader("Cc", content),
1419 getMailHeader("Date", content),
1420 getMailHeader("Subject", content),
1421 body, sernum);
1423 #ifdef KMAIL_SUPPORTED
1424 else if (KPIM::MailList::canDecode(data))
1426 mailList = KPIM::MailList::fromMimeData(data);
1427 // KMail message(s). Ignore all but the first.
1428 kDebug() << "KMail_list";
1429 if (mailList.isEmpty())
1430 return;
1431 KPIM::MailSummary& summary = mailList[0];
1432 QDateTime dt;
1433 dt.setTime_t(summary.date());
1434 QString body = KAMail::getMailBody(summary.serialNumber());
1435 alarmText.setEmail(summary.to(), summary.from(), QString(),
1436 KGlobal::locale()->formatDateTime(dt), summary.subject(),
1437 body, summary.serialNumber());
1439 #endif
1440 #ifdef USE_AKONADI
1441 else if (ICalDrag::fromMimeData(data, calendar))
1442 #else
1443 else if (ICalDrag::fromMimeData(data, &calendar))
1444 #endif
1446 // iCalendar - If events are included, use the first event
1447 kDebug() << "iCalendar";
1448 #ifdef USE_AKONADI
1449 Event::List events = calendar->rawEvents();
1450 #else
1451 Event::List events = calendar.rawEvents();
1452 #endif
1453 if (!events.isEmpty())
1455 KAEvent ev(events[0]);
1456 KAlarm::editNewAlarm(&ev, win);
1457 return;
1459 // If todos are included, use the first todo
1460 #ifdef USE_AKONADI
1461 Todo::List todos = calendar->rawTodos();
1462 #else
1463 Todo::List todos = calendar.rawTodos();
1464 #endif
1465 if (todos.isEmpty())
1466 return;
1467 #ifdef USE_AKONADI
1468 Todo::Ptr todo = todos[0];
1469 #else
1470 Todo* todo = todos[0];
1471 #endif
1472 alarmText.setTodo(todo);
1473 KDateTime start = todo->dtStart(true);
1474 if (!start.isValid() && todo->hasDueDate())
1475 start = todo->dtDue(true);
1476 KAEvent::Flags flags = KAEvent::DEFAULT_FONT;
1477 if (start.isDateOnly())
1478 flags |= KAEvent::ANY_TIME;
1479 KAEvent ev(start, alarmText.displayText(), Preferences::defaultBgColour(), Preferences::defaultFgColour(),
1480 QFont(), KAEvent::MESSAGE, 0, flags, true);
1481 if (todo->recurs())
1483 ev.setRecurrence(*todo->recurrence());
1484 ev.setNextOccurrence(KDateTime::currentUtcDateTime());
1486 ev.endChanges();
1487 KAlarm::editNewAlarm(&ev, win);
1488 return;
1490 else if (!(files = KUrl::List::fromMimeData(data)).isEmpty())
1492 kDebug() << "URL";
1493 // Try to find the mime type of the file, without downloading a remote file
1494 KMimeType::Ptr mimeType = KMimeType::findByUrl(files[0]);
1495 action = mimeType->name().startsWith(QLatin1String("audio/")) ? KAEvent::AUDIO : KAEvent::FILE;
1496 alarmText.setText(files[0].prettyUrl());
1498 else if (data->hasText())
1500 QString text = data->text();
1501 kDebug() << "text";
1502 alarmText.setText(text);
1504 else
1505 return;
1507 if (!alarmText.isEmpty())
1509 if (action == KAEvent::MESSAGE
1510 && (alarmText.isEmail() || alarmText.isScript()))
1512 // If the alarm text could be interpreted as an email or command script,
1513 // prompt for which type of alarm to create.
1514 QStringList types;
1515 types += i18nc("@item:inlistbox", "Display Alarm");
1516 if (alarmText.isEmail())
1517 types += i18nc("@item:inlistbox", "Email Alarm");
1518 else if (alarmText.isScript())
1519 types += i18nc("@item:inlistbox", "Command Alarm");
1520 bool ok = false;
1521 QString type = KInputDialog::getItem(i18nc("@title:window", "Alarm Type"),
1522 i18nc("@info", "Choose alarm type to create:"), types, 0, false, &ok, mainMainWindow());
1523 if (!ok)
1524 return; // user didn't press OK
1525 int i = types.indexOf(type);
1526 if (i == 1)
1527 action = alarmText.isEmail() ? KAEvent::EMAIL : KAEvent::COMMAND;
1529 KAlarm::editNewAlarm(action, win, &alarmText);
1533 /******************************************************************************
1534 * Called when the status of a calendar has changed.
1535 * Enable or disable actions appropriately.
1537 void MainWindow::slotCalendarStatusChanged()
1539 // Find whether there are any writable calendars
1540 #ifdef USE_AKONADI
1541 bool active = !CollectionControlModel::enabledCollections(CalEvent::ACTIVE, true).isEmpty();
1542 bool templat = !CollectionControlModel::enabledCollections(CalEvent::TEMPLATE, true).isEmpty();
1543 #else
1544 AlarmResources* resources = AlarmResources::instance();
1545 bool active = resources->activeCount(CalEvent::ACTIVE, true);
1546 bool templat = resources->activeCount(CalEvent::TEMPLATE, true);
1547 #endif
1548 for (int i = 0, end = mWindowList.count(); i < end; ++i)
1550 MainWindow* w = mWindowList[i];
1551 w->mActionImportAlarms->setEnabled(active || templat);
1552 w->mActionImportBirthdays->setEnabled(active);
1553 w->mActionCreateTemplate->setEnabled(templat);
1554 // Note: w->mActionNew enabled status is set in the NewAlarmAction class.
1555 w->slotSelection();
1559 /******************************************************************************
1560 * Called when the selected items in the ListView change.
1561 * Enables the actions appropriately.
1563 void MainWindow::slotSelection()
1565 // Find which events have been selected
1566 #ifdef USE_AKONADI
1567 QVector<KAEvent> events = mListView->selectedEvents();
1568 #else
1569 KAEvent::List events = mListView->selectedEvents();
1570 #endif
1571 int count = events.count();
1572 if (!count)
1574 selectionCleared(); // disable actions
1575 emit selectionChanged();
1576 return;
1579 // Find whether there are any writable resources
1580 bool active = mActionNew->isEnabled();
1582 bool readOnly = false;
1583 bool allArchived = true;
1584 bool enableReactivate = true;
1585 bool enableEnableDisable = true;
1586 bool enableEnable = false;
1587 bool enableDisable = false;
1588 AlarmCalendar* resources = AlarmCalendar::resources();
1589 KDateTime now = KDateTime::currentUtcDateTime();
1590 for (int i = 0; i < count; ++i)
1592 #ifdef USE_AKONADI
1593 KAEvent* ev = resources->event(events[i].id()); // get up-to-date status
1594 KAEvent* event = ev ? ev : &events[i];
1595 #else
1596 KAEvent* event = events[i];
1597 #endif
1598 bool expired = event->expired();
1599 if (!expired)
1600 allArchived = false;
1601 #ifdef USE_AKONADI
1602 if (resources->eventReadOnly(event->itemId()))
1603 #else
1604 if (resources->eventReadOnly(event->id()))
1605 #endif
1606 readOnly = true;
1607 if (enableReactivate
1608 && (!expired || !event->occursAfter(now, true)))
1609 enableReactivate = false;
1610 if (enableEnableDisable)
1612 if (expired)
1613 enableEnableDisable = enableEnable = enableDisable = false;
1614 else
1616 if (!enableEnable && !event->enabled())
1617 enableEnable = true;
1618 if (!enableDisable && event->enabled())
1619 enableDisable = true;
1624 kDebug() << "true";
1625 #ifdef USE_AKONADI
1626 mActionCreateTemplate->setEnabled((count == 1) && !CollectionControlModel::enabledCollections(CalEvent::TEMPLATE, true).isEmpty());
1627 #else
1628 mActionCreateTemplate->setEnabled((count == 1) && (AlarmResources::instance()->activeCount(CalEvent::TEMPLATE, true) > 0));
1629 #endif
1630 mActionExportAlarms->setEnabled(true);
1631 mActionExport->setEnabled(true);
1632 mActionCopy->setEnabled(active && count == 1);
1633 mActionModify->setEnabled(count == 1);
1634 mActionDelete->setEnabled(!readOnly && (active || allArchived));
1635 mActionReactivate->setEnabled(active && enableReactivate);
1636 mActionEnable->setEnabled(active && !readOnly && (enableEnable || enableDisable));
1637 if (enableEnable || enableDisable)
1638 setEnableText(enableEnable);
1640 emit selectionChanged();
1643 /******************************************************************************
1644 * Called when a context menu is requested in the ListView.
1645 * Displays a context menu to modify or delete the selected item.
1647 void MainWindow::slotContextMenuRequested(const QPoint& globalPos)
1649 kDebug();
1650 if (mContextMenu)
1651 mContextMenu->popup(globalPos);
1654 /******************************************************************************
1655 * Disables actions when no item is selected.
1657 void MainWindow::selectionCleared()
1659 mActionCreateTemplate->setEnabled(false);
1660 mActionExportAlarms->setEnabled(false);
1661 mActionExport->setEnabled(false);
1662 mActionCopy->setEnabled(false);
1663 mActionModify->setEnabled(false);
1664 mActionDelete->setEnabled(false);
1665 mActionReactivate->setEnabled(false);
1666 mActionEnable->setEnabled(false);
1669 /******************************************************************************
1670 * Set the text of the Enable/Disable menu action.
1672 void MainWindow::setEnableText(bool enable)
1674 mActionEnableEnable = enable;
1675 mActionEnable->setText(enable ? i18nc("@action", "Ena&ble") : i18nc("@action", "Disa&ble"));
1678 /******************************************************************************
1679 * Display or hide the specified main window.
1680 * This should only be called when the application doesn't run in the system tray.
1682 MainWindow* MainWindow::toggleWindow(MainWindow* win)
1684 if (win && mWindowList.indexOf(win) != -1)
1686 // A window is specified (and it exists)
1687 if (win->isVisible())
1689 // The window is visible, so close it
1690 win->close();
1691 return 0;
1693 else
1695 // The window is hidden, so display it
1696 win->hide(); // in case it's on a different desktop
1697 win->setWindowState(win->windowState() & ~Qt::WindowMinimized);
1698 win->raise();
1699 win->activateWindow();
1700 return win;
1704 // No window is specified, or the window doesn't exist. Open a new one.
1705 win = create();
1706 win->show();
1707 return win;
1710 /******************************************************************************
1711 * Called when the Edit button is clicked in an alarm message window.
1712 * This controls the alarm edit dialog created by the alarm window, and allows
1713 * it to remain unaffected by the alarm window closing.
1714 * See MessageWin::slotEdit() for more information.
1716 #ifdef USE_AKONADI
1717 void MainWindow::editAlarm(EditAlarmDlg* dlg, const KAEvent& event)
1718 #else
1719 void MainWindow::editAlarm(EditAlarmDlg* dlg, const KAEvent& event, AlarmResource* resource)
1720 #endif
1722 #ifdef USE_AKONADI
1723 mEditAlarmMap[dlg] = event;
1724 #else
1725 KAEvent ev = event;
1726 ev.setResource(resource);
1727 mEditAlarmMap[dlg] = ev;
1728 #endif
1729 connect(dlg, SIGNAL(accepted()), SLOT(editAlarmOk()));
1730 connect(dlg, SIGNAL(destroyed(QObject*)), SLOT(editAlarmDeleted(QObject*)));
1731 dlg->setAttribute(Qt::WA_DeleteOnClose, true); // ensure no memory leaks
1732 dlg->show();
1735 /******************************************************************************
1736 * Called when OK is clicked in the alarm edit dialog shown by editAlarm().
1737 * Updates the event which has been edited.
1739 void MainWindow::editAlarmOk()
1741 EditAlarmDlg* dlg = qobject_cast<EditAlarmDlg*>(sender());
1742 if (!dlg)
1743 return;
1744 QMap<EditAlarmDlg*, KAEvent>::Iterator it = mEditAlarmMap.find(dlg);
1745 if (it == mEditAlarmMap.end())
1746 return;
1747 KAEvent event = it.value();
1748 mEditAlarmMap.erase(it);
1749 if (!event.isValid())
1750 return;
1751 if (dlg->result() != QDialog::Accepted)
1752 return;
1753 #ifdef USE_AKONADI
1754 Akonadi::Collection c = AkonadiModel::instance()->collection(event);
1755 KAlarm::updateEditedAlarm(dlg, event, c);
1756 #else
1757 KAlarm::updateEditedAlarm(dlg, event, event.resource());
1758 #endif
1761 /******************************************************************************
1762 * Called when the alarm edit dialog shown by editAlarm() is deleted.
1763 * Removes the dialog from the pending list.
1765 void MainWindow::editAlarmDeleted(QObject* obj)
1767 mEditAlarmMap.remove(static_cast<EditAlarmDlg*>(obj));
1770 // vim: et sw=4: