diff --git a/abstractentry.cpp b/abstractentry.cpp index ac3a8f0bb..5ffab3464 100644 --- a/abstractentry.cpp +++ b/abstractentry.cpp @@ -1,106 +1,108 @@ /*************************************************************************** * Copyright (C) 2014-2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "abstractentry.h" +#include + AbstractEntry::AbstractEntry(AbstractModel *owner) : m_owner(owner) { } AbstractEntry::~AbstractEntry() { } AbstractModel *AbstractEntry::owner() const { return m_owner; } bool AbstractEntry::isValid() const { return true; } QIcon AbstractEntry::icon() const { return QIcon(); } QString AbstractEntry::name() const { return QString(); } QString AbstractEntry::group() const { return QString(); } QString AbstractEntry::description() const { return QString(); } QString AbstractEntry::id() const { return QString(); } QUrl AbstractEntry::url() const { return QUrl(); } bool AbstractEntry::hasChildren() const { return false; } AbstractModel *AbstractEntry::childModel() const { return nullptr; } bool AbstractEntry::hasActions() const { return false; } QVariantList AbstractEntry::actions() const { return QVariantList(); } bool AbstractEntry::run(const QString& actionId, const QVariant &argument) { Q_UNUSED(actionId) Q_UNUSED(argument) return false; } AbstractGroupEntry::AbstractGroupEntry(AbstractModel *owner) : AbstractEntry(owner) { } SeparatorEntry::SeparatorEntry(AbstractModel *owner) : AbstractEntry(owner) { } diff --git a/appentry.cpp b/appentry.cpp index 6f2cc8f8a..ca29c5748 100644 --- a/appentry.cpp +++ b/appentry.cpp @@ -1,297 +1,302 @@ /*************************************************************************** * Copyright (C) 201 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include #include "appentry.h" #include "actionlist.h" #include "appsmodel.h" #include "containmentinterface.h" #include #include #include #include #if HAVE_X11 #include #endif #include #include #include #include #include #include #include #include #include #include #include AppEntry::AppEntry(AbstractModel *owner, KService::Ptr service, NameFormat nameFormat) : AbstractEntry(owner) , m_service(service) { if (m_service) { init(nameFormat); } } AppEntry::AppEntry(AbstractModel *owner, const QString &id) : AbstractEntry(owner) { const QUrl url(id); if (url.scheme() == QStringLiteral("preferred")) { m_service = defaultAppByName(url.host()); m_id = id; } else { m_service = KService::serviceByStorageId(id); } if (m_service) { init((NameFormat)owner->rootModel()->property("appNameFormat").toInt()); } } void AppEntry::init(NameFormat nameFormat) { m_name = nameFromService(m_service, nameFormat); if (nameFormat == GenericNameOnly) { m_description = nameFromService(m_service, NameOnly); } else { m_description = nameFromService(m_service, GenericNameOnly); } } bool AppEntry::isValid() const { return m_service; } QIcon AppEntry::icon() const { if (m_icon.isNull()) { m_icon = QIcon::fromTheme(m_service->icon(), QIcon::fromTheme("unknown")); } return m_icon; } QString AppEntry::name() const { return m_name; } QString AppEntry::description() const { return m_description; } KService::Ptr AppEntry::service() const { return m_service; } QString AppEntry::id() const { if (!m_id.isEmpty()) { return m_id; } return m_service->storageId(); } +QString AppEntry::menuId() const +{ + return m_service->menuId(); +} + QUrl AppEntry::url() const { return QUrl::fromLocalFile(m_service->entryPath()); } bool AppEntry::hasActions() const { return true; } QVariantList AppEntry::actions() const { QVariantList actionList; actionList << Kicker::jumpListActions(m_service); if (!actionList.isEmpty()) { actionList << Kicker::createSeparatorActionItem(); } QObject *appletInterface = m_owner->rootModel()->property("appletInterface").value(); const bool systemImmutable = appletInterface->property("immutability").toInt() == Plasma::Types::SystemImmutable; const QVariantList &addLauncherActions = Kicker::createAddLauncherActionList(appletInterface, m_service); if (!systemImmutable && !addLauncherActions.isEmpty()) { actionList << addLauncherActions << Kicker::createSeparatorActionItem(); } const QVariantList &recentDocuments = Kicker::recentDocumentActions(m_service); if (!recentDocuments.isEmpty()) { actionList << recentDocuments << Kicker::createSeparatorActionItem(); } // Don't allow adding launchers, editing, hiding, or uninstalling applications // when system is immutable. if (systemImmutable) { return actionList; } if (m_service->isApplication()) { actionList << Kicker::createSeparatorActionItem(); actionList << Kicker::editApplicationAction(m_service); actionList << Kicker::appstreamActions(m_service); } QQmlPropertyMap *appletConfig = qobject_cast(appletInterface->property("configuration").value()); if (appletConfig && appletConfig->contains("hiddenApplications") && qobject_cast(m_owner)) { const QStringList &hiddenApps = appletConfig->value("hiddenApplications").toStringList(); if (!hiddenApps.contains(m_service->menuId())) { actionList << Kicker::createActionItem(i18n("Hide Application"), "hideApplication"); } } return actionList; } bool AppEntry::run(const QString& actionId, const QVariant &argument) { if (!m_service->isValid()) { return false; } if (actionId.isEmpty()) { quint32 timeStamp = 0; #if HAVE_X11 if (QX11Info::isPlatformX11()) { timeStamp = QX11Info::appUserTime(); } #endif // TODO Once we depend on KDE Frameworks 5.24 and D1902 is merged, use KRun::runApplication instead KRun::runService(*m_service, {}, nullptr, true, {}, KStartupInfo::createNewStartupIdForTimestamp(timeStamp)); KActivities::ResourceInstance::notifyAccessed(QUrl("applications:" + m_service->storageId()), "org.kde.plasma.kicker"); return true; } QObject *appletInterface = m_owner->rootModel()->property("appletInterface").value(); if (Kicker::handleAddLauncherAction(actionId, appletInterface, m_service)) { return true; } else if (Kicker::handleEditApplicationAction(actionId, m_service)) { return true; } else if (Kicker::handleAppstreamActions(actionId, argument)) { return true; } else if (actionId == "_kicker_jumpListAction") { return KRun::run(argument.toString(), {}, nullptr, m_service->name(), m_service->icon()); } return Kicker::handleRecentDocumentAction(m_service, actionId, argument); } QString AppEntry::nameFromService(const KService::Ptr service, NameFormat nameFormat) { const QString &name = service->name(); QString genericName = service->genericName(); if (genericName.isEmpty()) { genericName = service->comment(); } if (nameFormat == NameOnly || genericName.isEmpty() || name == genericName) { return name; } else if (nameFormat == GenericNameOnly) { return genericName; } else if (nameFormat == NameAndGenericName) { return i18nc("App name (Generic name)", "%1 (%2)", name, genericName); } else { return i18nc("Generic name (App name)", "%1 (%2)", genericName, name); } } KService::Ptr AppEntry::defaultAppByName(const QString& name) { if (name == QLatin1String("browser")) { KConfigGroup config(KSharedConfig::openConfig(), "General"); QString browser = config.readPathEntry("BrowserApplication", QString()); if (browser.isEmpty()) { return KMimeTypeTrader::self()->preferredService(QLatin1String("text/html")); } else if (browser.startsWith('!')) { browser = browser.mid(1); } return KService::serviceByStorageId(browser); } return KService::Ptr(); } AppGroupEntry::AppGroupEntry(AppsModel *parentModel, KServiceGroup::Ptr group, bool paginate, int pageSize, bool flat, bool sorted, bool separators, int appNameFormat) : AbstractGroupEntry(parentModel), m_group(group) { AppsModel* model = new AppsModel(group->entryPath(), paginate, pageSize, flat, sorted, separators, parentModel); model->setAppNameFormat(appNameFormat); m_childModel = model; QObject::connect(parentModel, &AppsModel::cleared, model, &AppsModel::deleteLater); QObject::connect(model, &AppsModel::countChanged, [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } } ); QObject::connect(model, &AppsModel::hiddenEntriesChanged, [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } } ); } QIcon AppGroupEntry::icon() const { if (m_icon.isNull()) { m_icon = QIcon::fromTheme(m_group->icon(), QIcon::fromTheme("unknown")); } return m_icon; } QString AppGroupEntry::name() const { return m_group->caption(); } bool AppGroupEntry::hasChildren() const { return m_childModel && m_childModel->count() > 0; } AbstractModel *AppGroupEntry::childModel() const { return m_childModel; } diff --git a/appentry.h b/appentry.h index 4823a2bc7..fd4c3b627 100644 --- a/appentry.h +++ b/appentry.h @@ -1,93 +1,95 @@ /*************************************************************************** * Copyright (C) 201 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #ifndef APPENTRY_H #define APPENTRY_H #include "abstractentry.h" #include #include class AppsModel; class MenuEntryEditor; class AppEntry : public AbstractEntry { public: enum NameFormat { NameOnly = 0, GenericNameOnly, NameAndGenericName, GenericNameAndName }; explicit AppEntry(AbstractModel *owner, KService::Ptr service, NameFormat nameFormat); explicit AppEntry(AbstractModel *owner, const QString &id); EntryType type() const Q_DECL_OVERRIDE { return RunnableType; } bool isValid() const Q_DECL_OVERRIDE; QIcon icon() const Q_DECL_OVERRIDE; QString name() const Q_DECL_OVERRIDE; QString description() const Q_DECL_OVERRIDE; KService::Ptr service() const; QString id() const Q_DECL_OVERRIDE; QUrl url() const Q_DECL_OVERRIDE; bool hasActions() const Q_DECL_OVERRIDE; QVariantList actions() const Q_DECL_OVERRIDE; bool run(const QString& actionId = QString(), const QVariant &argument = QVariant()) Q_DECL_OVERRIDE; + QString menuId() const; + static QString nameFromService(const KService::Ptr service, NameFormat nameFormat); static KService::Ptr defaultAppByName(const QString &name); private: void init(NameFormat nameFormat); QString m_id; QString m_name; QString m_description; mutable QIcon m_icon; KService::Ptr m_service; static MenuEntryEditor *m_menuEntryEditor; }; class AppGroupEntry : public AbstractGroupEntry { public: AppGroupEntry(AppsModel *parentModel, KServiceGroup::Ptr group, bool paginate, int pageSize, bool flat, bool sorted, bool separators, int appNameFormat); QIcon icon() const Q_DECL_OVERRIDE; QString name() const Q_DECL_OVERRIDE; bool hasChildren() const Q_DECL_OVERRIDE; AbstractModel *childModel() const Q_DECL_OVERRIDE; private: KServiceGroup::Ptr m_group; mutable QIcon m_icon; QPointer m_childModel; }; #endif diff --git a/computermodel.cpp b/computermodel.cpp index 41ea6b55e..bf08aa134 100644 --- a/computermodel.cpp +++ b/computermodel.cpp @@ -1,304 +1,305 @@ /*************************************************************************** * Copyright (C) 2007 Kevin Ottens * * Copyright (C) 2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "computermodel.h" #include "actionlist.h" -#include "favoritesmodel.h" +#include "simplefavoritesmodel.h" #include #include #include #include #include #include #include #include "krunner_interface.h" FilteredPlacesModel::FilteredPlacesModel(QObject *parent) : QSortFilterProxyModel(parent) , m_placesModel(new KFilePlacesModel(this)) { setSourceModel(m_placesModel); sort(0); } FilteredPlacesModel::~FilteredPlacesModel() { } QUrl FilteredPlacesModel::url(const QModelIndex &index) const { return m_placesModel->url(mapToSource(index)); } bool FilteredPlacesModel::isDevice(const QModelIndex &index) const { return m_placesModel->isDevice(mapToSource(index)); } Solid::Device FilteredPlacesModel::deviceForIndex(const QModelIndex &index) const { return m_placesModel->deviceForIndex(mapToSource(index)); } bool FilteredPlacesModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { const QModelIndex index = m_placesModel->index(sourceRow, 0, sourceParent); return !m_placesModel->isHidden(index) && !m_placesModel->data(index, KFilePlacesModel::FixedDeviceRole).toBool(); } bool FilteredPlacesModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { bool lDevice = m_placesModel->isDevice(left); bool rDevice = m_placesModel->isDevice(right); if (lDevice && !rDevice) { return false; } else if (!lDevice && rDevice) { return true; } return (left.row() < right.row()); } RunCommandModel::RunCommandModel(QObject *parent) : AbstractModel(parent) { } RunCommandModel::~RunCommandModel() { } QString RunCommandModel::description() const { return QString(); } QVariant RunCommandModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } if (role == Qt::DisplayRole) { return i18n("Run Command..."); } else if (role == Qt::DecorationRole) { return QIcon::fromTheme(QStringLiteral("system-run")); } else if (role == Kicker::DescriptionRole) { return i18n("Run a command or a search query"); } else if (role == Kicker::GroupRole) { return i18n("Applications"); } return QVariant(); } int RunCommandModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : (KAuthorized::authorize(QStringLiteral("run_command")) ? 1 : 0); } Q_INVOKABLE bool RunCommandModel::trigger(int row, const QString &actionId, const QVariant &argument) { Q_UNUSED(actionId) Q_UNUSED(argument) if (row == 0 && KAuthorized::authorize(QStringLiteral("run_command"))) { org::kde::krunner::App krunner(QStringLiteral("org.kde.krunner"), QStringLiteral("/App"), QDBusConnection::sessionBus()); krunner.display(); return true; } return false; } ComputerModel::ComputerModel(QObject *parent) : ForwardingModel(parent) , m_concatProxy(new KConcatenateRowsProxyModel(this)) , m_runCommandModel(new RunCommandModel(this)) -, m_systemAppsModel(new FavoritesModel(this)) +, m_systemAppsModel(new SimpleFavoritesModel(this)) , m_filteredPlacesModel(new FilteredPlacesModel(this)) , m_appNameFormat(AppEntry::NameOnly) , m_appletInterface(nullptr) { - connect(m_systemAppsModel, &FavoritesModel::favoritesChanged, this, &ComputerModel::systemApplicationsChanged); + connect(m_systemAppsModel, &SimpleFavoritesModel::favoritesChanged, this, &ComputerModel::systemApplicationsChanged); + m_systemAppsModel->setFavorites(QStringList() << "systemsettings.desktop"); m_concatProxy->addSourceModel(m_runCommandModel); m_concatProxy->addSourceModel(m_systemAppsModel); m_concatProxy->addSourceModel(m_filteredPlacesModel); setSourceModel(m_concatProxy); } ComputerModel::~ComputerModel() { } QString ComputerModel::description() const { return i18n("Computer"); } int ComputerModel::appNameFormat() const { return m_appNameFormat; } void ComputerModel::setAppNameFormat(int format) { if (m_appNameFormat != (AppEntry::NameFormat)format) { m_appNameFormat = (AppEntry::NameFormat)format; m_systemAppsModel->refresh(); emit appNameFormatChanged(); } } QObject *ComputerModel::appletInterface() const { return m_appletInterface; } void ComputerModel::setAppletInterface(QObject *appletInterface) { if (m_appletInterface != appletInterface) { m_appletInterface = appletInterface; emit appletInterfaceChanged(); } } QStringList ComputerModel::systemApplications() const { return m_systemAppsModel->favorites(); } void ComputerModel::setSystemApplications(const QStringList &apps) { m_systemAppsModel->setFavorites(apps); } QVariant ComputerModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } const QModelIndex sourceIndex = m_concatProxy->mapToSource(m_concatProxy->index(index.row(), index.column())); bool isPlace = (sourceIndex.model() == m_filteredPlacesModel); if (isPlace) { if (role == Kicker::DescriptionRole) { if (m_filteredPlacesModel->isDevice(sourceIndex)) { Solid::Device device = m_filteredPlacesModel->deviceForIndex(sourceIndex); Solid::StorageAccess *access = device.as(); if (access) { return access->filePath(); } else { return QString(); } } else { const QUrl &url = m_filteredPlacesModel->url(sourceIndex); return url.toString(QUrl::PreferLocalFile); } } else if (role == Kicker::FavoriteIdRole) { if (!m_filteredPlacesModel->isDevice(sourceIndex)) { return m_filteredPlacesModel->url(sourceIndex); } } else if (role == Kicker::UrlRole) { return m_filteredPlacesModel->url(sourceIndex); } else if (role == Kicker::GroupRole) { if (m_filteredPlacesModel->isDevice(sourceIndex)) { return i18n("Removable Storage"); } else { return i18n("Places"); } } else if (role == Qt::DisplayRole || role == Qt::DecorationRole) { return sourceIndex.data(role); } } else if (role == Kicker::GroupRole) { return i18n("Applications"); } else { return sourceIndex.data(role); } return QVariant(); } bool ComputerModel::trigger(int row, const QString &actionId, const QVariant &argument) { const QModelIndex sourceIndex = m_concatProxy->mapToSource(m_concatProxy->index(row, 0)); if (sourceIndex.model() == m_filteredPlacesModel) { const QUrl &url = m_filteredPlacesModel->url(sourceIndex); if (url.isValid()) { new KRun(url, 0); return true; } Solid::Device device = m_filteredPlacesModel->deviceForIndex(sourceIndex); Solid::StorageAccess *access = device.as(); if (access && !access->isAccessible()) { connect(access, &Solid::StorageAccess::setupDone, this, &ComputerModel::onSetupDone); access->setup(); return true; } } else { AbstractModel *model = nullptr; if (sourceIndex.model() == m_systemAppsModel) { model = m_systemAppsModel; } else { model = m_runCommandModel; } return model->trigger(sourceIndex.row(), actionId, argument); } return false; } void ComputerModel::onSetupDone(Solid::ErrorType error, QVariant errorData, const QString &udi) { Q_UNUSED(errorData); if (error != Solid::NoError) { return; } Solid::Device device(udi); Solid::StorageAccess *access = device.as(); Q_ASSERT(access); new KRun(QUrl::fromLocalFile(access->filePath()), 0); } diff --git a/computermodel.h b/computermodel.h index aeb918e9a..b8cc96795 100644 --- a/computermodel.h +++ b/computermodel.h @@ -1,119 +1,119 @@ /*************************************************************************** * Copyright (C) 2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #ifndef COMPUTERMODEL_H #define COMPUTERMODEL_H #include "forwardingmodel.h" #include "appentry.h" #include #include -class FavoritesModel; +class SimpleFavoritesModel; class KConcatenateRowsProxyModel; class KFilePlacesModel; namespace Solid { class Device; } class FilteredPlacesModel : public QSortFilterProxyModel { Q_OBJECT public: FilteredPlacesModel(QObject *parent = 0); ~FilteredPlacesModel(); QUrl url(const QModelIndex &index) const; bool isDevice(const QModelIndex &index) const; Solid::Device deviceForIndex(const QModelIndex &index) const; protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_OVERRIDE; bool lessThan(const QModelIndex &left, const QModelIndex &right) const Q_DECL_OVERRIDE; private: KFilePlacesModel *m_placesModel; }; class RunCommandModel : public AbstractModel { Q_OBJECT public: RunCommandModel(QObject *parent = 0); ~RunCommandModel(); QString description() const Q_DECL_OVERRIDE; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument) Q_DECL_OVERRIDE; }; class ComputerModel : public ForwardingModel { Q_OBJECT Q_PROPERTY(int appNameFormat READ appNameFormat WRITE setAppNameFormat NOTIFY appNameFormatChanged) Q_PROPERTY(QObject* appletInterface READ appletInterface WRITE setAppletInterface NOTIFY appletInterfaceChanged) Q_PROPERTY(QStringList systemApplications READ systemApplications WRITE setSystemApplications NOTIFY systemApplicationsChanged) public: explicit ComputerModel(QObject *parent = 0); ~ComputerModel(); QString description() const Q_DECL_OVERRIDE; int appNameFormat() const; void setAppNameFormat(int format); QObject *appletInterface() const; void setAppletInterface(QObject *appletInterface); QStringList systemApplications() const; void setSystemApplications(const QStringList &apps); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument) Q_DECL_OVERRIDE; Q_SIGNALS: void appNameFormatChanged() const; void appletInterfaceChanged() const; void systemApplicationsChanged() const; private Q_SLOTS: void onSetupDone(Solid::ErrorType error, QVariant errorData, const QString &udi); private: KConcatenateRowsProxyModel *m_concatProxy; RunCommandModel *m_runCommandModel; - FavoritesModel *m_systemAppsModel; + SimpleFavoritesModel *m_systemAppsModel; FilteredPlacesModel *m_filteredPlacesModel; AppEntry::NameFormat m_appNameFormat; QObject *m_appletInterface; }; #endif diff --git a/kastatsfavoritesmodel.cpp b/kastatsfavoritesmodel.cpp new file mode 100644 index 000000000..787a66764 --- /dev/null +++ b/kastatsfavoritesmodel.cpp @@ -0,0 +1,717 @@ +/*************************************************************************** + * Copyright (C) 2014-2015 by Eike Hein * + * Copyright (C) 2016-2017 by Ivan Cukic * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "kastatsfavoritesmodel.h" +#include "appentry.h" +#include "contactentry.h" +#include "fileentry.h" +#include "actionlist.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace KAStats = KActivities::Stats; + +using namespace KAStats; +using namespace KAStats::Terms; + +#define AGENT_APPLICATIONS "org.kde.plasma.favorites.applications" +#define AGENT_CONTACTS "org.kde.plasma.favorites.contacts" +#define AGENT_DOCUMENTS "org.kde.plasma.favorites.documents" + +#define DEBUG_PREFIX "\033[0;31m[KASTATSFAVS]\033[0;34m " +#define DEBUG qDebug() << DEBUG_PREFIX << (void*)q << ((void*)this) << m_clientId << "\033[0;0m " + +QString agentForUrl(const QString &url) +{ + return url.startsWith("ktp:") + ? AGENT_CONTACTS + : url.startsWith("preferred:") + ? AGENT_APPLICATIONS + : url.startsWith("applications:") + ? AGENT_APPLICATIONS + : (url.startsWith("/") && !url.endsWith(".desktop")) + ? AGENT_DOCUMENTS + : (url.startsWith("file:/") && !url.endsWith(".desktop")) + ? AGENT_DOCUMENTS + // use applications as the default + : AGENT_APPLICATIONS; +} + +class KAStatsFavoritesModel::Private: public QAbstractListModel { +public: + class NormalizedId { + public: + NormalizedId() + { + } + + NormalizedId(const Private *parent, const QString &id) + { + if (id.isEmpty()) return; + + AbstractEntry *entry = nullptr; + QScopedPointer deleter; + + if (parent->m_itemEntries.contains(id)) { + entry = parent->m_itemEntries[id]; + } else { + // This entry is not cached - it is temporary, + // so let's clean up when we exit this function + entry = parent->entryForResource(id); + deleter.reset(entry); + } + + if (!entry || !entry->isValid()) { + qWarning() << "Entry is not valid" << id << entry; + m_id = id; + return; + } + + const auto url = entry->url(); + + qDebug() << "Original id is: " << id << ", and the url is" << url; + + // Preferred applications need special handling + if (entry->id().startsWith("preferred:")) { + m_id = entry->id(); + return; + } + + // If this is an application, use the applications:-format url + auto appEntry = dynamic_cast(entry); + if (appEntry && !appEntry->menuId().isEmpty()) { + m_id = "applications:" + appEntry->menuId(); + return; + } + + // We want to resolve symbolic links not to have two paths + // refer to the same .desktop file + if (url.isLocalFile()) { + QFileInfo file(url.toLocalFile()); + + if (file.exists()) { + m_id = QUrl::fromLocalFile(file.canonicalFilePath()).toString(); + return; + } + } + + // If this is a file, we should have already covered it + if (url.scheme() == "file") { + return; + } + + m_id = url.toString(); + } + + const QString& value() const + { + return m_id; + } + + bool operator==(const NormalizedId &other) const + { + return m_id == other.m_id; + } + + private: + QString m_id; + }; + + NormalizedId normalizedId(const QString &id) const + { + return NormalizedId(this, id); + } + + AbstractEntry *entryForResource(const QString &resource) const + { + const auto agent = + agentForUrl(resource); + + if (agent == AGENT_CONTACTS) { + return new ContactEntry(q, resource); + + } else if (agent == AGENT_DOCUMENTS) { + if (resource.startsWith("/")) { + return new FileEntry(q, QUrl::fromLocalFile(resource)); + } else { + return new FileEntry(q, QUrl(resource)); + } + + } else if (agent == AGENT_APPLICATIONS) { + if (resource.startsWith("applications:")) { + return new AppEntry(q, resource.mid(13)); + } else { + return new AppEntry(q, resource); + } + + } else { + return nullptr; + } + } + + Private(KAStatsFavoritesModel *parent, QString clientId) + : q(parent) + , m_query( + LinkedResources + | Agent { + AGENT_APPLICATIONS, + AGENT_CONTACTS, + AGENT_DOCUMENTS + } + | Type::any() + | Activity::current() + | Activity::global() + | Limit::all() + ) + , m_watcher(m_query) + , m_clientId(clientId) + { + // Connecting the watcher + connect(&m_watcher, &ResultWatcher::resultLinked, + [this] (const QString &resource) { + addResult(resource, -1); + }); + + connect(&m_watcher, &ResultWatcher::resultUnlinked, + [this] (const QString &resource) { + removeResult(resource); + }); + + // Loading the items order + const auto cfg = KSharedConfig::openConfig("kactivitymanagerd-statsrc"); + + // We want first to check whether we have an ordering for this activity. + // If not, we will try to get a global one for this applet + + const QString thisGroupName = + "Favorites-" + clientId + "-" + m_activities.currentActivity(); + const QString globalGroupName = + "Favorites-" + clientId + "-global"; + + KConfigGroup thisCfgGroup(cfg, thisGroupName); + KConfigGroup globalCfgGroup(cfg, globalGroupName); + + QStringList ordering = + thisCfgGroup.readEntry("ordering", QStringList()) + + globalCfgGroup.readEntry("ordering", QStringList()); + + DEBUG << "Loading the ordering " << ordering; + + // Loading the results without emitting any model signals + DEBUG << "Query is" << m_query; + ResultSet results(m_query); + + for (const auto& result: results) { + DEBUG << "Got " << result.resource() << " -->"; + addResult(result.resource(), -1, false); + } + + // Normalizing all the ids + std::transform(ordering.begin(), ordering.end(), ordering.begin(), + [&] (const QString &item) { + return normalizedId(item).value(); + }); + + // Sorting the items in the cache + std::sort(m_items.begin(), m_items.end(), + [&] (const NormalizedId &left, const NormalizedId &right) { + auto leftIndex = ordering.indexOf(left.value()); + auto rightIndex = ordering.indexOf(right.value()); + + return (leftIndex == -1 && rightIndex == -1) ? + left.value() < right.value() : + + (leftIndex == -1) ? + false : + + (rightIndex == -1) ? + true : + + // otherwise + leftIndex < rightIndex; + }); + + // Debugging: + QVector itemStrings(m_items.size()); + std::transform(m_items.cbegin(), m_items.cend(), itemStrings.begin(), + [] (const NormalizedId &item) { + return item.value(); + }); + DEBUG << "After ordering: " << itemStrings; + } + + void addResult(const QString &_resource, int index, bool notifyModel = true) + { + // We want even files to have a proper URL + const auto resource = + _resource.startsWith("/") ? QUrl::fromLocalFile(_resource).toString() : _resource; + + DEBUG << "Adding result" << resource << "already present?" << m_itemEntries.contains(resource); + + if (m_itemEntries.contains(resource)) return; + + auto entry = entryForResource(resource); + + if (!entry || !entry->isValid()) { + DEBUG << "Entry is not valid!"; + return; + } + + if (index == -1) { + index = m_items.count(); + } + + if (notifyModel) { + beginInsertRows(QModelIndex(), index, index); + } + + auto url = entry->url(); + + m_itemEntries[resource] + = m_itemEntries[entry->id()] + = m_itemEntries[url.toString()] + = m_itemEntries[url.toLocalFile()] + = entry; + + auto normalized = normalizedId(resource); + m_items.insert(index, normalized); + m_itemEntries[normalized.value()] = entry; + + if (notifyModel) { + endInsertRows(); + saveOrdering(); + } + } + + void removeResult(const QString &resource) + { + auto normalized = normalizedId(resource); + + // If we know this item will not really be removed, + // but only that activities it is on have changed, + // lets leave it + if (m_ignoredItems.contains(normalized.value())) { + m_ignoredItems.removeAll(normalized.value()); + return; + } + + DEBUG << "Removing result" << resource; + + auto index = m_items.indexOf(normalizedId(resource)); + + if (index == -1) return; + + beginRemoveRows(QModelIndex(), index, index); + auto entry = m_itemEntries[resource]; + m_items.removeAt(index); + + // Removing the entry from the cache + QMutableHashIterator i(m_itemEntries); + while (i.hasNext()) { + if (i.value() == entry) { + i.remove(); + } + i.next(); + } + delete entry; + + endRemoveRows(); + } + + + int rowCount(const QModelIndex &parent = QModelIndex()) const override + { + if (parent.isValid()) return 0; + + return m_items.count(); + } + + QVariant data(const QModelIndex &item, + int role = Qt::DisplayRole) const override + { + if (item.parent().isValid()) return QVariant(); + + const auto index = item.row(); + + const auto entry = m_itemEntries[m_items[index].value()]; + + return entry == nullptr ? QVariant() + : role == Qt::DisplayRole ? entry->name() + : role == Qt::DecorationRole ? entry->icon() + : role == Kicker::DescriptionRole ? entry->description() + : role == Kicker::FavoriteIdRole ? entry->id() + : role == Kicker::UrlRole ? entry->url() + : role == Kicker::HasActionListRole ? entry->hasActions() + : role == Kicker::ActionListRole ? entry->actions() + : QVariant(); + } + + bool trigger(int row, const QString &actionId, const QVariant &argument) + { + if (row < 0 || row >= rowCount()) { + return false; + } + + const QString id = data(index(row, 0), Kicker::UrlRole).toString(); + + return m_itemEntries.contains(id) + ? m_itemEntries[id]->run(actionId, argument) + : false; + } + + void move(int from, int to) + { + if (from < 0) return; + if (from >= m_items.count()) return; + if (to < 0) return; + if (to >= m_items.count()) return; + + if (from == to) return; + + const int modelTo = to + (to > from ? 1 : 0); + + if (q->beginMoveRows(QModelIndex(), from, from, + QModelIndex(), modelTo)) { + m_items.move(from, to); + q->endMoveRows(); + + DEBUG << "Save ordering (from Private::move) -->"; + saveOrdering(); + } + } + + void saveOrdering() + { + QStringList ids; + + for (const auto& item: m_items) { + ids << item.value(); + } + + DEBUG << "Save ordering (from Private::saveOrdering) -->"; + saveOrdering(ids, m_clientId, m_activities.currentActivity()); + } + + static void saveOrdering(const QStringList &ids, const QString &clientId, const QString ¤tActivity) + { + const auto cfg = KSharedConfig::openConfig("kactivitymanagerd-statsrc"); + + QStringList activities { + currentActivity, + "global" + }; + + qDebug() << "Saving ordering for" << currentActivity << "and global" << ids; + + for (const auto& activity: activities) { + const QString groupName = + "Favorites-" + clientId + "-" + activity; + + KConfigGroup cfgGroup(cfg, groupName); + + cfgGroup.writeEntry("ordering", ids); + } + + cfg->sync(); + } + + KAStatsFavoritesModel *const q; + KActivities::Consumer m_activities; + Query m_query; + ResultWatcher m_watcher; + QString m_clientId; + + QVector m_items; + QHash m_itemEntries; + QStringList m_ignoredItems; +}; + +#undef DEBUG +#define DEBUG qDebug() << DEBUG_PREFIX << ((void*)this) << ((void*)d) << (d ? d->m_clientId : QString("no client ID yet")) << "\033[0;0m " + +KAStatsFavoritesModel::KAStatsFavoritesModel(QObject *parent) +: PlaceholderModel(parent) +, d(nullptr) // we have no client id yet +, m_enabled(true) +, m_maxFavorites(-1) +, m_activities(new KActivities::Consumer(this)) +{ + connect(m_activities, &KActivities::Consumer::currentActivityChanged, + this, [&] (const QString ¤tActivity) { + DEBUG << "Activity just got changed to" << currentActivity; + Q_UNUSED(currentActivity); + auto clientId = d->m_clientId; + initForClient(clientId); + }); +} + +KAStatsFavoritesModel::~KAStatsFavoritesModel() +{ + delete d; +} + +void KAStatsFavoritesModel::initForClient(const QString &clientId) +{ + DEBUG << "initForClient" << clientId; + + setSourceModel(nullptr); + delete d; + d = new Private( + this, + clientId + ); + + setSourceModel(d); +} + +QString KAStatsFavoritesModel::description() const +{ + return i18n("Favorites"); +} + +bool KAStatsFavoritesModel::trigger(int row, const QString &actionId, const QVariant &argument) +{ + return d->trigger(row, actionId, argument); +} + +bool KAStatsFavoritesModel::enabled() const +{ + return m_enabled; +} + +int KAStatsFavoritesModel::maxFavorites() const +{ + return m_maxFavorites; +} + +void KAStatsFavoritesModel::setMaxFavorites(int max) +{ + Q_UNUSED(max); +} + +void KAStatsFavoritesModel::setEnabled(bool enable) +{ + if (m_enabled != enable) { + m_enabled = enable; + + emit enabledChanged(); + } +} + +QStringList KAStatsFavoritesModel::favorites() const +{ + qWarning() << "KAStatsFavoritesModel::favorites returns nothing, it is here just to keep the API backwards-compatible"; + return QStringList(); +} + +void KAStatsFavoritesModel::setFavorites(const QStringList& favorites) +{ + Q_UNUSED(favorites); + qWarning() << "KAStatsFavoritesModel::setFavorites is ignored"; +} + +bool KAStatsFavoritesModel::isFavorite(const QString &id) const +{ + return d && d->m_itemEntries.contains(id); +} + +void KAStatsFavoritesModel::portOldFavorites(const QStringList &ids) +{ + DEBUG << "portOldFavorites" << ids; + + const auto activityId = ":global"; + std::for_each(ids.begin(), ids.end(), [&] (const QString &id) { + addFavoriteTo(id, activityId); + }); + + // Resetting the model + auto clientId = d->m_clientId; + setSourceModel(nullptr); + delete d; + d = nullptr; + + DEBUG << "Save ordering (from portOldFavorites) -->"; + Private::saveOrdering(ids, clientId, m_activities->currentActivity()); + + QTimer::singleShot(500, + std::bind(&KAStatsFavoritesModel::initForClient, this, clientId)); +} + +void KAStatsFavoritesModel::addFavorite(const QString &id, int index) +{ + DEBUG << "addFavorite" << id << index << " -->"; + addFavoriteTo(id, Activity::current(), index); +} + +void KAStatsFavoritesModel::removeFavorite(const QString &id) +{ + DEBUG << "removeFavorite" << id << " -->"; + removeFavoriteFrom(id, Activity::current()); +} + +void KAStatsFavoritesModel::addFavoriteTo(const QString &id, const QString &activityId, int index) +{ + DEBUG << "addFavoriteTo" << id << activityId << index << " -->"; + addFavoriteTo(id, Activity(activityId), index); +} + +void KAStatsFavoritesModel::removeFavoriteFrom(const QString &id, const QString &activityId) +{ + DEBUG << "removeFavoriteFrom" << id << activityId << " -->"; + removeFavoriteFrom(id, Activity(activityId)); +} + +void KAStatsFavoritesModel::addFavoriteTo(const QString &id, const Activity &activity, int index) +{ + if (!d || id.isEmpty()) return; + + Q_ASSERT(!activity.values.isEmpty()); + + setDropPlaceholderIndex(-1); + + QStringList matchers { d->m_activities.currentActivity(), ":global", ":current" }; + if (std::find_first_of(activity.values.cbegin(), activity.values.cend(), + matchers.cbegin(), matchers.cend()) != activity.values.cend()) { + d->addResult(id, index); + } + + const auto url = d->normalizedId(id).value(); + + DEBUG << "addFavoriteTo" << id << activity << index << url << " (actual)"; + + if (url.isEmpty()) return; + + d->m_watcher.linkToActivity(QUrl(url), activity, + Agent(agentForUrl(url))); +} + +void KAStatsFavoritesModel::removeFavoriteFrom(const QString &id, const Activity &activity) +{ + const auto url = d->normalizedId(id).value(); + + Q_ASSERT(!activity.values.isEmpty()); + + DEBUG << "addFavoriteTo" << id << activity << url << " (actual)"; + + if (url.isEmpty()) return; + + d->m_watcher.unlinkFromActivity(QUrl(url), activity, + Agent(agentForUrl(url))); +} + +void KAStatsFavoritesModel::setFavoriteOn(const QString &id, const QString &activityId) +{ + const auto url = d->normalizedId(id).value(); + + DEBUG << "setFavoriteOn" << id << activityId << url << " (actual)"; + + DEBUG << "%%%%%%%%%%% Activity is" << activityId; + if (activityId.isEmpty() || activityId == ":any" || + activityId == ":global" || + activityId == m_activities->currentActivity()) { + d->m_ignoredItems << url; + } + + d->m_watcher.unlinkFromActivity(QUrl(url), Activity::any(), + Agent(agentForUrl(url))); + d->m_watcher.linkToActivity(QUrl(url), activityId, + Agent(agentForUrl(url))); +} + +void KAStatsFavoritesModel::moveRow(int from, int to) +{ + d->move(from, to); +} + +AbstractModel *KAStatsFavoritesModel::favoritesModel() +{ + return this; +} + +void KAStatsFavoritesModel::refresh() +{ +} + +QObject *KAStatsFavoritesModel::activities() const +{ + return m_activities; +} + +QString KAStatsFavoritesModel::activityNameForId(const QString &activityId) const +{ + // It is safe to use a short-lived object here, + // we are always synced with KAMD in plasma + KActivities::Info info(activityId); + return info.name(); +} + +QStringList KAStatsFavoritesModel::linkedActivitiesFor(const QString &id) const +{ + if (!d) { + DEBUG << "Linked for" << id << "is empty, no Private instance"; + return {}; + } + + auto url = d->normalizedId(id).value(); + + if (url.startsWith("file:")) { + url = QUrl(url).toLocalFile(); + } + + if (url.isEmpty()) { + DEBUG << "The url for" << id << "is empty"; + return {}; + } + + auto query = LinkedResources + | Agent { + AGENT_APPLICATIONS, + AGENT_CONTACTS, + AGENT_DOCUMENTS + } + | Type::any() + | Activity::any() + | Url(url) + | Limit::all(); + + ResultSet results(query); + + for (const auto &result: results) { + DEBUG << "Returning" << result.linkedActivities() << "for" << id << url; + return result.linkedActivities(); + } + + DEBUG << "Returning empty list of activities for" << id << url; + return {}; +} + diff --git a/favoritesmodel.h b/kastatsfavoritesmodel.h similarity index 60% copy from favoritesmodel.h copy to kastatsfavoritesmodel.h index 8ed1bf921..f41e4d092 100644 --- a/favoritesmodel.h +++ b/kastatsfavoritesmodel.h @@ -1,91 +1,117 @@ /*************************************************************************** * Copyright (C) 2014-2015 by Eike Hein * + * Copyright (C) 2016-2017 by Ivan Cukic * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #ifndef FAVORITESMODEL_H #define FAVORITESMODEL_H -#include "abstractmodel.h" +#include "placeholdermodel.h" #include #include +#include -class FavoritesModel : public AbstractModel +class PlaceholderModel; + +namespace KActivities { + class Consumer; +namespace Stats { + class ResultModel; +namespace Terms { + class Activity; +} // namespace Terms +} // namespace Stats +} // namespace KActivities + +class KAStatsFavoritesModel : public PlaceholderModel { Q_OBJECT Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(QStringList favorites READ favorites WRITE setFavorites NOTIFY favoritesChanged) Q_PROPERTY(int maxFavorites READ maxFavorites WRITE setMaxFavorites NOTIFY maxFavoritesChanged) - Q_PROPERTY(int dropPlaceholderIndex READ dropPlaceholderIndex WRITE setDropPlaceholderIndex NOTIFY dropPlaceholderIndexChanged) - - public: - explicit FavoritesModel(QObject *parent = 0); - ~FavoritesModel(); - QString description() const Q_DECL_OVERRIDE; + Q_PROPERTY(QObject* activities READ activities CONSTANT) - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + public: + explicit KAStatsFavoritesModel(QObject *parent = 0); + ~KAStatsFavoritesModel(); - int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + QString description() const; - Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument) Q_DECL_OVERRIDE; + Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument); bool enabled() const; void setEnabled(bool enable); QStringList favorites() const; void setFavorites(const QStringList &favorites); int maxFavorites() const; void setMaxFavorites(int max); Q_INVOKABLE bool isFavorite(const QString &id) const; + Q_INVOKABLE void addFavorite(const QString &id, int index = -1); Q_INVOKABLE void removeFavorite(const QString &id); + Q_INVOKABLE void addFavoriteTo(const QString &id, const QString &activityId, int index = -1); + Q_INVOKABLE void removeFavoriteFrom(const QString &id, const QString &activityId); + + Q_INVOKABLE void setFavoriteOn(const QString &id, const QString &activityId); + + Q_INVOKABLE void portOldFavorites(const QStringList &ids); + + Q_INVOKABLE QStringList linkedActivitiesFor(const QString &id) const; + Q_INVOKABLE void moveRow(int from, int to); - int dropPlaceholderIndex() const; - void setDropPlaceholderIndex(int index); + Q_INVOKABLE void initForClient(const QString &client); - AbstractModel* favoritesModel() Q_DECL_OVERRIDE; + QObject *activities() const; + Q_INVOKABLE QString activityNameForId(const QString &activityId) const; + + AbstractModel* favoritesModel(); public Q_SLOTS: - void refresh() Q_DECL_OVERRIDE; + virtual void refresh(); Q_SIGNALS: void enabledChanged() const; void favoritesChanged() const; void maxFavoritesChanged() const; - void dropPlaceholderIndexChanged(); private: - AbstractEntry *favoriteFromId(const QString &id); + class Private; + Private * d; + + AbstractEntry *favoriteFromId(const QString &id) const; + + void addFavoriteTo(const QString &id, const KActivities::Stats::Terms::Activity &activityId, int index = -1); + void removeFavoriteFrom(const QString &id, const KActivities::Stats::Terms::Activity &activityId); bool m_enabled; - QList m_entryList; - QStringList m_favorites; int m_maxFavorites; - int m_dropPlaceholderIndex; + KActivities::Consumer *m_activities; }; #endif diff --git a/kickerplugin.cpp b/kickerplugin.cpp index 11b780070..b41a304c9 100644 --- a/kickerplugin.cpp +++ b/kickerplugin.cpp @@ -1,63 +1,65 @@ /*************************************************************************** * Copyright (C) 2014 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "kickerplugin.h" #include "abstractmodel.h" #include "appsmodel.h" #include "computermodel.h" #include "containmentinterface.h" #include "draghelper.h" -#include "favoritesmodel.h" +#include "simplefavoritesmodel.h" +#include "kastatsfavoritesmodel.h" #include "dashboardwindow.h" #include "funnelmodel.h" #include "processrunner.h" #include "recentusagemodel.h" #include "rootmodel.h" #include "runnermodel.h" #include "submenu.h" #include "systemmodel.h" #include "systemsettings.h" #include "wheelinterceptor.h" #include "windowsystem.h" #include void KickerPlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("org.kde.plasma.private.kicker")); qmlRegisterType(); qmlRegisterType(uri, 0, 1, "AppsModel"); qmlRegisterType(uri, 0, 1, "ComputerModel"); qmlRegisterType(uri, 0, 1, "ContainmentInterface"); qmlRegisterType(uri, 0, 1, "DragHelper"); - qmlRegisterType(uri, 0, 1, "FavoritesModel"); + qmlRegisterType(uri, 0, 1, "FavoritesModel"); + qmlRegisterType(uri, 0, 1, "KAStatsFavoritesModel"); qmlRegisterType(uri, 0, 1, "DashboardWindow"); qmlRegisterType(uri, 0, 1, "FunnelModel"); qmlRegisterType(uri, 0, 1, "ProcessRunner"); qmlRegisterType(uri, 0, 1, "RecentUsageModel"); qmlRegisterType(uri, 0, 1, "RootModel"); qmlRegisterType(uri, 0, 1, "RunnerModel"); qmlRegisterType(uri, 0, 1, "SubMenu"); qmlRegisterType(uri, 0, 1, "SystemModel"); qmlRegisterType(uri, 0, 1, "SystemSettings"); qmlRegisterType(uri, 0, 1, "WheelInterceptor"); qmlRegisterType(uri, 0, 1, "WindowSystem"); } diff --git a/placeholdermodel.cpp b/placeholdermodel.cpp new file mode 100644 index 000000000..53d98303b --- /dev/null +++ b/placeholdermodel.cpp @@ -0,0 +1,407 @@ +/*************************************************************************** + * Copyright (C) 2015 by Eike Hein * + * Copyright (C) 2017 by Ivan Cukic * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "placeholdermodel.h" +#include "actionlist.h" + +#include +#include + +PlaceholderModel::PlaceholderModel(QObject *parent) + : AbstractModel(parent) + , m_dropPlaceholderIndex(-1) + , m_isTriggerInhibited(false) +{ + connect(&m_triggerInhibitor, &QTimer::timeout, + this, [&] { + qDebug() << "%%% Inhibit stopped"; + m_isTriggerInhibited = false; + }); + + m_triggerInhibitor.setInterval(500); + m_triggerInhibitor.setSingleShot(true); +} + +void PlaceholderModel::inhibitTriggering() +{ + qDebug() << "%%% Inhibit started"; + m_isTriggerInhibited = true; + m_triggerInhibitor.start(); +} + +PlaceholderModel::~PlaceholderModel() +{ +} + +QString PlaceholderModel::description() const +{ + if (auto abstractModel = qobject_cast(m_sourceModel)) { + return abstractModel->description(); + + } else { + return QString(); + } +} + +QAbstractItemModel *PlaceholderModel::sourceModel() const +{ + return m_sourceModel; +} + +void PlaceholderModel::setSourceModel(QAbstractItemModel *sourceModel) +{ + disconnectSignals(); + + beginResetModel(); + + m_sourceModel = sourceModel; + + connectSignals(); + + endResetModel(); + + emit countChanged(); + emit sourceModelChanged(); + emit descriptionChanged(); +} + +bool PlaceholderModel::canFetchMore(const QModelIndex &parent) const +{ + return m_sourceModel && m_sourceModel->canFetchMore(indexToSourceIndex(parent)); +} + +void PlaceholderModel::fetchMore(const QModelIndex &parent) +{ + if (m_sourceModel) { + m_sourceModel->fetchMore(indexToSourceIndex(parent)); + } +} + +QModelIndex PlaceholderModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return m_sourceModel ? createIndex(row, column) + : QModelIndex(); +} + +QModelIndex PlaceholderModel::parent(const QModelIndex &index) const +{ + Q_UNUSED(index) + + return QModelIndex(); +} + +QVariant PlaceholderModel::data(const QModelIndex &index, int role) const +{ + const auto row = index.row(); + + if (m_dropPlaceholderIndex == row) { + switch (role) { + case Kicker::IsDropPlaceholderRole: + return true; + + // TODO: Maybe it would be nice to show something here? + // case Qt::DisplayRole: + // return "placeholder"; + // + // case Qt::DecorationRole: + // return "select"; + + default: + return QVariant(); + + } + } + + return m_sourceModel ? m_sourceModel->data(indexToSourceIndex(index), role) + : QVariant(); +} + +int PlaceholderModel::rowCount(const QModelIndex &parent) const +{ + if (!m_sourceModel || parent.isValid()) { + return 0; + } + + return m_sourceModel->rowCount() + + (m_dropPlaceholderIndex != -1 ? 1 : 0); +} + +QModelIndex PlaceholderModel::indexToSourceIndex(const QModelIndex& index) const +{ + if (!m_sourceModel || !index.isValid()) { + return QModelIndex(); + } + + const auto row = index.row(); + const auto column = index.column(); + + return index.parent().isValid() ? + // We do not support tree models + QModelIndex() : + + // If we are on top-level, lets add a placeholder + m_sourceModel->index( + row - (m_dropPlaceholderIndex != -1 && row > m_dropPlaceholderIndex ? 1 : 0), + column, + QModelIndex() + ); +} + +int PlaceholderModel::sourceRowToRow(int sourceRow) const +{ + return sourceRow + + (m_dropPlaceholderIndex != -1 && sourceRow >= m_dropPlaceholderIndex ? 1 : 0); +} + +int PlaceholderModel::rowToSourceRow(int row) const +{ + return row == m_dropPlaceholderIndex ? -1 : + row - (m_dropPlaceholderIndex != -1 && row > m_dropPlaceholderIndex ? 1 : 0); +} + +QModelIndex PlaceholderModel::sourceIndexToIndex(const QModelIndex& sourceIndex) const +{ + if (!m_sourceModel || !sourceIndex.isValid()) { + return QModelIndex(); + } + + const auto sourceRow = sourceIndex.row(); + const auto sourceColumn = sourceIndex.column(); + + return sourceIndex.parent().isValid() ? + // We do not support tree-models + QModelIndex() : + + // If we are on top-level, lets add a placeholder + index( + sourceRowToRow(sourceRow), + sourceColumn, + QModelIndex() + ); +} + +bool PlaceholderModel::trigger(int row, const QString &actionId, const QVariant &argument) +{ + if (m_isTriggerInhibited) return false; + + if (auto abstractModel = qobject_cast(m_sourceModel)) { + return abstractModel->trigger(rowToSourceRow(row), actionId, argument); + + } else { + return false; + } +} + +QString PlaceholderModel::labelForRow(int row) +{ + if (auto abstractModel = qobject_cast(m_sourceModel)) { + return abstractModel->labelForRow(rowToSourceRow(row)); + + } else { + return QString(); + } + +} + +AbstractModel* PlaceholderModel::modelForRow(int row) +{ + if (auto abstractModel = qobject_cast(m_sourceModel)) { + return abstractModel->modelForRow(rowToSourceRow(row)); + + } else { + return 0; + } +} + +AbstractModel* PlaceholderModel::favoritesModel() +{ + if (auto abstractModel = qobject_cast(m_sourceModel)) { + return abstractModel->favoritesModel(); + + } else { + return AbstractModel::favoritesModel(); + } +} + +int PlaceholderModel::separatorCount() const +{ + if (auto abstractModel = qobject_cast(m_sourceModel)) { + return abstractModel->separatorCount(); + + } else { + return 0; + } +} + +void PlaceholderModel::reset() +{ + emit beginResetModel(); + emit endResetModel(); + emit countChanged(); + emit separatorCountChanged(); +} + +void PlaceholderModel::connectSignals() +{ + if (!m_sourceModel) { + return; + } + + const auto sourceModelPtr = m_sourceModel.data(); + + connect(sourceModelPtr, SIGNAL(destroyed()), this, SLOT(reset())); + + connect(sourceModelPtr, &QAbstractItemModel::dataChanged, + this, [this] (const QModelIndex &from, const QModelIndex &to, const QVector &roles) { + emit dataChanged(sourceIndexToIndex(from), + sourceIndexToIndex(to), + roles); + }); + + connect(sourceModelPtr, &QAbstractItemModel::rowsAboutToBeInserted, + this, [this] (const QModelIndex &parent, int from, int to) { + if (parent.isValid()) { + qWarning() << "We do not support tree models"; + + } else { + beginInsertRows(QModelIndex(), + sourceRowToRow(from), + sourceRowToRow(to)); + + } + }); + + connect(sourceModelPtr, &QAbstractItemModel::rowsInserted, + this, [this] { + endInsertRows(); + emit countChanged(); + }); + + + connect(sourceModelPtr, &QAbstractItemModel::rowsAboutToBeMoved, + this, [this] (const QModelIndex &source, int from, int to, const QModelIndex &dest, int destRow) { + if (source.isValid() || dest.isValid()) { + qWarning() << "We do not support tree models"; + + } else { + beginMoveRows(QModelIndex(), + sourceRowToRow(from), + sourceRowToRow(to), + QModelIndex(), + sourceRowToRow(destRow)); + } + }); + + connect(sourceModelPtr, &QAbstractItemModel::rowsMoved, + this, [this] { + endMoveRows(); + }); + + + connect(sourceModelPtr, &QAbstractItemModel::rowsAboutToBeRemoved, + this, [this] (const QModelIndex &parent, int from, int to) { + if (parent.isValid()) { + qWarning() << "We do not support tree models"; + + } else { + beginRemoveRows(QModelIndex(), + sourceRowToRow(from), + sourceRowToRow(to)); + } + }); + + connect(sourceModelPtr, &QAbstractItemModel::rowsRemoved, + this, [this] { + endRemoveRows(); + emit countChanged(); + }); + + + connect(sourceModelPtr, &QAbstractItemModel::modelAboutToBeReset, + this, [this] { + beginResetModel(); + }); + + connect(sourceModelPtr, &QAbstractItemModel::modelReset, + this, [this] { + endResetModel(); + emit countChanged(); + }); + + // We do not have persistant indices + // connect(sourceModelPtr, &QAbstractItemModel::layoutAboutToBeChanged), + // this, &PlaceholderModel::layoutAboutToBeChanged); + // connect(sourceModelPtr, &QAbstractItemModel::layoutChanged), + // this, SIGNAL(layoutChanged(QList,QAbstractItemModel::LayoutChangeHint)), + // Qt::UniqueConnection); +} + +void PlaceholderModel::disconnectSignals() +{ + if (!m_sourceModel) { + return; + } + + disconnect(m_sourceModel, 0, this, 0); +} + +int PlaceholderModel::dropPlaceholderIndex() const +{ + return m_dropPlaceholderIndex; +} + +void PlaceholderModel::setDropPlaceholderIndex(int index) +{ + if (index == m_dropPlaceholderIndex) return; + + inhibitTriggering(); + + if (index == -1 && m_dropPlaceholderIndex != -1) { + // Removing the placeholder + beginRemoveRows(QModelIndex(), m_dropPlaceholderIndex, m_dropPlaceholderIndex); + m_dropPlaceholderIndex = index; + endRemoveRows(); + + emit countChanged(); + + } else if (index != -1 && m_dropPlaceholderIndex == -1) { + // Creating the placeholder + beginInsertRows(QModelIndex(), index, index); + m_dropPlaceholderIndex = index; + endInsertRows(); + + emit countChanged(); + + } else if (m_dropPlaceholderIndex != index) { + // Moving the placeholder + int modelTo = index + (index > m_dropPlaceholderIndex ? 1 : 0); + + if (beginMoveRows( + QModelIndex(), m_dropPlaceholderIndex, m_dropPlaceholderIndex, + QModelIndex(), modelTo)) { + m_dropPlaceholderIndex = index; + endMoveRows(); + } + } + + emit dropPlaceholderIndexChanged(); +} diff --git a/favoritesmodel.h b/placeholdermodel.h similarity index 52% copy from favoritesmodel.h copy to placeholdermodel.h index 8ed1bf921..c6835d8ca 100644 --- a/favoritesmodel.h +++ b/placeholdermodel.h @@ -1,91 +1,94 @@ /*************************************************************************** - * Copyright (C) 2014-2015 by Eike Hein * + * Copyright (C) 2015 by Eike Hein * + * Copyright (C) 2017 by Ivan Cukic * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ -#ifndef FAVORITESMODEL_H -#define FAVORITESMODEL_H +#ifndef PLACEHOLDERMODEL_H +#define PLACEHOLDERMODEL_H #include "abstractmodel.h" #include +#include -#include - -class FavoritesModel : public AbstractModel +class PlaceholderModel : public AbstractModel { Q_OBJECT - Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) - Q_PROPERTY(QStringList favorites READ favorites WRITE setFavorites NOTIFY favoritesChanged) - Q_PROPERTY(int maxFavorites READ maxFavorites WRITE setMaxFavorites NOTIFY maxFavoritesChanged) + Q_PROPERTY(QAbstractItemModel* sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged); Q_PROPERTY(int dropPlaceholderIndex READ dropPlaceholderIndex WRITE setDropPlaceholderIndex NOTIFY dropPlaceholderIndexChanged) public: - explicit FavoritesModel(QObject *parent = 0); - ~FavoritesModel(); + explicit PlaceholderModel(QObject *parent = 0); + ~PlaceholderModel(); + + QString description() const; + + QAbstractItemModel *sourceModel() const; + virtual void setSourceModel(QAbstractItemModel *sourceModel); - QString description() const Q_DECL_OVERRIDE; + bool canFetchMore(const QModelIndex &parent) const; + void fetchMore(const QModelIndex &parent); - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; - int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument) Q_DECL_OVERRIDE; + int rowCount(const QModelIndex &parent = QModelIndex()) const; - bool enabled() const; - void setEnabled(bool enable); + Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument); - QStringList favorites() const; - void setFavorites(const QStringList &favorites); + Q_INVOKABLE QString labelForRow(int row); - int maxFavorites() const; - void setMaxFavorites(int max); + Q_INVOKABLE AbstractModel *modelForRow(int row); - Q_INVOKABLE bool isFavorite(const QString &id) const; - Q_INVOKABLE void addFavorite(const QString &id, int index = -1); - Q_INVOKABLE void removeFavorite(const QString &id); + AbstractModel* favoritesModel(); - Q_INVOKABLE void moveRow(int from, int to); + int separatorCount() const; int dropPlaceholderIndex() const; void setDropPlaceholderIndex(int index); - AbstractModel* favoritesModel() Q_DECL_OVERRIDE; - public Q_SLOTS: - void refresh() Q_DECL_OVERRIDE; + void reset(); Q_SIGNALS: - void enabledChanged() const; - void favoritesChanged() const; - void maxFavoritesChanged() const; + void sourceModelChanged() const; void dropPlaceholderIndexChanged(); + protected: + void inhibitTriggering(); + private: - AbstractEntry *favoriteFromId(const QString &id); + QModelIndex indexToSourceIndex(const QModelIndex &index) const; + QModelIndex sourceIndexToIndex(const QModelIndex &index) const; + int sourceRowToRow(int sourceRow) const; + int rowToSourceRow(int row) const; - bool m_enabled; + void connectSignals(); + void disconnectSignals(); - QList m_entryList; - QStringList m_favorites; - int m_maxFavorites; + QPointer m_sourceModel; int m_dropPlaceholderIndex; + bool m_isTriggerInhibited; + QTimer m_triggerInhibitor; }; #endif diff --git a/recentusagemodel.cpp b/recentusagemodel.cpp index 0bab03e71..8f1711c6e 100644 --- a/recentusagemodel.cpp +++ b/recentusagemodel.cpp @@ -1,462 +1,462 @@ /*************************************************************************** * Copyright (C) 2014-2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "recentusagemodel.h" #include "actionlist.h" #include "appsmodel.h" #include "appentry.h" -#include "favoritesmodel.h" +#include "kastatsfavoritesmodel.h" #include #include #include #if HAVE_X11 #include #endif #include #include #include #include #include #include #include #include #include namespace KAStats = KActivities::Stats; using namespace KAStats; using namespace KAStats::Terms; GroupSortProxy::GroupSortProxy(QAbstractItemModel *sourceModel) : QSortFilterProxyModel(sourceModel) { sourceModel->setParent(this); setSourceModel(sourceModel); sort(0); } GroupSortProxy::~GroupSortProxy() { } InvalidAppsFilterProxy::InvalidAppsFilterProxy(AbstractModel *parentModel, QAbstractItemModel *sourceModel) : QSortFilterProxyModel(sourceModel) , m_parentModel(parentModel) { connect(parentModel, &AbstractModel::favoritesModelChanged, this, &InvalidAppsFilterProxy::connectNewFavoritesModel); connectNewFavoritesModel(); sourceModel->setParent(this); setSourceModel(sourceModel); } InvalidAppsFilterProxy::~InvalidAppsFilterProxy() { } void InvalidAppsFilterProxy::connectNewFavoritesModel() { - FavoritesModel* favoritesModel = static_cast(m_parentModel->favoritesModel()); - connect(favoritesModel, &FavoritesModel::favoritesChanged, this, &QSortFilterProxyModel::invalidate); + KAStatsFavoritesModel* favoritesModel = static_cast(m_parentModel->favoritesModel()); + connect(favoritesModel, &KAStatsFavoritesModel::favoritesChanged, this, &QSortFilterProxyModel::invalidate); invalidate(); } bool InvalidAppsFilterProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { Q_UNUSED(source_parent); const QString resource = sourceModel()->index(source_row, 0).data(ResultModel::ResourceRole).toString(); if (resource.startsWith(QLatin1String("applications:"))) { KService::Ptr service = KService::serviceByStorageId(resource.section(':', 1)); - FavoritesModel* favoritesModel = m_parentModel ? static_cast(m_parentModel->favoritesModel()) : nullptr; + KAStatsFavoritesModel* favoritesModel = m_parentModel ? static_cast(m_parentModel->favoritesModel()) : nullptr; return (service && (!favoritesModel || !favoritesModel->isFavorite(service->storageId()))); } return true; } bool GroupSortProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const { const QString &lResource = sourceModel()->data(left, ResultModel::ResourceRole).toString(); const QString &rResource = sourceModel()->data(right, ResultModel::ResourceRole).toString(); if (lResource.startsWith(QLatin1String("applications:")) && !rResource.startsWith(QLatin1String("applications:"))) { return true; } else if (!lResource.startsWith(QLatin1String("applications:")) && rResource.startsWith(QLatin1String("applications:"))) { return false; } return (left.row() < right.row()); } RecentUsageModel::RecentUsageModel(QObject *parent, IncludeUsage usage, int ordering) : ForwardingModel(parent) , m_usage(usage) , m_ordering((Ordering)ordering) , m_complete(false) { refresh(); } RecentUsageModel::~RecentUsageModel() { } RecentUsageModel::IncludeUsage RecentUsageModel::usage() const { return m_usage; } QString RecentUsageModel::description() const { switch (m_usage) { case AppsAndDocs: return i18n("Recently Used"); case OnlyApps: return i18n("Applications"); case OnlyDocs: default: return i18n("Documents"); } } QString RecentUsageModel::resourceAt(int row) const { QSortFilterProxyModel *sourceProxy = qobject_cast(sourceModel()); if (sourceProxy) { return sourceProxy->sourceModel()->data(sourceProxy->mapToSource(sourceProxy->index(row, 0)), ResultModel::ResourceRole).toString(); } return sourceModel()->data(index(row, 0), ResultModel::ResourceRole).toString(); } QVariant RecentUsageModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } const QString &resource = resourceAt(index.row()); if (resource.startsWith(QLatin1String("applications:"))) { return appData(resource, role); } else { return docData(resource, role); } } QVariant RecentUsageModel::appData(const QString &resource, int role) const { const QString storageId = resource.section(':', 1); KService::Ptr service = KService::serviceByStorageId(storageId); QStringList allowedTypes({ QLatin1String("Service"), QLatin1String("Application") }); if (!service || !allowedTypes.contains(service->property(QLatin1String("Type")).toString()) || service->exec().isEmpty()) { return QVariant(); } if (role == Qt::DisplayRole) { AppsModel *parentModel = qobject_cast(QObject::parent()); if (parentModel) { return AppEntry::nameFromService(service, (AppEntry::NameFormat)qobject_cast(QObject::parent())->appNameFormat()); } else { return AppEntry::nameFromService(service, AppEntry::NameOnly); } } else if (role == Qt::DecorationRole) { return QIcon::fromTheme(service->icon(), QIcon::fromTheme("unknown")); } else if (role == Kicker::DescriptionRole) { return service->comment(); } else if (role == Kicker::GroupRole) { return i18n("Applications"); } else if (role == Kicker::FavoriteIdRole) { return service->storageId(); } else if (role == Kicker::HasActionListRole) { return true; } else if (role == Kicker::ActionListRole) { QVariantList actionList; const QVariantList &jumpList = Kicker::jumpListActions(service); if (jumpList.count()) { actionList << jumpList << Kicker::createSeparatorActionItem(); } const QVariantList &recentDocuments = Kicker::recentDocumentActions(service); if (recentDocuments.count()) { actionList << recentDocuments << Kicker::createSeparatorActionItem(); } const QVariantMap &forgetAction = Kicker::createActionItem(i18n("Forget Application"), "forget"); actionList << forgetAction; const QVariantMap &forgetAllAction = Kicker::createActionItem(forgetAllActionName(), "forgetAll"); actionList << forgetAllAction; return actionList; } return QVariant(); } QVariant RecentUsageModel::docData(const QString &resource, int role) const { QUrl url(resource); if (url.scheme().isEmpty()) { url.setScheme(QStringLiteral("file")); } const KFileItem fileItem(url); if (!url.isValid() || !(fileItem.isFile() || fileItem.isDir())) { return QVariant(); } if (role == Qt::DisplayRole) { return fileItem.text(); } else if (role == Qt::DecorationRole) { return QIcon::fromTheme(fileItem.iconName(), QIcon::fromTheme("unknown")); } else if (role == Kicker::GroupRole) { return i18n("Documents"); } else if (role == Kicker::FavoriteIdRole || role == Kicker::UrlRole) { return url.toString(); } else if (role == Kicker::UrlRole) { return url; } else if (role == Kicker::HasActionListRole) { return true; } else if (role == Kicker::ActionListRole) { QVariantList actionList = Kicker::createActionListForFileItem(fileItem); actionList << Kicker::createSeparatorActionItem(); const QVariantMap &forgetAction = Kicker::createActionItem(i18n("Forget Document"), "forget"); actionList << forgetAction; const QVariantMap &forgetAllAction = Kicker::createActionItem(forgetAllActionName(), "forgetAll"); actionList << forgetAllAction; return actionList; } return QVariant(); } bool RecentUsageModel::trigger(int row, const QString &actionId, const QVariant &argument) { Q_UNUSED(argument) bool withinBounds = row >= 0 && row < rowCount(); if (actionId.isEmpty() && withinBounds) { const QString &resource = resourceAt(row); if (!resource.startsWith(QLatin1String("applications:"))) { new KRun(docData(resource, Kicker::UrlRole).toUrl(), 0); return true; } const QString storageId = resource.section(':', 1); KService::Ptr service = KService::serviceByStorageId(storageId); if (!service) { return false; } quint32 timeStamp = 0; #if HAVE_X11 if (QX11Info::isPlatformX11()) { timeStamp = QX11Info::appUserTime(); } #endif // TODO Once we depend on KDE Frameworks 5.24 and D1902 is merged, use KRun::runApplication instead KRun::runService(*service, {}, nullptr, true, {}, KStartupInfo::createNewStartupIdForTimestamp(timeStamp)); KActivities::ResourceInstance::notifyAccessed(QUrl("applications:" + storageId), "org.kde.plasma.kicker"); return true; } else if (actionId == "forget" && withinBounds) { if (m_activitiesModel) { QModelIndex idx = sourceModel()->index(row, 0); QSortFilterProxyModel *sourceProxy = qobject_cast(sourceModel()); while (sourceProxy) { idx = sourceProxy->mapToSource(idx); sourceProxy = qobject_cast(sourceProxy->sourceModel()); } static_cast(m_activitiesModel.data())->forgetResource(idx.row()); } return false; } else if (actionId == "forgetAll") { if (m_activitiesModel) { static_cast(m_activitiesModel.data())->forgetAllResources(); } return false; } else if (withinBounds) { const QString &resource = resourceAt(row); if (resource.startsWith(QLatin1String("applications:"))) { const QString storageId = sourceModel()->data(sourceModel()->index(row, 0), ResultModel::ResourceRole).toString().section(':', 1); KService::Ptr service = KService::serviceByStorageId(storageId); if (service) { return Kicker::handleRecentDocumentAction(service, actionId, argument); } } else { bool close = false; QUrl url(sourceModel()->data(sourceModel()->index(row, 0), ResultModel::ResourceRole).toString()); KFileItem item(url); if (Kicker::handleFileItemAction(item, actionId, argument, &close)) { return close; } } } return false; } bool RecentUsageModel::hasActions() const { return rowCount(); } QVariantList RecentUsageModel::actions() const { QVariantList actionList; if (rowCount()) { actionList << Kicker::createActionItem(forgetAllActionName(), "forgetAll"); } return actionList; } QString RecentUsageModel::forgetAllActionName() const { switch (m_usage) { case AppsAndDocs: return i18n("Forget All"); case OnlyApps: return i18n("Forget All Applications"); case OnlyDocs: default: return i18n("Forget All Documents"); } } void RecentUsageModel::setOrdering(int ordering) { if (ordering == m_ordering) return; m_ordering = (Ordering)ordering; refresh(); emit orderingChanged(ordering); } int RecentUsageModel::ordering() const { return m_ordering; } void RecentUsageModel::classBegin() { } void RecentUsageModel::componentComplete() { m_complete = true; refresh(); } void RecentUsageModel::refresh() { if (qmlEngine(this) && !m_complete) { return; } setSourceModel(nullptr); delete m_activitiesModel; auto query = UsedResources | (m_ordering == Recent ? RecentlyUsedFirst : HighScoredFirst) | Agent::any() | Type::any() | Activity::current(); switch (m_usage) { case AppsAndDocs: { query = query | Url::startsWith("applications:") | Url::file() | Limit(30); break; } case OnlyApps: { query = query | Url::startsWith("applications:") | Limit(15); break; } case OnlyDocs: default: { query = query | Url::file() | Limit(15); } } m_activitiesModel = new ResultModel(query); QAbstractItemModel *model = m_activitiesModel; QModelIndex index; if (model->canFetchMore(index)) { model->fetchMore(index); } if (m_usage != OnlyDocs) { model = new InvalidAppsFilterProxy(this, model); } if (m_usage == AppsAndDocs) { model = new GroupSortProxy(model); } setSourceModel(model); } diff --git a/rootmodel.cpp b/rootmodel.cpp index 2260c681f..6750d8a89 100644 --- a/rootmodel.cpp +++ b/rootmodel.cpp @@ -1,453 +1,453 @@ /*************************************************************************** * Copyright (C) 2014-2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "rootmodel.h" #include "actionlist.h" -#include "favoritesmodel.h" +#include "kastatsfavoritesmodel.h" #include "recentcontactsmodel.h" #include "recentusagemodel.h" #include "systemmodel.h" #include #include GroupEntry::GroupEntry(AppsModel *parentModel, const QString &name, const QString &iconName, AbstractModel *childModel) : AbstractGroupEntry(parentModel) , m_name(name) , m_iconName(iconName) , m_childModel(childModel) { QObject::connect(parentModel, &RootModel::cleared, childModel, &AbstractModel::deleteLater); QObject::connect(childModel, &AbstractModel::countChanged, [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } } ); } QString GroupEntry::name() const { return m_name; } QIcon GroupEntry::icon() const { return QIcon::fromTheme(m_iconName, QIcon::fromTheme("unknown")); } bool GroupEntry::hasChildren() const { return m_childModel && m_childModel->count() > 0; } AbstractModel *GroupEntry::childModel() const { return m_childModel; } RootModel::RootModel(QObject *parent) : AppsModel(QString(), parent) , m_complete(false) -, m_favorites(new FavoritesModel(this)) +, m_favorites(new KAStatsFavoritesModel(this)) , m_systemModel(nullptr) , m_autoPopulate(true) , m_showAllApps(false) , m_showRecentApps(true) , m_showRecentDocs(true) , m_showRecentContacts(false) , m_recentOrdering(RecentUsageModel::Recent) , m_showPowerSession(true) , m_recentAppsModel(0) , m_recentDocsModel(0) , m_recentContactsModel(0) { } RootModel::~RootModel() { } QVariant RootModel::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.row() >= m_entryList.count()) { return QVariant(); } if (role == Kicker::HasActionListRole || role == Kicker::ActionListRole) { const AbstractEntry *entry = m_entryList.at(index.row()); if (entry->type() == AbstractEntry::GroupType) { const GroupEntry *group = static_cast(entry); AbstractModel *model = group->childModel(); if (model == m_recentAppsModel || model == m_recentDocsModel || model == m_recentContactsModel) { if (role == Kicker::HasActionListRole) { return true; } else if (role == Kicker::ActionListRole) { QVariantList actionList; actionList << model->actions(); actionList << Kicker::createSeparatorActionItem(); actionList << Kicker::createActionItem(i18n("Hide %1", group->name()), "hideCategory"); return actionList; } } } } return AppsModel::data(index, role); } bool RootModel::trigger(int row, const QString& actionId, const QVariant& argument) { const AbstractEntry *entry = m_entryList.at(row); if (entry->type() == AbstractEntry::GroupType) { if (actionId == "hideCategory") { AbstractModel *model = entry->childModel(); if (model == m_recentAppsModel) { setShowRecentApps(false); return true; } else if (model == m_recentDocsModel) { setShowRecentDocs(false); return true; } else if (model == m_recentContactsModel) { setShowRecentContacts(false); return true; } } else if (entry->childModel()->hasActions()) { return entry->childModel()->trigger(-1, actionId, QVariant()); } } return AppsModel::trigger(row, actionId, argument); } bool RootModel::autoPopulate() const { return m_autoPopulate; } void RootModel::setAutoPopulate(bool populate) { if (m_autoPopulate != populate) { m_autoPopulate = populate; emit autoPopulateChanged(); } } bool RootModel::showAllApps() const { return m_showAllApps; } void RootModel::setShowAllApps(bool show) { if (m_showAllApps != show) { m_showAllApps = show; refresh(); emit showAllAppsChanged(); } } bool RootModel::showRecentApps() const { return m_showRecentApps; } void RootModel::setShowRecentApps(bool show) { if (show != m_showRecentApps) { m_showRecentApps = show; refresh(); emit showRecentAppsChanged(); } } bool RootModel::showRecentDocs() const { return m_showRecentDocs; } void RootModel::setShowRecentDocs(bool show) { if (show != m_showRecentDocs) { m_showRecentDocs = show; refresh(); emit showRecentDocsChanged(); } } bool RootModel::showRecentContacts() const { return m_showRecentContacts; } void RootModel::setShowRecentContacts(bool show) { if (show != m_showRecentContacts) { m_showRecentContacts = show; refresh(); emit showRecentContactsChanged(); } } int RootModel::recentOrdering() const { return m_recentOrdering; } void RootModel::setRecentOrdering(int ordering) { if (ordering != m_recentOrdering) { m_recentOrdering = ordering; refresh(); emit recentOrderingChanged(); } } bool RootModel::showPowerSession() const { return m_showPowerSession; } void RootModel::setShowPowerSession(bool show) { if (show != m_showPowerSession) { m_showPowerSession = show; refresh(); emit showPowerSessionChanged(); } } AbstractModel* RootModel::favoritesModel() { return m_favorites; } AbstractModel* RootModel::systemFavoritesModel() { if (m_systemModel) { return m_systemModel->favoritesModel(); } return nullptr; } void RootModel::classBegin() { } void RootModel::componentComplete() { m_complete = true; if (m_autoPopulate) { refresh(); } } void RootModel::refresh() { if (!m_complete) { return; } beginResetModel(); AppsModel::refreshInternal(); AppsModel *allModel = nullptr; m_recentAppsModel = nullptr; m_recentDocsModel = nullptr; m_recentContactsModel = nullptr; if (m_showAllApps) { QList groups; if (m_paginate) { - m_favorites = new FavoritesModel(this); + m_favorites = new KAStatsFavoritesModel(this); emit favoritesModelChanged(); QHash appsHash; QList apps; foreach (const AbstractEntry *groupEntry, m_entryList) { AbstractModel *model = groupEntry->childModel(); for (int i = 0; i < model->count(); ++i) { GroupEntry *subGroupEntry = static_cast(model->index(i, 0).internalPointer()); AbstractModel *subModel = subGroupEntry->childModel(); for (int j = 0; j < subModel->count(); ++j) { AppEntry *appEntry = static_cast(subModel->index(j, 0).internalPointer()); if (appEntry->name().isEmpty()) { continue; } appsHash.insert(appEntry->service()->menuId(), appEntry); } } } apps = appsHash.values(); QCollator c; std::sort(apps.begin(), apps.end(), [&c](AbstractEntry* a, AbstractEntry* b) { if (a->type() != b->type()) { return a->type() > b->type(); } else { return c.compare(a->name(), b->name()) < 0; } }); int at = 0; QList page; page.reserve(m_pageSize); foreach(AppEntry *app, apps) { page.append(app); if (at == (m_pageSize - 1)) { at = 0; AppsModel *model = new AppsModel(page, false, this); groups.append(new GroupEntry(this, QString(), QString(), model)); page.clear(); } else { ++at; } } if (!page.isEmpty()) { AppsModel *model = new AppsModel(page, false, this); groups.append(new GroupEntry(this, QString(), QString(), model)); } groups.prepend(new GroupEntry(this, QString(), QString(), m_favorites)); } else { QHash> m_categoryHash; foreach (const AbstractEntry *groupEntry, m_entryList) { AbstractModel *model = groupEntry->childModel(); for (int i = 0; i < model->count(); ++i) { AbstractEntry *appEntry = static_cast(model->index(i, 0).internalPointer()); if (appEntry->name().isEmpty()) { continue; } const QChar &first = appEntry->name().at(0).toUpper(); m_categoryHash[first.isDigit() ? QStringLiteral("0-9") : first].append(appEntry); } } QHashIterator> i(m_categoryHash); while (i.hasNext()) { i.next(); AppsModel *model = new AppsModel(i.value(), false, this); model->setDescription(i.key()); groups.append(new GroupEntry(this, i.key(), QString(), model)); } } allModel = new AppsModel(groups, true, this); allModel->setDescription(QStringLiteral("KICKER_ALL_MODEL")); // Intentionally no i18n. } int separatorPosition = 0; if (allModel) { m_entryList.prepend(new GroupEntry(this, i18n("All Applications"), QString(), allModel)); ++separatorPosition; } if (m_showRecentContacts) { m_recentContactsModel = new RecentContactsModel(this); m_entryList.prepend(new GroupEntry(this, i18n("Recent Contacts"), QString(), m_recentContactsModel)); ++separatorPosition; } if (m_showRecentDocs) { m_recentDocsModel = new RecentUsageModel(this, RecentUsageModel::OnlyDocs, m_recentOrdering); m_entryList.prepend(new GroupEntry(this, m_recentOrdering == RecentUsageModel::Recent ? i18n("Recent Documents") : i18n("Often Used Documents"), QString(), m_recentDocsModel)); ++separatorPosition; } if (m_showRecentApps) { m_recentAppsModel = new RecentUsageModel(this, RecentUsageModel::OnlyApps, m_recentOrdering); m_entryList.prepend(new GroupEntry(this, m_recentOrdering == RecentUsageModel::Recent ? i18n("Recent Applications") : i18n("Often Used Applications"), QString(), m_recentAppsModel)); ++separatorPosition; } if (m_showSeparators && separatorPosition > 0) { m_entryList.insert(separatorPosition, new SeparatorEntry(this)); ++m_separatorCount; } m_systemModel = new SystemModel(this); if (m_showPowerSession) { m_entryList << new GroupEntry(this, i18n("Power / Session"), QString(), m_systemModel); } endResetModel(); m_favorites->refresh(); emit systemFavoritesModelChanged(); emit countChanged(); emit separatorCountChanged(); emit refreshed(); } diff --git a/rootmodel.h b/rootmodel.h index b9356fa9c..781d5cab2 100644 --- a/rootmodel.h +++ b/rootmodel.h @@ -1,137 +1,137 @@ /*************************************************************************** * Copyright (C) 2014-2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #ifndef ROOTMODEL_H #define ROOTMODEL_H #include "appsmodel.h" #include -class FavoritesModel; +class KAStatsFavoritesModel; class RecentContactsModel; class RecentUsageModel; class SystemModel; class RootModel; class GroupEntry : public AbstractGroupEntry { public: GroupEntry(AppsModel *parentModel, const QString &name, const QString &iconName, AbstractModel *childModel); QIcon icon() const Q_DECL_OVERRIDE; QString name() const Q_DECL_OVERRIDE; bool hasChildren() const Q_DECL_OVERRIDE; AbstractModel *childModel() const Q_DECL_OVERRIDE; private: QString m_name; QString m_iconName; QPointer m_childModel; }; class RootModel : public AppsModel, public QQmlParserStatus { Q_OBJECT Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(bool autoPopulate READ autoPopulate WRITE setAutoPopulate NOTIFY autoPopulateChanged) Q_PROPERTY(QObject* systemFavoritesModel READ systemFavoritesModel NOTIFY systemFavoritesModelChanged) Q_PROPERTY(bool showAllApps READ showAllApps WRITE setShowAllApps NOTIFY showAllAppsChanged) Q_PROPERTY(bool showRecentApps READ showRecentApps WRITE setShowRecentApps NOTIFY showRecentAppsChanged) Q_PROPERTY(bool showRecentDocs READ showRecentDocs WRITE setShowRecentDocs NOTIFY showRecentDocsChanged) Q_PROPERTY(bool showRecentContacts READ showRecentContacts WRITE setShowRecentContacts NOTIFY showRecentContactsChanged) Q_PROPERTY(int recentOrdering READ recentOrdering WRITE setRecentOrdering NOTIFY recentOrderingChanged) Q_PROPERTY(bool showPowerSession READ showPowerSession WRITE setShowPowerSession NOTIFY showPowerSessionChanged) public: explicit RootModel(QObject *parent = 0); ~RootModel(); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; Q_INVOKABLE virtual bool trigger(int row, const QString &actionId, const QVariant &argument) Q_DECL_OVERRIDE; bool autoPopulate() const; void setAutoPopulate(bool populate); bool showAllApps() const; void setShowAllApps(bool show); bool showRecentApps() const; void setShowRecentApps(bool show); bool showRecentDocs() const; void setShowRecentDocs(bool show); bool showRecentContacts() const; void setShowRecentContacts(bool show); int recentOrdering() const; void setRecentOrdering(int ordering); bool showPowerSession() const; void setShowPowerSession(bool show); AbstractModel* favoritesModel() Q_DECL_OVERRIDE; AbstractModel* systemFavoritesModel(); void classBegin() override; void componentComplete() override; Q_SIGNALS: void refreshed() const; void systemFavoritesModelChanged() const; void autoPopulateChanged() const; void showAllAppsChanged() const; void showRecentAppsChanged() const; void showRecentDocsChanged() const; void showRecentContactsChanged() const; void showPowerSessionChanged() const; void recentOrderingChanged() const; void recentAppsModelChanged() const; protected Q_SLOTS: void refresh() Q_DECL_OVERRIDE; private: bool m_complete; - FavoritesModel *m_favorites; + KAStatsFavoritesModel *m_favorites; SystemModel *m_systemModel; bool m_autoPopulate; bool m_showAllApps; bool m_showRecentApps; bool m_showRecentDocs; bool m_showRecentContacts; int m_recentOrdering; bool m_showPowerSession; RecentUsageModel *m_recentAppsModel; RecentUsageModel *m_recentDocsModel; RecentContactsModel *m_recentContactsModel; }; #endif diff --git a/favoritesmodel.cpp b/simplefavoritesmodel.cpp similarity index 84% rename from favoritesmodel.cpp rename to simplefavoritesmodel.cpp index 0ee6a5391..a2e68abff 100644 --- a/favoritesmodel.cpp +++ b/simplefavoritesmodel.cpp @@ -1,333 +1,333 @@ /*************************************************************************** * Copyright (C) 2014-2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ -#include "favoritesmodel.h" +#include "simplefavoritesmodel.h" #include "appentry.h" #include "contactentry.h" #include "fileentry.h" #include "systementry.h" #include "actionlist.h" #include -FavoritesModel::FavoritesModel(QObject *parent) : AbstractModel(parent) +SimpleFavoritesModel::SimpleFavoritesModel(QObject *parent) : AbstractModel(parent) , m_enabled(true) , m_maxFavorites(-1) , m_dropPlaceholderIndex(-1) { } -FavoritesModel::~FavoritesModel() +SimpleFavoritesModel::~SimpleFavoritesModel() { qDeleteAll(m_entryList); } -QString FavoritesModel::description() const +QString SimpleFavoritesModel::description() const { return i18n("Favorites"); } -QVariant FavoritesModel::data(const QModelIndex& index, int role) const +QVariant SimpleFavoritesModel::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.row() >= rowCount()) { return QVariant(); } if (index.row() == m_dropPlaceholderIndex) { if (role == Kicker::IsDropPlaceholderRole) { return true; } else { return QVariant(); } } int mappedIndex = index.row(); if (m_dropPlaceholderIndex != -1 && mappedIndex > m_dropPlaceholderIndex) { --mappedIndex; } const AbstractEntry *entry = m_entryList.at(mappedIndex); if (role == Qt::DisplayRole) { return entry->name(); } else if (role == Qt::DecorationRole) { return entry->icon(); } else if (role == Kicker::DescriptionRole) { return entry->description(); } else if (role == Kicker::FavoriteIdRole) { return entry->id(); } else if (role == Kicker::UrlRole) { return entry->url(); } else if (role == Kicker::HasActionListRole) { return entry->hasActions(); } else if (role == Kicker::ActionListRole) { return entry->actions(); } return QVariant(); } -int FavoritesModel::rowCount(const QModelIndex& parent) const +int SimpleFavoritesModel::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_entryList.count() + (m_dropPlaceholderIndex != -1 ? 1 : 0); } -bool FavoritesModel::trigger(int row, const QString &actionId, const QVariant &argument) +bool SimpleFavoritesModel::trigger(int row, const QString &actionId, const QVariant &argument) { if (row < 0 || row >= m_entryList.count()) { return false; } return m_entryList.at(row)->run(actionId, argument); } -bool FavoritesModel::enabled() const +bool SimpleFavoritesModel::enabled() const { return m_enabled; } -void FavoritesModel::setEnabled(bool enable) +void SimpleFavoritesModel::setEnabled(bool enable) { if (m_enabled != enable) { m_enabled = enable; emit enabledChanged(); } } -QStringList FavoritesModel::favorites() const +QStringList SimpleFavoritesModel::favorites() const { return m_favorites; } -void FavoritesModel::setFavorites(const QStringList& favorites) +void SimpleFavoritesModel::setFavorites(const QStringList& favorites) { QStringList _favorites(favorites); _favorites.removeDuplicates(); if (_favorites != m_favorites) { m_favorites = _favorites; refresh(); } } -int FavoritesModel::maxFavorites() const +int SimpleFavoritesModel::maxFavorites() const { return m_maxFavorites; } -void FavoritesModel::setMaxFavorites(int max) +void SimpleFavoritesModel::setMaxFavorites(int max) { if (m_maxFavorites != max) { m_maxFavorites = max; if (m_maxFavorites != -1 && m_favorites.count() > m_maxFavorites) { refresh(); } emit maxFavoritesChanged(); } } -bool FavoritesModel::isFavorite(const QString &id) const +bool SimpleFavoritesModel::isFavorite(const QString &id) const { return m_favorites.contains(id); } -void FavoritesModel::addFavorite(const QString &id, int index) +void SimpleFavoritesModel::addFavorite(const QString &id, int index) { if (!m_enabled || id.isEmpty()) { return; } if (m_maxFavorites != -1 && m_favorites.count() == m_maxFavorites) { return; } AbstractEntry *entry = favoriteFromId(id); if (!entry || !entry->isValid()) { delete entry; return; } setDropPlaceholderIndex(-1); int insertIndex = (index != -1) ? index : m_entryList.count(); beginInsertRows(QModelIndex(), insertIndex, insertIndex); m_entryList.insert(insertIndex, entry); m_favorites.insert(insertIndex, entry->id()); endInsertRows(); emit countChanged(); emit favoritesChanged(); } -void FavoritesModel::removeFavorite(const QString &id) +void SimpleFavoritesModel::removeFavorite(const QString &id) { if (!m_enabled || id.isEmpty()) { return; } int index = m_favorites.indexOf(id); if (index != -1) { setDropPlaceholderIndex(-1); beginRemoveRows(QModelIndex(), index, index); delete m_entryList[index]; m_entryList.removeAt(index); m_favorites.removeAt(index); endRemoveRows(); emit countChanged(); emit favoritesChanged(); } } -void FavoritesModel::moveRow(int from, int to) +void SimpleFavoritesModel::moveRow(int from, int to) { if (from >= m_favorites.count() || to >= m_favorites.count()) { return; } if (from == to) { return; } setDropPlaceholderIndex(-1); int modelTo = to + (to > from ? 1 : 0); bool ok = beginMoveRows(QModelIndex(), from, from, QModelIndex(), modelTo); if (ok) { m_entryList.move(from, to); m_favorites.move(from, to); endMoveRows(); emit favoritesChanged(); } } -int FavoritesModel::dropPlaceholderIndex() const +int SimpleFavoritesModel::dropPlaceholderIndex() const { return m_dropPlaceholderIndex; } -void FavoritesModel::setDropPlaceholderIndex(int index) +void SimpleFavoritesModel::setDropPlaceholderIndex(int index) { if (index == -1 && m_dropPlaceholderIndex != -1) { beginRemoveRows(QModelIndex(), m_dropPlaceholderIndex, m_dropPlaceholderIndex); m_dropPlaceholderIndex = index; endRemoveRows(); emit countChanged(); } else if (index != -1 && m_dropPlaceholderIndex == -1) { beginInsertRows(QModelIndex(), index, index); m_dropPlaceholderIndex = index; endInsertRows(); emit countChanged(); } else if (m_dropPlaceholderIndex != index) { int modelTo = index + (index > m_dropPlaceholderIndex ? 1 : 0); bool ok = beginMoveRows(QModelIndex(), m_dropPlaceholderIndex, m_dropPlaceholderIndex, QModelIndex(), modelTo); if (ok) { m_dropPlaceholderIndex = index; endMoveRows(); } } } -AbstractModel *FavoritesModel::favoritesModel() +AbstractModel *SimpleFavoritesModel::favoritesModel() { return this; } -void FavoritesModel::refresh() +void SimpleFavoritesModel::refresh() { beginResetModel(); setDropPlaceholderIndex(-1); int oldCount = m_entryList.count(); qDeleteAll(m_entryList); m_entryList.clear(); QStringList newFavorites; foreach(const QString &id, m_favorites) { AbstractEntry *entry = favoriteFromId(id); if (entry && entry->isValid()) { m_entryList << entry; newFavorites << entry->id(); if (m_maxFavorites != -1 && newFavorites.count() == m_maxFavorites) { break; } } else if (entry) { delete entry; } } m_favorites = newFavorites; endResetModel(); if (oldCount != m_entryList.count()) { emit countChanged(); } emit favoritesChanged(); } -AbstractEntry *FavoritesModel::favoriteFromId(const QString &id) +AbstractEntry *SimpleFavoritesModel::favoriteFromId(const QString &id) { const QUrl url(id); const QString &s = url.scheme(); if ((s.isEmpty() && id.contains(QStringLiteral(".desktop"))) || s == QStringLiteral("preferred")) { return new AppEntry(this, id); } else if (s == QStringLiteral("ktp")) { return new ContactEntry(this, id); } else if (url.isValid() && !url.scheme().isEmpty()) { return new FileEntry(this, url); } else { return new SystemEntry(this, id); } return nullptr; } diff --git a/favoritesmodel.h b/simplefavoritesmodel.h similarity index 94% rename from favoritesmodel.h rename to simplefavoritesmodel.h index 8ed1bf921..7b044760c 100644 --- a/favoritesmodel.h +++ b/simplefavoritesmodel.h @@ -1,91 +1,91 @@ /*************************************************************************** * Copyright (C) 2014-2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ -#ifndef FAVORITESMODEL_H -#define FAVORITESMODEL_H +#ifndef SIMPLEFAVORITESMODEL_H +#define SIMPLEFAVORITESMODEL_H #include "abstractmodel.h" #include #include -class FavoritesModel : public AbstractModel +class SimpleFavoritesModel : public AbstractModel { Q_OBJECT Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(QStringList favorites READ favorites WRITE setFavorites NOTIFY favoritesChanged) Q_PROPERTY(int maxFavorites READ maxFavorites WRITE setMaxFavorites NOTIFY maxFavoritesChanged) Q_PROPERTY(int dropPlaceholderIndex READ dropPlaceholderIndex WRITE setDropPlaceholderIndex NOTIFY dropPlaceholderIndexChanged) public: - explicit FavoritesModel(QObject *parent = 0); - ~FavoritesModel(); + explicit SimpleFavoritesModel(QObject *parent = 0); + ~SimpleFavoritesModel(); QString description() const Q_DECL_OVERRIDE; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument) Q_DECL_OVERRIDE; bool enabled() const; void setEnabled(bool enable); QStringList favorites() const; void setFavorites(const QStringList &favorites); int maxFavorites() const; void setMaxFavorites(int max); Q_INVOKABLE bool isFavorite(const QString &id) const; Q_INVOKABLE void addFavorite(const QString &id, int index = -1); Q_INVOKABLE void removeFavorite(const QString &id); Q_INVOKABLE void moveRow(int from, int to); int dropPlaceholderIndex() const; void setDropPlaceholderIndex(int index); AbstractModel* favoritesModel() Q_DECL_OVERRIDE; public Q_SLOTS: void refresh() Q_DECL_OVERRIDE; Q_SIGNALS: void enabledChanged() const; void favoritesChanged() const; void maxFavoritesChanged() const; void dropPlaceholderIndexChanged(); private: AbstractEntry *favoriteFromId(const QString &id); bool m_enabled; QList m_entryList; QStringList m_favorites; int m_maxFavorites; int m_dropPlaceholderIndex; }; #endif diff --git a/systemmodel.cpp b/systemmodel.cpp index 498e38058..f5dcd1d23 100644 --- a/systemmodel.cpp +++ b/systemmodel.cpp @@ -1,134 +1,134 @@ /*************************************************************************** * Copyright (C) 2014-2015 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "systemmodel.h" #include "actionlist.h" -#include "favoritesmodel.h" +#include "simplefavoritesmodel.h" #include "systementry.h" #include #include #include SystemModel::SystemModel(QObject *parent) : AbstractModel(parent) { init(); - m_favoritesModel = new FavoritesModel(this); + m_favoritesModel = new SimpleFavoritesModel(this); const QString configFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/ksmserverrc"; KDirWatch *watch = new KDirWatch(this); watch->addFile(configFile); connect(watch, &KDirWatch::dirty, this, &SystemModel::refresh); connect(watch, &KDirWatch::created, this, &SystemModel::refresh); } SystemModel::~SystemModel() { qDeleteAll(m_entryList); } void SystemModel::init() { QList actions; actions << new SystemEntry(this, SystemEntry::LockSession); actions << new SystemEntry(this, SystemEntry::LogoutSession); actions << new SystemEntry(this, SystemEntry::SaveSession); actions << new SystemEntry(this, SystemEntry::SwitchUser); actions << new SystemEntry(this, SystemEntry::SuspendToRam); actions << new SystemEntry(this, SystemEntry::SuspendToDisk); actions << new SystemEntry(this, SystemEntry::Reboot); actions << new SystemEntry(this, SystemEntry::Shutdown); foreach(SystemEntry *entry, actions) { if (entry->isValid()) { m_entryList << entry; } else { delete entry; } } } QString SystemModel::description() const { return i18n("System actions"); } QVariant SystemModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= m_entryList.count()) { return QVariant(); } const SystemEntry *entry = m_entryList.at(index.row()); if (role == Qt::DisplayRole) { return entry->name(); } else if (role == Qt::DecorationRole) { return entry->iconName(); } else if (role == Kicker::DescriptionRole) { return entry->description(); } else if (role == Kicker::GroupRole) { return entry->group(); } else if (role == Kicker::FavoriteIdRole) { return entry->id(); } return QVariant(); } int SystemModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : m_entryList.count(); } bool SystemModel::trigger(int row, const QString &actionId, const QVariant &argument) { Q_UNUSED(actionId) Q_UNUSED(argument) if (row >= 0 && row < m_entryList.count()) { m_entryList.at(row)->run(); return true; } return false; } void SystemModel::refresh() { beginResetModel(); qDeleteAll(m_entryList); m_entryList.clear(); init(); endResetModel(); emit countChanged(); m_favoritesModel->refresh(); }