File indexing completed on 2024-12-22 05:15:22
0001 /* 0002 SPDX-FileCopyrightText: 2014-2015 Eike Hein <hein@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "rootmodel.h" 0008 #include "actionlist.h" 0009 #include "kastatsfavoritesmodel.h" 0010 #include "recentusagemodel.h" 0011 #include "systemmodel.h" 0012 0013 #include <KLocalizedString> 0014 0015 #include <QCollator> 0016 0017 GroupEntry::GroupEntry(AppsModel *parentModel, const QString &name, const QString &iconName, AbstractModel *childModel) 0018 : AbstractGroupEntry(parentModel) 0019 , m_name(name) 0020 , m_iconName(iconName) 0021 , m_childModel(childModel) 0022 { 0023 QObject::connect(parentModel, &RootModel::cleared, childModel, &AbstractModel::deleteLater); 0024 0025 QObject::connect(childModel, &AbstractModel::countChanged, [parentModel, this] { 0026 if (parentModel) { 0027 parentModel->entryChanged(this); 0028 } 0029 }); 0030 } 0031 0032 QString GroupEntry::name() const 0033 { 0034 return m_name; 0035 } 0036 0037 QString GroupEntry::icon() const 0038 { 0039 return m_iconName; 0040 } 0041 0042 bool GroupEntry::hasChildren() const 0043 { 0044 return m_childModel && m_childModel->count() > 0; 0045 } 0046 0047 AbstractModel *GroupEntry::childModel() const 0048 { 0049 return m_childModel; 0050 } 0051 0052 RootModel::RootModel(QObject *parent) 0053 : AppsModel(QString(), parent) 0054 , m_favorites(new KAStatsFavoritesModel(this)) 0055 , m_systemModel(nullptr) 0056 , m_showAllApps(false) 0057 , m_showAllAppsCategorized(false) 0058 , m_showRecentApps(true) 0059 , m_showRecentDocs(true) 0060 , m_recentOrdering(RecentUsageModel::Recent) 0061 , m_showPowerSession(true) 0062 , m_showFavoritesPlaceholder(false) 0063 , m_recentAppsModel(nullptr) 0064 , m_recentDocsModel(nullptr) 0065 { 0066 } 0067 0068 RootModel::~RootModel() 0069 { 0070 } 0071 0072 QVariant RootModel::data(const QModelIndex &index, int role) const 0073 { 0074 if (!index.isValid() || index.row() >= m_entryList.count()) { 0075 return QVariant(); 0076 } 0077 0078 if (role == Kicker::HasActionListRole || role == Kicker::ActionListRole) { 0079 const AbstractEntry *entry = m_entryList.at(index.row()); 0080 0081 if (entry->type() == AbstractEntry::GroupType) { 0082 const GroupEntry *group = static_cast<const GroupEntry *>(entry); 0083 AbstractModel *model = group->childModel(); 0084 0085 if (model == m_recentAppsModel || model == m_recentDocsModel) { 0086 if (role == Kicker::HasActionListRole) { 0087 return true; 0088 } else if (role == Kicker::ActionListRole) { 0089 QVariantList actionList; 0090 actionList << model->actions(); 0091 actionList << Kicker::createSeparatorActionItem(); 0092 actionList << Kicker::createActionItem(i18n("Hide %1", group->name()), QStringLiteral("view-hidden"), QStringLiteral("hideCategory")); 0093 return actionList; 0094 } 0095 } 0096 } 0097 } 0098 0099 return AppsModel::data(index, role); 0100 } 0101 0102 bool RootModel::trigger(int row, const QString &actionId, const QVariant &argument) 0103 { 0104 const AbstractEntry *entry = m_entryList.at(row); 0105 0106 if (entry->type() == AbstractEntry::GroupType) { 0107 if (actionId == QLatin1String("hideCategory")) { 0108 AbstractModel *model = entry->childModel(); 0109 0110 if (model == m_recentAppsModel) { 0111 setShowRecentApps(false); 0112 0113 return true; 0114 } else if (model == m_recentDocsModel) { 0115 setShowRecentDocs(false); 0116 0117 return true; 0118 } 0119 } else if (entry->childModel()->hasActions()) { 0120 return entry->childModel()->trigger(-1, actionId, QVariant()); 0121 } 0122 } 0123 0124 return AppsModel::trigger(row, actionId, argument); 0125 } 0126 0127 bool RootModel::showAllApps() const 0128 { 0129 return m_showAllApps; 0130 } 0131 0132 void RootModel::setShowAllApps(bool show) 0133 { 0134 if (m_showAllApps != show) { 0135 m_showAllApps = show; 0136 0137 refresh(); 0138 0139 Q_EMIT showAllAppsChanged(); 0140 } 0141 } 0142 0143 bool RootModel::showAllAppsCategorized() const 0144 { 0145 return m_showAllAppsCategorized; 0146 } 0147 0148 void RootModel::setShowAllAppsCategorized(bool showCategorized) 0149 { 0150 if (m_showAllAppsCategorized != showCategorized) { 0151 m_showAllAppsCategorized = showCategorized; 0152 0153 refresh(); 0154 0155 Q_EMIT showAllAppsCategorizedChanged(); 0156 } 0157 } 0158 0159 bool RootModel::showRecentApps() const 0160 { 0161 return m_showRecentApps; 0162 } 0163 0164 void RootModel::setShowRecentApps(bool show) 0165 { 0166 if (show != m_showRecentApps) { 0167 m_showRecentApps = show; 0168 0169 refresh(); 0170 0171 Q_EMIT showRecentAppsChanged(); 0172 } 0173 } 0174 0175 bool RootModel::showRecentDocs() const 0176 { 0177 return m_showRecentDocs; 0178 } 0179 0180 void RootModel::setShowRecentDocs(bool show) 0181 { 0182 if (show != m_showRecentDocs) { 0183 m_showRecentDocs = show; 0184 0185 refresh(); 0186 0187 Q_EMIT showRecentDocsChanged(); 0188 } 0189 } 0190 0191 int RootModel::recentOrdering() const 0192 { 0193 return m_recentOrdering; 0194 } 0195 0196 void RootModel::setRecentOrdering(int ordering) 0197 { 0198 if (ordering != m_recentOrdering) { 0199 m_recentOrdering = ordering; 0200 0201 refresh(); 0202 0203 Q_EMIT recentOrderingChanged(); 0204 } 0205 } 0206 0207 bool RootModel::showPowerSession() const 0208 { 0209 return m_showPowerSession; 0210 } 0211 0212 void RootModel::setShowPowerSession(bool show) 0213 { 0214 if (show != m_showPowerSession) { 0215 m_showPowerSession = show; 0216 0217 refresh(); 0218 0219 Q_EMIT showPowerSessionChanged(); 0220 } 0221 } 0222 0223 bool RootModel::showFavoritesPlaceholder() const 0224 { 0225 return m_showFavoritesPlaceholder; 0226 } 0227 0228 void RootModel::setShowFavoritesPlaceholder(bool show) 0229 { 0230 if (show != m_showFavoritesPlaceholder) { 0231 m_showFavoritesPlaceholder = show; 0232 0233 refresh(); 0234 0235 Q_EMIT showFavoritesPlaceholderChanged(); 0236 } 0237 } 0238 0239 AbstractModel *RootModel::favoritesModel() 0240 { 0241 return m_favorites; 0242 } 0243 0244 AbstractModel *RootModel::systemFavoritesModel() 0245 { 0246 if (m_systemModel) { 0247 return m_systemModel->favoritesModel(); 0248 } 0249 0250 return nullptr; 0251 } 0252 0253 void RootModel::refresh() 0254 { 0255 if (!m_complete) { 0256 return; 0257 } 0258 0259 beginResetModel(); 0260 0261 AppsModel::refreshInternal(); 0262 0263 AppsModel *allModel = nullptr; 0264 m_recentAppsModel = nullptr; 0265 m_recentDocsModel = nullptr; 0266 0267 if (m_showAllApps) { 0268 QHash<QString, AbstractEntry *> appsHash; 0269 0270 std::function<void(AbstractEntry *)> processEntry = [&](AbstractEntry *entry) { 0271 if (entry->type() == AbstractEntry::RunnableType) { 0272 AppEntry *appEntry = static_cast<AppEntry *>(entry); 0273 appsHash.insert(appEntry->service()->menuId(), appEntry); 0274 } else if (entry->type() == AbstractEntry::GroupType) { 0275 GroupEntry *groupEntry = static_cast<GroupEntry *>(entry); 0276 AbstractModel *model = groupEntry->childModel(); 0277 0278 if (!model) { 0279 return; 0280 } 0281 0282 for (int i = 0; i < model->count(); ++i) { 0283 processEntry(static_cast<AbstractEntry *>(model->index(i, 0).internalPointer())); 0284 } 0285 } 0286 }; 0287 0288 for (AbstractEntry *entry : std::as_const(m_entryList)) { 0289 processEntry(entry); 0290 } 0291 0292 QList<AbstractEntry *> apps(appsHash.values()); 0293 sortEntries(apps); 0294 0295 if (!m_showAllAppsCategorized && !m_paginate) { // The app list built above goes into a model. 0296 allModel = new AppsModel(apps, false, this); 0297 } else if (m_paginate) { // We turn the apps list into a subtree of pages. 0298 m_favorites = new KAStatsFavoritesModel(this); 0299 Q_EMIT favoritesModelChanged(); 0300 0301 QList<AbstractEntry *> groups; 0302 0303 int at = 0; 0304 QList<AbstractEntry *> page; 0305 page.reserve(m_pageSize); 0306 0307 for (AbstractEntry *app : std::as_const(apps)) { 0308 page.append(app); 0309 0310 if (at == (m_pageSize - 1)) { 0311 at = 0; 0312 AppsModel *model = new AppsModel(page, false, this); 0313 groups.append(new GroupEntry(this, QString(), QString(), model)); 0314 page.clear(); 0315 } else { 0316 ++at; 0317 } 0318 } 0319 0320 if (!page.isEmpty()) { 0321 AppsModel *model = new AppsModel(page, false, this); 0322 groups.append(new GroupEntry(this, QString(), QString(), model)); 0323 } 0324 0325 groups.prepend(new GroupEntry(this, QString(), QString(), m_favorites)); 0326 0327 allModel = new AppsModel(groups, true, this); 0328 } else { // We turn the apps list into a subtree of apps by starting letter. 0329 QList<AbstractEntry *> groups; 0330 QHash<QString, QList<AbstractEntry *>> m_categoryHash; 0331 0332 for (const AbstractEntry *groupEntry : std::as_const(m_entryList)) { 0333 AbstractModel *model = groupEntry->childModel(); 0334 0335 if (!model) 0336 continue; 0337 0338 for (int i = 0; i < model->count(); ++i) { 0339 AbstractEntry *appEntry = static_cast<AbstractEntry *>(model->index(i, 0).internalPointer()); 0340 0341 // App entry's group stores a transliterated first character of the name. Prefer to use that. 0342 QString name = appEntry->group(); 0343 if (name.isEmpty()) { 0344 name = appEntry->name(); 0345 } 0346 0347 if (name.isEmpty()) { 0348 continue; 0349 } 0350 0351 const QChar &first = name.at(0).toUpper(); 0352 m_categoryHash[first.isDigit() ? QStringLiteral("0-9") : first].append(appEntry); 0353 } 0354 } 0355 0356 QHashIterator<QString, QList<AbstractEntry *>> i(m_categoryHash); 0357 0358 while (i.hasNext()) { 0359 i.next(); 0360 AppsModel *model = new AppsModel(i.value(), false, this); 0361 model->setDescription(i.key()); 0362 groups.append(new GroupEntry(this, i.key(), QString(), model)); 0363 } 0364 0365 allModel = new AppsModel(groups, true, this); 0366 } 0367 0368 allModel->setDescription(QStringLiteral("KICKER_ALL_MODEL")); // Intentionally no i18n. 0369 } 0370 0371 int separatorPosition = 0; 0372 0373 if (allModel) { 0374 m_entryList.prepend(new GroupEntry(this, i18n("All Applications"), QStringLiteral("applications-all"), allModel)); 0375 ++separatorPosition; 0376 } 0377 0378 if (m_showFavoritesPlaceholder) { 0379 // This entry is a placeholder and shouldn't ever be visible 0380 QList<AbstractEntry *> placeholderList; 0381 AppsModel *placeholderModel = new AppsModel(placeholderList, false, this); 0382 0383 // Favorites group containing a placeholder entry, so it would be considered as a group, not an entry 0384 QList<AbstractEntry *> placeholderEntry; 0385 placeholderEntry.append(new GroupEntry(this, // 0386 i18n("This shouldn't be visible! Use KICKER_FAVORITES_MODEL"), 0387 QStringLiteral("dialog-warning"), 0388 placeholderModel)); 0389 AppsModel *favoritesPlaceholderModel = new AppsModel(placeholderEntry, false, this); 0390 0391 favoritesPlaceholderModel->setDescription(QStringLiteral("KICKER_FAVORITES_MODEL")); // Intentionally no i18n. 0392 m_entryList.prepend(new GroupEntry(this, i18n("Favorites"), QStringLiteral("bookmarks"), favoritesPlaceholderModel)); 0393 ++separatorPosition; 0394 } 0395 0396 if (m_showRecentDocs) { 0397 m_recentDocsModel = new RecentUsageModel(this, RecentUsageModel::OnlyDocs, m_recentOrdering); 0398 m_entryList.prepend(new GroupEntry(this, 0399 m_recentOrdering == RecentUsageModel::Recent ? i18n("Recent Files") : i18n("Often Used Files"), 0400 m_recentOrdering == RecentUsageModel::Recent ? QStringLiteral("view-history") : QStringLiteral("office-chart-pie"), 0401 m_recentDocsModel)); 0402 ++separatorPosition; 0403 } 0404 0405 if (m_showRecentApps) { 0406 m_recentAppsModel = new RecentUsageModel(this, RecentUsageModel::OnlyApps, m_recentOrdering); 0407 m_entryList.prepend(new GroupEntry(this, 0408 m_recentOrdering == RecentUsageModel::Recent ? i18n("Recent Applications") : i18n("Often Used Applications"), 0409 m_recentOrdering == RecentUsageModel::Recent ? QStringLiteral("view-history") : QStringLiteral("office-chart-pie"), 0410 m_recentAppsModel)); 0411 ++separatorPosition; 0412 } 0413 0414 if (m_showSeparators && separatorPosition > 0) { 0415 m_entryList.insert(separatorPosition, new SeparatorEntry(this)); 0416 ++m_separatorCount; 0417 } 0418 0419 m_systemModel = new SystemModel(this); 0420 QObject::connect(m_systemModel, &SystemModel::sessionManagementStateChanged, this, &RootModel::refresh); 0421 0422 if (m_showPowerSession) { 0423 m_entryList << new GroupEntry(this, i18n("Power / Session"), QStringLiteral("system-log-out"), m_systemModel); 0424 } 0425 0426 endResetModel(); 0427 0428 m_favorites->refresh(); 0429 0430 Q_EMIT systemFavoritesModelChanged(); 0431 Q_EMIT countChanged(); 0432 Q_EMIT separatorCountChanged(); 0433 0434 Q_EMIT refreshed(); 0435 }