diff --git a/faces/ConfigSensors.qml b/faces/ConfigSensors.qml index dd878f6..0955264 100644 --- a/faces/ConfigSensors.qml +++ b/faces/ConfigSensors.qml @@ -1,285 +1,285 @@ /* * Copyright 2019 Marco Martin * Copyright 2019 David Edmundson * Copyright 2019 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.2 as QQC2 import QtQml.Models 2.12 import org.kde.kirigami 2.8 as Kirigami import org.kde.kquickcontrols 2.0 import org.kde.kitemmodels 1.0 as KItemModels import org.kde.quickcharts 1.0 as Charts import org.kde.ksysguard.sensors 1.0 as Sensors import org.kde.ksysguard.faces 1.0 as Faces import "./" as Local ColumnLayout { id: root readonly property int implicitHeight: 1 //HACK FIXME to disable external scrollbar signal configurationChanged - property string cfg_totalSensor + property var cfg_totalSensors property alias cfg_highPrioritySensorIds: usedSensorsView.sensorIds property alias cfg_highPrioritySensorColors: usedSensorsView.sensorColors property alias cfg_lowPrioritySensorIds: lowPrioritySensorsView.sensorIds - onCfg_totalSensorChanged: configurationChanged(); + onCfg_totalSensorsChanged: configurationChanged(); onCfg_highPrioritySensorIdsChanged: configurationChanged(); onCfg_highPrioritySensorColorsChanged: configurationChanged(); onCfg_lowPrioritySensorIdsChanged: configurationChanged(); property Faces.SensorFaceController controller Sensors.Sensor { id: totalSensor - sensorId: cfg_totalSensor + sensorId: cfg_totalSensors.length > 0 ? cfg_totalSensors[0] : "" } function saveConfig() { - controller.totalSensor = cfg_totalSensor; + controller.totalSensors = cfg_totalSensors; controller.highPrioritySensorIds = cfg_highPrioritySensorIds; controller.highPrioritySensorColors = cfg_highPrioritySensorColors; controller.lowPrioritySensorIds = cfg_lowPrioritySensorIds; } function loadConfig() { - cfg_totalSensor = controller.totalSensor; + cfg_totalSensors = controller.totalSensors; cfg_highPrioritySensorIds = controller.highPrioritySensorIds; cfg_highPrioritySensorColors = controller.highPrioritySensorColors; usedSensorsView.load(); cfg_lowPrioritySensorIds = controller.lowPrioritySensorIds; lowPrioritySensorsView.load(); } Component.onCompleted: loadConfig() Connections { target: controller - onTotalSensorChanged: Qt.callLater(root.loadConfig) + onTotalSensorsChanged: Qt.callLater(root.loadConfig) onHighPrioritySensorIdsChanged: Qt.callLater(root.loadConfig) onHighPrioritySensorColorsChanged: Qt.callLater(root.loadConfig) onLowPrioritySensorIdsChanged: Qt.callLater(root.loadConfig) } Component { id: delegateComponent Kirigami.SwipeListItem { id: listItem width: usedSensorsView.width actions: Kirigami.Action { icon.name: "list-remove" text: i18n("Remove") onTriggered: { usedSensorsModel.remove(index, 1); usedSensorsModel.save(); } } contentItem: RowLayout { Kirigami.ListItemDragHandle { listItem: listItem listView: usedSensorsView onMoveRequested: { usedSensorsModel.move(oldIndex, newIndex, 1) usedSensorsModel.save(); } } ColorButton { id: textColorButton color: model.color onColorChanged: { usedSensorsModel.setProperty(index, "color", color.toString()); usedSensorsModel.save(); } } QQC2.Label { Layout.fillWidth: true text: sensor.name Sensors.Sensor { id: sensor sensorId: model.sensorId } } } } } RowLayout { Layout.preferredHeight: sensorListHeader.implicitHeight - visible: controller.supportsTotalSensor + visible: controller.supportsTotalSensors QQC2.Label { text: i18n("Total Sensor:") } QQC2.Label { Layout.fillWidth: true - text: cfg_totalSensor.length > 0 ? totalSensor.name : i18n("Drop Sensor Here") + text: cfg_totalSensors.length > 0 ? totalSensor.name : i18n("Drop Sensor Here") elide: Text.ElideRight DropArea { anchors.fill: parent onEntered: { if (drag.formats.indexOf("application/x-ksysguard") == -1) { drag.accepted = false; return; } } onDropped: { - cfg_totalSensor = drop.getDataAsString("application/x-ksysguard") + cfg_totalSensors = drop.getDataAsString("application/x-ksysguard") } } } QQC2.ToolButton { icon.name: "list-remove" - opacity: cfg_totalSensor.length > 0 - onClicked: cfg_totalSensor = ""; + opacity: cfg_totalSensors.length > 0 + onClicked: cfg_totalSensors = []; } } RowLayout { Layout.fillWidth: true Layout.fillHeight: true Layout.minimumHeight: 0 Layout.preferredHeight: 0 ColumnLayout { Layout.fillWidth: true Layout.fillHeight: true Layout.preferredHeight: 0 Layout.preferredWidth: Kirigami.Units.gridUnit * 14 Kirigami.Heading { Layout.preferredHeight: sensorListHeader.implicitHeight level: 3 text: i18n("Chart Sensors") } Local.UsedSensorsView { id: usedSensorsView showColor: controller.supportsSensorsColors sensorColors: root.controller.highPrioritySensorColors } Kirigami.Heading { Layout.preferredHeight: sensorListHeader.implicitHeight text: i18n("Text Only Sensors") level: 3 visible: lowPrioritySensorsView.visible } Local.UsedSensorsView { id: lowPrioritySensorsView visible: controller.supportsLowPrioritySensors showColor: false } } ColumnLayout { RowLayout { id: sensorListHeader Layout.fillWidth: true QQC2.ToolButton { icon.name: "go-previous" enabled: sensorsDelegateModel.rootIndex.valid onClicked: sensorsDelegateModel.rootIndex = sensorsDelegateModel.parentModelIndex() } Kirigami.Heading { Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter level: 3 text: i18n("All Sensors") } } Kirigami.SearchField { id: searchQuery Layout.fillWidth: true } QQC2.ScrollView { Layout.fillWidth: true Layout.fillHeight: true Layout.preferredWidth: Kirigami.Units.gridUnit * 14 ListView { KItemModels.KSortFilterProxyModel { id: sensorsSearchableModel filterCaseSensitivity: Qt.CaseInsensitive filterString: searchQuery.text sourceModel: KItemModels.KSortFilterProxyModel { filterRole: "SensorId" filterRowCallback: function(row, value) { return (value && value.length) } sourceModel: KItemModels.KDescendantsProxyModel { model: allSensorsTreeModel } } } Sensors.SensorTreeModel { id: allSensorsTreeModel } model: DelegateModel { id: sensorsDelegateModel model: searchQuery.text.length == 0 ? allSensorsTreeModel : sensorsSearchableModel delegate: Kirigami.BasicListItem { id: sensorTreeDelegate text: model.display icon: (model.SensorId.length == 0) ? "folder" : "" Drag.active: model.SensorId.length > 0 && sensorTreeDelegate.pressed Drag.dragType: Drag.Automatic Drag.supportedActions: Qt.CopyAction Drag.hotSpot.x: sensorTreeDelegate.pressX Drag.hotSpot.y: sensorTreeDelegate.pressY Drag.mimeData: { "application/x-ksysguard": model.SensorId } //FIXME: better handling of Drag onPressed: { onPressed: grabToImage(function(result) { Drag.imageSource = result.url }) } onClicked: { if (model.SensorId.length == 0) { sensorsDelegateModel.rootIndex = sensorsDelegateModel.modelIndex(index); } } onDoubleClicked: { if (model.SensorId) { usedSensorsView.appendSensor(model.SensorId); usedSensorsView.positionViewAtIndex(usedSensorsView.count - 1, ListView.Contain); } } } } } Component.onCompleted: background.visible = true; QQC2.ScrollBar.horizontal.visible: false } } } } diff --git a/faces/SensorFaceController.cpp b/faces/SensorFaceController.cpp index 56810ba..25d76db 100644 --- a/faces/SensorFaceController.cpp +++ b/faces/SensorFaceController.cpp @@ -1,685 +1,685 @@ /* Copyright (C) 2020 Marco Martin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "SensorFaceController.h" #include "SensorFaceController_p.h" #include "SensorFace_p.h" #include #include #include #include #include #include #include #include using namespace KSysGuard; FacesModel::FacesModel(QObject *parent) : QStandardItemModel(parent) { reload(); } void FacesModel::reload() { clear(); auto list = KPackage::PackageLoader::self()->listPackages(QStringLiteral("KSysguard/SensorFace")); // NOTE: This will disable completely the internal in-memory cache KPackage::Package p; p.install(QString(), QString()); for (auto plugin : list) { QStandardItem *item = new QStandardItem(plugin.name()); item->setData(plugin.pluginId(), FacesModel::PluginIdRole); appendRow(item); } } QString FacesModel::pluginId(int row) { return data(index(row, 0), PluginIdRole).toString(); } QHash FacesModel::roleNames() const { QHash roles = QAbstractItemModel::roleNames(); roles[PluginIdRole] = "pluginId"; return roles; } PresetsModel::PresetsModel(QObject *parent) : QStandardItemModel(parent) { reload(); } void PresetsModel::reload() { clear(); QList plugins = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), [](const KPluginMetaData &plugin) { return plugin.value(QStringLiteral("X-Plasma-RootPath")) == QStringLiteral("org.kde.plasma.systemmonitor"); }); QSet usedNames; // We iterate backwards because packages under ~/.local are listed first, while we want them last auto it = plugins.rbegin(); for (; it != plugins.rend(); ++it) { const auto &plugin = *it; KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"), plugin.pluginId()); KDesktopFile df(p.path() + QStringLiteral("metadata.desktop")); QString baseName = df.readName(); QString name = baseName; int id = 0; while (usedNames.contains(name)) { name = baseName + QStringLiteral(" (") + QString::number(++id) + QStringLiteral(")"); } usedNames << name; QStandardItem *item = new QStandardItem(baseName); // TODO config QVariantMap config; KConfigGroup configGroup(df.group("Config")); const QStringList keys = configGroup.keyList(); for (const QString &key : keys) { // all strings for now, type conversion happens in QML side when we have the config property map config.insert(key, configGroup.readEntry(key)); } item->setData(plugin.pluginId(), PresetsModel::PluginIdRole); item->setData(config, PresetsModel::ConfigRole); item->setData(QFileInfo(p.path() + QStringLiteral("metadata.desktop")).isWritable(), PresetsModel::WritableRole); appendRow(item); } } QHash PresetsModel::roleNames() const { QHash roles = QAbstractItemModel::roleNames(); roles[PluginIdRole] = "pluginId"; roles[ConfigRole] = "config"; roles[WritableRole] = "writable"; return roles; } class SensorFaceController::Private { public: Private(); SensorFace *createGui(const QString &qmlPath); QQuickItem *createConfigUi(const QString &file, const QVariantMap &initialProperties); SensorFaceController *q; QString title; QQmlEngine *engine; KDesktopFile *faceMetadata = nullptr; KDeclarative::ConfigPropertyMap *faceConfiguration = nullptr; KConfigLoader *faceConfigLoader = nullptr; bool configNeedsSave = false; KPackage::Package facePackage; QString faceId; KConfigGroup configGroup; KConfigGroup appearanceGroup; KConfigGroup sensorsGroup; QPointer fullRepresentation; QPointer compactRepresentation; QPointer faceConfigUi; QPointer appearanceConfigUi; QPointer sensorsConfigUi; QTimer *syncTimer; FacesModel *availableFacesModel = nullptr; PresetsModel *availablePresetsModel = nullptr; }; SensorFaceController::Private::Private() {} SensorFace *SensorFaceController::Private::createGui(const QString &qmlPath) { QQmlComponent *component = new QQmlComponent(engine, qmlPath, nullptr); // TODO: eventually support async components? (only useful for qml files from http, we probably don't want that) if (component->status() != QQmlComponent::Ready) { qCritical() << "Error creating component:"; for (auto err : component->errors()) { qWarning() << err.toString(); } component->deleteLater(); return nullptr; } //TODO: add i18n context object QQmlContext *context = new QQmlContext(engine); QObject *guiObject = component->beginCreate(context); SensorFace *gui = qobject_cast(guiObject); if (!gui) { qWarning()<<"ERROR: QML gui" << guiObject << "not a SensorFace instance"; guiObject->deleteLater(); context->deleteLater(); return nullptr; } // context->setParent(gui); gui->setController(q); component->completeCreate(); component->deleteLater(); return gui; } QQuickItem *SensorFaceController::Private::createConfigUi(const QString &file, const QVariantMap &initialProperties) { QQmlComponent *component = new QQmlComponent(engine, file, nullptr); // TODO: eventually support async components? (only useful for qml files from http, we probably don't want that) if (component->status() != QQmlComponent::Ready) { qCritical() << "Error creating component:"; for (auto err : component->errors()) { qWarning() << err.toString(); } component->deleteLater(); return nullptr; } //TODO: add i18n context object QQmlContext *context = new QQmlContext(engine); QObject *guiObject = component->createWithInitialProperties( initialProperties, context); QQuickItem *gui = qobject_cast(guiObject); Q_ASSERT(gui); component->deleteLater(); return gui; } SensorFaceController::SensorFaceController(KConfigGroup &config, QQmlEngine *engine) : QObject(engine), d(std::make_unique()) { d->q = this; d->configGroup = config; d->appearanceGroup = KConfigGroup(&config, "Appearance"); d->sensorsGroup = KConfigGroup(&config, "Sensors"); d->engine = engine; d->syncTimer = new QTimer(this); d->syncTimer->setSingleShot(true); d->syncTimer->setInterval(5000); connect(d->syncTimer, &QTimer::timeout, this, [this]() { d->appearanceGroup.sync(); d->sensorsGroup.sync(); }); setFaceId(d->appearanceGroup.readEntry("chartFace", QStringLiteral("org.kde.ksysguard.piechart"))); } SensorFaceController::~SensorFaceController() { } QString SensorFaceController::title() const { return d->appearanceGroup.readEntry("title", name()); } void SensorFaceController::setTitle(const QString &title) { if (title == SensorFaceController::title()) { return; } d->appearanceGroup.writeEntry("title", title); d->syncTimer->start(); emit titleChanged(); } -QString SensorFaceController::totalSensor() const +QStringList SensorFaceController::totalSensors() const { - return d->sensorsGroup.readEntry("totalSensor", QString()); + return d->sensorsGroup.readEntry("totalSensors", QStringList()); } -void SensorFaceController::setTotalSensor(const QString &totalSensor) +void SensorFaceController::setTotalSensors(const QStringList &totalSensors) { - if (totalSensor == SensorFaceController::totalSensor()) { + if (totalSensors == SensorFaceController::totalSensors()) { return; } - d->sensorsGroup.writeEntry("totalSensor", totalSensor); + d->sensorsGroup.writeEntry("totalSensors", totalSensors); d->syncTimer->start(); - emit totalSensorChanged(); + emit totalSensorsChanged(); } QStringList SensorFaceController::highPrioritySensorIds() const { return d->sensorsGroup.readEntry("highPrioritySensorIds", QStringList()); } void SensorFaceController::setHighPrioritySensorIds(const QStringList &highPrioritySensorIds) { if (highPrioritySensorIds == SensorFaceController::highPrioritySensorIds()) { return; } d->sensorsGroup.writeEntry("highPrioritySensorIds", highPrioritySensorIds); d->syncTimer->start(); emit highPrioritySensorIdsChanged(); } QStringList SensorFaceController::highPrioritySensorColors() const { return d->sensorsGroup.readEntry("highPrioritySensorColors", QStringList()); } void SensorFaceController::setHighPrioritySensorColors(const QStringList &highPrioritySensorColors) { if (highPrioritySensorColors == SensorFaceController::highPrioritySensorColors()) { return; } d->sensorsGroup.writeEntry("highPrioritySensorColors", highPrioritySensorColors); d->syncTimer->start(); emit highPrioritySensorColorsChanged(); } QStringList SensorFaceController::lowPrioritySensorIds() const { return d->sensorsGroup.readEntry("lowPrioritySensorIds", QStringList()); } void SensorFaceController::setLowPrioritySensorIds(const QStringList &lowPrioritySensorIds) { if (lowPrioritySensorIds == SensorFaceController::lowPrioritySensorIds()) { return; } d->sensorsGroup.writeEntry("lowPrioritySensorIds", lowPrioritySensorIds); d->syncTimer->start(); emit lowPrioritySensorIdsChanged(); } // from face config, immutable by the user QString SensorFaceController::name() const { if (!d->faceMetadata) { return QString(); } return d->faceMetadata->readName(); } const QString SensorFaceController::icon() const { if (!d->faceMetadata) { return QString(); } return d->faceMetadata->readIcon(); } bool SensorFaceController::supportsSensorsColors() const { if (!d->faceMetadata) { return false; } KConfigGroup cg(d->faceMetadata, QStringLiteral("Config")); return cg.readEntry("SupportsSensorsColors", false); } -bool SensorFaceController::supportsTotalSensor() const +bool SensorFaceController::supportsTotalSensors() const { if (!d->faceMetadata) { return false; } KConfigGroup cg(d->faceMetadata, QStringLiteral("Config")); return cg.readEntry("SupportsTotalSensor", false); } bool SensorFaceController::supportsLowPrioritySensors() const { if (!d->faceMetadata) { return false; } KConfigGroup cg(d->faceMetadata, QStringLiteral("Config")); return cg.readEntry("SupportsLowPrioritySensors", false); } void SensorFaceController::setFaceId(const QString &face) { if (d->faceId == face) { return; } if (d->fullRepresentation) { d->fullRepresentation->deleteLater(); d->fullRepresentation.clear(); } if (d->compactRepresentation) { d->compactRepresentation->deleteLater(); d->fullRepresentation.clear(); } d->faceId = face; d->facePackage = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("KSysguard/SensorFace"), face); delete d->faceMetadata; d->faceMetadata = nullptr; if (d->faceConfiguration) { d->faceConfiguration->deleteLater(); d->faceConfiguration = nullptr; } if (d->faceConfigLoader) { d->faceConfigLoader ->deleteLater(); d->faceConfigLoader = nullptr; } if (!d->facePackage.isValid()) { emit faceIdChanged(); return; } //TODO: should be in a different config file rather than metadata //d->faceMetadata = new KDesktopFile(d->facePackage.filePath("metadata")); d->faceMetadata = new KDesktopFile(d->facePackage.path() + QStringLiteral("metadata.desktop")); const QString xmlPath = d->facePackage.filePath("mainconfigxml"); if (!xmlPath.isEmpty()) { QFile file(xmlPath); KConfigGroup cg(&d->configGroup, d->faceId); d->faceConfigLoader = new KConfigLoader(cg, &file, this); d->faceConfiguration = new KDeclarative::ConfigPropertyMap(d->faceConfigLoader, this); } d->appearanceGroup.writeEntry("chartFace", face); d->syncTimer->start(); emit faceIdChanged(); return; } QString SensorFaceController::faceId() const { return d->faceId; } KDeclarative::ConfigPropertyMap *SensorFaceController::faceConfiguration() const { return d->faceConfiguration; } QQuickItem *SensorFaceController::compactRepresentation() { if (!d->facePackage.isValid()) { return nullptr; } else if (d->compactRepresentation) { return d->compactRepresentation; } d->fullRepresentation = d->createGui(d->facePackage.filePath("ui", QStringLiteral("CompactRepresentation.qml"))); return d->compactRepresentation; } QQuickItem *SensorFaceController::fullRepresentation() { if (!d->facePackage.isValid()) { return nullptr; } else if (d->fullRepresentation) { return d->fullRepresentation; } d->fullRepresentation = d->createGui(d->facePackage.filePath("ui", QStringLiteral("FullRepresentation.qml"))); return d->fullRepresentation; } QQuickItem *SensorFaceController::faceConfigUi() { if (!d->facePackage.isValid()) { return nullptr; } else if (d->faceConfigUi) { return d->faceConfigUi; } d->faceConfigUi = d->createConfigUi(QStringLiteral(":/FaceDetailsConfig.qml"), {{QStringLiteral("controller"), QVariant::fromValue(this)}, {QStringLiteral("source"), d->facePackage.filePath("ui", QStringLiteral("Config.qml"))}}); return d->faceConfigUi; } QQuickItem *SensorFaceController::appearanceConfigUi() { if (d->appearanceConfigUi) { return d->appearanceConfigUi; } d->appearanceConfigUi = d->createConfigUi(QStringLiteral(":/ConfigAppearance.qml"), {{QStringLiteral("controller"), QVariant::fromValue(this)}}); return d->appearanceConfigUi; } QQuickItem *SensorFaceController::sensorsConfigUi() { if (d->sensorsConfigUi) { return d->sensorsConfigUi; } d->sensorsConfigUi = d->createConfigUi(QStringLiteral(":/ConfigSensors.qml"), {{QStringLiteral("controller"), QVariant::fromValue(this)}}); return d->sensorsConfigUi; } QAbstractItemModel *SensorFaceController::availableFacesModel() { if (d->availableFacesModel) { return d->availableFacesModel; } d->availableFacesModel = new FacesModel(this); return d->availableFacesModel; } QAbstractItemModel *SensorFaceController::availablePresetsModel() { if (d->availablePresetsModel) { return d->availablePresetsModel; } d->availablePresetsModel = new PresetsModel(this); return d->availablePresetsModel; } void SensorFaceController::reloadConfig() { if (d->faceConfigLoader) { d->faceConfigLoader->load(); } //Force to re-read all the values setFaceId(d->appearanceGroup.readEntry("chartFace", QStringLiteral("org.kde.ksysguard.textonly"))); titleChanged(); - totalSensorChanged(); + totalSensorsChanged(); highPrioritySensorIdsChanged(); highPrioritySensorColorsChanged(); lowPrioritySensorIdsChanged(); } void SensorFaceController::loadPreset(const QString &preset) { if (preset.isEmpty()) { return; } auto presetPackage = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet")); presetPackage.setPath(preset); if (!presetPackage.isValid()) { return; } if (presetPackage.metadata().value(QStringLiteral("X-Plasma-RootPath")) != QStringLiteral("org.kde.plasma.systemmonitor")) { return; } KDesktopFile df(presetPackage.path() + QStringLiteral("metadata.desktop")); KConfigGroup presetGroup(df.group("Config")); // Load the title setTitle(df.readName()); //Remove the "custon" value from presets models if (d->availablePresetsModel && d->availablePresetsModel->data(d->availablePresetsModel->index(0, 0), PresetsModel::PluginIdRole).toString().isEmpty()) { d->availablePresetsModel->removeRow(0); } auto loadSensors = [this](const QStringList &partialEntries) { QStringList sensors; for (const QString &id : partialEntries) { KSysGuard::SensorQuery query{id}; query.execute(); query.waitForFinished(); sensors.append(query.sensorIds()); } return sensors; }; - setTotalSensor(presetGroup.readEntry(QStringLiteral("totalSensor"), QString())); + setTotalSensors(presetGroup.readEntry(QStringLiteral("totalSensors"), QStringList())); setHighPrioritySensorIds(loadSensors(presetGroup.readEntry(QStringLiteral("highPrioritySensorIds"), QStringList()))); setLowPrioritySensorIds(loadSensors(presetGroup.readEntry(QStringLiteral("lowPrioritySensorIds"), QStringList()))); setHighPrioritySensorColors(presetGroup.readEntry(QStringLiteral("highPrioritySensorColors"), QStringList())); setFaceId(presetGroup.readEntry(QStringLiteral("chartFace"), QStringLiteral("org.kde.ksysguard.piechart"))); if (d->faceConfigLoader) { presetGroup = KConfigGroup(df.group("FaceConfig")); for (const QString &key : presetGroup.keyList()) { KConfigSkeletonItem *item = d->faceConfigLoader->findItemByName(key); if (item) { if (item->property().type() == QVariant::StringList) { item->setProperty(presetGroup.readEntry(key, QStringList())); } else { item->setProperty(presetGroup.readEntry(key)); } d->faceConfigLoader->save(); d->faceConfigLoader->read(); } } } } void SensorFaceController::savePreset() { QString pluginName = QStringLiteral("org.kde.plasma.systemmonitor.") + title().simplified().replace(QLatin1Char(' '), QChar()).toLower(); int suffix = 0; auto presetPackage = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet")); presetPackage.setPath(pluginName); if (presetPackage.isValid()) { do { presetPackage.setPath(QString()); presetPackage.setPath(pluginName + QString::number(++suffix)); } while (presetPackage.isValid()); pluginName += QString::number(suffix); } QTemporaryDir dir; if (!dir.isValid()) { return; } KConfig c(dir.path() % QLatin1Literal("/metadata.desktop")); KConfigGroup cg(&c, "Desktop Entry"); cg.writeEntry("Name", title()); cg.writeEntry("Icon", "ksysguardd"); cg.writeEntry("X-Plasma-API", "declarativeappletscript"); cg.writeEntry("X-Plasma-MainScript", "ui/main.qml"); cg.writeEntry("X-Plasma-Provides", "org.kde.plasma.systemmonitor"); cg.writeEntry("X-Plasma-RootPath", "org.kde.plasma.systemmonitor"); cg.writeEntry("X-KDE-PluginInfo-Name", pluginName); cg.writeEntry("X-KDE-ServiceTypes", "Plasma/Applet"); cg.writeEntry("X-KDE-PluginInfo-Category", "System Information"); cg.writeEntry("X-KDE-PluginInfo-License", "LGPL 2.1+"); cg.writeEntry("X-KDE-PluginInfo-EnabledByDefault", "true"); cg.writeEntry("X-KDE-PluginInfo-Version", "0.1"); cg.sync(); KConfigGroup configGroup(&c, "Config"); - configGroup.writeEntry(QStringLiteral("totalSensor"), totalSensor()); + configGroup.writeEntry(QStringLiteral("totalSensors"), totalSensors()); configGroup.writeEntry(QStringLiteral("highPrioritySensorIds"), highPrioritySensorIds()); configGroup.writeEntry(QStringLiteral("lowPrioritySensorIds"), lowPrioritySensorIds()); configGroup.writeEntry(QStringLiteral("highPrioritySensorColors"), highPrioritySensorColors()); auto *job = presetPackage.install(dir.path()); connect(job, &KJob::finished, this, [this, pluginName] () { d->availablePresetsModel->reload(); }); } void SensorFaceController::uninstallPreset(const QString &pluginId) { auto presetPackage = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"), pluginId); if (presetPackage.metadata().value(QStringLiteral("X-Plasma-RootPath")) != QStringLiteral("org.kde.plasma.systemmonitor")) { return; } QDir root(presetPackage.path()); root.cdUp(); auto *job = presetPackage.uninstall(pluginId, root.path()); connect(job, &KJob::finished, this, [this] () { d->availablePresetsModel->reload(); }); } #include "moc_SensorFaceController.cpp" diff --git a/faces/SensorFaceController.h b/faces/SensorFaceController.h index f204bf5..d8dfc4d 100644 --- a/faces/SensorFaceController.h +++ b/faces/SensorFaceController.h @@ -1,128 +1,128 @@ /* Copyright (C) 2020 Marco Martin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #pragma once #include #include #include #include #include #include "sensorfaces_export.h" namespace KDeclarative { class ConfigPropertyMap; } class QQmlEngine; class KDesktopFile; class KConfigLoader; namespace KSysGuard { class SensorFace; class SENSORFACES_EXPORT SensorFaceController : public QObject { Q_OBJECT Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) Q_PROPERTY(QString faceId READ faceId WRITE setFaceId NOTIFY faceIdChanged) - Q_PROPERTY(QString totalSensor READ totalSensor WRITE setTotalSensor NOTIFY totalSensorChanged) + Q_PROPERTY(QStringList totalSensors READ totalSensors WRITE setTotalSensors NOTIFY totalSensorsChanged) Q_PROPERTY(QStringList highPrioritySensorIds READ highPrioritySensorIds WRITE setHighPrioritySensorIds NOTIFY highPrioritySensorIdsChanged) Q_PROPERTY(QStringList highPrioritySensorColors READ highPrioritySensorColors WRITE setHighPrioritySensorColors NOTIFY highPrioritySensorColorsChanged) Q_PROPERTY(QStringList lowPrioritySensorIds READ lowPrioritySensorIds WRITE setLowPrioritySensorIds NOTIFY lowPrioritySensorIdsChanged) Q_PROPERTY(QString name READ name NOTIFY faceIdChanged) Q_PROPERTY(QString icon READ icon NOTIFY faceIdChanged) Q_PROPERTY(bool supportsSensorsColors READ supportsSensorsColors NOTIFY faceIdChanged) - Q_PROPERTY(bool supportsTotalSensor READ supportsTotalSensor NOTIFY faceIdChanged) + Q_PROPERTY(bool supportsTotalSensors READ supportsTotalSensors NOTIFY faceIdChanged) Q_PROPERTY(bool supportsLowPrioritySensors READ supportsLowPrioritySensors NOTIFY faceIdChanged) Q_PROPERTY(KDeclarative::ConfigPropertyMap *faceConfiguration READ faceConfiguration NOTIFY faceIdChanged) Q_PROPERTY(QQuickItem *fullRepresentation READ fullRepresentation NOTIFY faceIdChanged) Q_PROPERTY(QQuickItem *compactRepresentation READ compactRepresentation NOTIFY faceIdChanged) Q_PROPERTY(QQuickItem *faceConfigUi READ faceConfigUi NOTIFY faceIdChanged) Q_PROPERTY(QQuickItem *appearanceConfigUi READ appearanceConfigUi NOTIFY faceIdChanged) Q_PROPERTY(QQuickItem *sensorsConfigUi READ sensorsConfigUi NOTIFY faceIdChanged) Q_PROPERTY(QAbstractItemModel *availableFacesModel READ availableFacesModel CONSTANT) Q_PROPERTY(QAbstractItemModel *availablePresetsModel READ availablePresetsModel CONSTANT) public: SensorFaceController(KConfigGroup &config, QQmlEngine *engine); ~SensorFaceController(); void setFaceId(const QString &face); QString faceId() const; QQuickItem *fullRepresentation(); QQuickItem *compactRepresentation(); QQuickItem *faceConfigUi(); QQuickItem *appearanceConfigUi(); QQuickItem *sensorsConfigUi(); KDeclarative::ConfigPropertyMap *faceConfiguration() const; QString title() const; void setTitle(const QString &title); - QString totalSensor() const; - void setTotalSensor(const QString &sensor); + QStringList totalSensors() const; + void setTotalSensors(const QStringList &sensor); QStringList highPrioritySensorIds() const; void setHighPrioritySensorIds(const QStringList &ids); QStringList highPrioritySensorColors() const; void setHighPrioritySensorColors(const QStringList &colors); QStringList lowPrioritySensorIds() const; void setLowPrioritySensorIds(const QStringList &ids); // from face config, immutable by the user QString name() const; const QString icon() const; bool supportsSensorsColors() const; - bool supportsTotalSensor() const; + bool supportsTotalSensors() const; bool supportsLowPrioritySensors() const; QAbstractItemModel *availableFacesModel(); QAbstractItemModel *availablePresetsModel(); Q_INVOKABLE void reloadConfig(); Q_INVOKABLE void loadPreset(const QString &preset); Q_INVOKABLE void savePreset(); Q_INVOKABLE void uninstallPreset(const QString &pluginId); Q_SIGNALS: void faceIdChanged(); void titleChanged(); - void totalSensorChanged(); + void totalSensorsChanged(); void highPrioritySensorIdsChanged(); void highPrioritySensorColorsChanged(); void lowPrioritySensorIdsChanged(); private: class Private; const std::unique_ptr d; }; } diff --git a/faces/facepackages/piechart/contents/ui/PieChart.qml b/faces/facepackages/piechart/contents/ui/PieChart.qml index 90db944..f0746b7 100644 --- a/faces/facepackages/piechart/contents/ui/PieChart.qml +++ b/faces/facepackages/piechart/contents/ui/PieChart.qml @@ -1,82 +1,82 @@ /* * Copyright 2019 Marco Martin * Copyright 2019 David Edmundson * Copyright 2019 Arjen Hiemstra * Copyright 2019 Kai Uwe Broulik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Layouts 1.1 import org.kde.kirigami 2.8 as Kirigami import org.kde.ksysguard.sensors 1.0 as Sensors import org.kde.ksysguard.faces 1.0 as Faces import org.kde.quickcharts 1.0 as Charts import org.kde.quickcharts.controls 1.0 as ChartControls ChartControls.PieChartControl { id: chart property alias headingSensor: sensor.sensorId property alias sensors: sensorsModel.sensors property alias sensorsModel: sensorsModel Layout.minimumWidth: root.formFactor != Faces.SensorFace.Vertical ? Kirigami.Units.gridUnit * 4 : Kirigami.Units.gridUnit Layout.minimumHeight: root.formFactor == Faces.SensorFace.Vertical ? width : Kirigami.Units.gridUnit leftPadding: 0 rightPadding: 0 topPadding: 0 bottomPadding: 0 chart.smoothEnds: root.controller.faceConfiguration.smoothEnds chart.fromAngle: root.controller.faceConfiguration.fromAngle chart.toAngle: root.controller.faceConfiguration.toAngle range { from: root.controller.faceConfiguration.rangeFrom to: root.controller.faceConfiguration.rangeTo automatic: root.controller.faceConfiguration.rangeAuto } chart.backgroundColor: Qt.rgba(0.0, 0.0, 0.0, 0.2) text: sensor.formattedValue valueSources: Charts.ModelSource { model: Sensors.SensorDataModel { id: sensorsModel sensors: root.controller.highPrioritySensorIds } roleName: "Value" indexColumns: true } chart.nameSource: Charts.ModelSource { roleName: "ShortName"; model: valueSources[0].model; indexColumns: true } chart.colorSource: root.colorSource Sensors.Sensor { id: sensor - sensorId: root.controller.totalSensor + sensorId: root.controller.totalSensors.length > 0 ? root.controller.totalSensors[0] : "" } }