File indexing completed on 2024-06-16 05:08:35
0001 /* 0002 SPDX-FileCopyrightText: 2014 Antonis Tsiapaliokas <antonis.tsiapaliokas@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 // Self 0008 #include "applicationlistmodel.h" 0009 0010 // Qt 0011 #include <QByteArray> 0012 #include <QDebug> 0013 #include <QModelIndex> 0014 #include <QProcess> 0015 #include <QRegularExpression> 0016 0017 // KDE 0018 #include <KConfigGroup> 0019 #include <KIO/ApplicationLauncherJob> 0020 #include <KNotificationJobUiDelegate> 0021 #include <KService> 0022 #include <KServiceGroup> 0023 #include <KSharedConfig> 0024 #include <KShell> 0025 #include <KSycoca> 0026 #include <KSycocaEntry> 0027 #include <PlasmaActivities/ResourceInstance> 0028 0029 ApplicationListModel::ApplicationListModel(QObject *parent) 0030 : QAbstractListModel(parent) 0031 { 0032 connect(KSycoca::self(), static_cast<void (KSycoca::*)()>(&KSycoca::databaseChanged), this, &ApplicationListModel::sycocaDbChanged); 0033 } 0034 0035 ApplicationListModel::~ApplicationListModel() = default; 0036 0037 QHash<int, QByteArray> ApplicationListModel::roleNames() const 0038 { 0039 QHash<int, QByteArray> roleNames; 0040 roleNames[ApplicationNameRole] = "ApplicationNameRole"; 0041 roleNames[ApplicationCommentRole] = "ApplicationCommentRole"; 0042 roleNames[ApplicationIconRole] = "ApplicationIconRole"; 0043 roleNames[ApplicationCategoriesRole] = "ApplicationCategoriesRole"; 0044 roleNames[ApplicationStorageIdRole] = "ApplicationStorageIdRole"; 0045 roleNames[ApplicationEntryPathRole] = "ApplicationEntryPathRole"; 0046 roleNames[ApplicationDesktopRole] = "ApplicationDesktopRole"; 0047 roleNames[ApplicationStartupNotifyRole] = "ApplicationStartupNotifyRole"; 0048 roleNames[ApplicationOriginalRowRole] = "ApplicationOriginalRowRole"; 0049 0050 return roleNames; 0051 } 0052 0053 void ApplicationListModel::sycocaDbChanged() 0054 { 0055 m_applicationList.clear(); 0056 m_voiceAppSkills.clear(); 0057 0058 loadApplications(); 0059 } 0060 0061 bool appNameLessThan(const ApplicationData &a1, const ApplicationData &a2) 0062 { 0063 return a1.name.toLower() < a2.name.toLower(); 0064 } 0065 0066 QStringList ApplicationListModel::voiceAppSkills() const 0067 { 0068 return m_voiceAppSkills; 0069 } 0070 0071 void ApplicationListModel::loadApplications() 0072 { 0073 auto cfg = KSharedConfig::openConfig("applications-blacklistrc"); 0074 auto blgroup = KConfigGroup(cfg, QStringLiteral("Applications")); 0075 0076 // This is only temporary to get a clue what those apps' desktop files are called 0077 // I'll remove it once I've done a blacklist 0078 QStringList bl; 0079 0080 QStringList blacklist = blgroup.readEntry("blacklist", QStringList()); 0081 0082 beginResetModel(); 0083 0084 m_applicationList.clear(); 0085 0086 KServiceGroup::Ptr group = KServiceGroup::root(); 0087 if (!group || !group->isValid()) { 0088 return; 0089 } 0090 KServiceGroup::List subGroupList = group->entries(true); 0091 0092 QMap<int, ApplicationData> orderedList; 0093 QList<ApplicationData> unorderedList; 0094 0095 // Iterate over all entries in the group 0096 while (!subGroupList.isEmpty()) { 0097 KSycocaEntry::Ptr groupEntry = subGroupList.first(); 0098 subGroupList.pop_front(); 0099 0100 if (groupEntry->isType(KST_KServiceGroup)) { 0101 KServiceGroup::Ptr serviceGroup(static_cast<KServiceGroup *>(groupEntry.data())); 0102 0103 if (!serviceGroup->noDisplay()) { 0104 KServiceGroup::List entryGroupList = serviceGroup->entries(true); 0105 0106 for (KServiceGroup::List::ConstIterator it = entryGroupList.constBegin(); it != entryGroupList.constEnd(); it++) { 0107 KSycocaEntry::Ptr entry = (*it); 0108 0109 if (entry->isType(KST_KServiceGroup)) { 0110 KServiceGroup::Ptr serviceGroup(static_cast<KServiceGroup *>(entry.data())); 0111 subGroupList << serviceGroup; 0112 0113 } else if (const auto service = static_cast<KService *>(entry.data()); entry->isType(KST_KService) && !service->exec().isEmpty()) { 0114 qDebug() << service->property<QStringList>("Categories"); 0115 qDebug() << " desktopEntryName: " << service->desktopEntryName(); 0116 0117 // else if (entry->property("Exec").isValid()) { 0118 // KService::Ptr service(static_cast<KService* >(entry.data())); 0119 0120 // qDebug() << " desktopEntryName: " << service->desktopEntryName(); 0121 0122 if (service->isApplication() && !blacklist.contains(service->desktopEntryName()) && service->showOnCurrentPlatform() 0123 && !service->property<bool>("Terminal")) { 0124 QRegularExpression voiceExpr(QStringLiteral("mycroft-gui-app .* --skill=(.*)\\.home")); 0125 0126 if (service->categories().contains(QStringLiteral("VoiceApp")) && voiceExpr.match(service->exec()).hasMatch()) { 0127 QString exec = service->exec(); 0128 exec.replace(voiceExpr, QStringLiteral("\\1")); 0129 if (!exec.isEmpty()) { 0130 m_voiceAppSkills << exec; 0131 } 0132 } 0133 0134 bl << service->desktopEntryName(); 0135 0136 ApplicationData data; 0137 data.name = service->name(); 0138 data.comment = service->comment(); 0139 data.icon = service->icon(); 0140 data.categories = service->categories(); 0141 data.storageId = service->storageId(); 0142 data.entryPath = service->exec(); 0143 data.desktopPath = service->entryPath(); 0144 data.startupNotify = service->property<bool>("StartupNotify"); 0145 0146 auto it = m_appPositions.constFind(service->storageId()); 0147 if (it != m_appPositions.constEnd()) { 0148 orderedList[*it] = data; 0149 } else { 0150 unorderedList << data; 0151 } 0152 } 0153 } 0154 } 0155 } 0156 } 0157 } 0158 0159 Q_EMIT voiceAppSkillsChanged(); 0160 0161 blgroup.writeEntry("allapps", bl); 0162 blgroup.writeEntry("blacklist", blacklist); 0163 cfg->sync(); 0164 0165 std::sort(unorderedList.begin(), unorderedList.end(), appNameLessThan); 0166 m_applicationList << orderedList.values(); 0167 m_applicationList << unorderedList; 0168 0169 endResetModel(); 0170 Q_EMIT countChanged(); 0171 } 0172 0173 QVariant ApplicationListModel::data(const QModelIndex &index, int role) const 0174 { 0175 if (!index.isValid()) { 0176 return QVariant(); 0177 } 0178 0179 switch (role) { 0180 case Qt::DisplayRole: 0181 case ApplicationNameRole: 0182 return m_applicationList.at(index.row()).name; 0183 case ApplicationCommentRole: 0184 return m_applicationList.at(index.row()).comment; 0185 case ApplicationIconRole: 0186 return m_applicationList.at(index.row()).icon; 0187 case ApplicationCategoriesRole: 0188 return m_applicationList.at(index.row()).categories; 0189 case ApplicationStorageIdRole: 0190 return m_applicationList.at(index.row()).storageId; 0191 case ApplicationEntryPathRole: 0192 return m_applicationList.at(index.row()).entryPath; 0193 case ApplicationDesktopRole: 0194 return m_applicationList.at(index.row()).desktopPath; 0195 case ApplicationStartupNotifyRole: 0196 return m_applicationList.at(index.row()).startupNotify; 0197 case ApplicationOriginalRowRole: 0198 return index.row(); 0199 0200 default: 0201 return QVariant(); 0202 } 0203 } 0204 0205 Qt::ItemFlags ApplicationListModel::flags(const QModelIndex &index) const 0206 { 0207 if (!index.isValid()) { 0208 return Qt::NoItemFlags; 0209 } 0210 return Qt::ItemIsDragEnabled | QAbstractItemModel::flags(index); 0211 } 0212 0213 int ApplicationListModel::rowCount(const QModelIndex &parent) const 0214 { 0215 if (parent.isValid()) { 0216 return 0; 0217 } 0218 0219 return m_applicationList.count(); 0220 } 0221 0222 void ApplicationListModel::moveRow(const QModelIndex & /* sourceParent */, int sourceRow, const QModelIndex & /* destinationParent */, int destinationChild) 0223 { 0224 moveItem(sourceRow, destinationChild); 0225 } 0226 0227 Q_INVOKABLE void ApplicationListModel::moveItem(int row, int destination) 0228 { 0229 if (row < 0 || destination < 0 || row >= m_applicationList.length() || destination >= m_applicationList.length() || row == destination) { 0230 return; 0231 } 0232 if (destination > row) { 0233 ++destination; 0234 } 0235 0236 beginMoveRows(QModelIndex(), row, row, QModelIndex(), destination); 0237 if (destination > row) { 0238 ApplicationData data = m_applicationList.at(row); 0239 m_applicationList.insert(destination, data); 0240 m_applicationList.takeAt(row); 0241 } else { 0242 ApplicationData data = m_applicationList.takeAt(row); 0243 m_applicationList.insert(destination, data); 0244 } 0245 0246 m_appOrder.clear(); 0247 m_appPositions.clear(); 0248 int i = 0; 0249 for (const auto &app : std::as_const(m_applicationList)) { 0250 m_appOrder << app.storageId; 0251 m_appPositions[app.storageId] = i; 0252 ++i; 0253 } 0254 0255 Q_EMIT appOrderChanged(); 0256 endMoveRows(); 0257 } 0258 0259 void ApplicationListModel::executeCommand(const QString &command) 0260 { 0261 qWarning() << "Executing" << command; 0262 QStringList args = command.split(QStringLiteral(" ")); 0263 QString app = args.takeFirst(); 0264 QProcess::startDetached(app, args); 0265 } 0266 0267 void ApplicationListModel::runApplication(const QString &storageId) 0268 { 0269 if (storageId.isEmpty()) { 0270 return; 0271 } 0272 0273 KService::Ptr service = KService::serviceByStorageId(storageId); 0274 0275 // KRun::runApplication(*service, QList<QUrl>(), nullptr); 0276 KIO::ApplicationLauncherJob *job = new KIO::ApplicationLauncherJob(service); 0277 job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled)); 0278 job->start(); 0279 0280 KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("applications:") + service->storageId()), QStringLiteral("org.kde.plasma.kicker")); 0281 } 0282 0283 QStringList ApplicationListModel::appOrder() const 0284 { 0285 return m_appOrder; 0286 } 0287 0288 void ApplicationListModel::setAppOrder(const QStringList &order) 0289 { 0290 if (m_appOrder == order) { 0291 return; 0292 } 0293 0294 m_appOrder = order; 0295 m_appPositions.clear(); 0296 int i = 0; 0297 for (const auto &app : std::as_const(m_appOrder)) { 0298 m_appPositions[app] = i; 0299 ++i; 0300 } 0301 Q_EMIT appOrderChanged(); 0302 }