File indexing completed on 2024-04-14 05:39:37

0001 // SPDX-License-Identifier: GPL-3.0-or-later
0002 /*
0003   Copyright 2017,2019 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 <PkUpdates.hxx>
0022 #include <PkUpdateList.hxx>
0023 
0024 #include <QIcon>
0025 #include <QDateTime>
0026 #include <QPainter>
0027 #include <QDebug>
0028 
0029 #include <KLocalizedString>
0030 #include <KIconLoader>
0031 #include <KConfig>
0032 #include <KConfigGroup>
0033 
0034 //--------------------------------------------------------------------------------
0035 
0036 PkUpdates::PkUpdates(QWidget *parent)
0037   : SysTrayItem(parent)
0038 {
0039   KConfig config;
0040   KConfigGroup group = config.group("SoftwareUpdates");
0041   if ( !group.hasKey("enabled") )  // create config entry so that one knows it exists
0042     group.writeEntry("enabled", true);
0043 
0044   bool isEnabled = group.readEntry("enabled", true);
0045 
0046   if ( !isEnabled )
0047   {
0048     hide();
0049     return;
0050   }
0051 
0052   setPixmap(currentPixmap = QIcon::fromTheme("system-software-update").pixmap(size()));
0053   connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, this, [this]() { createToolTip(); });
0054 
0055   // check every hour if the next checkpoint was reached. This ensures that
0056   // we check for updates even when the computer was suspended for a while
0057   // and therefore a normal QTimer timeout for 1 day will not be reached in 1 day
0058   // TODO check again. start() was missing
0059   updateTimer.setInterval(3600 * 1000);
0060   updateTimer.start();
0061   connect(&updateTimer, &QTimer::timeout, this, &PkUpdates::checkForUpdatesReached);
0062 
0063   QString nextCheck = QDateTime::currentDateTime()
0064                           .addMSecs(updateTimer.interval())
0065                           .toString(Qt::SystemLocaleShortDate);
0066   setToolTip(i18n("Next check: %1", nextCheck));
0067 
0068   //QTimer::singleShot(0, this, &PkUpdates::checkForUpdatesReached);
0069 }
0070 
0071 //--------------------------------------------------------------------------------
0072 
0073 void PkUpdates::checkForUpdatesReached()
0074 {
0075   if ( updateList && updateList->isInstallInProgress() )
0076     return;
0077 
0078   QDateTime current = QDateTime::currentDateTime();
0079 
0080   if ( !nextCheck.isValid() || (current >= nextCheck) )
0081   {
0082     checkForUpdates();
0083     nextCheck = current.addDays(1);
0084   }
0085 }
0086 
0087 //--------------------------------------------------------------------------------
0088 
0089 void PkUpdates::checkForUpdates()
0090 {
0091   setPixmap(currentPixmap = QIcon::fromTheme("system-software-update").pixmap(size()));
0092   setToolTip(i18n("Checking for updates ..."));
0093 
0094   packages.clear();
0095   setRefreshProgress(0);
0096 
0097   PackageKit::Transaction *transaction = PackageKit::Daemon::refreshCache(true);
0098 
0099   connect(transaction, &PackageKit::Transaction::errorCode, this, &PkUpdates::transactionError);
0100   connect(transaction, &PackageKit::Transaction::finished, this, &PkUpdates::refreshFinished);
0101 
0102   connect(transaction, &PackageKit::Transaction::percentageChanged, this,
0103           [this, transaction]()
0104           {
0105             if ( (transaction->percentage() <= 100) && (transaction->status() != PackageKit::Transaction::StatusFinished) )
0106             {
0107               setRefreshProgress(transaction->percentage() / 2);  // first step: refresh cache, second: get updates
0108               setToolTip(i18n("Checking for updates ... %1%", refreshProgress));
0109             }
0110           });
0111 }
0112 
0113 //--------------------------------------------------------------------------------
0114 
0115 void PkUpdates::refreshFinished(PackageKit::Transaction::Exit status, uint runtime)
0116 {
0117   Q_UNUSED(runtime)
0118   Q_UNUSED(status)
0119 
0120   // don't stop on exit error; it could e.g. only be an error on one of the repos
0121   //if ( status != PackageKit::Transaction::ExitSuccess )
0122     //return;
0123 
0124   /////////////////
0125   // hmm ... does that do anything on openSuse ? seems not implemented there
0126   /*
0127   {
0128     PackageKit::Transaction *transaction = PackageKit::Daemon::getDistroUpgrades();
0129 
0130     connect(transaction, &PackageKit::Transaction::errorCode, this, &PkUpdates::transactionError);
0131 
0132     connect(transaction, &PackageKit::Transaction::distroUpgrade, this,
0133             [this](PackageKit::Transaction::DistroUpgrade type, const QString &name, const QString &description)
0134             {
0135               qDebug() << "distroUpgrade" << type << name << description;
0136             });
0137   }
0138   */
0139   /////////////////
0140 
0141   PackageKit::Transaction *transaction = PackageKit::Daemon::getUpdates();
0142 
0143   connect(transaction, &PackageKit::Transaction::package, this, &PkUpdates::package);
0144   connect(transaction, &PackageKit::Transaction::errorCode, this, &PkUpdates::transactionError);
0145 
0146   connect(transaction, &PackageKit::Transaction::finished, this,
0147           [this]()
0148           {
0149             if ( updateList )
0150               updateList->setPackages(packages);
0151 
0152             setRefreshProgress(100);
0153             createToolTip(true);
0154           });
0155 
0156   connect(transaction, &PackageKit::Transaction::percentageChanged, this,
0157           [this, transaction]()
0158           {
0159             if ( (transaction->percentage() <= 100) && (transaction->status() != PackageKit::Transaction::StatusFinished) )
0160             {
0161               setRefreshProgress(50 + (transaction->percentage() / 2));  // second step in checking for updates
0162               setToolTip(i18n("Checking for updates ... %1%", refreshProgress));
0163             }
0164           });
0165 }
0166 
0167 //--------------------------------------------------------------------------------
0168 
0169 void PkUpdates::package(PackageKit::Transaction::Info info, const QString &packageID, const QString &summary)
0170 {
0171   PackageData pkg;
0172   pkg.id = packageID;
0173   pkg.summary = summary;
0174 
0175   packages.insert(info, pkg);
0176 }
0177 
0178 //--------------------------------------------------------------------------------
0179 
0180 void PkUpdates::transactionError(PackageKit::Transaction::Error error, const QString &details)
0181 {
0182   Q_UNUSED(error)
0183 
0184   setToolTip(i18n("Last check: %1\nError on checking for updates: %2",
0185                   QDateTime::currentDateTime().toString(Qt::SystemLocaleShortDate), details));
0186 
0187   KNotification::event("update error", i18n("Software Update Error"), details,
0188                        QIcon::fromTheme("dialog-error").pixmap(32), this);
0189 
0190   setRefreshProgress(100);
0191 }
0192 
0193 //--------------------------------------------------------------------------------
0194 
0195 void PkUpdates::createToolTip(bool notify)
0196 {
0197   PackageKit::Transaction::Info info = PackageKit::Transaction::InfoUnknown;
0198   QString tooltip;
0199 
0200   int count = 0;
0201   QList<PackageData> list = packages.values(PackageKit::Transaction::InfoSecurity);
0202 
0203   if ( list.count() )
0204   {
0205     info = PackageKit::Transaction::InfoSecurity;
0206 
0207     count += list.count();
0208     tooltip += i18np("%1 <b>security</b> update available", "%1 <b>security</b> updates available", list.count());
0209     addItems(tooltip, list);
0210   }
0211 
0212   list = packages.values(PackageKit::Transaction::InfoImportant);
0213 
0214   if ( list.count() )
0215   {
0216     if ( info == PackageKit::Transaction::InfoUnknown )
0217       info = PackageKit::Transaction::InfoImportant;
0218 
0219     count += list.count();
0220     tooltip += i18np("%1 <i>important</i> update available", "%1 <i>important</i> updates available", list.count());
0221     addItems(tooltip, list);
0222   }
0223 
0224   list = packages.values(PackageKit::Transaction::InfoBugfix);
0225 
0226   if ( list.count() )
0227   {
0228     if ( info == PackageKit::Transaction::InfoUnknown )
0229       info = PackageKit::Transaction::InfoBugfix;
0230 
0231     count += list.count();
0232     tooltip += i18np("%1 bugfix update available", "%1 bugfix updates available", list.count());
0233     addItems(tooltip, list);
0234   }
0235 
0236   int others = packages.count() - count;
0237 
0238   if ( tooltip.isEmpty() )
0239   {
0240     if ( others )
0241     {
0242       setToolTip(i18np("Last check: %1\nNo important updates available\n%2 other",
0243                        "Last check: %1\nNo important updates available\n%2 others",
0244                        QDateTime::currentDateTime().toString(Qt::SystemLocaleShortDate), others));
0245 
0246       if ( QIcon::hasThemeIcon("software-update-available") )
0247         setPixmap(currentPixmap = QIcon::fromTheme("software-update-available").pixmap(size()));
0248       else
0249         setPixmap(currentPixmap = QIcon::fromTheme("update-low").pixmap(size()));
0250     }
0251     else
0252     {
0253       setToolTip(i18n("Last check: %1\nNo updates available",
0254                       QDateTime::currentDateTime().toString(Qt::SystemLocaleShortDate)));
0255 
0256       setPixmap(currentPixmap = QIcon::fromTheme("update-none").pixmap(size()));
0257     }
0258   }
0259   else
0260   {
0261     if ( others )
0262       tooltip += i18np("<br>%1 other", "<br>%1 others", others);
0263 
0264     tooltip = i18n("<html>Last check: %1<br>%2</html>",
0265                    QDateTime::currentDateTime().toString(Qt::SystemLocaleShortDate), tooltip);
0266     setToolTip(tooltip);
0267 
0268     QString icon;
0269     switch ( info )
0270     {
0271       case PackageKit::Transaction::InfoSecurity:
0272       {
0273         if ( QIcon::hasThemeIcon("software-update-urgent") )
0274           icon = "software-update-urgent";
0275         else
0276           icon = "update-high";
0277         break;
0278       }
0279       case PackageKit::Transaction::InfoImportant:
0280       {
0281         if ( QIcon::hasThemeIcon("software-update-urgent") )
0282           icon = "software-update-urgent";
0283         else
0284           icon = "update-medium";
0285         break;
0286       }
0287       default: // PackageKit::Transaction::InfoBugfix:  // and others
0288       {
0289         if ( QIcon::hasThemeIcon("software-update-available") )
0290           icon = "software-update-available";
0291         else
0292           icon = "update-low";
0293         break;
0294       }
0295     }
0296     setPixmap(currentPixmap = QIcon::fromTheme(icon).pixmap(size()));
0297 
0298     if ( notify )
0299     {
0300       if ( notification )
0301         notification->close();  // close previous notification if it's still here
0302 
0303       notification = KNotification::event("updates available", i18n("Software Updates Available"), tooltip,
0304                                           QIcon::fromTheme(icon).pixmap(32), this);
0305     }
0306   }
0307 }
0308 
0309 //--------------------------------------------------------------------------------
0310 
0311 void PkUpdates::addItems(QString &tooltip, const QList<PackageData> &list) const
0312 {
0313   tooltip += "<ul>";
0314 
0315   int count = std::min(3, list.count());
0316   if ( list.count() == 4 )  // if there's just one more, show it directly instead of "1 more"
0317     count++;
0318 
0319   for (int i = 0; i < count; i++)
0320     tooltip += "<li>" + list[i].summary + "</li>";
0321 
0322   if ( list.count() > 4 )
0323     tooltip += i18n("<li>%1 more ...</li>", list.count() - count);
0324 
0325   tooltip += "</ul>";
0326 }
0327 
0328 //--------------------------------------------------------------------------------
0329 
0330 QWidget *PkUpdates::getDetailsList()
0331 {
0332   if ( !updateList )
0333   {
0334     updateList = new PkUpdateList(this);
0335     updateList->setPackages(packages);
0336     updateList->setRefreshProgress(refreshProgress);
0337     connect(updateList, &PkUpdateList::refreshRequested, this, &PkUpdates::checkForUpdates);
0338     connect(updateList, &PkUpdateList::packageInstalled, this, &PkUpdates::packageInstalled);
0339     connect(updateList, &PkUpdateList::packageCountToInstall, this, &PkUpdates::packageCountToInstallChanged);
0340   }
0341 
0342   return updateList;
0343 }
0344 
0345 //--------------------------------------------------------------------------------
0346 
0347 void PkUpdates::packageInstalled(const QString &id)
0348 {
0349   for (PackageList::iterator it = packages.begin(); it != packages.end(); ++it)
0350   {
0351     if ( (*it).id == id )
0352     {
0353       packages.erase(it);
0354       break;
0355     }
0356   }
0357   createToolTip();
0358 }
0359 
0360 //--------------------------------------------------------------------------------
0361 
0362 void PkUpdates::setRefreshProgress(int progress)
0363 {
0364   refreshProgress = progress;
0365 
0366   if ( updateList )
0367     updateList->setRefreshProgress(refreshProgress);
0368 }
0369 
0370 //--------------------------------------------------------------------------------
0371 
0372 void PkUpdates::packageCountToInstallChanged(int num)
0373 {
0374   if ( num == 0 )
0375   {
0376     setPixmap(currentPixmap);
0377     return;
0378   }
0379 
0380   QPixmap pix = currentPixmap;
0381   QPainter painter(&pix);
0382   QFont f = font();
0383   f.setPixelSize(contentsRect().width() / 2);
0384   f.setBold(true);
0385   painter.setFont(f);
0386 
0387   painter.drawText(contentsRect(), Qt::AlignCenter, QString::number(num));
0388   painter.end();
0389 
0390   setPixmap(pix);
0391 }
0392 
0393 //--------------------------------------------------------------------------------