diff --git a/CMakeLists.txt b/CMakeLists.txt index 6027ad9d..1f787f32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,125 +1,125 @@ cmake_minimum_required(VERSION 3.5) set(KDEPIM_VERSION_NUMBER "5.11.2") set(PIM_VERSION ${KDEPIM_VERSION_NUMBER}) -project(kalarm VERSION "2.12.2") +project(kalarm VERSION "2.12.3") set(KF5_MIN_VERSION "5.56.0") find_package(ECM ${KF5_MIN_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${kalarm_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH}) include(ECMInstallIcons) include(ECMSetupVersion) include(ECMAddTests) include(GenerateExportHeader) include(ECMGenerateHeaders) include(FeatureSummary) include(CheckFunctionExists) include(ECMGeneratePriFile) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(ECMAddAppIcon) include(ECMQtDeclareLoggingCategory) # Do NOT add quote set(KDEPIM_DEV_VERSION ) # add an extra space if(DEFINED KDEPIM_DEV_VERSION) set(KDEPIM_DEV_VERSION " ${KDEPIM_DEV_VERSION}") endif() set(KDEPIM_VERSION "${KDEPIM_VERSION_NUMBER}${KDEPIM_DEV_VERSION}") set(KIMAP_LIB_VERSION "5.11.2") set(AKONADI_MIMELIB_VERSION "5.11.2") set(AKONADI_CONTACT_VERSION "5.11.2") set(KMAILTRANSPORT_LIB_VERSION "5.11.2") set(KPIMTEXTEDIT_LIB_VERSION "5.11.2") set(IDENTITYMANAGEMENT_LIB_VERSION "5.11.2") set(AKONADI_VERSION "5.11.2") set(KMIME_LIB_VERSION "5.11.2") set(AKONADIKALARM_LIB_VERSION "5.11.2") set(PIMCOMMON_LIB_VERSION_LIB "5.11.2") set(KDEPIM_LIB_VERSION "${KDEPIM_VERSION_NUMBER}") set(KDEPIM_LIB_SOVERSION "5") set(QT_REQUIRED_VERSION "5.10.0") find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED DBus Gui Network Widgets) find_package(Qt5X11Extras NO_MODULE) set(MAILCOMMON_LIB_VERSION_LIB "5.11.2") set(LIBKDEPIM_LIB_VERSION_LIB "5.11.2") set(KCALENDARCORE_LIB_VERSION "5.11.2") set(CALENDARUTILS_LIB_VERSION "5.11.2") set(KDEPIM_APPS_LIB_VERSION_LIB "5.11.2") # Find KF5 package find_package(KF5Auth ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Codecs ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Completion ${KF5_MIN_VERSION} REQUIRED) find_package(KF5Config ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5ConfigWidgets ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5DBusAddons ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5DocTools ${KF5_MIN_VERSION} REQUIRED) find_package(KF5GlobalAccel ${KF5_MIN_VERSION} REQUIRED) find_package(KF5GuiAddons ${KF5_MIN_VERSION} REQUIRED) find_package(KF5I18n ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5IconThemes ${KF5_MIN_VERSION} REQUIRED) find_package(KF5JobWidgets ${KF5_MIN_VERSION} REQUIRED) find_package(KF5KCMUtils ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5KDELibs4Support ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5KIO ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Notifications ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Service ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5WidgetsAddons ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5WindowSystem ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5XmlGui ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Holidays ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(Phonon4Qt5 CONFIG REQUIRED) # Find KdepimLibs Package find_package(KF5IMAP ${KIMAP_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Akonadi ${AKONADI_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiContact ${AKONADI_CONTACT_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiMime ${AKONADI_MIMELIB_VERSION} CONFIG REQUIRED) find_package(KF5AlarmCalendar ${AKONADIKALARM_LIB_VERSION} CONFIG REQUIRED) find_package(KF5CalendarCore ${KCALENDARCORE_LIB_VERSION} CONFIG REQUIRED) find_package(KF5CalendarUtils ${CALENDARUTILS_LIB_VERSION} CONFIG REQUIRED) find_package(KF5IdentityManagement ${IDENTITYMANAGEMENT_LIB_VERSION} CONFIG REQUIRED) find_package(KF5KdepimDBusInterfaces ${KDEPIM_APPS_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5Libkdepim ${LIBKDEPIM_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5MailCommon ${MAILCOMMON_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5MailTransportAkonadi ${KMAILTRANSPORT_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Mime ${KMIME_LIB_VERSION} CONFIG REQUIRED) find_package(KF5PimCommon ${PIMCOMMON_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5PimTextEdit ${KPIMTEXTEDIT_LIB_VERSION} CONFIG REQUIRED) if (NOT APPLE) find_package(X11) endif() set(CMAKE_MODULE_PATH ${kalarm_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH}) find_package(Xsltproc) set_package_properties(Xsltproc PROPERTIES DESCRIPTION "XSLT processor from libxslt" TYPE REQUIRED PURPOSE "Required to generate D-Bus interfaces for all Akonadi resources.") set(KDEPIM_HAVE_X11 ${X11_FOUND}) configure_file(src/config-kalarm.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kalarm.h ) include_directories(${kalarm_SOURCE_DIR} ${kalarm_BINARY_DIR}) add_definitions(-DQT_MESSAGELOGCONTEXT) add_subdirectory(src) install( FILES kalarm.renamecategories kalarm.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) add_subdirectory(doc) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/akonadiresourcecreator.cpp b/src/akonadiresourcecreator.cpp index c24512ba..30862300 100644 --- a/src/akonadiresourcecreator.cpp +++ b/src/akonadiresourcecreator.cpp @@ -1,181 +1,185 @@ /* * akonadiresourcecreator.cpp - interactively create an Akonadi resource * Program: kalarm - * Copyright © 2011 by David Jarvie + * Copyright © 2011,2019 by David Jarvie * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akonadiresourcecreator.h" #include "autoqpointer.h" #include "kalarmsettings.h" #include "kalarmdirsettings.h" #include "controlinterface.h" #include #include #include #include #include +#include #include #include #include #include "kalarm_debug.h" using namespace Akonadi; using namespace KAlarmCal; AkonadiResourceCreator::AkonadiResourceCreator(CalEvent::Type defaultType, QWidget* parent) : QObject(), mParent(parent), mDefaultType(defaultType) { } /****************************************************************************** * Create a new resource. The user will be prompted to enter its configuration. */ void AkonadiResourceCreator::createResource() { QTimer::singleShot(0, this, &AkonadiResourceCreator::getAgentType); } void AkonadiResourceCreator::getAgentType() { qCDebug(KALARM_LOG) << "Type:" << mDefaultType; // Use AutoQPointer to guard against crash on application exit while // the dialogue is still open. It prevents double deletion (both on // deletion of parent, and on return from this function). AutoQPointer dlg = new AgentTypeDialog(mParent); QString mimeType; switch (mDefaultType) { case CalEvent::ACTIVE: mimeType = KAlarmCal::MIME_ACTIVE; break; case CalEvent::ARCHIVED: mimeType = KAlarmCal::MIME_ARCHIVED; break; case CalEvent::TEMPLATE: mimeType = KAlarmCal::MIME_TEMPLATE; break; default: Q_EMIT finished(this, false); return; } dlg->agentFilterProxyModel()->addMimeTypeFilter(mimeType); dlg->agentFilterProxyModel()->addCapabilityFilter(QStringLiteral("Resource")); if (dlg->exec() != QDialog::Accepted) { Q_EMIT finished(this, false); return; } mAgentType = dlg->agentType(); if (!mAgentType.isValid()) { Q_EMIT finished(this, false); return; } AgentInstanceCreateJob* job = new AgentInstanceCreateJob(mAgentType, mParent); connect(job, &AgentInstanceCreateJob::result, this, &AkonadiResourceCreator::agentInstanceCreated); job->start(); } /****************************************************************************** * Called when an agent creation job has completed. * Checks for any error. */ void AkonadiResourceCreator::agentInstanceCreated(KJob* j) { AgentInstanceCreateJob* job = static_cast(j); if (j->error()) { qCCritical(KALARM_LOG) << "Failed to create new calendar resource:" << j->errorString(); KMessageBox::error(nullptr, xi18nc("@info", "%1(%2)", i18nc("@info", "Failed to create new calendar resource"), j->errorString())); exitWithError(); } else { // Set the default alarm type for a directory resource config dialog mAgentInstance = job->instance(); QString type = mAgentInstance.type().identifier(); if (type == QLatin1String("akonadi_kalarm_dir_resource")) setResourceAlarmType(); else if (type == QLatin1String("akonadi_kalarm_resource")) setResourceAlarmType(); // Display the resource config dialog, but first ensure we get // notified of the user cancelling the operation. org::freedesktop::Akonadi::Agent::Control* agentControlIface = new org::freedesktop::Akonadi::Agent::Control(QStringLiteral("org.freedesktop.Akonadi.Agent.") + mAgentInstance.identifier(), QStringLiteral("/"), KDBusConnectionPool::threadConnection(), this); bool controlOk = agentControlIface && agentControlIface->isValid(); if (!controlOk) { delete agentControlIface; qCWarning(KALARM_LOG) << "Unable to access D-Bus interface of created agent."; } else { connect(agentControlIface, &org::freedesktop::Akonadi::Agent::Control::configurationDialogAccepted, this, &AkonadiResourceCreator::configurationDialogAccepted); connect(agentControlIface, &org::freedesktop::Akonadi::Agent::Control::configurationDialogRejected, this, &AkonadiResourceCreator::exitWithError); } - mAgentInstance.configure(mParent); + + QPointer dlg = new AgentConfigurationDialog(mAgentInstance, mParent); + dlg->exec(); + delete dlg; if (!controlOk) Q_EMIT finished(this, true); // don't actually know the result in this case } } /****************************************************************************** * Set the alarm type for an Akonadi resource. */ template void AkonadiResourceCreator::setResourceAlarmType() { Settings iface(QStringLiteral("org.freedesktop.Akonadi.Resource.") + mAgentInstance.identifier(), QStringLiteral("/Settings"), QDBusConnection::sessionBus(), this); if (!iface.isValid()) qCCritical(KALARM_LOG) << "Error creating D-Bus interface for" << mAgentInstance.identifier() << "resource configuration."; else { iface.setAlarmTypes(CalEvent::mimeTypes(mDefaultType)); iface.save(); mAgentInstance.reconfigure(); // notify the agent that its configuration has changed } } /****************************************************************************** * Called when the user has clicked OK in the resource configuration dialog. */ void AkonadiResourceCreator::configurationDialogAccepted() { Q_EMIT finished(this, true); } /****************************************************************************** * Called when the user has clicked cancel in the resource configuration dialog. * Remove the newly created agent instance. */ void AkonadiResourceCreator::exitWithError() { AgentManager::self()->removeInstance(mAgentInstance); Q_EMIT finished(this, false); } // vim: et sw=4: diff --git a/src/kalarm.h b/src/kalarm.h index 2db66404..3174c0c7 100644 --- a/src/kalarm.h +++ b/src/kalarm.h @@ -1,39 +1,39 @@ /* * kalarm.h - global header file * Program: kalarm - * Copyright © 2001-2018 by David Jarvie + * Copyright © 2001-2019 by David Jarvie * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KALARM_H #define KALARM_H #define VERSION_SUFFIX "" -#define KALARM_VERSION "2.12.2" VERSION_SUFFIX +#define KALARM_VERSION "2.12.3" VERSION_SUFFIX #define KALARM_NAME "KAlarm" #define KALARM_DBUS_SERVICE "org.kde.kalarm" // D-Bus service name of KAlarm application //#include namespace KAlarm { /** Return current KAlarm version number as an integer. */ int Version(); } #endif // KALARM_H diff --git a/src/main.cpp b/src/main.cpp index 068de93c..76609693 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,97 +1,97 @@ /* * main.cpp * Program: kalarm - * Copyright © 2001-2018 by David Jarvie + * Copyright © 2001-2019 by David Jarvie * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kalarm.h" #include "kalarmapp.h" #include "kalarm_debug.h" #include #include #include #include #include #include #include #include #define PROGRAM_NAME "kalarm" int main(int argc, char* argv[]) { // Use QScopedPointer to ensure the QCoreApplication instance is deleted // before libraries unload, to avoid crashes during clean-up. QScopedPointer app(KAlarmApp::create(argc, argv)); const QStringList args = app->arguments(); app->setAttribute(Qt::AA_UseHighDpiPixmaps, true); app->setAttribute(Qt::AA_EnableHighDpiScaling); KCrash::initialize(); KLocalizedString::setApplicationDomain("kalarm"); KAboutData aboutData(QStringLiteral(PROGRAM_NAME), i18n("KAlarm"), QStringLiteral(KALARM_VERSION), i18n("Personal alarm message, command and email scheduler by KDE"), KAboutLicense::GPL, - ki18n("Copyright 2001-%1, David Jarvie").subs(2018).toString(), QString(), + ki18n("Copyright 2001-%1, David Jarvie").subs(2019).toString(), QString(), QStringLiteral("http://www.astrojar.org.uk/kalarm")); aboutData.addAuthor(i18n("David Jarvie"), i18n("Author"), QStringLiteral("djarvie@kde.org")); aboutData.setOrganizationDomain("kde.org"); aboutData.setDesktopFileName(QStringLiteral("org.kde.kalarm")); KAboutData::setApplicationData(aboutData); qCDebug(KALARM_LOG) << "initialising"; QString outputText; int exitCode = app->activate(args, QDir::currentPath(), outputText); if (exitCode >= 0) { if (exitCode > 0) std::cout << qPrintable(outputText) << std::endl; else std::cerr << qPrintable(outputText) << std::endl; exit(exitCode); } app->restoreSession(); return app->exec(); } namespace KAlarm { /****************************************************************************** * Return the current KAlarm version number. */ int Version() { static int version = 0; if (!version) version = KAlarmCal::getVersionNumber(QStringLiteral(KALARM_VERSION)); return version; } } // namespace KAlarm // vim: et sw=4: diff --git a/src/resourceselector.cpp b/src/resourceselector.cpp index 3504f838..64bbcfd5 100644 --- a/src/resourceselector.cpp +++ b/src/resourceselector.cpp @@ -1,637 +1,642 @@ /* * resourceselector.cpp - calendar resource selection widget * Program: kalarm - * Copyright © 2006-2013 by David Jarvie + * Copyright © 2006-2019 by David Jarvie * Based on KOrganizer's ResourceView class and KAddressBook's ResourceSelection class, * Copyright (C) 2003,2004 Cornelius Schumacher * Copyright (C) 2003-2004 Reinhold Kainhofer * Copyright (c) 2004 Tobias Koenig * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "resourceselector.h" #include "kalarm.h" #include "alarmcalendar.h" #include "autoqpointer.h" #include "akonadiresourcecreator.h" #include "calendarmigrator.h" #include "kalarmapp.h" #include "messagebox.h" #include "packedlayout.h" #include "preferences.h" #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kalarm_debug.h" using namespace KCalCore; using namespace Akonadi; ResourceSelector::ResourceSelector(QWidget* parent) : QFrame(parent), mContextMenu(nullptr), mActionReload(nullptr), mActionShowDetails(nullptr), mActionSetColour(nullptr), mActionClearColour(nullptr), mActionEdit(nullptr), mActionUpdate(nullptr), mActionRemove(nullptr), mActionImport(nullptr), mActionExport(nullptr), mActionSetDefault(nullptr) { QBoxLayout* topLayout = new QVBoxLayout(this); QLabel* label = new QLabel(i18nc("@title:group", "Calendars"), this); topLayout->addWidget(label, 0, Qt::AlignHCenter); mAlarmType = new QComboBox(this); mAlarmType->addItem(i18nc("@item:inlistbox", "Active Alarms")); mAlarmType->addItem(i18nc("@item:inlistbox", "Archived Alarms")); mAlarmType->addItem(i18nc("@item:inlistbox", "Alarm Templates")); mAlarmType->setFixedHeight(mAlarmType->sizeHint().height()); mAlarmType->setWhatsThis(i18nc("@info:whatsthis", "Choose which type of data to show alarm calendars for")); topLayout->addWidget(mAlarmType); // No spacing between combo box and listview. CollectionFilterCheckListModel* model = new CollectionFilterCheckListModel(this); mListView = new CollectionView(model, this); connect(mListView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ResourceSelector::selectionChanged); mListView->setContextMenuPolicy(Qt::CustomContextMenu); connect(mListView, &CollectionView::customContextMenuRequested, this, &ResourceSelector::contextMenuRequested); mListView->setWhatsThis(i18nc("@info:whatsthis", "List of available calendars of the selected type. The checked state shows whether a calendar " "is enabled (checked) or disabled (unchecked). The default calendar is shown in bold.")); topLayout->addWidget(mListView, 1); PackedLayout* blayout = new PackedLayout(Qt::AlignHCenter); blayout->setMargin(0); topLayout->addLayout(blayout); mAddButton = new QPushButton(i18nc("@action:button", "Add..."), this); mEditButton = new QPushButton(i18nc("@action:button", "Edit..."), this); mDeleteButton = new QPushButton(i18nc("@action:button", "Remove"), this); blayout->addWidget(mAddButton); blayout->addWidget(mEditButton); blayout->addWidget(mDeleteButton); mEditButton->setWhatsThis(i18nc("@info:whatsthis", "Edit the highlighted calendar")); mDeleteButton->setWhatsThis(xi18nc("@info:whatsthis", "Remove the highlighted calendar from the list." "The calendar itself is left intact, and may subsequently be reinstated in the list if desired.")); mEditButton->setDisabled(true); mDeleteButton->setDisabled(true); connect(mAddButton, &QPushButton::clicked, this, &ResourceSelector::addResource); connect(mEditButton, &QPushButton::clicked, this, &ResourceSelector::editResource); connect(mDeleteButton, &QPushButton::clicked, this, &ResourceSelector::removeResource); connect(AkonadiModel::instance(), &AkonadiModel::collectionAdded, this, &ResourceSelector::slotCollectionAdded); connect(mAlarmType, static_cast(&QComboBox::activated), this, &ResourceSelector::alarmTypeSelected); QTimer::singleShot(0, this, SLOT(alarmTypeSelected())); Preferences::connect(SIGNAL(archivedKeepDaysChanged(int)), this, SLOT(archiveDaysChanged(int))); } /****************************************************************************** * Called when an alarm type has been selected. * Filter the resource list to show resources of the selected alarm type, and * add appropriate whatsThis texts to the list and to the Add button. */ void ResourceSelector::alarmTypeSelected() { QString addTip; switch (mAlarmType->currentIndex()) { case 0: mCurrentAlarmType = CalEvent::ACTIVE; addTip = i18nc("@info:tooltip", "Add a new active alarm calendar"); break; case 1: mCurrentAlarmType = CalEvent::ARCHIVED; addTip = i18nc("@info:tooltip", "Add a new archived alarm calendar"); break; case 2: mCurrentAlarmType = CalEvent::TEMPLATE; addTip = i18nc("@info:tooltip", "Add a new alarm template calendar"); break; } // WORKAROUND: Switch scroll bars off to avoid crash (see explanation // in reinstateAlarmTypeScrollBars() description). mListView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); mListView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); mListView->collectionModel()->setEventTypeFilter(mCurrentAlarmType); mAddButton->setWhatsThis(addTip); mAddButton->setToolTip(addTip); // WORKAROUND: Switch scroll bars back on after allowing geometry to update ... QTimer::singleShot(0, this, &ResourceSelector::reinstateAlarmTypeScrollBars); selectionChanged(); // enable/disable buttons } /****************************************************************************** * WORKAROUND for crash due to presumed Qt bug. * Switch scroll bars off. This is to avoid a crash which can very occasionally * happen when changing from a list of calendars which requires vertical scroll * bars, to a list whose text is very slightly wider but which doesn't require * scroll bars at all. (The suspicion is that the width is such that it would * require horizontal scroll bars if the vertical scroll bars were still * present.) Presumably due to a Qt bug, this can result in a recursive call to * ResourceView::viewportEvent() with a Resize event. * * The crash only occurs if the ResourceSelector happens to have exactly (within * one pixel) the "right" width to create the crash. */ void ResourceSelector::reinstateAlarmTypeScrollBars() { mListView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); mListView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); } /****************************************************************************** * Prompt the user for a new resource to add to the list. */ void ResourceSelector::addResource() { AkonadiResourceCreator* creator = new AkonadiResourceCreator(mCurrentAlarmType, this); connect(creator, &AkonadiResourceCreator::finished, this, &ResourceSelector::resourceAdded); creator->createResource(); } /****************************************************************************** * Called when the job started by AkonadiModel::addCollection() has completed. */ void ResourceSelector::resourceAdded(AkonadiResourceCreator* creator, bool success) { if (success) { AgentInstance agent = creator->agentInstance(); if (agent.isValid()) { // Note that we're expecting the agent's Collection to be added mAddAgents += agent; } } delete creator; } /****************************************************************************** * Called when a collection is added to the AkonadiModel. */ void ResourceSelector::slotCollectionAdded(const Collection& collection) { if (collection.isValid()) { AgentInstance agent = AgentManager::self()->instance(collection.resource()); if (agent.isValid()) { int i = mAddAgents.indexOf(agent); if (i >= 0) { // The collection belongs to an agent created by addResource() CalEvent::Types types = CalEvent::types(collection.contentMimeTypes()); CollectionControlModel::setEnabled(collection, types, true); if (!(types & mCurrentAlarmType)) { // The user has selected alarm types for the resource // which don't include the currently displayed type. // Show a collection list which includes a selected type. int index = -1; if (types & CalEvent::ACTIVE) index = 0; else if (types & CalEvent::ARCHIVED) index = 1; else if (types & CalEvent::TEMPLATE) index = 2; if (index >= 0) { mAlarmType->setCurrentIndex(index); alarmTypeSelected(); } } mAddAgents.removeAt(i); } } } } /****************************************************************************** * Edit the currently selected resource. */ void ResourceSelector::editResource() { Collection collection = currentResource(); if (collection.isValid()) { AgentInstance instance = AgentManager::self()->instance(collection.resource()); if (instance.isValid()) - instance.configure(this); + { + QPointer dlg = new AgentConfigurationDialog(instance, this); + dlg->exec(); + delete dlg; + } } } /****************************************************************************** * Update the backend storage format for the currently selected resource in the * displayed list. */ void ResourceSelector::updateResource() { Collection collection = currentResource(); if (!collection.isValid()) return; AkonadiModel::instance()->refresh(collection); // update with latest data CalendarMigrator::updateToCurrentFormat(collection, true, this); } /****************************************************************************** * Remove the currently selected resource from the displayed list. */ void ResourceSelector::removeResource() { Collection collection = currentResource(); if (!collection.isValid()) return; QString name = collection.name(); // Check if it's the standard or only resource for at least one type. CalEvent::Types allTypes = AkonadiModel::types(collection); CalEvent::Types standardTypes = CollectionControlModel::standardTypes(collection, true); CalEvent::Type currentType = currentResourceType(); CalEvent::Type stdType = (standardTypes & CalEvent::ACTIVE) ? CalEvent::ACTIVE : (standardTypes & CalEvent::ARCHIVED) ? CalEvent::ARCHIVED : CalEvent::EMPTY; if (stdType == CalEvent::ACTIVE) { KAMessageBox::sorry(this, i18nc("@info", "You cannot remove your default active alarm calendar.")); return; } if (stdType == CalEvent::ARCHIVED && Preferences::archivedKeepDays()) { // Only allow the archived alarms standard resource to be removed if // we're not saving archived alarms. KAMessageBox::sorry(this, i18nc("@info", "You cannot remove your default archived alarm calendar " "while expired alarms are configured to be kept.")); return; } QString text; if (standardTypes) { // It's a standard resource for at least one alarm type if (allTypes != currentType) { // It also contains alarm types other than the currently displayed type QString stdTypes = CollectionControlModel::typeListForDisplay(standardTypes); QString otherTypes; CalEvent::Types nonStandardTypes(allTypes & ~standardTypes); if (nonStandardTypes != currentType) otherTypes = xi18nc("@info", "It also contains:%1", CollectionControlModel::typeListForDisplay(nonStandardTypes)); text = xi18nc("@info", "%1 is the default calendar for:%2%3" "Do you really want to remove it from all calendar lists?", name, stdTypes, otherTypes); } else text = xi18nc("@info", "Do you really want to remove your default calendar (%1) from the list?", name); } else if (allTypes != currentType) text = xi18nc("@info", "%1 contains:%2Do you really want to remove it from all calendar lists?", name, CollectionControlModel::typeListForDisplay(allTypes)); else text = xi18nc("@info", "Do you really want to remove the calendar %1 from the list?", name); if (KAMessageBox::warningContinueCancel(this, text, QString(), KStandardGuiItem::remove()) == KMessageBox::Cancel) return; AkonadiModel::instance()->removeCollection(collection); } /****************************************************************************** * Called when the current selection changes, to enable/disable the * Delete and Edit buttons accordingly. */ void ResourceSelector::selectionChanged() { bool state = mListView->selectionModel()->selectedRows().count(); mDeleteButton->setEnabled(state); mEditButton->setEnabled(state); } /****************************************************************************** * Initialise the button and context menu actions. */ void ResourceSelector::initActions(KActionCollection* actions) { mActionReload = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18nc("@action Reload calendar", "Re&load"), this); actions->addAction(QStringLiteral("resReload"), mActionReload); connect(mActionReload, &QAction::triggered, this, &ResourceSelector::reloadResource); mActionShowDetails = new QAction(QIcon::fromTheme(QStringLiteral("help-about")), i18nc("@action", "Show &Details"), this); actions->addAction(QStringLiteral("resDetails"), mActionShowDetails); connect(mActionShowDetails, &QAction::triggered, this, &ResourceSelector::showInfo); mActionSetColour = new QAction(QIcon::fromTheme(QStringLiteral("color-picker")), i18nc("@action", "Set &Color..."), this); actions->addAction(QStringLiteral("resSetColour"), mActionSetColour); connect(mActionSetColour, &QAction::triggered, this, &ResourceSelector::setColour); mActionClearColour = new QAction(i18nc("@action", "Clear C&olor"), this); actions->addAction(QStringLiteral("resClearColour"), mActionClearColour); connect(mActionClearColour, &QAction::triggered, this, &ResourceSelector::clearColour); mActionEdit = new QAction(QIcon::fromTheme(QStringLiteral("document-properties")), i18nc("@action", "&Edit..."), this); actions->addAction(QStringLiteral("resEdit"), mActionEdit); connect(mActionEdit, &QAction::triggered, this, &ResourceSelector::editResource); mActionUpdate = new QAction(i18nc("@action", "&Update Calendar Format"), this); actions->addAction(QStringLiteral("resUpdate"), mActionUpdate); connect(mActionUpdate, &QAction::triggered, this, &ResourceSelector::updateResource); mActionRemove = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action", "&Remove"), this); actions->addAction(QStringLiteral("resRemove"), mActionRemove); connect(mActionRemove, &QAction::triggered, this, &ResourceSelector::removeResource); mActionSetDefault = new KToggleAction(this); actions->addAction(QStringLiteral("resDefault"), mActionSetDefault); connect(mActionSetDefault, &KToggleAction::triggered, this, &ResourceSelector::setStandard); QAction* action = new QAction(QIcon::fromTheme(QStringLiteral("document-new")), i18nc("@action", "&Add..."), this); actions->addAction(QStringLiteral("resAdd"), action); connect(action, &QAction::triggered, this, &ResourceSelector::addResource); mActionImport = new QAction(i18nc("@action", "Im&port..."), this); actions->addAction(QStringLiteral("resImport"), mActionImport); connect(mActionImport, &QAction::triggered, this, &ResourceSelector::importCalendar); mActionExport = new QAction(i18nc("@action", "E&xport..."), this); actions->addAction(QStringLiteral("resExport"), mActionExport); connect(mActionExport, &QAction::triggered, this, &ResourceSelector::exportCalendar); } void ResourceSelector::setContextMenu(QMenu* menu) { mContextMenu = menu; } /****************************************************************************** * Display the context menu for the selected calendar. */ void ResourceSelector::contextMenuRequested(const QPoint& viewportPos) { if (!mContextMenu) return; bool active = false; bool writable = false; bool updatable = false; Collection collection; if (mListView->selectionModel()->hasSelection()) { QModelIndex index = mListView->indexAt(viewportPos); if (index.isValid()) collection = mListView->collectionModel()->collection(index); else mListView->clearSelection(); } CalEvent::Type type = currentResourceType(); bool haveCalendar = collection.isValid(); if (haveCalendar) { // Note: the CollectionControlModel functions call AkonadiModel::refresh(collection) active = CollectionControlModel::isEnabled(collection, type); KACalendar::Compat compatibility; int rw = CollectionControlModel::isWritableEnabled(collection, type, compatibility); writable = (rw > 0); if (!rw && (compatibility & ~KACalendar::Converted) && !(compatibility & ~(KACalendar::Convertible | KACalendar::Converted))) updatable = true; // the calendar format is convertible to the current KAlarm format if (!(AkonadiModel::instance()->types(collection) & type)) type = CalEvent::EMPTY; } mActionReload->setEnabled(active); mActionShowDetails->setEnabled(haveCalendar); mActionSetColour->setEnabled(haveCalendar); mActionClearColour->setEnabled(haveCalendar); mActionClearColour->setVisible(AkonadiModel::instance()->backgroundColor(collection).isValid()); mActionEdit->setEnabled(haveCalendar); mActionUpdate->setEnabled(updatable); mActionRemove->setEnabled(haveCalendar); mActionImport->setEnabled(active && writable); mActionExport->setEnabled(active); QString text; switch (type) { case CalEvent::ACTIVE: text = i18nc("@action", "Use as &Default for Active Alarms"); break; case CalEvent::ARCHIVED: text = i18nc("@action", "Use as &Default for Archived Alarms"); break; case CalEvent::TEMPLATE: text = i18nc("@action", "Use as &Default for Alarm Templates"); break; default: break; } mActionSetDefault->setText(text); bool standard = CollectionControlModel::isStandard(collection, type); mActionSetDefault->setChecked(active && writable && standard); mActionSetDefault->setEnabled(active && writable); mContextMenu->popup(mListView->viewport()->mapToGlobal(viewportPos)); } /****************************************************************************** * Called from the context menu to reload the selected resource. */ void ResourceSelector::reloadResource() { Collection collection = currentResource(); if (collection.isValid()) AkonadiModel::instance()->reloadCollection(collection); } /****************************************************************************** * Called from the context menu to save the selected resource. */ void ResourceSelector::saveResource() { // Save resource is not applicable to Akonadi } /****************************************************************************** * Called when the length of time archived alarms are to be stored changes. * If expired alarms are now to be stored, set any single archived alarm * resource to be the default. */ void ResourceSelector::archiveDaysChanged(int days) { if (days) { if (!CollectionControlModel::getStandard(CalEvent::ARCHIVED).isValid()) { Collection::List cols = CollectionControlModel::enabledCollections(CalEvent::ARCHIVED, true); if (cols.count() == 1) { CollectionControlModel::setStandard(cols[0], CalEvent::ARCHIVED); theApp()->purgeNewArchivedDefault(cols[0]); } } } } /****************************************************************************** * Called from the context menu to set the selected resource as the default * for its alarm type. The resource is automatically made active. */ void ResourceSelector::setStandard() { Collection collection = currentResource(); if (collection.isValid()) { CalEvent::Type alarmType = currentResourceType(); bool standard = mActionSetDefault->isChecked(); if (standard) CollectionControlModel::setEnabled(collection, alarmType, true); CollectionControlModel::setStandard(collection, alarmType, standard); if (alarmType == CalEvent::ARCHIVED) theApp()->purgeNewArchivedDefault(collection); } } /****************************************************************************** * Called from the context menu to merge alarms from an external calendar into * the selected resource (if any). */ void ResourceSelector::importCalendar() { Collection collection = currentResource(); AlarmCalendar::importAlarms(this, (collection.isValid() ? &collection : nullptr)); } /****************************************************************************** * Called from the context menu to copy the selected resource's alarms to an * external calendar. */ void ResourceSelector::exportCalendar() { Collection calendar = currentResource(); if (calendar.isValid()) AlarmCalendar::exportAlarms(AlarmCalendar::resources()->events(calendar), this); } /****************************************************************************** * Called from the context menu to set a colour for the selected resource. */ void ResourceSelector::setColour() { Collection collection = currentResource(); if (collection.isValid()) { QColor colour = AkonadiModel::instance()->backgroundColor(collection); if (!colour.isValid()) colour = QApplication::palette().color(QPalette::Base); colour = QColorDialog::getColor(colour, this); if (colour.isValid()) AkonadiModel::instance()->setBackgroundColor(collection, colour); } } /****************************************************************************** * Called from the context menu to clear the display colour for the selected * resource. */ void ResourceSelector::clearColour() { Collection collection = currentResource(); if (collection.isValid()) AkonadiModel::instance()->setBackgroundColor(collection, QColor()); } /****************************************************************************** * Called from the context menu to display information for the selected resource. */ void ResourceSelector::showInfo() { Collection collection = currentResource(); if (collection.isValid()) { const QString name = collection.displayName(); QString id = collection.resource(); // resource name CalEvent::Type alarmType = currentResourceType(); QString calType = AgentManager::self()->instance(id).type().name(); QString storage = AkonadiModel::instance()->storageType(collection); QString location = collection.remoteId(); QUrl url = QUrl::fromUserInput(location, QString(), QUrl::AssumeLocalFile); if (url.isLocalFile()) location = url.path(); CalEvent::Types altypes = AkonadiModel::instance()->types(collection); QStringList alarmTypes; if (altypes & CalEvent::ACTIVE) alarmTypes << i18nc("@info", "Active alarms"); if (altypes & CalEvent::ARCHIVED) alarmTypes << i18nc("@info", "Archived alarms"); if (altypes & CalEvent::TEMPLATE) alarmTypes << i18nc("@info", "Alarm templates"); QString alarmTypeString = alarmTypes.join(i18nc("@info List separator", ", ")); QString perms = AkonadiModel::readOnlyTooltip(collection); if (perms.isEmpty()) perms = i18nc("@info", "Read-write"); QString enabled = CollectionControlModel::isEnabled(collection, alarmType) ? i18nc("@info", "Enabled") : i18nc("@info", "Disabled"); QString std = CollectionControlModel::isStandard(collection, alarmType) ? i18nc("@info Parameter in 'Default calendar: Yes/No'", "Yes") : i18nc("@info Parameter in 'Default calendar: Yes/No'", "No"); QString text = xi18nc("@info", "%1" "ID: %2" "Calendar type: %3" "Contents: %4" "%5: %6" "Permissions: %7" "Status: %8" "Default calendar: %9", name, id, calType, alarmTypeString, storage, location, perms, enabled, std); // Display the collection information. Because the user requested // the information, don't raise a KNotify event. KAMessageBox::information(this, text, QString(), QString(), KMessageBox::Options()); } } /****************************************************************************** * Return the currently selected resource in the list. */ Collection ResourceSelector::currentResource() const { return mListView->collection(mListView->selectionModel()->currentIndex()); } /****************************************************************************** * Return the currently selected resource type. */ CalEvent::Type ResourceSelector::currentResourceType() const { switch (mAlarmType->currentIndex()) { case 0: return CalEvent::ACTIVE; case 1: return CalEvent::ARCHIVED; case 2: return CalEvent::TEMPLATE; default: return CalEvent::EMPTY; } } void ResourceSelector::resizeEvent(QResizeEvent* re) { Q_EMIT resized(re->oldSize(), re->size()); } // vim: et sw=4: