Warning, /plasma/plasma-workspace/applets/batterymonitor/package/contents/ui/main.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2011 Sebastian Kügler <sebas@kde.org>
0003     SPDX-FileCopyrightText: 2011 Viranch Mehta <viranch.mehta@gmail.com>
0004     SPDX-FileCopyrightText: 2013-2015 Kai Uwe Broulik <kde@privat.broulik.de>
0005     SPDX-FileCopyrightText: 2021-2022 ivan tkachenko <me@ratijas.tk>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 import QtQuick
0011 import QtQuick.Layouts
0012 
0013 import org.kde.coreaddons as KCoreAddons
0014 import org.kde.kcmutils // KCMLauncher
0015 import org.kde.config // KAuthorized
0016 import org.kde.notification
0017 import org.kde.plasma.core as PlasmaCore
0018 import org.kde.plasma.plasma5support as P5Support
0019 import org.kde.plasma.plasmoid
0020 import org.kde.kirigami as Kirigami
0021 import org.kde.kitemmodels as KItemModels
0022 
0023 import "logic.js" as Logic
0024 
0025 PlasmoidItem {
0026     id: batterymonitor
0027 
0028     property QtObject pmSource: P5Support.DataSource {
0029         id: pmSource
0030         engine: "powermanagement"
0031         connectedSources: sources
0032         onSourceAdded: source => {
0033             disconnectSource(source);
0034             connectSource(source);
0035         }
0036         onSourceRemoved: source => {
0037             disconnectSource(source);
0038         }
0039         onDataChanged: {
0040             Logic.updateInhibitions(batterymonitor, pmSource);
0041         }
0042     }
0043     property QtObject batteries: KItemModels.KSortFilterProxyModel {
0044         id: batteries
0045         filterRoleName: "Is Power Supply"
0046         sortOrder: Qt.DescendingOrder
0047         sourceModel: KItemModels.KSortFilterProxyModel {
0048             sortRoleName: "Pretty Name"
0049             sortOrder: Qt.AscendingOrder
0050             sortCaseSensitivity: Qt.CaseInsensitive
0051             sourceModel: P5Support.DataModel {
0052                 dataSource: pmSource
0053                 sourceFilter: "Battery[0-9]+"
0054             }
0055         }
0056     }
0057 
0058     readonly property bool hasBatteries: batteries.count > 0 && pmSource.data["Battery"]["Has Cumulative"]
0059     readonly property bool kcmAuthorized: KAuthorized.authorizeControlModule("powerdevilprofilesconfig")
0060     readonly property bool kcmEnergyInformationAuthorized: KAuthorized.authorizeControlModule("kcm_energyinfo")
0061     readonly property bool isPluggedIn: pmSource.data["AC Adapter"]["Plugged in"]
0062     readonly property bool isSomehowFullyCharged: (pmSource.data["AC Adapter"]["Plugged in"] && pmSource.data["Battery"]["State"] === "FullyCharged") ||
0063                                                    // When we are using a charge threshold, the kernel
0064                                                    // may stop charging within a percentage point of the actual threshold
0065                                                    // and this is considered correct behavior, so we have to handle
0066                                                    // that. See https://bugzilla.kernel.org/show_bug.cgi?id=215531.
0067                                                    (pmSource.data["AC Adapter"]["Plugged in"]
0068                                                    && typeof pmSource.data["Battery"]["Charge Stop Threshold"] === "number"
0069                                                    && (pmSource.data.Battery.Percent  >= pmSource.data["Battery"]["Charge Stop Threshold"] - 1
0070                                                        && pmSource.data.Battery.Percent  <= pmSource.data["Battery"]["Charge Stop Threshold"] + 1)
0071                                                    // Also, Upower may give us a status of "Not charging" rather than
0072                                                    // "Fully charged", so we need to account for that as well. See
0073                                                    // https://gitlab.freedesktop.org/upower/upower/-/issues/142.
0074                                                    && (pmSource.data["Battery"]["State"] === "NoCharge" || pmSource.data["Battery"]["State"] === "FullyCharged"))
0075     readonly property int remainingTime: Number(pmSource.data["Battery"]["Smoothed Remaining msec"])
0076 
0077     readonly property var profiles: pmSource.data["Power Profiles"] ? (pmSource.data["Power Profiles"]["Profiles"] || []) : []
0078     property bool isManuallyInPerformanceMode: false // to be set on power profile requested through the applet
0079     property bool isManuallyInPowerSaveMode: false // to be set on power profile requested through the applet
0080     readonly property bool isSomehowInPerformanceMode: actuallyActiveProfile === "performance"// Don't care about whether it was manually one or due to holds
0081     readonly property bool isSomehowInPowerSaveMode: actuallyActiveProfile === "power-saver" // Don't care about whether it was manually one or due to holds
0082     readonly property bool isHeldOnPerformanceMode: isSomehowInPerformanceMode && activeProfileHolds.length > 0
0083     readonly property bool isHeldOnPowerSaveMode: isSomehowInPowerSaveMode && activeProfileHolds.length > 0
0084 
0085     readonly property bool inPanel: (Plasmoid.location === PlasmaCore.Types.TopEdge
0086         || Plasmoid.location === PlasmaCore.Types.RightEdge
0087         || Plasmoid.location === PlasmaCore.Types.BottomEdge
0088         || Plasmoid.location === PlasmaCore.Types.LeftEdge)
0089 
0090     property bool powermanagementDisabled: false
0091 
0092     // List of active power management inhibitions (applications that are
0093     // blocking sleep and screen locking).
0094     //
0095     // type: [{
0096     //  Icon: string,
0097     //  Name: string,
0098     //  Reason: string,
0099     // }]
0100     property var inhibitions: []
0101     property bool manuallyInhibited: false
0102     readonly property var activeProfileHolds: pmSource.data["Power Profiles"] ? (pmSource.data["Power Profiles"]["Profile Holds"] || []) : []
0103     readonly property string actuallyActiveProfile: pmSource.data["Power Profiles"] ? (pmSource.data["Power Profiles"]["Current Profile"] || "") : ""
0104 
0105     function symbolicizeIconName(iconName) {
0106         const symbolicSuffix = "-symbolic";
0107         if (iconName.endsWith(symbolicSuffix)) {
0108             return iconName;
0109         }
0110 
0111         return iconName + symbolicSuffix;
0112     }
0113 
0114     switchWidth: Kirigami.Units.gridUnit * 10
0115     switchHeight: Kirigami.Units.gridUnit * 10
0116 
0117     Plasmoid.title: hasBatteries ? i18n("Power and Battery") : i18n("Power Management")
0118 
0119     LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft
0120     LayoutMirroring.childrenInherit: true
0121 
0122     Plasmoid.status: {
0123         if (powermanagementDisabled) {
0124             return PlasmaCore.Types.ActiveStatus;
0125         }
0126 
0127         if (pmSource.data.Battery["Has Cumulative"] && pmSource.data["Battery"]["State"] === "Discharging") {
0128             return PlasmaCore.Types.ActiveStatus;
0129         }
0130 
0131         if (isManuallyInPerformanceMode || isManuallyInPowerSaveMode || isHeldOnPerformanceMode || isHeldOnPowerSaveMode) {
0132             return PlasmaCore.Types.ActiveStatus;
0133         }
0134 
0135         return PlasmaCore.Types.PassiveStatus;
0136     }
0137 
0138     toolTipMainText: {
0139         if (!hasBatteries) {
0140             return Plasmoid.title
0141         } else if (isSomehowFullyCharged) {
0142             return i18n("Fully Charged");
0143         }
0144 
0145         const percent = pmSource.data.Battery.Percent;
0146         if (pmSource.data["AC Adapter"] && pmSource.data["AC Adapter"]["Plugged in"]) {
0147             const state = pmSource.data.Battery.State;
0148             if (state === "NoCharge") {
0149                 return i18n("Battery at %1%, not Charging", percent);
0150             } else if (state === "Discharging") {
0151                 return i18n("Battery at %1%, plugged in but still discharging", percent);
0152             } else if (state === "Charging") {
0153                 return i18n("Battery at %1%, Charging", percent);
0154             }
0155         }
0156         return i18n("Battery at %1%", percent);
0157     }
0158 
0159     toolTipSubText: {
0160         const parts = [];
0161 
0162         // Add special text for the "plugged in but still discharging" case
0163         if (pmSource.data["AC Adapter"] && pmSource.data["AC Adapter"]["Plugged in"] && pmSource.data.Battery.State === "Discharging") {
0164             parts.push(i18n("The power supply is not powerful enough to charge the battery"));
0165         }
0166 
0167         if (batteries.count === 0) {
0168             parts.push(i18n("No Batteries Available"));
0169         } else if (remainingTime > 0) {
0170             const remainingTimeString = KCoreAddons.Format.formatDuration(remainingTime, KCoreAddons.FormatTypes.HideSeconds);
0171             if (pmSource.data["Battery"]["State"] === "FullyCharged") {
0172                 // Don't add anything
0173             } else if (pmSource.data["AC Adapter"] && pmSource.data["AC Adapter"]["Plugged in"] && pmSource.data.Battery.State === "Charging") {
0174                 parts.push(i18nc("time until fully charged - HH:MM","%1 until fully charged", remainingTimeString));
0175             } else {
0176                 parts.push(i18nc("remaining time left of battery usage - HH:MM","%1 remaining", remainingTimeString));
0177             }
0178         } else if (pmSource.data.Battery.State === "NoCharge" && !isSomehowFullyCharged) {
0179             parts.push(i18n("Not charging"));
0180         } // otherwise, don't add anything
0181 
0182         if (powermanagementDisabled) {
0183             parts.push(i18n("Automatic sleep and screen locking are disabled"));
0184         }
0185 
0186         if (isSomehowInPerformanceMode) {
0187             if (isHeldOnPerformanceMode) {
0188                 parts.push(i18np("An application has requested activating Performance mode",
0189                                  "%1 applications have requested activating Performance mode",
0190                                  activeProfileHolds.length));
0191             } else {
0192                 parts.push(i18n("System is in Performance mode"));
0193             }
0194         } else if (isSomehowInPowerSaveMode) {
0195             if (isHeldOnPowerSaveMode) {
0196                 parts.push(i18np("An application has requested activating Power Save mode",
0197                                 "%1 applications have requested activating Power Save mode",
0198                                 activeProfileHolds.length));
0199             } else {
0200                 parts.push(i18n("System is in Power Save mode"));
0201             }
0202         }
0203 
0204         return parts.join("\n");
0205     }
0206 
0207     Plasmoid.icon: {
0208         let iconName;
0209         if (hasBatteries) {
0210             iconName = "battery-full";
0211         } else {
0212             iconName = "battery-profile-performance";
0213         }
0214 
0215         if (inPanel) {
0216             return symbolicizeIconName(iconName);
0217         }
0218 
0219         return iconName;
0220     }
0221 
0222     compactRepresentation: CompactRepresentation {
0223         hasBatteries: batterymonitor.hasBatteries
0224         batteries: batterymonitor.batteries
0225         isSetToPerformanceMode: batterymonitor.isHeldOnPerformanceMode || batterymonitor.isManuallyInPerformanceMode
0226         isSetToPowerSaveMode: batterymonitor.isHeldOnPowerSaveMode || batterymonitor.isManuallyInPowerSaveMode
0227         isSomehowFullyCharged: batterymonitor.isSomehowFullyCharged
0228     }
0229 
0230     fullRepresentation: PopupDialog {
0231         id: dialogItem
0232 
0233         readonly property var appletInterface: batterymonitor
0234 
0235         Layout.minimumWidth: Kirigami.Units.gridUnit * 10
0236         Layout.maximumWidth: Kirigami.Units.gridUnit * 80
0237         Layout.preferredWidth: Kirigami.Units.gridUnit * 20
0238 
0239         Layout.minimumHeight: Kirigami.Units.gridUnit * 10
0240         Layout.maximumHeight: Kirigami.Units.gridUnit * 40
0241         Layout.preferredHeight: implicitHeight
0242 
0243         model: batteries
0244 
0245         pluggedIn: pmSource.data["AC Adapter"] !== undefined && pmSource.data["AC Adapter"]["Plugged in"]
0246         remainingTime: batterymonitor.remainingTime
0247         activeProfile: batterymonitor.actuallyActiveProfile
0248         inhibitions: batterymonitor.inhibitions
0249         manuallyInhibited: batterymonitor.manuallyInhibited
0250         inhibitsLidAction: pmSource.data["PowerDevil"] && pmSource.data["PowerDevil"]["Is Lid Present"] && !pmSource.data["PowerDevil"]["Triggers Lid Action"] ? true : false
0251         profilesInstalled: pmSource.data["Power Profiles"] ? pmSource.data["Power Profiles"]["Installed"] : false
0252         profiles: pmSource.data["Power Profiles"] ? (pmSource.data["Power Profiles"]["Profiles"] || []) : []
0253         inhibitionReason: pmSource.data["Power Profiles"] ? (pmSource.data["Power Profiles"]["Performance Inhibited Reason"] || "") : ""
0254         degradationReason: pmSource.data["Power Profiles"] ? (pmSource.data["Power Profiles"]["Performance Degraded Reason"] || "") : ""
0255         profileHolds: batterymonitor.activeProfileHolds
0256 
0257         onInhibitionChangeRequested: inhibit => {
0258             const service = pmSource.serviceForSource("PowerDevil");
0259             if (inhibit) {
0260                 const reason = i18n("The battery applet has enabled system-wide inhibition");
0261                 const op1 = service.operationDescription("beginSuppressingSleep");
0262                 op1.reason = reason;
0263                 const op2 = service.operationDescription("beginSuppressingScreenPowerManagement");
0264                 op2.reason = reason;
0265 
0266                 const job1 = service.startOperationCall(op1);
0267                 const job2 = service.startOperationCall(op2);
0268             } else {
0269                 const op1 = service.operationDescription("stopSuppressingSleep");
0270                 const op2 = service.operationDescription("stopSuppressingScreenPowerManagement");
0271 
0272                 const job1 = service.startOperationCall(op1);
0273                 const job2 = service.startOperationCall(op2);
0274             }
0275             Logic.updateInhibitions(batterymonitor, pmSource);
0276         }
0277         onPowerManagementChanged: disabled => {
0278             batterymonitor.powermanagementDisabled = disabled
0279         }
0280 
0281         Notification {
0282             id: powerProfileError
0283             componentName: "plasma_workspace"
0284             eventId: "warning"
0285             iconName: "speedometer"
0286             title: i18n("Power Management")
0287         }
0288 
0289         onActivateProfileRequested: profile => {
0290             dialogItem.activeProfile = profile;
0291             const service = pmSource.serviceForSource("PowerDevil");
0292             const op = service.operationDescription("setPowerProfile");
0293             op.profile = profile;
0294 
0295             const job = service.startOperationCall(op);
0296             job.finished.connect(job => {
0297                 dialogItem.activeProfile = Qt.binding(() => actuallyActiveProfile);
0298                 if (!job.result) {
0299                     powerProfileError.text = i18n("Failed to activate %1 mode", profile);
0300                     powerProfileError.sendEvent();
0301                     return;
0302                 }
0303                 batterymonitor.isManuallyInPerformanceMode = profile == "performance";
0304                 batterymonitor.isManuallyInPowerSaveMode = profile == "power-saver";
0305             });
0306         }
0307     }
0308 
0309     Plasmoid.contextualActions: [
0310         PlasmaCore.Action {
0311             text: i18n("&Show Energy Information…")
0312             icon.name: "documentinfo"
0313             visible: batterymonitor.kcmEnergyInformationAuthorized
0314             onTriggered: KCMLauncher.openInfoCenter("kcm_energyinfo")
0315         },
0316         PlasmaCore.Action {
0317             text: i18n("Show Battery Percentage on Icon When Not Fully Charged")
0318             icon.name: "format-number-percent"
0319             checkable: true
0320             checked: Plasmoid.configuration.showPercentage
0321             onTriggered: checked => {
0322                 Plasmoid.configuration.showPercentage = checked
0323             }
0324         }
0325     ]
0326 
0327     PlasmaCore.Action {
0328         id: configureAction
0329         text: i18n("&Configure Power Management…")
0330         icon.name: "configure"
0331         shortcut: "alt+d, s"
0332         onTriggered: {
0333             KCMLauncher.openSystemSettings("kcm_powerdevilprofilesconfig");
0334         }
0335     }
0336 
0337     Component.onCompleted: {
0338         Logic.updateInhibitions(batterymonitor, pmSource)
0339 
0340         Plasmoid.setInternalAction("configure", configureAction);
0341     }
0342 }