Compare commits

...

15 Commits

2
.gitignore vendored

@ -0,0 +1,2 @@
CMakeLists.txt.user
build

@ -9,52 +9,62 @@ set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets LinguistTools)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets LinguistTools)
find_package(Qt6 REQUIRED COMPONENTS Widgets DBus LinguistTools)
set(TS_FILES translations/lubuntu-update_en_US.ts)
set(TS_FILES
src/translations/lubuntu-update_en_US.ts
src/translations/lubuntu-update_es_ES.ts
src/translations/lubuntu-update_zh_CN.ts
)
set(PROJECT_SOURCES
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
orchestrator.h
orchestrator.cpp
aptmanager.h
aptmanager.cpp
conffilewidget.h
conffilewidget.cpp
conffilewidget.ui
diffdisplaydialog.h
diffdisplaydialog.cpp
diffdisplaydialog.ui
conffilehandlerdialog.h
conffilehandlerdialog.cpp
conffilehandlerdialog.ui
ipcfilewatcher.h
ipcfilewatcher.cpp
src/main.cpp
src/mainwindow.cpp
src/mainwindow.h
src/mainwindow.ui
src/orchestrator.h
src/orchestrator.cpp
src/aptmanager.h
src/aptmanager.cpp
src/conffilewidget.h
src/conffilewidget.cpp
src/conffilewidget.ui
src/diffdisplaydialog.h
src/diffdisplaydialog.cpp
src/diffdisplaydialog.ui
src/conffilehandlerdialog.h
src/conffilehandlerdialog.cpp
src/conffilehandlerdialog.ui
src/releaseupgradewindow.h
src/releaseupgradewindow.cpp
src/releaseupgradewindow.ui
src/upgradedelaywindow.h
src/upgradedelaywindow.cpp
src/upgradedelaywindow.ui
src/windowshowwatcher.h
src/windowshowwatcher.cpp
${TS_FILES}
)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(lubuntu-update
MANUAL_FINALIZATION
${PROJECT_SOURCES}
resources.qrc
)
set(TRANSLATION_RESOURCES "src/translations.qrc")
configure_file(${TRANSLATION_RESOURCES} translations.qrc COPYONLY)
qt6_add_translation(QM_FILES ${TS_FILES})
qt6_add_resources(QM_RESOURCES ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
add_custom_target(translations ALL DEPENDS ${QM_FILES})
qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
else()
add_executable(lubuntu-update
${PROJECT_SOURCES}
resources.qrc
${QM_RESOURCES}
src/resources.qrc
)
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
endif()
add_dependencies(lubuntu-update translations)
target_link_libraries(lubuntu-update PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
target_link_libraries(lubuntu-update PRIVATE
Qt6::Widgets
Qt6::DBus)
install(TARGETS lubuntu-update
BUNDLE DESTINATION .
@ -70,8 +80,6 @@ install(TARGETS lubuntu-update
# has gotten into /usr/libexec you're compromised anyway, so not much to worry
# about. Therefore we always, *always* install this script into /usr/libexec/,
# no matter where the rest of the program goes.
install(FILES lubuntu-update-backend DESTINATION /usr/libexec/)
install(FILES src/lubuntu-update-backend DESTINATION /usr/libexec/)
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(lubuntu-update)
endif()
install(FILES lubuntu-update.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications/)

@ -2,7 +2,7 @@
Copyright (c) 2023-2024 Lubuntu Contributors. Licensed under the GNU General Public License version 2, or (at your option) any later version.
Build dependencies are Qt 5.15 and cmake, runtime dependencies are apt, apt-get, and diff.
Build dependencies are Qt 5.15 and cmake, runtime dependencies are apt, apt-get, curl, and diff.
To build:
@ -20,10 +20,39 @@ It is highly recommended that you use a Lubuntu virtual machine for testing and
Qt Creator is recommended for editing the code. It is present in Ubuntu's official repos and can be installed using `sudo apt install qtcreator`.
## Config file format:
There's only one field here:
* nextDoReleaseUpgradeNotify=123456789 - Value is number of seconds since the UNIX epoch. Used to determine when to offer the user an upgrade.
## Missing features
* The Details button is hidden and does nothing. Eventually it should display a list of packages, the old version of them, the new version of them, and a link to their Launchpad page.
* Double-clicking on a package doesn't show detailed information for it yet.
* There's no support for release upgrading. This is currently unnecessary as this updater is only going to be shipped in Noble and later, but it will become a big deal in the (potentially near) future.
* There's no support for doing an `apt update` for checking for recent updates. This seems rather important *now*.
* Most of the internal strings aren't translatable...
* ...and the bit of translation support there is, is totally untested and quite possibly not functioning properly. Borrow from what we did with lubuntu-installer-prompt to fix this.
* The translation support is mostly untested, though it should work.
## Translations
Run the `gen_ts.sh` script after making any code modifications to ensure that the translations files are up-to-date for translators to work on.
To add a new language to be translated:
* Open the `gen_ts.sh` script and add the locale code for the new language to the `langList` array.
* Run the script after doing this - a new template .ts file will be generated under `src/translations/`.
* Next, add the new template file to the `TS_FILES` list in `CMakeLists.txt` - it will be named `src/translations/lubuntu-update_locale_CODE.ts`, where `locale_CODE` is the locale code of the added language.
* Finally, add a line in the src/translations.qrc resource file to include the new translation file. The line should look like `<file alias="locale_CODE">lubuntu-update_locale_CODE.qm</file>`, where `locale_CODE` is the locale code of the added language. This line should go inside the `<qresource>` tag.
For instance, if I were to add Chinese to the list of languages that could be translated into, I would do this:
vim gen_ts.sh
# add this code to the langList array:
# 'zh_CN'
./gen_ts.sh
vim CMakeLists.txt
# add this line to the TS_FILES list:
# src/translations/lubuntu-update_zh_CN.ts
vim src/translations.qrc
# add this line to the list of file resources:
# <file alias="zh_CN">lubuntu-update_zh_CN.qm</file>
The program will now pick up the added language at build time. Any translations added to the newly created .ts file will be shown to program users who select the new language.

46
debian/changelog vendored

@ -1,3 +1,49 @@
lubuntu-update-notifier (1.1.0) oracular; urgency=medium
* Port to Qt6.
-- Aaron Rainbolt <arraybolt3@ubuntu.com> Thu, 15 Aug 2024 16:58:41 -0500
lubuntu-update-notifier (1.0.0) noble; urgency=medium
* Change from beta to final release.
* Use D-Bus as a window show trigger rather than clunky drop files, this
will prevent issues if multiple users are logged in at once.
-- Aaron Rainbolt <arraybolt3@ubuntu.com> Mon, 25 Mar 2024 16:15:49 -0500
lubuntu-update-notifier (1.0.0~beta1) noble; urgency=medium
* Change from alpha to beta testing phase, this has been tested for a while
and looks pretty stable so far.
* Wait to start until lxqt-notificationd is present. (LP: #2056379)
-- Aaron Rainbolt <arraybolt3@ubuntu.com> Thu, 07 Mar 2024 04:06:13 +0000
lubuntu-update-notifier (1.0.0~alpha4) noble; urgency=medium
* Fix infinite loop when no eligible new release is available.
* Fix a bug where only the first eligible new release would be considered.
-- Aaron Rainbolt <arraybolt3@ubuntu.com> Fri, 09 Feb 2024 22:26:59 +0000
lubuntu-update-notifier (1.0.0~alpha3) noble; urgency=medium
* New feature release. Notable additions:
- do-release-upgrade support
* Updated runtime dependencies.
-- Aaron Rainbolt <arraybolt3@ubuntu.com> Fri, 02 Feb 2024 11:37:36 -0600
lubuntu-update-notifier (1.0.0~alpha2) noble; urgency=medium
* New feature release. Notable additions:
- The ability to check for updates directly in the updater has been added.
- Irrelevant package sections are no longer shown.
- Strings are now translatable.
-- Aaron Rainbolt <arraybolt3@ubuntu.com> Fri, 26 Jan 2024 22:11:49 +0000
lubuntu-update-notifier (1.0.0~alpha1) noble; urgency=medium
* Replace the old update notifier with Lubuntu Update.

7
debian/control vendored

@ -5,14 +5,15 @@ Maintainer: Lubuntu Developers <lubuntu-devel@lists.ubuntu.com>
Build-Depends: cmake,
debhelper-compat (= 13),
lxqt-sudo (>= 1.4.0-0ubuntu2),
qtbase5-dev,
qttools5-dev
qt6-base-dev,
qt6-tools-dev
Standards-Version: 4.6.2
Rules-Requires-Root: no
Package: lubuntu-update-notifier
Architecture: any
Depends: ${misc:Depends},
Depends: curl,
${misc:Depends},
${shlibs:Depends}
Description: Lubuntu's update installer
Lubuntu Update is an enhanced, modern update installer for the Lubuntu

2
debian/files vendored

@ -1 +1 @@
lubuntu-update-notifier_1.0.0~alpha1_source.buildinfo admin optional
lubuntu-update-notifier_1.1.0~ppa1_source.buildinfo admin optional

@ -0,0 +1,11 @@
#!/bin/bash
langList=('en_US' 'es_ES' 'zh_CN')
for i in ${langList[@]}; do
targetFile="translations/lubuntu-update_$i.ts"
if [ ! -e $targetFile ]; then
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>" > $targetFile
echo "<!DOCTYPE TS>" >> $targetFile
echo "<TS version=\"2.1\" language=\"$i\"></TS>" >> $targetFile
fi
done
lupdate *.cpp *.h *.ui -ts translations/*

@ -1,38 +0,0 @@
#include "ipcfilewatcher.h"
#include <QApplication>
#include <QDir>
#include <QFile>
#include <QFileSystemWatcher>
#include <QDebug>
IPCFileWatcher::IPCFileWatcher(QObject *parent)
: QObject{parent}
{
QDir targetDir("/dev/shm/lubuntu-update");
bool couldRemove = targetDir.removeRecursively();
if (!couldRemove) {
qCritical() << "Could not clear IPC directory. Ensure that /dev/shm and /dev/shm/lubuntu-update are world-readable and world-writable.";
initFailed = true;
return;
}
targetDir.mkdir("/dev/shm/lubuntu-update");
QFileSystemWatcher *watcher = new QFileSystemWatcher(QStringList() << "/dev/shm/lubuntu-update");
connect(watcher, &QFileSystemWatcher::directoryChanged, this, &IPCFileWatcher::checkForShowWindowFile);
initFailed = false;
}
bool IPCFileWatcher::didInitFail()
{
return initFailed;
}
void IPCFileWatcher::checkForShowWindowFile()
{
QFile flagFile("/dev/shm/lubuntu-update/lubuntu-update-show-win");
if (flagFile.exists()) {
flagFile.remove();
emit showWindowFlagDetected();
}
}

@ -1,22 +0,0 @@
#ifndef IPCFILEWATCHER_H
#define IPCFILEWATCHER_H
#include <QObject>
class IPCFileWatcher : public QObject
{
Q_OBJECT
public:
explicit IPCFileWatcher(QObject *parent = nullptr);
bool didInitFail();
signals:
void showWindowFlagDetected();
private:
bool initFailed;
void checkForShowWindowFile();
};
#endif // IPCFILEWATCHER_H

@ -1,92 +0,0 @@
#!/bin/bash
# Backend script for Lubuntu Update (does text processing and update installation, can be run as root and must be in order to install updates)
set -e
export LC_ALL='C'
if [ "$1" = 'pkgver' ]; then
shift
while [ "$1" != '' ]; do
source="$(apt-cache show "$1" | grep 'Source:' | cut -d' ' -f2)"
version="$(apt-cache show lubuntu-default-settings | grep Version: | head -n1 | cut -d' ' -f2)"
if [ "$source" = '' ]; then
echo "$1"
else
echo "$source"
fi
echo "$version"
shift
done
elif [ "$1" = 'doupdate' ]; then
# Prepare to be able to grep through the logs
rm /run/lubuntu-update-apt-log || true
touch /run/lubuntu-update-apt-log
chmod 0600 /run/lubuntu-update-apt-log # prevent non-root from being able to trick the script into deleting arbitrary files later on
# Repair an interrupted upgrade if necessary
dpkg --configure -a
# Run the real update
DEBIAN_FRONTEND='kde' apt-get -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' -o Apt::Color='0' -o Dpkg::Use-Pty='0' -y dist-upgrade |& tee /run/lubuntu-update-apt-log
# Find all the conffiles
mapfile conffileRawList <<< "$(grep -P "^Configuration file \'.*\'$" '/run/lubuntu-update-apt-log')"
if [ "$(echo "${conffileRawList[0]}" | head -c1)" != 'C' ]; then # Empty or invalid list, we're done
exit 0
fi
conffileList=()
counter=0
while [ "$counter" -lt "${#conffileRawList[@]}" ]; do
# Cut off "Configuration file '" from the start and "'" plus a couple trailing characters from the end
conffileList[counter]="$(echo "${conffileRawList[$counter]}" | tail -c+21 | head -c-3)"
counter=$((counter+1))
done
echo "Lubuntu Update !!! CONFIGURATION FILE LIST START";
counter=0
while [ "$counter" -lt "${#conffileList[@]}" ]; do
echo "${conffileList[$counter]}"
counter=$((counter+1))
done
echo "Lubuntu Update !!! CONFIGURATION FILE LIST END";
# If we make it this far, there were conffiles to deal with
breakLoop='no'
gotCommand='no'
commandName=''
while [ "$breakLoop" = 'no' ]; do
read -r inputVal
if [ "$gotCommand" = 'no' ]; then
if [ "$inputVal" = 'done' ]; then
breakLoop='yes'
else
commandName="$inputVal"
gotCommand='yes'
fi
else
if [ "$commandName" = 'replace' ]; then # Replace an existing file
counter=0
while [ "$counter" -lt "${#conffileList[@]}" ]; do
if [ "$inputVal" = "${conffileList[$counter]}" ]; then
mv "$inputVal.dpkg-dist" "$inputVal"
break
fi
counter=$((counter+1))
done
elif [ "$commandName" = 'keep' ]; then # Keep an existing file
counter=0
while [ "$counter" -lt "${#conffileList[@]}" ]; do
if [ "$inputVal" = "${conffileList[$counter]}" ]; then
rm "$inputVal.dpkg-dist"
break
fi
counter=$((counter+1))
done
fi
gotCommand='no'
fi
done
echo 'Update installation complete.'
fi

@ -1,89 +0,0 @@
#include "ipcfilewatcher.h"
#include "orchestrator.h"
#include "mainwindow.h"
#include "conffilehandlerdialog.h"
#include <QApplication>
#include <QDialog>
#include <QLocale>
#include <QProcess>
#include <QTranslator>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTranslator translator;
const QStringList uiLanguages = QLocale::system().uiLanguages();
for (const QString &locale : uiLanguages) {
const QString baseName = "lubuntu-update_" + QLocale(locale).name();
if (translator.load(":/i18n/" + baseName)) {
a.installTranslator(&translator);
break;
}
}
/*
* If Lubuntu Update is already running, create
* /dev/shm/lubuntu-update/lubuntu-update-show-win and exit. This will
* trigger the existing process to pop up a window.
*/
QProcess procDetector;
procDetector.setProgram("/usr/bin/bash");
procDetector.setArguments(QStringList() << "-c" << "ps axo comm | grep lubuntu-update");
procDetector.start();
procDetector.waitForFinished();
QString procDetectResult = procDetector.readAllStandardOutput();
procDetectResult = procDetectResult.trimmed();
QStringList procDetectResultList = procDetectResult.split('\n');
if (procDetectResultList.count() > 1) {
QFile flagFile("/dev/shm/lubuntu-update/lubuntu-update-show-win");
flagFile.open(QFile::WriteOnly);
flagFile.close();
return 0;
}
// Don't want the updater to stop just because the user closed it :P
a.setQuitOnLastWindowClosed(false);
/*
* IPCFileWatcher just watches the /dev/shm/lubuntu-update folder for the
* creation of a lubuntu-update-show-win file. If it detects it, it
* immediately deletes it and emits a signal. This is then used later to
* cause the updater window to pop up.
*/
IPCFileWatcher *p = new IPCFileWatcher();
if (p->didInitFail()) {
return 1;
}
/*
* As this is a background process, we don't pop up any window upon
* startup. An Orchestrator object periodically checks to see if new
* updates have been detected, and offers them to the user (by displaying
* a tray icon) if so. The user can click on this tray icon to see the
* updater window.
*
* Orchestrator's constructor automatically starts the update checker, so
* there's no need to do anything with this except create it and then
* start the event loop.
*/
Orchestrator *o = new Orchestrator();
QObject::connect(p, &IPCFileWatcher::showWindowFlagDetected, o, &Orchestrator::displayUpdater);
/*
* This is an artifact from testing the conffile handler window. You can
* uncomment this and rebuild lubuntu-update in order to test the conffile
* handler UI and develop it further.
*
* ConffileHandlerDialog cfhd(QStringList() << "/home/user/testfile");
* cfhd.show();
*/
return a.exec();
}

@ -1,60 +0,0 @@
#include "orchestrator.h"
#include "mainwindow.h"
#include "aptmanager.h"
#include <QIcon>
#include <QSystemTrayIcon>
#include <QTimer>
Orchestrator::Orchestrator(QObject *parent)
: QObject{parent}
{
checkTimer = new QTimer(); // every time this triggers, the apt database is checked for new updates
trayIcon = new QSystemTrayIcon(); // this is shown to the user to offer updates
connect(checkTimer, &QTimer::timeout, this, &Orchestrator::checkForUpdates);
connect(trayIcon, &QSystemTrayIcon::activated, this, &Orchestrator::displayUpdater);
connect(&updaterWindow, &MainWindow::updatesInstalled, this, &Orchestrator::handleUpdatesInstalled);
checkTimer->start(21600000); // check four times a day, at least one of those times unattended-upgrades should have refreshed the apt database
checkForUpdates(); // check immediately after launch, which usually will be immediately after boot or login
}
/*
* Checks the apt database to see if updated software is available, and places
* the results in the updateInfo variable. If updated software is available,
* show the system tray icon and trigger a notification.
*/
void Orchestrator::checkForUpdates()
{
updateInfo = AptManager::getUpdateInfo();
if (!updateInfo[0].isEmpty() || !updateInfo[1].isEmpty() || !updateInfo[2].isEmpty() || !updateInfo[3].isEmpty()) { // no need to check updateInfo[4], it will only ever contain security updates already listed in updateInfo[1]
trayIcon->setIcon(QPixmap(":/res/images/update.svg"));
trayIcon->show();
// Yes, we do intentionally use updateInfo[1], then updateInfo[0], then updateInfo[2]. The updateInfo array is populated in a different order than the one we display in.
trayIcon->showMessage("",
QString("Updates available!\n\n%1 to upgrade, %2 to install, and %3 to remove.\n\nClick the tray icon to install the updates.")
.arg(QString::number(updateInfo[1].count()), QString::number(updateInfo[0].count()), QString::number(updateInfo[2].count())));
}
}
void Orchestrator::displayUpdater()
{
if (!updaterWindow.isLockedOpen()) {
if (!updaterWindow.isVisible()) {
updaterWindow.hide();
}
updaterWindow.setUpdateInfo(updateInfo);
updaterWindow.show();
}
}
void Orchestrator::handleUpdatesInstalled()
{
// We can't clear the updateInfo list directly as MainWindow::setUpdateInfo requires that it contains five inner lists (even if those lists are all empty).
for (int i = 0;i < 5;i++) {
updateInfo[i].clear();
}
trayIcon->hide();
}

@ -30,8 +30,19 @@ void AptManager::applyFullUpgrade()
// Note that the lubuntu-update-backend script sets LC_ALL=C in it already, so we don't need to add that here.
aptProcess->setArguments(QStringList() << "/usr/libexec/lubuntu-update-backend" << "doupdate");
aptProcess->setProcessChannelMode(QProcess::MergedChannels);
QObject::connect(aptProcess, &QProcess::readyRead, this, &AptManager::handleProcessBuffer);
QObject::connect(aptProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &AptManager::handleProcessBuffer);
QObject::connect(aptProcess, &QProcess::readyRead, this, &AptManager::handleUpdateProcessBuffer);
QObject::connect(aptProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &AptManager::handleUpdateProcessBuffer);
aptProcess->start();
}
void AptManager::checkForUpdates()
{
aptProcess = new QProcess();
aptProcess->setProgram("/usr/bin/lxqt-sudo");
aptProcess->setArguments(QStringList() << "/usr/libexec/lubuntu-update-backend" << "checkupdate");
aptProcess->setProcessChannelMode(QProcess::MergedChannels);
QObject::connect(aptProcess, &QProcess::readyRead, this, &AptManager::handleCheckUpdateProcessBuffer);
QObject::connect(aptProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &AptManager::handleCheckUpdateProcessBuffer);
aptProcess->start();
}
@ -56,7 +67,7 @@ void AptManager::doneWithConffiles()
aptProcess->closeWriteChannel();
}
void AptManager::handleProcessBuffer()
void AptManager::handleUpdateProcessBuffer()
{
int maxWaitTime = 20;
while (!aptProcess->canReadLine() && maxWaitTime > 0) {
@ -77,10 +88,10 @@ void AptManager::handleProcessBuffer()
// yes, this gave me a headache also
if (dlLineMatch.hasMatch()) { // Increments the progress counter for each package downloaded
internalUpdateProgress++;
} else if (line.count() >= 25 && line.left(24) == "Preparing to unpack .../" && numPackagesToPrep != 0) {
} else if (line.length() >= 25 && line.left(24) == "Preparing to unpack .../" && numPackagesToPrep != 0) {
internalUpdateProgress++; // Increments the progress counter for each package that is "prepared to unpack"
numPackagesToPrep--;
} else if (line.count() >= 10 && line.left(9) == "Unpacking") {
} else if (line.length() >= 10 && line.left(9) == "Unpacking") {
/*
* Increments the progress counter for each package that is unpacked
* The package name may be suffixed with ":amd64" or some other
@ -98,7 +109,7 @@ void AptManager::handleProcessBuffer()
internalUpdateProgress++;
}
}
} else if (line.count() >= 11 && line.left(10) == "Setting up") {
} else if (line.length() >= 11 && line.left(10) == "Setting up") {
QStringList parts = line.split(' ');
QString packageName;
if (parts.count() >= 3) {
@ -107,7 +118,7 @@ void AptManager::handleProcessBuffer()
internalUpdateProgress++;
}
}
} else if (line.count() >= 9 && line.left(8) == "Removing") {
} else if (line.length() >= 9 && line.left(8) == "Removing") {
QStringList parts = line.split(' ');
QString packageName;
if (parts.count() >= 2) {
@ -125,7 +136,7 @@ void AptManager::handleProcessBuffer()
}
aptProcess->readLine(lineBuf, 2048);
QString confLine = QString(lineBuf);
confLine = confLine.left(confLine.count() - 2);
confLine = confLine.left(confLine.length() - 2);
if (confLine == "Lubuntu Update !!! CONFIGURATION FILE LIST END") {
emit conffileListReady(conffileList); // this triggers the main window to show the conffile handler window
break;
@ -133,6 +144,15 @@ void AptManager::handleProcessBuffer()
conffileList.append(confLine);
}
}
} else if (line == "Lubuntu Update !!! NEW RELEASE\r\n") {
// Same busy-wait technique, but here we're just getting one extra line, the release code.
while (!aptProcess->canReadLine()) {
QThread::msleep(20);
}
aptProcess->readLine(lineBuf, 2048);
QString releaseCode = QString(lineBuf);
releaseCode = releaseCode.left(releaseCode.length() - 2);
emit newRelease(releaseCode);
}
double percentageDone = (static_cast<double>(internalUpdateProgress) / (((internalUpdateInfo[0].count() + internalUpdateInfo[1].count()) * 4) + internalUpdateInfo[2].count())) * 100;
@ -149,6 +169,27 @@ void AptManager::handleProcessBuffer()
}
}
void AptManager::handleCheckUpdateProcessBuffer()
{
/*
* We don't have the busy wait here because the apt output when doing
* `apt-get update` is somewhat ill-formed and difficult to fix, and
* busy-waiting was resulting in *awful* progress bar choppiness.
*/
char lineBuf[2048];
while(aptProcess->canReadLine()) {
aptProcess->readLine(lineBuf, 2048);
QString line = QString(lineBuf);
emit logLineReady(line);
}
if (aptProcess->state() == QProcess::NotRunning) {
emit checkUpdatesComplete();
aptProcess->deleteLater();
}
}
QList<QStringList> AptManager::getUpdateInfo()
{
/*
@ -205,7 +246,7 @@ QList<QStringList> AptManager::getUpdateInfo()
* spaces, we know we're no longer reading a package list.
*/
if (stdoutLine.count() < 3 || stdoutLine.left(2) != " ") {
if (stdoutLine.length() < 3 || stdoutLine.left(2) != " ") {
gettingInstallPackages = false;
gettingUpgradePackages = false;
gettingUninstallPackages = false;
@ -276,7 +317,7 @@ QStringList AptManager::getSecurityUpdateList()
QString distroLine;
while (distroFinder.readLineInto(&distroLine)) {
// The line has to be at least 18 characters long - 16 for the string "DISTRIB_CODENAME", one for the = sign, and one for a codename with a length of at least one.
if (distroLine.count() >= 18 && distroLine.left(16) == "DISTRIB_CODENAME") {
if (distroLine.length() >= 18 && distroLine.left(16) == "DISTRIB_CODENAME") {
QStringList distroParts = distroLine.split('=');
if (distroParts.count() >= 2) {
distroName = distroParts[1];

@ -17,18 +17,22 @@ public:
AptManager(QObject *parent = nullptr);
static QList<QStringList> getUpdateInfo();
void applyFullUpgrade();
void checkForUpdates();
void keepConffile(QString conffile);
void replaceConffile(QString conffile);
void doneWithConffiles();
signals:
void updateComplete();
void checkUpdatesComplete();
void progressUpdated(int progress);
void logLineReady(QString logLine);
void conffileListReady(QStringList conffileList);
void newRelease(QString code);
private slots:
void handleProcessBuffer();
void handleUpdateProcessBuffer();
void handleCheckUpdateProcessBuffer();
private:
static void parseAptLine(QString line, bool *gettingInstallPackages, bool *gettingUpgradePackages, bool *gettingUninstallPackages, bool *gettingHeldPackages, bool *gettingPackageList);

@ -4,8 +4,6 @@
#include <QProcess>
#include <QDebug>
ConffileWidget::ConffileWidget(QString filename, QWidget *parent) :
QWidget(parent),
ui(new Ui::ConffileWidget)

@ -0,0 +1,193 @@
#!/bin/bash
# Backend script for Lubuntu Update (does text processing and update installation, can be run as root and must be in order to install updates)
set -e
export LC_ALL='C'
# Returns 0 if the release is supported, 1 if unsupported, 2 if if didn't exist at all, and 3 if something went wrong.
isReleaseSupported () {
releaseYear="${1:-}";
releaseMonth="${2:-}";
metaReleaseStr="${3:-}";
if [ -z "$releaseYear" ]; then
echo '! ! ! releaseYear is blank';
return 3;
elif [ -z "$releaseMonth" ]; then
echo '! ! ! releaseMonth is blank';
return 3;
elif [ -z "$metaReleaseStr" ]; then
echo '! ! ! metaReleaseStr is blank';
return 3;
fi
releaseCode="$releaseYear.$releaseMonth";
scanForSupported='n';
while IFS= read -r line || [[ -n $line ]]; do
if [[ "$line" =~ $releaseCode ]]; then
scanForSupported='y';
fi
if [ "$scanForSupported" = 'y' ]; then
if [[ "$line" =~ Supported ]]; then
if [ "$(echo "$line" | cut -d':' -f2 | tail -c+2)" = '0' ]; then
return 1;
else
return 0;
fi
fi
fi
done < <(printf '%s' "$metaReleaseStr")
return 2;
}
if [ "$1" = 'pkgver' ]; then
shift
while [ "$1" != '' ]; do
source="$(apt-cache show "$1" | grep 'Source:' | cut -d' ' -f2)"
version="$(apt-cache show "$1" | grep Version: | head -n1 | cut -d' ' -f2)"
if [ "$source" = '' ]; then
echo "$1"
else
echo "$source"
fi
echo "$version"
shift
done
elif [ "$1" = 'checkupdate' ]; then
apt-get -o Apt::Color='0' -o Dpkg::Use-Pty='0' update
elif [ "$1" = 'doupdate' ]; then
# Prepare to be able to grep through the logs
rm /run/lubuntu-update-apt-log || true
touch /run/lubuntu-update-apt-log
chmod 0600 /run/lubuntu-update-apt-log # prevent non-root from being able to trick the script into deleting arbitrary files later on
# Repair an interrupted upgrade if necessary
dpkg --configure -a
# Run the real update
DEBIAN_FRONTEND='kde' apt-get -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' -o Apt::Color='0' -o Dpkg::Use-Pty='0' -y dist-upgrade |& tee /run/lubuntu-update-apt-log
# Find all the conffiles
doConffiles='y';
mapfile conffileRawList <<< "$(grep -P "^Configuration file \'.*\'$" '/run/lubuntu-update-apt-log')"
if [ "$(echo "${conffileRawList[0]}" | head -c1)" != 'C' ]; then # Empty or invalid list, we're done
doConffiles='n';
fi
if [ "$doConffiles" = 'y' ]; then
conffileList=()
counter=0
while [ "$counter" -lt "${#conffileRawList[@]}" ]; do
# Cut off "Configuration file '" from the start and "'" plus a couple trailing characters from the end
conffileList[counter]="$(echo "${conffileRawList[$counter]}" | tail -c+21 | head -c-3)"
counter=$((counter+1))
done
echo "Lubuntu Update !!! CONFIGURATION FILE LIST START";
counter=0
while [ "$counter" -lt "${#conffileList[@]}" ]; do
echo "${conffileList[$counter]}"
counter=$((counter+1))
done
echo "Lubuntu Update !!! CONFIGURATION FILE LIST END";
# If we make it this far, there were conffiles to deal with
breakLoop='no'
gotCommand='no'
commandName=''
while [ "$breakLoop" = 'no' ]; do
read -r inputVal
if [ "$gotCommand" = 'no' ]; then
if [ "$inputVal" = 'done' ]; then
breakLoop='yes'
else
commandName="$inputVal"
gotCommand='yes'
fi
else
if [ "$commandName" = 'replace' ]; then # Replace an existing file
counter=0
while [ "$counter" -lt "${#conffileList[@]}" ]; do
if [ "$inputVal" = "${conffileList[$counter]}" ]; then
mv "$inputVal.dpkg-dist" "$inputVal"
break
fi
counter=$((counter+1))
done
elif [ "$commandName" = 'keep' ]; then # Keep an existing file
counter=0
while [ "$counter" -lt "${#conffileList[@]}" ]; do
if [ "$inputVal" = "${conffileList[$counter]}" ]; then
rm "$inputVal.dpkg-dist"
break
fi
counter=$((counter+1))
done
fi
gotCommand='no'
fi
done
fi
echo 'Checking release status...'
releaseCode="$(cat /etc/lsb-release | grep "DISTRIB_RELEASE" | cut -d'=' -f2)";
releaseYear="$(cut -d'.' -f1 <<< "$releaseCode")";
releaseMonth="$(cut -d'.' -f2 <<< "$releaseCode")";
metaReleaseData="$(curl https://changelogs.ubuntu.com/meta-release)";
#nextReleaseMonth='';
#nextReleaseYear='';
#nextLTSReleaseMonth='';
#nextLTSReleaseYear='';
while true; do
if ((releaseMonth == 4)); then
releaseMonth='10';
else
releaseMonth='04';
((releaseYear++));
fi
releaseSupportedResult="$(isReleaseSupported "$releaseYear" "$releaseMonth" "$metaReleaseData"; echo "$?")";
if [ "$releaseSupportedResult" = '0' ]; then
echo 'Lubuntu Update !!! NEW RELEASE';
echo "$releaseYear.$releaseMonth";
elif [ "$releaseSupportedResult" = '2' ]; then
break;
fi
done
# if ((releaseMonth == 4)); then
# nextReleaseMonth=$((releaseMonth + 6));
# nextReleaseYear="$releaseYear";
# if (((releaseYear % 2) == 0)); then
# nextLTSReleaseMonth='04';
# nextLTSReleaseYear=$((releaseYear + 2));
# fi
# else
# nextReleaseMonth="$releaseMonth";
# nextReleaseYear=$((releaseYear + 1));
# fi
#
# if [ -n "$nextLTSReleaseYear" ]; then
# if isReleaseSupported "$nextLTSReleaseYear" "$nextLTSReleaseMonth" "$metaReleaseData"; then
# echo 'Lubuntu Update !!! NEW RELEASE';
# echo "$nextLTSReleaseYear.$nextLTSReleaseMonth";
# fi
# fi
#
# if ! (((nextReleaseYear == nextLTSReleaseYear) && (nextReleaseMonth == nextLTSReleaseMonth))); then
# if isReleaseSupported "$nextReleaseYear" "$nextReleaseMonth" "$metaReleaseData"; then
# echo 'Lubuntu Update !!! NEW RELEASE';
# echo "$nextReleaseYear.$nextReleaseMonth";
# else
# echo "Unsupported release: $nextReleaseYear.$nextReleaseMonth";
# fi
# fi
echo 'Update installation complete.'
elif [ "$1" = 'doReleaseUpgrade' ]; then
do-release-upgrade -m desktop -f DistUpgradeViewKDE;
elif [ "$1" = 'declineReleaseUpgrade' ]; then
sed -i -E 's/^Prompt=(never|lts|normal)$/Prompt=never/' /etc/update-manager/release-upgrades;
fi

@ -0,0 +1,125 @@
#include "windowshowwatcher.h"
#include "orchestrator.h"
#include "mainwindow.h"
#include "conffilehandlerdialog.h"
#include <QApplication>
#include <QDBusConnection>
#include <QDBusError>
#include <QDBusInterface>
#include <QDebug>
#include <QDialog>
#include <QLocale>
#include <QProcess>
#include <QThread>
#include <QTranslator>
/*
* Detects if at least `count` processes that match `procName` are running.
*/
bool detectProc(QString procName, int count)
{
QProcess procDetector;
procDetector.setProgram("/usr/bin/bash");
procDetector.setArguments(QStringList() << "-c" << "ps axo comm | grep " + procName);
procDetector.start();
procDetector.waitForFinished();
QString procDetectResult = procDetector.readAllStandardOutput();
procDetectResult = procDetectResult.trimmed();
QStringList procDetectResultList = procDetectResult.split('\n');
if (procDetectResultList.count() >= count) {
return true;
}
return false;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTranslator translator;
const QStringList uiLanguages = QLocale::system().uiLanguages();
for (const QString &locale : uiLanguages) {
const QString baseName = QLocale(locale).name();
if (translator.load(":/i18n/" + baseName)) {
a.installTranslator(&translator);
break;
}
}
// Connect to D-Bus.
auto dbusConnection = QDBusConnection::sessionBus();
/*
* If Lubuntu Update is already running, instruct the running instance to
* display its window via the D-Bus connection.
*/
if (detectProc("lubuntu-update", 2)) {
auto iface = new QDBusInterface("me.lubuntu.LubuntuUpdate.window", "/", "me.lubuntu.LubuntuUpdate.window", dbusConnection);
if (!iface->isValid()) {
qWarning().noquote() << dbusConnection.lastError().message();
return 1;
}
iface->call("showWindow");
return 0;
}
/*
* Wait to run until lxqt-notificationd is running. This avoids a bug that
* causes notifications to show up in the entirely wrong spot. If it takes
* longer than about 30 seconds to show up, we continue on without it for
* the sake of getting security updates.
*/
for (int i = 0;i < 30;i++) {
// "lxqt-notificati" is intentionally truncated here since that's how it shows up in the output of `ps axo comm`.
if (detectProc("lxqt-notificati", 1)) {
// Wait for it to initialize fully - 3 seconds should be way more than enough
QThread::sleep(3);
break;
} else {
QThread::sleep(1);
}
}
// Don't want the updater to stop just because the user closed it :P
a.setQuitOnLastWindowClosed(false);
/*
* WindowShowWatcher is a very simple D-Bus service that allow triggering
* the Lubuntu Update window to be shown. If anything calls "showWindow"
* on this service, Lubuntu Update's window will pop up.
*/
QObject obj;
auto *wsw = new WindowShowWatcher(&obj);
dbusConnection.registerObject("/", &obj);
if (!dbusConnection.registerService("me.lubuntu.LubuntuUpdate.window")) {
return 1;
}
/*
* As this is a background process, we don't pop up any window upon
* startup. An Orchestrator object periodically checks to see if new
* updates have been detected, and offers them to the user (by displaying
* a tray icon) if so. The user can click on this tray icon to see the
* updater window.
*
* Orchestrator's constructor automatically starts the update checker, so
* there's no need to do anything with this except create it and then
* start the event loop.
*/
Orchestrator *o = new Orchestrator();
QObject::connect(wsw, &WindowShowWatcher::showWindowTriggered, o, &Orchestrator::displayUpdater);
/*
* This is an artifact from testing the conffile handler window. You can
* uncomment this and rebuild lubuntu-update in order to test the conffile
* handler UI and develop it further.
*
* ConffileHandlerDialog cfhd(QStringList() << "/home/user/testfile");
* cfhd.show();
*/
return a.exec();
}

@ -17,15 +17,15 @@ MainWindow::MainWindow(QWidget *parent)
ui->progressBar->setVisible(false);
ui->logView->setVisible(false);
// FIXME: Implement the Details screen and attach this button to it rather than disabling it.
ui->detailsButton->setVisible(false);
connect(ui->installButton, &QPushButton::clicked, this, &MainWindow::onInstallButtonClicked);
connect(ui->checkUpdatesButton, &QPushButton::clicked, this, &MainWindow::onCheckUpdatesButtonClicked);
connect(ui->closeButton, &QPushButton::clicked, this, &MainWindow::onCloseButtonClicked);
connect(aptManager, &AptManager::updateComplete, this, &MainWindow::onUpdateCompleted);
connect(aptManager, &AptManager::checkUpdatesComplete, this, &MainWindow::onCheckUpdatesCompleted);
connect(aptManager, &AptManager::progressUpdated, this, &MainWindow::onProgressUpdate);
connect(aptManager, &AptManager::logLineReady, this, &MainWindow::onLogLineReady);
connect(aptManager, &AptManager::conffileListReady, this, &MainWindow::onConffileListReady);
connect(aptManager, &AptManager::newRelease, this, &MainWindow::onNewRelease);
}
MainWindow::~MainWindow()
@ -39,41 +39,46 @@ void MainWindow::setUpdateInfo(QList<QStringList> updateInfo)
// The progress bar and log view are shown after the user chooses to begin installing updates
ui->progressBar->setVisible(false);
ui->logView->setVisible(false);
ui->detailsButton->setEnabled(true);
ui->closeButton->setEnabled(true);
ui->installButton->setEnabled(false); // Correct, it starts out false, we turn it to true if there are any updates.
ui->checkUpdatesButton->setEnabled(true);
bool installEnabled = false;
for (int i = 0;i < 4;i++) {
if (updateInfo[i].count() > 0) {
ui->installButton->setEnabled(true);
}
int count = updateInfo[i].count();
if (count > 0) {
installEnabled = true;
} else { continue; }
QTreeWidgetItem *installItem;
switch (i) {
case 0:
installItem = new QTreeWidgetItem(QStringList() << "To be installed");
installItem = new QTreeWidgetItem(QStringList() << tr("To be installed"));
break;
case 1:
installItem = new QTreeWidgetItem(QStringList() << "To be upgraded");
installItem = new QTreeWidgetItem(QStringList() << tr("To be upgraded"));
break;
case 2:
installItem = new QTreeWidgetItem(QStringList() << "To be removed");
installItem = new QTreeWidgetItem(QStringList() << tr("To be removed"));
break;
case 3:
installItem = new QTreeWidgetItem(QStringList() << "Held back (usually temporarily)");
installItem = new QTreeWidgetItem(QStringList() << tr("Held back (usually temporarily)"));
break;
}
for (int j = 0;j < updateInfo[i].count();j++) {
for (int j = 0;j < count;j++) {
// TODO: Add security update detection here - security updates should be marked in some way
installItem->addChild(new QTreeWidgetItem(QStringList() << updateInfo[i][j]));
}
ui->packageView->addTopLevelItem(installItem);
}
ui->statLabel->setText(QString("%1 package(s) will be updated. %2 of these updates are security-related.")
ui->statLabel->setText(tr("%1 package(s) will be updated. %2 of these updates are security-related.")
.arg(QString::number(updateInfo[0].count() + updateInfo[1].count() + updateInfo[2].count()),
QString::number(updateInfo[4].count())));
ui->installButton->setEnabled(installEnabled);
}
bool MainWindow::isLockedOpen()
@ -102,14 +107,30 @@ void MainWindow::closeEvent(QCloseEvent *event)
void MainWindow::onInstallButtonClicked()
{
ui->logView->clear();
ui->progressBar->setVisible(true);
ui->logView->setVisible(true);
ui->installButton->setEnabled(false);
ui->detailsButton->setEnabled(false);
ui->checkUpdatesButton->setEnabled(false);
ui->closeButton->setEnabled(false);
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(100);
aptManager->applyFullUpgrade();
}
void MainWindow::onCheckUpdatesButtonClicked()
{
ui->logView->clear();
ui->progressBar->setVisible(true);
ui->logView->setVisible(true);
ui->installButton->setEnabled(false);
ui->checkUpdatesButton->setEnabled(false);
ui->closeButton->setEnabled(false);
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(0);
aptManager->checkForUpdates();
}
void MainWindow::onCloseButtonClicked()
{
hide();
@ -118,10 +139,20 @@ void MainWindow::onCloseButtonClicked()
void MainWindow::onUpdateCompleted()
{
ui->closeButton->setEnabled(true);
ui->checkUpdatesButton->setEnabled(true);
ui->installButton->setEnabled(false);
ui->progressBar->setVisible(false);
ui->statLabel->setText("Update installation complete.");
ui->statLabel->setText(tr("Update installation complete."));
emit updatesInstalled(); // this tells the orchestrator to hide the tray icon
if (releaseCodes.count() > 0) {
handleNewReleases();
}
}
void MainWindow::onCheckUpdatesCompleted()
{
ui->closeButton->setEnabled(true); // unlocks the updater so that the orchestrator actually can update things
emit updatesRefreshed(); // this tells the orchestrator to re-scan for updates
}
void MainWindow::onProgressUpdate(int progress)
@ -148,3 +179,14 @@ void MainWindow::onConffileListReady(QStringList conffileList)
}
aptManager->doneWithConffiles();
}
void MainWindow::onNewRelease(QString code)
{
releaseCodes.append(code);
}
void MainWindow::handleNewReleases()
{
emit newReleaseAvailable(releaseCodes);
releaseCodes.clear();
}

@ -25,20 +25,28 @@ public:
signals:
void updatesInstalled();
void updatesRefreshed();
void newReleaseAvailable(QStringList releaseCodes);
protected slots:
void closeEvent(QCloseEvent *event) override;
private slots:
void onInstallButtonClicked();
void onCheckUpdatesButtonClicked();
void onCloseButtonClicked();
void onUpdateCompleted();
void onCheckUpdatesCompleted();
void onProgressUpdate(int progress);
void onLogLineReady(QString logLine);
void onConffileListReady(QStringList conffileList);
void onNewRelease(QString code);
private:
Ui::MainWindow *ui;
AptManager *aptManager;
QStringList releaseCodes;
void handleNewReleases();
};
#endif // MAINWINDOW_H

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>462</width>
<width>473</width>
<height>600</height>
</rect>
</property>
@ -37,6 +37,9 @@
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>0</number>
</property>
@ -86,9 +89,9 @@ font: 9pt &quot;Monospace&quot;;</string>
</widget>
</item>
<item>
<widget class="QPushButton" name="detailsButton">
<widget class="QPushButton" name="checkUpdatesButton">
<property name="text">
<string>Details</string>
<string>Check for Updates</string>
</property>
</widget>
</item>

@ -0,0 +1,188 @@
#include "orchestrator.h"
#include "mainwindow.h"
#include "aptmanager.h"
#include "releaseupgradewindow.h"
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QIcon>
#include <QProcess>
#include <QSystemTrayIcon>
#include <QTimer>
Orchestrator::Orchestrator(QObject *parent)
: QObject{parent}
{
checkTimer = new QTimer(); // every time this triggers, the apt database is checked for new updates
trayIcon = new QSystemTrayIcon(); // this is shown to the user to offer updates
/*
* Parse the Lubuntu Update config file. It contains two critical pieces
* of info - when the system last offered the user a release upgrade,
* and whether the user has disabled release upgrade notifications.
*/
QFile configFile(QDir::homePath() + "/.config/lubuntu-update.conf");
bool success = configFile.open(QFile::ReadOnly);
if (success) {
char lineBuf[2048];
while (!configFile.atEnd()) {
configFile.readLine(lineBuf, 2048);
QString line(lineBuf);
line = line.trimmed();
QStringList lineParts = line.split("=");
if (lineParts.count() == 2) {
if (lineParts[0] == "nextDoReleaseUpgradeNotify") {
nextUpgradeCheck = QDateTime::fromSecsSinceEpoch(lineParts[1].toLongLong());
} else {
qWarning() << "Unrecognized config line: " << line;
}
} else {
qWarning() << "Wrong number of fields in line: " << line;
}
}
}
configFile.close();
connect(checkTimer, &QTimer::timeout, this, &Orchestrator::checkForUpdates);
connect(trayIcon, &QSystemTrayIcon::activated, this, &Orchestrator::displayUpdater);
connect(&updaterWindow, &MainWindow::updatesInstalled, this, &Orchestrator::handleUpdatesInstalled);
connect(&updaterWindow, &MainWindow::updatesRefreshed, this, &Orchestrator::handleUpdatesRefreshed);
connect(&updaterWindow, &MainWindow::newReleaseAvailable, this, &Orchestrator::onNewReleaseAvailable);
checkTimer->start(21600000); // check four times a day, at least one of those times unattended-upgrades should have refreshed the apt database
checkForUpdates(); // check immediately after launch, which usually will be immediately after boot or login
}
/*
* Checks the apt database to see if updated software is available, and places
* the results in the updateInfo variable. If updated software is available,
* show the system tray icon and trigger a notification.
*/
void Orchestrator::checkForUpdates()
{
updateInfo = AptManager::getUpdateInfo();
if (!updateInfo[0].isEmpty() || !updateInfo[1].isEmpty() || !updateInfo[2].isEmpty() || !updateInfo[3].isEmpty()) { // no need to check updateInfo[4], it will only ever contain security updates already listed in updateInfo[1]
trayIcon->setIcon(QPixmap(":/res/images/update.svg"));
trayIcon->show();
// Yes, we do intentionally use updateInfo[1], then updateInfo[0], then updateInfo[2]. The updateInfo array is populated in a different order than the one we display in.
trayIcon->showMessage("",
tr("Updates available!\n\n%1 to upgrade, %2 to install, and %3 to remove.\n\nClick the tray icon to install the updates.")
.arg(QString::number(updateInfo[1].count()), QString::number(updateInfo[0].count()), QString::number(updateInfo[2].count())));
}
}
void Orchestrator::displayUpdater()
{
if (!updaterWindow.isLockedOpen()) {
if (!updaterWindow.isVisible()) {
updaterWindow.hide();
}
updaterWindow.setUpdateInfo(updateInfo);
updaterWindow.show();
}
}
void Orchestrator::handleUpdatesInstalled()
{
// We can't clear the updateInfo list directly as MainWindow::setUpdateInfo requires that it contains five inner lists (even if those lists are all empty).
for (int i = 0;i < 5;i++) {
updateInfo[i].clear();
}
trayIcon->hide();
}
void Orchestrator::handleUpdatesRefreshed()
{
checkForUpdates();
displayUpdater();
}
void Orchestrator::onNewReleaseAvailable(QStringList releaseCodes)
{
// First, determine what kinds of releases the user wants to see.
QFile druTypeFile("/etc/update-manager/release-upgrades");
bool success = druTypeFile.open(QFile::ReadOnly);
QString druType;
if (success) {
char lineBuf[2048];
while (!druTypeFile.atEnd()) {
druTypeFile.readLine(lineBuf, 2048);
QString line(lineBuf);
line = line.trimmed();
if (line == "Prompt=lts") {
druType="lts";
druTypeFile.close();
break;
} else if (line == "Prompt=none") {
// The user has disabled all upgrade prompts.
druTypeFile.close();
return;
} else if (line == "Prompt=normal") {
druType="normal";
druTypeFile.close();
break;
}
}
} else {
druType="normal";
druTypeFile.close();
}
for (int i = 0;i < releaseCodes.count();i++) {
QStringList releaseCodeParts = releaseCodes[i].split('.');
if (releaseCodeParts.count() >= 2) {
int releaseYear = releaseCodeParts[0].toInt();
int releaseMonth = releaseCodeParts[1].toInt();
if (((releaseYear % 2 == 0) && (releaseMonth == 4)) || druType == "normal") {
QDateTime now = QDateTime::currentDateTime();
if (nextUpgradeCheck < now) {
ReleaseUpgradeWindow upgradeWindow(releaseCodes[i]);
upgradeWindow.exec();
if (upgradeWindow.getUpgradeAccepted()) {
doReleaseUpgrade();
} else if (upgradeWindow.getUpgradeDelayStamp() > 0) {
delayReleaseUpgrade(upgradeWindow.getUpgradeDelayStamp());
} else {
declineReleaseUpgrade();
}
break;
}
}
}
}
}
void Orchestrator::doReleaseUpgrade()
{
QProcess druProcess;
druProcess.setProgram("/usr/bin/lxqt-sudo");
druProcess.setArguments(QStringList() << "/usr/libexec/lubuntu-update-backend" << "doReleaseUpgrade");
druProcess.start();
druProcess.waitForFinished(-1);
}
void Orchestrator::delayReleaseUpgrade(qint64 timestamp)
{
QFile configFile(QDir::homePath() + "/.config/lubuntu-update.conf");
bool success = configFile.open(QFile::WriteOnly);
if (success) {
configFile.write("nextDoReleaseUpgradeNotify=");
configFile.write(QString::number(timestamp).toUtf8());
configFile.write("\n");
} else {
qWarning() << "Could not write to " + QDir::homePath() + "/.config/lubuntu-update.conf, check permissions";
}
configFile.close();
nextUpgradeCheck = QDateTime::fromSecsSinceEpoch(timestamp);
}
void Orchestrator::declineReleaseUpgrade()
{
QProcess druProcess;
druProcess.setProgram("/usr/bin/lxqt-sudo");
druProcess.setArguments(QStringList() << "/usr/libexec/lubuntu-update-backend" << "declineReleaseUpgrade");
druProcess.start();
druProcess.waitForFinished(-1);
}

@ -3,11 +3,13 @@
#include "mainwindow.h"
#include <QDateTime>
#include <QObject>
#include <QStringList>
class QTimer;
class QSystemTrayIcon;
class ReleaseUpgradeWindow;
class Orchestrator : public QObject
{
@ -21,12 +23,18 @@ public slots:
private slots:
void checkForUpdates();
void handleUpdatesInstalled();
void handleUpdatesRefreshed();
void onNewReleaseAvailable(QStringList releaseCodes);
private:
QTimer *checkTimer;
QSystemTrayIcon *trayIcon;
QList<QStringList> updateInfo;
MainWindow updaterWindow;
QDateTime nextUpgradeCheck;
void doReleaseUpgrade();
void delayReleaseUpgrade(qint64 timestamp);
void declineReleaseUpgrade();
};
#endif // ORCHESTRATOR_H

@ -0,0 +1,67 @@
#include "releaseupgradewindow.h"
#include "ui_releaseupgradewindow.h"
#include "upgradedelaywindow.h"
#include <QDateTime>
#include <QMessageBox>
ReleaseUpgradeWindow::ReleaseUpgradeWindow(QString releaseCode, QWidget *parent) :
QDialog(parent),
ui(new Ui::ReleaseUpgradeWindow)
{
ui->setupUi(this);
ui->upgradeLabel->setText(tr("An upgrade to Lubuntu %1 is available! Would you like to install this upgrade now?").arg(releaseCode));
code=releaseCode;
connect(ui->upgradeButton, &QPushButton::clicked, this, &ReleaseUpgradeWindow::onUpgradeClicked);
connect(ui->remindButton, &QPushButton::clicked, this, &ReleaseUpgradeWindow::onRemindClicked);
connect(ui->declineButton, &QPushButton::clicked, this, &ReleaseUpgradeWindow::onDeclineClicked);
}
ReleaseUpgradeWindow::~ReleaseUpgradeWindow()
{
delete ui;
}
bool ReleaseUpgradeWindow::getUpgradeAccepted()
{
return upgradeAccepted;
}
qint64 ReleaseUpgradeWindow::getUpgradeDelayStamp()
{
return upgradeDelayStamp;
}
void ReleaseUpgradeWindow::onUpgradeClicked()
{
upgradeAccepted = true;
this->done(0);
}
void ReleaseUpgradeWindow::onRemindClicked()
{
UpgradeDelayWindow delayWindow;
delayWindow.exec();
if (delayWindow.getDelayDays() > 0) {
QDateTime delayDateTime = QDateTime::currentDateTime();
delayDateTime = delayDateTime.addDays(delayWindow.getDelayDays());
upgradeAccepted = false;
upgradeDelayStamp = delayDateTime.toSecsSinceEpoch();
this->done(0);
}
}
void ReleaseUpgradeWindow::onDeclineClicked()
{
QMessageBox confirmDialog;
confirmDialog.setWindowTitle(tr("Lubuntu Update"));
confirmDialog.setText(tr("You have declined the upgrade to Lubuntu %1.\nYou can upgrade manually by following the directions at https://manual.lubuntu.me/stable/D/upgrading.html.").arg(code));
confirmDialog.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
int result = confirmDialog.exec();
if (result == QMessageBox::Ok) {
upgradeDelayStamp = 0;
upgradeAccepted = false;
this->done(0);
}
}

@ -0,0 +1,32 @@
#ifndef RELEASEUPGRADEWINDOW_H
#define RELEASEUPGRADEWINDOW_H
#include <QDialog>
namespace Ui {
class ReleaseUpgradeWindow;
}
class ReleaseUpgradeWindow : public QDialog
{
Q_OBJECT
public:
explicit ReleaseUpgradeWindow(QString releaseCode, QWidget *parent = nullptr);
~ReleaseUpgradeWindow();
bool getUpgradeAccepted();
qint64 getUpgradeDelayStamp();
private slots:
void onUpgradeClicked();
void onRemindClicked();
void onDeclineClicked();
private:
Ui::ReleaseUpgradeWindow *ui;
QString code;
bool upgradeAccepted = false;
qint64 upgradeDelayStamp = 0;
};
#endif // RELEASEUPGRADEWINDOW_H

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ReleaseUpgradeWindow</class>
<widget class="QDialog" name="ReleaseUpgradeWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>393</width>
<height>116</height>
</rect>
</property>
<property name="windowTitle">
<string>Lubuntu Update</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="upgradeLabel">
<property name="text">
<string>upgrade available</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>6</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="upgradeButton">
<property name="text">
<string>Upgrade Now</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="remindButton">
<property name="text">
<string>Remind me later</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="declineButton">
<property name="text">
<string>Decline Upgrade</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/i18n">
<file alias="en_US">lubuntu-update_en_US.qm</file>
<file alias="es_ES">lubuntu-update_es_ES.qm</file>
<file alias="zh_CN">lubuntu-update_zh_CN.qm</file>
</qresource>
</RCC>

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en_US">
<context>
<name>ConffileHandlerDialog</name>
<message>
<location filename="../conffilehandlerdialog.ui" line="14"/>
<source>Configuration File Conflicts</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilehandlerdialog.ui" line="24"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Some of the newly installed updates have updated configuration files. &lt;/p&gt;&lt;p&gt;Please choose what to do with these files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilehandlerdialog.ui" line="81"/>
<source>Done</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ConffileWidget</name>
<message>
<location filename="../conffilewidget.ui" line="14"/>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilewidget.ui" line="20"/>
<source>Filename</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilewidget.ui" line="40"/>
<source>Keep old</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilewidget.ui" line="47"/>
<source>Replace with new</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilewidget.ui" line="54"/>
<source>Show diff</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DiffDisplayDialog</name>
<message>
<location filename="../diffdisplaydialog.ui" line="14"/>
<source>Dialog</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../diffdisplaydialog.ui" line="24"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Lines that start with a &amp;quot;+&amp;quot; only exist in the &lt;span style=&quot; font-weight:700;&quot;&gt;new&lt;/span&gt; file.&lt;/p&gt;&lt;p&gt;Lines that start with a &amp;quot;-&amp;quot; only exist in the &lt;span style=&quot; font-weight:700;&quot;&gt;old&lt;/span&gt; file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../diffdisplaydialog.ui" line="56"/>
<source>Done</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../mainwindow.ui" line="14"/>
<source>Lubuntu Update</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="25"/>
<source>0 package(s) will be updated. 0 of these updates are security-related.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="33"/>
<source>Packages</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="80"/>
<source>Install Updates</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="91"/>
<source>Check for Updates</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="98"/>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="53"/>
<source>To be installed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="56"/>
<source>To be upgraded</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="59"/>
<source>To be removed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="62"/>
<source>Held back (usually temporarily)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="73"/>
<source>%1 package(s) will be updated. %2 of these updates are security-related.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="139"/>
<source>Update installation complete.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Orchestrator</name>
<message>
<location filename="../orchestrator.cpp" line="38"/>
<source>Updates available!
%1 to upgrade, %2 to install, and %3 to remove.
Click the tray icon to install the updates.</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="es_ES">
<context>
<name>ConffileHandlerDialog</name>
<message>
<location filename="../conffilehandlerdialog.ui" line="14"/>
<source>Configuration File Conflicts</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilehandlerdialog.ui" line="24"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Some of the newly installed updates have updated configuration files. &lt;/p&gt;&lt;p&gt;Please choose what to do with these files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilehandlerdialog.ui" line="81"/>
<source>Done</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ConffileWidget</name>
<message>
<location filename="../conffilewidget.ui" line="14"/>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilewidget.ui" line="20"/>
<source>Filename</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilewidget.ui" line="40"/>
<source>Keep old</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilewidget.ui" line="47"/>
<source>Replace with new</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilewidget.ui" line="54"/>
<source>Show diff</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DiffDisplayDialog</name>
<message>
<location filename="../diffdisplaydialog.ui" line="14"/>
<source>Dialog</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../diffdisplaydialog.ui" line="24"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Lines that start with a &amp;quot;+&amp;quot; only exist in the &lt;span style=&quot; font-weight:700;&quot;&gt;new&lt;/span&gt; file.&lt;/p&gt;&lt;p&gt;Lines that start with a &amp;quot;-&amp;quot; only exist in the &lt;span style=&quot; font-weight:700;&quot;&gt;old&lt;/span&gt; file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../diffdisplaydialog.ui" line="56"/>
<source>Done</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../mainwindow.ui" line="14"/>
<source>Lubuntu Update</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="25"/>
<source>0 package(s) will be updated. 0 of these updates are security-related.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="33"/>
<source>Packages</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="80"/>
<source>Install Updates</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="91"/>
<source>Check for Updates</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="98"/>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="53"/>
<source>To be installed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="56"/>
<source>To be upgraded</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="59"/>
<source>To be removed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="62"/>
<source>Held back (usually temporarily)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="73"/>
<source>%1 package(s) will be updated. %2 of these updates are security-related.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="139"/>
<source>Update installation complete.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Orchestrator</name>
<message>
<location filename="../orchestrator.cpp" line="38"/>
<source>Updates available!
%1 to upgrade, %2 to install, and %3 to remove.
Click the tray icon to install the updates.</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="zh_CN">
<context>
<name>ConffileHandlerDialog</name>
<message>
<location filename="../conffilehandlerdialog.ui" line="14"/>
<source>Configuration File Conflicts</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilehandlerdialog.ui" line="24"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Some of the newly installed updates have updated configuration files. &lt;/p&gt;&lt;p&gt;Please choose what to do with these files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilehandlerdialog.ui" line="81"/>
<source>Done</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ConffileWidget</name>
<message>
<location filename="../conffilewidget.ui" line="14"/>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilewidget.ui" line="20"/>
<source>Filename</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilewidget.ui" line="40"/>
<source>Keep old</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilewidget.ui" line="47"/>
<source>Replace with new</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../conffilewidget.ui" line="54"/>
<source>Show diff</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DiffDisplayDialog</name>
<message>
<location filename="../diffdisplaydialog.ui" line="14"/>
<source>Dialog</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../diffdisplaydialog.ui" line="24"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Lines that start with a &amp;quot;+&amp;quot; only exist in the &lt;span style=&quot; font-weight:700;&quot;&gt;new&lt;/span&gt; file.&lt;/p&gt;&lt;p&gt;Lines that start with a &amp;quot;-&amp;quot; only exist in the &lt;span style=&quot; font-weight:700;&quot;&gt;old&lt;/span&gt; file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../diffdisplaydialog.ui" line="56"/>
<source>Done</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../mainwindow.ui" line="14"/>
<source>Lubuntu Update</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="25"/>
<source>0 package(s) will be updated. 0 of these updates are security-related.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="33"/>
<source>Packages</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="80"/>
<source>Install Updates</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="91"/>
<source>Check for Updates</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="98"/>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="53"/>
<source>To be installed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="56"/>
<source>To be upgraded</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="59"/>
<source>To be removed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="62"/>
<source>Held back (usually temporarily)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="73"/>
<source>%1 package(s) will be updated. %2 of these updates are security-related.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="139"/>
<source>Update installation complete.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Orchestrator</name>
<message>
<location filename="../orchestrator.cpp" line="38"/>
<source>Updates available!
%1 to upgrade, %2 to install, and %3 to remove.
Click the tray icon to install the updates.</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

@ -0,0 +1,42 @@
#include "upgradedelaywindow.h"
#include "ui_upgradedelaywindow.h"
UpgradeDelayWindow::UpgradeDelayWindow(QWidget *parent) :
QDialog(parent),
ui(new Ui::UpgradeDelayWindow)
{
ui->setupUi(this);
connect(ui->okButton, &QPushButton::clicked, this, &UpgradeDelayWindow::onOkClicked);
connect(ui->cancelButton, &QPushButton::clicked, this, &UpgradeDelayWindow::onCancelClicked);
}
UpgradeDelayWindow::~UpgradeDelayWindow()
{
delete ui;
}
qint64 UpgradeDelayWindow::getDelayDays()
{
return delayDays;
}
void UpgradeDelayWindow::onOkClicked()
{
switch (ui->timeTypeComboBox->currentIndex()) {
case 0: // days
delayDays = ui->timeSpinBox->value();
break;
case 1: // weeks
delayDays = ui->timeSpinBox->value() * 7;
break;
case 2: // months
delayDays = ui->timeSpinBox->value() * 28;
break;
}
this->done(0);
}
void UpgradeDelayWindow::onCancelClicked()
{
this->done(0);
}

@ -0,0 +1,28 @@
#ifndef UPGRADEDELAYWINDOW_H
#define UPGRADEDELAYWINDOW_H
#include <QDialog>
namespace Ui {
class UpgradeDelayWindow;
}
class UpgradeDelayWindow : public QDialog
{
Q_OBJECT
public:
explicit UpgradeDelayWindow(QWidget *parent = nullptr);
~UpgradeDelayWindow();
qint64 getDelayDays();
private slots:
void onOkClicked();
void onCancelClicked();
private:
Ui::UpgradeDelayWindow *ui;
qint64 delayDays;
};
#endif // UPGRADEDELAYWINDOW_H

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UpgradeDelayWindow</class>
<widget class="QDialog" name="UpgradeDelayWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>242</width>
<height>137</height>
</rect>
</property>
<property name="windowTitle">
<string>Lubuntu Update</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;How long would you like to wait before being reminded of the upgrade again?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QSpinBox" name="timeSpinBox"/>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="okButton">
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QComboBox" name="timeTypeComboBox">
<item>
<property name="text">
<string>Days</string>
</property>
</item>
<item>
<property name="text">
<string>Weeks</string>
</property>
</item>
<item>
<property name="text">
<string>Months</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

@ -0,0 +1,6 @@
#include "windowshowwatcher.h"
void WindowShowWatcher::showWindow()
{
emit showWindowTriggered();
}

@ -0,0 +1,23 @@
#ifndef WINDOWSHOWWATCHER_H
#define WINDOWSHOWWATCHER_H
#include <QObject>
#include <QDBusAbstractAdaptor>
#include <QDBusVariant>
class WindowShowWatcher : public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "me.lubuntu.LubuntuUpdate.window")
public:
explicit WindowShowWatcher(QObject *obj) : QDBusAbstractAdaptor(obj) {}
public slots:
void showWindow();
signals:
void showWindowTriggered();
};
#endif // WINDOWSHOWWATCHER_H

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en_US"></TS>
Loading…
Cancel
Save