Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -31,7 +31,7 @@ Concurrent ) -set(KF5_MIN_VERSION "5.24.0") +set(KF5_MIN_VERSION "5.32.0") find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Auth Plasma 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,8 @@ plugin/systemsettings.cpp plugin/wheelinterceptor.cpp plugin/windowsystem.cpp + plugin/funnelmodel.cpp + plugin/modeltest.cpp ) qt5_add_dbus_interface(kickerplugin_SRCS ${KRUNNERAPP_INTERFACE} krunner_interface) @@ -56,6 +60,7 @@ Qt5::Qml Qt5::Quick Qt5::X11Extras + Qt5Test KF5::Activities KF5::ActivitiesStats KF5::ConfigCore 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/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/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; @@ -108,7 +108,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.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,126 @@ +/*************************************************************************** + * Copyright (C) 2014-2015 by Eike Hein * + * Copyright (C) 2016-2015 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; + + int rowCount(const QModelIndex &parent = QModelIndex()) 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 QStringList linkedActivitiesFor(const QString &id) const; + + Q_INVOKABLE void moveRow(int from, int to); + + QObject *activities() const; + Q_INVOKABLE QString activityNameForId(const QString &activityId) const; + + AbstractModel* favoritesModel(); + + protected: + QVariant internalData(const QModelIndex &index, int role = Qt::DisplayRole) const; + + public Q_SLOTS: + virtual void refresh(); + + Q_SIGNALS: + void enabledChanged() const; + void favoritesChanged() const; + void maxFavoritesChanged() const; + + private: + AbstractEntry *favoriteFromId(const QString &id) const; + + QUrl urlForId(const QString &id) const; + QString agentForScheme(const QString &scheme) 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; + mutable QHash m_entries; + void removeOldCachedEntries() const; + int m_maxFavorites; + + mutable int m_whereTheItemIsBeingDropped; + QString m_whichIdIsBeingDropped; + + KActivities::Stats::ResultModel *m_sourceModel; + KActivities::Consumer *m_activities; + mutable QList m_invalidUrls; + + KConfig m_config; +}; + +#endif Index: applets/kicker/plugin/kastatsfavoritesmodel.cpp =================================================================== --- /dev/null +++ applets/kicker/plugin/kastatsfavoritesmodel.cpp @@ -0,0 +1,436 @@ +/*************************************************************************** + * Copyright (C) 2014-2015 by Eike Hein * + * Copyright (C) 2015 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 "modeltest.h" + +namespace KAStats = KActivities::Stats; + +using namespace KAStats; +using namespace KAStats::Terms; + +QListView *mainList; +QListView *sourceList; + +KAStatsFavoritesModel::KAStatsFavoritesModel(QObject *parent) : PlaceholderModel(parent) +, m_enabled(true) +, m_maxFavorites(-1) +, m_whereTheItemIsBeingDropped(-1) +, m_sourceModel(nullptr) +, m_activities(new KActivities::Consumer(this)) +, m_config("TESTTEST") +{ + // new ModelTest(this); + + mainList = new QListView(); + mainList->setModel(this); + mainList->setWindowTitle("Main"); + mainList->show(); + + sourceList = new QListView(); + sourceList->setWindowTitle("Source"); + sourceList->show(); + + auto query = LinkedResources + | Agent { + "org.kde.plasma.favorites.applications", + "org.kde.plasma.favorites.contacts" + } + | Type::any() + | Activity::current() + | Activity::global() + | Limit(15); + + m_sourceModel = new ResultModel(query, "org.kde.plasma.favorites", this); + + sourceList->setModel(m_sourceModel); + + connect(m_sourceModel, &ResultModel::rowsInserted, + this, &KAStatsFavoritesModel::rowsInserted); + + QModelIndex index; + + if (m_sourceModel->canFetchMore(index)) { + m_sourceModel->fetchMore(index); + } + + setSourceModel(m_sourceModel); +} + +KAStatsFavoritesModel::~KAStatsFavoritesModel() +{ +} + +QString KAStatsFavoritesModel::description() const +{ + return i18n("Favorites"); +} + +QVariant KAStatsFavoritesModel::internalData(const QModelIndex& index, int role) const +{ + if (!index.isValid() || index.row() >= rowCount()) { + return QVariant(); + } + + const QString id = + sourceModel()->data(index, ResultModel::ResourceRole).toString(); + + // const casts are bad, but we can not achieve this + // with the standard 'mutable' members for lazy evaluation, + // at least, not with the current design of the library + const auto _this = const_cast(this); + + if (m_whereTheItemIsBeingDropped != -1) { + // If we are in the process of dropping an item, we need to wait + // for it to get here. When it does, request it to be moved + // to its desired location. We can not do it in a better way + // because the model might be reset in the mean time + qDebug() << "Waiting for " << m_whichIdIsBeingDropped + << "and the id is " << id << "<-------------"; + if (m_whichIdIsBeingDropped == id) { + qDebug() << "Requesting the item to be moved... async... <------"; + + // This needs to happen only once, we need to make the captured + // variable independent of the member variable and we are not + // in C++14 + const auto where = m_whereTheItemIsBeingDropped; + m_whereTheItemIsBeingDropped = -1; + _this->m_sourceModel->setResultPosition(id, where); + + } + } + + const auto *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 url = urlForId(id); + + if (!m_invalidUrls.contains(url)) { + m_sourceModel->unlinkFromActivity( + url, Activity::any(), + Agent(agentForScheme(url.scheme())) + ); + m_invalidUrls << url; + } + + // return QVariant("NULL for '" + url + "'! - " + agentForScheme(scheme)); + + return role == Qt::DecorationRole ? "unknown" + : QVariant(); + } + + // qDebug() << "Entry: " << entry->name() << entry->icon(); + + return 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(); +} + +int KAStatsFavoritesModel::rowCount(const QModelIndex& parent) const +{ + return parent.isValid() + ? 0 + : PlaceholderModel::rowCount(parent); +} + +bool KAStatsFavoritesModel::trigger(int row, const QString &actionId, const QVariant &argument) +{ + if (row < 0 || row >= rowCount()) { + return false; + } + + const QString id = + PlaceholderModel::data(index(row, 0), ResultModel::ResourceRole).toString(); + + return m_entries.contains(id) + ? m_entries[id]->run(actionId, argument) + : false; +} + +bool KAStatsFavoritesModel::enabled() const +{ + return m_enabled; +} + +int KAStatsFavoritesModel::maxFavorites() const +{ + return m_maxFavorites; +} + +void KAStatsFavoritesModel::setMaxFavorites(int max) +{ +} + +void KAStatsFavoritesModel::setEnabled(bool enable) +{ + if (m_enabled != enable) { + m_enabled = enable; + + emit enabledChanged(); + } +} + +QStringList KAStatsFavoritesModel::favorites() const +{ + return QStringList(); +} + +void KAStatsFavoritesModel::setFavorites(const QStringList& favorites) +{ +} + +void KAStatsFavoritesModel::removeOldCachedEntries() const +{ + QList knownUrls; + for (int row = 0; row < rowCount(); ++row) { + // qDebug() << "URL we got is" << sourceModel()->data(index(row, 0), ResultModel::ResourceRole); + knownUrls << + urlForId(sourceModel()->data(index(row, 0), ResultModel::ResourceRole).toString()); + } + + // qDebug() << "Known urls are: " << knownUrls; + + QMutableHashIterator i(m_entries); + while (i.hasNext()) { + i.next(); + + const auto url = urlForId(i.key()); + + if (!knownUrls.contains(url)) { + delete i.value(); + i.remove(); + } + } +} + +bool KAStatsFavoritesModel::isFavorite(const QString &id) const +{ + removeOldCachedEntries(); + return m_entries.contains(id); +} + +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 (index == -1) { + index = m_sourceModel->rowCount(); + setDropPlaceholderIndex(index); + } + + setDropPlaceholderIndex(-1); + + if (id.isEmpty()) return; + + const auto url = urlForId(id); + + qDebug() << "Adding favorite to " << id << activity << url; + + // This is a file, we want to check that it exists + if (url.isLocalFile() && !QFileInfo::exists(url.toLocalFile())) return; + + m_sourceModel->linkToActivity( + url, activity, + Agent(agentForScheme(url.scheme())) + ); + + // Lets handle async repositioning of the item, see ::data + qDebug() << "We need to add " << url << "at" << index << "<------------"; + m_whereTheItemIsBeingDropped = index; + + if (index != -1) { + m_whichIdIsBeingDropped = url.toLocalFile(); + } else { + m_whichIdIsBeingDropped.clear(); + } +} + +void KAStatsFavoritesModel::removeFavoriteFrom(const QString &id, const Activity &activity) +{ + const auto url = urlForId(id); + + qDebug() << "Removing favorite from " << id << activity << url; + + m_sourceModel->unlinkFromActivity( + url, activity, + Agent(agentForScheme(url.scheme())) + ); +} + +void KAStatsFavoritesModel::moveRow(int from, int to) +{ + const QString id = + PlaceholderModel::data(index(from, 0), ResultModel::ResourceRole).toString(); + + m_sourceModel->setResultPosition(urlForId(id).toString(), to); +} + +AbstractModel *KAStatsFavoritesModel::favoritesModel() +{ + return this; +} + +void KAStatsFavoritesModel::refresh() +{ +} + +AbstractEntry *KAStatsFavoritesModel::favoriteFromId(const QString &id) const +{ + auto _this = const_cast(this); + + if (!m_entries.contains(id)) { + const QUrl url(id); + const QString &scheme = url.scheme(); + + qDebug() << "favoriteFromId: " << id << " - " << url << " - " << scheme; + + AbstractEntry *entry = nullptr; + + if (scheme == "ktp") { + entry = new ContactEntry(_this, id); + } else { + entry = new AppEntry(_this, id); + } + + m_entries[id] = entry; + } + + return m_entries[id]; +} + +QUrl KAStatsFavoritesModel::urlForId(const QString &id) const +{ + const auto entry = favoriteFromId(id); + + const auto url = entry && entry->isValid() ? entry->url() + : QUrl(); + + // 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 QUrl::fromLocalFile(file.canonicalFilePath()); + } + } + + return url; +} + +QString KAStatsFavoritesModel::agentForScheme(const QString &scheme) const +{ + return scheme == + QStringLiteral("ktp") ? "org.kde.plasma.favorites.contacts" + : "org.kde.plasma.favorites.applications"; +} + +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 +{ + auto url = urlForId(id); + + if (!url.isValid()) { + return {}; + } + + auto urlString = + url.scheme() == "file" ? + url.toLocalFile() : url.toString(); + + qDebug() << "Fetching linked activities for: " << id << urlString; + + auto query = LinkedResources + | Agent { + "org.kde.plasma.favorites.applications", + "org.kde.plasma.favorites.contacts" + } + | Type::any() + | Activity::any() + | Url(urlString); + + ResultSet results(query); + + for (const auto &result: results) { + qDebug() << "Linked activities for " << id << "are" << result.linkedActivities(); + return result.linkedActivities(); + } + + qDebug() << "NO linked activities for " << id; + return {}; +} + Index: applets/kicker/plugin/kickerplugin.cpp =================================================================== --- applets/kicker/plugin/kickerplugin.cpp +++ applets/kicker/plugin/kickerplugin.cpp @@ -23,7 +23,7 @@ #include "computermodel.h" #include "containmentinterface.h" #include "draghelper.h" -#include "favoritesmodel.h" +#include "simplefavoritesmodel.h" #include "dashboardwindow.h" #include "funnelmodel.h" #include "processrunner.h" @@ -48,7 +48,7 @@ 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, "DashboardWindow"); qmlRegisterType(uri, 0, 1, "FunnelModel"); qmlRegisterType(uri, 0, 1, "ProcessRunner"); Index: applets/kicker/plugin/modeltest.h =================================================================== --- /dev/null +++ applets/kicker/plugin/modeltest.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef MODELTEST_H +#define MODELTEST_H + +#include +#include +#include + +class ModelTest : public QObject +{ + Q_OBJECT + +public: + ModelTest( QAbstractItemModel *model, QObject *parent = 0 ); + +private Q_SLOTS: + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); + +protected Q_SLOTS: + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end ); + void rowsInserted( const QModelIndex & parent, int start, int end ); + void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end ); + void rowsRemoved( const QModelIndex & parent, int start, int end ); + +private: + void checkChildren( const QModelIndex &parent, int currentDepth = 0 ); + + QAbstractItemModel *model; + + struct Changing { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack insert; + QStack remove; + + bool fetchingMore; + + QList changing; +}; + +#endif Index: applets/kicker/plugin/modeltest.cpp =================================================================== --- /dev/null +++ applets/kicker/plugin/modeltest.cpp @@ -0,0 +1,564 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +#include "modeltest.h" + +#include + +Q_DECLARE_METATYPE ( QModelIndex ) + +/*! + Connect to all of the models signals. Whenever anything happens recheck everything. +*/ +ModelTest::ModelTest ( QAbstractItemModel *_model, QObject *parent ) : QObject ( parent ), model ( _model ), fetchingMore ( false ) +{ + if (!model) + qFatal("%s: model must not be null", Q_FUNC_INFO); + + connect ( model, SIGNAL ( columnsAboutToBeInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( columnsAboutToBeRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( columnsInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( columnsRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( dataChanged ( const QModelIndex &, const QModelIndex & ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( headerDataChanged ( Qt::Orientation, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( layoutAboutToBeChanged () ), this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( layoutChanged () ), this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( modelReset () ), this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( rowsAboutToBeInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( rowsAboutToBeRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( rowsInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( rowsRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + + // Special checks for inserting/removing + connect ( model, SIGNAL ( layoutAboutToBeChanged() ), + this, SLOT ( layoutAboutToBeChanged() ) ); + connect ( model, SIGNAL ( layoutChanged() ), + this, SLOT ( layoutChanged() ) ); + + connect ( model, SIGNAL ( rowsAboutToBeInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( rowsAboutToBeInserted ( const QModelIndex &, int, int ) ) ); + connect ( model, SIGNAL ( rowsAboutToBeRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( rowsAboutToBeRemoved ( const QModelIndex &, int, int ) ) ); + connect ( model, SIGNAL ( rowsInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( rowsInserted ( const QModelIndex &, int, int ) ) ); + connect ( model, SIGNAL ( rowsRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( rowsRemoved ( const QModelIndex &, int, int ) ) ); + + runAllTests(); +} + +void ModelTest::runAllTests() +{ + if ( fetchingMore ) + return; + nonDestructiveBasicTest(); + rowCount(); + columnCount(); + hasIndex(); + index(); + parent(); + data(); +} + +/*! + nonDestructiveBasicTest tries to call a number of the basic functions (not all) + to make sure the model doesn't outright segfault, testing the functions that makes sense. +*/ +void ModelTest::nonDestructiveBasicTest() +{ + QVERIFY( model->buddy ( QModelIndex() ) == QModelIndex() ); + model->canFetchMore ( QModelIndex() ); + QVERIFY( model->columnCount ( QModelIndex() ) >= 0 ); + QVERIFY( model->data ( QModelIndex() ) == QVariant() ); + fetchingMore = true; + model->fetchMore ( QModelIndex() ); + fetchingMore = false; + Qt::ItemFlags flags = model->flags ( QModelIndex() ); + QVERIFY( flags == Qt::ItemIsDropEnabled || flags == 0 ); + model->hasChildren ( QModelIndex() ); + model->hasIndex ( 0, 0 ); + model->headerData ( 0, Qt::Horizontal ); + model->index ( 0, 0 ); + model->itemData ( QModelIndex() ); + QVariant cache; + model->match ( QModelIndex(), -1, cache ); + model->mimeTypes(); + QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() ); + QVERIFY( model->rowCount() >= 0 ); + QVariant variant; + model->setData ( QModelIndex(), variant, -1 ); + model->setHeaderData ( -1, Qt::Horizontal, QVariant() ); + model->setHeaderData ( 999999, Qt::Horizontal, QVariant() ); + QMap roles; + model->sibling ( 0, 0, QModelIndex() ); + model->span ( QModelIndex() ); + model->supportedDropActions(); +} + +/*! + Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() + + Models that are dynamically populated are not as fully tested here. + */ +void ModelTest::rowCount() +{ +// qDebug() << "rc"; + // check top row + QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + int rows = model->rowCount ( topIndex ); + QVERIFY( rows >= 0 ); + if ( rows > 0 ) + QVERIFY( model->hasChildren ( topIndex ) ); + + QModelIndex secondLevelIndex = model->index ( 0, 0, topIndex ); + if ( secondLevelIndex.isValid() ) { // not the top level + // check a row count where parent is valid + rows = model->rowCount ( secondLevelIndex ); + QVERIFY( rows >= 0 ); + if ( rows > 0 ) + QVERIFY( model->hasChildren ( secondLevelIndex ) ); + } + + // The models rowCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() + */ +void ModelTest::columnCount() +{ + // check top row + QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + QVERIFY( model->columnCount ( topIndex ) >= 0 ); + + // check a column count where parent is valid + QModelIndex childIndex = model->index ( 0, 0, topIndex ); + if ( childIndex.isValid() ) + QVERIFY( model->columnCount ( childIndex ) >= 0 ); + + // columnCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::hasIndex() + */ +void ModelTest::hasIndex() +{ +// qDebug() << "hi"; + // Make sure that invalid values returns an invalid index + QVERIFY( !model->hasIndex ( -2, -2 ) ); + QVERIFY( !model->hasIndex ( -2, 0 ) ); + QVERIFY( !model->hasIndex ( 0, -2 ) ); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + // check out of bounds + QVERIFY( !model->hasIndex ( rows, columns ) ); + QVERIFY( !model->hasIndex ( rows + 1, columns + 1 ) ); + + if ( rows > 0 ) + QVERIFY( model->hasIndex ( 0, 0 ) ); + + // hasIndex() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::index() + */ +void ModelTest::index() +{ +// qDebug() << "i"; + // Make sure that invalid values returns an invalid index + QVERIFY( model->index ( -2, -2 ) == QModelIndex() ); + QVERIFY( model->index ( -2, 0 ) == QModelIndex() ); + QVERIFY( model->index ( 0, -2 ) == QModelIndex() ); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + if ( rows == 0 ) + return; + + // Catch off by one errors + QVERIFY( model->index ( rows, columns ) == QModelIndex() ); + QVERIFY( model->index ( 0, 0 ).isValid() ); + + // Make sure that the same index is *always* returned + QModelIndex a = model->index ( 0, 0 ); + QModelIndex b = model->index ( 0, 0 ); + QVERIFY( a == b ); + + // index() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::parent() + */ +void ModelTest::parent() +{ +// qDebug() << "p"; + // Make sure the model wont crash and will return an invalid QModelIndex + // when asked for the parent of an invalid index. + QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() ); + + if ( model->rowCount() == 0 ) + return; + + // Column 0 | Column 1 | + // QModelIndex() | | + // \- topIndex | topIndex1 | + // \- childIndex | childIndex1 | + + // Common error test #1, make sure that a top level index has a parent + // that is a invalid QModelIndex. + QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + QVERIFY( model->parent ( topIndex ) == QModelIndex() ); + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. + if ( model->rowCount ( topIndex ) > 0 ) { + QModelIndex childIndex = model->index ( 0, 0, topIndex ); + QVERIFY( model->parent ( childIndex ) == topIndex ); + } + + // Common error test #3, the second column should NOT have the same children + // as the first column in a row. + // Usually the second column shouldn't have children. + QModelIndex topIndex1 = model->index ( 0, 1, QModelIndex() ); + if ( model->rowCount ( topIndex1 ) > 0 ) { + QModelIndex childIndex = model->index ( 0, 0, topIndex ); + QModelIndex childIndex1 = model->index ( 0, 0, topIndex1 ); + QVERIFY( childIndex != childIndex1 ); + } + + // Full test, walk n levels deep through the model making sure that all + // parent's children correctly specify their parent. + checkChildren ( QModelIndex() ); +} + +/*! + Called from the parent() test. + + A model that returns an index of parent X should also return X when asking + for the parent of the index. + + This recursive function does pretty extensive testing on the whole model in an + effort to catch edge cases. + + This function assumes that rowCount(), columnCount() and index() already work. + If they have a bug it will point it out, but the above tests should have already + found the basic bugs because it is easier to figure out the problem in + those tests then this one. + */ +void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth ) +{ + // First just try walking back up the tree. + QModelIndex p = parent; + while ( p.isValid() ) + p = p.parent(); + + // For models that are dynamically populated + if ( model->canFetchMore ( parent ) ) { + fetchingMore = true; + model->fetchMore ( parent ); + fetchingMore = false; + } + + int rows = model->rowCount ( parent ); + int columns = model->columnCount ( parent ); + + if ( rows > 0 ) + QVERIFY( model->hasChildren ( parent ) ); + + // Some further testing against rows(), columns(), and hasChildren() + QVERIFY( rows >= 0 ); + QVERIFY( columns >= 0 ); + if ( rows > 0 ) + QVERIFY( model->hasChildren ( parent ) ); + + //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows + // << "columns:" << columns << "parent column:" << parent.column(); + + QVERIFY( !model->hasIndex ( rows + 1, 0, parent ) ); + for ( int r = 0; r < rows; ++r ) { + if ( model->canFetchMore ( parent ) ) { + fetchingMore = true; + model->fetchMore ( parent ); + fetchingMore = false; + } + QVERIFY( !model->hasIndex ( r, columns + 1, parent ) ); + for ( int c = 0; c < columns; ++c ) { + QVERIFY( model->hasIndex ( r, c, parent ) ); + QModelIndex index = model->index ( r, c, parent ); + // rowCount() and columnCount() said that it existed... + QVERIFY( index.isValid() ); + + // index() should always return the same index when called twice in a row + QModelIndex modifiedIndex = model->index ( r, c, parent ); + QVERIFY( index == modifiedIndex ); + + // Make sure we get the same index if we request it twice in a row + QModelIndex a = model->index ( r, c, parent ); + QModelIndex b = model->index ( r, c, parent ); + QVERIFY( a == b ); + + // Some basic checking on the index that is returned + QVERIFY( index.model() == model ); + QCOMPARE( index.row(), r ); + QCOMPARE( index.column(), c ); + // While you can technically return a QVariant usually this is a sign + // of a bug in data(). Disable if this really is ok in your model. +// QVERIFY( model->data ( index, Qt::DisplayRole ).isValid() ); + + // If the next test fails here is some somewhat useful debug you play with. + + if (model->parent(index) != parent) { + qDebug() << r << c << currentDepth << model->data(index).toString() + << model->data(parent).toString(); + qDebug() << index << parent << model->parent(index); +// And a view that you can even use to show the model. +// QTreeView view; +// view.setModel(model); +// view.show(); + } + + // Check that we can get back our real parent. + QCOMPARE( model->parent ( index ), parent ); + + // recursively go down the children + if ( model->hasChildren ( index ) && currentDepth < 10 ) { + //qDebug() << r << c << "has children" << model->rowCount(index); + checkChildren ( index, ++currentDepth ); + }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ + + // make sure that after testing the children that the index doesn't change. + QModelIndex newerIndex = model->index ( r, c, parent ); + QVERIFY( index == newerIndex ); + } + } +} + +/*! + Tests model's implementation of QAbstractItemModel::data() + */ +void ModelTest::data() +{ + // Invalid index should return an invalid qvariant + QVERIFY( !model->data ( QModelIndex() ).isValid() ); + + if ( model->rowCount() == 0 ) + return; + + // A valid index should have a valid QVariant data + QVERIFY( model->index ( 0, 0 ).isValid() ); + + // shouldn't be able to set data on an invalid index + QVERIFY( !model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) ); + + // General Purpose roles that should return a QString + QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole ); + if ( variant.isValid() ) { + QVERIFY( qVariantCanConvert ( variant ) ); + } + variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole ); + if ( variant.isValid() ) { + QVERIFY( qVariantCanConvert ( variant ) ); + } + variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole ); + if ( variant.isValid() ) { + QVERIFY( qVariantCanConvert ( variant ) ); + } + + // General Purpose roles that should return a QSize + variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole ); + if ( variant.isValid() ) { + QVERIFY( qVariantCanConvert ( variant ) ); + } + + // General Purpose roles that should return a QFont + QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole ); + if ( fontVariant.isValid() ) { + QVERIFY( qVariantCanConvert ( fontVariant ) ); + } + + // Check that the alignment is one we know about + QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole ); + if ( textAlignmentVariant.isValid() ) { + int alignment = textAlignmentVariant.toInt(); + QCOMPARE( alignment, ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) ); + } + + // General Purpose roles that should return a QColor + QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole ); + if ( colorVariant.isValid() ) { + QVERIFY( qVariantCanConvert ( colorVariant ) ); + } + + colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole ); + if ( colorVariant.isValid() ) { + QVERIFY( qVariantCanConvert ( colorVariant ) ); + } + + // Check that the "check state" is one we know about. + QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole ); + if ( checkStateVariant.isValid() ) { + int state = checkStateVariant.toInt(); + QVERIFY( state == Qt::Unchecked || + state == Qt::PartiallyChecked || + state == Qt::Checked ); + } +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsInserted() + */ +void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int end ) +{ +// Q_UNUSED(end); +// qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString() +// << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) ); +// qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) ); + Changing c; + c.parent = parent; + c.oldSize = model->rowCount ( parent ); + c.last = model->data ( model->index ( start - 1, 0, parent ) ); + c.next = model->data ( model->index ( start, 0, parent ) ); + insert.push ( c ); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeInserted() + */ +void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end ) +{ + Changing c = insert.pop(); + QVERIFY( c.parent == parent ); +// qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize +// << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent ); + +// for (int ii=start; ii <= end; ii++) +// { +// qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent )); +// } +// qDebug(); + + QVERIFY( c.oldSize + ( end - start + 1 ) == model->rowCount ( parent ) ); + QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) ); + + if (c.next != model->data(model->index(end + 1, 0, c.parent))) { + qDebug() << start << end; + for (int i=0; i < model->rowCount(); ++i) + qDebug() << model->index(i, 0).data().toString(); + qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); + } + + QVERIFY( c.next == model->data ( model->index ( end + 1, 0, c.parent ) ) ); +} + +void ModelTest::layoutAboutToBeChanged() +{ + for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i ) + changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) ); +} + +void ModelTest::layoutChanged() +{ + for ( int i = 0; i < changing.count(); ++i ) { + QPersistentModelIndex p = changing[i]; + QVERIFY( p == model->index ( p.row(), p.column(), p.parent() ) ); + } + changing.clear(); +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsRemoved() + */ +void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end ) +{ +qDebug() << "ratbr" << parent << start << end; + Changing c; + c.parent = parent; + c.oldSize = model->rowCount ( parent ); + c.last = model->data ( model->index ( start - 1, 0, parent ) ); + c.next = model->data ( model->index ( end + 1, 0, parent ) ); + remove.push ( c ); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeRemoved() + */ +void ModelTest::rowsRemoved ( const QModelIndex & parent, int start, int end ) +{ + qDebug() << "rr" << parent << start << end; + Changing c = remove.pop(); + QVERIFY( c.parent == parent ); + QVERIFY( c.oldSize - ( end - start + 1 ) == model->rowCount ( parent ) ); + QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) ); + QVERIFY( c.next == model->data ( model->index ( start, 0, c.parent ) ) ); +} + + Index: applets/kicker/plugin/placeholdermodel.h =================================================================== --- /dev/null +++ applets/kicker/plugin/placeholdermodel.h @@ -0,0 +1,89 @@ +/*************************************************************************** + * 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 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; + + virtual QVariant internalData(const QModelIndex &index, int role) 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,406 @@ +/*************************************************************************** + * Copyright (C) 2017 by Ivan Cukic * + * 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 "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::internalData(const QModelIndex &index, int role) const +{ + return m_sourceModel->data(indexToSourceIndex(index), role); +} + +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; + + case Qt::DisplayRole: + return QString(); + + case Qt::DecorationRole: + return QString(); + + default: + return QVariant(); + + } + } + + return m_sourceModel ? internalData(index, role) + : QVariant(); +} + +int PlaceholderModel::rowCount(const QModelIndex &parent) const +{ + if (!m_sourceModel) { + return 0; + } + + // qDebug() << "Row count is: " + // << (m_sourceModel->rowCount(indexToSourceIndex(parent)) + // + (m_dropPlaceholderIndex != -1 ? 1 : 0)) << "<--------------"; + + return m_sourceModel->rowCount(indexToSourceIndex(parent)) + + (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; + } + + qDebug() << m_sourceModel << "CONNECTING SINGALS!!! <---------"; + 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) { + qDebug() << "Data changing" << from << to << 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 { + qDebug() << "Rows inserting" << parent << from << to << "<--------------"; + beginInsertRows(QModelIndex(), + sourceRowToRow(from), + sourceRowToRow(to)); + + } + }); + + connect(sourceModelPtr, &QAbstractItemModel::rowsInserted, + this, [this] { + qDebug() << "Rows insert ended" << "<--------------"; + 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 { + qDebug() << "Rows moving" << source << from << to << dest << destRow << "<--------------"; + beginMoveRows(QModelIndex(), + sourceRowToRow(from), + sourceRowToRow(to), + QModelIndex(), + sourceRowToRow(destRow)); + } + }); + + connect(sourceModelPtr, &QAbstractItemModel::rowsMoved, + this, [this] { + qDebug() << "Rows move ended" << "<--------------"; + 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 { + qDebug() << "Rows removing" << parent << from << to << "<--------------"; + beginRemoveRows(QModelIndex(), + sourceRowToRow(from), + sourceRowToRow(to)); + } + }); + + connect(sourceModelPtr, &QAbstractItemModel::rowsRemoved, + this, [this] { + qDebug() << "Rows remove ended" << "<--------------"; + endRemoveRows(); + emit countChanged(); + }); + + + connect(sourceModelPtr, &QAbstractItemModel::modelAboutToBeReset, + this, [this] { + qDebug() << "Model is going for a reset" << "<--------------"; + beginResetModel(); + }); + + connect(sourceModelPtr, &QAbstractItemModel::modelReset, + this, [this] { + qDebug() << "Model was reset" << "<--------------"; + 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; + } + + qDebug() << "DISCON <---------"; + 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); + + bool ok = beginMoveRows(QModelIndex(), m_dropPlaceholderIndex, m_dropPlaceholderIndex, QModelIndex(), modelTo); + + if (ok) { + 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 @@ -73,8 +73,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(); } @@ -88,7 +88,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; @@ -112,7 +112,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" @@ -63,8 +63,8 @@ } RootModel::RootModel(QObject *parent) : AppsModel(QString(), parent) +, m_favorites(new KAStatsFavoritesModel(this)) , m_complete(false) -, m_favorites(new FavoritesModel(this)) , m_systemModel(nullptr) , m_autoPopulate(true) , m_showAllApps(false) @@ -284,7 +284,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; 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";