// SPDX-License-Identifier: MIT #include "toolmodel.h" #include #include #include #include #include #include #include #include #include #include namespace { QStringList readConfigurationCommand(const QString &packageName) { const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("fedora-tools/settings/") + packageName + QStringLiteral(".json")); if (path.isEmpty()) { return {}; } QFile file(path); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "Cannot read tool configuration registration:" << path << file.errorString(); return {}; } QJsonParseError error; const auto document = QJsonDocument::fromJson(file.readAll(), &error); const auto command = document.object().value(QStringLiteral("command")).toArray(); if (error.error != QJsonParseError::NoError || command.isEmpty()) { qWarning() << "Invalid tool configuration registration:" << path; return {}; } QStringList result; for (const auto &argument : command) { if (!argument.isString()) { qWarning() << "Invalid tool configuration argument:" << path; return {}; } result.append(argument.toString()); } const QFileInfo executable(result.first()); if (!executable.isAbsolute() || !executable.isExecutable() || !executable.isFile()) { qWarning() << "Tool configuration executable is unavailable:" << result.first(); return {}; } return result; } } ToolModel::ToolModel(QObject *parent) : QAbstractListModel(parent) { } int ToolModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : m_tools.size(); } QVariant ToolModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= m_tools.size()) { return {}; } const Tool &tool = m_tools.at(index.row()); switch (role) { case PackageNameRole: return tool.packageName; case VersionRole: return tool.version; case AvailableVersionRole: return tool.availableVersion; case ArchitectureRole: return tool.architecture; case SummaryRole: return tool.summary; case InstalledRole: return tool.installed; case AvailableRole: return tool.available; case UpdateAvailableRole: return tool.updateAvailable; case ConfigurableRole: return tool.installed && (!tool.configurationCommand.isEmpty() || tool.packageName == QStringLiteral("plasma-fingerprint-workaround") || tool.packageName == QStringLiteral("plasma-task-group-shortcuts") || tool.packageName == QStringLiteral("touchpad-hold-tap")); case ExternalConfigurationRole: return tool.installed && !tool.configurationCommand.isEmpty(); default: return {}; } } QHash ToolModel::roleNames() const { return { {PackageNameRole, "packageName"}, {VersionRole, "version"}, {AvailableVersionRole, "availableVersion"}, {ArchitectureRole, "architecture"}, {SummaryRole, "summary"}, {InstalledRole, "installed"}, {AvailableRole, "available"}, {UpdateAvailableRole, "updateAvailable"}, {ConfigurableRole, "configurable"}, {ExternalConfigurationRole, "externalConfiguration"}, }; } void ToolModel::setPackages(const QList &installed, const QList &available, const QSet &updates) { QMap tools; for (const PackageRecord &package : installed) { Tool &tool = tools[package.name]; tool.packageName = package.name; tool.version = package.version; tool.architecture = package.architecture; tool.summary = package.summary; tool.installed = true; tool.configurationCommand = readConfigurationCommand(package.name); } for (const PackageRecord &package : available) { Tool &tool = tools[package.name]; tool.packageName = package.name; tool.availableVersion = package.version; tool.architecture = package.architecture; if (!tool.installed) { tool.summary = package.summary; } tool.available = true; tool.updateAvailable = tool.installed && updates.contains(package.name); } QList merged = tools.values(); std::ranges::sort(merged, [](const Tool &left, const Tool &right) { return left.packageName < right.packageName; }); beginResetModel(); m_tools = std::move(merged); endResetModel(); } bool ToolModel::mayInstall(const QString &packageName) const { return std::ranges::any_of(m_tools, [&packageName](const Tool &tool) { return tool.packageName == packageName && tool.available; }); } bool ToolModel::mayRemove(const QString &packageName) const { return std::ranges::any_of(m_tools, [&packageName](const Tool &tool) { return tool.packageName == packageName && tool.installed; }); } QStringList ToolModel::configurationCommand(const QString &packageName) const { for (const Tool &tool : m_tools) { if (tool.packageName == packageName && tool.installed) { return tool.configurationCommand; } } return {}; }