File indexing completed on 2024-05-12 05:35:38

0001 /*
0002     SPDX-FileCopyrightText: 2022 Méven Car <meven@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "applicationmodel.h"
0008 
0009 #include <KApplicationTrader>
0010 #include <KLocalizedString>
0011 #include <KService>
0012 
0013 #include <optional>
0014 
0015 #include "componentchooser.h"
0016 
0017 ApplicationModel::ApplicationModel(QObject *parent)
0018     : QAbstractItemModel(parent)
0019 {
0020 }
0021 
0022 void ApplicationModel::addApplication(const QString &name, const QString &iconName, const QString &storageId, bool isSelected, const QString &execLine)
0023 {
0024     QVariantMap application;
0025     application[QStringLiteral("name")] = name;
0026     application[QStringLiteral("icon")] = iconName;
0027     application[QStringLiteral("storageId")] = storageId;
0028     application[QStringLiteral("isSelected")] = isSelected;
0029     application[QStringLiteral("execLine")] = execLine;
0030     m_applications += application;
0031 }
0032 
0033 void ApplicationModel::load(const QString &mimeType,
0034                             const QString &applicationCategory,
0035                             const QString &defaultApplication,
0036                             const KService::Ptr preferredService)
0037 {
0038     beginResetModel();
0039 
0040     m_applications.clear();
0041 
0042     if (preferredService) {
0043         addApplication(preferredService->name(), preferredService->icon(), preferredService->storageId(), true, preferredService->exec());
0044         if (preferredService->storageId() == defaultApplication) {
0045             m_defaultIndex = 0;
0046         }
0047     }
0048 
0049     KApplicationTrader::query([preferredService, applicationCategory, mimeType, defaultApplication, this](const KService::Ptr &service) {
0050         if (service->exec().isEmpty() || (!applicationCategory.isEmpty() && !service->categories().contains(applicationCategory))
0051             || (!mimeType.isEmpty() && !ComponentChooser::serviceSupportsMimeType(service, mimeType))
0052             || (preferredService && preferredService->storageId() == service->storageId())) {
0053             return false;
0054         }
0055 
0056         const auto icon = service->icon().isEmpty() ? QStringLiteral("application-x-shellscript") : service->icon();
0057         bool isDefault = service->storageId() == defaultApplication;
0058 
0059         addApplication(service->name(), icon, service->storageId(), false, service->exec());
0060 
0061         if (isDefault) {
0062             m_defaultIndex = m_applications.length() - 1;
0063         }
0064         return false;
0065     });
0066 
0067     addApplication(i18n("Other…"), QStringLiteral("application-x-shellscript"), QString(), false, QString());
0068 
0069     endResetModel();
0070 }
0071 
0072 int ApplicationModel::currentIndex() const
0073 {
0074     int index = 0;
0075     for (const auto &application : std::as_const(m_applications)) {
0076         if (application["isSelected"].toBool()) {
0077             return index;
0078         }
0079         ++index;
0080     }
0081 
0082     if (m_defaultIndex != -1) {
0083         return m_defaultIndex;
0084     }
0085 
0086     return 0;
0087 }
0088 
0089 std::optional<int> ApplicationModel::defaultIndex() const
0090 {
0091     if (m_defaultIndex == -1)
0092         return {};
0093     else {
0094         return m_defaultIndex;
0095     }
0096 }
0097 
0098 int ApplicationModel::addApplicationBeforeLast(const KServicePtr service)
0099 {
0100     // the application will grow by 1, we want to add the new application at the before
0101     // last position taking into account the future new size of m_applications
0102     int newRowIndex = rowCount() - 1;
0103     beginInsertRows(QModelIndex(), newRowIndex, newRowIndex);
0104 
0105     addApplication(service->name(), service->icon(), service->storageId(), false, service->exec());
0106     // addApplication adds at the end of of the list
0107     // swaps the last and before last applications
0108     m_applications.swapItemsAt(rowCount() - 2, rowCount() - 1);
0109 
0110     endInsertRows();
0111 
0112     return newRowIndex;
0113 }
0114 
0115 QVariant ApplicationModel::headerData(int section, Qt::Orientation orientation, int role) const
0116 {
0117     Q_UNUSED(section)
0118     Q_UNUSED(orientation)
0119     Q_UNUSED(role)
0120 
0121     return QVariant();
0122 }
0123 
0124 QModelIndex ApplicationModel::index(int row, int column, const QModelIndex &parent) const
0125 {
0126     Q_UNUSED(parent)
0127 
0128     if (row >= 0 && row < m_applications.length()) {
0129         return createIndex(row, column);
0130     }
0131     return QModelIndex();
0132 }
0133 
0134 QModelIndex ApplicationModel::parent(const QModelIndex &index) const
0135 {
0136     Q_UNUSED(index)
0137     return QModelIndex();
0138 }
0139 
0140 int ApplicationModel::rowCount(const QModelIndex &parent) const
0141 {
0142     Q_UNUSED(parent)
0143     return m_applications.length();
0144 }
0145 
0146 int ApplicationModel::columnCount(const QModelIndex &parent) const
0147 {
0148     if (!parent.isValid())
0149         return 0;
0150 
0151     return 1;
0152 }
0153 
0154 QVariant ApplicationModel::data(const QModelIndex &index, int role) const
0155 {
0156     if (!isValid(index))
0157         return QVariant();
0158 
0159     int row = index.row();
0160     const auto map = m_applications.at(row);
0161     switch (role) {
0162     case Qt::DisplayRole:
0163         return map["name"];
0164     case Icon:
0165         return map["icon"];
0166     case StorageId:
0167         return map["storageId"];
0168     case Selected:
0169         return map["isSelected"];
0170     case ExecLine:
0171         return map["execLine"];
0172     }
0173 
0174     return QVariant();
0175 }
0176 
0177 QVariant ApplicationModel::data(const int &row, int role) const
0178 {
0179     return data(index(row, 0), role);
0180 }
0181 
0182 bool ApplicationModel::isValid(const QModelIndex &index) const
0183 {
0184     return index.column() == 0 && index.row() >= 0 && index.row() < m_applications.length();
0185 }
0186 
0187 bool ApplicationModel::setData(const QModelIndex &index, const QVariant &value, int role)
0188 {
0189     if (role != Selected) {
0190         return false;
0191     }
0192     if (!isValid(index)) {
0193         return false;
0194     }
0195     if (!value.canConvert(QMetaType(QMetaType::Type::Bool)) && !value.toBool()) {
0196         return false;
0197     }
0198 
0199     for (auto &application : m_applications) {
0200         application["isSelected"] = false;
0201     }
0202 
0203     const auto row = index.row();
0204 
0205     // auto map = m_applications[row].toMap();
0206     m_applications[row]["isSelected"] = true;
0207     // m_applications[row] = map;
0208 
0209     Q_EMIT dataChanged(index, index, {role});
0210 
0211     return true;
0212 }
0213 
0214 QModelIndex ApplicationModel::findByStorageId(const QString &storageId) const
0215 {
0216     int i = 0;
0217     for (const auto &application : std::as_const(m_applications)) {
0218         if (application["storageId"] == storageId) {
0219             return index(i, 0);
0220         }
0221         ++i;
0222     }
0223     return QModelIndex();
0224 }
0225 
0226 QHash<int, QByteArray> ApplicationModel::roleNames() const
0227 {
0228     return {
0229         {Qt::DisplayRole, "name"},
0230         {Icon, "icon"},
0231         {StorageId, "storageId"},
0232         {Selected, "isSelected"},
0233         {ExecLine, "execLine"},
0234     };
0235 }