File indexing completed on 2024-03-24 17:23:05

0001 // SPDX-License-Identifier: GPL-3.0-or-later
0002 /*
0003   Copyright 2017 - 2023 Martin Koller, kollix@aon.at
0004 
0005   This file is part of liquidshell.
0006 
0007   liquidshell is free software: you can redistribute it and/or modify
0008   it under the terms of the GNU General Public License as published by
0009   the Free Software Foundation, either version 3 of the License, or
0010   (at your option) any later version.
0011 
0012   liquidshell is distributed in the hope that it will be useful,
0013   but WITHOUT ANY WARRANTY; without even the implied warranty of
0014   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0015   GNU General Public License for more details.
0016 
0017   You should have received a copy of the GNU General Public License
0018   along with liquidshell.  If not, see <http://www.gnu.org/licenses/>.
0019 */
0020 
0021 #include <Battery.hxx>
0022 
0023 #include <QIcon>
0024 #include <QDBusConnection>
0025 #include <QDBusMessage>
0026 #include <QDBusVariant>
0027 #include <QDebug>
0028 
0029 #include <Solid/Battery>
0030 
0031 #include <KLocalizedString>
0032 #include <KIconLoader>
0033 #include <kcmutils_version.h>
0034 
0035 #include <cmath>
0036 
0037 //--------------------------------------------------------------------------------
0038 
0039 Battery::Battery(QWidget *parent)
0040   : SysTrayItem(parent)
0041 {
0042   QList<Solid::Device> devices = Solid::Device::listFromType(Solid::DeviceInterface::Battery);
0043 
0044   for (Solid::Device dev : devices)
0045   {
0046     if ( dev.is<Solid::Battery>() && (dev.as<Solid::Battery>()->type() == Solid::Battery::PrimaryBattery) )
0047     {
0048       device = dev;
0049       break;
0050     }
0051   }
0052 
0053   if ( !device.isValid() )
0054     hide();
0055   else
0056   {
0057     QDBusConnection::systemBus()
0058       .connect("org.freedesktop.UPower",
0059                "/org/freedesktop/UPower",
0060                "org.freedesktop.DBus.Properties",
0061                "PropertiesChanged",
0062                this,
0063                SLOT(upowerPropertiesChanged(QString, QVariantMap, QStringList)));
0064 
0065     QDBusMessage msg =
0066       QDBusMessage::createMethodCall("org.freedesktop.UPower",
0067                                      "/org/freedesktop/UPower",
0068                                      "org.freedesktop.DBus.Properties",
0069                                      "Get");
0070     msg << QLatin1String("org.freedesktop.UPower") << QLatin1String("OnBattery");
0071     QDBusConnection::systemBus().callWithCallback(msg, this, SLOT(onBatteryReply(QDBusMessage)), nullptr);
0072 
0073     connect(device.as<Solid::Battery>(), &Solid::Battery::chargePercentChanged, this, &Battery::changed);
0074     connect(device.as<Solid::Battery>(), &Solid::Battery::chargeStateChanged, this, &Battery::changed);
0075     connect(device.as<Solid::Battery>(), &Solid::Battery::timeToFullChanged, this, &Battery::changed);
0076     connect(device.as<Solid::Battery>(), &Solid::Battery::timeToEmptyChanged, this, &Battery::changed);
0077 
0078     connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, this, &Battery::changed);
0079 
0080     changed();
0081   }
0082 }
0083 
0084 //--------------------------------------------------------------------------------
0085 
0086 QString Battery::secsToHM(int secs) const
0087 {
0088   int h = secs / 3600;
0089   int m = (secs % 3600) / 60;
0090 
0091   QString hStr = i18np("%1 hour", "%1 hours", h);
0092   QString mStr = i18np("%1 minute", "%1 minutes", m);
0093 
0094   QString result;
0095 
0096   if ( h )
0097     result = hStr;
0098 
0099   if ( m )
0100   {
0101     if ( h )
0102       result += ", ";
0103 
0104     result += mStr;
0105   }
0106 
0107   return result;
0108 }
0109 
0110 //--------------------------------------------------------------------------------
0111 
0112 void Battery::onBatteryReply(const QDBusMessage &msg)
0113 {
0114   onBattery = msg.arguments()[0].value<QDBusVariant>().variant().toBool();
0115   changed();
0116 }
0117 
0118 //--------------------------------------------------------------------------------
0119 
0120 void Battery::upowerPropertiesChanged(const QString &interface,
0121                                       const QVariantMap &properties,
0122                                       const QStringList &invalidated)
0123 {
0124   Q_UNUSED(interface)
0125   Q_UNUSED(invalidated)
0126 
0127   if ( properties.contains("OnBattery") )
0128   {
0129     onBattery = properties.value("OnBattery").toBool();
0130     changed();
0131   }
0132 }
0133 
0134 //--------------------------------------------------------------------------------
0135 
0136 QIcon Battery::getStatusIcon(int charge, bool isCharging)
0137 {
0138   QString iconName;
0139 
0140   int p = qRound(charge / 20.0) * 20;
0141 
0142   if ( p < 20 )
0143     iconName = "caution";
0144   else if ( p < 40 )
0145     iconName = "low";
0146   else
0147     iconName = QString("%1").arg(p, 3, 10, QLatin1Char('0'));
0148 
0149   iconName = QString("battery%1-%2").arg(isCharging ? "-charging" : "").arg(iconName);
0150 
0151   return QIcon::fromTheme(iconName);
0152 }
0153 
0154 //--------------------------------------------------------------------------------
0155 
0156 void Battery::changed()
0157 {
0158   Solid::Battery *battery = device.as<Solid::Battery>();
0159 
0160   QString tip;
0161 
0162   switch ( battery->chargeState() )
0163   {
0164     case Solid::Battery::NoCharge: tip = i18n("Not Charging"); break;
0165     case Solid::Battery::FullyCharged: tip = i18n("Fully Charged"); break;
0166 
0167     case Solid::Battery::Charging:
0168     case Solid::Battery::Discharging:
0169     {
0170       if ( battery->chargeState() == Solid::Battery::Charging )
0171       {
0172         tip = i18n("Charging at %1%", battery->chargePercent());
0173         if ( battery->timeToFull() )  // it can be 0, so we don't know
0174           tip += '\n' + i18n("Time until full: ") + secsToHM(battery->timeToFull());
0175       }
0176       else
0177       {
0178         tip = i18n("Discharging at %1%", battery->chargePercent());
0179         if ( battery->timeToEmpty() )  // it can be 0, so we don't know
0180           tip += '\n' + i18n("Remaining Time: ") + secsToHM(battery->timeToEmpty());
0181       }
0182 
0183       break;
0184     }
0185   }
0186 
0187   setPixmap(getStatusIcon(battery->chargePercent(),
0188                           battery->chargeState() == Solid::Battery::Charging).pixmap(size()));
0189   setToolTip(tip);
0190 
0191   setVisible(onBattery || (battery->chargeState() != Solid::Battery::FullyCharged));
0192 }
0193 
0194 //--------------------------------------------------------------------------------
0195 
0196 QWidget *Battery::getDetailsList()
0197 {
0198   if ( !dialog )
0199   {
0200     dialog = new KCMultiDialog(this);
0201     dialog->setAttribute(Qt::WA_DeleteOnClose);
0202 
0203     // different KDE versions need different ways ...
0204     KPluginMetaData data("plasma/kcms/systemsettings_qwidgets/kcm_powerdevilglobalconfig");
0205 
0206     if ( data.isValid() )
0207     {
0208       dialog->addModule(data);
0209       dialog->addModule(KPluginMetaData("plasma/kcms/systemsettings_qwidgets/kcm_powerdevilprofilesconfig"));
0210     }
0211     else
0212     {
0213       dialog->addModule("powerdevilglobalconfig");
0214       dialog->addModule("powerdevilprofilesconfig");
0215     }
0216 
0217     dialog->adjustSize();
0218     dialog->setWindowTitle(i18n("Power Management"));
0219   }
0220 
0221   return dialog;
0222 }
0223 
0224 //--------------------------------------------------------------------------------