diff --git a/PkUpdateList.cxx b/PkUpdateList.cxx
index d06b859..8f12d50 100644
--- a/PkUpdateList.cxx
+++ b/PkUpdateList.cxx
@@ -1,609 +1,653 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 - 2019 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
liquidshell is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with liquidshell. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
+#include
+#include
#include
#include
#include
//#define TEST_LOGOUT
//#define TEST_REBOOT
//--------------------------------------------------------------------------------
PkUpdateListItem::PkUpdateListItem(QWidget *parent, PackageKit::Transaction::Info info, const PkUpdates::PackageData &data)
: QWidget(parent), package(data)
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
QGridLayout *grid = new QGridLayout(this);
grid->setContentsMargins(QMargins());
grid->setSpacing(0);
checkBox = new QCheckBox;
checkBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
grid->addWidget(checkBox, 0, 0);
QString icon;
switch ( info )
{
case PackageKit::Transaction::InfoSecurity: icon = "update-high"; break;
case PackageKit::Transaction::InfoImportant: icon = "update-medium"; break;
case PackageKit::Transaction::InfoBugfix: icon = "update-low"; break;
default: ;
}
QLabel *iconLabel = new QLabel;
iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
iconLabel->setPixmap(QIcon::fromTheme(icon).pixmap(16));
grid->addWidget(iconLabel, 0, 1);
checkBox->setChecked(!icon.isEmpty()); // auto-check the important ones
connect(checkBox, &QCheckBox::toggled, this, &PkUpdateListItem::toggled);
label = new QToolButton;
label->setAutoRaise(true);
label->setText(package.summary + " [" +
PackageKit::Daemon::packageName(package.id) + ", " +
PackageKit::Daemon::packageVersion(package.id) + ']');
grid->addWidget(label, 0, 2, Qt::AlignLeft);
detailsLabel = new QLabel;
detailsLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
detailsLabel->setIndent(30);
detailsLabel->setAlignment(Qt::AlignLeft);
grid->addWidget(detailsLabel, 1, 2);
detailsLabel->hide();
connect(label, &QToolButton::clicked, this, &PkUpdateListItem::getUpdateDetails);
{
QHBoxLayout *progressHbox = new QHBoxLayout;
progressHbox->setSpacing(10);
progress = new QProgressBar;
progress->setFixedWidth(300);
progressHbox->addWidget(progress);
errorLabel = new QLabel;
errorLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
errorLabel->hide();
progressHbox->addWidget(errorLabel);
packageLabel = new QLabel;
packageLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
packageLabel->hide();
progressHbox->addWidget(packageLabel);
grid->addLayout(progressHbox, 2, 2, Qt::AlignLeft);
showProgress(false);
}
}
//--------------------------------------------------------------------------------
void PkUpdateListItem::showProgress(bool yes)
{
progress->setValue(0);
progress->setVisible(yes);
}
//--------------------------------------------------------------------------------
void PkUpdateListItem::getUpdateDetails()
{
if ( !detailsLabel->isHidden() )
{
detailsLabel->hide();
return;
}
//qDebug() << "getUpdateDetails" << package.id;
PackageKit::Transaction *transaction = PackageKit::Daemon::getUpdateDetail(package.id);
detailsLabel->setText(i18n("Getting details ..."));
detailsLabel->show();
connect(transaction, &PackageKit::Transaction::updateDetail, this, &PkUpdateListItem::updateDetail);
connect(transaction, &PackageKit::Transaction::errorCode, this,
[this](PackageKit::Transaction::Error error, const QString &details)
{
Q_UNUSED(error)
detailsLabel->setText(details);
});
}
//--------------------------------------------------------------------------------
void PkUpdateListItem::updateDetail(const QString &packageID,
const QStringList &updates,
const QStringList &obsoletes,
const QStringList &vendorUrls,
const QStringList &bugzillaUrls,
const QStringList &cveUrls,
PackageKit::Transaction::Restart restart,
const QString &updateText,
const QString &changelog,
PackageKit::Transaction::UpdateState state,
const QDateTime &issued,
const QDateTime &updated)
{
Q_UNUSED(packageID)
Q_UNUSED(updates)
Q_UNUSED(obsoletes)
Q_UNUSED(vendorUrls)
Q_UNUSED(bugzillaUrls)
Q_UNUSED(cveUrls)
Q_UNUSED(changelog)
Q_UNUSED(state)
Q_UNUSED(issued)
Q_UNUSED(updated)
QString text;
if ( restart == PackageKit::Transaction::RestartSession )
text = i18n("Session restart required
");
else if ( restart == PackageKit::Transaction::RestartSystem )
text = i18n("Reboot required
");
text += updateText.trimmed();
text.replace("\n", "
");
detailsLabel->setText(text);
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
PkUpdateList::PkUpdateList(QWidget *parent)
: QWidget(parent), restart(PackageKit::Transaction::RestartNone)
{
setWindowFlags(windowFlags() | Qt::Tool);
setWindowTitle(i18n("Software Updates"));
vbox = new QVBoxLayout(this);
vbox->setContentsMargins(QMargins(0, -1, 0, 0));
// action buttons
QHBoxLayout *hbox = new QHBoxLayout;
- QCheckBox *checkAll = new QCheckBox(i18n("All"));
- connect(checkAll, &QCheckBox::toggled, this, &PkUpdateList::checkAll);
+ checkAllBox = new QCheckBox(i18n("All"));
+ connect(checkAllBox, &QCheckBox::toggled, this, &PkUpdateList::checkAll);
filterEdit = new QLineEdit;
filterEdit->setPlaceholderText(i18n("Filter"));
filterEdit->setClearButtonEnabled(true);
connect(filterEdit, &QLineEdit::textEdited, this, &PkUpdateList::filterChanged);
+ // create "busy" indicator
+ progressBar = new QProgressBar;
+
installButton = new QPushButton(i18n("Install"));
installButton->setEnabled(false);
connect(installButton, &QPushButton::clicked, this, &PkUpdateList::install);
refreshButton = new QPushButton(i18n("Refresh"));
connect(refreshButton, &QPushButton::clicked, this, [this]() { setPackages(PkUpdates::PackageList()); emit refreshRequested(); });
- hbox->addWidget(checkAll);
+ hbox->addWidget(checkAllBox);
hbox->addWidget(filterEdit);
+ hbox->addWidget(progressBar);
hbox->addWidget(installButton);
hbox->addWidget(refreshButton);
vbox->addLayout(hbox);
// list of items in the order: security, important, bugfix, others
scrollArea = new QScrollArea;
scrollArea->setWidgetResizable(true);
vbox->addWidget(scrollArea);
QWidget *w = new QWidget;
vbox = new QVBoxLayout(w);
itemsLayout = new QVBoxLayout;
itemsLayout->setSpacing(0);
vbox->addLayout(itemsLayout);
vbox->addStretch();
scrollArea->setWidget(w);
}
//--------------------------------------------------------------------------------
QSize PkUpdateList::sizeHint() const
{
QSize s = scrollArea->widget()->sizeHint() + QSize(2 * scrollArea->frameWidth(), 2 * scrollArea->frameWidth());
s.setWidth(s.width() + scrollArea->verticalScrollBar()->sizeHint().width());
s.setHeight(layout()->contentsMargins().top() + installButton->sizeHint().height() +
((layout()->spacing() == -1) ? style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing) : layout()->spacing()) +
s.height() + layout()->contentsMargins().bottom());
+
+ s = s.expandedTo(QSize(500, 300));
+
+ // Qt doc: The size of top-level widgets are constrained to 2/3 of the desktop's height and width.
+ QSize screen = QApplication::primaryScreen()->availableSize();
+ s = s.boundedTo(screen * 2 / 3);
+
return s;
}
//--------------------------------------------------------------------------------
+void PkUpdateList::setRefreshProgress(int progress)
+{
+ if ( progress >= 100 )
+ {
+ filterEdit->show();
+ progressBar->hide();
+ refreshButton->setEnabled(true);
+ }
+ else if ( progress == 0 )
+ {
+ // we don't know if there will be a value between 0..100
+ // Until the first value arrives, use a busy indicator
+ progressBar->setMinimum(0);
+ progressBar->setMaximum(0);
+ progressBar->setValue(0);
+ progressBar->show();
+ filterEdit->hide();
+ refreshButton->setEnabled(false);
+ }
+ else
+ {
+ progressBar->setMinimum(0);
+ progressBar->setMaximum(100);
+ progressBar->setValue(progress);
+ }
+}
+
+//--------------------------------------------------------------------------------
+
void PkUpdateList::setPackages(const PkUpdates::PackageList &packages)
{
QLayoutItem *child;
while ( (child = itemsLayout->takeAt(0)) )
{
delete child->widget();
delete child;
}
+ checkAllBox->setChecked(false);
+
QList list = packages.values(PackageKit::Transaction::InfoSecurity);
for (const PkUpdates::PackageData &data : list)
{
auto *item = new PkUpdateListItem(itemsLayout->parentWidget(), PackageKit::Transaction::InfoSecurity, data);
connect(item, &PkUpdateListItem::toggled, this, &PkUpdateList::countChecked);
itemsLayout->addWidget(item);
item->show();
}
list = packages.values(PackageKit::Transaction::InfoImportant);
for (const PkUpdates::PackageData &data : list)
{
auto *item = new PkUpdateListItem(itemsLayout->parentWidget(), PackageKit::Transaction::InfoImportant, data);
connect(item, &PkUpdateListItem::toggled, this, &PkUpdateList::countChecked);
itemsLayout->addWidget(item);
item->show();
}
list = packages.values(PackageKit::Transaction::InfoBugfix);
for (const PkUpdates::PackageData &data : list)
{
auto *item = new PkUpdateListItem(itemsLayout->parentWidget(), PackageKit::Transaction::InfoBugfix, data);
connect(item, &PkUpdateListItem::toggled, this, &PkUpdateList::countChecked);
itemsLayout->addWidget(item);
item->show();
}
// all the others
for (PkUpdates::PackageList::const_iterator it = packages.constBegin(); it != packages.constEnd(); ++it)
{
if ( (it.key() != PackageKit::Transaction::InfoSecurity) &&
(it.key() != PackageKit::Transaction::InfoImportant) &&
(it.key() != PackageKit::Transaction::InfoBugfix) )
{
auto *item = new PkUpdateListItem(itemsLayout->parentWidget(), it.key(), it.value());
connect(item, &PkUpdateListItem::toggled, this, &PkUpdateList::countChecked);
itemsLayout->addWidget(item);
item->show();
}
}
filterChanged(filterEdit->text());
countChecked();
}
//--------------------------------------------------------------------------------
void PkUpdateList::checkAll(bool on)
{
for (int i = 0; i < itemsLayout->count(); i++)
{
PkUpdateListItem *item = qobject_cast(itemsLayout->itemAt(i)->widget());
if ( item && !item->isHidden() )
{
item->checkBox->blockSignals(true);
item->checkBox->setChecked(on);
item->checkBox->blockSignals(false);
}
}
countChecked();
}
//--------------------------------------------------------------------------------
void PkUpdateList::countChecked()
{
int count = 0;
for (int i = 0; i < itemsLayout->count(); i++)
{
PkUpdateListItem *item = qobject_cast(itemsLayout->itemAt(i)->widget());
if ( item && !item->isHidden() && item->checkBox->isChecked() )
count++;
}
if ( count )
{
installButton->setText(i18np("Install %1 package", "Install %1 packages", count));
installButton->setEnabled(true);
}
else
{
installButton->setText(i18n("Install"));
installButton->setEnabled(false);
}
installButton->setIcon(QIcon());
refreshButton->setEnabled(true);
}
//--------------------------------------------------------------------------------
void PkUpdateList::filterChanged(const QString &text)
{
itemsLayout->parentWidget()->layout()->setEnabled(false);
for (int i = 0; i < itemsLayout->count(); i++)
{
PkUpdateListItem *item = qobject_cast(itemsLayout->itemAt(i)->widget());
item->setVisible(text.isEmpty() || (item->label->text().indexOf(text, 0, Qt::CaseInsensitive) != -1));
}
itemsLayout->parentWidget()->layout()->setEnabled(true);
countChecked();
}
//--------------------------------------------------------------------------------
void PkUpdateList::install()
{
if ( !installQ.isEmpty() ) // installation in progress; cancel it
{
QPointer currentItem = installQ.head();
if ( transaction )
{
QDBusPendingReply<> reply = transaction->cancel();
reply.waitForFinished();
if ( reply.isError() && currentItem )
{
currentItem->errorLabel->setText(reply.error().message());
currentItem->errorLabel->show();
}
}
for (int i = 0; i < itemsLayout->count(); i++)
{
PkUpdateListItem *item = qobject_cast(itemsLayout->itemAt(i)->widget());
if ( !transaction || (item != currentItem) )
{
item->showProgress(false);
item->errorLabel->hide();
}
}
installQ.clear();
countChecked();
return;
}
restart = PackageKit::Transaction::RestartNone;
for (int i = 0; i < itemsLayout->count(); i++)
{
QPointer item = qobject_cast(itemsLayout->itemAt(i)->widget());
if ( item && !item->isHidden() && item->checkBox->isChecked() )
{
item->showProgress(true);
item->errorLabel->hide();
item->detailsLabel->hide();
installQ.enqueue(item);
}
}
installOne();
}
//--------------------------------------------------------------------------------
void PkUpdateList::installOne()
{
if ( installQ.isEmpty() ) // installation finished
{
if ( restart != PackageKit::Transaction::RestartNone )
{
QString text;
if ( (restart == PackageKit::Transaction::RestartSystem) ||
(restart == PackageKit::Transaction::RestartSecuritySystem) )
{
KNotification *notif = new KNotification("restart needed", parentWidget(), KNotification::Persistent);
notif->setTitle(i18n("System Reboot Required"));
notif->setText(i18n("One of the installed packages requires a system reboot"));
notif->setActions(QStringList() << i18n("Reboot System"));
connect(notif, &KNotification::action1Activated, this,
[]()
{
QDBusMessage msg = QDBusMessage::createMethodCall("org.kde.ksmserver", "/KSMServer",
"org.kde.KSMServerInterface", "logout");
msg << 0/*no confirm*/ << 1/*reboot*/ << 0; // plasma-workspace/libkworkspace/kworkspace.h
QDBusConnection::sessionBus().send(msg);
});
notif->sendEvent();
}
else if ( (restart == PackageKit::Transaction::RestartSession) ||
(restart == PackageKit::Transaction::RestartSecuritySession) )
{
KNotification *notif = new KNotification("restart needed", parentWidget(), KNotification::Persistent);
notif->setTitle(i18n("Session Restart Required"));
notif->setText(i18n("One of the installed packages requires you to logout"));
notif->setActions(QStringList() << i18n("Logout"));
connect(notif, &KNotification::action1Activated, this,
[]()
{
QDBusMessage msg = QDBusMessage::createMethodCall("org.kde.ksmserver", "/KSMServer",
"org.kde.KSMServerInterface", "logout");
msg << 0/*no confirm*/ << 0/*logout*/ << 0; // plasma-workspace/libkworkspace/kworkspace.h
QDBusConnection::sessionBus().send(msg);
});
notif->sendEvent();
}
}
countChecked();
return;
}
QPointer item = installQ.head();
if ( !item )
return;
installButton->setText(i18n("Cancel Installation"));
installButton->setIcon(QIcon::fromTheme("process-stop"));
refreshButton->setEnabled(false);
PackageKit::Transaction::TransactionFlag flag = PackageKit::Transaction::TransactionFlagOnlyTrusted;
#ifdef TEST_LOGOUT
flag |= PackageKit::Transaction::TransactionFlagSimulate;
restart = PackageKit::Transaction::RestartSession;
#elif defined TEST_REBOOT
flag |= PackageKit::Transaction::TransactionFlagSimulate;
restart = PackageKit::Transaction::RestartSystem;
#endif
transaction = PackageKit::Daemon::updatePackage(item->package.id, flag);
packageNoLongerAvailable = false;
//qDebug() << "installing" << item->package.id;
connect(transaction.data(), &PackageKit::Transaction::statusChanged, this,
[item, this]()
{
if ( !item ) // already deleted
return;
//qDebug() << "status" << QMetaEnum::fromType().valueToKey(transaction->status());
QString text;
switch ( transaction->status() )
{
case PackageKit::Transaction::StatusWait: text = i18n("Waiting"); break;
case PackageKit::Transaction::StatusWaitingForAuth: text = i18n("Waiting for authentication"); break;
case PackageKit::Transaction::StatusDepResolve: text = i18n("Resolving dependencies"); break;
case PackageKit::Transaction::StatusUpdate: text = i18n("Updating"); break;
case PackageKit::Transaction::StatusInstall: text = i18n("Installing"); break;
case PackageKit::Transaction::StatusDownload: text = i18n("Downloading"); break;
case PackageKit::Transaction::StatusCancel: text = i18n("Canceling"); break;
case PackageKit::Transaction::StatusFinished: return; // don't hide error label
default: return;
}
if ( text.isEmpty() )
item->errorLabel->hide();
else
{
item->errorLabel->setText(text);
item->errorLabel->show();
}
});
connect(transaction.data(), &PackageKit::Transaction::itemProgress, this,
[item](const QString &itemID, PackageKit::Transaction::Status status, uint percentage)
{
Q_UNUSED(status)
if ( !item ) // already deleted
return;
item->packageLabel->setText(PackageKit::Daemon::packageName(itemID));
item->packageLabel->show();
if ( percentage <= 100 ) // 101 .. unknown
item->progress->setValue(percentage);
});
connect(transaction.data(), &PackageKit::Transaction::requireRestart, this,
[this](PackageKit::Transaction::Restart type, const QString &/*packageID*/)
{
// keep most important restart type: System, Session
if ( (type == PackageKit::Transaction::RestartSystem) ||
(type == PackageKit::Transaction::RestartSecuritySystem) ||
(((type == PackageKit::Transaction::RestartSession) ||
(type == PackageKit::Transaction::RestartSecuritySession)) &&
(restart != PackageKit::Transaction::RestartSystem) &&
(restart != PackageKit::Transaction::RestartSecuritySystem)) )
restart = type;
});
connect(transaction.data(), &PackageKit::Transaction::errorCode, this,
[item, this](PackageKit::Transaction::Error error, const QString &details)
{
if ( !item )
return;
item->showProgress(false);
item->errorLabel->setText(details);
item->errorLabel->show();
//qDebug() << "errorCode" << details << QMetaEnum::fromType().valueToKey(error);
if ( (error == PackageKit::Transaction::ErrorDepResolutionFailed) &&
(transaction->status() == PackageKit::Transaction::StatusSetup) )
{
// when a package was already installed due to another dependency, then it's no more an update candidate.
// Still the finished() signal will arrive which will do cleanup (delete item, dequeue, etc.).
packageNoLongerAvailable = true;
}
});
connect(transaction.data(), &PackageKit::Transaction::finished, this,
[item, this](PackageKit::Transaction::Exit status, uint runtime)
{
Q_UNUSED(runtime)
if ( !item )
return;
if ( (status == PackageKit::Transaction::ExitSuccess) || packageNoLongerAvailable )
{
item->hide();
item->deleteLater();
emit packageInstalled(item->package.id);
}
else
{
item->showProgress(false);
item->detailsLabel->setText(i18n("Update failed"));
item->detailsLabel->show();
}
if ( !installQ.isEmpty() ) // might have been cancelled, q cleared
installQ.dequeue();
installOne();
});
}
//--------------------------------------------------------------------------------
diff --git a/PkUpdateList.hxx b/PkUpdateList.hxx
index 347fd2e..8d15622 100644
--- a/PkUpdateList.hxx
+++ b/PkUpdateList.hxx
@@ -1,113 +1,116 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 - 2019 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
liquidshell is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with liquidshell. If not, see .
*/
#ifndef _PkUpdateList_H_
#define _PkUpdateList_H_
#include
#include
#include
#include
class QCheckBox;
class QProgressBar;
class QVBoxLayout;
class QToolButton;
class QPushButton;
class QLabel;
class QLineEdit;
class PkUpdateList : public QWidget
{
Q_OBJECT
public:
PkUpdateList(QWidget *parent);
void setPackages(const PkUpdates::PackageList &packages);
+ void setRefreshProgress(int progress);
QSize sizeHint() const override;
Q_SIGNALS:
void refreshRequested();
void packageInstalled(QString id);
private Q_SLOTS:
void checkAll(bool on);
void install();
void installOne();
void countChecked();
void filterChanged(const QString &text);
private:
QVBoxLayout *vbox;
QScrollArea *scrollArea;
QVBoxLayout *itemsLayout;
QLineEdit *filterEdit;
+ QProgressBar *progressBar;
QPushButton *installButton;
QPushButton *refreshButton;
+ QCheckBox *checkAllBox;
QQueue> installQ;
QPointer transaction;
bool packageNoLongerAvailable;
PackageKit::Transaction::Restart restart;
};
//--------------------------------------------------------------------------------
class PkUpdateListItem : public QWidget
{
Q_OBJECT
public:
PkUpdateListItem(QWidget *parent, PackageKit::Transaction::Info info, const PkUpdates::PackageData &data);
void showProgress(bool yes);
PkUpdates::PackageData package;
QToolButton *label;
QCheckBox *checkBox;
QProgressBar *progress;
QToolButton *cancelButton;
QLabel *detailsLabel;
QLabel *errorLabel;
QLabel *packageLabel;
Q_SIGNALS:
void toggled();
private Q_SLOTS:
void getUpdateDetails();
void updateDetail(const QString &packageID,
const QStringList &updates,
const QStringList &obsoletes,
const QStringList &vendorUrls,
const QStringList &bugzillaUrls,
const QStringList &cveUrls,
PackageKit::Transaction::Restart restart,
const QString &updateText,
const QString &changelog,
PackageKit::Transaction::UpdateState state,
const QDateTime &issued,
const QDateTime &updated);
};
#endif
diff --git a/PkUpdates.cxx b/PkUpdates.cxx
index 2931e35..9bf8d75 100644
--- a/PkUpdates.cxx
+++ b/PkUpdates.cxx
@@ -1,288 +1,306 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017,2019 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
liquidshell is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with liquidshell. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//--------------------------------------------------------------------------------
PkUpdates::PkUpdates(QWidget *parent)
: SysTrayItem(parent)
{
KConfig config;
KConfigGroup group = config.group("SoftwareUpdates");
if ( !group.hasKey("enabled") ) // create config entry so that one knows it exists
group.writeEntry("enabled", true);
bool isEnabled = group.readEntry("enabled", true);
if ( !isEnabled )
{
hide();
return;
}
setPixmap(QIcon::fromTheme("system-software-update").pixmap(size()));
connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, this, [this]() { createToolTip(); });
// check every hour if the next checkpoint was reached. This ensures that
// we check for updates even when the computer was suspended for a while
// and therefore a normal QTimer timeout for 1 day will not be reached in 1 day
// TODO check again. start() was missing
updateTimer.setInterval(3600 * 1000);
updateTimer.start();
connect(&updateTimer, &QTimer::timeout, this, &PkUpdates::checkForUpdatesReached);
//QTimer::singleShot(0, this, &PkUpdates::checkForUpdatesReached);
}
//--------------------------------------------------------------------------------
void PkUpdates::checkForUpdatesReached()
{
QDateTime current = QDateTime::currentDateTime();
if ( !nextCheck.isValid() || (current >= nextCheck) )
{
checkForUpdates();
nextCheck = current.addDays(1);
}
}
//--------------------------------------------------------------------------------
void PkUpdates::checkForUpdates()
{
setPixmap(QIcon::fromTheme("system-software-update").pixmap(size()));
setToolTip(i18n("Checking for updates ..."));
packages.clear();
+ setRefreshProgress(0);
PackageKit::Transaction *transaction = PackageKit::Daemon::refreshCache(true);
connect(transaction, &PackageKit::Transaction::errorCode, this, &PkUpdates::transactionError);
connect(transaction, &PackageKit::Transaction::finished, this, &PkUpdates::refreshFinished);
}
//--------------------------------------------------------------------------------
void PkUpdates::refreshFinished(PackageKit::Transaction::Exit status, uint runtime)
{
Q_UNUSED(runtime)
Q_UNUSED(status)
// don't stop on exit error; it could e.g. only be an error on one of the repos
//if ( status != PackageKit::Transaction::ExitSuccess )
//return;
PackageKit::Transaction *transaction = PackageKit::Daemon::getUpdates();
connect(transaction, &PackageKit::Transaction::package, this, &PkUpdates::package);
connect(transaction, &PackageKit::Transaction::errorCode, this, &PkUpdates::transactionError);
connect(transaction, &PackageKit::Transaction::finished, this,
[this]()
{
if ( updateList )
updateList->setPackages(packages);
+ setRefreshProgress(100);
createToolTip(true);
});
connect(transaction, &PackageKit::Transaction::percentageChanged, this,
[this, transaction]()
{
if ( (transaction->percentage() <= 100) && (transaction->status() != PackageKit::Transaction::StatusFinished) )
- setToolTip(i18n("Checking for updates ... %1%", transaction->percentage()));
+ {
+ setRefreshProgress(transaction->percentage());
+ setToolTip(i18n("Checking for updates ... %1%", refreshProgress));
+ }
});
}
//--------------------------------------------------------------------------------
void PkUpdates::package(PackageKit::Transaction::Info info, const QString &packageID, const QString &summary)
{
PackageData pkg;
pkg.id = packageID;
pkg.summary = summary;
packages.insert(info, pkg);
}
//--------------------------------------------------------------------------------
void PkUpdates::transactionError(PackageKit::Transaction::Error error, const QString &details)
{
Q_UNUSED(error)
setToolTip(i18n("Last check: %1\nError on checking for updates: %2",
QDateTime::currentDateTime().toString(Qt::SystemLocaleShortDate), details));
KNotification::event("update error", i18n("Software Update Error"), details,
QIcon::fromTheme("dialog-error").pixmap(32), this);
+
+ setRefreshProgress(100);
}
//--------------------------------------------------------------------------------
void PkUpdates::createToolTip(bool notify)
{
PackageKit::Transaction::Info info = PackageKit::Transaction::InfoUnknown;
QString tooltip;
int count = 0;
QList list = packages.values(PackageKit::Transaction::InfoSecurity);
if ( list.count() )
{
info = PackageKit::Transaction::InfoSecurity;
count += list.count();
tooltip += i18np("%1 security update available", "%1 security updates available", list.count());
addItems(tooltip, list);
}
list = packages.values(PackageKit::Transaction::InfoImportant);
if ( list.count() )
{
if ( info == PackageKit::Transaction::InfoUnknown )
info = PackageKit::Transaction::InfoImportant;
count += list.count();
tooltip += i18np("%1 important update available", "%1 important updates available", list.count());
addItems(tooltip, list);
}
list = packages.values(PackageKit::Transaction::InfoBugfix);
if ( list.count() )
{
if ( info == PackageKit::Transaction::InfoUnknown )
info = PackageKit::Transaction::InfoBugfix;
count += list.count();
tooltip += i18np("%1 bugfix update available", "%1 bugfix updates available", list.count());
addItems(tooltip, list);
}
int others = packages.count() - count;
if ( tooltip.isEmpty() )
{
if ( others )
{
setToolTip(i18np("Last check: %1\nNo important updates available\n%2 other",
"Last check: %1\nNo important updates available\n%2 others",
QDateTime::currentDateTime().toString(Qt::SystemLocaleShortDate), others));
}
else
{
setToolTip(i18n("Last check: %1\nNo important updates available",
QDateTime::currentDateTime().toString(Qt::SystemLocaleShortDate)));
}
setPixmap(QIcon::fromTheme("update-none").pixmap(size()));
}
else
{
if ( others )
tooltip += i18np("
%1 other", "
%1 others", others);
tooltip = i18n("Last check: %1
%2",
QDateTime::currentDateTime().toString(Qt::SystemLocaleShortDate), tooltip);
setToolTip(tooltip);
QString icon;
switch ( info )
{
case PackageKit::Transaction::InfoSecurity: icon = "update-high"; break;
case PackageKit::Transaction::InfoImportant: icon = "update-medium"; break;
case PackageKit::Transaction::InfoBugfix: icon = "update-low"; break;
default: ;
}
setPixmap(QIcon::fromTheme(icon).pixmap(size()));
if ( notify )
{
KNotification::event("updates available", i18n("Software Updates Available"), tooltip,
QIcon::fromTheme(icon).pixmap(32), this, KNotification::Persistent);
}
}
}
//--------------------------------------------------------------------------------
void PkUpdates::addItems(QString &tooltip, const QList &list) const
{
tooltip += "";
int count = std::min(3, list.count());
if ( list.count() == 4 ) // if there's just one more, show it directly instead of "1 more"
count++;
for (int i = 0; i < count; i++)
tooltip += "- " + list[i].summary + "
";
if ( list.count() > 4 )
tooltip += i18n("- %1 more ...
", list.count() - count);
tooltip += "
";
}
//--------------------------------------------------------------------------------
QWidget *PkUpdates::getDetailsList()
{
if ( !updateList )
{
updateList = new PkUpdateList(this);
updateList->setPackages(packages);
+ updateList->setRefreshProgress(refreshProgress);
connect(updateList, &PkUpdateList::refreshRequested, this, &PkUpdates::checkForUpdates);
connect(updateList, &PkUpdateList::packageInstalled, this, &PkUpdates::packageInstalled);
}
return updateList;
}
//--------------------------------------------------------------------------------
void PkUpdates::packageInstalled(const QString &id)
{
for (PackageList::iterator it = packages.begin(); it != packages.end(); ++it)
{
if ( (*it).id == id )
{
packages.erase(it);
break;
}
}
createToolTip();
}
//--------------------------------------------------------------------------------
+
+void PkUpdates::setRefreshProgress(int progress)
+{
+ refreshProgress = progress;
+
+ if ( updateList )
+ updateList->setRefreshProgress(refreshProgress);
+}
+
+//--------------------------------------------------------------------------------
diff --git a/PkUpdates.hxx b/PkUpdates.hxx
index 045abdb..652fbc0 100644
--- a/PkUpdates.hxx
+++ b/PkUpdates.hxx
@@ -1,71 +1,73 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
liquidshell is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with liquidshell. If not, see .
*/
#ifndef _PkUpdates_H_
#define _PkUpdates_H_
// software updates via PackageKit
#include
#include
#include
#include
#include
class PkUpdateList;
class PkUpdates : public SysTrayItem
{
Q_OBJECT
public:
PkUpdates(QWidget *parent);
struct PackageData
{
QString id;
QString summary;
};
typedef QMultiMap PackageList;
protected:
QWidget *getDetailsList() override;
private Q_SLOTS:
void checkForUpdatesReached();
void checkForUpdates();
void refreshFinished(PackageKit::Transaction::Exit status, uint runtime);
void package(PackageKit::Transaction::Info info, const QString &packageID, const QString &summary);
void transactionError(PackageKit::Transaction::Error error, const QString &details);
void packageInstalled(const QString &id);
private:
void addItems(QString &tooltip, const QList &list) const;
void createToolTip(bool notify = false);
+ void setRefreshProgress(int progress);
private:
PackageList packages;
QTimer updateTimer;
QDateTime nextCheck;
PkUpdateList *updateList = nullptr;
+ int refreshProgress = 100;
};
#endif
diff --git a/SysTrayItem.cxx b/SysTrayItem.cxx
index 1b526c1..68caf63 100644
--- a/SysTrayItem.cxx
+++ b/SysTrayItem.cxx
@@ -1,90 +1,90 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 - 2019 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
liquidshell is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with liquidshell. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
//--------------------------------------------------------------------------------
SysTrayItem::SysTrayItem(QWidget *parent, const QString &icon)
: QLabel(parent), iconName(icon)
{
setFixedSize(QSize(22, 22));
if ( !iconName.isEmpty() )
{
setPixmap(QIcon::fromTheme(iconName).pixmap(size()));
connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, this,
[this]() { setPixmap(QIcon::fromTheme(iconName).pixmap(size())); });
}
}
//--------------------------------------------------------------------------------
void SysTrayItem::mousePressEvent(QMouseEvent *event)
{
if ( event->button() != Qt::LeftButton )
return;
toggleDetailsList();
}
//--------------------------------------------------------------------------------
void SysTrayItem::showDetailsList()
{
QWidget *detailsList = getDetailsList();
if ( !detailsList )
return;
QPoint point = mapToGlobal(pos());
QRect screen = QApplication::primaryScreen()->availableGeometry();
- point.setX(std::min(point.x(), screen.x() + screen.width() - detailsList->width()));
- point.setY(screen.bottom() - detailsList->height());
+ point.setX(std::min(point.x(), screen.x() + screen.width() - detailsList->sizeHint().width()));
+ point.setY(screen.bottom() - detailsList->sizeHint().height());
detailsList->move(point);
detailsList->show();
KWindowSystem::raiseWindow(detailsList->winId());
}
//--------------------------------------------------------------------------------
void SysTrayItem::toggleDetailsList()
{
QWidget *detailsList = getDetailsList();
if ( !detailsList )
return;
if ( detailsList->isVisible() )
detailsList->close();
else
showDetailsList();
}
//--------------------------------------------------------------------------------