diff --git a/src/widgets/actionstatemanager.cpp b/src/widgets/actionstatemanager.cpp index 4f878bf75..25d37df79 100644 --- a/src/widgets/actionstatemanager.cpp +++ b/src/widgets/actionstatemanager.cpp @@ -1,422 +1,413 @@ /* Copyright (c) 2010 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "actionstatemanager_p.h" #include "agentmanager.h" #include "collectionutils.h" #include "pastehelper_p.h" #include "specialcollectionattribute.h" #include "standardactionmanager.h" #include "entitydeletedattribute.h" #include #include using namespace Akonadi; static bool canCreateSubCollection(const Collection &collection) { if (!(collection.rights() & Collection::CanCreateCollection)) { return false; } if (!collection.contentMimeTypes().contains(Collection::mimeType()) && !collection.contentMimeTypes().contains(Collection::virtualMimeType())) { return false; } return true; } static inline bool canContainItems(const Collection &collection) { if (collection.contentMimeTypes().isEmpty()) { return false; } if ((collection.contentMimeTypes().count() == 1) && ((collection.contentMimeTypes().at(0) == Collection::mimeType()) || (collection.contentMimeTypes().at(0) == Collection::virtualMimeType()))) { return false; } return true; } -ActionStateManager::ActionStateManager() - : mReceiver(nullptr) -{ -} - -ActionStateManager::~ActionStateManager() -{ -} - void ActionStateManager::setReceiver(QObject *object) { mReceiver = object; } void ActionStateManager::updateState(const Collection::List &collections, const Collection::List &favoriteCollections, const Item::List &items) { const int collectionCount = collections.count(); const bool singleCollectionSelected = (collectionCount == 1); const bool multipleCollectionsSelected = (collectionCount > 1); const bool atLeastOneCollectionSelected = (singleCollectionSelected || multipleCollectionsSelected); const int itemCount = items.count(); const bool singleItemSelected = (itemCount == 1); const bool multipleItemsSelected = (itemCount > 1); const bool atLeastOneItemSelected = (singleItemSelected || multipleItemsSelected); const bool listOfCollectionNotEmpty = !collections.isEmpty(); bool canDeleteCollections = listOfCollectionNotEmpty; if (canDeleteCollections) { for (const Collection &collection : collections) { // do we have the necessary rights? if (!(collection.rights() &Collection::CanDeleteCollection)) { canDeleteCollections = false; break; } if (isRootCollection(collection)) { canDeleteCollections = false; break; } if (isResourceCollection(collection)) { canDeleteCollections = false; break; } } } bool canCutCollections = canDeleteCollections; // we must be able to delete for cutting for (const Collection &collection : collections) { if (isSpecialCollection(collection)) { canCutCollections = false; break; } if (!isFolderCollection(collection)) { canCutCollections = false; break; } } const bool canMoveCollections = canCutCollections; // we must be able to cut for moving bool canCopyCollections = listOfCollectionNotEmpty; if (canCopyCollections) { for (const Collection &collection : collections) { if (isRootCollection(collection)) { canCopyCollections = false; break; } if (!isFolderCollection(collection)) { canCopyCollections = false; break; } } } bool canAddToFavoriteCollections = listOfCollectionNotEmpty; if (canAddToFavoriteCollections) { for (const Collection &collection : collections) { if (isRootCollection(collection)) { canAddToFavoriteCollections = false; break; } if (isFavoriteCollection(collection)) { canAddToFavoriteCollections = false; break; } if (!isFolderCollection(collection)) { canAddToFavoriteCollections = false; break; } if (!canContainItems(collection)) { canAddToFavoriteCollections = false; break; } } } bool collectionsAreFolders = listOfCollectionNotEmpty; for (const Collection &collection : collections) { if (!isFolderCollection(collection)) { collectionsAreFolders = false; break; } } bool collectionsAreInTrash = false; for (const Collection &collection : collections) { if (collection.hasAttribute()) { collectionsAreInTrash = true; break; } } bool atLeastOneCollectionCanHaveItems = false; for (const Collection &collection : collections) { if (collectionCanHaveItems(collection)) { atLeastOneCollectionCanHaveItems = true; break; } } for (const Collection &collection : favoriteCollections) { if (collectionCanHaveItems(collection)) { atLeastOneCollectionCanHaveItems = true; break; } } const Collection collection = (!collections.isEmpty() ? collections.first() : Collection()); // collection specific actions enableAction(StandardActionManager::CreateCollection, singleCollectionSelected && // we can create only inside one collection canCreateSubCollection(collection)); // we need the necessary rights enableAction(StandardActionManager::DeleteCollections, canDeleteCollections); enableAction(StandardActionManager::CopyCollections, canCopyCollections); enableAction(StandardActionManager::CutCollections, canCutCollections); enableAction(StandardActionManager::CopyCollectionToMenu, canCopyCollections); enableAction(StandardActionManager::MoveCollectionToMenu, canMoveCollections); enableAction(StandardActionManager::MoveCollectionsToTrash, atLeastOneCollectionSelected && canMoveCollections && !collectionsAreInTrash); enableAction(StandardActionManager::RestoreCollectionsFromTrash, atLeastOneCollectionSelected && canMoveCollections && collectionsAreInTrash); enableAction(StandardActionManager::CopyCollectionToDialog, canCopyCollections); enableAction(StandardActionManager::MoveCollectionToDialog, canMoveCollections); enableAction(StandardActionManager::CollectionProperties, singleCollectionSelected && // we can only configure one collection at a time !isRootCollection(collection)); // we can not configure the root collection enableAction(StandardActionManager::SynchronizeCollections, atLeastOneCollectionCanHaveItems); // it must be a valid folder collection enableAction(StandardActionManager::SynchronizeCollectionsRecursive, atLeastOneCollectionSelected && collectionsAreFolders); // it must be a valid folder collection #ifndef QT_NO_CLIPBOARD enableAction(StandardActionManager::Paste, singleCollectionSelected && // we can paste only into a single collection PasteHelper::canPaste(QApplication::clipboard()->mimeData(), collection, Qt::CopyAction)); // there must be data on the clipboard #else enableAction(StandardActionManager::Paste, false); // no support for clipboard -> no paste #endif // favorite collections specific actions enableAction(StandardActionManager::AddToFavoriteCollections, canAddToFavoriteCollections); const bool canRemoveFromFavoriteCollections = !favoriteCollections.isEmpty(); enableAction(StandardActionManager::RemoveFromFavoriteCollections, canRemoveFromFavoriteCollections); enableAction(StandardActionManager::RenameFavoriteCollection, favoriteCollections.count() == 1); // we can rename only one collection at a time // resource specific actions int resourceCollectionCount = 0; bool canDeleteResources = true; bool canConfigureResource = true; bool canSynchronizeResources = true; for (const Collection &collection : collections) { if (isResourceCollection(collection)) { resourceCollectionCount++; // check that the 'NoConfig' flag is not set for the resource if (hasResourceCapability(collection, QStringLiteral("NoConfig"))) { canConfigureResource = false; } } else { // we selected a non-resource collection canDeleteResources = false; canConfigureResource = false; canSynchronizeResources = false; } } if (resourceCollectionCount == 0) { // not a single resource collection has been selected canDeleteResources = false; canConfigureResource = false; canSynchronizeResources = false; } enableAction(StandardActionManager::CreateResource, true); enableAction(StandardActionManager::DeleteResources, canDeleteResources); enableAction(StandardActionManager::ResourceProperties, canConfigureResource); enableAction(StandardActionManager::SynchronizeResources, canSynchronizeResources); enableAction(StandardActionManager::SynchronizeCollectionTree, canSynchronizeResources); if (collectionsAreInTrash) { updateAlternatingAction(StandardActionManager::MoveToTrashRestoreCollectionAlternative); //updatePluralLabel( StandardActionManager::MoveToTrashRestoreCollectionAlternative, collectionCount ); } else { updateAlternatingAction(StandardActionManager::MoveToTrashRestoreCollection); } enableAction(StandardActionManager::MoveToTrashRestoreCollection, atLeastOneCollectionSelected && canMoveCollections); // item specific actions bool canDeleteItems = (!items.isEmpty()); //TODO: fixme for (const Item &item : qAsConst(items)) { const Collection parentCollection = item.parentCollection(); if (!parentCollection.isValid()) { continue; } canDeleteItems = canDeleteItems && (parentCollection.rights() &Collection::CanDeleteItem); } bool itemsAreInTrash = false; for (const Item &item : qAsConst(items)) { if (item.hasAttribute()) { itemsAreInTrash = true; break; } } enableAction(StandardActionManager::CopyItems, atLeastOneItemSelected); // we need items to work with enableAction(StandardActionManager::CutItems, atLeastOneItemSelected && // we need items to work with canDeleteItems); // we need the necessary rights enableAction(StandardActionManager::DeleteItems, atLeastOneItemSelected && // we need items to work with canDeleteItems); // we need the necessary rights enableAction(StandardActionManager::CopyItemToMenu, atLeastOneItemSelected); // we need items to work with enableAction(StandardActionManager::MoveItemToMenu, atLeastOneItemSelected && // we need items to work with canDeleteItems); // we need the necessary rights enableAction(StandardActionManager::MoveItemsToTrash, atLeastOneItemSelected && canDeleteItems && !itemsAreInTrash); enableAction(StandardActionManager::RestoreItemsFromTrash, atLeastOneItemSelected && itemsAreInTrash); enableAction(StandardActionManager::CopyItemToDialog, atLeastOneItemSelected); // we need items to work with enableAction(StandardActionManager::MoveItemToDialog, atLeastOneItemSelected && // we need items to work with canDeleteItems); // we need the necessary rights if (itemsAreInTrash) { updateAlternatingAction(StandardActionManager::MoveToTrashRestoreItemAlternative); //updatePluralLabel( StandardActionManager::MoveToTrashRestoreItemAlternative, itemCount ); } else { updateAlternatingAction(StandardActionManager::MoveToTrashRestoreItem); } enableAction(StandardActionManager::MoveToTrashRestoreItem, atLeastOneItemSelected && // we need items to work with canDeleteItems); // we need the necessary rights // update the texts of the actions updatePluralLabel(StandardActionManager::CopyCollections, collectionCount); updatePluralLabel(StandardActionManager::CopyItems, itemCount); updatePluralLabel(StandardActionManager::DeleteItems, itemCount); updatePluralLabel(StandardActionManager::CutItems, itemCount); updatePluralLabel(StandardActionManager::CutCollections, collectionCount); updatePluralLabel(StandardActionManager::DeleteCollections, collectionCount); updatePluralLabel(StandardActionManager::SynchronizeCollections, collectionCount); updatePluralLabel(StandardActionManager::SynchronizeCollectionsRecursive, collectionCount); updatePluralLabel(StandardActionManager::DeleteResources, resourceCollectionCount); updatePluralLabel(StandardActionManager::SynchronizeResources, resourceCollectionCount); updatePluralLabel(StandardActionManager::SynchronizeCollectionTree, resourceCollectionCount); } bool ActionStateManager::isRootCollection(const Collection &collection) const { return CollectionUtils::isRoot(collection); } bool ActionStateManager::isResourceCollection(const Collection &collection) const { return CollectionUtils::isResource(collection); } bool ActionStateManager::isFolderCollection(const Collection &collection) const { return (CollectionUtils::isFolder(collection) || CollectionUtils::isResource(collection) || CollectionUtils::isStructural(collection)); } bool ActionStateManager::isSpecialCollection(const Collection &collection) const { return collection.hasAttribute(); } bool ActionStateManager::isFavoriteCollection(const Collection &collection) const { if (!mReceiver) { return false; } bool result = false; QMetaObject::invokeMethod(mReceiver, "isFavoriteCollection", Qt::DirectConnection, Q_RETURN_ARG(bool, result), Q_ARG(Akonadi::Collection, collection)); return result; } bool ActionStateManager::hasResourceCapability(const Collection &collection, const QString &capability) const { const Akonadi::AgentInstance instance = AgentManager::self()->instance(collection.resource()); return instance.type().capabilities().contains(capability); } bool ActionStateManager::collectionCanHaveItems(const Collection &collection) const { return !(collection.contentMimeTypes() == (QStringList() << QStringLiteral("inode/directory")) || CollectionUtils::isStructural(collection)); } void ActionStateManager::enableAction(int action, bool state) { if (!mReceiver) { return; } QMetaObject::invokeMethod(mReceiver, "enableAction", Qt::DirectConnection, Q_ARG(int, action), Q_ARG(bool, state)); } void ActionStateManager::updatePluralLabel(int action, int count) { if (!mReceiver) { return; } QMetaObject::invokeMethod(mReceiver, "updatePluralLabel", Qt::DirectConnection, Q_ARG(int, action), Q_ARG(int, count)); } void ActionStateManager::updateAlternatingAction(int action) { if (!mReceiver) { return; } QMetaObject::invokeMethod(mReceiver, "updateAlternatingAction", Qt::DirectConnection, Q_ARG(int, action)); } diff --git a/src/widgets/actionstatemanager_p.h b/src/widgets/actionstatemanager_p.h index a0acb193d..9850dfef1 100644 --- a/src/widgets/actionstatemanager_p.h +++ b/src/widgets/actionstatemanager_p.h @@ -1,87 +1,86 @@ /* Copyright (c) 2010 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_ACTIONSTATEMANAGER_P_H #define AKONADI_ACTIONSTATEMANAGER_P_H #include "collection.h" #include "item.h" class QObject; namespace Akonadi { /** * @short A helper class to manage action states. * * @author Tobias Koenig */ class ActionStateManager { public: /* * Creates a new action state manager. */ - ActionStateManager(); + explicit ActionStateManager() = default; - /** - * Destroys the action state manager. - */ - virtual ~ActionStateManager(); + virtual ~ActionStateManager() = default; /** * Updates the states according to the selected collections and items. * @param collections selected collections (from the folder tree) * @param favoriteCollections selected collections (among the ones marked as favorites) * @param items selected items */ void updateState(const Collection::List &collections, const Collection::List &favoriteCollections, const Item::List &items); /** * Sets the @p receiver object that will actually update the states. * * The object must provide the following three slots: * - void enableAction( int, bool ) * - void updatePluralLabel( int, int ) * - bool isFavoriteCollection( const Akonadi::Collection& ) * @param receiver object that will actually update the states. */ void setReceiver(QObject *receiver); protected: virtual bool isRootCollection(const Collection &collection) const; virtual bool isResourceCollection(const Collection &collection) const; virtual bool isFolderCollection(const Collection &collection) const; virtual bool isSpecialCollection(const Collection &collection) const; virtual bool isFavoriteCollection(const Collection &collection) const; virtual bool hasResourceCapability(const Collection &collection, const QString &capability) const; virtual bool collectionCanHaveItems(const Collection &collection) const; virtual void enableAction(int action, bool state); virtual void updatePluralLabel(int action, int count); virtual void updateAlternatingAction(int action); private: + Q_DISABLE_COPY_MOVE(ActionStateManager) + QObject *mReceiver = nullptr; }; } #endif diff --git a/src/widgets/agentactionmanager.cpp b/src/widgets/agentactionmanager.cpp index df4f7bd7f..063c68230 100644 --- a/src/widgets/agentactionmanager.cpp +++ b/src/widgets/agentactionmanager.cpp @@ -1,365 +1,366 @@ /* Copyright (c) 2010 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "agentactionmanager.h" #include "agentfilterproxymodel.h" #include "agentinstancecreatejob.h" #include "agentinstancemodel.h" #include "agentmanager.h" #include "agenttypedialog.h" #include "metatypes.h" #include #include #include #include #include #include #include using namespace Akonadi; //@cond PRIVATE static const struct { const char *name; const char *label; const char *icon; int shortcut; const char *slot; } agentActionData[] = { { "akonadi_agentinstance_create", I18N_NOOP("&New Agent Instance..."), "folder-new", 0, SLOT(slotCreateAgentInstance()) }, { "akonadi_agentinstance_delete", I18N_NOOP("&Delete Agent Instance"), "edit-delete", 0, SLOT(slotDeleteAgentInstance()) }, { "akonadi_agentinstance_configure", I18N_NOOP("&Configure Agent Instance"), "configure", 0, SLOT(slotConfigureAgentInstance()) } }; static const int numAgentActionData = sizeof agentActionData / sizeof * agentActionData; static_assert(numAgentActionData == AgentActionManager::LastType, "agentActionData table does not match AgentActionManager types"); /** * @internal */ class Q_DECL_HIDDEN AgentActionManager::Private { public: Private(AgentActionManager *parent) : q(parent) , mActionCollection(nullptr) , mParentWidget(nullptr) , mSelectionModel(nullptr) { mActions.fill(nullptr, AgentActionManager::LastType); setContextText(AgentActionManager::CreateAgentInstance, AgentActionManager::DialogTitle, i18nc("@title:window", "New Agent Instance")); setContextText(AgentActionManager::CreateAgentInstance, AgentActionManager::ErrorMessageText, ki18n("Could not create agent instance: %1")); setContextText(AgentActionManager::CreateAgentInstance, AgentActionManager::ErrorMessageTitle, i18n("Agent instance creation failed")); setContextText(AgentActionManager::DeleteAgentInstance, AgentActionManager::MessageBoxTitle, i18nc("@title:window", "Delete Agent Instance?")); setContextText(AgentActionManager::DeleteAgentInstance, AgentActionManager::MessageBoxText, i18n("Do you really want to delete the selected agent instance?")); } void enableAction(AgentActionManager::Type type, bool enable) { Q_ASSERT(type >= 0 && type < AgentActionManager::LastType); if (QAction *act = mActions[type]) { act->setEnabled(enable); } } void updateActions() { const AgentInstance::List instances = selectedAgentInstances(); const bool createActionEnabled = true; bool deleteActionEnabled = true; bool configureActionEnabled = true; if (instances.isEmpty()) { deleteActionEnabled = false; configureActionEnabled = false; } if (instances.count() == 1) { const AgentInstance instance = instances.first(); if (instance.type().capabilities().contains(QLatin1String("NoConfig"))) { configureActionEnabled = false; } } enableAction(CreateAgentInstance, createActionEnabled); enableAction(DeleteAgentInstance, deleteActionEnabled); enableAction(ConfigureAgentInstance, configureActionEnabled); Q_EMIT q->actionStateUpdated(); } AgentInstance::List selectedAgentInstances() const { AgentInstance::List instances; if (!mSelectionModel) { return instances; } const QModelIndexList lstModelIndex = mSelectionModel->selectedRows(); for (const QModelIndex &index : lstModelIndex ) { const AgentInstance instance = index.data(AgentInstanceModel::InstanceRole).value(); if (instance.isValid()) { instances << instance; } } return instances; } void slotCreateAgentInstance() { QPointer dlg(new Akonadi::AgentTypeDialog(mParentWidget)); dlg->setWindowTitle(contextText(AgentActionManager::CreateAgentInstance, AgentActionManager::DialogTitle)); for (const QString &mimeType : qAsConst(mMimeTypeFilter)) { dlg->agentFilterProxyModel()->addMimeTypeFilter(mimeType); } for (const QString &capability : qAsConst(mCapabilityFilter)) { dlg->agentFilterProxyModel()->addCapabilityFilter(capability); } if (dlg->exec() == QDialog::Accepted) { const AgentType agentType = dlg->agentType(); if (agentType.isValid()) { AgentInstanceCreateJob *job = new AgentInstanceCreateJob(agentType, q); - q->connect(job, SIGNAL(result(KJob*)), SLOT(slotAgentInstanceCreationResult(KJob*))); + q->connect(job, &KJob::result, q, [this](KJob *job) { slotAgentInstanceCreationResult(job); }); job->configure(mParentWidget); job->start(); } } delete dlg; } void slotDeleteAgentInstance() { const AgentInstance::List instances = selectedAgentInstances(); if (!instances.isEmpty()) { if (KMessageBox::questionYesNo( mParentWidget, contextText(AgentActionManager::DeleteAgentInstance, AgentActionManager::MessageBoxText), contextText(AgentActionManager::DeleteAgentInstance, AgentActionManager::MessageBoxTitle), KStandardGuiItem::del(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) == KMessageBox::Yes) { for (const AgentInstance &instance : instances) { AgentManager::self()->removeInstance(instance); } } } } void slotConfigureAgentInstance() { AgentInstance::List instances = selectedAgentInstances(); if (instances.isEmpty()) { return; } instances.first().configure(mParentWidget); } void slotAgentInstanceCreationResult(KJob *job) { if (job->error()) { KMessageBox::error( mParentWidget, contextText(AgentActionManager::CreateAgentInstance, AgentActionManager::ErrorMessageText).arg(job->errorString()), contextText(AgentActionManager::CreateAgentInstance, AgentActionManager::ErrorMessageTitle)); } } void setContextText(AgentActionManager::Type type, AgentActionManager::TextContext context, const QString &data) { mContextTexts[type].insert(context, data); } void setContextText(AgentActionManager::Type type, AgentActionManager::TextContext context, const KLocalizedString &data) { mContextTexts[type].insert(context, data.toString()); } QString contextText(AgentActionManager::Type type, AgentActionManager::TextContext context) const { return mContextTexts[type].value(context); } AgentActionManager *q = nullptr; KActionCollection *mActionCollection = nullptr; QWidget *mParentWidget = nullptr; QItemSelectionModel *mSelectionModel = nullptr; QVector mActions; QStringList mMimeTypeFilter; QStringList mCapabilityFilter; typedef QHash ContextTexts; QHash mContextTexts; }; //@endcond AgentActionManager::AgentActionManager(KActionCollection *actionCollection, QWidget *parent) : QObject(parent) , d(new Private(this)) { d->mParentWidget = parent; d->mActionCollection = actionCollection; } AgentActionManager::~AgentActionManager() { delete d; } void AgentActionManager::setSelectionModel(QItemSelectionModel *selectionModel) { d->mSelectionModel = selectionModel; - connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - SLOT(updateActions())); + connect(selectionModel, &QItemSelectionModel::selectionChanged, + this, [this]() { d->updateActions(); }); } void AgentActionManager::setMimeTypeFilter(const QStringList &mimeTypes) { d->mMimeTypeFilter = mimeTypes; } void AgentActionManager::setCapabilityFilter(const QStringList &capabilities) { d->mCapabilityFilter = capabilities; } QAction *AgentActionManager::createAction(Type type) { Q_ASSERT(type >= 0 && type < LastType); Q_ASSERT(agentActionData[type].name); if (QAction *act = d->mActions[type]) { return act; } QAction *action = new QAction(d->mParentWidget); action->setText(i18n(agentActionData[type].label)); if (agentActionData[type].icon) { action->setIcon(QIcon::fromTheme(QString::fromLatin1(agentActionData[type].icon))); } action->setShortcut(agentActionData[type].shortcut); if (agentActionData[type].slot) { connect(action, SIGNAL(triggered()), agentActionData[type].slot); } d->mActionCollection->addAction(QString::fromLatin1(agentActionData[type].name), action); d->mActions[type] = action; d->updateActions(); return action; } void AgentActionManager::createAllActions() { for (int type = 0; type < LastType; ++type) { - createAction(static_cast(type)); + auto action = createAction(static_cast(type)); + Q_UNUSED(action); } } QAction *AgentActionManager::action(Type type) const { Q_ASSERT(type >= 0 && type < LastType); return d->mActions[type]; } void AgentActionManager::interceptAction(Type type, bool intercept) { Q_ASSERT(type >= 0 && type < LastType); const QAction *action = d->mActions[type]; if (!action) { return; } if (intercept) { disconnect(action, SIGNAL(triggered()), this, agentActionData[type].slot); } else { connect(action, SIGNAL(triggered()), agentActionData[type].slot); } } AgentInstance::List AgentActionManager::selectedAgentInstances() const { return d->selectedAgentInstances(); } void AgentActionManager::setContextText(Type type, TextContext context, const QString &text) { d->setContextText(type, context, text); } void AgentActionManager::setContextText(Type type, TextContext context, const KLocalizedString &text) { d->setContextText(type, context, text); } #include "moc_agentactionmanager.cpp" diff --git a/src/widgets/agentconfigurationwidget.cpp b/src/widgets/agentconfigurationwidget.cpp index 500a5774a..3121538e3 100644 --- a/src/widgets/agentconfigurationwidget.cpp +++ b/src/widgets/agentconfigurationwidget.cpp @@ -1,181 +1,179 @@ /* Copyright (c) 2018 Daniel Vrátil This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "agentconfigurationwidget.h" #include "agentconfigurationwidget_p.h" #include "agentconfigurationdialog.h" #include "akonadiwidgets_debug.h" #include "core/agentconfigurationmanager_p.h" #include "core/agentconfigurationbase.h" #include "core/agentconfigurationfactorybase.h" #include "core/agentmanager.h" #include "core/servermanager.h" #include #include #include #include #include #include #include #include using namespace Akonadi; AgentConfigurationWidget::Private::Private(const AgentInstance &instance) : agentInstance(instance) { } AgentConfigurationWidget::Private::~Private() { } void AgentConfigurationWidget::Private::setupErrorWidget(QWidget *parent, const QString &text) { QVBoxLayout *layout = new QVBoxLayout(parent); layout->addStretch(2); auto label = new QLabel(text, parent); label->setAlignment(Qt::AlignCenter); layout->addWidget(label); layout->addStretch(2); } bool AgentConfigurationWidget::Private::loadPlugin(const QString &pluginPath) { if (pluginPath.isEmpty()) { qCDebug(AKONADIWIDGETS_LOG) << "Haven't found config plugin for" << agentInstance.type().identifier(); return false; } loader = decltype(loader)(new QPluginLoader(pluginPath)); if (!loader->load()) { qCWarning(AKONADIWIDGETS_LOG) << "Failed to load config plugin" << pluginPath << ":" << loader->errorString(); loader.reset(); return false; } factory = qobject_cast(loader->instance()); if (!factory) { // will unload the QPluginLoader and thus delete the factory as well qCWarning(AKONADIWIDGETS_LOG) << "Config plugin" << pluginPath << "does not contain AgentConfigurationFactory!"; loader.reset(); return false; } qCDebug(AKONADIWIDGETS_LOG) << "Loaded agent configuration plugin" << pluginPath; return true; } AgentConfigurationWidget::AgentConfigurationWidget(const AgentInstance &instance, QWidget *parent) : QWidget(parent) , d(new Private(instance)) { if (AgentConfigurationManager::self()->registerInstanceConfiguration(instance.identifier())) { const auto pluginPath = AgentConfigurationManager::self()->findConfigPlugin(instance.type().identifier()); if (d->loadPlugin(pluginPath)) { QString configName = instance.identifier() + QStringLiteral("rc"); configName = Akonadi::ServerManager::addNamespace(configName); KSharedConfigPtr config = KSharedConfig::openConfig(configName); QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); d->plugin = d->factory->create(config, this, { instance.identifier() }); connect(d->plugin.data(), &AgentConfigurationBase::enableOkButton, this, &AgentConfigurationWidget::enableOkButton); } else { // Hide this dialog and fallback to calling the out-of-process configuration if (auto dlg = qobject_cast(parent)) { const_cast(instance).configure(topLevelWidget()->parentWidget()); // If we are inside the AgentConfigurationDialog, hide the dialog - QTimer::singleShot(0, [dlg]() { - dlg->reject(); - }); + QTimer::singleShot(0, this, [dlg]() { dlg->reject(); }); } else { const_cast(instance).configure(); // Otherwise show a message that this is opened externally d->setupErrorWidget(this, i18n("The configuration dialog has been opened in another window")); } // TODO: Re-enable once we can kill the fallback code above ^^ //d->setupErrorWidget(this, i18n("Failed to load configuration plugin")); } } else if (AgentConfigurationManager::self()->isInstanceRegistered(instance.identifier())) { d->setupErrorWidget(this, i18n("Configuration for %1 is already opened elsewhere.", instance.name())); } else { d->setupErrorWidget(this, i18n("Failed to register %1 configuration dialog.", instance.name())); } QTimer::singleShot(0, this, &AgentConfigurationWidget::load); } AgentConfigurationWidget::~AgentConfigurationWidget() { AgentConfigurationManager::self()->unregisterInstanceConfiguration(d->agentInstance.identifier()); } void AgentConfigurationWidget::load() { if (d->plugin) { d->plugin->load(); } } void AgentConfigurationWidget::save() { qCDebug(AKONADIWIDGETS_LOG) << "Saving configuration for" << d->agentInstance.identifier(); if (d->plugin) { if (d->plugin->save()) { d->agentInstance.reconfigure(); } } } QSize AgentConfigurationWidget::restoreDialogSize() const { if (d->plugin) { return d->plugin->restoreDialogSize(); } return {}; } -void AgentConfigurationWidget::saveDialogSize(const QSize &size) +void AgentConfigurationWidget::saveDialogSize(QSize size) { if (d->plugin) { d->plugin->saveDialogSize(size); } } QDialogButtonBox::StandardButtons AgentConfigurationWidget::standardButtons() const { if (d->plugin) { return d->plugin->standardButtons(); } return QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel; } void AgentConfigurationWidget::childEvent(QChildEvent *event) { if (event->added()) { - if (auto widget = qobject_cast(event->child())) { - layout()->addWidget(widget); + if (event->child()->isWidgetType()) { + layout()->addWidget(static_cast(event->child())); } } QWidget::childEvent(event); } diff --git a/src/widgets/agentconfigurationwidget.h b/src/widgets/agentconfigurationwidget.h index e9f229b3e..20c0138fc 100644 --- a/src/widgets/agentconfigurationwidget.h +++ b/src/widgets/agentconfigurationwidget.h @@ -1,65 +1,65 @@ /* Copyright (c) 2018 Daniel Vrátil This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_AGENTCONFIGURATIONWIDGET_H #define AKONADI_AGENTCONFIGURATIONWIDGET_H #include #include #include "akonadiwidgets_export.h" namespace Akonadi { class AgentInstance; class AgentConfigurationDialog; /** * @brief A widget for displaying agent configuration in applications. * * To implement an agent configuration widget, see AgentConfigurationBase. */ class AKONADIWIDGETS_EXPORT AgentConfigurationWidget : public QWidget { Q_OBJECT public: explicit AgentConfigurationWidget(const Akonadi::AgentInstance &instance, QWidget *parent = nullptr); ~AgentConfigurationWidget() override; void load(); void save(); QSize restoreDialogSize() const; - void saveDialogSize(const QSize &size); + void saveDialogSize(QSize size); QDialogButtonBox::StandardButtons standardButtons() const; Q_SIGNALS: void enableOkButton(bool enabled); protected: void childEvent(QChildEvent *event) override; private: class Private; friend class Private; friend class AgentConfigurationDialog; QScopedPointer d; }; } #endif diff --git a/src/widgets/agentinstancewidget.cpp b/src/widgets/agentinstancewidget.cpp index 0f8663623..86e120360 100644 --- a/src/widgets/agentinstancewidget.cpp +++ b/src/widgets/agentinstancewidget.cpp @@ -1,305 +1,305 @@ /* Copyright (c) 2006-2008 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "agentinstancewidget.h" #include "agentfilterproxymodel.h" #include "agentinstance.h" #include "agentinstancemodel.h" #include #include #include #include #include #include namespace Akonadi { namespace Internal { static void iconsEarlyCleanup(); struct Icons { Icons() : readyPixmap(QIcon::fromTheme(QStringLiteral("user-online")).pixmap(QSize(16, 16))) , syncPixmap(QIcon::fromTheme(QStringLiteral("network-connect")).pixmap(QSize(16, 16))) , errorPixmap(QIcon::fromTheme(QStringLiteral("dialog-error")).pixmap(QSize(16, 16))) , offlinePixmap(QIcon::fromTheme(QStringLiteral("network-disconnect")).pixmap(QSize(16, 16))) { qAddPostRoutine(iconsEarlyCleanup); } QPixmap readyPixmap, syncPixmap, errorPixmap, offlinePixmap; }; Q_GLOBAL_STATIC(Icons, s_icons) // called as a Qt post routine, to prevent pixmap leaking void iconsEarlyCleanup() { Icons *const ic = s_icons; ic->readyPixmap = ic->syncPixmap = ic->errorPixmap = ic->offlinePixmap = QPixmap(); } static const int s_delegatePaddingSize = 7; /** * @internal */ class AgentInstanceWidgetDelegate : public QAbstractItemDelegate { + Q_OBJECT public: explicit AgentInstanceWidgetDelegate(QObject *parent = nullptr); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; } using Akonadi::Internal::AgentInstanceWidgetDelegate; /** * @internal */ class Q_DECL_HIDDEN AgentInstanceWidget::Private { public: Private(AgentInstanceWidget *parent) : mParent(parent) , mView(nullptr) , mModel(nullptr) , proxy(nullptr) { } void currentAgentInstanceChanged(const QModelIndex ¤tIndex, const QModelIndex &previousIndex); void currentAgentInstanceDoubleClicked(const QModelIndex ¤tIndex); void currentAgentInstanceClicked(const QModelIndex ¤tIndex); AgentInstanceWidget *mParent = nullptr; QListView *mView = nullptr; AgentInstanceModel *mModel = nullptr; AgentFilterProxyModel *proxy = nullptr; }; void AgentInstanceWidget::Private::currentAgentInstanceChanged(const QModelIndex ¤tIndex, const QModelIndex &previousIndex) { AgentInstance currentInstance; if (currentIndex.isValid()) { currentInstance = currentIndex.data(AgentInstanceModel::InstanceRole).value(); } AgentInstance previousInstance; if (previousIndex.isValid()) { previousInstance = previousIndex.data(AgentInstanceModel::InstanceRole).value(); } Q_EMIT mParent->currentChanged(currentInstance, previousInstance); } void AgentInstanceWidget::Private::currentAgentInstanceDoubleClicked(const QModelIndex ¤tIndex) { AgentInstance currentInstance; if (currentIndex.isValid()) { currentInstance = currentIndex.data(AgentInstanceModel::InstanceRole).value(); } Q_EMIT mParent->doubleClicked(currentInstance); } void AgentInstanceWidget::Private::currentAgentInstanceClicked(const QModelIndex ¤tIndex) { AgentInstance currentInstance; if (currentIndex.isValid()) { currentInstance = currentIndex.data(AgentInstanceModel::InstanceRole).value(); } Q_EMIT mParent->clicked(currentInstance); } AgentInstanceWidget::AgentInstanceWidget(QWidget *parent) : QWidget(parent) , d(new Private(this)) { QHBoxLayout *layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); d->mView = new QListView(this); d->mView->setContextMenuPolicy(Qt::NoContextMenu); d->mView->setItemDelegate(new Internal::AgentInstanceWidgetDelegate(d->mView)); d->mView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); d->mView->setAlternatingRowColors(true); d->mView->setSelectionMode(QAbstractItemView::ExtendedSelection); layout->addWidget(d->mView); d->mModel = new AgentInstanceModel(this); d->proxy = new AgentFilterProxyModel(this); d->proxy->setDynamicSortFilter(true); d->proxy->sort(0); d->proxy->setSortCaseSensitivity(Qt::CaseInsensitive); d->proxy->setSourceModel(d->mModel); d->mView->setModel(d->proxy); d->mView->selectionModel()->setCurrentIndex(d->mView->model()->index(0, 0), QItemSelectionModel::Select); d->mView->scrollTo(d->mView->model()->index(0, 0)); - connect(d->mView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this, SLOT(currentAgentInstanceChanged(QModelIndex,QModelIndex))); + connect(d->mView->selectionModel(), &QItemSelectionModel::currentChanged, + this, [this](const auto &tl, const auto &br) { d->currentAgentInstanceChanged(tl, br); }); connect(d->mView, &QListView::doubleClicked, this, [this](const QModelIndex ¤tIndex) { d->currentAgentInstanceDoubleClicked(currentIndex); }); - connect(d->mView, SIGNAL(clicked(QModelIndex)), - this, SLOT(currentAgentInstanceClicked(QModelIndex))); + connect(d->mView, &QListView::clicked, this, [this](const auto &mi) { d->currentAgentInstanceClicked(mi); }); } AgentInstanceWidget::~AgentInstanceWidget() { delete d; } AgentInstance AgentInstanceWidget::currentAgentInstance() const { QItemSelectionModel *selectionModel = d->mView->selectionModel(); if (!selectionModel) { return AgentInstance(); } QModelIndex index = selectionModel->currentIndex(); if (!index.isValid()) { return AgentInstance(); } return index.data(AgentInstanceModel::InstanceRole).value(); } AgentInstance::List AgentInstanceWidget::selectedAgentInstances() const { AgentInstance::List list; QItemSelectionModel *selectionModel = d->mView->selectionModel(); if (!selectionModel) { return list; } const QModelIndexList indexes = selectionModel->selection().indexes(); list.reserve(indexes.count()); for (const QModelIndex &index : indexes) { list.append(index.data(AgentInstanceModel::InstanceRole).value()); } return list; } QAbstractItemView *AgentInstanceWidget::view() const { return d->mView; } AgentFilterProxyModel *AgentInstanceWidget::agentFilterProxyModel() const { return d->proxy; } AgentInstanceWidgetDelegate::AgentInstanceWidgetDelegate(QObject *parent) : QAbstractItemDelegate(parent) { } void AgentInstanceWidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (!index.isValid()) { return; } QStyle *style = QApplication::style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, nullptr); QIcon icon = index.data(Qt::DecorationRole).value(); const QString name = index.model()->data(index, Qt::DisplayRole).toString(); int status = index.model()->data(index, AgentInstanceModel::StatusRole).toInt(); uint progress = index.model()->data(index, AgentInstanceModel::ProgressRole).toUInt(); QString statusMessage = index.model()->data(index, AgentInstanceModel::StatusMessageRole).toString(); QPixmap statusPixmap; if (!index.data(AgentInstanceModel::OnlineRole).toBool()) { statusPixmap = s_icons->offlinePixmap; } else if (status == AgentInstance::Idle) { statusPixmap = s_icons->readyPixmap; } else if (status == AgentInstance::Running) { statusPixmap = s_icons->syncPixmap; } else { statusPixmap = s_icons->errorPixmap; } if (status == 1) { statusMessage.append(QStringLiteral(" (%1%)").arg(progress)); } const QPixmap iconPixmap = icon.pixmap(style->pixelMetric(QStyle::PM_MessageBoxIconSize)); QRect innerRect = option.rect.adjusted(s_delegatePaddingSize, s_delegatePaddingSize, -s_delegatePaddingSize, -s_delegatePaddingSize); //add some padding round entire delegate const QSize decorationSize = iconPixmap.size(); const QSize statusIconSize = statusPixmap.size();//= KIconLoader::global()->currentSize(KIconLoader::Small); QFont nameFont = option.font; nameFont.setBold(true); QFont statusTextFont = option.font; const QRect decorationRect(innerRect.left(), innerRect.top(), decorationSize.width(), innerRect.height()); const QRect nameTextRect(decorationRect.topRight() + QPoint(4, 0), innerRect.topRight() + QPoint(0, innerRect.height() / 2)); const QRect statusTextRect(decorationRect.bottomRight() + QPoint(4, - innerRect.height() / 2), innerRect.bottomRight()); QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) { cg = QPalette::Inactive; } if (option.state & QStyle::State_Selected) { painter->setPen(option.palette.color(cg, QPalette::HighlightedText)); } else { painter->setPen(option.palette.color(cg, QPalette::Text)); } painter->drawPixmap(style->itemPixmapRect(decorationRect, Qt::AlignCenter, iconPixmap), iconPixmap); painter->setFont(nameFont); painter->drawText(nameTextRect, Qt::AlignVCenter | Qt::AlignLeft, name); painter->setFont(statusTextFont); painter->drawText(statusTextRect.adjusted(statusIconSize.width() + 4, 0, 0, 0), Qt::AlignVCenter | Qt::AlignLeft, statusMessage); painter->drawPixmap(style->itemPixmapRect(statusTextRect, Qt::AlignVCenter | Qt::AlignLeft, statusPixmap), statusPixmap); } QSize AgentInstanceWidgetDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index); const int iconHeight = QApplication::style()->pixelMetric(QStyle::PM_MessageBoxIconSize) + (s_delegatePaddingSize * 2); //icon height + padding either side const int textHeight = option.fontMetrics.height() + qMax(option.fontMetrics.height(), 16) + (s_delegatePaddingSize * 2); //height of text + icon/text + padding either side return QSize(1, qMax(iconHeight, textHeight)); //any width,the view will give us the whole thing in list mode } } -#include "moc_agentinstancewidget.cpp" +#include "agentinstancewidget.moc" diff --git a/src/widgets/agentinstancewidget.h b/src/widgets/agentinstancewidget.h index 374dc5910..ae10d05fb 100644 --- a/src/widgets/agentinstancewidget.h +++ b/src/widgets/agentinstancewidget.h @@ -1,144 +1,141 @@ /* Copyright (c) 2006-2008 Tobias Koenig Copyright (C) 2012-2020 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_AGENTINSTANCEWIDGET_H #define AKONADI_AGENTINSTANCEWIDGET_H #include "akonadiwidgets_export.h" #include class QAbstractItemView; namespace Akonadi { class AgentInstance; class AgentFilterProxyModel; /** * @short Provides a widget that lists all available agent instances. * * The widget is listening on the dbus for changes, so the * widget is updated automatically as soon as new agent instances * are added to or removed from the system. * * @code * * MyWidget::MyWidget( QWidget *parent ) * : QWidget( parent ) * { * QVBoxLayout *layout = new QVBoxLayout( this ); * * mAgentInstanceWidget = new Akonadi::AgentInstanceWidget( this ); * layout->addWidget( mAgentInstanceWidget ); * * connect( mAgentInstanceWidget, SIGNAL(doubleClicked(Akonadi::AgentInstance)), * this, SLOT(slotInstanceSelected(Akonadi::AgentInstance)) ); * } * * ... * * MyWidget::slotInstanceSelected( Akonadi::AgentInstance &instance ) * { * qCDebug(AKONADIWIDGETS_LOG) << "Selected instance" << instance.name(); * } * * @endcode * * @author Tobias Koenig */ class AKONADIWIDGETS_EXPORT AgentInstanceWidget : public QWidget { Q_OBJECT public: /** * Creates a new agent instance widget. * * @param parent The parent widget. */ explicit AgentInstanceWidget(QWidget *parent = nullptr); /** * Destroys the agent instance widget. */ ~AgentInstanceWidget(); /** * Returns the current agent instance or an invalid agent instance * if no agent instance is selected. */ Q_REQUIRED_RESULT AgentInstance currentAgentInstance() const; /** * Returns the selected agent instances. * @since 4.5 */ Q_REQUIRED_RESULT QVector selectedAgentInstances() const; /** * Returns the agent filter proxy model, use this to filter by * agent mimetype or capabilities. */ Q_REQUIRED_RESULT AgentFilterProxyModel *agentFilterProxyModel() const; /** * Returns the view used in the widget. * @since 4.5 */ Q_REQUIRED_RESULT QAbstractItemView *view() const; Q_SIGNALS: /** * This signal is emitted whenever the current agent instance changes. * * @param current The current agent instance. * @param previous The previous agent instance. */ void currentChanged(const Akonadi::AgentInstance ¤t, const Akonadi::AgentInstance &previous); /** * This signal is emitted whenever there is a double click on an agent instance. * * @param current The current agent instance. */ void doubleClicked(const Akonadi::AgentInstance ¤t); /** * This signal is emitted whenever there is a click on an agent instance. * * @param current The current agent instance. * @since 4.9.1 */ void clicked(const Akonadi::AgentInstance ¤t); private: //@cond PRIVATE class Private; Private *const d; - - Q_PRIVATE_SLOT(d, void currentAgentInstanceChanged(const QModelIndex &, const QModelIndex &)) - Q_PRIVATE_SLOT(d, void currentAgentInstanceClicked(const QModelIndex ¤tIndex)) //@endcond }; } #endif diff --git a/src/widgets/agenttypewidget.cpp b/src/widgets/agenttypewidget.cpp index 46feedea5..8fdedc90a 100644 --- a/src/widgets/agenttypewidget.cpp +++ b/src/widgets/agenttypewidget.cpp @@ -1,278 +1,279 @@ /* Copyright (c) 2006-2008 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "agenttypewidget.h" #include #include #include #include #include "agentfilterproxymodel.h" #include "agenttype.h" #include "agenttypemodel.h" namespace Akonadi { namespace Internal { /** * @internal */ class AgentTypeWidgetDelegate : public QAbstractItemDelegate { + Q_OBJECT public: explicit AgentTypeWidgetDelegate(QObject *parent = nullptr); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; private: - void drawFocus(QPainter *, const QStyleOptionViewItem &, const QRect &) const; + void drawFocus(QPainter *, const QStyleOptionViewItem &, QRect) const; }; } using Akonadi::Internal::AgentTypeWidgetDelegate; /** * @internal */ class Q_DECL_HIDDEN AgentTypeWidget::Private { public: Private(AgentTypeWidget *parent) : mParent(parent), mView(nullptr), mModel(nullptr), proxyModel(nullptr) { } void currentAgentTypeChanged(const QModelIndex &, const QModelIndex &); void typeActivated(const QModelIndex &index) { if (index.flags() & (Qt::ItemIsSelectable | Qt::ItemIsEnabled)) { Q_EMIT mParent->activated(); } } AgentTypeWidget *mParent = nullptr; QListView *mView = nullptr; AgentTypeModel *mModel = nullptr; AgentFilterProxyModel *proxyModel = nullptr; }; void AgentTypeWidget::Private::currentAgentTypeChanged(const QModelIndex ¤tIndex, const QModelIndex &previousIndex) { AgentType currentType; if (currentIndex.isValid()) { currentType = currentIndex.data(AgentTypeModel::TypeRole).value(); } AgentType previousType; if (previousIndex.isValid()) { previousType = previousIndex.data(AgentTypeModel::TypeRole).value(); } Q_EMIT mParent->currentChanged(currentType, previousType); } AgentTypeWidget::AgentTypeWidget(QWidget *parent) : QWidget(parent) , d(new Private(this)) { QHBoxLayout *layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); d->mView = new QListView(this); d->mView->setItemDelegate(new AgentTypeWidgetDelegate(d->mView)); d->mView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); d->mView->setAlternatingRowColors(true); layout->addWidget(d->mView); d->mModel = new AgentTypeModel(d->mView); d->proxyModel = new AgentFilterProxyModel(this); d->proxyModel->setSourceModel(d->mModel); d->proxyModel->sort(0); d->mView->setModel(d->proxyModel); d->mView->selectionModel()->setCurrentIndex(d->mView->model()->index(0, 0), QItemSelectionModel::Select); d->mView->scrollTo(d->mView->model()->index(0, 0)); connect(d->mView->selectionModel(), &QItemSelectionModel::currentChanged, this, [this](const QModelIndex &start, const QModelIndex &end) {d->currentAgentTypeChanged(start, end);}); connect(d->mView, QOverload::of(&QListView::activated), this, [this](const QModelIndex &index) { d->typeActivated(index); }); } AgentTypeWidget::~AgentTypeWidget() { delete d; } AgentType AgentTypeWidget::currentAgentType() const { QItemSelectionModel *selectionModel = d->mView->selectionModel(); if (!selectionModel) { return AgentType(); } QModelIndex index = selectionModel->currentIndex(); if (!index.isValid()) { return AgentType(); } return index.data(AgentTypeModel::TypeRole).value(); } AgentFilterProxyModel *AgentTypeWidget::agentFilterProxyModel() const { return d->proxyModel; } /** * AgentTypeWidgetDelegate */ AgentTypeWidgetDelegate::AgentTypeWidgetDelegate(QObject *parent) : QAbstractItemDelegate(parent) { } void AgentTypeWidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (!index.isValid()) { return; } painter->setRenderHint(QPainter::Antialiasing); const QString name = index.model()->data(index, Qt::DisplayRole).toString(); const QString comment = index.model()->data(index, AgentTypeModel::DescriptionRole).toString(); const QVariant data = index.model()->data(index, Qt::DecorationRole); QPixmap pixmap; if (data.isValid() && data.type() == QVariant::Icon) { pixmap = qvariant_cast(data).pixmap(64, 64); } const QFont oldFont = painter->font(); QFont boldFont(oldFont); boldFont.setBold(true); painter->setFont(boldFont); QFontMetrics fm = painter->fontMetrics(); int hn = fm.boundingRect(0, 0, 0, 0, Qt::AlignLeft, name).height(); int wn = fm.boundingRect(0, 0, 0, 0, Qt::AlignLeft, name).width(); painter->setFont(oldFont); fm = painter->fontMetrics(); int hc = fm.boundingRect(0, 0, 0, 0, Qt::AlignLeft, comment).height(); int wc = fm.boundingRect(0, 0, 0, 0, Qt::AlignLeft, comment).width(); int wp = pixmap.width(); QStyleOptionViewItem opt(option); opt.showDecorationSelected = true; QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter); QPen pen = painter->pen(); QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) { cg = QPalette::Inactive; } if (option.state & QStyle::State_Selected) { painter->setPen(option.palette.color(cg, QPalette::HighlightedText)); } else { painter->setPen(option.palette.color(cg, QPalette::Text)); } painter->setFont(option.font); painter->drawPixmap(option.rect.x() + 5, option.rect.y() + 5, pixmap); painter->setFont(boldFont); if (!name.isEmpty()) { painter->drawText(option.rect.x() + 5 + wp + 5, option.rect.y() + 7, wn, hn, Qt::AlignLeft, name); } painter->setFont(oldFont); if (!comment.isEmpty()) { painter->drawText(option.rect.x() + 5 + wp + 5, option.rect.y() + 7 + hn, wc, hc, Qt::AlignLeft, comment); } painter->setPen(pen); drawFocus(painter, option, option.rect); } QSize AgentTypeWidgetDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { if (!index.isValid()) { return QSize(0, 0); } const QString name = index.model()->data(index, Qt::DisplayRole).toString(); const QString comment = index.model()->data(index, AgentTypeModel::DescriptionRole).toString(); QFontMetrics fm = option.fontMetrics; int hn = fm.boundingRect(0, 0, 0, 0, Qt::AlignLeft, name).height(); int wn = fm.boundingRect(0, 0, 0, 0, Qt::AlignLeft, name).width(); int hc = fm.boundingRect(0, 0, 0, 0, Qt::AlignLeft, comment).height(); int wc = fm.boundingRect(0, 0, 0, 0, Qt::AlignLeft, comment).width(); int width = 0; int height = 0; if (!name.isEmpty()) { height += hn; width = qMax(width, wn); } if (!comment.isEmpty()) { height += hc; width = qMax(width, wc); } height = qMax(height, 64) + 10; width += 64 + 15; return QSize(width, height); } -void AgentTypeWidgetDelegate::drawFocus(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect) const +void AgentTypeWidgetDelegate::drawFocus(QPainter *painter, const QStyleOptionViewItem &option, QRect rect) const { if (option.state & QStyle::State_HasFocus) { QStyleOptionFocusRect o; o.QStyleOption::operator=(option); o.rect = rect; o.state |= QStyle::State_KeyboardFocusChange; QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Window); QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter); } } } -#include "moc_agenttypewidget.cpp" +#include "agenttypewidget.moc" diff --git a/src/widgets/collectioncombobox.cpp b/src/widgets/collectioncombobox.cpp index 5c188fc5e..95ebfde2e 100644 --- a/src/widgets/collectioncombobox.cpp +++ b/src/widgets/collectioncombobox.cpp @@ -1,190 +1,185 @@ /* This file is part of Akonadi Contact. Copyright (c) 2007-2009 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "collectioncombobox.h" #include "asyncselectionhandler_p.h" #include "collectiondialog.h" #include "monitor.h" #include "collectionfetchscope.h" #include "collectionfilterproxymodel.h" #include "entityrightsfiltermodel.h" #include "entitytreemodel.h" #include "session.h" #include "collectionutils.h" #include #include using namespace Akonadi; class Q_DECL_HIDDEN CollectionComboBox::Private { public: Private(QAbstractItemModel *customModel, CollectionComboBox *parent) : mParent(parent) { if (customModel) { mBaseModel = customModel; } else { mMonitor = new Akonadi::Monitor(mParent); mMonitor->setObjectName(QStringLiteral("CollectionComboBoxMonitor")); mMonitor->fetchCollection(true); mMonitor->setCollectionMonitored(Akonadi::Collection::root()); // This ETM will be set to only show collections with the wanted mimetype in setMimeTypeFilter mModel = new EntityTreeModel(mMonitor, mParent); mModel->setItemPopulationStrategy(EntityTreeModel::NoItemPopulation); mModel->setListFilter(CollectionFetchScope::Display); mBaseModel = mModel; } // Flatten the tree, e.g. // Kolab // Kolab / Inbox // Kolab / Inbox / Calendar KDescendantsProxyModel *proxyModel = new KDescendantsProxyModel(parent); proxyModel->setDisplayAncestorData(true); proxyModel->setSourceModel(mBaseModel); // Filter it by mimetype again, to only keep // Kolab / Inbox / Calendar mMimeTypeFilterModel = new CollectionFilterProxyModel(parent); mMimeTypeFilterModel->setSourceModel(proxyModel); // Filter by access rights. TODO: maybe this functionality could be provided by CollectionFilterProxyModel, to save one proxy? mRightsFilterModel = new EntityRightsFilterModel(parent); mRightsFilterModel->setSourceModel(mMimeTypeFilterModel); mParent->setModel(mRightsFilterModel); mParent->model()->sort(mParent->modelColumn()); mSelectionHandler = new AsyncSelectionHandler(mRightsFilterModel, mParent); - mParent->connect(mSelectionHandler, SIGNAL(collectionAvailable(QModelIndex)), - mParent, SLOT(activated(QModelIndex))); - - mParent->connect(mParent, SIGNAL(activated(int)), - mParent, SLOT(activated(int))); + mParent->connect(mSelectionHandler, &AsyncSelectionHandler::collectionAvailable, + mParent, [this](const auto &mi) { activated(mi); }); } - ~Private() - { - } + ~Private() = default; void activated(int index); void activated(const QModelIndex &index); CollectionComboBox *mParent = nullptr; Monitor *mMonitor = nullptr; EntityTreeModel *mModel = nullptr; QAbstractItemModel *mBaseModel = nullptr; CollectionFilterProxyModel *mMimeTypeFilterModel = nullptr; EntityRightsFilterModel *mRightsFilterModel = nullptr; AsyncSelectionHandler *mSelectionHandler = nullptr; }; void CollectionComboBox::Private::activated(int index) { const QModelIndex modelIndex = mParent->model()->index(index, 0); if (modelIndex.isValid()) { Q_EMIT mParent->currentChanged(modelIndex.data(EntityTreeModel::CollectionRole).value()); } } void CollectionComboBox::Private::activated(const QModelIndex &index) { mParent->setCurrentIndex(index.row()); } CollectionComboBox::CollectionComboBox(QWidget *parent) : QComboBox(parent) , d(new Private(nullptr, this)) { } CollectionComboBox::CollectionComboBox(QAbstractItemModel *model, QWidget *parent) : QComboBox(parent) , d(new Private(model, this)) { } CollectionComboBox::~CollectionComboBox() { delete d; } void CollectionComboBox::setMimeTypeFilter(const QStringList &contentMimeTypes) { d->mMimeTypeFilterModel->clearFilters(); d->mMimeTypeFilterModel->addMimeTypeFilters(contentMimeTypes); if (d->mMonitor) { for (const QString &mimeType : contentMimeTypes) { d->mMonitor->setMimeTypeMonitored(mimeType, true); } } } QStringList CollectionComboBox::mimeTypeFilter() const { return d->mMimeTypeFilterModel->mimeTypeFilters(); } void CollectionComboBox::setAccessRightsFilter(Collection::Rights rights) { d->mRightsFilterModel->setAccessRights(rights); } Akonadi::Collection::Rights CollectionComboBox::accessRightsFilter() const { return d->mRightsFilterModel->accessRights(); } void CollectionComboBox::setDefaultCollection(const Collection &collection) { d->mSelectionHandler->waitForCollection(collection); } Akonadi::Collection CollectionComboBox::currentCollection() const { const QModelIndex modelIndex = model()->index(currentIndex(), 0); if (modelIndex.isValid()) { return modelIndex.data(Akonadi::EntityTreeModel::CollectionRole).value(); } else { return Akonadi::Collection(); } } void CollectionComboBox::setExcludeVirtualCollections(bool b) { d->mMimeTypeFilterModel->setExcludeVirtualCollections(b); } bool CollectionComboBox::excludeVirtualCollections() const { return d->mMimeTypeFilterModel->excludeVirtualCollections(); } #include "moc_collectioncombobox.cpp" diff --git a/src/widgets/collectiondialog.cpp b/src/widgets/collectiondialog.cpp index 8288f58df..e7fd37967 100644 --- a/src/widgets/collectiondialog.cpp +++ b/src/widgets/collectiondialog.cpp @@ -1,421 +1,420 @@ /* Copyright 2008 Ingo Klöcker Copyright 2010-2020 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "collectiondialog.h" #include "asyncselectionhandler_p.h" #include "monitor.h" #include "collectionfetchscope.h" #include "collectionfilterproxymodel.h" #include "entityrightsfiltermodel.h" #include "entitytreemodel.h" #include "entitytreeview.h" #include "session.h" #include "collectioncreatejob.h" #include "collectionutils.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace Akonadi; class Q_DECL_HIDDEN CollectionDialog::Private { public: Private(QAbstractItemModel *customModel, CollectionDialog *parent, CollectionDialogOptions options) : mParent(parent) { // setup GUI QVBoxLayout *layout = new QVBoxLayout(mParent); mTextLabel = new QLabel(mParent); layout->addWidget(mTextLabel); mTextLabel->hide(); QLineEdit *filterCollectionLineEdit = new QLineEdit(mParent); filterCollectionLineEdit->setClearButtonEnabled(true); filterCollectionLineEdit->setPlaceholderText(i18nc("@info Displayed grayed-out inside the " "textbox, verb to search", "Search")); layout->addWidget(filterCollectionLineEdit); mView = new EntityTreeView(mParent); mView->setDragDropMode(QAbstractItemView::NoDragDrop); mView->header()->hide(); layout->addWidget(mView); mUseByDefault = new QCheckBox(i18n("Use folder by default"), mParent); mUseByDefault->hide(); layout->addWidget(mUseByDefault); mButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, mParent); mParent->connect(mButtonBox, &QDialogButtonBox::accepted, mParent, &QDialog::accept); mParent->connect(mButtonBox, &QDialogButtonBox::rejected, mParent, &QDialog::reject); layout->addWidget(mButtonBox); mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false); // setup models QAbstractItemModel *baseModel = nullptr; if (customModel) { baseModel = customModel; } else { mMonitor = new Akonadi::Monitor(mParent); mMonitor->setObjectName(QStringLiteral("CollectionDialogMonitor")); mMonitor->fetchCollection(true); mMonitor->setCollectionMonitored(Akonadi::Collection::root()); EntityTreeModel *model = new EntityTreeModel(mMonitor, mParent); model->setItemPopulationStrategy(EntityTreeModel::NoItemPopulation); model->setListFilter(CollectionFetchScope::Display); baseModel = model; } mMimeTypeFilterModel = new CollectionFilterProxyModel(mParent); mMimeTypeFilterModel->setSourceModel(baseModel); mMimeTypeFilterModel->setExcludeVirtualCollections(true); mRightsFilterModel = new EntityRightsFilterModel(mParent); mRightsFilterModel->setSourceModel(mMimeTypeFilterModel); mFilterCollection = new QSortFilterProxyModel(mParent); mFilterCollection->setRecursiveFilteringEnabled(true); mFilterCollection->setSourceModel(mRightsFilterModel); mFilterCollection->setFilterCaseSensitivity(Qt::CaseInsensitive); mView->setModel(mFilterCollection); changeCollectionDialogOptions(options); mParent->connect(filterCollectionLineEdit, &QLineEdit::textChanged, mParent, [this](const QString &str) { slotFilterFixedString(str); }); - mParent->connect(mView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - mParent, SLOT(slotSelectionChanged())); - - mParent->connect(mView, SIGNAL(doubleClicked(QModelIndex)), - mParent, SLOT(slotDoubleClicked())); + mParent->connect(mView->selectionModel(), &QItemSelectionModel::selectionChanged, + mParent, [this]() { slotSelectionChanged(); }); + mParent->connect(mView, qOverload(&QAbstractItemView::doubleClicked), + mParent, [this]() { slotDoubleClicked(); }); mSelectionHandler = new AsyncSelectionHandler(mFilterCollection, mParent); mParent->connect(mSelectionHandler, &AsyncSelectionHandler::collectionAvailable, mParent, [this](const QModelIndex &index) {slotCollectionAvailable(index);}); readConfig(); } ~Private() { writeConfig(); } void slotCollectionAvailable(const QModelIndex &index) { mView->expandAll(); mView->setCurrentIndex(index); } void slotFilterFixedString(const QString &filter) { mFilterCollection->setFilterFixedString(filter); if (mKeepTreeExpanded) { mView->expandAll(); } } void readConfig() { KConfig config(QStringLiteral("akonadi_contactrc")); KConfigGroup group(&config, QStringLiteral("CollectionDialog")); const QSize size = group.readEntry("Size", QSize(800, 500)); if (size.isValid()) { mParent->resize(size); } } void writeConfig() { KConfig config(QStringLiteral("akonadi_contactrc")); KConfigGroup group(&config, QStringLiteral("CollectionDialog")); group.writeEntry("Size", mParent->size()); group.sync(); } CollectionDialog *mParent = nullptr; Monitor *mMonitor = nullptr; CollectionFilterProxyModel *mMimeTypeFilterModel = nullptr; EntityRightsFilterModel *mRightsFilterModel = nullptr; EntityTreeView *mView = nullptr; AsyncSelectionHandler *mSelectionHandler = nullptr; QLabel *mTextLabel = nullptr; QSortFilterProxyModel *mFilterCollection = nullptr; QCheckBox *mUseByDefault = nullptr; QStringList mContentMimeTypes; QDialogButtonBox *mButtonBox = nullptr; QPushButton *mNewSubfolderButton = nullptr; bool mAllowToCreateNewChildCollection = false; bool mKeepTreeExpanded = false; void slotDoubleClicked(); void slotSelectionChanged(); void slotAddChildCollection(); void slotCollectionCreationResult(KJob *job); bool canCreateCollection(const Akonadi::Collection &parentCollection) const; void changeCollectionDialogOptions(CollectionDialogOptions options); bool canSelectCollection() const; }; void CollectionDialog::Private::slotDoubleClicked() { if (canSelectCollection()) { mParent->accept(); } } bool CollectionDialog::Private::canSelectCollection() const { bool result = (!mView->selectionModel()->selectedIndexes().isEmpty()); if (mAllowToCreateNewChildCollection) { const Akonadi::Collection parentCollection = mParent->selectedCollection(); if (parentCollection.isValid()) { result = (parentCollection.rights() & Akonadi::Collection::CanCreateItem); } } return result; } void CollectionDialog::Private::slotSelectionChanged() { mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(!mView->selectionModel()->selectedIndexes().isEmpty()); if (mAllowToCreateNewChildCollection) { const Akonadi::Collection parentCollection = mParent->selectedCollection(); const bool canCreateChildCollections = canCreateCollection(parentCollection); mNewSubfolderButton->setEnabled(canCreateChildCollections && !parentCollection.isVirtual()); if (parentCollection.isValid()) { const bool canCreateItems = (parentCollection.rights() & Akonadi::Collection::CanCreateItem); mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(canCreateItems); } } } void CollectionDialog::Private::changeCollectionDialogOptions(CollectionDialogOptions options) { mAllowToCreateNewChildCollection = (options & AllowToCreateNewChildCollection); if (mAllowToCreateNewChildCollection) { mNewSubfolderButton = mButtonBox->addButton(i18n("&New Subfolder..."), QDialogButtonBox::NoRole); mNewSubfolderButton->setIcon(QIcon::fromTheme(QStringLiteral("folder-new"))); mNewSubfolderButton->setToolTip(i18n("Create a new subfolder under the currently selected folder")); mNewSubfolderButton->setEnabled(false); - connect(mNewSubfolderButton, SIGNAL(clicked(bool)), mParent, SLOT(slotAddChildCollection())); + connect(mNewSubfolderButton, &QPushButton::clicked, mParent, [this]() { slotAddChildCollection(); }); } mKeepTreeExpanded = (options & KeepTreeExpanded); if (mKeepTreeExpanded) { mParent->connect(mRightsFilterModel, &EntityRightsFilterModel::rowsInserted, mView, &EntityTreeView::expandAll, Qt::UniqueConnection); mView->expandAll(); } } bool CollectionDialog::Private::canCreateCollection(const Akonadi::Collection &parentCollection) const { if (!parentCollection.isValid()) { return false; } if ((parentCollection.rights() & Akonadi::Collection::CanCreateCollection)) { const QStringList dialogMimeTypeFilter = mParent->mimeTypeFilter(); const QStringList parentCollectionMimeTypes = parentCollection.contentMimeTypes(); for (const QString &mimetype : dialogMimeTypeFilter) { if (parentCollectionMimeTypes.contains(mimetype)) { return true; } } return true; } return false; } void CollectionDialog::Private::slotAddChildCollection() { const Akonadi::Collection parentCollection = mParent->selectedCollection(); if (canCreateCollection(parentCollection)) { const QString name = QInputDialog::getText(mParent, i18nc("@title:window", "New Folder"), i18nc("@label:textbox, name of a thing", "Name")); if (name.trimmed().isEmpty()) { return; } Akonadi::Collection collection; collection.setName(name); collection.setParentCollection(parentCollection); if (!mContentMimeTypes.isEmpty()) { collection.setContentMimeTypes(mContentMimeTypes); } Akonadi::CollectionCreateJob *job = new Akonadi::CollectionCreateJob(collection); connect(job, &Akonadi::CollectionCreateJob::result, mParent, [this](KJob *job) {slotCollectionCreationResult(job);}); } } void CollectionDialog::Private::slotCollectionCreationResult(KJob *job) { if (job->error()) { QMessageBox::critical(mParent, i18n("Folder creation failed"), i18n("Could not create folder: %1", job->errorString())); } } CollectionDialog::CollectionDialog(QWidget *parent) : QDialog(parent) , d(new Private(nullptr, this, CollectionDialog::None)) { } CollectionDialog::CollectionDialog(QAbstractItemModel *model, QWidget *parent) : QDialog(parent) , d(new Private(model, this, CollectionDialog::None)) { } CollectionDialog::CollectionDialog(CollectionDialogOptions options, QAbstractItemModel *model, QWidget *parent) : QDialog(parent) , d(new Private(model, this, options)) { } CollectionDialog::~CollectionDialog() { delete d; } Akonadi::Collection CollectionDialog::selectedCollection() const { if (selectionMode() == QAbstractItemView::SingleSelection) { const QModelIndex index = d->mView->currentIndex(); if (index.isValid()) { return index.model()->data(index, EntityTreeModel::CollectionRole).value(); } } return Collection(); } Akonadi::Collection::List CollectionDialog::selectedCollections() const { Collection::List collections; const QItemSelectionModel *selectionModel = d->mView->selectionModel(); const QModelIndexList selectedIndexes = selectionModel->selectedIndexes(); for (const QModelIndex &index : selectedIndexes) { if (index.isValid()) { const Collection collection = index.model()->data(index, EntityTreeModel::CollectionRole).value(); if (collection.isValid()) { collections.append(collection); } } } return collections; } void CollectionDialog::setMimeTypeFilter(const QStringList &mimeTypes) { if (mimeTypeFilter() == mimeTypes) { return; } d->mMimeTypeFilterModel->clearFilters(); d->mMimeTypeFilterModel->addMimeTypeFilters(mimeTypes); if (d->mMonitor) { for (const QString &mimetype : mimeTypes) { d->mMonitor->setMimeTypeMonitored(mimetype); } } } QStringList CollectionDialog::mimeTypeFilter() const { return d->mMimeTypeFilterModel->mimeTypeFilters(); } void CollectionDialog::setAccessRightsFilter(Collection::Rights rights) { if (accessRightsFilter() == rights) { return; } d->mRightsFilterModel->setAccessRights(rights); } Akonadi::Collection::Rights CollectionDialog::accessRightsFilter() const { return d->mRightsFilterModel->accessRights(); } void CollectionDialog::setDescription(const QString &text) { d->mTextLabel->setText(text); d->mTextLabel->show(); } void CollectionDialog::setDefaultCollection(const Collection &collection) { d->mSelectionHandler->waitForCollection(collection); } void CollectionDialog::setSelectionMode(QAbstractItemView::SelectionMode mode) { d->mView->setSelectionMode(mode); } QAbstractItemView::SelectionMode CollectionDialog::selectionMode() const { return d->mView->selectionMode(); } void CollectionDialog::changeCollectionDialogOptions(CollectionDialogOptions options) { d->changeCollectionDialogOptions(options); } void CollectionDialog::setUseFolderByDefault(bool b) { d->mUseByDefault->setChecked(b); d->mUseByDefault->show(); } bool CollectionDialog::useFolderByDefault() const { return d->mUseByDefault->isChecked(); } void CollectionDialog::setContentMimeTypes(const QStringList &mimetypes) { d->mContentMimeTypes = mimetypes; } #include "moc_collectiondialog.cpp" diff --git a/src/widgets/collectiondialog.h b/src/widgets/collectiondialog.h index 02918d244..cc92eb9f2 100644 --- a/src/widgets/collectiondialog.h +++ b/src/widgets/collectiondialog.h @@ -1,222 +1,218 @@ /* Copyright 2008 Ingo Klöcker Copyright 2010-2020 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_COLLECTIONDIALOG_H #define AKONADI_COLLECTIONDIALOG_H #include "akonadiwidgets_export.h" #include "collection.h" #include #include namespace Akonadi { /** * @short A collection selection dialog. * * Provides a dialog that lists collections that are available * on the Akonadi storage and allows the selection of one or multiple * collections. * * The list of shown collections can be filtered by mime type and access * rights. Note that mime types are not enabled by default, so * setMimeTypeFilter() must be called to enable the desired mime types. * * Example: * * @code * * using namespace Akonadi; * * // Show the user a dialog to select a writable collection of contacts * CollectionDialog dlg( this ); * dlg.setMimeTypeFilter( QStringList() << KContacts::Addressee::mimeType() ); * dlg.setAccessRightsFilter( Collection::CanCreateItem ); * dlg.setDescription( i18n( "Select an address book for saving:" ) ); * * if ( dlg.exec() ) { * const Collection collection = dlg.selectedCollection(); * ... * } * * @endcode * * @author Ingo Klöcker * @since 4.3 */ class AKONADIWIDGETS_EXPORT CollectionDialog : public QDialog { Q_OBJECT Q_DISABLE_COPY(CollectionDialog) public: /* @since 4.6 */ enum CollectionDialogOption { None = 0, AllowToCreateNewChildCollection = 1, KeepTreeExpanded = 2 }; Q_DECLARE_FLAGS(CollectionDialogOptions, CollectionDialogOption) /** * Creates a new collection dialog. * * @param parent The parent widget. */ explicit CollectionDialog(QWidget *parent = nullptr); /** * Creates a new collection dialog with a custom @p model. * * The filtering by content mime type and access rights is done * on top of the custom model. * * @param model The custom model to use. * @param parent The parent widget. * * @since 4.4 */ explicit CollectionDialog(QAbstractItemModel *model, QWidget *parent = nullptr); /** * Creates a new collection dialog with a custom @p model. * * The filtering by content mime type and access rights is done * on top of the custom model. * * @param options The collection dialog options. * @param model The custom model to use. * @param parent The parent widget. * * @since 4.6 */ explicit CollectionDialog(CollectionDialogOptions options, QAbstractItemModel *model = nullptr, QWidget *parent = nullptr); /** * Destroys the collection dialog. */ ~CollectionDialog(); /** * Sets the mime types any of which the selected collection(s) shall support. * Note that mime types are not enabled by default. * @param mimeTypes MIME type filter values */ void setMimeTypeFilter(const QStringList &mimeTypes); /** * Returns the mime types any of which the selected collection(s) shall support. */ Q_REQUIRED_RESULT QStringList mimeTypeFilter() const; /** * Sets the access @p rights that the listed collections shall match with. * @param rights access rights filter values * @since 4.4 */ void setAccessRightsFilter(Collection::Rights rights); /** * Sets the access @p rights that the listed collections shall match with. * * @since 4.4 */ Q_REQUIRED_RESULT Collection::Rights accessRightsFilter() const; /** * Sets the @p text that will be shown in the dialog. * @param text the dialog's description text * @since 4.4 */ void setDescription(const QString &text); /** * Sets the @p collection that shall be selected by default. * @param collection the dialog's pre-selected collection * @since 4.4 */ void setDefaultCollection(const Collection &collection); /** * Sets the selection mode. The initial default mode is * QAbstractItemView::SingleSelection. * @param mode the selection mode to use * @see QAbstractItemView::setSelectionMode() */ void setSelectionMode(QAbstractItemView::SelectionMode mode); /** * Returns the selection mode. * @see QAbstractItemView::selectionMode() */ Q_REQUIRED_RESULT QAbstractItemView::SelectionMode selectionMode() const; /** * Returns the selected collection if the selection mode is * QAbstractItemView::SingleSelection. If another selection mode was set, * or nothing is selected, an invalid collection is returned. */ Q_REQUIRED_RESULT Akonadi::Collection selectedCollection() const; /** * Returns the list of selected collections. */ Q_REQUIRED_RESULT Akonadi::Collection::List selectedCollections() const; /** * Change collection dialog options. * @param options the collection dialog options to change * @since 4.6 */ void changeCollectionDialogOptions(CollectionDialogOptions options); /** * @since 4.13 */ void setUseFolderByDefault(bool b); /** * @since 4.13 */ Q_REQUIRED_RESULT bool useFolderByDefault() const; /** * Allow to specify collection content mimetype when we create new one. * @since 4.14.6 */ void setContentMimeTypes(const QStringList &mimetypes); private: //@cond PRIVATE class Private; Private *const d; - - Q_PRIVATE_SLOT(d, void slotSelectionChanged()) - Q_PRIVATE_SLOT(d, void slotAddChildCollection()) - Q_PRIVATE_SLOT(d, void slotDoubleClicked()) //@endcond }; } // namespace Akonadi #endif // AKONADI_COLLECTIONDIALOG_H diff --git a/src/widgets/collectionpropertiesdialog.cpp b/src/widgets/collectionpropertiesdialog.cpp index 6d1b7a758..902b0b7ab 100644 --- a/src/widgets/collectionpropertiesdialog.cpp +++ b/src/widgets/collectionpropertiesdialog.cpp @@ -1,234 +1,234 @@ /* Copyright (c) 2008 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "collectionpropertiesdialog.h" #include "cachepolicy.h" #include "cachepolicypage.h" #include "collection.h" #include "collectiongeneralpropertiespage_p.h" #include "collectionmodifyjob.h" #include "akonadiwidgets_debug.h" #include #include #include #include using namespace Akonadi; /** * @internal */ class Q_DECL_HIDDEN CollectionPropertiesDialog::Private { public: Private(CollectionPropertiesDialog *parent, const Akonadi::Collection &collection, const QStringList &pageNames); void init(); static void registerBuiltinPages(); void save() { const int numberOfTab(mTabWidget->count()); for (int i = 0; i < numberOfTab; ++i) { CollectionPropertiesPage *page = static_cast(mTabWidget->widget(i)); page->save(mCollection); } // We use WA_DeleteOnClose => Don't use dialog as parent otherwise we can't save modified collection. CollectionModifyJob *job = new CollectionModifyJob(mCollection); connect(job, &CollectionModifyJob::result, q, [this](KJob *job) { saveResult(job); }); Q_EMIT q->settingsSaved(); } void saveResult(KJob *job) { if (job->error()) { // TODO qCWarning(AKONADIWIDGETS_LOG) << job->errorString(); } } void setCurrentPage(const QString &name) { const int numberOfTab(mTabWidget->count()); for (int i = 0; i < numberOfTab; ++i) { QWidget *w = mTabWidget->widget(i); if (w->objectName() == name) { mTabWidget->setCurrentIndex(i); break; } } } CollectionPropertiesDialog *q = nullptr; Collection mCollection; QStringList mPageNames; QTabWidget *mTabWidget = nullptr; }; typedef QList CollectionPropertiesPageFactoryList; Q_GLOBAL_STATIC(CollectionPropertiesPageFactoryList, s_pages) static bool s_defaultPage = true; CollectionPropertiesDialog::Private::Private(CollectionPropertiesDialog *qq, const Akonadi::Collection &collection, const QStringList &pageNames) : q(qq) , mCollection(collection) , mPageNames(pageNames) , mTabWidget(nullptr) { if (s_defaultPage) { registerBuiltinPages(); } } void CollectionPropertiesDialog::Private::registerBuiltinPages() { static bool registered = false; if (registered) { return; } s_pages->append(new CollectionGeneralPropertiesPageFactory()); s_pages->append(new CachePolicyPageFactory()); registered = true; } void CollectionPropertiesDialog::Private::init() { QVBoxLayout *mainLayout = new QVBoxLayout(q); q->setAttribute(Qt::WA_DeleteOnClose); mTabWidget = new QTabWidget(q); mainLayout->addWidget(mTabWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, q); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); q->connect(buttonBox, &QDialogButtonBox::accepted, q, &QDialog::accept); q->connect(buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject); mainLayout->addWidget(buttonBox); if (mPageNames.isEmpty()) { // default loading - for (CollectionPropertiesPageFactory *factory : *s_pages) { + for (CollectionPropertiesPageFactory *factory : qAsConst(*s_pages)) { CollectionPropertiesPage *page = factory->createWidget(mTabWidget); if (page->canHandle(mCollection)) { mTabWidget->addTab(page, page->pageTitle()); page->load(mCollection); } else { delete page; } } } else { // custom loading QHash pages; - for (CollectionPropertiesPageFactory *factory : *s_pages) { + for (CollectionPropertiesPageFactory *factory : qAsConst(*s_pages)) { CollectionPropertiesPage *page = factory->createWidget(mTabWidget); const QString pageName = page->objectName(); if (page->canHandle(mCollection) && mPageNames.contains(pageName) && !pages.contains(pageName)) { pages.insert(page->objectName(), page); } else { delete page; } } for (const QString &pageName : qAsConst(mPageNames)) { CollectionPropertiesPage *page = pages.value(pageName); if (page) { mTabWidget->addTab(page, page->pageTitle()); page->load(mCollection); } } } q->connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, q, [this]() { save(); }); q->connect(buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::clicked, q, &QObject::deleteLater); KConfigGroup group(KSharedConfig::openConfig(), "CollectionPropertiesDialog"); const QSize size = group.readEntry("Size", QSize()); if (size.isValid()) { q->resize(size); } else { q->resize(q->sizeHint().width(), q->sizeHint().height()); } } CollectionPropertiesDialog::CollectionPropertiesDialog(const Collection &collection, QWidget *parent) : QDialog(parent) , d(new Private(this, collection, QStringList())) { d->init(); } CollectionPropertiesDialog::CollectionPropertiesDialog(const Collection &collection, const QStringList &pages, QWidget *parent) : QDialog(parent) , d(new Private(this, collection, pages)) { d->init(); } CollectionPropertiesDialog::~CollectionPropertiesDialog() { KConfigGroup group(KSharedConfig::openConfig(), "CollectionPropertiesDialog"); group.writeEntry("Size", size()); delete d; } void CollectionPropertiesDialog::registerPage(CollectionPropertiesPageFactory *factory) { if (s_pages->isEmpty() && s_defaultPage) { Private::registerBuiltinPages(); } s_pages->append(factory); } void CollectionPropertiesDialog::useDefaultPage(bool defaultPage) { s_defaultPage = defaultPage; } QString CollectionPropertiesDialog::defaultPageObjectName(DefaultPage page) { switch (page) { case GeneralPage: return QStringLiteral("Akonadi::CollectionGeneralPropertiesPage"); case CachePage: return QStringLiteral("Akonadi::CachePolicyPage"); } return QString(); } void CollectionPropertiesDialog::setCurrentPage(const QString &name) { d->setCurrentPage(name); } #include "moc_collectionpropertiesdialog.cpp" diff --git a/src/widgets/collectionpropertiespage.h b/src/widgets/collectionpropertiespage.h index 9072ad3ac..d320c29c6 100644 --- a/src/widgets/collectionpropertiespage.h +++ b/src/widgets/collectionpropertiespage.h @@ -1,220 +1,226 @@ /* Copyright (c) 2008 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_COLLECTIONPROPERTIESPAGE_H #define AKONADI_COLLECTIONPROPERTIESPAGE_H #include "akonadiwidgets_export.h" #include namespace Akonadi { class Collection; /** * @short A single page in a collection properties dialog. * * The collection properties dialog can be extended by custom * collection properties pages, which provide gui elements for * viewing and changing collection attributes. * * The following example shows how to create a simple collection * properties page for the secrecy attribute from the Akonadi::Attribute * example. * * @code * * class SecrecyPage : public CollectionPropertiesPage * { * public: * SecrecyPage( QWidget *parent = nullptr ) * : CollectionPropertiesPage( parent ) * { * QVBoxLayout *layout = new QVBoxLayout( this ); * * mSecrecy = new QComboBox( this ); * mSecrecy->addItem( "Public" ); * mSecrecy->addItem( "Private" ); * mSecrecy->addItem( "Confidential" ); * * layout->addWidget( new QLabel( "Secrecy:" ) ); * layout->addWidget( mSecrecy ); * * setPageTitle( i18n( "Secrecy" ) ); * } * * void load( const Collection &collection ) * { * SecrecyAttribute *attr = collection.attribute( "secrecy" ); * * switch ( attr->secrecy() ) { * case SecrecyAttribute::Public: mSecrecy->setCurrentIndex( 0 ); break; * case SecrecyAttribute::Private: mSecrecy->setCurrentIndex( 1 ); break; * case SecrecyAttribute::Confidential: mSecrecy->setCurrentIndex( 2 ); break; * } * } * * void save( Collection &collection ) * { * SecrecyAttribute *attr = collection.attribute( "secrecy" ); * * switch ( mSecrecy->currentIndex() ) { * case 0: attr->setSecrecy( SecrecyAttribute::Public ); break; * case 1: attr->setSecrecy( SecrecyAttribute::Private ); break; * case 2: attr->setSecrecy( SecrecyAttribute::Confidential ); break; * } * } * * bool canHandle( const Collection &collection ) const * { * return collection.hasAttribute( "secrecy" ); * } * }; * * AKONADI_COLLECTION_PROPERTIES_PAGE_FACTORY( SecrecyPageFactory, SecrecyPage ) * * @endcode * * @see Akonadi::CollectionPropertiesDialog, Akonadi::CollectionPropertiesPageFactory * * @author Volker Krause */ class AKONADIWIDGETS_EXPORT CollectionPropertiesPage : public QWidget { Q_OBJECT public: /** * Creates a new collection properties page. * * @param parent The parent widget. */ explicit CollectionPropertiesPage(QWidget *parent = nullptr); /** * Destroys the collection properties page. */ ~CollectionPropertiesPage(); /** * Loads the page content from the given collection. * * @param collection The collection to load. */ virtual void load(const Collection &collection) = 0; /** * Saves page content to the given collection. * * @param collection Reference to the collection to save to. */ virtual void save(Collection &collection) = 0; /** * Checks if this page can actually handle the given collection. * * Returns @c true if the collection can be handled, @c false otherwise * The default implementation returns always @c true. When @c false is returned * this page is not shown in the properties dialog. * @param collection The collection to check. */ virtual bool canHandle(const Collection &collection) const; /** * Sets the page title. * * @param title Translated, preferably short tab title. */ void setPageTitle(const QString &title); /** * Returns the page title. */ QString pageTitle() const; private: //@cond PRIVATE class Private; Private *const d; //@endcond }; /** * @short A factory class for collection properties dialog pages. * * The factory encapsulates the creation of the collection properties * dialog page. * You can use the AKONADI_COLLECTION_PROPERTIES_PAGE_FACTORY macro * to create a factory class automatically. * * @author Volker Krause */ class AKONADIWIDGETS_EXPORT CollectionPropertiesPageFactory { public: /** * Destroys the collection properties page factory. */ virtual ~CollectionPropertiesPageFactory(); /** * Returns the actual page widget. * * @param parent The parent widget. */ virtual CollectionPropertiesPage *createWidget(QWidget *parent = nullptr) const = 0; + +protected: + explicit CollectionPropertiesPageFactory() = default; + +private: + Q_DISABLE_COPY_MOVE(CollectionPropertiesPageFactory) }; /** * @def AKONADI_COLLECTION_PROPERTIES_PAGE_FACTORY * * The AKONADI_COLLECTION_PROPERTIES_PAGE_FACTORY macro can be used to * create a factory for a custom collection properties page. * * @code * * class MyPage : public Akonadi::CollectionPropertiesPage * { * ... * } * * AKONADI_COLLECTION_PROPERTIES_PAGE_FACTORY( MyPageFactory, MyPage ) * * @endcode * * The macro takes two arguments, where the first one is the name of the * factory class that shall be created and the second arguments is the name * of the custom collection properties page class. * * @ingroup AkonadiMacros */ #define AKONADI_COLLECTION_PROPERTIES_PAGE_FACTORY(factoryName, className) \ class factoryName : public Akonadi::CollectionPropertiesPageFactory \ { \ public: \ inline Akonadi::CollectionPropertiesPage *createWidget( QWidget *parent = nullptr ) const override \ { \ return new className( parent ); \ } \ }; } #endif diff --git a/src/widgets/collectionstatisticsdelegate.h b/src/widgets/collectionstatisticsdelegate.h index ee94ae24e..8e07b366b 100644 --- a/src/widgets/collectionstatisticsdelegate.h +++ b/src/widgets/collectionstatisticsdelegate.h @@ -1,147 +1,146 @@ /* Copyright (c) 2008 Thomas McGuire This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_COLLECTIONSTATISTICSDELEGATE_H #define AKONADI_COLLECTIONSTATISTICSDELEGATE_H #include "akonadiwidgets_export.h" #include class QAbstractItemView; class QTreeView; namespace Akonadi { class CollectionStatisticsDelegatePrivate; /** * @short A delegate that draws unread and total count for StatisticsProxyModel. * * The delegate provides the following features: * * - Collections with unread items will have the foldername and the unread * column marked in bold. * - If a folder is collapsed, the unread and the total column will contain * the total sum of all child folders * - It has the possibility to draw the unread count directly after the * foldername, see toggleUnreadAfterFolderName(). * * Example: * @code * * Akonadi::EntityTreeView *view = new Akonadi::EntityTreeView( this ); * * Akonadi::StatisticsProxyModel *statisticsProxy = new Akonadi::StatisticsProxyModel( view ); * view->setModel( statisticsProxy ); * * Akonadi::CollectionStatisticsDelegate *delegate = new Akonadi::CollectionStatisticsDelegate( view ); * view->setItemDelegate( delegate ); * * @endcode * * @note This proxy model is intended to be used on top of the EntityTreeModel. One of the proxies * between the EntityTreeModel (the root model) and the view must be a StatisticsProxyModel. That * proxy model may appear anywhere in the chain. * * @author Thomas McGuire */ class AKONADIWIDGETS_EXPORT CollectionStatisticsDelegate : public QStyledItemDelegate { Q_OBJECT public: /** * Creates a new collection statistics delegate. * * @param parent The parent item view, which will also take ownership. * * @since 4.6 */ explicit CollectionStatisticsDelegate(QAbstractItemView *parent); /** * Creates a new collection statistics delegate. * * @param parent The parent tree view, which will also take ownership. */ explicit CollectionStatisticsDelegate(QTreeView *parent); /** * Destroys the collection statistics delegate. */ ~CollectionStatisticsDelegate() override; /** * @since 4.9.1 */ void updatePalette(); -public Q_SLOTS: /** * Sets whether the unread count is drawn next to the folder name. * * You probably want to enable this when the unread count is hidden only. * This is disabled by default. * * @param enable If @c true, the unread count is drawn next to the folder name, * if @c false, the folder name will be drawn normally. */ void setUnreadCountShown(bool enable); /** * Returns whether the unread count is drawn next to the folder name. */ bool unreadCountShown() const; /** * @param enable new mode of progress animation */ void setProgressAnimationEnabled(bool enable); bool progressAnimationEnabled() const; protected: /** * @param painter pointer for QPainter to use in method * @param option style options * @param index model index (QModelIndex) */ void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; /** * @param option style option view item * @param index model index (QModelIndex) */ void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override; private: //@cond PRIVATE CollectionStatisticsDelegatePrivate *const d_ptr; //@endcond Q_DECLARE_PRIVATE(CollectionStatisticsDelegate) }; } #endif diff --git a/src/widgets/collectionview.cpp b/src/widgets/collectionview.cpp index 078870892..490ae245a 100644 --- a/src/widgets/collectionview.cpp +++ b/src/widgets/collectionview.cpp @@ -1,263 +1,263 @@ /* Copyright (c) 2006 - 2007 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "collectionview.h" #include "collection.h" #include "controlgui.h" #include "entitytreemodel.h" #include "akonadiwidgets_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace Akonadi; /** * @internal */ class Q_DECL_HIDDEN CollectionView::Private { public: Private(CollectionView *parent) : mParent(parent) { } void init(); void dragExpand(); void itemClicked(const QModelIndex &index); void itemCurrentChanged(const QModelIndex &index); bool hasParent(const QModelIndex &idx, Collection::Id parentId); CollectionView *mParent = nullptr; QModelIndex dragOverIndex; QTimer dragExpandTimer; KXMLGUIClient *xmlGuiClient = nullptr; }; void CollectionView::Private::init() { mParent->header()->setSectionsClickable(true); mParent->header()->setStretchLastSection(false); mParent->setSortingEnabled(true); mParent->sortByColumn(0, Qt::AscendingOrder); mParent->setEditTriggers(QAbstractItemView::EditKeyPressed); mParent->setAcceptDrops(true); mParent->setDropIndicatorShown(true); mParent->setDragDropMode(DragDrop); mParent->setDragEnabled(true); dragExpandTimer.setSingleShot(true); mParent->connect(&dragExpandTimer, &QTimer::timeout, mParent, [this]() { dragExpand(); }); - mParent->connect(mParent, SIGNAL(clicked(QModelIndex)), mParent, SLOT(itemClicked(QModelIndex))); + mParent->connect(mParent, &QAbstractItemView::clicked, mParent, [this](const QModelIndex &mi) { itemClicked(mi); }); ControlGui::widgetNeedsAkonadi(mParent); } bool CollectionView::Private::hasParent(const QModelIndex &idx, Collection::Id parentId) { QModelIndex idx2 = idx; while (idx2.isValid()) { if (mParent->model()->data(idx2, EntityTreeModel::CollectionIdRole).toLongLong() == parentId) { return true; } idx2 = idx2.parent(); } return false; } void CollectionView::Private::dragExpand() { mParent->setExpanded(dragOverIndex, true); dragOverIndex = QModelIndex(); } void CollectionView::Private::itemClicked(const QModelIndex &index) { if (!index.isValid()) { return; } const Collection collection = index.model()->data(index, EntityTreeModel::CollectionRole).value(); if (!collection.isValid()) { return; } Q_EMIT mParent->clicked(collection); } void CollectionView::Private::itemCurrentChanged(const QModelIndex &index) { if (!index.isValid()) { return; } const Collection collection = index.model()->data(index, EntityTreeModel::CollectionRole).value(); if (!collection.isValid()) { return; } Q_EMIT mParent->currentChanged(collection); } CollectionView::CollectionView(QWidget *parent) : QTreeView(parent) , d(new Private(this)) { d->init(); } CollectionView::CollectionView(KXMLGUIClient *xmlGuiClient, QWidget *parent) : QTreeView(parent) , d(new Private(this)) { d->xmlGuiClient = xmlGuiClient; d->init(); } CollectionView::~CollectionView() { delete d; } void CollectionView::setModel(QAbstractItemModel *model) { QTreeView::setModel(model); header()->setStretchLastSection(true); - connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this, SLOT(itemCurrentChanged(QModelIndex))); + connect(selectionModel(), &QItemSelectionModel::currentChanged, + this, [this](const QModelIndex &mi) { d->itemCurrentChanged(mi); }); } void CollectionView::dragMoveEvent(QDragMoveEvent *event) { QModelIndex index = indexAt(event->pos()); if (d->dragOverIndex != index) { d->dragExpandTimer.stop(); if (index.isValid() && !isExpanded(index) && itemsExpandable()) { d->dragExpandTimer.start(QApplication::startDragTime()); d->dragOverIndex = index; } } // Check if the collection under the cursor accepts this data type const QStringList supportedContentTypes = model()->data(index, EntityTreeModel::CollectionRole).value().contentMimeTypes(); const QMimeData *mimeData = event->mimeData(); if (!mimeData) { return; } const QList urls = mimeData->urls(); for (const QUrl &url : urls) { const Collection collection = Collection::fromUrl(url); if (collection.isValid()) { if (!supportedContentTypes.contains(QLatin1String("inode/directory"))) { break; } // Check if we don't try to drop on one of the children if (d->hasParent(index, collection.id())) { break; } } else { const QList > query = QUrlQuery(url).queryItems(); const int numberOfQuery(query.count()); for (int i = 0; i < numberOfQuery; ++i) { if (query.at(i).first == QLatin1String("type")) { const QString type = query.at(i).second; if (!supportedContentTypes.contains(type)) { break; } } } } QTreeView::dragMoveEvent(event); return; } event->setDropAction(Qt::IgnoreAction); } void CollectionView::dragLeaveEvent(QDragLeaveEvent *event) { d->dragExpandTimer.stop(); d->dragOverIndex = QModelIndex(); QTreeView::dragLeaveEvent(event); } void CollectionView::dropEvent(QDropEvent *event) { d->dragExpandTimer.stop(); d->dragOverIndex = QModelIndex(); // open a context menu offering different drop actions (move, copy and cancel) // TODO If possible, hide non available actions ... QMenu popup(this); QAction *moveDropAction = popup.addAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18n("&Move here")); QAction *copyDropAction = popup.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("&Copy here")); popup.addSeparator(); popup.addAction(QIcon::fromTheme(QStringLiteral("process-stop")), i18n("Cancel")); QAction *activatedAction = popup.exec(QCursor::pos()); if (activatedAction == moveDropAction) { event->setDropAction(Qt::MoveAction); } else if (activatedAction == copyDropAction) { event->setDropAction(Qt::CopyAction); } else { return; } QTreeView::dropEvent(event); } void CollectionView::contextMenuEvent(QContextMenuEvent *event) { if (!d->xmlGuiClient) { return; } QMenu *popup = static_cast(d->xmlGuiClient->factory()->container( QStringLiteral("akonadi_collectionview_contextmenu"), d->xmlGuiClient)); if (popup) { popup->exec(event->globalPos()); } } void CollectionView::setXmlGuiClient(KXMLGUIClient *xmlGuiClient) { d->xmlGuiClient = xmlGuiClient; } #include "moc_collectionview.cpp" diff --git a/src/widgets/conflictresolvedialog.cpp b/src/widgets/conflictresolvedialog.cpp index 365100620..4e98c9a3c 100644 --- a/src/widgets/conflictresolvedialog.cpp +++ b/src/widgets/conflictresolvedialog.cpp @@ -1,314 +1,312 @@ /* Copyright (c) 2010 KDAB Author: Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "conflictresolvedialog_p.h" #include "abstractdifferencesreporter.h" #include "differencesalgorithminterface.h" #include "typepluginloader_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Akonadi; using namespace AkRanges; static inline QString textToHTML(const QString &text) { return Qt::convertFromPlainText(text); } class HtmlDifferencesReporter : public AbstractDifferencesReporter { public: - HtmlDifferencesReporter() - { - } + HtmlDifferencesReporter() = default; QString toHtml() const { return header() + mContent + footer(); } QString plainText() const { return mTextContent; } void setPropertyNameTitle(const QString &title) override { mNameTitle = title; } void setLeftPropertyValueTitle(const QString &title) override { mLeftTitle = title; } void setRightPropertyValueTitle(const QString &title) override { mRightTitle = title; } void addProperty(Mode mode, const QString &name, const QString &leftValue, const QString &rightValue) override { switch (mode) { case NormalMode: mContent.append(QStringLiteral("%1:%2%3") .arg(name, textToHTML(leftValue), textToHTML(rightValue))); mTextContent.append(QStringLiteral("%1:\n%2\n%3\n\n").arg(name, leftValue, rightValue)); break; case ConflictMode: mContent.append(QStringLiteral("%1:%2%3") .arg(name, textToHTML(leftValue), textToHTML(rightValue))); mTextContent.append(QStringLiteral("%1:\n%2\n%3\n\n").arg(name, leftValue, rightValue)); break; case AdditionalLeftMode: mContent.append(QStringLiteral("%1:%2") .arg(name, textToHTML(leftValue))); mTextContent.append(QStringLiteral("%1:\n%2\n\n").arg(name, leftValue)); break; case AdditionalRightMode: mContent.append(QStringLiteral("%1:%2") .arg(name, textToHTML(rightValue))); mTextContent.append(QStringLiteral("%1:\n%2\n\n").arg(name, rightValue)); break; } } private: QString header() const { QString header = QStringLiteral(""); header += QStringLiteral("") .arg(KColorScheme(QPalette::Active, KColorScheme::View).foreground().color().name(), KColorScheme(QPalette::Active, KColorScheme::View).background().color().name()); header += QLatin1String("
"); header += QStringLiteral("") .arg(mNameTitle, mLeftTitle, mRightTitle); return header; } QString footer() const { return QStringLiteral("
%1%2 %3
" "" ""); } QString mContent; QString mNameTitle; QString mLeftTitle; QString mRightTitle; QString mTextContent; }; static void compareItems(AbstractDifferencesReporter *reporter, const Akonadi::Item &localItem, const Akonadi::Item &otherItem) { if (localItem.modificationTime() != otherItem.modificationTime()) { reporter->addProperty(AbstractDifferencesReporter::ConflictMode, i18n("Modification Time"), QLocale().toString(localItem.modificationTime(), QLocale::ShortFormat), QLocale().toString(otherItem.modificationTime(), QLocale::ShortFormat)); } if (localItem.flags() != otherItem.flags()) { const auto toQString = [](const QByteArray &s) { return QString::fromUtf8(s); }; const auto localFlags = localItem.flags() | Views::transform(toQString) | Actions::toQList; const auto otherFlags = otherItem.flags() | Views::transform(toQString) | Actions::toQList; reporter->addProperty(AbstractDifferencesReporter::ConflictMode, i18n("Flags"), localFlags.join(QLatin1String(", ")), otherFlags.join(QLatin1String(", "))); } const auto toPair = [](Attribute *attr) { return std::pair{attr->type(), attr->serialized()}; }; const auto localAttributes = localItem.attributes() | Views::transform(toPair) | Actions::toQHash; const auto otherAttributes = otherItem.attributes() | Views::transform(toPair) | Actions::toQHash; if (localAttributes != otherAttributes) { for (const QByteArray &localKey : localAttributes) { if (!otherAttributes.contains(localKey)) { reporter->addProperty(AbstractDifferencesReporter::AdditionalLeftMode, i18n("Attribute: %1", QString::fromUtf8(localKey)), QString::fromUtf8(localAttributes.value(localKey)), QString()); } else { const QByteArray localValue = localAttributes.value(localKey); const QByteArray otherValue = otherAttributes.value(localKey); if (localValue != otherValue) { reporter->addProperty(AbstractDifferencesReporter::ConflictMode, i18n("Attribute: %1", QString::fromUtf8(localKey)), QString::fromUtf8(localValue), QString::fromUtf8(otherValue)); } } } for (const QByteArray &otherKey : otherAttributes) { if (!localAttributes.contains(otherKey)) { reporter->addProperty(AbstractDifferencesReporter::AdditionalRightMode, i18n("Attribute: %1", QString::fromUtf8(otherKey)), QString(), QString::fromUtf8(otherAttributes.value(otherKey))); } } } } ConflictResolveDialog::ConflictResolveDialog(QWidget *parent) : QDialog(parent), mResolveStrategy(ConflictHandler::UseBothItems) { setWindowTitle(i18nc("@title:window", "Conflict Resolution")); QVBoxLayout *mainLayout = new QVBoxLayout(this); // Don't use QDialogButtonBox, order is very important (left on the left, right on the right) QHBoxLayout *buttonLayout = new QHBoxLayout(); QPushButton *takeLeftButton = new QPushButton(this); takeLeftButton->setText(i18nc("@action:button", "Take my version")); connect(takeLeftButton, &QPushButton::clicked, this, &ConflictResolveDialog::slotUseLocalItemChoosen); buttonLayout->addWidget(takeLeftButton); takeLeftButton->setObjectName(QStringLiteral("takeLeftButton")); QPushButton *takeRightButton = new QPushButton(this); takeRightButton->setText(i18nc("@action:button", "Take their version")); takeRightButton->setObjectName(QStringLiteral("takeRightButton")); connect(takeRightButton, &QPushButton::clicked, this, &ConflictResolveDialog::slotUseOtherItemChoosen); buttonLayout->addWidget(takeRightButton); QPushButton *keepBothButton = new QPushButton(this); keepBothButton->setText(i18nc("@action:button", "Keep both versions")); keepBothButton->setObjectName(QStringLiteral("keepBothButton")); buttonLayout->addWidget(keepBothButton); connect(keepBothButton, &QPushButton::clicked, this, &ConflictResolveDialog::slotUseBothItemsChoosen); keepBothButton->setDefault(true); mView = new QTextBrowser(this); mView->setObjectName(QStringLiteral("view")); mView->setOpenLinks(false); QLabel *docuLabel = new QLabel(i18n("Your changes conflict with those made by someone else meanwhile.
" "Unless one version can just be thrown away, you will have to integrate those changes manually.
" "Click on \"Open text editor\" to keep a copy of the texts, then select which version is most correct, then re-open it and modify it again to add what's missing.")); connect(docuLabel, &QLabel::linkActivated, this, &ConflictResolveDialog::slotOpenEditor); docuLabel->setContextMenuPolicy(Qt::NoContextMenu); docuLabel->setWordWrap(true); docuLabel->setObjectName(QStringLiteral("doculabel")); mainLayout->addWidget(mView); mainLayout->addWidget(docuLabel); mainLayout->addLayout(buttonLayout); // default size is tiny, and there's usually lots of text, so make it much bigger create(); // ensure a window is created const QSize availableSize = windowHandle()->screen()->availableSize(); windowHandle()->resize(availableSize.width() * 0.7, availableSize.height() * 0.5); KWindowConfig::restoreWindowSize(windowHandle(), KSharedConfig::openConfig()->group("ConflictResolveDialog")); resize(windowHandle()->size()); // workaround for QTBUG-40584 } ConflictResolveDialog::~ConflictResolveDialog() { KConfigGroup group(KSharedConfig::openConfig()->group("ConflictResolveDialog")); KWindowConfig::saveWindowSize(windowHandle(), group); } void ConflictResolveDialog::setConflictingItems(const Akonadi::Item &localItem, const Akonadi::Item &otherItem) { mLocalItem = localItem; mOtherItem = otherItem; HtmlDifferencesReporter reporter; compareItems(&reporter, localItem, otherItem); if (mLocalItem.hasPayload() && mOtherItem.hasPayload()) { QObject *object = TypePluginLoader::objectForMimeTypeAndClass(localItem.mimeType(), localItem.availablePayloadMetaTypeIds()); if (object) { DifferencesAlgorithmInterface *algorithm = qobject_cast(object); if (algorithm) { algorithm->compare(&reporter, localItem, otherItem); mView->setHtml(reporter.toHtml()); mTextContent = reporter.plainText(); return; } } reporter.addProperty(HtmlDifferencesReporter::NormalMode, i18n("Data"), QString::fromUtf8(mLocalItem.payloadData()), QString::fromUtf8(mOtherItem.payloadData())); } mView->setHtml(reporter.toHtml()); mTextContent = reporter.plainText(); } void ConflictResolveDialog::slotOpenEditor() { QTemporaryFile file(QDir::tempPath() + QStringLiteral("/akonadi-XXXXXX.txt")); if (file.open()) { file.setAutoRemove(false); file.write(mTextContent.toLocal8Bit()); const QString fileName = file.fileName(); file.close(); QDesktopServices::openUrl(QUrl::fromLocalFile(fileName)); } } ConflictHandler::ResolveStrategy ConflictResolveDialog::resolveStrategy() const { return mResolveStrategy; } void ConflictResolveDialog::slotUseLocalItemChoosen() { mResolveStrategy = ConflictHandler::UseLocalItem; accept(); } void ConflictResolveDialog::slotUseOtherItemChoosen() { mResolveStrategy = ConflictHandler::UseOtherItem; accept(); } void ConflictResolveDialog::slotUseBothItemsChoosen() { mResolveStrategy = ConflictHandler::UseBothItems; accept(); } #include "moc_conflictresolvedialog_p.cpp" diff --git a/src/widgets/controlgui.cpp b/src/widgets/controlgui.cpp index 7e29a2d7e..2144d8888 100644 --- a/src/widgets/controlgui.cpp +++ b/src/widgets/controlgui.cpp @@ -1,276 +1,273 @@ /* Copyright (c) 2007 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "controlgui.h" #include "servermanager.h" #include "ui_controlprogressindicator.h" #include "selftestdialog.h" #include "erroroverlay_p.h" #include "akonadiwidgets_debug.h" #include #include #include #include #include #include using namespace Akonadi; namespace Akonadi { namespace Internal { class ControlProgressIndicator : public QFrame { + Q_OBJECT public: ControlProgressIndicator(QWidget *parent = nullptr) : QFrame(parent) { setWindowModality(Qt::ApplicationModal); resize(400, 100); setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog); ui.setupUi(this); setFrameShadow(QFrame::Plain); setFrameShape(QFrame::Box); } void setMessage(const QString &msg) { ui.statusLabel->setText(msg); } Ui::ControlProgressIndicator ui; }; class StaticControlGui : public ControlGui { -public: - StaticControlGui() - : ControlGui() - { - } + Q_OBJECT }; } Q_GLOBAL_STATIC(Internal::StaticControlGui, s_instance) /** * @internal */ class Q_DECL_HIDDEN ControlGui::Private { public: Private(ControlGui *parent) : mParent(parent) , mEventLoop(nullptr) , mProgressIndicator(nullptr) , mSuccess(false) , mStarting(false) , mStopping(false) { } ~Private() { delete mProgressIndicator; } void setupProgressIndicator(const QString &msg, QWidget *parent = nullptr) { if (!mProgressIndicator) { mProgressIndicator = new Internal::ControlProgressIndicator(parent); } mProgressIndicator->setMessage(msg); } void createErrorOverlays() { for (QWidget *widget : qAsConst(mPendingOverlays)) { if (widget) { new ErrorOverlay(widget); } } mPendingOverlays.clear(); } void cleanup() { //delete s_instance; } bool exec(); void serverStateChanged(ServerManager::State state); QPointer mParent; QEventLoop *mEventLoop = nullptr; QPointer mProgressIndicator; QList > mPendingOverlays; bool mSuccess; bool mStarting; bool mStopping; }; bool ControlGui::Private::exec() { if (mProgressIndicator) { mProgressIndicator->show(); } qCDebug(AKONADIWIDGETS_LOG) << "Starting/Stopping Akonadi (using an event loop)."; mEventLoop = new QEventLoop(mParent); mEventLoop->exec(); mEventLoop->deleteLater(); mEventLoop = nullptr; if (!mSuccess) { qCWarning(AKONADIWIDGETS_LOG) << "Could not start/stop Akonadi!"; if (mProgressIndicator && mStarting) { QPointer dlg = new SelfTestDialog(mProgressIndicator->parentWidget()); dlg->exec(); delete dlg; if (!mParent) { return false; } } } delete mProgressIndicator; mProgressIndicator = nullptr; mStarting = false; mStopping = false; const bool rv = mSuccess; mSuccess = false; return rv; } void ControlGui::Private::serverStateChanged(ServerManager::State state) { qCDebug(AKONADIWIDGETS_LOG) << "Server state changed to" << state; if (mEventLoop && mEventLoop->isRunning()) { // ignore transient states going into the right direction if ((mStarting && (state == ServerManager::Starting || state == ServerManager::Upgrading)) || (mStopping && state == ServerManager::Stopping)) { return; } mEventLoop->quit(); mSuccess = (mStarting && state == ServerManager::Running) || (mStopping && state == ServerManager::NotRunning); } } ControlGui::ControlGui() : d(new Private(this)) { connect(ServerManager::self(), &ServerManager::stateChanged, this, [this](Akonadi::ServerManager::State state) { d->serverStateChanged(state); }); // mProgressIndicator is a widget, so it better be deleted before the QApplication is deleted // Otherwise we get a crash in QCursor code with Qt-4.5 if (QCoreApplication::instance()) { connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, [this]() {d->cleanup();}); } } ControlGui::~ControlGui() { delete d; } bool ControlGui::start() { if (ServerManager::state() == ServerManager::Stopping) { qCDebug(AKONADIWIDGETS_LOG) << "Server is currently being stopped, wont try to start it now"; return false; } if (ServerManager::isRunning() || s_instance->d->mEventLoop) { qCDebug(AKONADIWIDGETS_LOG) << "Server is already running"; return true; } s_instance->d->mStarting = true; if (!ServerManager::start()) { qCDebug(AKONADIWIDGETS_LOG) << "ServerManager::start failed -> return false"; return false; } return s_instance->d->exec(); } bool ControlGui::stop() { if (ServerManager::state() == ServerManager::Starting) { return false; } if (!ServerManager::isRunning() || s_instance->d->mEventLoop) { return true; } s_instance->d->mStopping = true; if (!ServerManager::stop()) { return false; } return s_instance->d->exec(); } bool ControlGui::restart() { if (ServerManager::isRunning()) { if (!stop()) { return false; } } return start(); } bool ControlGui::start(QWidget *parent) { s_instance->d->setupProgressIndicator(i18n("Starting Akonadi server..."), parent); return start(); } bool ControlGui::stop(QWidget *parent) { s_instance->d->setupProgressIndicator(i18n("Stopping Akonadi server..."), parent); return stop(); } bool ControlGui::restart(QWidget *parent) { if (ServerManager::isRunning()) { if (!stop(parent)) { return false; } } return start(parent); } void ControlGui::widgetNeedsAkonadi(QWidget *widget) { s_instance->d->mPendingOverlays.append(widget); // delay the overlay creation since we rely on widget being reparented // correctly already - QTimer::singleShot(0, s_instance, SLOT(createErrorOverlays())); + QTimer::singleShot(0, s_instance, []() { s_instance->d->createErrorOverlays(); }); } } -#include "moc_controlgui.cpp" +#include "controlgui.moc" diff --git a/src/widgets/controlgui.h b/src/widgets/controlgui.h index 1267be01b..1950f3f7e 100644 --- a/src/widgets/controlgui.h +++ b/src/widgets/controlgui.h @@ -1,144 +1,142 @@ /* Copyright (c) 2007 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_ControlGuiGUI_H #define AKONADI_ControlGuiGUI_H #include "akonadiwidgets_export.h" #include namespace Akonadi { /** * @short Provides methods to ControlGui the Akonadi server process. * * This class provides synchronous methods (ie. use a sub-eventloop) * to ControlGui the Akonadi service. For asynchronous methods see * Akonadi::ServerManager. * * The most important method in here is widgetNeedsAkonadi(). It is * recommended to call it with every top-level widget of your application * as argument, assuming your application relies on Akonadi being operational * of course. * * While the Akonadi server automatically started by Akonadi::Session * on first use, it might be necessary for some use-cases to guarantee * a running Akonadi service at some point. This can be done using * start(). * * Example: * * @code * * if ( !Akonadi::ControlGui::start() ) { * qDebug() << "Unable to start Akonadi server, exit application"; * return 1; * } else { * ... * } * * @endcode * * @author Volker Krause * * @see Akonadi::ServerManager */ class AKONADIWIDGETS_EXPORT ControlGui : public QObject { Q_OBJECT public: /** * Destroys the ControlGui object. */ ~ControlGui(); /** * Starts the Akonadi server synchronously if it is not already running. * @return @c true if the server was started successfully or was already * running, @c false otherwise */ static bool start(); /** * Same as start(), but with GUI feedback. * @param parent The parent widget. * @since 4.2 */ static bool start(QWidget *parent); /** * Stops the Akonadi server synchronously if it is currently running. * @return @c true if the server was shutdown successfully or was * not running at all, @c false otherwise. * @since 4.2 */ static bool stop(); /** * Same as stop(), but with GUI feedback. * @param parent The parent widget. * @since 4.2 */ static bool stop(QWidget *parent); /** * Restarts the Akonadi server synchronously. * @return @c true if the restart was successful, @c false otherwise, * the server state is undefined in this case. * @since 4.2 */ static bool restart(); /** * Same as restart(), but with GUI feedback. * @param parent The parent widget. * @since 4.2 */ static bool restart(QWidget *parent); /** * Disable the given widget when Akonadi is not operational and show * an error overlay (given enough space). Cascading use is automatically * detected and resolved. * @param widget The widget depending on Akonadi being operational. * @since 4.2 */ static void widgetNeedsAkonadi(QWidget *widget); protected: /** * Creates the ControlGui object. */ ControlGui(); private: //@cond PRIVATE class Private; Private *const d; - - Q_PRIVATE_SLOT(d, void createErrorOverlays()) //@endcond }; } #endif diff --git a/src/widgets/entitylistview.cpp b/src/widgets/entitylistview.cpp index 4c67a5179..9a1626239 100644 --- a/src/widgets/entitylistview.cpp +++ b/src/widgets/entitylistview.cpp @@ -1,252 +1,251 @@ /* Copyright (c) 2006 - 2007 Volker Krause Copyright (c) 2008 Stephen Kelly Copyright (c) 2009 Kevin Ottens This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "entitylistview.h" #include "dragdropmanager_p.h" #include #include #include "akonadiwidgets_debug.h" #include #include #include "collection.h" #include "controlgui.h" #include "item.h" #include "entitytreemodel.h" #include "progressspinnerdelegate_p.h" using namespace Akonadi; /** * @internal */ class Q_DECL_HIDDEN EntityListView::Private { public: Private(EntityListView *parent) : mParent(parent) #ifndef QT_NO_DRAGANDDROP , mDragDropManager(new DragDropManager(mParent)) #endif { } void init(); void itemClicked(const QModelIndex &index); void itemDoubleClicked(const QModelIndex &index); void itemCurrentChanged(const QModelIndex &index); EntityListView *mParent = nullptr; DragDropManager *mDragDropManager = nullptr; KXMLGUIClient *mXmlGuiClient = nullptr; }; void EntityListView::Private::init() { mParent->setEditTriggers(QAbstractItemView::EditKeyPressed); mParent->setAcceptDrops(true); #ifndef QT_NO_DRAGANDDROP mParent->setDropIndicatorShown(true); mParent->setDragDropMode(DragDrop); mParent->setDragEnabled(true); #endif - mParent->connect(mParent, SIGNAL(clicked(QModelIndex)), mParent, SLOT(itemClicked(QModelIndex))); - mParent->connect(mParent, SIGNAL(doubleClicked(QModelIndex)), mParent, SLOT(itemDoubleClicked(QModelIndex))); + mParent->connect(mParent, &QAbstractItemView::clicked, mParent, [this](const auto &index) { itemClicked(index); }); + mParent->connect(mParent, &QAbstractItemView::doubleClicked, mParent, [this](const auto &index) { itemDoubleClicked(index); }); DelegateAnimator *animator = new DelegateAnimator(mParent); ProgressSpinnerDelegate *customDelegate = new ProgressSpinnerDelegate(animator, mParent); mParent->setItemDelegate(customDelegate); ControlGui::widgetNeedsAkonadi(mParent); } void EntityListView::Private::itemClicked(const QModelIndex &index) { if (!index.isValid()) { return; } const Collection collection = index.model()->data(index, EntityTreeModel::CollectionRole).value(); if (collection.isValid()) { Q_EMIT mParent->clicked(collection); } else { const Item item = index.model()->data(index, EntityTreeModel::ItemRole).value(); if (item.isValid()) { Q_EMIT mParent->clicked(item); } } } void EntityListView::Private::itemDoubleClicked(const QModelIndex &index) { if (!index.isValid()) { return; } const Collection collection = index.model()->data(index, EntityTreeModel::CollectionRole).value(); if (collection.isValid()) { Q_EMIT mParent->doubleClicked(collection); } else { const Item item = index.model()->data(index, EntityTreeModel::ItemRole).value(); if (item.isValid()) { Q_EMIT mParent->doubleClicked(item); } } } void EntityListView::Private::itemCurrentChanged(const QModelIndex &index) { if (!index.isValid()) { return; } const Collection collection = index.model()->data(index, EntityTreeModel::CollectionRole).value(); if (collection.isValid()) { Q_EMIT mParent->currentChanged(collection); } else { const Item item = index.model()->data(index, EntityTreeModel::ItemRole).value(); if (item.isValid()) { Q_EMIT mParent->currentChanged(item); } } } EntityListView::EntityListView(QWidget *parent) : QListView(parent) , d(new Private(this)) { setSelectionMode(QAbstractItemView::SingleSelection); d->init(); } EntityListView::EntityListView(KXMLGUIClient *xmlGuiClient, QWidget *parent) : QListView(parent) , d(new Private(this)) { d->mXmlGuiClient = xmlGuiClient; d->init(); } EntityListView::~EntityListView() { delete d->mDragDropManager; delete d; } void EntityListView::setModel(QAbstractItemModel *model) { if (selectionModel()) { - disconnect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this, SLOT(itemCurrentChanged(QModelIndex))); + disconnect(selectionModel(), &QItemSelectionModel::currentChanged, this, nullptr); } QListView::setModel(model); - connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), - SLOT(itemCurrentChanged(QModelIndex))); + connect(selectionModel(), &QItemSelectionModel::currentChanged, + this, [this](const QModelIndex &index) { d->itemCurrentChanged(index); }); } #ifndef QT_NO_DRAGANDDROP void EntityListView::dragMoveEvent(QDragMoveEvent *event) { if (d->mDragDropManager->dropAllowed(event)) { // All urls are supported. process the event. QListView::dragMoveEvent(event); return; } event->setDropAction(Qt::IgnoreAction); } void EntityListView::dropEvent(QDropEvent *event) { bool menuCanceled = false; if (d->mDragDropManager->processDropEvent(event, menuCanceled) && !menuCanceled) { QListView::dropEvent(event); } } #endif #ifndef QT_NO_CONTEXTMENU void EntityListView::contextMenuEvent(QContextMenuEvent *event) { if (!d->mXmlGuiClient) { return; } const QModelIndex index = indexAt(event->pos()); QMenu *popup = nullptr; // check if the index under the cursor is a collection or item const Collection collection = model()->data(index, EntityTreeModel::CollectionRole).value(); if (collection.isValid()) { popup = static_cast(d->mXmlGuiClient->factory()->container( QStringLiteral("akonadi_favoriteview_contextmenu"), d->mXmlGuiClient)); } else { popup = static_cast(d->mXmlGuiClient->factory()->container( QStringLiteral("akonadi_favoriteview_emptyselection_contextmenu"), d->mXmlGuiClient)); } if (popup) { popup->exec(event->globalPos()); } } #endif void EntityListView::setXmlGuiClient(KXMLGUIClient *xmlGuiClient) { d->mXmlGuiClient = xmlGuiClient; } KXMLGUIClient *EntityListView::xmlGuiClient() const { return d->mXmlGuiClient; } #ifndef QT_NO_DRAGANDDROP void EntityListView::startDrag(Qt::DropActions supportedActions) { d->mDragDropManager->startDrag(supportedActions); } #endif void EntityListView::setDropActionMenuEnabled(bool enabled) { #ifndef QT_NO_DRAGANDDROP d->mDragDropManager->setShowDropActionMenu(enabled); #endif } bool EntityListView::isDropActionMenuEnabled() const { #ifndef QT_NO_DRAGANDDROP return d->mDragDropManager->showDropActionMenu(); #else return false; #endif } #include "moc_entitylistview.cpp" diff --git a/src/widgets/entitylistview.h b/src/widgets/entitylistview.h index 89d79dad9..67f74bc03 100644 --- a/src/widgets/entitylistview.h +++ b/src/widgets/entitylistview.h @@ -1,213 +1,209 @@ /* Copyright (c) 2006 - 2007 Volker Krause Copyright (c) 2008 Stephen Kelly Copyright (c) 2009 Kevin Ottens This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_ENTITYLISTVIEW_H #define AKONADI_ENTITYLISTVIEW_H #include "akonadiwidgets_export.h" #include class KXMLGUIClient; class QDragMoveEvent; namespace Akonadi { class Collection; class Item; /** * @short A view to show an item/collection list provided by an EntityTreeModel. * * When a KXmlGuiWindow is passed to the constructor, the XMLGUI * defined context menu @c akonadi_collectionview_contextmenu or * @c akonadi_itemview_contextmenu is used if available. * * Example: * * @code * * using namespace Akonadi; * * class MyWindow : public KXmlGuiWindow * { * public: * MyWindow() * : KXmlGuiWindow() * { * EntityListView *view = new EntityListView( this, this ); * setCentralWidget( view ); * * EntityTreeModel *model = new EntityTreeModel( ... ); * * KDescendantsProxyModel *flatModel = new KDescendantsProxyModel( this ); * flatModel->setSourceModel( model ); * * view->setModel( flatModel ); * } * } * * @endcode * * @author Volker Krause * @author Stephen Kelly * @since 4.4 */ class AKONADIWIDGETS_EXPORT EntityListView : public QListView { Q_OBJECT public: /** * Creates a new favorite collections view. * * @param parent The parent widget. */ explicit EntityListView(QWidget *parent = nullptr); /** * Creates a new favorite collections view. * * @param xmlGuiClient The KXMLGUIClient the view is used in. * This is needed for the XMLGUI based context menu. * Passing 0 is ok and will disable the builtin context menu. * @param parent The parent widget. */ explicit EntityListView(KXMLGUIClient *xmlGuiClient, QWidget *parent = nullptr); /** * Destroys the favorite collections view. */ ~EntityListView() override; /** * Sets the XML GUI client which the view is used in. * * This is needed if you want to use the built-in context menu. * * @param xmlGuiClient The KXMLGUIClient the view is used in. */ void setXmlGuiClient(KXMLGUIClient *xmlGuiClient); /** * Return the XML GUI client which the view is used in. * @since 4.12 */ KXMLGUIClient *xmlGuiClient() const; /** * @reimp * @param model the model to set */ void setModel(QAbstractItemModel *model) override; /** * Sets whether the drop action menu is @p enabled and will * be shown on drop operation. * @param enabled enables drop action menu if set as @c true * @since 4.7 */ void setDropActionMenuEnabled(bool enabled); /** * Returns whether the drop action menu is enabled and will * be shown on drop operation. * * @since 4.7 */ Q_REQUIRED_RESULT bool isDropActionMenuEnabled() const; Q_SIGNALS: /** * This signal is emitted whenever the user has clicked * a collection in the view. * * @param collection The clicked collection. */ void clicked(const Akonadi::Collection &collection); /** * This signal is emitted whenever the user has clicked * an item in the view. * * @param item The clicked item. */ void clicked(const Akonadi::Item &item); /** * This signal is emitted whenever the user has double clicked * a collection in the view. * * @param collection The double clicked collection. */ void doubleClicked(const Akonadi::Collection &collection); /** * This signal is emitted whenever the user has double clicked * an item in the view. * * @param item The double clicked item. */ void doubleClicked(const Akonadi::Item &item); /** * This signal is emitted whenever the current collection * in the view has changed. * * @param collection The new current collection. */ void currentChanged(const Akonadi::Collection &collection); /** * This signal is emitted whenever the current item * in the view has changed. * * @param item The new current item. */ void currentChanged(const Akonadi::Item &item); protected: using QListView::currentChanged; #ifndef QT_NO_DRAGANDDROP void startDrag(Qt::DropActions supportedActions) override; void dropEvent(QDropEvent *event) override; void dragMoveEvent(QDragMoveEvent *event) override; #endif #ifndef QT_NO_CONTEXTMENU void contextMenuEvent(QContextMenuEvent *event) override; #endif private: //@cond PRIVATE class Private; Private *const d; - - Q_PRIVATE_SLOT(d, void itemClicked(const QModelIndex &)) - Q_PRIVATE_SLOT(d, void itemDoubleClicked(const QModelIndex &)) - Q_PRIVATE_SLOT(d, void itemCurrentChanged(const QModelIndex &)) //@endcond }; } #endif diff --git a/src/widgets/entitytreeview.cpp b/src/widgets/entitytreeview.cpp index ced3bc13c..89c2cf346 100644 --- a/src/widgets/entitytreeview.cpp +++ b/src/widgets/entitytreeview.cpp @@ -1,339 +1,335 @@ /* Copyright (c) 2006 - 2007 Volker Krause Copyright (c) 2008 Stephen Kelly Copyright (C) 2012-2020 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "entitytreeview.h" #include "dragdropmanager_p.h" #include #include #include #include #include #include "collection.h" #include "controlgui.h" #include "item.h" #include "entitytreemodel.h" #include #include #include "progressspinnerdelegate_p.h" using namespace Akonadi; /** * @internal */ class Q_DECL_HIDDEN EntityTreeView::Private { public: Private(EntityTreeView *parent) : mParent(parent) #ifndef QT_NO_DRAGANDDROP , mDragDropManager(new DragDropManager(mParent)) #endif , mDefaultPopupMenu(QStringLiteral("akonadi_collectionview_contextmenu")) { } void init(); void itemClicked(const QModelIndex &index); void itemDoubleClicked(const QModelIndex &index); void itemCurrentChanged(const QModelIndex &index); void slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); EntityTreeView *mParent = nullptr; QBasicTimer mDragExpandTimer; DragDropManager *mDragDropManager = nullptr; KXMLGUIClient *mXmlGuiClient = nullptr; QString mDefaultPopupMenu; }; void EntityTreeView::Private::init() { Akonadi::DelegateAnimator *animator = new Akonadi::DelegateAnimator(mParent); Akonadi::ProgressSpinnerDelegate *customDelegate = new Akonadi::ProgressSpinnerDelegate(animator, mParent); mParent->setItemDelegate(customDelegate); mParent->header()->setSectionsClickable(true); mParent->header()->setStretchLastSection(false); // mParent->setRootIsDecorated( false ); // QTreeView::autoExpandDelay has very strange behaviour. It toggles the collapse/expand state // of the item the cursor is currently over when a timer event fires. // The behaviour we want is to expand a collapsed row on drag-over, but not collapse it. // mDragExpandTimer is used to achieve this. // mParent->setAutoExpandDelay ( QApplication::startDragTime() ); mParent->setSortingEnabled(true); mParent->sortByColumn(0, Qt::AscendingOrder); mParent->setEditTriggers(QAbstractItemView::EditKeyPressed); mParent->setAcceptDrops(true); #ifndef QT_NO_DRAGANDDROP mParent->setDropIndicatorShown(true); mParent->setDragDropMode(DragDrop); mParent->setDragEnabled(true); #endif - mParent->connect(mParent, SIGNAL(clicked(QModelIndex)), mParent, SLOT(itemClicked(QModelIndex))); - mParent->connect(mParent, SIGNAL(doubleClicked(QModelIndex)), mParent, SLOT(itemDoubleClicked(QModelIndex))); + mParent->connect(mParent, &QAbstractItemView::clicked, mParent, [this](const auto &index) { itemClicked(index); }); + mParent->connect(mParent, &QAbstractItemView::doubleClicked, mParent, [this](const auto &index) { itemDoubleClicked(index); }); ControlGui::widgetNeedsAkonadi(mParent); } void EntityTreeView::Private::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { Q_UNUSED(deselected) const int column = 0; for (const QItemSelectionRange &range : selected) { const QModelIndex index = range.topLeft(); if (index.column() > 0) { continue; } for (int row = index.row(); row <= range.bottomRight().row(); ++row) { // Don't use canFetchMore here. We need to bypass the check in // the EntityFilterModel when it shows only collections. mParent->model()->fetchMore(index.sibling(row, column)); } } if (selected.size() == 1) { const QItemSelectionRange &range = selected.first(); if (range.topLeft().row() == range.bottomRight().row()) { mParent->scrollTo(range.topLeft(), QTreeView::EnsureVisible); } } } void EntityTreeView::Private::itemClicked(const QModelIndex &index) { if (!index.isValid()) { return; } QModelIndex idx = index.sibling(index.row(), 0); const Collection collection = idx.model()->data(idx, EntityTreeModel::CollectionRole).value(); if (collection.isValid()) { Q_EMIT mParent->clicked(collection); } else { const Item item = idx.model()->data(idx, EntityTreeModel::ItemRole).value(); if (item.isValid()) { Q_EMIT mParent->clicked(item); } } } void EntityTreeView::Private::itemDoubleClicked(const QModelIndex &index) { if (!index.isValid()) { return; } QModelIndex idx = index.sibling(index.row(), 0); const Collection collection = idx.model()->data(idx, EntityTreeModel::CollectionRole).value(); if (collection.isValid()) { Q_EMIT mParent->doubleClicked(collection); } else { const Item item = idx.model()->data(idx, EntityTreeModel::ItemRole).value(); if (item.isValid()) { Q_EMIT mParent->doubleClicked(item); } } } void EntityTreeView::Private::itemCurrentChanged(const QModelIndex &index) { if (!index.isValid()) { return; } QModelIndex idx = index.sibling(index.row(), 0); const Collection collection = idx.model()->data(idx, EntityTreeModel::CollectionRole).value(); if (collection.isValid()) { Q_EMIT mParent->currentChanged(collection); } else { const Item item = idx.model()->data(idx, EntityTreeModel::ItemRole).value(); if (item.isValid()) { Q_EMIT mParent->currentChanged(item); } } } EntityTreeView::EntityTreeView(QWidget *parent) : QTreeView(parent) , d(new Private(this)) { setSelectionMode(QAbstractItemView::SingleSelection); d->init(); } EntityTreeView::EntityTreeView(KXMLGUIClient *xmlGuiClient, QWidget *parent) : QTreeView(parent) , d(new Private(this)) { d->mXmlGuiClient = xmlGuiClient; d->init(); } EntityTreeView::~EntityTreeView() { delete d->mDragDropManager; delete d; } void EntityTreeView::setModel(QAbstractItemModel *model) { if (selectionModel()) { - disconnect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this, SLOT(itemCurrentChanged(QModelIndex))); - - disconnect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection))); + disconnect(selectionModel(), &QItemSelectionModel::currentChanged, this, nullptr); + disconnect(selectionModel(), &QItemSelectionModel::selectionChanged, this, nullptr); } QTreeView::setModel(model); header()->setStretchLastSection(true); - connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), - SLOT(itemCurrentChanged(QModelIndex))); - - connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - SLOT(slotSelectionChanged(QItemSelection,QItemSelection))); + connect(selectionModel(), &QItemSelectionModel::currentChanged, + this, [this](const auto &index) { d->itemCurrentChanged(index); }); + connect(selectionModel(), &QItemSelectionModel::selectionChanged, + this, [this](const auto &oldSel, const auto &newSel) { d->slotSelectionChanged(oldSel, newSel); }); } void EntityTreeView::timerEvent(QTimerEvent *event) { if (event->timerId() == d->mDragExpandTimer.timerId()) { const QPoint pos = viewport()->mapFromGlobal(QCursor::pos()); if (state() == QAbstractItemView::DraggingState && viewport()->rect().contains(pos)) { setExpanded(indexAt(pos), true); } } QTreeView::timerEvent(event); } #ifndef QT_NO_DRAGANDDROP void EntityTreeView::dragMoveEvent(QDragMoveEvent *event) { d->mDragExpandTimer.start(QApplication::startDragTime(), this); if (d->mDragDropManager->dropAllowed(event)) { // All urls are supported. process the event. QTreeView::dragMoveEvent(event); return; } event->setDropAction(Qt::IgnoreAction); } void EntityTreeView::dropEvent(QDropEvent *event) { d->mDragExpandTimer.stop(); bool menuCanceled = false; if (d->mDragDropManager->processDropEvent(event, menuCanceled, (dropIndicatorPosition() == QAbstractItemView::OnItem))) { QTreeView::dropEvent(event); } } #endif #ifndef QT_NO_CONTEXTMENU void EntityTreeView::contextMenuEvent(QContextMenuEvent *event) { if (!d->mXmlGuiClient || !model()) { return; } const QModelIndex index = indexAt(event->pos()); QString popupName = d->mDefaultPopupMenu; if (index.isValid()) { // popup not over empty space // check whether the index under the cursor is a collection or item const Item item = model()->data(index, EntityTreeModel::ItemRole).value(); popupName = (item.isValid() ? QStringLiteral("akonadi_itemview_contextmenu") : QStringLiteral("akonadi_collectionview_contextmenu")); } QMenu *popup = static_cast(d->mXmlGuiClient->factory()->container(popupName, d->mXmlGuiClient)); if (popup) { popup->exec(event->globalPos()); } } #endif void EntityTreeView::setXmlGuiClient(KXMLGUIClient *xmlGuiClient) { d->mXmlGuiClient = xmlGuiClient; } KXMLGUIClient *EntityTreeView::xmlGuiClient() const { return d->mXmlGuiClient; } #ifndef QT_NO_DRAGANDDROP void EntityTreeView::startDrag(Qt::DropActions supportedActions) { d->mDragDropManager->startDrag(supportedActions); } #endif void EntityTreeView::setDropActionMenuEnabled(bool enabled) { #ifndef QT_NO_DRAGANDDROP d->mDragDropManager->setShowDropActionMenu(enabled); #endif } bool EntityTreeView::isDropActionMenuEnabled() const { #ifndef QT_NO_DRAGANDDROP return d->mDragDropManager->showDropActionMenu(); #else return false; #endif } void EntityTreeView::setManualSortingActive(bool active) { #ifndef QT_NO_DRAGANDDROP d->mDragDropManager->setManualSortingActive(active); #endif } bool EntityTreeView::isManualSortingActive() const { #ifndef QT_NO_DRAGANDDROP return d->mDragDropManager->isManualSortingActive(); #else return false; #endif } void EntityTreeView::setDefaultPopupMenu(const QString &name) { d->mDefaultPopupMenu = name; } #include "moc_entitytreeview.cpp" diff --git a/src/widgets/entitytreeview.h b/src/widgets/entitytreeview.h index 5e671d058..89b9c6662 100644 --- a/src/widgets/entitytreeview.h +++ b/src/widgets/entitytreeview.h @@ -1,245 +1,240 @@ /* Copyright (c) 2006 - 2007 Volker Krause Copyright (c) 2008 Stephen Kelly Copyright (C) 2012-2020 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_ENTITYTREEVIEW_H #define AKONADI_ENTITYTREEVIEW_H #include "akonadiwidgets_export.h" #include class KXMLGUIClient; class QDragMoveEvent; namespace Akonadi { class Collection; class Item; /** * @short A view to show an item/collection tree provided by an EntityTreeModel. * * When a KXmlGuiWindow is passed to the constructor, the XMLGUI * defined context menu @c akonadi_collectionview_contextmenu or * @c akonadi_itemview_contextmenu is used if available. * * Example: * * @code * * using namespace Akonadi; * * class MyWindow : public KXmlGuiWindow * { * public: * MyWindow() * : KXmlGuiWindow() * { * EntityTreeView *view = new EntityTreeView( this, this ); * setCentralWidget( view ); * * EntityTreeModel *model = new EntityTreeModel( ... ); * view->setModel( model ); * } * } * * @endcode * * @author Volker Krause * @author Stephen Kelly * @since 4.4 */ class AKONADIWIDGETS_EXPORT EntityTreeView : public QTreeView { Q_OBJECT public: /** * Creates a new entity tree view. * * @param parent The parent widget. */ explicit EntityTreeView(QWidget *parent = nullptr); /** * Creates a new entity tree view. * * @param xmlGuiClient The KXMLGUIClient the view is used in. * This is needed for the XMLGUI based context menu. * Passing 0 is ok and will disable the builtin context menu. * @param parent The parent widget. */ explicit EntityTreeView(KXMLGUIClient *xmlGuiClient, QWidget *parent = nullptr); /** * Destroys the entity tree view. */ ~EntityTreeView() override; /** * Sets the XML GUI client which the view is used in. * * This is needed if you want to use the built-in context menu. * * @param xmlGuiClient The KXMLGUIClient the view is used in. */ void setXmlGuiClient(KXMLGUIClient *xmlGuiClient); /** * Return the XML GUI client which the view is used in. * @since 4.12 */ KXMLGUIClient *xmlGuiClient() const; /** * @reimp * @param model the model to set */ void setModel(QAbstractItemModel *model) override; /** * Sets whether the drop action menu is @p enabled and will * be shown on drop operation. * @param enabled enables drop action menu if set as @c true * @since 4.5 */ void setDropActionMenuEnabled(bool enabled); /** * Returns whether the drop action menu is enabled and will * be shown on drop operation. * * @since 4.5 */ Q_REQUIRED_RESULT bool isDropActionMenuEnabled() const; /** * Return true if we use an manual sorting * Necessary to fix dnd menu * We must show just move when we move item between two items * When automatic no show dnd menu between two items. * @since 4.8.1 */ Q_REQUIRED_RESULT bool isManualSortingActive() const; /** * Set true if we automatic sorting * @param active enables automatic sorting if set as @c true * @since 4.8.1 */ void setManualSortingActive(bool active); /** * Set the name of the default popup menu (retrieved from the * application's XMLGUI file). * * This menu is used as a fallback if the context of the menu request * is neither an item nor a collection, e.g. the click is on an empty * area inside the view. If the click is over an entry in the view, * the menu which is applicable to the clicked entry (either an Item * or a Collection) is used. * * @param name The name of the popup menu * * @since 4.9 * @note For backwards compatibility, the default is the standard * collection popup menu, "akonadi_collectionview_contextmenu". * @see KXMLGUIClient, KXMLGUIFactory::container() */ void setDefaultPopupMenu(const QString &name); Q_SIGNALS: /** * This signal is emitted whenever the user has clicked * a collection in the view. * * @param collection The clicked collection. */ void clicked(const Akonadi::Collection &collection); /** * This signal is emitted whenever the user has clicked * an item in the view. * * @param item The clicked item. */ void clicked(const Akonadi::Item &item); /** * This signal is emitted whenever the user has double clicked * a collection in the view. * * @param collection The double clicked collection. */ void doubleClicked(const Akonadi::Collection &collection); /** * This signal is emitted whenever the user has double clicked * an item in the view. * * @param item The double clicked item. */ void doubleClicked(const Akonadi::Item &item); /** * This signal is emitted whenever the current collection * in the view has changed. * * @param collection The new current collection. */ void currentChanged(const Akonadi::Collection &collection); /** * This signal is emitted whenever the current item * in the view has changed. * * @param item The new current item. */ void currentChanged(const Akonadi::Item &item); protected: using QTreeView::currentChanged; #ifndef QT_NO_DRAGANDDROP void startDrag(Qt::DropActions supportedActions) override; void dragMoveEvent(QDragMoveEvent *event) override; void dropEvent(QDropEvent *event) override; #endif void timerEvent(QTimerEvent *event) override; #ifndef QT_NO_CONTEXTMENU void contextMenuEvent(QContextMenuEvent *event) override; #endif private: //@cond PRIVATE class Private; Private *const d; - - Q_PRIVATE_SLOT(d, void itemClicked(const QModelIndex &)) - Q_PRIVATE_SLOT(d, void itemDoubleClicked(const QModelIndex &)) - Q_PRIVATE_SLOT(d, void itemCurrentChanged(const QModelIndex &)) - Q_PRIVATE_SLOT(d, void slotSelectionChanged(const QItemSelection &, const QItemSelection &)) //@endcond }; } #endif diff --git a/src/widgets/itemview.cpp b/src/widgets/itemview.cpp index ae2b6d57c..75944f420 100644 --- a/src/widgets/itemview.cpp +++ b/src/widgets/itemview.cpp @@ -1,170 +1,174 @@ /* Copyright (c) 2007 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "itemview.h" #include "controlgui.h" #include "entitytreemodel.h" #include #include #include #include #include using namespace Akonadi; /** * @internal */ class Q_DECL_HIDDEN ItemView::Private { public: Private(ItemView *parent) : mParent(parent) { } void init(); void itemActivated(const QModelIndex &index); void itemCurrentChanged(const QModelIndex &index); void itemClicked(const QModelIndex &index); void itemDoubleClicked(const QModelIndex &index); Item itemForIndex(const QModelIndex &index); KXMLGUIClient *xmlGuiClient = nullptr; private: ItemView *mParent = nullptr; }; void ItemView::Private::init() { mParent->setRootIsDecorated(false); mParent->header()->setSectionsClickable(true); mParent->header()->setStretchLastSection(true); - mParent->connect(mParent, SIGNAL(activated(QModelIndex)), mParent, SLOT(itemActivated(QModelIndex))); - mParent->connect(mParent, SIGNAL(clicked(QModelIndex)), mParent, SLOT(itemClicked(QModelIndex))); - mParent->connect(mParent, QOverload::of(&QAbstractItemView::doubleClicked), mParent, [this](const QModelIndex &index) { itemDoubleClicked(index); }); + mParent->connect(mParent, &QAbstractItemView::activated, mParent, [this](const auto &index) { itemActivated(index); }); + mParent->connect(mParent, &QAbstractItemView::clicked, mParent, [this](const auto &index) { itemClicked(index); }); + mParent->connect(mParent, &QAbstractItemView::doubleClicked, [this](const auto &index) { itemDoubleClicked(index); }); ControlGui::widgetNeedsAkonadi(mParent); } Item ItemView::Private::itemForIndex(const QModelIndex &index) { if (!index.isValid()) { return Item(); } return mParent->model()->data(index, EntityTreeModel::ItemRole).value(); } void ItemView::Private::itemActivated(const QModelIndex &index) { const Item item = itemForIndex(index); if (!item.isValid()) { return; } Q_EMIT mParent->activated(item); } void ItemView::Private::itemCurrentChanged(const QModelIndex &index) { const Item item = itemForIndex(index); if (!item.isValid()) { return; } Q_EMIT mParent->currentChanged(item); } void ItemView::Private::itemClicked(const QModelIndex &index) { const Item item = itemForIndex(index); if (!item.isValid()) { return; } Q_EMIT mParent->clicked(item); } void ItemView::Private::itemDoubleClicked(const QModelIndex &index) { const Item item = itemForIndex(index); if (!item.isValid()) { return; } Q_EMIT mParent->doubleClicked(item); } ItemView::ItemView(QWidget *parent) : QTreeView(parent) , d(new Private(this)) { d->init(); } ItemView::ItemView(KXMLGUIClient *xmlGuiClient, QWidget *parent) : QTreeView(parent) , d(new Private(this)) { d->xmlGuiClient = xmlGuiClient; d->init(); } ItemView::~ItemView() { delete d; } void ItemView::setModel(QAbstractItemModel *model) { + if (selectionModel()) { + disconnect(selectionModel(), &QItemSelectionModel::currentChanged, this, nullptr); + } + QTreeView::setModel(model); - connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this, SLOT(itemCurrentChanged(QModelIndex))); + connect(selectionModel(), &QItemSelectionModel::currentChanged, + this, [this](const auto &index) { d->itemCurrentChanged(index); }); } void ItemView::contextMenuEvent(QContextMenuEvent *event) { if (!d->xmlGuiClient) { return; } QMenu *popup = static_cast(d->xmlGuiClient->factory()->container( QStringLiteral("akonadi_itemview_contextmenu"), d->xmlGuiClient)); if (popup) { popup->exec(event->globalPos()); } } void ItemView::setXmlGuiClient(KXMLGUIClient *xmlGuiClient) { d->xmlGuiClient = xmlGuiClient; } #include "moc_itemview.cpp" diff --git a/src/widgets/itemview.h b/src/widgets/itemview.h index 5449e16ef..c4fcb07c9 100644 --- a/src/widgets/itemview.h +++ b/src/widgets/itemview.h @@ -1,154 +1,149 @@ /* Copyright (c) 2007 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_ITEM_VIEW #define AKONADI_ITEM_VIEW #include "akonadiwidgets_export.h" #include class KXmlGuiWindow; class KXMLGUIClient; namespace Akonadi { class Item; /** * @short A view to show an item list provided by an ItemModel. * * When a KXmlGuiWindow is set, the XMLGUI defined context menu * @c akonadi_itemview_contextmenu is used if available. * * Example: * * @code * * class MyWindow : public KXmlGuiWindow * { * public: * MyWindow() * : KXmlGuiWindow() * { * Akonadi::ItemView *view = new Akonadi::ItemView( this, this ); * setCentralWidget( view ); * * Akonadi::ItemModel *model = new Akonadi::ItemModel( this ); * view->setModel( model ); * } * } * * @endcode * * @deprecated Use EntityTreeView or EntityListView on top of EntityTreeModel instead. * * @author Tobias Koenig */ class AKONADIWIDGETS_DEPRECATED_EXPORT ItemView : public QTreeView { Q_OBJECT public: /** * Creates a new item view. * * @param parent The parent widget. */ explicit ItemView(QWidget *parent = nullptr); /** * Creates a new item view. * * @param xmlGuiClient The KXMLGUIClient this is used in. * This is needed for the XMLGUI based context menu. * Passing 0 is ok and will disable the builtin context menu. * @param parent The parent widget. * @since 4.3 */ explicit ItemView(KXMLGUIClient *xmlGuiClient, QWidget *parent = nullptr); /** * Destroys the item view. */ ~ItemView() override; /** * Sets the KXMLGUIFactory which this view is used in. * This is needed if you want to use the built-in context menu. * * @param xmlGuiClient The KXMLGUIClient this view is used in. */ void setXmlGuiClient(KXMLGUIClient *xmlGuiClient); void setModel(QAbstractItemModel *model) override; Q_SIGNALS: /** * This signal is emitted whenever the user has activated * an item in the view. * * @param item The activated item. */ void activated(const Akonadi::Item &item); /** * This signal is emitted whenever the current item * in the view has changed. * * @param item The current item. */ void currentChanged(const Akonadi::Item &item); /** * This signal is emitted whenever the user clicked on an item * in the view. * * @param item The item the user clicked on. * @since 4.3 */ void clicked(const Akonadi::Item &item); /** * This signal is emitted whenever the user double clicked on an item * in the view. * * @param item The item the user double clicked on. * @since 4.3 */ void doubleClicked(const Akonadi::Item &item); protected: using QTreeView::currentChanged; void contextMenuEvent(QContextMenuEvent *event) override; private: //@cond PRIVATE class Private; Private *const d; - - Q_PRIVATE_SLOT(d, void itemActivated(const QModelIndex &)) - Q_PRIVATE_SLOT(d, void itemCurrentChanged(const QModelIndex &)) - Q_PRIVATE_SLOT(d, void itemClicked(const QModelIndex &)) - Q_PRIVATE_SLOT(d, void itemDoubleClicked(const QModelIndex &)) //@endcond }; } #endif diff --git a/src/widgets/progressspinnerdelegate.cpp b/src/widgets/progressspinnerdelegate.cpp index 8bce11924..d0f0ad2d2 100644 --- a/src/widgets/progressspinnerdelegate.cpp +++ b/src/widgets/progressspinnerdelegate.cpp @@ -1,125 +1,126 @@ /* Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "progressspinnerdelegate_p.h" #include "entitytreemodel.h" #include #include #include using namespace Akonadi; DelegateAnimator::DelegateAnimator(QAbstractItemView *view) : QObject(view) , m_view(view) , m_timerId(-1) { m_pixmapSequence = KIconLoader::global()->loadPixmapSequence(QStringLiteral("process-working"), 22); } void DelegateAnimator::push(const QModelIndex &index) { if (m_animations.isEmpty()) { m_timerId = startTimer(200); } m_animations.insert(Animation(index)); } void DelegateAnimator::pop(const QModelIndex &index) { if (m_animations.remove(Animation(index))) { if (m_animations.isEmpty() && m_timerId != -1) { killTimer(m_timerId); m_timerId = -1; } } } void DelegateAnimator::timerEvent(QTimerEvent *event) { if (!(event->timerId() == m_timerId && m_view)) { - return QObject::timerEvent(event); + QObject::timerEvent(event); + return; } QRegion region; // Do no port this to for(:)! The pop() inside the loop invalidates (even implicit) iterators. Q_FOREACH (const Animation &animation, m_animations) { // Check if loading is finished (we might not be notified, if the index is scrolled out of view) const QVariant fetchState = animation.index.data(Akonadi::EntityTreeModel::FetchStateRole); if (fetchState.toInt() != Akonadi::EntityTreeModel::FetchingState) { pop(animation.index); continue; } // This repaints the entire delegate (icon and text). // TODO: See if there's a way to repaint only part of it (the icon). animation.nextFrame(); const QRect rect = m_view->visualRect(animation.index); region += rect; } if (!region.isEmpty()) { m_view->viewport()->update(region); } } QPixmap DelegateAnimator::sequenceFrame(const QModelIndex &index) { for (const Animation &animation : qAsConst(m_animations)) { if (animation.index == index) { return m_pixmapSequence.frameAt(animation.frame); } } return QPixmap(); } ProgressSpinnerDelegate::ProgressSpinnerDelegate(DelegateAnimator *animator, QObject *parent) : QStyledItemDelegate(parent) , m_animator(animator) { } void ProgressSpinnerDelegate::initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const { QStyledItemDelegate::initStyleOption(option, index); const QVariant fetchState = index.data(Akonadi::EntityTreeModel::FetchStateRole); if (!fetchState.isValid() || fetchState.toInt() != Akonadi::EntityTreeModel::FetchingState) { m_animator->pop(index); return; } m_animator->push(index); if (QStyleOptionViewItem *v = qstyleoption_cast(option)) { v->icon = m_animator->sequenceFrame(index); } } uint Akonadi::qHash(const Akonadi::DelegateAnimator::Animation &anim) { return qHash(anim.index); } diff --git a/src/widgets/standardactionmanager.cpp b/src/widgets/standardactionmanager.cpp index 0558de67d..9cd3202c9 100644 --- a/src/widgets/standardactionmanager.cpp +++ b/src/widgets/standardactionmanager.cpp @@ -1,1839 +1,1836 @@ /* Copyright (c) 2008 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "standardactionmanager.h" #include "actionstatemanager_p.h" #include "agentfilterproxymodel.h" #include "agentinstancecreatejob.h" #include "agentmanager.h" #include "agenttypedialog.h" #include "collectioncreatejob.h" #include "collectiondeletejob.h" #include "collectiondialog.h" #include "collectionutils.h" #include "entitytreemodel.h" #include "favoritecollectionsmodel.h" #include "itemdeletejob.h" #include "metatypes.h" #include "pastehelper_p.h" #include "specialcollectionattribute.h" #include "collectionpropertiesdialog.h" #include "subscriptiondialog.h" #include "renamefavoritedialog_p.h" #include "trashjob.h" #include "trashrestorejob.h" #include "entitydeletedattribute.h" #include "recentcollectionaction_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Akonadi; //@cond PRIVATE enum ActionType { NormalAction, ActionWithAlternative, //Normal action, but with an alternative state ActionAlternative, //Alternative state of the ActionWithAlternative MenuAction, ToggleAction }; static const struct { const char *name; const char *label; const char *iconLabel; const char *icon; int shortcut; const char *slot; ActionType actionType; } standardActionData[] = { { "akonadi_collection_create", I18N_NOOP("&New Folder..."), I18N_NOOP("New"), "folder-new", 0, SLOT(slotCreateCollection()), NormalAction }, { "akonadi_collection_copy", nullptr, nullptr, "edit-copy", 0, SLOT(slotCopyCollections()), NormalAction }, { "akonadi_collection_delete", I18N_NOOP("&Delete Folder"), I18N_NOOP("Delete"), "edit-delete", 0, SLOT(slotDeleteCollection()), NormalAction }, { "akonadi_collection_sync", I18N_NOOP("&Synchronize Folder"), I18N_NOOP("Synchronize"), "view-refresh", Qt::Key_F5, SLOT(slotSynchronizeCollection()), NormalAction }, { "akonadi_collection_properties", I18N_NOOP("Folder &Properties"), I18N_NOOP("Properties"), "configure", 0, SLOT(slotCollectionProperties()), NormalAction }, { "akonadi_item_copy", nullptr, nullptr, "edit-copy", 0, SLOT(slotCopyItems()), NormalAction }, { "akonadi_paste", I18N_NOOP("&Paste"), I18N_NOOP("Paste"), "edit-paste", Qt::CTRL + Qt::Key_V, SLOT(slotPaste()), NormalAction }, { "akonadi_item_delete", nullptr, nullptr, "edit-delete", 0, SLOT(slotDeleteItems()), NormalAction }, { "akonadi_manage_local_subscriptions", I18N_NOOP("Manage Local &Subscriptions..."), I18N_NOOP("Manage Local Subscriptions"), "folder-bookmarks", 0, SLOT(slotLocalSubscription()), NormalAction }, { "akonadi_collection_add_to_favorites", I18N_NOOP("Add to Favorite Folders"), I18N_NOOP("Add to Favorite"), "bookmark-new", 0, SLOT(slotAddToFavorites()), NormalAction }, { "akonadi_collection_remove_from_favorites", I18N_NOOP("Remove from Favorite Folders"), I18N_NOOP("Remove from Favorite"), "edit-delete", 0, SLOT(slotRemoveFromFavorites()), NormalAction }, { "akonadi_collection_rename_favorite", I18N_NOOP("Rename Favorite..."), I18N_NOOP("Rename"), "edit-rename", 0, SLOT(slotRenameFavorite()), NormalAction }, { "akonadi_collection_copy_to_menu", I18N_NOOP("Copy Folder To..."), I18N_NOOP("Copy To"), "edit-copy", 0, SLOT(slotCopyCollectionTo(QAction*)), MenuAction }, { "akonadi_item_copy_to_menu", I18N_NOOP("Copy Item To..."), I18N_NOOP("Copy To"), "edit-copy", 0, SLOT(slotCopyItemTo(QAction*)), MenuAction }, { "akonadi_item_move_to_menu", I18N_NOOP("Move Item To..."), I18N_NOOP("Move To"), "go-jump", 0, SLOT(slotMoveItemTo(QAction*)), MenuAction }, { "akonadi_collection_move_to_menu", I18N_NOOP("Move Folder To..."), I18N_NOOP("Move To"), "go-jump", 0, SLOT(slotMoveCollectionTo(QAction*)), MenuAction }, { "akonadi_item_cut", I18N_NOOP("&Cut Item"), I18N_NOOP("Cut"), "edit-cut", Qt::CTRL + Qt::Key_X, SLOT(slotCutItems()), NormalAction }, { "akonadi_collection_cut", I18N_NOOP("&Cut Folder"), I18N_NOOP("Cut"), "edit-cut", Qt::CTRL + Qt::Key_X, SLOT(slotCutCollections()), NormalAction }, { "akonadi_resource_create", I18N_NOOP("Create Resource"), nullptr, "folder-new", 0, SLOT(slotCreateResource()), NormalAction }, { "akonadi_resource_delete", I18N_NOOP("Delete Resource"), nullptr, "edit-delete", 0, SLOT(slotDeleteResource()), NormalAction }, { "akonadi_resource_properties", I18N_NOOP("&Resource Properties"), I18N_NOOP("Properties"), "configure", 0, SLOT(slotResourceProperties()), NormalAction }, { "akonadi_resource_synchronize", I18N_NOOP("Synchronize Resource"), I18N_NOOP("Synchronize"), "view-refresh", 0, SLOT(slotSynchronizeResource()), NormalAction }, { "akonadi_work_offline", I18N_NOOP("Work Offline"), nullptr, "user-offline", 0, SLOT(slotToggleWorkOffline(bool)), ToggleAction }, { "akonadi_collection_copy_to_dialog", I18N_NOOP("Copy Folder To..."), I18N_NOOP("Copy To"), "edit-copy", 0, SLOT(slotCopyCollectionTo()), NormalAction }, { "akonadi_collection_move_to_dialog", I18N_NOOP("Move Folder To..."), I18N_NOOP("Move To"), "go-jump", 0, SLOT(slotMoveCollectionTo()), NormalAction }, { "akonadi_item_copy_to_dialog", I18N_NOOP("Copy Item To..."), I18N_NOOP("Copy To"), "edit-copy", 0, SLOT(slotCopyItemTo()), NormalAction }, { "akonadi_item_move_to_dialog", I18N_NOOP("Move Item To..."), I18N_NOOP("Move To"), "go-jump", 0, SLOT(slotMoveItemTo()), NormalAction }, { "akonadi_collection_sync_recursive", I18N_NOOP("&Synchronize Folder Recursively"), I18N_NOOP("Synchronize Recursively"), "view-refresh", Qt::CTRL + Qt::Key_F5, SLOT(slotSynchronizeCollectionRecursive()), NormalAction }, { "akonadi_move_collection_to_trash", I18N_NOOP("&Move Folder To Trash"), I18N_NOOP("Move Folder To Trash"), "edit-delete", 0, SLOT(slotMoveCollectionToTrash()), NormalAction }, { "akonadi_move_item_to_trash", I18N_NOOP("&Move Item To Trash"), I18N_NOOP("Move Item To Trash"), "edit-delete", 0, SLOT(slotMoveItemToTrash()), NormalAction }, { "akonadi_restore_collection_from_trash", I18N_NOOP("&Restore Folder From Trash"), I18N_NOOP("Restore Folder From Trash"), "view-refresh", 0, SLOT(slotRestoreCollectionFromTrash()), NormalAction }, { "akonadi_restore_item_from_trash", I18N_NOOP("&Restore Item From Trash"), I18N_NOOP("Restore Item From Trash"), "view-refresh", 0, SLOT(slotRestoreItemFromTrash()), NormalAction }, { "akonadi_collection_trash_restore", I18N_NOOP("&Restore Folder From Trash"), I18N_NOOP("Restore Folder From Trash"), "edit-delete", 0, SLOT(slotTrashRestoreCollection()), ActionWithAlternative }, { nullptr, I18N_NOOP("&Restore Collection From Trash"), I18N_NOOP("Restore Collection From Trash"), "view-refresh", 0, nullptr, ActionAlternative }, { "akonadi_item_trash_restore", I18N_NOOP("&Restore Item From Trash"), I18N_NOOP("Restore Item From Trash"), "edit-delete", 0, SLOT(slotTrashRestoreItem()), ActionWithAlternative }, { nullptr, I18N_NOOP("&Restore Item From Trash"), I18N_NOOP("Restore Item From Trash"), "view-refresh", 0, nullptr, ActionAlternative }, { "akonadi_collection_sync_favorite_folders", I18N_NOOP("&Synchronize Favorite Folders"), I18N_NOOP("Synchronize Favorite Folders"), "view-refresh", Qt::CTRL + Qt::SHIFT + Qt::Key_L, SLOT(slotSynchronizeFavoriteCollections()), NormalAction }, { "akonadi_resource_synchronize_collectiontree", I18N_NOOP("Synchronize Folder Tree"), I18N_NOOP("Synchronize"), "view-refresh", 0, SLOT(slotSynchronizeCollectionTree()), NormalAction } }; static const int numStandardActionData = sizeof standardActionData / sizeof * standardActionData; static_assert(numStandardActionData == StandardActionManager::LastType, "StandardActionData table does not match StandardActionManager types"); static bool canCreateCollection(const Akonadi::Collection &collection) { if (!(collection.rights() & Akonadi::Collection::CanCreateCollection)) { return false; } return true; } static void setWorkOffline(bool offline) { KConfig config(QStringLiteral("akonadikderc")); KConfigGroup group(&config, QStringLiteral("Actions")); group.writeEntry("WorkOffline", offline); } static bool workOffline() { KConfig config(QStringLiteral("akonadikderc")); const KConfigGroup group(&config, QStringLiteral("Actions")); return group.readEntry("WorkOffline", false); } static QModelIndexList safeSelectedRows(QItemSelectionModel *selectionModel) { QModelIndexList selectedRows = selectionModel->selectedRows(); if (!selectedRows.isEmpty()) { return selectedRows; } // try harder for selected rows that don't span the full row for some reason (e.g. due to buggy column adding proxy models etc) const auto selection = selectionModel->selection(); for (const auto &range : selection) { if (!range.isValid() || range.isEmpty()) { continue; } const QModelIndex parent = range.parent(); for (int row = range.top(); row <= range.bottom(); ++row) { const QModelIndex index = range.model()->index(row, range.left(), parent); const Qt::ItemFlags flags = range.model()->flags(index); if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled)) { selectedRows.push_back(index); } } } return selectedRows; } /** * @internal */ class Q_DECL_HIDDEN StandardActionManager::Private { public: Private(StandardActionManager *parent) : q(parent) , actionCollection(nullptr) , parentWidget(nullptr) , collectionSelectionModel(nullptr) , itemSelectionModel(nullptr) , favoritesModel(nullptr) , favoriteSelectionModel(nullptr) , insideSelectionSlot(false) { actions.fill(nullptr, StandardActionManager::LastType); pluralLabels.insert(StandardActionManager::CopyCollections, ki18np("&Copy Folder", "&Copy %1 Folders")); pluralLabels.insert(StandardActionManager::CopyItems, ki18np("&Copy Item", "&Copy %1 Items")); pluralLabels.insert(StandardActionManager::CutItems, ki18np("&Cut Item", "&Cut %1 Items")); pluralLabels.insert(StandardActionManager::CutCollections, ki18np("&Cut Folder", "&Cut %1 Folders")); pluralLabels.insert(StandardActionManager::DeleteItems, ki18np("&Delete Item", "&Delete %1 Items")); pluralLabels.insert(StandardActionManager::DeleteCollections, ki18np("&Delete Folder", "&Delete %1 Folders")); pluralLabels.insert(StandardActionManager::SynchronizeCollections, ki18np("&Synchronize Folder", "&Synchronize %1 Folders")); pluralLabels.insert(StandardActionManager::DeleteResources, ki18np("&Delete Resource", "&Delete %1 Resources")); pluralLabels.insert(StandardActionManager::SynchronizeResources, ki18np("&Synchronize Resource", "&Synchronize %1 Resources")); pluralIconLabels.insert(StandardActionManager::CopyCollections, ki18np("Copy Folder", "Copy %1 Folders")); pluralIconLabels.insert(StandardActionManager::CopyItems, ki18np("Copy Item", "Copy %1 Items")); pluralIconLabels.insert(StandardActionManager::CutItems, ki18np("Cut Item", "Cut %1 Items")); pluralIconLabels.insert(StandardActionManager::CutCollections, ki18np("Cut Folder", "Cut %1 Folders")); pluralIconLabels.insert(StandardActionManager::DeleteItems, ki18np("Delete Item", "Delete %1 Items")); pluralIconLabels.insert(StandardActionManager::DeleteCollections, ki18np("Delete Folder", "Delete %1 Folders")); pluralIconLabels.insert(StandardActionManager::SynchronizeCollections, ki18np("Synchronize Folder", "Synchronize %1 Folders")); pluralIconLabels.insert(StandardActionManager::DeleteResources, ki18np("Delete Resource", "Delete %1 Resources")); pluralIconLabels.insert(StandardActionManager::SynchronizeResources, ki18np("Synchronize Resource", "Synchronize %1 Resources")); setContextText(StandardActionManager::CreateCollection, StandardActionManager::DialogTitle, i18nc("@title:window", "New Folder")); setContextText(StandardActionManager::CreateCollection, StandardActionManager::DialogText, i18nc("@label:textbox name of Akonadi folder", "Name")); setContextText(StandardActionManager::CreateCollection, StandardActionManager::ErrorMessageText, ki18n("Could not create folder: %1")); setContextText(StandardActionManager::CreateCollection, StandardActionManager::ErrorMessageTitle, i18n("Folder creation failed")); setContextText(StandardActionManager::DeleteCollections, StandardActionManager::MessageBoxText, ki18np("Do you really want to delete this folder and all its sub-folders?", "Do you really want to delete %1 folders and all their sub-folders?")); setContextText(StandardActionManager::DeleteCollections, StandardActionManager::MessageBoxTitle, ki18ncp("@title:window", "Delete folder?", "Delete folders?")); setContextText(StandardActionManager::DeleteCollections, StandardActionManager::ErrorMessageText, ki18n("Could not delete folder: %1")); setContextText(StandardActionManager::DeleteCollections, StandardActionManager::ErrorMessageTitle, i18n("Folder deletion failed")); setContextText(StandardActionManager::CollectionProperties, StandardActionManager::DialogTitle, ki18nc("@title:window", "Properties of Folder %1")); setContextText(StandardActionManager::DeleteItems, StandardActionManager::MessageBoxText, ki18np("Do you really want to delete the selected item?", "Do you really want to delete %1 items?")); setContextText(StandardActionManager::DeleteItems, StandardActionManager::MessageBoxTitle, ki18ncp("@title:window", "Delete item?", "Delete items?")); setContextText(StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageText, ki18n("Could not delete item: %1")); setContextText(StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageTitle, i18n("Item deletion failed")); setContextText(StandardActionManager::RenameFavoriteCollection, StandardActionManager::DialogTitle, i18nc("@title:window", "Rename Favorite")); setContextText(StandardActionManager::RenameFavoriteCollection, StandardActionManager::DialogText, i18nc("@label:textbox name of the folder", "Name:")); setContextText(StandardActionManager::CreateResource, StandardActionManager::DialogTitle, i18nc("@title:window", "New Resource")); setContextText(StandardActionManager::CreateResource, StandardActionManager::ErrorMessageText, ki18n("Could not create resource: %1")); setContextText(StandardActionManager::CreateResource, StandardActionManager::ErrorMessageTitle, i18n("Resource creation failed")); setContextText(StandardActionManager::DeleteResources, StandardActionManager::MessageBoxText, ki18np("Do you really want to delete this resource?", "Do you really want to delete %1 resources?")); setContextText(StandardActionManager::DeleteResources, StandardActionManager::MessageBoxTitle, ki18ncp("@title:window", "Delete Resource?", "Delete Resources?")); setContextText(StandardActionManager::Paste, StandardActionManager::ErrorMessageText, ki18n("Could not paste data: %1")); setContextText(StandardActionManager::Paste, StandardActionManager::ErrorMessageTitle, i18n("Paste failed")); qRegisterMetaType("Akonadi::Item::List"); } void enableAction(int type, bool enable) // private slot, called by ActionStateManager { enableAction(static_cast(type), enable); } void enableAction(StandardActionManager::Type type, bool enable) { Q_ASSERT(type < StandardActionManager::LastType); if (actions[type]) { actions[type]->setEnabled(enable); } // Update the action menu KActionMenu *actionMenu = qobject_cast(actions[type]); if (actionMenu) { //get rid of the submenus, they are re-created in enableAction. clear() is not enough, doesn't remove the submenu object instances. QMenu *menu = actionMenu->menu(); //Not necessary to delete and recreate menu when it was not created if (menu->property("actionType").isValid() && menu->isEmpty()) { return; } mRecentCollectionsMenu.remove(type); delete menu; menu = new QMenu(); menu->setProperty("actionType", static_cast(type)); - q->connect(menu, SIGNAL(aboutToShow()), SLOT(aboutToShowMenu())); - q->connect(menu, SIGNAL(triggered(QAction*)), standardActionData[type].slot); + q->connect(menu, &QMenu::aboutToShow, q, [this]() { aboutToShowMenu(); }); + q->connect(menu, SIGNAL(triggered(QAction*)), standardActionData[type].slot); // clazy:exclude=old-style-connect actionMenu->setMenu(menu); } } void aboutToShowMenu() { QMenu *menu = qobject_cast(q->sender()); if (!menu) { return; } if (!menu->isEmpty()) { return; } // collect all selected collections const Akonadi::Collection::List selectedCollectionsList = selectedCollections(); const StandardActionManager::Type type = static_cast(menu->property("actionType").toInt()); QPointer recentCollection = new RecentCollectionAction(type, selectedCollectionsList, collectionSelectionModel->model(), menu); mRecentCollectionsMenu.insert(type, recentCollection); const QSet mimeTypes = mimeTypesOfSelection(type); fillFoldersMenu(selectedCollectionsList, mimeTypes, type, menu, collectionSelectionModel->model(), QModelIndex()); } void createActionFolderMenu(QMenu *menu, StandardActionManager::Type type) { if (type == CopyCollectionToMenu || type == CopyItemToMenu || type == MoveItemToMenu || type == MoveCollectionToMenu) { new RecentCollectionAction(type, Akonadi::Collection::List(), collectionSelectionModel->model(), menu); Collection::List selectedCollectionsList = selectedCollections(); const QSet mimeTypes = mimeTypesOfSelection(type); fillFoldersMenu(selectedCollectionsList, mimeTypes, type, menu, collectionSelectionModel->model(), QModelIndex()); } } void updateAlternatingAction(int type) // private slot, called by ActionStateManager { updateAlternatingAction(static_cast(type)); } void updateAlternatingAction(StandardActionManager::Type type) { Q_ASSERT(type < StandardActionManager::LastType); if (!actions[type]) { return; } /* * The same action is stored at the ActionWithAlternative indexes as well as the corresponding ActionAlternative indexes in the actions array. * The following simply changes the standardActionData */ if ((standardActionData[type].actionType == ActionWithAlternative) || (standardActionData[type].actionType == ActionAlternative)) { actions[type]->setText(i18n(standardActionData[type].label)); actions[type]->setIcon(QIcon::fromTheme(QString::fromLatin1(standardActionData[type].icon))); if (pluralLabels.contains(type) && !pluralLabels.value(type).isEmpty()) { actions[type]->setText(pluralLabels.value(type).subs(1).toString()); } else if (standardActionData[type].label) { actions[type]->setText(i18n(standardActionData[type].label)); } if (pluralIconLabels.contains(type) && !pluralIconLabels.value(type).isEmpty()) { actions[type]->setIconText(pluralIconLabels.value(type).subs(1).toString()); } else if (standardActionData[type].iconLabel) { actions[type]->setIconText(i18n(standardActionData[type].iconLabel)); } if (standardActionData[type].icon) { actions[type]->setIcon(QIcon::fromTheme(QString::fromLatin1(standardActionData[type].icon))); } //actions[type]->setShortcut( standardActionData[type].shortcut ); /*if ( standardActionData[type].slot ) { switch ( standardActionData[type].actionType ) { case NormalAction: case ActionWithAlternative: connect( action, SIGNAL(triggered()), standardActionData[type].slot ); break; } }*/ } } void updatePluralLabel(int type, int count) // private slot, called by ActionStateManager { updatePluralLabel(static_cast(type), count); } void updatePluralLabel(StandardActionManager::Type type, int count) // private slot, called by ActionStateManager { Q_ASSERT(type < StandardActionManager::LastType); if (actions[type] && pluralLabels.contains(type) && !pluralLabels.value(type).isEmpty()) { actions[type]->setText(pluralLabels.value(type).subs(qMax(count, 1)).toString()); } } bool isFavoriteCollection(const Akonadi::Collection &collection) // private slot, called by ActionStateManager { if (!favoritesModel) { return false; } return favoritesModel->collectionIds().contains(collection.id()); } void encodeToClipboard(QItemSelectionModel *selectionModel, bool cut = false) { Q_ASSERT(selectionModel); if (safeSelectedRows(selectionModel).isEmpty()) { return; } #ifndef QT_NO_CLIPBOARD QAbstractItemModel *model = const_cast(selectionModel->model()); QMimeData *mimeData = selectionModel->model()->mimeData(safeSelectedRows(selectionModel)); model->setData(QModelIndex(), false, EntityTreeModel::PendingCutRole); markCutAction(mimeData, cut); QApplication::clipboard()->setMimeData(mimeData); if (cut) { const auto rows = safeSelectedRows(selectionModel); for (const auto &index : rows) { model->setData(index, true, EntityTreeModel::PendingCutRole); } } #endif } static Akonadi::Collection::List collectionsForIndexes(const QModelIndexList& list) { Akonadi::Collection::List collectionList; for (const QModelIndex &index : list) { Collection collection = index.data(EntityTreeModel::CollectionRole).value(); if (!collection.isValid()) { continue; } const Collection parentCollection = index.data(EntityTreeModel::ParentCollectionRole).value(); collection.setParentCollection(parentCollection); collectionList << std::move(collection); } return collectionList; } void updateActions() { // favorite collections Collection::List selectedFavoriteCollectionsList; if (favoriteSelectionModel) { const QModelIndexList rows = safeSelectedRows(favoriteSelectionModel); selectedFavoriteCollectionsList = collectionsForIndexes(rows); } // collect all selected collections Collection::List selectedCollectionsList; if (collectionSelectionModel) { const QModelIndexList rows = safeSelectedRows(collectionSelectionModel); selectedCollectionsList = collectionsForIndexes(rows); } // collect all selected items Item::List selectedItems; if (itemSelectionModel) { const QModelIndexList rows = safeSelectedRows(itemSelectionModel); for (const QModelIndex &index : rows) { Item item = index.data(EntityTreeModel::ItemRole).value(); if (!item.isValid()) { continue; } const Collection parentCollection = index.data(EntityTreeModel::ParentCollectionRole).value(); item.setParentCollection(parentCollection); selectedItems << item; } } mActionStateManager.updateState(selectedCollectionsList, selectedFavoriteCollectionsList, selectedItems); if (favoritesModel) { enableAction(StandardActionManager::SynchronizeFavoriteCollections, (favoritesModel->rowCount() > 0)); } Q_EMIT q->actionStateUpdated(); } #ifndef QT_NO_CLIPBOARD void clipboardChanged(QClipboard::Mode mode) { if (mode == QClipboard::Clipboard) { updateActions(); } } #endif QItemSelection mapToEntityTreeModel(const QAbstractItemModel *model, const QItemSelection &selection) const { const QAbstractProxyModel *proxy = qobject_cast(model); if (proxy) { return mapToEntityTreeModel(proxy->sourceModel(), proxy->mapSelectionToSource(selection)); } else { return selection; } } QItemSelection mapFromEntityTreeModel(const QAbstractItemModel *model, const QItemSelection &selection) const { const QAbstractProxyModel *proxy = qobject_cast(model); if (proxy) { const QItemSelection select = mapFromEntityTreeModel(proxy->sourceModel(), selection); return proxy->mapSelectionFromSource(select); } else { return selection; } } // RAII class for setting insideSelectionSlot to true on entering, and false on exiting, the two slots below. class InsideSelectionSlotBlocker { public: InsideSelectionSlotBlocker(Private *p) : _p(p) { Q_ASSERT(!p->insideSelectionSlot); p->insideSelectionSlot = true; } ~InsideSelectionSlotBlocker() { Q_ASSERT(_p->insideSelectionSlot); _p->insideSelectionSlot = false; } private: Q_DISABLE_COPY(InsideSelectionSlotBlocker) Private *_p; }; void collectionSelectionChanged() { if (insideSelectionSlot) { return; } InsideSelectionSlotBlocker block(this); if (favoriteSelectionModel) { QItemSelection selection = collectionSelectionModel->selection(); selection = mapToEntityTreeModel(collectionSelectionModel->model(), selection); selection = mapFromEntityTreeModel(favoriteSelectionModel->model(), selection); favoriteSelectionModel->select(selection, QItemSelectionModel::ClearAndSelect); } updateActions(); } void favoriteSelectionChanged() { if (insideSelectionSlot) { return; } QItemSelection selection = favoriteSelectionModel->selection(); if (selection.isEmpty()) { return; } selection = mapToEntityTreeModel(favoriteSelectionModel->model(), selection); selection = mapFromEntityTreeModel(collectionSelectionModel->model(), selection); InsideSelectionSlotBlocker block(this); collectionSelectionModel->select(selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); // Also set the current index. This will trigger KMMainWidget::slotFolderChanged in kmail, which we want. if (!selection.indexes().isEmpty()) { collectionSelectionModel->setCurrentIndex(selection.indexes().first(), QItemSelectionModel::NoUpdate); } updateActions(); } void slotCreateCollection() { Q_ASSERT(collectionSelectionModel); if (collectionSelectionModel->selection().indexes().isEmpty()) { return; } const QModelIndex index = collectionSelectionModel->selection().indexes().at(0); Q_ASSERT(index.isValid()); const Collection parentCollection = index.data(EntityTreeModel::CollectionRole).value(); Q_ASSERT(parentCollection.isValid()); if (!canCreateCollection(parentCollection)) { return; } QString name = QInputDialog::getText(parentWidget, contextText(StandardActionManager::CreateCollection, StandardActionManager::DialogTitle), contextText(StandardActionManager::CreateCollection, StandardActionManager::DialogText)); name = name.trimmed(); if (name.isEmpty()) { return; } if (name.contains(QLatin1Char('/'))) { KMessageBox::error(parentWidget, i18n("We can not add \"/\" in folder name."), i18n("Create new folder error")); return; } if (name.startsWith(QLatin1Char('.')) || name.endsWith(QLatin1Char('.'))) { KMessageBox::error(parentWidget, i18n("We can not add \".\" at begin or end of folder name."), i18n("Create new folder error")); return; } Collection collection; collection.setName(name); collection.setParentCollection(parentCollection); if (actions[StandardActionManager::CreateCollection]) { const QStringList mts = actions[StandardActionManager::CreateCollection]->property("ContentMimeTypes").toStringList(); if (!mts.isEmpty()) { collection.setContentMimeTypes(mts); } } if (parentCollection.contentMimeTypes().contains(Collection::virtualMimeType())) { collection.setVirtual(true); collection.setContentMimeTypes(collection.contentMimeTypes() << Collection::virtualMimeType()); } CollectionCreateJob *job = new CollectionCreateJob(collection); - q->connect(job, SIGNAL(result(KJob*)), q, SLOT(collectionCreationResult(KJob*))); + q->connect(job, &KJob::result, q, [this](KJob *job) { collectionCreationResult(job); }); } void slotCopyCollections() { encodeToClipboard(collectionSelectionModel); } void slotCutCollections() { encodeToClipboard(collectionSelectionModel, true); } Collection::List selectedCollections() { Collection::List collections; Q_ASSERT(collectionSelectionModel); const QModelIndexList indexes = safeSelectedRows(collectionSelectionModel); collections.reserve(indexes.count()); for (const QModelIndex &index : indexes) { Q_ASSERT(index.isValid()); const Collection collection = index.data(EntityTreeModel::CollectionRole).value(); Q_ASSERT(collection.isValid()); collections << collection; } return collections; } void slotDeleteCollection() { const Collection::List collections = selectedCollections(); if (collections.isEmpty()) { return; } const QString collectionName = collections.first().name(); const QString text = contextText(StandardActionManager::DeleteCollections, StandardActionManager::MessageBoxText, collections.count(), collectionName); if (KMessageBox::questionYesNo(parentWidget, text, contextText(StandardActionManager::DeleteCollections, StandardActionManager::MessageBoxTitle, collections.count(), collectionName), KStandardGuiItem::del(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) != KMessageBox::Yes) { return; } for (const Collection &collection : collections) { CollectionDeleteJob *job = new CollectionDeleteJob(collection, q); q->connect(job, &CollectionDeleteJob::result, q, [this](KJob* job) { collectionDeletionResult(job); }); } } void slotMoveCollectionToTrash() { const Collection::List collections = selectedCollections(); if (collections.isEmpty()) { return; } for (const Collection &collection : collections) { TrashJob *job = new TrashJob(collection, q); q->connect(job, &TrashJob::result, q, [this](KJob *job) { moveCollectionToTrashResult(job);}); } } void slotRestoreCollectionFromTrash() { const Collection::List collections = selectedCollections(); if (collections.isEmpty()) { return; } for (const Collection &collection : collections) { TrashRestoreJob *job = new TrashRestoreJob(collection, q); q->connect(job, &TrashRestoreJob::result, q, [this](KJob*job) {moveCollectionToTrashResult(job);}); } } Item::List selectedItems() const { Item::List items; Q_ASSERT(itemSelectionModel); const QModelIndexList indexes = safeSelectedRows(itemSelectionModel); items.reserve(indexes.count()); for (const QModelIndex &index : indexes) { Q_ASSERT(index.isValid()); const Item item = index.data(EntityTreeModel::ItemRole).value(); Q_ASSERT(item.isValid()); items << item; } return items; } void slotMoveItemToTrash() { const Item::List items = selectedItems(); if (items.isEmpty()) { return; } TrashJob *job = new TrashJob(items, q); q->connect(job, &TrashJob::result, q, [this](KJob *job) {moveItemToTrashResult(job); }); } void slotRestoreItemFromTrash() { const Item::List items = selectedItems(); if (items.isEmpty()) { return; } TrashRestoreJob *job = new TrashRestoreJob(items, q); q->connect(job, &TrashRestoreJob::result, q, [this](KJob *job) {moveItemToTrashResult(job);}); } void slotTrashRestoreCollection() { const Collection::List collections = selectedCollections(); if (collections.isEmpty()) { return; } bool collectionsAreInTrash = false; for (const Collection &collection : collections) { if (collection.hasAttribute()) { collectionsAreInTrash = true; break; } } if (collectionsAreInTrash) { slotRestoreCollectionFromTrash(); } else { slotMoveCollectionToTrash(); } } void slotTrashRestoreItem() { const Item::List items = selectedItems(); if (items.isEmpty()) { return; } bool itemsAreInTrash = false; for (const Item &item : items) { if (item.hasAttribute()) { itemsAreInTrash = true; break; } } if (itemsAreInTrash) { slotRestoreItemFromTrash(); } else { slotMoveItemToTrash(); } } void slotSynchronizeCollection() { Q_ASSERT(collectionSelectionModel); const QModelIndexList list = safeSelectedRows(collectionSelectionModel); if (list.isEmpty()) { return; } const Collection::List collections = selectedCollections(); if (collections.isEmpty()) { return; } for (const Collection &collection : collections) { if (!testAndSetOnlineResources(collection)) { break; } AgentManager::self()->synchronizeCollection(collection, false); } } bool testAndSetOnlineResources(const Akonadi::Collection &collection) { // Shortcut for the Search resource, which is a virtual resource and thus // is always online (but AgentManager does not know about it, so it returns // an invalid AgentInstance, which is "offline"). // // FIXME: AgentManager should return a valid AgentInstance even // for virtual resources, which would be always online. if (collection.resource() == QLatin1String("akonadi_search_resource")) { return true; } Akonadi::AgentInstance instance = Akonadi::AgentManager::self()->instance(collection.resource()); if (!instance.isOnline()) { if (KMessageBox::questionYesNo(parentWidget, i18n("Before syncing folder \"%1\" it is necessary to have the resource online. Do you want to make it online?", collection.displayName()), i18n("Account \"%1\" is offline", instance.name()), KGuiItem(i18nc("@action:button", "Go Online")), KStandardGuiItem::cancel()) != KMessageBox::Yes) { return false; } instance.setIsOnline(true); } return true; } void slotSynchronizeCollectionRecursive() { Q_ASSERT(collectionSelectionModel); const QModelIndexList list = safeSelectedRows(collectionSelectionModel); if (list.isEmpty()) { return; } const Collection::List collections = selectedCollections(); if (collections.isEmpty()) { return; } for (const Collection &collection : collections) { if (!testAndSetOnlineResources(collection)) { break; } AgentManager::self()->synchronizeCollection(collection, true); } } void slotCollectionProperties() { const QModelIndexList list = safeSelectedRows(collectionSelectionModel); if (list.isEmpty()) { return; } const QModelIndex index = list.first(); Q_ASSERT(index.isValid()); const Collection collection = index.data(EntityTreeModel::CollectionRole).value(); Q_ASSERT(collection.isValid()); CollectionPropertiesDialog *dlg = new CollectionPropertiesDialog(collection, mCollectionPropertiesPageNames, parentWidget); dlg->setWindowTitle(contextText(StandardActionManager::CollectionProperties, StandardActionManager::DialogTitle, collection.displayName())); dlg->show(); } void slotCopyItems() { encodeToClipboard(itemSelectionModel); } void slotCutItems() { encodeToClipboard(itemSelectionModel, true); } void slotPaste() { Q_ASSERT(collectionSelectionModel); const QModelIndexList list = safeSelectedRows(collectionSelectionModel); if (list.isEmpty()) { return; } const QModelIndex index = list.first(); Q_ASSERT(index.isValid()); #ifndef QT_NO_CLIPBOARD // TODO: Copy or move? We can't seem to cut yet QAbstractItemModel *model = const_cast(collectionSelectionModel->model()); const QMimeData *mimeData = QApplication::clipboard()->mimeData(); model->dropMimeData(mimeData, isCutAction(mimeData) ? Qt::MoveAction : Qt::CopyAction, -1, -1, index); model->setData(QModelIndex(), false, EntityTreeModel::PendingCutRole); QApplication::clipboard()->clear(); #endif } void slotDeleteItems() { Q_ASSERT(itemSelectionModel); Item::List items; const QModelIndexList indexes = safeSelectedRows(itemSelectionModel); items.reserve(indexes.count()); for (const QModelIndex &index : indexes) { bool ok; const qlonglong id = index.data(EntityTreeModel::ItemIdRole).toLongLong(&ok); Q_ASSERT(ok); items << Item(id); } if (items.isEmpty()) { return; } QMetaObject::invokeMethod(q, [this, items] {slotDeleteItemsDeferred(items); }, Qt::QueuedConnection); } void slotDeleteItemsDeferred(const Akonadi::Item::List &items) { Q_ASSERT(itemSelectionModel); if (KMessageBox::questionYesNo(parentWidget, contextText(StandardActionManager::DeleteItems, StandardActionManager::MessageBoxText, items.count(), QString()), contextText(StandardActionManager::DeleteItems, StandardActionManager::MessageBoxTitle, items.count(), QString()), KStandardGuiItem::del(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) != KMessageBox::Yes) { return; } ItemDeleteJob *job = new ItemDeleteJob(items, q); q->connect(job, &ItemDeleteJob::result, q, [this](KJob*job) {itemDeletionResult(job);}); } void slotLocalSubscription() { SubscriptionDialog *dlg = new SubscriptionDialog(mMimeTypeFilter, parentWidget); dlg->showHiddenCollection(true); dlg->show(); } void slotAddToFavorites() { Q_ASSERT(collectionSelectionModel); Q_ASSERT(favoritesModel); const QModelIndexList list = safeSelectedRows(collectionSelectionModel); if (list.isEmpty()) { return; } for (const QModelIndex &index : list) { Q_ASSERT(index.isValid()); const Collection collection = index.data(EntityTreeModel::CollectionRole).value(); Q_ASSERT(collection.isValid()); favoritesModel->addCollection(collection); } updateActions(); } void slotRemoveFromFavorites() { Q_ASSERT(favoriteSelectionModel); Q_ASSERT(favoritesModel); const QModelIndexList list = safeSelectedRows(favoriteSelectionModel); if (list.isEmpty()) { return; } for (const QModelIndex &index : list) { Q_ASSERT(index.isValid()); const Collection collection = index.data(EntityTreeModel::CollectionRole).value(); Q_ASSERT(collection.isValid()); favoritesModel->removeCollection(collection); } updateActions(); } void slotRenameFavorite() { Q_ASSERT(favoriteSelectionModel); Q_ASSERT(favoritesModel); const QModelIndexList list = safeSelectedRows(favoriteSelectionModel); if (list.isEmpty()) { return; } const QModelIndex index = list.first(); Q_ASSERT(index.isValid()); const Collection collection = index.data(EntityTreeModel::CollectionRole).value(); Q_ASSERT(collection.isValid()); QPointer dlg(new RenameFavoriteDialog( favoritesModel->favoriteLabel(collection), favoritesModel->defaultFavoriteLabel(collection), parentWidget)); if (dlg->exec() == QDialog::Accepted) { favoritesModel->setFavoriteLabel(collection, dlg->newName()); } delete dlg; } void slotSynchronizeFavoriteCollections() { Q_ASSERT(favoritesModel); const auto collections = favoritesModel->collections(); for (const auto &collection : collections) { // there might be virtual collections in favorites which cannot be checked // so let's be safe here, agentmanager asserts otherwise if (!collection.resource().isEmpty()) { AgentManager::self()->synchronizeCollection(collection, false); } } } void slotCopyCollectionTo() { pasteTo(collectionSelectionModel, collectionSelectionModel->model(), CopyCollectionToMenu, Qt::CopyAction); } void slotCopyItemTo() { pasteTo(itemSelectionModel, collectionSelectionModel->model(), CopyItemToMenu, Qt::CopyAction); } void slotMoveCollectionTo() { pasteTo(collectionSelectionModel, collectionSelectionModel->model(), MoveCollectionToMenu, Qt::MoveAction); } void slotMoveItemTo() { pasteTo(itemSelectionModel, collectionSelectionModel->model(), MoveItemToMenu, Qt::MoveAction); } void slotCopyCollectionTo(QAction *action) { pasteTo(collectionSelectionModel, action, Qt::CopyAction); } void slotCopyItemTo(QAction *action) { pasteTo(itemSelectionModel, action, Qt::CopyAction); } void slotMoveCollectionTo(QAction *action) { pasteTo(collectionSelectionModel, action, Qt::MoveAction); } void slotMoveItemTo(QAction *action) { pasteTo(itemSelectionModel, action, Qt::MoveAction); } AgentInstance::List selectedAgentInstances() const { AgentInstance::List instances; Q_ASSERT(collectionSelectionModel); if (collectionSelectionModel->selection().indexes().isEmpty()) { return instances; } const QModelIndexList lstIndexes = collectionSelectionModel->selection().indexes(); for (const QModelIndex &index : lstIndexes) { Q_ASSERT(index.isValid()); const Collection collection = index.data(EntityTreeModel::CollectionRole).value(); Q_ASSERT(collection.isValid()); if (collection.isValid()) { const QString identifier = collection.resource(); instances << AgentManager::self()->instance(identifier); } } return instances; } AgentInstance selectedAgentInstance() const { const AgentInstance::List instances = selectedAgentInstances(); if (instances.isEmpty()) { return AgentInstance(); } return instances.first(); } void slotCreateResource() { QPointer dlg(new Akonadi::AgentTypeDialog(parentWidget)); dlg->setWindowTitle(contextText(StandardActionManager::CreateResource, StandardActionManager::DialogTitle)); for (const QString &mimeType : qAsConst(mMimeTypeFilter)) { dlg->agentFilterProxyModel()->addMimeTypeFilter(mimeType); } for (const QString &capability : qAsConst(mCapabilityFilter)) { dlg->agentFilterProxyModel()->addCapabilityFilter(capability); } if (dlg->exec() == QDialog::Accepted) { const AgentType agentType = dlg->agentType(); if (agentType.isValid()) { AgentInstanceCreateJob *job = new AgentInstanceCreateJob(agentType, q); - q->connect(job, SIGNAL(result(KJob*)), SLOT(resourceCreationResult(KJob*))); + q->connect(job, &KJob::result, q, [this](KJob *job) { resourceCreationResult(job); }); job->configure(parentWidget); job->start(); } } delete dlg; } void slotDeleteResource() { const AgentInstance::List instances = selectedAgentInstances(); if (instances.isEmpty()) { return; } if (KMessageBox::questionYesNo(parentWidget, contextText(StandardActionManager::DeleteResources, StandardActionManager::MessageBoxText, instances.count(), instances.first().name()), contextText(StandardActionManager::DeleteResources, StandardActionManager::MessageBoxTitle, instances.count(), instances.first().name()), KStandardGuiItem::del(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) != KMessageBox::Yes) { return; } for (const AgentInstance &instance : instances) { AgentManager::self()->removeInstance(instance); } } void slotSynchronizeResource() { const AgentInstance::List instances = selectedAgentInstances(); if (instances.isEmpty()) { return; } for (AgentInstance instance : instances) { instance.synchronize(); } } void slotSynchronizeCollectionTree() { const AgentInstance::List instances = selectedAgentInstances(); if (instances.isEmpty()) { return; } for (AgentInstance instance : instances) { instance.synchronizeCollectionTree(); } } void slotResourceProperties() { AgentInstance instance = selectedAgentInstance(); if (!instance.isValid()) { return; } instance.configure(parentWidget); } void slotToggleWorkOffline(bool offline) { setWorkOffline(offline); const AgentInstance::List instances = AgentManager::self()->instances(); for (AgentInstance instance : instances) { instance.setIsOnline(!offline); } } void pasteTo(QItemSelectionModel *selectionModel, const QAbstractItemModel *model, StandardActionManager::Type type, Qt::DropAction dropAction) { const QSet mimeTypes = mimeTypesOfSelection(type); QPointer dlg(new CollectionDialog(const_cast(model))); dlg->setMimeTypeFilter(mimeTypes.values()); if (type == CopyItemToMenu || type == MoveItemToMenu) { dlg->setAccessRightsFilter(Collection::CanCreateItem); } else if (type == CopyCollectionToMenu || type == MoveCollectionToMenu) { dlg->setAccessRightsFilter(Collection::CanCreateCollection); } if (dlg->exec() == QDialog::Accepted && dlg != nullptr) { const QModelIndex index = EntityTreeModel::modelIndexForCollection(collectionSelectionModel->model(), dlg->selectedCollection()); if (!index.isValid()) { delete dlg; return; } const QMimeData *mimeData = selectionModel->model()->mimeData(safeSelectedRows(selectionModel)); QAbstractItemModel *model = const_cast(index.model()); model->dropMimeData(mimeData, dropAction, -1, -1, index); } delete dlg; } void pasteTo(QItemSelectionModel *selectionModel, QAction *action, Qt::DropAction dropAction) { Q_ASSERT(selectionModel); Q_ASSERT(action); if (safeSelectedRows(selectionModel).count() <= 0) { return; } const QMimeData *mimeData = selectionModel->model()->mimeData(selectionModel->selectedRows()); const QModelIndex index = action->data().toModelIndex(); Q_ASSERT(index.isValid()); QAbstractItemModel *model = const_cast(index.model()); const Collection collection = index.data(EntityTreeModel::CollectionRole).value(); addRecentCollection(collection.id()); model->dropMimeData(mimeData, dropAction, -1, -1, index); } void addRecentCollection(Akonadi::Collection::Id id) { QMapIterator > item(mRecentCollectionsMenu); while (item.hasNext()) { item.next(); if (item.value().data()) { item.value().data()->addRecentCollection(item.key(), id); } } } void collectionCreationResult(KJob *job) { if (job->error()) { KMessageBox::error(parentWidget, contextText(StandardActionManager::CreateCollection, StandardActionManager::ErrorMessageText, job->errorString()), contextText(StandardActionManager::CreateCollection, StandardActionManager::ErrorMessageTitle)); } } void collectionDeletionResult(KJob *job) { if (job->error()) { KMessageBox::error(parentWidget, contextText(StandardActionManager::DeleteCollections, StandardActionManager::ErrorMessageText, job->errorString()), contextText(StandardActionManager::DeleteCollections, StandardActionManager::ErrorMessageTitle)); } } void moveCollectionToTrashResult(KJob *job) { if (job->error()) { KMessageBox::error(parentWidget, contextText(StandardActionManager::MoveCollectionsToTrash, StandardActionManager::ErrorMessageText, job->errorString()), contextText(StandardActionManager::MoveCollectionsToTrash, StandardActionManager::ErrorMessageTitle)); } } void moveItemToTrashResult(KJob *job) { if (job->error()) { KMessageBox::error(parentWidget, contextText(StandardActionManager::MoveItemsToTrash, StandardActionManager::ErrorMessageText, job->errorString()), contextText(StandardActionManager::MoveItemsToTrash, StandardActionManager::ErrorMessageTitle)); } } void itemDeletionResult(KJob *job) { if (job->error()) { KMessageBox::error(parentWidget, contextText(StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageText, job->errorString()), contextText(StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageTitle)); } } void resourceCreationResult(KJob *job) { if (job->error()) { KMessageBox::error(parentWidget, contextText(StandardActionManager::CreateResource, StandardActionManager::ErrorMessageText, job->errorString()), contextText(StandardActionManager::CreateResource, StandardActionManager::ErrorMessageTitle)); } } void pasteResult(KJob *job) { if (job->error()) { KMessageBox::error(parentWidget, contextText(StandardActionManager::Paste, StandardActionManager::ErrorMessageText, job->errorString()), contextText(StandardActionManager::Paste, StandardActionManager::ErrorMessageTitle)); } } /** * Returns a set of mime types of the entities that are currently selected. */ QSet mimeTypesOfSelection(StandardActionManager::Type type) const { QModelIndexList list; QSet mimeTypes; const bool isItemAction = (type == CopyItemToMenu || type == MoveItemToMenu); const bool isCollectionAction = (type == CopyCollectionToMenu || type == MoveCollectionToMenu); if (isItemAction) { list = safeSelectedRows(itemSelectionModel); mimeTypes.reserve(list.count()); for (const QModelIndex &index : qAsConst(list)) { mimeTypes << index.data(EntityTreeModel::MimeTypeRole).toString(); } } if (isCollectionAction) { list = safeSelectedRows(collectionSelectionModel); for (const QModelIndex &index : qAsConst(list)) { const Collection collection = index.data(EntityTreeModel::CollectionRole).value(); // The mimetypes that the selected collection can possibly contain const auto mimeTypesResult = AgentManager::self()->instance(collection.resource()).type().mimeTypes(); #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) mimeTypes = mimeTypesResult.toSet(); #else mimeTypes = QSet(mimeTypesResult.begin(), mimeTypesResult.end()); #endif } } return mimeTypes; } /** * Returns whether items with the given @p mimeTypes can be written to the given @p collection. */ bool isWritableTargetCollectionForMimeTypes(const Collection &collection, const QSet &mimeTypes, StandardActionManager::Type type) const { if (collection.isVirtual()) { return false; } const bool isItemAction = (type == CopyItemToMenu || type == MoveItemToMenu); const bool isCollectionAction = (type == CopyCollectionToMenu || type == MoveCollectionToMenu); const bool canContainRequiredMimeTypes = collection.contentMimeTypes().toSet().intersects(mimeTypes); const bool canCreateNewItems = (collection.rights() & Collection::CanCreateItem); const bool canCreateNewCollections = (collection.rights() & Collection::CanCreateCollection); const bool canContainCollections = collection.contentMimeTypes().contains(Collection::mimeType()) || collection.contentMimeTypes().contains(Collection::virtualMimeType()); const bool resourceAllowsRequiredMimeTypes = AgentManager::self()->instance(collection.resource()).type().mimeTypes().toSet().contains(mimeTypes); const bool isReadOnlyForItems = (isItemAction && (!canCreateNewItems || !canContainRequiredMimeTypes)); const bool isReadOnlyForCollections = (isCollectionAction && (!canCreateNewCollections || !canContainCollections || !resourceAllowsRequiredMimeTypes)); return !(CollectionUtils::isStructural(collection) || isReadOnlyForItems || isReadOnlyForCollections); } void fillFoldersMenu(const Akonadi::Collection::List &selectedCollectionsList, const QSet &mimeTypes, StandardActionManager::Type type, QMenu *menu, const QAbstractItemModel *model, const QModelIndex &parentIndex) { const int rowCount = model->rowCount(parentIndex); for (int row = 0; row < rowCount; ++row) { const QModelIndex index = model->index(row, 0, parentIndex); const Collection collection = model->data(index, EntityTreeModel::CollectionRole).value(); if (collection.isVirtual()) { continue; } const bool readOnly = !isWritableTargetCollectionForMimeTypes(collection, mimeTypes, type); const bool collectionIsSelected = selectedCollectionsList.contains(collection); if (type == MoveCollectionToMenu && collectionIsSelected) { continue; } QString label = model->data(index).toString(); label.replace(QLatin1Char('&'), QStringLiteral("&&")); const QIcon icon = model->data(index, Qt::DecorationRole).value(); if (model->rowCount(index) > 0) { // new level QMenu *popup = new QMenu(menu); const bool moveAction = (type == MoveCollectionToMenu || type == MoveItemToMenu); popup->setObjectName(QStringLiteral("subMenu")); popup->setTitle(label); popup->setIcon(icon); fillFoldersMenu(selectedCollectionsList, mimeTypes, type, popup, model, index); if (!(type == CopyCollectionToMenu && collectionIsSelected)) { if (!readOnly) { popup->addSeparator(); QAction *action = popup->addAction(moveAction ? i18n("Move to This Folder") : i18n("Copy to This Folder")); action->setData(QVariant::fromValue(index)); } } if (!popup->isEmpty()) { menu->addMenu(popup); } } else { // insert an item QAction *action = menu->addAction(icon, label); action->setData(QVariant::fromValue(index)); action->setEnabled(!readOnly && !collectionIsSelected); } } } void checkModelsConsistency() { if (favoritesModel == nullptr || favoriteSelectionModel == nullptr) { // No need to check when the favorite collections feature is not used return; } // find the base ETM of the favourites view const QAbstractItemModel *favModel = favoritesModel; while (const QAbstractProxyModel *proxy = qobject_cast(favModel)) { favModel = proxy->sourceModel(); } // Check that the collection selection model maps to the same // EntityTreeModel than favoritesModel if (collectionSelectionModel != nullptr) { const QAbstractItemModel *model = collectionSelectionModel->model(); while (const QAbstractProxyModel *proxy = qobject_cast(model)) { model = proxy->sourceModel(); } Q_ASSERT(model == favModel); } // Check that the favorite selection model maps to favoritesModel const QAbstractItemModel *model = favoriteSelectionModel->model(); while (const QAbstractProxyModel *proxy = qobject_cast(model)) { model = proxy->sourceModel(); } Q_ASSERT(model == favModel); } void markCutAction(QMimeData *mimeData, bool cut) const { if (!cut) { return; } const QByteArray cutSelectionData = "1"; //krazy:exclude=doublequote_chars mimeData->setData(QStringLiteral("application/x-kde.akonadi-cutselection"), cutSelectionData); } bool isCutAction(const QMimeData *mimeData) const { const QByteArray data = mimeData->data(QStringLiteral("application/x-kde.akonadi-cutselection")); if (data.isEmpty()) { return false; } else { return (data.at(0) == '1'); // true if 1 } } void setContextText(StandardActionManager::Type type, StandardActionManager::TextContext context, const QString &data) { ContextTextEntry entry; entry.text = data; contextTexts[type].insert(context, entry); } void setContextText(StandardActionManager::Type type, StandardActionManager::TextContext context, const KLocalizedString &data) { ContextTextEntry entry; entry.localizedText = data; contextTexts[type].insert(context, entry); } QString contextText(StandardActionManager::Type type, StandardActionManager::TextContext context) const { return contextTexts[type].value(context).text; } QString contextText(StandardActionManager::Type type, StandardActionManager::TextContext context, const QString &value) const { KLocalizedString text = contextTexts[type].value(context).localizedText; if (text.isEmpty()) { return contextTexts[type].value(context).text; } return text.subs(value).toString(); } QString contextText(StandardActionManager::Type type, StandardActionManager::TextContext context, int count, const QString &value) const { KLocalizedString text = contextTexts[type].value(context).localizedText; if (text.isEmpty()) { return contextTexts[type].value(context).text; } const QString str = text.subs(count).toString(); const int argCount = str.count(QRegExp(QStringLiteral("%[0-9]"))); if (argCount > 0) { return text.subs(count).subs(value).toString(); } else { return text.subs(count).toString(); } } StandardActionManager *q; KActionCollection *actionCollection; QWidget *parentWidget; QItemSelectionModel *collectionSelectionModel; QItemSelectionModel *itemSelectionModel; FavoriteCollectionsModel *favoritesModel; QItemSelectionModel *favoriteSelectionModel; bool insideSelectionSlot; QVector actions; QHash pluralLabels; QHash pluralIconLabels; struct ContextTextEntry { QString text; KLocalizedString localizedText; bool isLocalized; }; typedef QHash ContextTexts; QHash contextTexts; ActionStateManager mActionStateManager; QStringList mMimeTypeFilter; QStringList mCapabilityFilter; QStringList mCollectionPropertiesPageNames; QMap > mRecentCollectionsMenu; }; //@endcond StandardActionManager::StandardActionManager(KActionCollection *actionCollection, QWidget *parent) : QObject(parent) , d(new Private(this)) { d->parentWidget = parent; d->actionCollection = actionCollection; d->mActionStateManager.setReceiver(this); #ifndef QT_NO_CLIPBOARD - connect(QApplication::clipboard(), SIGNAL(changed(QClipboard::Mode)), SLOT(clipboardChanged(QClipboard::Mode))); + connect(QApplication::clipboard(), &QClipboard::changed, this, [this](auto mode) { d->clipboardChanged(mode); }); #endif } StandardActionManager::~StandardActionManager() { delete d; } void StandardActionManager::setCollectionSelectionModel(QItemSelectionModel *selectionModel) { d->collectionSelectionModel = selectionModel; - connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - SLOT(collectionSelectionChanged())); + connect(selectionModel, &QItemSelectionModel::selectionChanged, this, [this]() { d->collectionSelectionChanged(); }); d->checkModelsConsistency(); } void StandardActionManager::setItemSelectionModel(QItemSelectionModel *selectionModel) { d->itemSelectionModel = selectionModel; - connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - SLOT(updateActions())); + connect(selectionModel, &QItemSelectionModel::selectionChanged, this, [this]() { d->updateActions(); }); } void StandardActionManager::setFavoriteCollectionsModel(FavoriteCollectionsModel *favoritesModel) { d->favoritesModel = favoritesModel; d->checkModelsConsistency(); } void StandardActionManager::setFavoriteSelectionModel(QItemSelectionModel *selectionModel) { d->favoriteSelectionModel = selectionModel; - connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - SLOT(favoriteSelectionChanged())); + connect(selectionModel, &QItemSelectionModel::selectionChanged, this, [this]() { d->favoriteSelectionChanged(); }); d->checkModelsConsistency(); } QAction *StandardActionManager::createAction(Type type) { Q_ASSERT(type < LastType); if (d->actions[type]) { return d->actions[type]; } QAction *action = nullptr; switch (standardActionData[type].actionType) { case NormalAction: case ActionWithAlternative: action = new QAction(d->parentWidget); break; case ActionAlternative: d->actions[type] = d->actions[type - 1]; Q_ASSERT(d->actions[type]); if ((LastType > type + 1) && (standardActionData[type + 1].actionType == ActionAlternative)) { createAction(static_cast(type + 1)); //ensure that alternative actions are initialized when not created by createAllActions } return d->actions[type]; case MenuAction: action = new KActionMenu(d->parentWidget); break; case ToggleAction: action = new KToggleAction(d->parentWidget); break; } if (d->pluralLabels.contains(type) && !d->pluralLabels.value(type).isEmpty()) { action->setText(d->pluralLabels.value(type).subs(1).toString()); } else if (standardActionData[type].label) { action->setText(i18n(standardActionData[type].label)); } if (d->pluralIconLabels.contains(type) && !d->pluralIconLabels.value(type).isEmpty()) { action->setIconText(d->pluralIconLabels.value(type).subs(1).toString()); } else if (standardActionData[type].iconLabel) { action->setIconText(i18n(standardActionData[type].iconLabel)); } if (standardActionData[type].icon) { action->setIcon(QIcon::fromTheme(QString::fromLatin1(standardActionData[type].icon))); } if (d->actionCollection) { d->actionCollection->setDefaultShortcut(action, QKeySequence(standardActionData[type].shortcut)); } else { action->setShortcut(standardActionData[type].shortcut); } if (standardActionData[type].slot) { switch (standardActionData[type].actionType) { case NormalAction: case ActionWithAlternative: - connect(action, SIGNAL(triggered()), standardActionData[type].slot); + connect(action, SIGNAL(triggered()), standardActionData[type].slot); // clazy:exclude=old-style-connect break; case MenuAction: { KActionMenu *actionMenu = qobject_cast(action); - connect(actionMenu->menu(), SIGNAL(triggered(QAction*)), standardActionData[type].slot); + connect(actionMenu->menu(), SIGNAL(triggered(QAction*)), standardActionData[type].slot); // clazy:exclude=old-style-connect break; } case ToggleAction: { - connect(action, SIGNAL(triggered(bool)), standardActionData[type].slot); + connect(action, SIGNAL(triggered(bool)), standardActionData[type].slot); // clazy:exclude=old-style-connect break; } case ActionAlternative: Q_ASSERT(0); } } if (type == ToggleWorkOffline) { // inititalize the action state with information from config file - disconnect(action, SIGNAL(triggered(bool)), this, standardActionData[type].slot); + disconnect(action, SIGNAL(triggered(bool)), this, standardActionData[type].slot); // clazy:exclude=old-style-connect action->setChecked(workOffline()); - connect(action, SIGNAL(triggered(bool)), this, standardActionData[type].slot); + connect(action, SIGNAL(triggered(bool)), this, standardActionData[type].slot); // clazy:exclude=old-style-connect //TODO: find a way to check for updates to the config file } Q_ASSERT(standardActionData[type].name); d->actionCollection->addAction(QString::fromLatin1(standardActionData[type].name), action); d->actions[type] = action; if ((standardActionData[type].actionType == ActionWithAlternative) && (standardActionData[type + 1].actionType == ActionAlternative)) { createAction(static_cast(type + 1)); //ensure that alternative actions are initialized when not created by createAllActions } d->updateActions(); return action; } void StandardActionManager::createAllActions() { for (uint i = 0; i < LastType; ++i) { createAction((Type)i); } } QAction *StandardActionManager::action(Type type) const { Q_ASSERT(type < LastType); return d->actions[type]; } void StandardActionManager::setActionText(Type type, const KLocalizedString &text) { Q_ASSERT(type < LastType); d->pluralLabels.insert(type, text); d->updateActions(); } void StandardActionManager::interceptAction(Type type, bool intercept) { Q_ASSERT(type < LastType); const QAction *action = d->actions[type]; if (!action) { return; } if (intercept) { - disconnect(action, SIGNAL(triggered()), this, standardActionData[type].slot); + disconnect(action, SIGNAL(triggered()), this, standardActionData[type].slot); // clazy:exclude=old-style-connect } else { - connect(action, SIGNAL(triggered()), standardActionData[type].slot); + connect(action, SIGNAL(triggered()), standardActionData[type].slot); // clazy:exclude=old-style-connect } } Akonadi::Collection::List StandardActionManager::selectedCollections() const { Collection::List collections; if (!d->collectionSelectionModel) { return collections; } const QModelIndexList lst = safeSelectedRows(d->collectionSelectionModel); for (const QModelIndex &index : lst) { const Collection collection = index.data(EntityTreeModel::CollectionRole).value(); if (collection.isValid()) { collections << collection; } } return collections; } Item::List StandardActionManager::selectedItems() const { Item::List items; if (!d->itemSelectionModel) { return items; } const QModelIndexList lst = safeSelectedRows(d->itemSelectionModel); for (const QModelIndex &index : lst) { const Item item = index.data(EntityTreeModel::ItemRole).value(); if (item.isValid()) { items << item; } } return items; } void StandardActionManager::setContextText(Type type, TextContext context, const QString &text) { d->setContextText(type, context, text); } void StandardActionManager::setContextText(Type type, TextContext context, const KLocalizedString &text) { d->setContextText(type, context, text); } void StandardActionManager::setMimeTypeFilter(const QStringList &mimeTypes) { d->mMimeTypeFilter = mimeTypes; } void StandardActionManager::setCapabilityFilter(const QStringList &capabilities) { d->mCapabilityFilter = capabilities; } void StandardActionManager::setCollectionPropertiesPageNames(const QStringList &names) { d->mCollectionPropertiesPageNames = names; } void StandardActionManager::createActionFolderMenu(QMenu *menu, Type type) { d->createActionFolderMenu(menu, type); } #include "moc_standardactionmanager.cpp" diff --git a/src/widgets/standardactionmanager.h b/src/widgets/standardactionmanager.h index 92f238a87..cd2316f94 100644 --- a/src/widgets/standardactionmanager.h +++ b/src/widgets/standardactionmanager.h @@ -1,430 +1,423 @@ /* Copyright (c) 2008 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_STANDARDACTIONMANAGER_H #define AKONADI_STANDARDACTIONMANAGER_H #include "akonadiwidgets_export.h" #include #include "collection.h" #include "item.h" class QAction; class KActionCollection; class KLocalizedString; class QItemSelectionModel; class QWidget; class QMenu; namespace Akonadi { class FavoriteCollectionsModel; /** * @short Manages generic actions for collection and item views. * * Manages generic Akonadi actions common for all types. This covers * creating of the actions with appropriate labels, icons, shortcuts * etc., updating the action state depending on the current selection * as well as default implementations for the actual operations. * * If the default implementation is not appropriate for your application * you can still use the state tracking by disconnecting the triggered() * signal and re-connecting it to your implementation. The actual KAction * objects can be retrieved by calling createAction() or action() for that. * * If the default look and feel (labels, icons, shortcuts) of the actions * is not appropriate for your application, you can access them as noted * above and customize them to your needs. Additionally, you can set a * KLocalizedString which should be used as a action label with correct * plural handling for actions operating on multiple objects with * setActionText(). * * Finally, if you have special needs for the action states, connect to * the actionStateUpdated() signal and adjust the state accordingly. * * The following actions are provided (KAction name in parenthesis): * - Creation of a new collection (@c akonadi_collection_create) * - Copying of selected collections (@c akonadi_collection_copy) * - Deletion of selected collections (@c akonadi_collection_delete) * - Synchronization of selected collections (@c akonadi_collection_sync) * - Showing the collection properties dialog for the current collection (@c akonadi_collection_properties) * - Copying of selected items (@c akonadi_itemcopy) * - Pasting collections, items or raw data (@c akonadi_paste) * - Deleting of selected items (@c akonadi_item_delete) * - Managing local subscriptions (@c akonadi_manage_local_subscriptions) * * The following example shows how to use standard actions in your application: * * @code * * Akonadi::StandardActionManager *actMgr = new Akonadi::StandardActionManager( actionCollection(), this ); * actMgr->setCollectionSelectionModel( collectionView->collectionSelectionModel() ); * actMgr->createAllActions(); * * @endcode * * Additionally you have to add the actions to the KXMLGUI file of your application, * using the names listed above. * * If you only need a subset of the actions provided, you can call createAction() * instead of createAllActions() for the action types you want. * * If you want to use your own implementation of the actual action operation and * not the default implementation, you can call interceptAction() on the action type * you want to handle yourself and connect the slot with your own implementation * to the triggered() signal of the action: * * @code * * using namespace Akonadi; * * StandardActionManager *manager = new StandardActionManager( actionCollection(), this ); * manager->setCollectionSelectionModel( collectionView->collectionSelectionModel() ); * manager->createAllActions(); * * // disable default implementation * manager->interceptAction( StandardActionManager::CopyCollections ); * * // connect your own implementation * connect( manager->action( StandardActionManager::CopyCollections ), SIGNAL(triggered(bool)), * this, SLOT(myCopyImplementation()) ); * ... * * void MyClass::myCopyImplementation() * { * const Collection::List collections = manager->selectedCollections(); * for ( const Collection &collection : collections ) { * // copy the collection manually... * } * } * * @endcode * * @todo collection deleting and sync do not support multi-selection yet * * @author Volker Krause */ class AKONADIWIDGETS_EXPORT StandardActionManager : public QObject { Q_OBJECT public: /** * Describes the supported actions. */ enum Type { CreateCollection, ///< Creates an collection CopyCollections, ///< Copies the selected collections DeleteCollections, ///< Deletes the selected collections SynchronizeCollections, ///< Synchronizes collections CollectionProperties, ///< Provides collection properties CopyItems, ///< Copies the selected items Paste, ///< Paste collections or items DeleteItems, ///< Deletes the selected items ManageLocalSubscriptions, ///< Manages local subscriptions AddToFavoriteCollections, ///< Add the collection to the favorite collections model @since 4.4 RemoveFromFavoriteCollections, ///< Remove the collection from the favorite collections model @since 4.4 RenameFavoriteCollection, ///< Rename the collection of the favorite collections model @since 4.4 CopyCollectionToMenu, ///< Menu allowing to quickly copy a collection into another collection @since 4.4 CopyItemToMenu, ///< Menu allowing to quickly copy an item into a collection @since 4.4 MoveItemToMenu, ///< Menu allowing to move item into a collection @since 4.4 MoveCollectionToMenu, ///< Menu allowing to move a collection into another collection @since 4.4 CutItems, ///< Cuts the selected items @since 4.4 CutCollections, ///< Cuts the selected collections @since 4.4 CreateResource, ///< Creates a new resource @since 4.6 DeleteResources, ///< Deletes the selected resources @since 4.6 ResourceProperties, ///< Provides the resource properties @since 4.6 SynchronizeResources, ///< Synchronizes the selected resources @since 4.6 ToggleWorkOffline, ///< Toggles the work offline state of all resources @since 4.6 CopyCollectionToDialog, ///< Copy a collection into another collection, select the target in a dialog @since 4.6 MoveCollectionToDialog, ///< Move a collection into another collection, select the target in a dialog @since 4.6 CopyItemToDialog, ///< Copy an item into a collection, select the target in a dialog @since 4.6 MoveItemToDialog, ///< Move an item into a collection, select the target in a dialog @since 4.6 SynchronizeCollectionsRecursive, ///< Synchronizes collections in a recursive way @since 4.6 MoveCollectionsToTrash, ///< Moves the selected collection to trash and marks it as deleted, needs EntityDeletedAttribute @since 4.8 MoveItemsToTrash, ///< Moves the selected items to trash and marks them as deleted, needs EntityDeletedAttribute @since 4.8 RestoreCollectionsFromTrash, ///< Restores the selected collection from trash, needs EntityDeletedAttribute @since 4.8 RestoreItemsFromTrash, ///< Restores the selected items from trash, needs EntityDeletedAttribute @since 4.8 MoveToTrashRestoreCollection, ///< Move Collection to Trash or Restore it from Trash, needs EntityDeletedAttribute @since 4.8 MoveToTrashRestoreCollectionAlternative, ///< Helper type for MoveToTrashRestoreCollection, do not create directly. Use this to override texts of the restore action. @since 4.8 MoveToTrashRestoreItem, ///< Move Item to Trash or Restore it from Trash, needs EntityDeletedAttribute @since 4.8 MoveToTrashRestoreItemAlternative, ///< Helper type for MoveToTrashRestoreItem, do not create directly. Use this to override texts of the restore action. @since 4.8 SynchronizeFavoriteCollections, ///< Synchronize favorite collections @since 4.8 SynchronizeCollectionTree, ///< Synchronize collection tree @since 4.15 LastType ///< Marks last action }; /** * Describes the text context that can be customized. */ enum TextContext { DialogTitle, ///< The window title of a dialog DialogText, ///< The text of a dialog MessageBoxTitle, ///< The window title of a message box MessageBoxText, ///< The text of a message box MessageBoxAlternativeText, ///< An alternative text of a message box ErrorMessageTitle, ///< The window title of an error message ErrorMessageText ///< The text of an error message }; /** * Creates a new standard action manager. * * @param actionCollection The action collection to operate on. * @param parent The parent widget. */ explicit StandardActionManager(KActionCollection *actionCollection, QWidget *parent = nullptr); /** * Destroys the standard action manager. */ ~StandardActionManager(); /** * Sets the collection selection model based on which the collection * related actions should operate. If none is set, all collection actions * will be disabled. * * @param selectionModel model to be set for collection */ void setCollectionSelectionModel(QItemSelectionModel *selectionModel); /** * Sets the item selection model based on which the item related actions * should operate. If none is set, all item actions will be disabled. * * @param selectionModel selection model for items */ void setItemSelectionModel(QItemSelectionModel *selectionModel); /** * Sets the favorite collections model based on which the collection * relatedactions should operate. If none is set, the "Add to Favorite Folders" action * will be disabled. * * @param favoritesModel model for the user's favorite collections * @since 4.4 */ void setFavoriteCollectionsModel(FavoriteCollectionsModel *favoritesModel); /** * Sets the favorite collection selection model based on which the favorite * collection related actions should operate. If none is set, all favorite modifications * actions will be disabled. * * @param selectionModel selection model for favorite collections * @since 4.4 */ void setFavoriteSelectionModel(QItemSelectionModel *selectionModel); /** * Creates the action of the given type and adds it to the action collection * specified in the constructor if it does not exist yet. The action is * connected to its default implementation provided by this class. * * @param type action to be created */ QAction *createAction(Type type); /** * Convenience method to create all standard actions. * @see createAction() */ void createAllActions(); /** * Returns the action of the given type, 0 if it has not been created (yet). * @param type action type */ QAction *action(Type type) const; /** * Sets the label of the action @p type to @p text, which is used during * updating the action state and substituted according to the number of * selected objects. This is mainly useful to customize the label of actions * that can operate on multiple objects. * @param type the action to set a text for * @param text the text to display for the given action * Example: * @code * acctMgr->setActionText( Akonadi::StandardActionManager::CopyItems, * ki18np( "Copy Mail", "Copy %1 Mails" ) ); * @endcode */ void setActionText(Type type, const KLocalizedString &text); /** * Sets whether the default implementation for the given action @p type * shall be executed when the action is triggered. * * @param type action type * @param intercept If @c false, the default implementation will be executed, * if @c true no action is taken. * * @since 4.6 */ void interceptAction(Type type, bool intercept = true); /** * Returns the list of collections that are currently selected. * The list is empty if no collection is currently selected. * * @since 4.6 */ Akonadi::Collection::List selectedCollections() const; /** * Returns the list of items that are currently selected. * The list is empty if no item is currently selected. * * @since 4.6 */ Akonadi::Item::List selectedItems() const; /** * Sets the @p text of the action @p type for the given @p context. * * @param type action type * @param context context for action * @param text content to set for the action * @since 4.6 */ void setContextText(Type type, TextContext context, const QString &text); /** * Sets the @p text of the action @p type for the given @p context. * * @param type action type * @param context context for action * @param text content to set for the action * @since 4.6 */ void setContextText(Type type, TextContext context, const KLocalizedString &text); /** * Sets the mime type filter that will be used when creating new resources. * * @param mimeTypes filter for creating new resources * @since 4.6 */ void setMimeTypeFilter(const QStringList &mimeTypes); /** * Sets the capability filter that will be used when creating new resources. * * @param capabilities filter for creating new resources * @since 4.6 */ void setCapabilityFilter(const QStringList &capabilities); /** * Sets the page @p names of the config pages that will be used by the * built-in collection properties dialog. * * @param names list of names which will be used * @since 4.6 */ void setCollectionPropertiesPageNames(const QStringList &names); /** * Create a popup menu. * * @param menu parent menu for a popup * @param type action type * @since 4.8 */ void createActionFolderMenu(QMenu *menu, Type type); Q_SIGNALS: /** * This signal is emitted whenever the action state has been updated. * In case you have special needs for changing the state of some actions, * connect to this signal and adjust the action state. */ void actionStateUpdated(); private: //@cond PRIVATE class Private; Private *const d; Q_PRIVATE_SLOT(d, void updateActions()) -#ifndef QT_NO_CLIPBOARD - Q_PRIVATE_SLOT(d, void clipboardChanged(QClipboard::Mode)) -#endif - Q_PRIVATE_SLOT(d, void collectionSelectionChanged()) - Q_PRIVATE_SLOT(d, void favoriteSelectionChanged()) Q_PRIVATE_SLOT(d, void slotCreateCollection()) Q_PRIVATE_SLOT(d, void slotCopyCollections()) Q_PRIVATE_SLOT(d, void slotCutCollections()) Q_PRIVATE_SLOT(d, void slotDeleteCollection()) Q_PRIVATE_SLOT(d, void slotMoveCollectionToTrash()) Q_PRIVATE_SLOT(d, void slotMoveItemToTrash()) Q_PRIVATE_SLOT(d, void slotRestoreCollectionFromTrash()) Q_PRIVATE_SLOT(d, void slotRestoreItemFromTrash()) Q_PRIVATE_SLOT(d, void slotTrashRestoreCollection()) Q_PRIVATE_SLOT(d, void slotTrashRestoreItem()) Q_PRIVATE_SLOT(d, void slotSynchronizeCollection()) Q_PRIVATE_SLOT(d, void slotSynchronizeCollectionRecursive()) Q_PRIVATE_SLOT(d, void slotSynchronizeFavoriteCollections()) Q_PRIVATE_SLOT(d, void slotCollectionProperties()) Q_PRIVATE_SLOT(d, void slotCopyItems()) Q_PRIVATE_SLOT(d, void slotCutItems()) Q_PRIVATE_SLOT(d, void slotPaste()) Q_PRIVATE_SLOT(d, void slotDeleteItems()) Q_PRIVATE_SLOT(d, void slotDeleteItemsDeferred(const Akonadi::Item::List &)) Q_PRIVATE_SLOT(d, void slotLocalSubscription()) Q_PRIVATE_SLOT(d, void slotAddToFavorites()) Q_PRIVATE_SLOT(d, void slotRemoveFromFavorites()) Q_PRIVATE_SLOT(d, void slotRenameFavorite()) Q_PRIVATE_SLOT(d, void slotCopyCollectionTo()) Q_PRIVATE_SLOT(d, void slotMoveCollectionTo()) Q_PRIVATE_SLOT(d, void slotCopyItemTo()) Q_PRIVATE_SLOT(d, void slotMoveItemTo()) Q_PRIVATE_SLOT(d, void slotCopyCollectionTo(QAction *)) Q_PRIVATE_SLOT(d, void slotMoveCollectionTo(QAction *)) Q_PRIVATE_SLOT(d, void slotCopyItemTo(QAction *)) Q_PRIVATE_SLOT(d, void slotMoveItemTo(QAction *)) Q_PRIVATE_SLOT(d, void slotCreateResource()) Q_PRIVATE_SLOT(d, void slotDeleteResource()) Q_PRIVATE_SLOT(d, void slotResourceProperties()) Q_PRIVATE_SLOT(d, void slotSynchronizeResource()) Q_PRIVATE_SLOT(d, void slotToggleWorkOffline(bool)) Q_PRIVATE_SLOT(d, void slotSynchronizeCollectionTree()) Q_PRIVATE_SLOT(d, void collectionCreationResult(KJob *)) Q_PRIVATE_SLOT(d, void moveItemToTrashResult(KJob *)) Q_PRIVATE_SLOT(d, void resourceCreationResult(KJob *)) Q_PRIVATE_SLOT(d, void pasteResult(KJob *)) Q_PRIVATE_SLOT(d, void enableAction(int, bool)) Q_PRIVATE_SLOT(d, void updatePluralLabel(int, int)) Q_PRIVATE_SLOT(d, void updateAlternatingAction(int)) Q_PRIVATE_SLOT(d, bool isFavoriteCollection(const Akonadi::Collection &)) - - Q_PRIVATE_SLOT(d, void aboutToShowMenu()) //@endcond }; } #endif