diff options
-rw-r--r-- | ui/qt5/CMakeLists.txt | 13 | ||||
-rw-r--r-- | ui/qt5/horizonwizardpage.cc | 2 | ||||
-rw-r--r-- | ui/qt5/horizonwizardpage.hh | 4 | ||||
-rw-r--r-- | ui/qt5/runner/executepage.cc | 198 | ||||
-rw-r--r-- | ui/qt5/runner/executepage.hh | 70 | ||||
-rw-r--r-- | ui/qt5/runner/executorwizard.cc | 40 | ||||
-rw-r--r-- | ui/qt5/runner/executorwizard.hh | 29 | ||||
-rw-r--r-- | ui/qt5/runner/finishedpage.cc | 17 | ||||
-rw-r--r-- | ui/qt5/runner/finishedpage.hh | 23 | ||||
-rw-r--r-- | ui/qt5/runner/main.cc | 39 |
10 files changed, 434 insertions, 1 deletions
diff --git a/ui/qt5/CMakeLists.txt b/ui/qt5/CMakeLists.txt index e752749..930a84a 100644 --- a/ui/qt5/CMakeLists.txt +++ b/ui/qt5/CMakeLists.txt @@ -26,6 +26,15 @@ set(UI_SOURCES horizon.qrc) +set(RUN_QT_SOURCES + horizonwizardpage.cc + runner/main.cc + runner/executorwizard.cc + runner/executepage.cc + runner/finishedpage.cc + + horizon.qrc) + IF(INSTALL) LIST(APPEND UI_SOURCES commitpage.cc) ELSE(INSTALL) @@ -51,5 +60,7 @@ IF(INSTALL) pkg_check_modules(LIBX11 REQUIRED x11) pkg_check_modules(LibCap REQUIRED libcap) target_link_libraries(horizon-qt5 ${BCNM} ${SKARNET} ${LIBUDEV_LIBRARIES} ${LibCap_LIBRARIES} ${LIBX11_LIBRARIES} ${XKBFile_LIBRARIES}) - # add_executable(horizon-run-qt5 ${RUN_QT_SOURCES}) + add_executable(horizon-run-qt5 ${RUN_QT_SOURCES}) + target_link_libraries(horizon-run-qt5 Qt5::Widgets) + target_compile_definitions(horizon-run-qt5 PRIVATE IN_RUNNER) ENDIF(INSTALL) diff --git a/ui/qt5/horizonwizardpage.cc b/ui/qt5/horizonwizardpage.cc index 352de0e..dcd3d8f 100644 --- a/ui/qt5/horizonwizardpage.cc +++ b/ui/qt5/horizonwizardpage.cc @@ -49,6 +49,8 @@ void HorizonWizardPage::loadWatermark(string page) { setPixmap(QWizard::WatermarkPixmap, pixmap); } +#ifndef IN_RUNNER HorizonWizard *HorizonWizardPage::horizonWizard() const { return dynamic_cast<HorizonWizard *>(this->wizard()); } +#endif /* !IN_RUNNER */ diff --git a/ui/qt5/horizonwizardpage.hh b/ui/qt5/horizonwizardpage.hh index 6ed8a6e..16cc92c 100644 --- a/ui/qt5/horizonwizardpage.hh +++ b/ui/qt5/horizonwizardpage.hh @@ -16,7 +16,9 @@ #include <QWizardPage> #include <string> +#ifndef IN_RUNNER #include "horizonwizard.hh" +#endif /* !IN_RUNNER */ class HorizonWizardPage : public QWizardPage { public: @@ -24,7 +26,9 @@ public: QPixmap loadDPIAwarePixmap(std::string pixmap, std::string type = ".png"); void loadWatermark(std::string page); +#ifndef IN_RUNNER HorizonWizard *horizonWizard() const; +#endif /* !IN_RUNNER */ }; #endif /* !HORIZONWIZARDPAGE_HH */ diff --git a/ui/qt5/runner/executepage.cc b/ui/qt5/runner/executepage.cc new file mode 100644 index 0000000..817a61c --- /dev/null +++ b/ui/qt5/runner/executepage.cc @@ -0,0 +1,198 @@ +/* + * executepage.cc - Implementation of the UI.Perform page + * horizon-run-qt5, the Qt 5 executor user interface for + * Project Horizon + * + * Copyright (c) 2019 Adélie Linux and contributors. All rights reserved. + * This code is licensed under the AGPL 3.0 license, as noted in the + * LICENSE-code file in the root directory of this repository. + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#include "executepage.hh" + +#include <QAbstractButton> +#include <QGridLayout> +#include <QProcess> +#include <QTimer> +#include <QVBoxLayout> + +ExecutePage::ExecutePage(QWidget *parent) : HorizonWizardPage(parent) { + setTitle(tr("Installing Adélie Linux…")); + loadWatermark("intro"); + + QLabel *descLabel = new QLabel(tr("Please wait while System Installation performs the following tasks:")); + descLabel->setWordWrap(true); + + prepareStatus = new QLabel; + prepare = new QLabel(tr("Prepare installation")); + validateStatus = new QLabel; + validate = new QLabel(tr("Validate installation")); + diskStatus = new QLabel; + disk = new QLabel(tr("Configure hard disk(s)")); + preMetaStatus = new QLabel; + preMeta = new QLabel(tr("Initial configuration")); + netStatus = new QLabel; + net = new QLabel(tr("Networking configuration")); + pkgStatus = new QLabel; + pkg = new QLabel(tr("Install software")); + postMetaStatus = new QLabel; + postMeta = new QLabel(tr("Final configuration")); + + QGridLayout *progressLayout = new QGridLayout; + progressLayout->addWidget(prepareStatus, 0, 0); + progressLayout->addWidget(prepare, 0, 1); + progressLayout->addWidget(validateStatus, 1, 0); + progressLayout->addWidget(validate, 1, 1); + progressLayout->addWidget(diskStatus, 2, 0); + progressLayout->addWidget(disk, 2, 1); + progressLayout->addWidget(preMetaStatus, 3, 0); + progressLayout->addWidget(preMeta, 3, 1); + progressLayout->addWidget(netStatus, 4, 0); + progressLayout->addWidget(net, 4, 1); + progressLayout->addWidget(pkgStatus, 5, 0); + progressLayout->addWidget(pkg, 5, 1); + progressLayout->addWidget(postMetaStatus, 6, 0); + progressLayout->addWidget(postMeta, 6, 1); + progressLayout->setColumnStretch(1, 100); + + normalFont = validate->font(); + boldFont = normalFont; + boldFont.setBold(true); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(descLabel); + mainLayout->addStretch(); + mainLayout->addLayout(progressLayout); + mainLayout->addStretch(); + + setLayout(mainLayout); + + finishTimer = new QTimer(this); + finishTimer->setInterval(1500); + finishTimer->setSingleShot(true); + connect(finishTimer, &QTimer::timeout, [=]{ + wizard()->next(); + }); + + log.setFileName("/var/log/horizon/executor.log"); + Q_ASSERT(log.open(QFile::Append)); + + this->current = Prepare; + markRunning(this->current); + + executor = new QProcess(this); + executor->setProgram("../../tools/hscript-simulate/hscript-simulate"); + executor->setArguments({"/etc/horizon/installfile"}); + connect(executor, &QProcess::readyReadStandardError, + this, &ExecutePage::executorReady); + connect(executor, &QProcess::readyReadStandardOutput, + this, &ExecutePage::executorOutReady); + connect(executor, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), + this, &ExecutePage::executorFinished); + executor->start(); +} + +ExecutePage::Phase ExecutePage::stepToPhase(QString step) { + if(step == "validate") return Validate; + if(step == "disk") return Disk; + if(step == "pre-metadata") return PreMeta; + if(step == "net") return Net; + if(step == "pkgdb") return Pkg; + if(step == "post-metadata") return PostMeta; + Q_ASSERT(false); +} + +void ExecutePage::labelsForPhase(Phase phase, QLabel **icon, QLabel **text) { + switch(phase) { + case Prepare: + *icon = prepareStatus; + *text = prepare; + break; + case Validate: + *icon = validateStatus; + *text = validate; + break; + case Disk: + *icon = diskStatus; + *text = disk; + break; + case PreMeta: + *icon = preMetaStatus; + *text = preMeta; + break; + case Net: + *icon = netStatus; + *text = net; + break; + case Pkg: + *icon = pkgStatus; + *text = pkg; + break; + case PostMeta: + *icon = postMetaStatus; + *text = postMeta; + break; + } +} + +void ExecutePage::markRunning(Phase phase) { + QLabel *icon, *text; + labelsForPhase(phase, &icon, &text); + icon->setPixmap(loadDPIAwarePixmap("status-current", ".svg")); + text->setFont(boldFont); +} + +void ExecutePage::markFinished(Phase phase) { + QLabel *icon, *text; + labelsForPhase(phase, &icon, &text); + icon->setPixmap(loadDPIAwarePixmap("status-success", ".svg")); + text->setFont(normalFont); +} + +void ExecutePage::markFailed(Phase phase) { + QLabel *icon, *text; + labelsForPhase(phase, &icon, &text); + icon->setPixmap(loadDPIAwarePixmap("status-issue", ".svg")); + text->setFont(boldFont); +} + +void ExecutePage::executorReady() { + QByteArray msgs = executor->readAllStandardError(); + log.write(msgs); + + QStringList msgList = QString::fromUtf8(msgs).split("\n"); + for(auto &msg : msgList) { + QString msgType = msg.section('\t', 1, 1); + if(msgType == "step-start") { + this->current = stepToPhase(msg.section('\t', 2)); + /* validate means prepare is done */ + if(this->current == Validate) markFinished(Prepare); + markRunning(this->current); + } else if(msgType == "step-end") { + Q_ASSERT(stepToPhase(msg.section('\t', 2)) == this->current); + markFinished(this->current); + } else if(msgType == "log") { + QString severity = msg.section(": ", 1, 1); + if(severity == "error") { + markFailed(this->current); + } + } else { + /* !? */ + } + } +} + +void ExecutePage::executorOutReady() { + log.write(executor->readAllStandardOutput()); +} + +void ExecutePage::executorFinished(int code, QProcess::ExitStatus status) { + if(status != QProcess::NormalExit || code != 0) { + markFailed(this->current); + } + + wizard()->button(QWizard::CancelButton)->setEnabled(false); + finishTimer->start(); +} diff --git a/ui/qt5/runner/executepage.hh b/ui/qt5/runner/executepage.hh new file mode 100644 index 0000000..b3d11b4 --- /dev/null +++ b/ui/qt5/runner/executepage.hh @@ -0,0 +1,70 @@ +/* + * executepage.cc - Implementation of the UI.Perform page + * horizon-run-qt5, the Qt 5 executor user interface for + * Project Horizon + * + * Copyright (c) 2019 Adélie Linux and contributors. All rights reserved. + * This code is licensed under the AGPL 3.0 license, as noted in the + * LICENSE-code file in the root directory of this repository. + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#ifndef EXECUTEPAGE_HH +#define EXECUTEPAGE_HH + +#include "../horizonwizardpage.hh" + +#include <QFile> +#include <QLabel> +#include <QProcess> +#include <QTimer> + +class ExecutePage : public HorizonWizardPage { +public: + enum Phase { + Prepare, + Validate, + Disk, + PreMeta, + Net, + Pkg, + PostMeta + }; + + ExecutePage(QWidget *parent = nullptr); +private: + QLabel *prepareStatus; + QLabel *prepare; + QLabel *validateStatus; + QLabel *validate; + QLabel *diskStatus; + QLabel *disk; + QLabel *preMetaStatus; + QLabel *preMeta; + QLabel *netStatus; + QLabel *net; + QLabel *pkgStatus; + QLabel *pkg; + QLabel *postMetaStatus; + QLabel *postMeta; + + QFont normalFont, boldFont; + QProcess *executor; + QTimer *finishTimer; + QFile log; + + Phase current; + + Phase stepToPhase(QString step); + void labelsForPhase(Phase phase, QLabel **icon, QLabel **text); + void markRunning(Phase phase); + void markFinished(Phase phase); + void markFailed(Phase phase); + + void executorReady(); + void executorOutReady(); + void executorFinished(int code, QProcess::ExitStatus status); +}; + +#endif /* !EXECUTEPAGE_HH */ diff --git a/ui/qt5/runner/executorwizard.cc b/ui/qt5/runner/executorwizard.cc new file mode 100644 index 0000000..ae58a21 --- /dev/null +++ b/ui/qt5/runner/executorwizard.cc @@ -0,0 +1,40 @@ +/* + * executorwizard.cc - Implementation of the wizard class + * horizon-run-qt5, the Qt 5 executor user interface for + * Project Horizon + * + * Copyright (c) 2019 Adélie Linux and contributors. All rights reserved. + * This code is licensed under the AGPL 3.0 license, as noted in the + * LICENSE-code file in the root directory of this repository. + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#include "executorwizard.hh" + +#include <QAbstractButton> + +#include "executepage.hh" +#include "finishedpage.hh" + +ExecutorWizard::ExecutorWizard(QWidget *parent) : QWizard(parent) { + setWindowTitle(tr("Adélie Linux System Installation")); + + setFixedSize(QSize(650, 450)); + + setPage(Page_Execute, new ExecutePage); + setPage(Page_Finished, new FinishedPage); + + setOption(NoBackButtonOnStartPage); + setOption(NoBackButtonOnLastPage); + setOption(NoCancelButtonOnLastPage); + + QList<QWizard::WizardButton> buttonLayout; + buttonLayout << Stretch << FinishButton << CancelButton; + setButtonLayout(buttonLayout); +} + +/* just plain don't allow rejection (Esc/F3/Cancel) */ +void ExecutorWizard::reject() { + return; +} diff --git a/ui/qt5/runner/executorwizard.hh b/ui/qt5/runner/executorwizard.hh new file mode 100644 index 0000000..578f745 --- /dev/null +++ b/ui/qt5/runner/executorwizard.hh @@ -0,0 +1,29 @@ +/* + * executorwizard.cc - Implementation of the wizard class + * horizon-run-qt5, the Qt 5 executor user interface for + * Project Horizon + * + * Copyright (c) 2019 Adélie Linux and contributors. All rights reserved. + * This code is licensed under the AGPL 3.0 license, as noted in the + * LICENSE-code file in the root directory of this repository. + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#ifndef EXECUTORWIZARD_HH +#define EXECUTORWIZARD_HH + +#include <QWizard> + +class ExecutorWizard : public QWizard { +public: + enum { + Page_Execute, + Page_Finished + }; + + ExecutorWizard(QWidget *parent = nullptr); + void reject(); +}; + +#endif /* !EXECUTORWIZARD_HH */ diff --git a/ui/qt5/runner/finishedpage.cc b/ui/qt5/runner/finishedpage.cc new file mode 100644 index 0000000..153f703 --- /dev/null +++ b/ui/qt5/runner/finishedpage.cc @@ -0,0 +1,17 @@ +/* + * finishedpage.cc - Implementation of the UI.Finish page + * horizon-run-qt5, the Qt 5 executor user interface for + * Project Horizon + * + * Copyright (c) 2019 Adélie Linux and contributors. All rights reserved. + * This code is licensed under the AGPL 3.0 license, as noted in the + * LICENSE-code file in the root directory of this repository. + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#include "finishedpage.hh" + +FinishedPage::FinishedPage(QWidget *parent) : QWizardPage(parent) { + setTitle(tr("Adélie Linux Successfully Installed")); +} diff --git a/ui/qt5/runner/finishedpage.hh b/ui/qt5/runner/finishedpage.hh new file mode 100644 index 0000000..4a97b28 --- /dev/null +++ b/ui/qt5/runner/finishedpage.hh @@ -0,0 +1,23 @@ +/* + * finishedpage.hh - Definition of the UI.Finish page + * horizon-run-qt5, the Qt 5 executor user interface for + * Project Horizon + * + * Copyright (c) 2019 Adélie Linux and contributors. All rights reserved. + * This code is licensed under the AGPL 3.0 license, as noted in the + * LICENSE-code file in the root directory of this repository. + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#ifndef FINISHEDPAGE_HH +#define FINISHEDPAGE_HH + +#include <QWizardPage> + +class FinishedPage : public QWizardPage { +public: + FinishedPage(QWidget *parent = nullptr); +}; + +#endif /* !FINISHEDPAGE_HH */ diff --git a/ui/qt5/runner/main.cc b/ui/qt5/runner/main.cc new file mode 100644 index 0000000..bac429d --- /dev/null +++ b/ui/qt5/runner/main.cc @@ -0,0 +1,39 @@ +/* + * main.cc - Implementation of the UI.Perform interface + * horizon-run-qt5, the Qt 5 executor user interface for + * Project Horizon + * + * Copyright (c) 2019 Adélie Linux and contributors. All rights reserved. + * This code is licensed under the AGPL 3.0 license, as noted in the + * LICENSE-code file in the root directory of this repository. + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#include <QApplication> +#include <QIcon> +#include <QLibraryInfo> +#include <QTranslator> + +#include "executorwizard.hh" + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + + QString translatorFileName = QLatin1String("qt_"); + translatorFileName += QLocale::system().name(); + QTranslator *translator = new QTranslator(&app); + if(translator->load(translatorFileName, + QLibraryInfo::location( + QLibraryInfo::TranslationsPath + ))) { + app.installTranslator(translator); + } + + app.setWindowIcon(QIcon(":/horizon-256.png")); + + ExecutorWizard wizard; + wizard.show(); + + return app.exec(); +} |