/* SPDX-FileCopyrightText: 2014-2015 Eike Hein SPDX-License-Identifier: GPL-2.0-or-later */ #include "rootmodel.h" #include "actionlist.h" #include "kastatsfavoritesmodel.h" #include "recentusagemodel.h" #include "systemmodel.h" #include #include GroupEntry::GroupEntry(AppsModel *parentModel, const QString &name, const QString &iconName, AbstractModel *childModel) : AbstractGroupEntry(parentModel) , m_name(name) , m_iconName(iconName) , m_childModel(childModel) { QObject::connect(parentModel, &RootModel::cleared, childModel, &AbstractModel::deleteLater); QObject::connect(childModel, &AbstractModel::countChanged, [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } }); } QString GroupEntry::name() const { return m_name; } QString GroupEntry::icon() const { return m_iconName; } bool GroupEntry::hasChildren() const { return m_childModel && m_childModel->count() > 0; } AbstractModel *GroupEntry::childModel() const { return m_childModel; } RootModel::RootModel(QObject *parent) : AppsModel(QString(), parent) , m_favorites(new KAStatsFavoritesModel(this)) , m_systemModel(nullptr) , m_showAllApps(false) , m_showAllAppsCategorized(false) , m_showRecentApps(true) , m_showRecentDocs(true) , m_recentOrdering(RecentUsageModel::Recent) , m_showPowerSession(true) , m_showFavoritesPlaceholder(false) , m_recentAppsModel(nullptr) , m_recentDocsModel(nullptr) { } RootModel::~RootModel() { } QVariant RootModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= m_entryList.count()) { return QVariant(); } if (role == Kicker::HasActionListRole || role == Kicker::ActionListRole) { const AbstractEntry *entry = m_entryList.at(index.row()); if (entry->type() == AbstractEntry::GroupType) { const GroupEntry *group = static_cast(entry); AbstractModel *model = group->childModel(); if (model == m_recentAppsModel || model == m_recentDocsModel) { if (role == Kicker::HasActionListRole) { return true; } else if (role == Kicker::ActionListRole) { QVariantList actionList; actionList << model->actions(); actionList << Kicker::createSeparatorActionItem(); actionList << Kicker::createActionItem(i18n("Hide %1", group->name()), QStringLiteral("view-hidden"), QStringLiteral("hideCategory")); return actionList; } } } } return AppsModel::data(index, role); } bool RootModel::trigger(int row, const QString &actionId, const QVariant &argument) { const AbstractEntry *entry = m_entryList.at(row); if (entry->type() == AbstractEntry::GroupType) { if (actionId == QLatin1String("hideCategory")) { AbstractModel *model = entry->childModel(); if (model == m_recentAppsModel) { setShowRecentApps(false); return true; } else if (model == m_recentDocsModel) { setShowRecentDocs(false); return true; } } else if (entry->childModel()->hasActions()) { return entry->childModel()->trigger(-1, actionId, QVariant()); } } return AppsModel::trigger(row, actionId, argument); } bool RootModel::showAllApps() const { return m_showAllApps; } void RootModel::setShowAllApps(bool show) { if (m_showAllApps != show) { m_showAllApps = show; refresh(); Q_EMIT showAllAppsChanged(); } } bool RootModel::showAllAppsCategorized() const { return m_showAllAppsCategorized; } void RootModel::setShowAllAppsCategorized(bool showCategorized) { if (m_showAllAppsCategorized != showCategorized) { m_showAllAppsCategorized = showCategorized; refresh(); Q_EMIT showAllAppsCategorizedChanged(); } } bool RootModel::showRecentApps() const { return m_showRecentApps; } void RootModel::setShowRecentApps(bool show) { if (show != m_showRecentApps) { m_showRecentApps = show; refresh(); Q_EMIT showRecentAppsChanged(); } } bool RootModel::showRecentDocs() const { return m_showRecentDocs; } void RootModel::setShowRecentDocs(bool show) { if (show != m_showRecentDocs) { m_showRecentDocs = show; refresh(); Q_EMIT showRecentDocsChanged(); } } int RootModel::recentOrdering() const { return m_recentOrdering; } void RootModel::setRecentOrdering(int ordering) { if (ordering != m_recentOrdering) { m_recentOrdering = ordering; refresh(); Q_EMIT recentOrderingChanged(); } } bool RootModel::showPowerSession() const { return m_showPowerSession; } void RootModel::setShowPowerSession(bool show) { if (show != m_showPowerSession) { m_showPowerSession = show; refresh(); Q_EMIT showPowerSessionChanged(); } } bool RootModel::showFavoritesPlaceholder() const { return m_showFavoritesPlaceholder; } void RootModel::setShowFavoritesPlaceholder(bool show) { if (show != m_showFavoritesPlaceholder) { m_showFavoritesPlaceholder = show; refresh(); Q_EMIT showFavoritesPlaceholderChanged(); } } AbstractModel *RootModel::favoritesModel() { return m_favorites; } AbstractModel *RootModel::systemFavoritesModel() { if (m_systemModel) { return m_systemModel->favoritesModel(); } return nullptr; } void RootModel::refresh() { if (!m_complete) { return; } beginResetModel(); AppsModel::refreshInternal(); AppsModel *allModel = nullptr; m_recentAppsModel = nullptr; m_recentDocsModel = nullptr; if (m_showAllApps) { QHash appsHash; std::function processEntry = [&](AbstractEntry *entry) { if (entry->type() == AbstractEntry::RunnableType) { AppEntry *appEntry = static_cast(entry); appsHash.insert(appEntry->service()->menuId(), appEntry); } else if (entry->type() == AbstractEntry::GroupType) { GroupEntry *groupEntry = static_cast(entry); AbstractModel *model = groupEntry->childModel(); if (!model) { return; } for (int i = 0; i < model->count(); ++i) { processEntry(static_cast(model->index(i, 0).internalPointer())); } } }; for (AbstractEntry *entry : std::as_const(m_entryList)) { processEntry(entry); } QList apps(appsHash.values()); sortEntries(apps); if (!m_showAllAppsCategorized && !m_paginate) { // The app list built above goes into a model. allModel = new AppsModel(apps, false, this); } else if (m_paginate) { // We turn the apps list into a subtree of pages. m_favorites = new KAStatsFavoritesModel(this); Q_EMIT favoritesModelChanged(); QList groups; int at = 0; QList page; page.reserve(m_pageSize); for (AbstractEntry *app : std::as_const(apps)) { page.append(app); if (at == (m_pageSize - 1)) { at = 0; AppsModel *model = new AppsModel(page, false, this); groups.append(new GroupEntry(this, QString(), QString(), model)); page.clear(); } else { ++at; } } if (!page.isEmpty()) { AppsModel *model = new AppsModel(page, false, this); groups.append(new GroupEntry(this, QString(), QString(), model)); } groups.prepend(new GroupEntry(this, QString(), QString(), m_favorites)); allModel = new AppsModel(groups, true, this); } else { // We turn the apps list into a subtree of apps by starting letter. QList groups; QHash> m_categoryHash; for (const AbstractEntry *groupEntry : std::as_const(m_entryList)) { AbstractModel *model = groupEntry->childModel(); if (!model) continue; for (int i = 0; i < model->count(); ++i) { AbstractEntry *appEntry = static_cast(model->index(i, 0).internalPointer()); // App entry's group stores a transliterated first character of the name. Prefer to use that. QString name = appEntry->group(); if (name.isEmpty()) { name = appEntry->name(); } if (name.isEmpty()) { continue; } const QChar &first = name.at(0).toUpper(); m_categoryHash[first.isDigit() ? QStringLiteral("0-9") : first].append(appEntry); } } QHashIterator> i(m_categoryHash); while (i.hasNext()) { i.next(); AppsModel *model = new AppsModel(i.value(), false, this); model->setDescription(i.key()); groups.append(new GroupEntry(this, i.key(), QString(), model)); } allModel = new AppsModel(groups, true, this); } allModel->setDescription(QStringLiteral("KICKER_ALL_MODEL")); // Intentionally no i18n. } int separatorPosition = 0; if (allModel) { m_entryList.prepend(new GroupEntry(this, i18n("All Applications"), QStringLiteral("applications-all"), allModel)); ++separatorPosition; } if (m_showFavoritesPlaceholder) { // This entry is a placeholder and shouldn't ever be visible QList placeholderList; AppsModel *placeholderModel = new AppsModel(placeholderList, false, this); // Favorites group containing a placeholder entry, so it would be considered as a group, not an entry QList placeholderEntry; placeholderEntry.append(new GroupEntry(this, // i18n("This shouldn't be visible! Use KICKER_FAVORITES_MODEL"), QStringLiteral("dialog-warning"), placeholderModel)); AppsModel *favoritesPlaceholderModel = new AppsModel(placeholderEntry, false, this); favoritesPlaceholderModel->setDescription(QStringLiteral("KICKER_FAVORITES_MODEL")); // Intentionally no i18n. m_entryList.prepend(new GroupEntry(this, i18n("Favorites"), QStringLiteral("bookmarks"), favoritesPlaceholderModel)); ++separatorPosition; } if (m_showRecentDocs) { m_recentDocsModel = new RecentUsageModel(this, RecentUsageModel::OnlyDocs, m_recentOrdering); m_entryList.prepend(new GroupEntry(this, m_recentOrdering == RecentUsageModel::Recent ? i18n("Recent Files") : i18n("Often Used Files"), m_recentOrdering == RecentUsageModel::Recent ? QStringLiteral("view-history") : QStringLiteral("office-chart-pie"), m_recentDocsModel)); ++separatorPosition; } if (m_showRecentApps) { m_recentAppsModel = new RecentUsageModel(this, RecentUsageModel::OnlyApps, m_recentOrdering); m_entryList.prepend(new GroupEntry(this, m_recentOrdering == RecentUsageModel::Recent ? i18n("Recent Applications") : i18n("Often Used Applications"), m_recentOrdering == RecentUsageModel::Recent ? QStringLiteral("view-history") : QStringLiteral("office-chart-pie"), m_recentAppsModel)); ++separatorPosition; } if (m_showSeparators && separatorPosition > 0) { m_entryList.insert(separatorPosition, new SeparatorEntry(this)); ++m_separatorCount; } m_systemModel = new SystemModel(this); QObject::connect(m_systemModel, &SystemModel::sessionManagementStateChanged, this, &RootModel::refresh); if (m_showPowerSession) { m_entryList << new GroupEntry(this, i18n("Power / Session"), QStringLiteral("system-log-out"), m_systemModel); } endResetModel(); m_favorites->refresh(); Q_EMIT systemFavoritesModelChanged(); Q_EMIT countChanged(); Q_EMIT separatorCountChanged(); Q_EMIT refreshed(); }