diff --git a/kastatsfavoritesmodel.cpp b/kastatsfavoritesmodel.cpp index c8adef6c7..027d0b8f6 100644 --- a/kastatsfavoritesmodel.cpp +++ b/kastatsfavoritesmodel.cpp @@ -1,717 +1,711 @@ /*************************************************************************** * 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 "debug.h" -#include #include #include #include #include #include #include #include #include #include #include #include namespace KAStats = KActivities::Stats; using namespace KAStats; using namespace KAStats::Terms; #define AGENT_APPLICATIONS "org.kde.plasma.favorites.applications" #define AGENT_CONTACTS "org.kde.plasma.favorites.contacts" #define AGENT_DOCUMENTS "org.kde.plasma.favorites.documents" -#define DEBUG_PREFIX "\033[0;31m[KASTATSFAVS]\033[0;34m " -#define DEBUG qDebug() << DEBUG_PREFIX << (void*)q << ((void*)this) << m_clientId << "\033[0;0m " - QString agentForUrl(const QString &url) { return url.startsWith(QLatin1String("ktp:")) ? AGENT_CONTACTS : url.startsWith(QLatin1String("preferred:")) ? AGENT_APPLICATIONS : url.startsWith(QLatin1String("applications:")) ? AGENT_APPLICATIONS : (url.startsWith(QLatin1Char('/')) && !url.endsWith(QLatin1String(".desktop"))) ? AGENT_DOCUMENTS : (url.startsWith(QLatin1String("file:/")) && !url.endsWith(QLatin1String(".desktop"))) ? AGENT_DOCUMENTS // use applications as the default : AGENT_APPLICATIONS; } class KAStatsFavoritesModel::Private: public QAbstractListModel { public: class NormalizedId { public: NormalizedId() { } NormalizedId(const Private *parent, const QString &id) { if (id.isEmpty()) return; AbstractEntry *entry = nullptr; QScopedPointer deleter; if (parent->m_itemEntries.contains(id)) { entry = parent->m_itemEntries[id]; } else { // This entry is not cached - it is temporary, // so let's clean up when we exit this function entry = parent->entryForResource(id); deleter.reset(entry); } if (!entry || !entry->isValid()) { qWarning() << "Entry is not valid" << id << entry; m_id = id; return; } const auto url = entry->url(); - qDebug() << "Original id is: " << id << ", and the url is" << url; + qCDebug(KICKER_DEBUG) << "Original id is: " << id << ", and the url is" << url; // Preferred applications need special handling if (entry->id().startsWith("preferred:")) { m_id = entry->id(); return; } // If this is an application, use the applications:-format url auto appEntry = dynamic_cast(entry); if (appEntry && !appEntry->menuId().isEmpty()) { m_id = "applications:" + appEntry->menuId(); return; } // We want to resolve symbolic links not to have two paths // refer to the same .desktop file if (url.isLocalFile()) { QFileInfo file(url.toLocalFile()); if (file.exists()) { m_id = QUrl::fromLocalFile(file.canonicalFilePath()).toString(); return; } } // If this is a file, we should have already covered it if (url.scheme() == "file") { return; } m_id = url.toString(); } const QString& value() const { return m_id; } bool operator==(const NormalizedId &other) const { return m_id == other.m_id; } private: QString m_id; }; NormalizedId normalizedId(const QString &id) const { return NormalizedId(this, id); } AbstractEntry *entryForResource(const QString &resource) const { const auto agent = agentForUrl(resource); if (agent == AGENT_CONTACTS) { return new ContactEntry(q, resource); } else if (agent == AGENT_DOCUMENTS) { if (resource.startsWith("/")) { return new FileEntry(q, QUrl::fromLocalFile(resource)); } else { return new FileEntry(q, QUrl(resource)); } } else if (agent == AGENT_APPLICATIONS) { if (resource.startsWith("applications:")) { return new AppEntry(q, resource.mid(13)); } else { return new AppEntry(q, resource); } } else { return nullptr; } } Private(KAStatsFavoritesModel *parent, QString clientId) : q(parent) , m_query( LinkedResources | Agent { AGENT_APPLICATIONS, AGENT_CONTACTS, AGENT_DOCUMENTS } | Type::any() | Activity::current() | Activity::global() | Limit::all() ) , m_watcher(m_query) , m_clientId(clientId) { // Connecting the watcher connect(&m_watcher, &ResultWatcher::resultLinked, [this] (const QString &resource) { addResult(resource, -1); }); connect(&m_watcher, &ResultWatcher::resultUnlinked, [this] (const QString &resource) { removeResult(resource); }); // Loading the items order const auto cfg = KSharedConfig::openConfig("kactivitymanagerd-statsrc"); // We want first to check whether we have an ordering for this activity. // If not, we will try to get a global one for this applet const QString thisGroupName = "Favorites-" + clientId + "-" + m_activities.currentActivity(); const QString globalGroupName = "Favorites-" + clientId + "-global"; KConfigGroup thisCfgGroup(cfg, thisGroupName); KConfigGroup globalCfgGroup(cfg, globalGroupName); QStringList ordering = thisCfgGroup.readEntry("ordering", QStringList()) + globalCfgGroup.readEntry("ordering", QStringList()); - DEBUG << "Loading the ordering " << ordering; + qCDebug(KICKER_DEBUG) << "Loading the ordering " << ordering; // Loading the results without emitting any model signals - DEBUG << "Query is" << m_query; + qCDebug(KICKER_DEBUG) << "Query is" << m_query; ResultSet results(m_query); for (const auto& result: results) { - DEBUG << "Got " << result.resource() << " -->"; + qCDebug(KICKER_DEBUG) << "Got " << result.resource() << " -->"; addResult(result.resource(), -1, false); } // Normalizing all the ids std::transform(ordering.begin(), ordering.end(), ordering.begin(), [&] (const QString &item) { return normalizedId(item).value(); }); // Sorting the items in the cache std::sort(m_items.begin(), m_items.end(), [&] (const NormalizedId &left, const NormalizedId &right) { auto leftIndex = ordering.indexOf(left.value()); auto rightIndex = ordering.indexOf(right.value()); return (leftIndex == -1 && rightIndex == -1) ? left.value() < right.value() : (leftIndex == -1) ? false : (rightIndex == -1) ? true : // otherwise leftIndex < rightIndex; }); // Debugging: QVector itemStrings(m_items.size()); std::transform(m_items.cbegin(), m_items.cend(), itemStrings.begin(), [] (const NormalizedId &item) { return item.value(); }); - DEBUG << "After ordering: " << itemStrings; + qCDebug(KICKER_DEBUG) << "After ordering: " << itemStrings; } void addResult(const QString &_resource, int index, bool notifyModel = true) { // We want even files to have a proper URL const auto resource = _resource.startsWith("/") ? QUrl::fromLocalFile(_resource).toString() : _resource; - DEBUG << "Adding result" << resource << "already present?" << m_itemEntries.contains(resource); + qCDebug(KICKER_DEBUG) << "Adding result" << resource << "already present?" << m_itemEntries.contains(resource); if (m_itemEntries.contains(resource)) return; auto entry = entryForResource(resource); if (!entry || !entry->isValid()) { - DEBUG << "Entry is not valid!"; + qCDebug(KICKER_DEBUG) << "Entry is not valid!"; return; } if (index == -1) { index = m_items.count(); } if (notifyModel) { beginInsertRows(QModelIndex(), index, index); } auto url = entry->url(); m_itemEntries[resource] = m_itemEntries[entry->id()] = m_itemEntries[url.toString()] = m_itemEntries[url.toLocalFile()] = entry; auto normalized = normalizedId(resource); m_items.insert(index, normalized); m_itemEntries[normalized.value()] = entry; if (notifyModel) { endInsertRows(); saveOrdering(); } } void removeResult(const QString &resource) { auto normalized = normalizedId(resource); // If we know this item will not really be removed, // but only that activities it is on have changed, // lets leave it if (m_ignoredItems.contains(normalized.value())) { m_ignoredItems.removeAll(normalized.value()); return; } - DEBUG << "Removing result" << resource; + qCDebug(KICKER_DEBUG) << "Removing result" << resource; auto index = m_items.indexOf(normalizedId(resource)); if (index == -1) return; beginRemoveRows(QModelIndex(), index, index); auto entry = m_itemEntries[resource]; m_items.removeAt(index); // Removing the entry from the cache QMutableHashIterator i(m_itemEntries); while (i.hasNext()) { if (i.value() == entry) { i.remove(); } i.next(); } delete entry; endRemoveRows(); } int rowCount(const QModelIndex &parent = QModelIndex()) const override { if (parent.isValid()) return 0; return m_items.count(); } QVariant data(const QModelIndex &item, int role = Qt::DisplayRole) const override { if (item.parent().isValid()) return QVariant(); const auto index = item.row(); const auto entry = m_itemEntries[m_items[index].value()]; return entry == nullptr ? QVariant() : role == Qt::DisplayRole ? entry->name() : role == Qt::DecorationRole ? entry->icon() : role == Kicker::DescriptionRole ? entry->description() : role == Kicker::FavoriteIdRole ? entry->id() : role == Kicker::UrlRole ? entry->url() : role == Kicker::HasActionListRole ? entry->hasActions() : role == Kicker::ActionListRole ? entry->actions() : QVariant(); } bool trigger(int row, const QString &actionId, const QVariant &argument) { if (row < 0 || row >= rowCount()) { return false; } const QString id = data(index(row, 0), Kicker::UrlRole).toString(); return m_itemEntries.contains(id) ? m_itemEntries[id]->run(actionId, argument) : false; } void move(int from, int to) { if (from < 0) return; if (from >= m_items.count()) return; if (to < 0) return; if (to >= m_items.count()) return; if (from == to) return; const int modelTo = to + (to > from ? 1 : 0); if (q->beginMoveRows(QModelIndex(), from, from, QModelIndex(), modelTo)) { m_items.move(from, to); q->endMoveRows(); - DEBUG << "Save ordering (from Private::move) -->"; + qCDebug(KICKER_DEBUG) << "Save ordering (from Private::move) -->"; saveOrdering(); } } void saveOrdering() { QStringList ids; for (const auto& item: m_items) { ids << item.value(); } - DEBUG << "Save ordering (from Private::saveOrdering) -->"; + qCDebug(KICKER_DEBUG) << "Save ordering (from Private::saveOrdering) -->"; saveOrdering(ids, m_clientId, m_activities.currentActivity()); } static void saveOrdering(const QStringList &ids, const QString &clientId, const QString ¤tActivity) { const auto cfg = KSharedConfig::openConfig("kactivitymanagerd-statsrc"); QStringList activities { currentActivity, "global" }; - qDebug() << "Saving ordering for" << currentActivity << "and global" << ids; + qCDebug(KICKER_DEBUG) << "Saving ordering for" << currentActivity << "and global" << ids; for (const auto& activity: activities) { const QString groupName = "Favorites-" + clientId + "-" + activity; KConfigGroup cfgGroup(cfg, groupName); cfgGroup.writeEntry("ordering", ids); } cfg->sync(); } KAStatsFavoritesModel *const q; KActivities::Consumer m_activities; Query m_query; ResultWatcher m_watcher; QString m_clientId; QVector m_items; QHash m_itemEntries; QStringList m_ignoredItems; }; -#undef DEBUG -#define DEBUG qDebug() << DEBUG_PREFIX << ((void*)this) << ((void*)d) << (d ? d->m_clientId : QString("no client ID yet")) << "\033[0;0m " - KAStatsFavoritesModel::KAStatsFavoritesModel(QObject *parent) : PlaceholderModel(parent) , d(nullptr) // we have no client id yet , m_enabled(true) , m_maxFavorites(-1) , m_activities(new KActivities::Consumer(this)) { connect(m_activities, &KActivities::Consumer::currentActivityChanged, this, [&] (const QString ¤tActivity) { - DEBUG << "Activity just got changed to" << currentActivity; + qCDebug(KICKER_DEBUG) << "Activity just got changed to" << currentActivity; Q_UNUSED(currentActivity); auto clientId = d->m_clientId; initForClient(clientId); }); } KAStatsFavoritesModel::~KAStatsFavoritesModel() { delete d; } void KAStatsFavoritesModel::initForClient(const QString &clientId) { - DEBUG << "initForClient" << clientId; + qCDebug(KICKER_DEBUG) << "initForClient" << clientId; setSourceModel(nullptr); delete d; d = new Private( this, clientId ); setSourceModel(d); } QString KAStatsFavoritesModel::description() const { return i18n("Favorites"); } bool KAStatsFavoritesModel::trigger(int row, const QString &actionId, const QVariant &argument) { return d->trigger(row, actionId, argument); } bool KAStatsFavoritesModel::enabled() const { return m_enabled; } int KAStatsFavoritesModel::maxFavorites() const { return m_maxFavorites; } void KAStatsFavoritesModel::setMaxFavorites(int max) { Q_UNUSED(max); } void KAStatsFavoritesModel::setEnabled(bool enable) { if (m_enabled != enable) { m_enabled = enable; emit enabledChanged(); } } QStringList KAStatsFavoritesModel::favorites() const { qWarning() << "KAStatsFavoritesModel::favorites returns nothing, it is here just to keep the API backwards-compatible"; return QStringList(); } void KAStatsFavoritesModel::setFavorites(const QStringList& favorites) { Q_UNUSED(favorites); qWarning() << "KAStatsFavoritesModel::setFavorites is ignored"; } bool KAStatsFavoritesModel::isFavorite(const QString &id) const { return d && d->m_itemEntries.contains(id); } void KAStatsFavoritesModel::portOldFavorites(const QStringList &ids) { - DEBUG << "portOldFavorites" << ids; + qCDebug(KICKER_DEBUG) << "portOldFavorites" << ids; const auto activityId = ":global"; std::for_each(ids.begin(), ids.end(), [&] (const QString &id) { addFavoriteTo(id, activityId); }); // Resetting the model auto clientId = d->m_clientId; setSourceModel(nullptr); delete d; d = nullptr; - DEBUG << "Save ordering (from portOldFavorites) -->"; + qCDebug(KICKER_DEBUG) << "Save ordering (from portOldFavorites) -->"; Private::saveOrdering(ids, clientId, m_activities->currentActivity()); QTimer::singleShot(500, std::bind(&KAStatsFavoritesModel::initForClient, this, clientId)); } void KAStatsFavoritesModel::addFavorite(const QString &id, int index) { - DEBUG << "addFavorite" << id << index << " -->"; + qCDebug(KICKER_DEBUG) << "addFavorite" << id << index << " -->"; addFavoriteTo(id, QStringLiteral(":global")); } void KAStatsFavoritesModel::removeFavorite(const QString &id) { - DEBUG << "removeFavorite" << id << " -->"; + qCDebug(KICKER_DEBUG) << "removeFavorite" << id << " -->"; removeFavoriteFrom(id, QStringLiteral(":any")); } void KAStatsFavoritesModel::addFavoriteTo(const QString &id, const QString &activityId, int index) { - DEBUG << "addFavoriteTo" << id << activityId << index << " -->"; + qCDebug(KICKER_DEBUG) << "addFavoriteTo" << id << activityId << index << " -->"; addFavoriteTo(id, Activity(activityId), index); } void KAStatsFavoritesModel::removeFavoriteFrom(const QString &id, const QString &activityId) { - DEBUG << "removeFavoriteFrom" << id << activityId << " -->"; + qCDebug(KICKER_DEBUG) << "removeFavoriteFrom" << id << activityId << " -->"; removeFavoriteFrom(id, Activity(activityId)); } void KAStatsFavoritesModel::addFavoriteTo(const QString &id, const Activity &activity, int index) { if (!d || id.isEmpty()) return; Q_ASSERT(!activity.values.isEmpty()); setDropPlaceholderIndex(-1); QStringList matchers { d->m_activities.currentActivity(), ":global", ":current" }; if (std::find_first_of(activity.values.cbegin(), activity.values.cend(), matchers.cbegin(), matchers.cend()) != activity.values.cend()) { d->addResult(id, index); } const auto url = d->normalizedId(id).value(); - DEBUG << "addFavoriteTo" << id << activity << index << url << " (actual)"; + qCDebug(KICKER_DEBUG) << "addFavoriteTo" << id << activity << index << url << " (actual)"; if (url.isEmpty()) return; d->m_watcher.linkToActivity(QUrl(url), activity, Agent(agentForUrl(url))); } void KAStatsFavoritesModel::removeFavoriteFrom(const QString &id, const Activity &activity) { const auto url = d->normalizedId(id).value(); Q_ASSERT(!activity.values.isEmpty()); - DEBUG << "addFavoriteTo" << id << activity << url << " (actual)"; + qCDebug(KICKER_DEBUG) << "addFavoriteTo" << id << activity << url << " (actual)"; if (url.isEmpty()) return; d->m_watcher.unlinkFromActivity(QUrl(url), activity, Agent(agentForUrl(url))); } void KAStatsFavoritesModel::setFavoriteOn(const QString &id, const QString &activityId) { const auto url = d->normalizedId(id).value(); - DEBUG << "setFavoriteOn" << id << activityId << url << " (actual)"; + qCDebug(KICKER_DEBUG) << "setFavoriteOn" << id << activityId << url << " (actual)"; - DEBUG << "%%%%%%%%%%% Activity is" << activityId; + qCDebug(KICKER_DEBUG) << "%%%%%%%%%%% Activity is" << activityId; if (activityId.isEmpty() || activityId == ":any" || activityId == ":global" || activityId == m_activities->currentActivity()) { d->m_ignoredItems << url; } d->m_watcher.unlinkFromActivity(QUrl(url), Activity::any(), Agent(agentForUrl(url))); d->m_watcher.linkToActivity(QUrl(url), activityId, Agent(agentForUrl(url))); } void KAStatsFavoritesModel::moveRow(int from, int to) { d->move(from, to); } AbstractModel *KAStatsFavoritesModel::favoritesModel() { return this; } void KAStatsFavoritesModel::refresh() { } QObject *KAStatsFavoritesModel::activities() const { return m_activities; } QString KAStatsFavoritesModel::activityNameForId(const QString &activityId) const { // It is safe to use a short-lived object here, // we are always synced with KAMD in plasma KActivities::Info info(activityId); return info.name(); } QStringList KAStatsFavoritesModel::linkedActivitiesFor(const QString &id) const { if (!d) { - DEBUG << "Linked for" << id << "is empty, no Private instance"; + qCDebug(KICKER_DEBUG) << "Linked for" << id << "is empty, no Private instance"; return {}; } auto url = d->normalizedId(id).value(); if (url.startsWith("file:")) { url = QUrl(url).toLocalFile(); } if (url.isEmpty()) { - DEBUG << "The url for" << id << "is empty"; + qCDebug(KICKER_DEBUG) << "The url for" << id << "is empty"; return {}; } auto query = LinkedResources | Agent { AGENT_APPLICATIONS, AGENT_CONTACTS, AGENT_DOCUMENTS } | Type::any() | Activity::any() | Url(url) | Limit::all(); ResultSet results(query); for (const auto &result: results) { - DEBUG << "Returning" << result.linkedActivities() << "for" << id << url; + qCDebug(KICKER_DEBUG) << "Returning" << result.linkedActivities() << "for" << id << url; return result.linkedActivities(); } - DEBUG << "Returning empty list of activities for" << id << url; + qCDebug(KICKER_DEBUG) << "Returning empty list of activities for" << id << url; return {}; } diff --git a/placeholdermodel.cpp b/placeholdermodel.cpp index 53d98303b..e0b5f763a 100644 --- a/placeholdermodel.cpp +++ b/placeholdermodel.cpp @@ -1,407 +1,407 @@ /*************************************************************************** * Copyright (C) 2015 by Eike Hein * * Copyright (C) 2017 by Ivan Cukic * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "placeholdermodel.h" #include "actionlist.h" +#include "debug.h" -#include #include PlaceholderModel::PlaceholderModel(QObject *parent) : AbstractModel(parent) , m_dropPlaceholderIndex(-1) , m_isTriggerInhibited(false) { connect(&m_triggerInhibitor, &QTimer::timeout, this, [&] { - qDebug() << "%%% Inhibit stopped"; + qCDebug(KICKER_DEBUG) << "%%% Inhibit stopped"; m_isTriggerInhibited = false; }); m_triggerInhibitor.setInterval(500); m_triggerInhibitor.setSingleShot(true); } void PlaceholderModel::inhibitTriggering() { - qDebug() << "%%% Inhibit started"; + qCDebug(KICKER_DEBUG) << "%%% Inhibit started"; m_isTriggerInhibited = true; m_triggerInhibitor.start(); } PlaceholderModel::~PlaceholderModel() { } QString PlaceholderModel::description() const { if (auto abstractModel = qobject_cast(m_sourceModel)) { return abstractModel->description(); } else { return QString(); } } QAbstractItemModel *PlaceholderModel::sourceModel() const { return m_sourceModel; } void PlaceholderModel::setSourceModel(QAbstractItemModel *sourceModel) { disconnectSignals(); beginResetModel(); m_sourceModel = sourceModel; connectSignals(); endResetModel(); emit countChanged(); emit sourceModelChanged(); emit descriptionChanged(); } bool PlaceholderModel::canFetchMore(const QModelIndex &parent) const { return m_sourceModel && m_sourceModel->canFetchMore(indexToSourceIndex(parent)); } void PlaceholderModel::fetchMore(const QModelIndex &parent) { if (m_sourceModel) { m_sourceModel->fetchMore(indexToSourceIndex(parent)); } } QModelIndex PlaceholderModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent) return m_sourceModel ? createIndex(row, column) : QModelIndex(); } QModelIndex PlaceholderModel::parent(const QModelIndex &index) const { Q_UNUSED(index) return QModelIndex(); } QVariant PlaceholderModel::data(const QModelIndex &index, int role) const { const auto row = index.row(); if (m_dropPlaceholderIndex == row) { switch (role) { case Kicker::IsDropPlaceholderRole: return true; // TODO: Maybe it would be nice to show something here? // case Qt::DisplayRole: // return "placeholder"; // // case Qt::DecorationRole: // return "select"; default: return QVariant(); } } return m_sourceModel ? m_sourceModel->data(indexToSourceIndex(index), role) : QVariant(); } int PlaceholderModel::rowCount(const QModelIndex &parent) const { if (!m_sourceModel || parent.isValid()) { return 0; } return m_sourceModel->rowCount() + (m_dropPlaceholderIndex != -1 ? 1 : 0); } QModelIndex PlaceholderModel::indexToSourceIndex(const QModelIndex& index) const { if (!m_sourceModel || !index.isValid()) { return QModelIndex(); } const auto row = index.row(); const auto column = index.column(); return index.parent().isValid() ? // We do not support tree models QModelIndex() : // If we are on top-level, lets add a placeholder m_sourceModel->index( row - (m_dropPlaceholderIndex != -1 && row > m_dropPlaceholderIndex ? 1 : 0), column, QModelIndex() ); } int PlaceholderModel::sourceRowToRow(int sourceRow) const { return sourceRow + (m_dropPlaceholderIndex != -1 && sourceRow >= m_dropPlaceholderIndex ? 1 : 0); } int PlaceholderModel::rowToSourceRow(int row) const { return row == m_dropPlaceholderIndex ? -1 : row - (m_dropPlaceholderIndex != -1 && row > m_dropPlaceholderIndex ? 1 : 0); } QModelIndex PlaceholderModel::sourceIndexToIndex(const QModelIndex& sourceIndex) const { if (!m_sourceModel || !sourceIndex.isValid()) { return QModelIndex(); } const auto sourceRow = sourceIndex.row(); const auto sourceColumn = sourceIndex.column(); return sourceIndex.parent().isValid() ? // We do not support tree-models QModelIndex() : // If we are on top-level, lets add a placeholder index( sourceRowToRow(sourceRow), sourceColumn, QModelIndex() ); } bool PlaceholderModel::trigger(int row, const QString &actionId, const QVariant &argument) { if (m_isTriggerInhibited) return false; if (auto abstractModel = qobject_cast(m_sourceModel)) { return abstractModel->trigger(rowToSourceRow(row), actionId, argument); } else { return false; } } QString PlaceholderModel::labelForRow(int row) { if (auto abstractModel = qobject_cast(m_sourceModel)) { return abstractModel->labelForRow(rowToSourceRow(row)); } else { return QString(); } } AbstractModel* PlaceholderModel::modelForRow(int row) { if (auto abstractModel = qobject_cast(m_sourceModel)) { return abstractModel->modelForRow(rowToSourceRow(row)); } else { return 0; } } AbstractModel* PlaceholderModel::favoritesModel() { if (auto abstractModel = qobject_cast(m_sourceModel)) { return abstractModel->favoritesModel(); } else { return AbstractModel::favoritesModel(); } } int PlaceholderModel::separatorCount() const { if (auto abstractModel = qobject_cast(m_sourceModel)) { return abstractModel->separatorCount(); } else { return 0; } } void PlaceholderModel::reset() { emit beginResetModel(); emit endResetModel(); emit countChanged(); emit separatorCountChanged(); } void PlaceholderModel::connectSignals() { if (!m_sourceModel) { return; } const auto sourceModelPtr = m_sourceModel.data(); connect(sourceModelPtr, SIGNAL(destroyed()), this, SLOT(reset())); connect(sourceModelPtr, &QAbstractItemModel::dataChanged, this, [this] (const QModelIndex &from, const QModelIndex &to, const QVector &roles) { emit dataChanged(sourceIndexToIndex(from), sourceIndexToIndex(to), roles); }); connect(sourceModelPtr, &QAbstractItemModel::rowsAboutToBeInserted, this, [this] (const QModelIndex &parent, int from, int to) { if (parent.isValid()) { qWarning() << "We do not support tree models"; } else { beginInsertRows(QModelIndex(), sourceRowToRow(from), sourceRowToRow(to)); } }); connect(sourceModelPtr, &QAbstractItemModel::rowsInserted, this, [this] { endInsertRows(); emit countChanged(); }); connect(sourceModelPtr, &QAbstractItemModel::rowsAboutToBeMoved, this, [this] (const QModelIndex &source, int from, int to, const QModelIndex &dest, int destRow) { if (source.isValid() || dest.isValid()) { qWarning() << "We do not support tree models"; } else { beginMoveRows(QModelIndex(), sourceRowToRow(from), sourceRowToRow(to), QModelIndex(), sourceRowToRow(destRow)); } }); connect(sourceModelPtr, &QAbstractItemModel::rowsMoved, this, [this] { endMoveRows(); }); connect(sourceModelPtr, &QAbstractItemModel::rowsAboutToBeRemoved, this, [this] (const QModelIndex &parent, int from, int to) { if (parent.isValid()) { qWarning() << "We do not support tree models"; } else { beginRemoveRows(QModelIndex(), sourceRowToRow(from), sourceRowToRow(to)); } }); connect(sourceModelPtr, &QAbstractItemModel::rowsRemoved, this, [this] { endRemoveRows(); emit countChanged(); }); connect(sourceModelPtr, &QAbstractItemModel::modelAboutToBeReset, this, [this] { beginResetModel(); }); connect(sourceModelPtr, &QAbstractItemModel::modelReset, this, [this] { endResetModel(); emit countChanged(); }); // We do not have persistant indices // connect(sourceModelPtr, &QAbstractItemModel::layoutAboutToBeChanged), // this, &PlaceholderModel::layoutAboutToBeChanged); // connect(sourceModelPtr, &QAbstractItemModel::layoutChanged), // this, SIGNAL(layoutChanged(QList,QAbstractItemModel::LayoutChangeHint)), // Qt::UniqueConnection); } void PlaceholderModel::disconnectSignals() { if (!m_sourceModel) { return; } disconnect(m_sourceModel, 0, this, 0); } int PlaceholderModel::dropPlaceholderIndex() const { return m_dropPlaceholderIndex; } void PlaceholderModel::setDropPlaceholderIndex(int index) { if (index == m_dropPlaceholderIndex) return; inhibitTriggering(); if (index == -1 && m_dropPlaceholderIndex != -1) { // Removing the placeholder beginRemoveRows(QModelIndex(), m_dropPlaceholderIndex, m_dropPlaceholderIndex); m_dropPlaceholderIndex = index; endRemoveRows(); emit countChanged(); } else if (index != -1 && m_dropPlaceholderIndex == -1) { // Creating the placeholder beginInsertRows(QModelIndex(), index, index); m_dropPlaceholderIndex = index; endInsertRows(); emit countChanged(); } else if (m_dropPlaceholderIndex != index) { // Moving the placeholder int modelTo = index + (index > m_dropPlaceholderIndex ? 1 : 0); if (beginMoveRows( QModelIndex(), m_dropPlaceholderIndex, m_dropPlaceholderIndex, QModelIndex(), modelTo)) { m_dropPlaceholderIndex = index; endMoveRows(); } } emit dropPlaceholderIndexChanged(); }