Index: applets/kicker/CMakeLists.txt
===================================================================
--- applets/kicker/CMakeLists.txt
+++ applets/kicker/CMakeLists.txt
@@ -22,9 +22,11 @@
plugin/contactentry.cpp
plugin/containmentinterface.cpp
plugin/draghelper.cpp
- plugin/favoritesmodel.cpp
+ plugin/simplefavoritesmodel.cpp
+ plugin/kastatsfavoritesmodel.cpp
plugin/fileentry.cpp
plugin/forwardingmodel.cpp
+ plugin/placeholdermodel.cpp
plugin/funnelmodel.cpp
plugin/dashboardwindow.cpp
plugin/kickerplugin.cpp
@@ -41,6 +43,7 @@
plugin/systemsettings.cpp
plugin/wheelinterceptor.cpp
plugin/windowsystem.cpp
+ plugin/funnelmodel.cpp
)
qt5_add_dbus_interface(kickerplugin_SRCS ${KRUNNERAPP_INTERFACE} krunner_interface)
Index: applets/kicker/package/contents/code/tools.js
===================================================================
--- applets/kicker/package/contents/code/tools.js
+++ applets/kicker/package/contents/code/tools.js
@@ -22,42 +22,128 @@
// Accessing actionList can be a costly operation, so we don't
// access it until we need the menu.
- var action = createFavoriteAction(favoriteModel, favoriteId);
+ var actions = createFavoriteActions(favoriteModel, favoriteId);
- if (action) {
+ if (actions) {
if (actionList && actionList.length > 0) {
var separator = { "type": "separator" };
- actionList.unshift(action, separator);
+ actionList.unshift(separator);
+ // actionList = actions.concat(actionList); // this crashes Qt O.o
+ actionList.unshift.apply(actionList, actions);
} else {
- actionList = [action];
+ actionList = actions;
}
}
actionMenu.actionList = actionList;
}
-function createFavoriteAction(favoriteModel, favoriteId) {
+function createFavoriteActions(favoriteModel, favoriteId) {
if (favoriteModel === null || !favoriteModel.enabled || favoriteId == null) {
return null;
}
- var action = {};
+ var activities = favoriteModel.activities.runningActivities;
+
+ if (activities.length <= 1) {
+ var action = {};
+
+ if (favoriteModel.isFavorite(favoriteId)) {
+ action.text = i18n("Remove from Favorites");
+ action.icon = "list-remove";
+ action.actionId = "_kicker_favorite_remove";
+ } else if (favoriteModel.maxFavorites == -1 || favoriteModel.count < favoriteModel.maxFavorites) {
+ action.text = i18n("Add to Favorites");
+ action.icon = "bookmark-new";
+ action.actionId = "_kicker_favorite_add";
+ } else {
+ return null;
+ }
+
+ action.actionArgument = { favoriteModel: favoriteModel, favoriteId: favoriteId };
+
+ return [action];
- if (favoriteModel.isFavorite(favoriteId)) {
- action.text = i18n("Remove from Favorites");
- action.icon = "list-remove";
- action.actionId = "_kicker_favorite_remove";
- } else if (favoriteModel.maxFavorites == -1 || favoriteModel.count < favoriteModel.maxFavorites) {
- action.text = i18n("Add to Favorites");
- action.icon = "bookmark-new";
- action.actionId = "_kicker_favorite_add";
} else {
- return null;
- }
+ var actions = [];
+
+ var linkedActivities = favoriteModel.linkedActivitiesFor(favoriteId);
+
+ // Adding the item to link/unlink to all activities
+
+ var linkedToAllActivities =
+ !(linkedActivities.indexOf(":global") === -1);
+
+ actions.push({
+ text : i18n("On All Activities"),
+ checkable : true,
+
+ actionId : linkedToAllActivities ?
+ "_kicker_favorite_remove_from_activity" :
+ "_kicker_favorite_set_to_activity",
+ checked : linkedToAllActivities,
+
+ actionArgument : {
+ favoriteModel: favoriteModel,
+ favoriteId: favoriteId,
+ favoriteActivity: ""
+ }
+ });
+
+
+ // Adding items for each activity separately
+
+ var addActivityItem = function(activityId, activityName) {
+ var linkedToThisActivity =
+ !(linkedActivities.indexOf(activityId) === -1);
- action.actionArgument = { favoriteModel: favoriteModel, favoriteId: favoriteId };
+ actions.push({
+ text : activityName,
+ checkable : true,
+ checked : linkedToThisActivity && !linkedToAllActivities,
- return action;
+ actionId :
+ // If we are on all activities, and the user clicks just one
+ // specific activity, unlink from everything else
+ linkedToAllActivities ? "_kicker_favorite_set_to_activity" :
+
+ // If we are linked to the current activity, just unlink from
+ // that single one
+ linkedToThisActivity ? "_kicker_favorite_remove_from_activity" :
+
+ // Otherwise, link to this activity, but do not unlink from
+ // other ones
+ "_kicker_favorite_add_to_activity",
+
+ actionArgument : {
+ favoriteModel : favoriteModel,
+ favoriteId : favoriteId,
+ favoriteActivity : activityId
+ }
+ });
+ };
+
+ // Adding the item to link/unlink to the current activity
+
+ addActivityItem(favoriteModel.activities.currentActivity, i18n("On The Current Activity"));
+
+ actions.push({
+ type: "separator",
+ actionId: "_kicker_favorite_separator"
+ });
+
+ // Adding the items for each activity
+
+ activities.forEach(function(activityId) {
+ addActivityItem(activityId, favoriteModel.activityNameForId(activityId));
+ });
+
+ return [{
+ text : i18n("Show In Favorites"),
+ icon : "favorite",
+ subActions : actions
+ }];
+ }
}
function triggerAction(model, index, actionId, actionArgument) {
@@ -88,9 +174,27 @@
if (favoriteModel === null || favoriteId == null) {
return null;
}
+
if (actionId == "_kicker_favorite_remove") {
- favoriteModel.removeFavorite(favoriteId);
+ console.log("Removing from all activities");
+ favoriteModel.removeFavoriteFrom(favoriteId, ":any");
+
} else if (actionId == "_kicker_favorite_add") {
- favoriteModel.addFavorite(favoriteId);
+ console.log("Adding to global activity");
+ favoriteModel.addFavoriteTo(favoriteId, ":global");
+
+ } else if (actionId == "_kicker_favorite_remove_from_activity") {
+ console.log("Removing from a specific activity");
+ favoriteModel.removeFavoriteFrom(favoriteId, actionArgument.favoriteActivity);
+
+ } else if (actionId == "_kicker_favorite_add_to_activity") {
+ console.log("Adding to another activity");
+ favoriteModel.addFavoriteTo(favoriteId, actionArgument.favoriteActivity);
+
+ } else if (actionId == "_kicker_favorite_set_to_activity") {
+ console.log("Removing the item from the favourites, and re-adding it just to be on a specific activity");
+ favoriteModel.removeFavoriteFrom(favoriteId, ":any");
+ favoriteModel.addFavoriteTo(favoriteId, actionArgument.favoriteActivity);
+
}
}
Index: applets/kicker/package/contents/config/main.xml
===================================================================
--- applets/kicker/package/contents/config/main.xml
+++ applets/kicker/package/contents/config/main.xml
@@ -44,6 +44,10 @@
logout,reboot,shutdown
+
+
+ false
+
Index: applets/kicker/package/contents/ui/ActionMenu.qml
===================================================================
--- applets/kicker/package/contents/ui/ActionMenu.qml
+++ applets/kicker/package/contents/ui/ActionMenu.qml
@@ -64,11 +64,28 @@
menu = contextMenuComponent.createObject(root);
- actionList.forEach(function(actionItem) {
- var item = contextMenuItemComponent.createObject(menu, {
- "actionItem": actionItem,
- });
+ fillMenu(menu, actionList);
+ }
+
+ function fillMenu(menu, items) {
+ items.forEach(function(actionItem) {
+ if (actionItem.subActions) {
+ // This is a menu
+ var submenuItem = contextSubmenuItemComponent.createObject(
+ menu, { "actionItem" : actionItem });
+
+ fillMenu(submenuItem.submenu, actionItem.subActions);
+
+ } else {
+ var item = contextMenuItemComponent.createObject(
+ menu,
+ {
+ "actionItem": actionItem,
+ }
+ );
+ }
});
+
}
Component {
@@ -80,17 +97,39 @@
}
Component {
- id: contextMenuItemComponent
+ id: contextSubmenuItemComponent
PlasmaComponents.MenuItem {
+ id: submenuItem
+
property variant actionItem
text: actionItem.text ? actionItem.text : ""
- enabled: actionItem.type != "title" && ("enabled" in actionItem ? actionItem.enabled : true)
- separator: actionItem.type == "separator"
- section: actionItem.type == "title"
icon: actionItem.icon ? actionItem.icon : null
+ property variant submenu : submenu_
+
+ PlasmaComponents.ContextMenu {
+ id: submenu_
+ visualParent: submenuItem.action
+ }
+ }
+ }
+
+ Component {
+ id: contextMenuItemComponent
+
+ PlasmaComponents.MenuItem {
+ property variant actionItem
+
+ text : actionItem.text ? actionItem.text : ""
+ enabled : actionItem.type != "title" && ("enabled" in actionItem ? actionItem.enabled : true)
+ separator : actionItem.type == "separator"
+ section : actionItem.type == "title"
+ icon : actionItem.icon ? actionItem.icon : null
+ checkable : actionItem.checkable ? actionItem.checkable : false
+ checked : actionItem.checked ? actionItem.checked : false
+
onClicked: {
actionClicked(actionItem.actionId, actionItem.actionArgument);
}
Index: applets/kicker/package/contents/ui/main.qml
===================================================================
--- applets/kicker/package/contents/ui/main.qml
+++ applets/kicker/package/contents/ui/main.qml
@@ -109,7 +109,13 @@
}
Component.onCompleted: {
- favoritesModel.favorites = plasmoid.configuration.favoriteApps;
+ favoritesModel.initForClient("org.kde.plasma.kicker.favorites.instance-" + plasmoid.id)
+
+ if (!plasmoid.configuration.favoritesPortedToKAstats) {
+ favoritesModel.addFavoritesTo(plasmoid.configuration.favoriteApps, ":global");
+ plasmoid.configuration.favoritesPortedToKAstats = true;
+ }
+
rootModel.refresh();
}
}
Index: applets/kicker/plugin/abstractentry.cpp
===================================================================
--- applets/kicker/plugin/abstractentry.cpp
+++ applets/kicker/plugin/abstractentry.cpp
@@ -19,6 +19,8 @@
#include "abstractentry.h"
+#include
+
AbstractEntry::AbstractEntry(AbstractModel *owner)
: m_owner(owner)
{
Index: applets/kicker/plugin/computermodel.h
===================================================================
--- applets/kicker/plugin/computermodel.h
+++ applets/kicker/plugin/computermodel.h
@@ -26,7 +26,7 @@
#include
#include
-class FavoritesModel;
+class SimpleFavoritesModel;
class KConcatenateRowsProxyModel;
class KFilePlacesModel;
@@ -110,7 +110,7 @@
private:
KConcatenateRowsProxyModel *m_concatProxy;
RunCommandModel *m_runCommandModel;
- FavoritesModel *m_systemAppsModel;
+ SimpleFavoritesModel *m_systemAppsModel;
FilteredPlacesModel *m_filteredPlacesModel;
AppEntry::NameFormat m_appNameFormat;
QObject *m_appletInterface;
Index: applets/kicker/plugin/computermodel.h.orig
===================================================================
--- /dev/null
+++ applets/kicker/plugin/computermodel.h.orig
@@ -0,0 +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 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;
+ FilteredPlacesModel *m_filteredPlacesModel;
+ AppEntry::NameFormat m_appNameFormat;
+ QObject *m_appletInterface;
+};
+
+#endif
Index: applets/kicker/plugin/computermodel.cpp
===================================================================
--- applets/kicker/plugin/computermodel.cpp
+++ applets/kicker/plugin/computermodel.cpp
@@ -20,7 +20,7 @@
#include "computermodel.h"
#include "actionlist.h"
-#include "favoritesmodel.h"
+#include "simplefavoritesmodel.h"
#include
@@ -137,12 +137,13 @@
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);
Index: applets/kicker/plugin/kastatsfavoritesmodel.h
===================================================================
--- /dev/null
+++ applets/kicker/plugin/kastatsfavoritesmodel.h
@@ -0,0 +1,116 @@
+/***************************************************************************
+ * 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 "placeholdermodel.h"
+
+#include
+
+#include
+#include
+
+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(QObject* activities READ activities CONSTANT)
+
+ public:
+ explicit KAStatsFavoritesModel(QObject *parent = 0);
+ ~KAStatsFavoritesModel();
+
+ QString description() const;
+
+ 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 addFavorites(const QStringList &ids, int index = -1);
+ Q_INVOKABLE void addFavoritesTo(const QStringList &ids, const QString &activityId, int index = -1);
+
+ Q_INVOKABLE QStringList linkedActivitiesFor(const QString &id) const;
+
+ Q_INVOKABLE void moveRow(int from, int to);
+
+ Q_INVOKABLE void initForClient(const QString &client);
+
+ QObject *activities() const;
+ Q_INVOKABLE QString activityNameForId(const QString &activityId) const;
+
+ AbstractModel* favoritesModel();
+
+ public Q_SLOTS:
+ virtual void refresh();
+
+ Q_SIGNALS:
+ void enabledChanged() const;
+ void favoritesChanged() const;
+ void maxFavoritesChanged() const;
+
+ private:
+ 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;
+
+ int m_maxFavorites;
+
+ KActivities::Consumer *m_activities;
+};
+
+#endif
Index: applets/kicker/plugin/kastatsfavoritesmodel.cpp
===================================================================
--- /dev/null
+++ applets/kicker/plugin/kastatsfavoritesmodel.cpp
@@ -0,0 +1,483 @@
+/***************************************************************************
+ * 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
+
+namespace KAStats = KActivities::Stats;
+
+using namespace KAStats;
+using namespace KAStats::Terms;
+
+QString agentForPath(const QString &url)
+{
+ return url.startsWith("ktp:")
+ ? "org.kde.plasma.favorites.contacts"
+ : (url.startsWith("file:") && !url.endsWith(".desktop"))
+ ? "org.kde.plasma.favorites.documents"
+ : (url.startsWith("/") && !url.endsWith(".desktop"))
+ ? "org.kde.plasma.favorites.documents"
+ // use applications as the default
+ : "org.kde.plasma.favorites.applications";
+}
+
+class KAStatsFavoritesModel::Private: public ResultModel {
+public:
+ Private(const KAStats::Query &query, const QString &clientId, KAStatsFavoritesModel *parent)
+ : ResultModel(query, clientId, parent)
+ , q(parent)
+ {
+ }
+
+ QString pathForId(const QString &id) const
+ {
+ if (id.isEmpty()) return QString();
+
+ const auto entry = favoriteFromId(id);
+
+ const auto url = entry && entry->isValid() ? entry->url()
+ : QUrl(id);
+
+ // 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()) {
+ return file.canonicalFilePath();
+ }
+ }
+
+ if (url.scheme() == "file") {
+ return QString();
+ }
+
+ return url.toString();
+ }
+
+ AbstractEntry *favoriteFromId(const QString &id) const
+ {
+ // do not use pathForId in this function
+ int pathForId; Q_UNUSED(pathForId);
+
+ // IDs can be plain appname.desktop
+ // Also, it can be a file:///... URL
+ // Or a file path
+ // Or it is an URL which is not a file
+ if (!m_entries.contains(id)) {
+ AbstractEntry *entry = nullptr;
+
+ const auto agent = agentForPath(id);
+
+ if (agent == "org.kde.plasma.favorites.contacts") {
+ entry = new ContactEntry(q, id);
+ } else if (agent == "org.kde.plasma.favorites.documents") {
+ entry = new FileEntry(q, QUrl(id));
+ } else {
+ entry = new AppEntry(q, id);
+ }
+
+ if (!entry->isValid()) return nullptr;
+
+ const QUrl url = entry->url();
+
+ // Registering the entry for the ID we got
+ m_entries[id] = entry;
+
+ // Registering the entry for the ID it thinks it has
+ m_entries[entry->id()] = entry;
+
+ // Registering the entry for the URL it thinks it has
+ m_entries[url.toString()] = entry;
+
+ // And if it is a local file, registering it for the path
+ if (url.isLocalFile()) {
+ m_entries[entry->url().toLocalFile()] = entry;
+ }
+ }
+
+ return m_entries[id];
+ }
+
+ void removeOldCachedEntries() const
+ {
+ QSet knownIds;
+ for (int row = 0; row < rowCount(); ++row) {
+ const auto id = data(index(row, 0), Kicker::UrlRole).toString();
+ // const auto id = data(index(row, 0), ResultModel::ResourceRole).toString();
+ Q_ASSERT(!id.isEmpty());
+
+ const auto favorite = favoriteFromId(id);
+
+ knownIds << id
+ << favorite->id()
+ << favorite->url().toString()
+ << favorite->url().toLocalFile();
+ }
+
+ QSet entriesToDelete;
+
+ QHashIterator i(m_entries);
+ while (i.hasNext()) {
+ i.next();
+
+ if (!knownIds.contains(i.key())) {
+ entriesToDelete << i.value();
+ }
+ }
+
+ QMutableHashIterator mi(m_entries);
+ while (mi.hasNext()) {
+ mi.next();
+
+ if (entriesToDelete.contains(mi.value())) {
+ mi.remove();
+ }
+ }
+
+ qDeleteAll(entriesToDelete);
+ }
+
+ QModelIndex cache_lastIndex;
+ AbstractEntry* cache_lastEntry = nullptr;
+
+ QVariant data(const QModelIndex &index, int role) const override
+ {
+ auto entry = cache_lastEntry;
+
+ if (cache_lastIndex != index) {
+ if (!index.isValid() || index.row() >= rowCount()) {
+ return QVariant();
+ }
+
+ const auto _this = const_cast(this);
+ const auto id = ResultModel::data(index, ResultModel::ResourceRole).toString();
+
+ if (id.isEmpty()) return QVariant();
+
+ entry = _this->favoriteFromId(id);
+
+ if (!entry || !entry->isValid()) {
+ // If the result is not valid, we need to unlink it -- to
+ // remove it from the model
+ const auto path = pathForId(id);
+
+ if (!path.isEmpty() && !m_invalidPaths.contains(path)) {
+ _this->unlinkFromActivity(QUrl(path), Activity::any(),
+ Agent(agentForPath(path)));
+ m_invalidPaths << path;
+ }
+
+ return role == Qt::DecorationRole ? "unknown"
+ : QVariant();
+ }
+
+ _this->cache_lastIndex = index;
+ _this->cache_lastEntry = entry;
+ }
+
+ 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_entries.contains(id)
+ ? m_entries[id]->run(actionId, argument)
+ : false;
+ }
+
+ KAStatsFavoritesModel *const q;
+ mutable QList m_invalidPaths;
+
+ // This can contain an entry multiple times - for the id, file and url
+ mutable QHash m_entries;
+};
+
+
+
+
+
+KAStatsFavoritesModel::KAStatsFavoritesModel(QObject *parent)
+: PlaceholderModel(parent)
+, d(new Private(
+ LinkedResources
+ | Agent {
+ "org.kde.plasma.favorites.applications",
+ "org.kde.plasma.favorites.documents",
+ "org.kde.plasma.favorites.contacts"
+ }
+ | Type::any()
+ | Activity::current()
+ | Activity::global()
+ | Limit(15),
+ QString(),
+ this)
+ )
+, m_enabled(true)
+, m_maxFavorites(-1)
+, m_activities(new KActivities::Consumer(this))
+{
+ QModelIndex index;
+
+ if (d->canFetchMore(index)) {
+ d->fetchMore(index);
+ }
+
+ setSourceModel(d);
+}
+
+KAStatsFavoritesModel::~KAStatsFavoritesModel()
+{
+}
+
+void KAStatsFavoritesModel::initForClient(const QString &clientId)
+{
+ setSourceModel(nullptr);
+ delete d;
+ d = new Private(
+ LinkedResources
+ | Agent {
+ "org.kde.plasma.favorites.applications",
+ "org.kde.plasma.favorites.documents",
+ "org.kde.plasma.favorites.contacts"
+ }
+ | Type::any()
+ | Activity::current()
+ | Activity::global()
+ | Limit(15),
+ clientId,
+ this);
+ setSourceModel(d);
+}
+
+QString KAStatsFavoritesModel::description() const
+{
+ return i18n("Favorites");
+}
+
+bool KAStatsFavoritesModel::trigger(int row, const QString &actionId, const QVariant &argument)
+{
+ // Do not allow triggering an item while another one is being dropped
+ if (m_dropPlaceholderIndex != -1) return false;
+
+ 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 API backwards-compatible";
+ return QStringList();
+}
+
+void KAStatsFavoritesModel::setFavorites(const QStringList& favorites)
+{
+ Q_UNUSED(favorites);
+ qWarning() << "KAStatsFavoritesModel::setFavorites is ignorred";
+}
+
+bool KAStatsFavoritesModel::isFavorite(const QString &id) const
+{
+ d->removeOldCachedEntries();
+ return d->m_entries.contains(id);
+}
+
+void KAStatsFavoritesModel::addFavorites(const QStringList &ids, int index)
+{
+ qDebug() << "Adding the favourites: " << ids;
+ std::for_each(ids.rbegin(), ids.rend(), [&] (const QString &id) {
+ addFavorite(id, index);
+ });
+}
+
+void KAStatsFavoritesModel::addFavoritesTo(const QStringList &ids, const QString &activityId, int index)
+{
+ qDebug() << "Adding the favourites: " << ids << "to" << activityId;
+ std::for_each(ids.rbegin(), ids.rend(), [&] (const QString &id) {
+ addFavoriteTo(id, activityId, index);
+ });
+}
+
+void KAStatsFavoritesModel::addFavorite(const QString &id, int index)
+{
+ addFavoriteTo(id, Activity::current(), index);
+}
+
+void KAStatsFavoritesModel::removeFavorite(const QString &id)
+{
+ removeFavoriteFrom(id, Activity::current());
+}
+
+void KAStatsFavoritesModel::addFavoriteTo(const QString &id, const QString &activityId, int index)
+{
+ addFavoriteTo(id, Activity(activityId), index);
+}
+
+void KAStatsFavoritesModel::removeFavoriteFrom(const QString &id, const QString &activityId)
+{
+ removeFavoriteFrom(id, Activity(activityId));
+}
+
+void KAStatsFavoritesModel::addFavoriteTo(const QString &id, const Activity &activity, int index)
+{
+ if (id.isEmpty()) return;
+
+ setDropPlaceholderIndex(-1);
+
+ const auto path = d->pathForId(id);
+
+ if (path.isEmpty()) return;
+
+ // This is a file, we want to check that it exists
+ // if (url.isLocalFile() && !QFileInfo::exists(url.toLocalFile())) return;
+
+ d->linkToActivity(QUrl(path), activity,
+ Agent(agentForPath(path)));
+
+ if (index != -1) {
+ d->setResultPosition(path, index);
+ }
+}
+
+void KAStatsFavoritesModel::removeFavoriteFrom(const QString &id, const Activity &activity)
+{
+ const auto path = d->pathForId(id);
+
+ if (path.isEmpty()) return;
+
+ d->unlinkFromActivity(
+ QUrl(path), activity,
+ Agent(agentForPath(path))
+ );
+}
+
+void KAStatsFavoritesModel::moveRow(int from, int to)
+{
+ const auto id = data(index(from, 0), Kicker::UrlRole).toString();
+ const auto path = d->pathForId(id);
+
+ if (path.isEmpty()) return;
+
+ d->setResultPosition(path, 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
+{
+ const auto path = d->pathForId(id);
+
+ if (path.isEmpty()) {
+ return {};
+ }
+
+ auto query = LinkedResources
+ | Agent {
+ "org.kde.plasma.favorites.applications",
+ "org.kde.plasma.favorites.documents",
+ "org.kde.plasma.favorites.contacts"
+ }
+ | Type::any()
+ | Activity::any()
+ | Url(path);
+
+ ResultSet results(query);
+
+ for (const auto &result: results) {
+ return result.linkedActivities();
+ }
+
+ return {};
+}
+
Index: applets/kicker/plugin/kickerplugin.cpp
===================================================================
--- applets/kicker/plugin/kickerplugin.cpp
+++ applets/kicker/plugin/kickerplugin.cpp
@@ -23,7 +23,8 @@
#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"
@@ -48,7 +49,8 @@
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");
Index: applets/kicker/plugin/placeholdermodel.h
===================================================================
--- /dev/null
+++ applets/kicker/plugin/placeholdermodel.h
@@ -0,0 +1,88 @@
+/***************************************************************************
+ * 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 PLACEHOLDERMODEL_H
+#define PLACEHOLDERMODEL_H
+
+#include "abstractmodel.h"
+
+#include
+
+class PlaceholderModel : public AbstractModel
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QAbstractItemModel* sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged);
+ Q_PROPERTY(int dropPlaceholderIndex READ dropPlaceholderIndex WRITE setDropPlaceholderIndex NOTIFY dropPlaceholderIndexChanged)
+
+ public:
+ explicit PlaceholderModel(QObject *parent = 0);
+ ~PlaceholderModel();
+
+ QString description() const;
+
+ QAbstractItemModel *sourceModel() const;
+ virtual void setSourceModel(QAbstractItemModel *sourceModel);
+
+ bool canFetchMore(const QModelIndex &parent) const;
+ void fetchMore(const QModelIndex &parent);
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex &index) const;
+
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+
+ Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument);
+
+ Q_INVOKABLE QString labelForRow(int row);
+
+ Q_INVOKABLE AbstractModel *modelForRow(int row);
+
+ AbstractModel* favoritesModel();
+
+ int separatorCount() const;
+
+ int dropPlaceholderIndex() const;
+ void setDropPlaceholderIndex(int index);
+
+ public Q_SLOTS:
+ void reset();
+
+ Q_SIGNALS:
+ void sourceModelChanged() const;
+ void dropPlaceholderIndexChanged();
+
+ protected:
+ QModelIndex indexToSourceIndex(const QModelIndex &index) const;
+ QModelIndex sourceIndexToIndex(const QModelIndex &index) const;
+ int sourceRowToRow(int sourceRow) const;
+ int rowToSourceRow(int row) const;
+
+ void connectSignals();
+ void disconnectSignals();
+
+ QPointer m_sourceModel;
+
+ int m_dropPlaceholderIndex;
+};
+
+#endif
Index: applets/kicker/plugin/placeholdermodel.cpp
===================================================================
--- /dev/null
+++ applets/kicker/plugin/placeholdermodel.cpp
@@ -0,0 +1,386 @@
+/***************************************************************************
+ * 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
+
+PlaceholderModel::PlaceholderModel(QObject *parent)
+ : AbstractModel(parent)
+ , m_dropPlaceholderIndex(-1)
+{
+}
+
+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 (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;
+
+ 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();
+}
Index: applets/kicker/plugin/recentusagemodel.cpp
===================================================================
--- applets/kicker/plugin/recentusagemodel.cpp
+++ applets/kicker/plugin/recentusagemodel.cpp
@@ -21,7 +21,7 @@
#include "actionlist.h"
#include "appsmodel.h"
#include "appentry.h"
-#include "favoritesmodel.h"
+#include "kastatsfavoritesmodel.h"
#include
@@ -74,8 +74,8 @@
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();
}
@@ -89,7 +89,7 @@
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())));
}
Index: applets/kicker/plugin/rootmodel.h
===================================================================
--- applets/kicker/plugin/rootmodel.h
+++ applets/kicker/plugin/rootmodel.h
@@ -24,7 +24,7 @@
#include
-class FavoritesModel;
+class KAStatsFavoritesModel;
class RecentContactsModel;
class RecentUsageModel;
class SystemModel;
@@ -117,7 +117,7 @@
private:
bool m_complete;
- FavoritesModel *m_favorites;
+ KAStatsFavoritesModel *m_favorites;
SystemModel *m_systemModel;
bool m_autoPopulate;
Index: applets/kicker/plugin/rootmodel.cpp
===================================================================
--- applets/kicker/plugin/rootmodel.cpp
+++ applets/kicker/plugin/rootmodel.cpp
@@ -19,7 +19,7 @@
#include "rootmodel.h"
#include "actionlist.h"
-#include "favoritesmodel.h"
+#include "kastatsfavoritesmodel.h"
#include "recentcontactsmodel.h"
#include "recentusagemodel.h"
#include "systemmodel.h"
@@ -64,7 +64,7 @@
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)
@@ -301,7 +301,7 @@
QList groups;
if (m_paginate) {
- m_favorites = new FavoritesModel(this);
+ m_favorites = new KAStatsFavoritesModel(this);
emit favoritesModelChanged();
QHash appsHash;
Index: applets/kicker/plugin/simplefavoritesmodel.h
===================================================================
--- applets/kicker/plugin/simplefavoritesmodel.h
+++ applets/kicker/plugin/simplefavoritesmodel.h
@@ -17,8 +17,8 @@
* 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"
@@ -26,7 +26,7 @@
#include
-class FavoritesModel : public AbstractModel
+class SimpleFavoritesModel : public AbstractModel
{
Q_OBJECT
@@ -36,8 +36,8 @@
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;
Index: applets/kicker/plugin/simplefavoritesmodel.cpp
===================================================================
--- applets/kicker/plugin/simplefavoritesmodel.cpp
+++ applets/kicker/plugin/simplefavoritesmodel.cpp
@@ -17,7 +17,7 @@
* 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"
@@ -26,24 +26,24 @@
#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();
@@ -84,12 +84,12 @@
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;
@@ -98,12 +98,12 @@
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;
@@ -112,12 +112,12 @@
}
}
-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();
@@ -128,12 +128,12 @@
}
}
-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)
{
@@ -147,12 +147,12 @@
}
}
-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;
@@ -184,7 +184,7 @@
emit favoritesChanged();
}
-void FavoritesModel::removeFavorite(const QString &id)
+void SimpleFavoritesModel::removeFavorite(const QString &id)
{
if (!m_enabled || id.isEmpty()) {
return;
@@ -208,7 +208,7 @@
}
}
-void FavoritesModel::moveRow(int from, int to)
+void SimpleFavoritesModel::moveRow(int from, int to)
{
if (from >= m_favorites.count() || to >= m_favorites.count()) {
return;
@@ -234,12 +234,12 @@
}
}
-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);
@@ -270,12 +270,12 @@
}
}
-AbstractModel *FavoritesModel::favoritesModel()
+AbstractModel *SimpleFavoritesModel::favoritesModel()
{
return this;
}
-void FavoritesModel::refresh()
+void SimpleFavoritesModel::refresh()
{
beginResetModel();
@@ -314,7 +314,7 @@
emit favoritesChanged();
}
-AbstractEntry *FavoritesModel::favoriteFromId(const QString &id)
+AbstractEntry *SimpleFavoritesModel::favoriteFromId(const QString &id)
{
const QUrl url(id);
const QString &s = url.scheme();
Index: applets/kicker/plugin/systemmodel.cpp
===================================================================
--- applets/kicker/plugin/systemmodel.cpp
+++ applets/kicker/plugin/systemmodel.cpp
@@ -19,7 +19,7 @@
#include "systemmodel.h"
#include "actionlist.h"
-#include "favoritesmodel.h"
+#include "simplefavoritesmodel.h"
#include "systementry.h"
#include
@@ -31,7 +31,7 @@
{
init();
- m_favoritesModel = new FavoritesModel(this);
+ m_favoritesModel = new SimpleFavoritesModel(this);
const QString configFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/ksmserverrc";
Index: applets/kickoff/package/contents/config/main.xml
===================================================================
--- applets/kickoff/package/contents/config/main.xml
+++ applets/kickoff/package/contents/config/main.xml
@@ -22,6 +22,10 @@
preferred://browser,kontact.desktop,systemsettings.desktop,org.kde.dolphin.desktop,ktp-contactlist.desktop,org.kde.kate.desktop,org.kde.discover.desktop
+
+
+ false
+
systemsettings.desktop,org.kde.kinfocenter.desktop,org.kde.discover.desktop
Index: applets/kickoff/package/contents/ui/FullRepresentation.qml
===================================================================
--- applets/kickoff/package/contents/ui/FullRepresentation.qml
+++ applets/kickoff/package/contents/ui/FullRepresentation.qml
@@ -65,7 +65,7 @@
sorted: plasmoid.configuration.alphaSort
showSeparators: false
- favoritesModel: Kicker.FavoritesModel {
+ favoritesModel: Kicker.KAStatsFavoritesModel {
id: rootModelFavorites
favorites: plasmoid.configuration.favorites
@@ -73,6 +73,17 @@
plasmoid.configuration.favorites = favorites;
}
}
+
+ Component.onCompleted: {
+ favoritesModel.initForClient("org.kde.plasma.kickoff.favorites.instance-" + plasmoid.id)
+
+ if (!plasmoid.configuration.favoritesPortedToKAstats) {
+ favoritesModel.addFavoritesTo(plasmoid.configuration.favorites, ":global");
+ plasmoid.configuration.favoritesPortedToKAstats = true;
+ }
+
+ rootModel.refresh();
+ }
}
Kicker.RunnerModel {