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 }