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 }