diff options
Diffstat (limited to 'src/gui.cpp')
-rw-r--r-- | src/gui.cpp | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/src/gui.cpp b/src/gui.cpp new file mode 100644 index 0000000..e27086a --- /dev/null +++ b/src/gui.cpp @@ -0,0 +1,217 @@ +#include "gui.h" +#include "progress.h" + +#include <QLabel> +#include <QVBoxLayout> +#include <QPushButton> +#include <QNetworkAccessManager> +#include <QNetworkReply> +#include <QMessageBox> +#include <QTimer> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +static QString STATE_COPYING("COPYING"); // Still busy copying original file, new chunks are only accepted in already copied range +static QString STATE_WAITING_FOR_UPLOAD_DONE("WAITING_FOR_UPLOAD_DONE"); // Copying done, just waiting for new chunks until client signals that it's done +static QString STATE_WAITING_FOR_COPYING_DONE("WAITING_FOR_COPYING_DONE"); // Copying not done, but dnbd3-fuse already told us it's done, wait for copying +static QString STATE_UPLOAD_DONE("UPLOAD_DONE"); // dnbd3-fuse signaled merge request, upload is thus done and no new chunks are accepted +static QString STATE_PROCESSING("PROCESSING"); // Hashing, renaming, creating DB entry +static QString STATE_ERROR("ERROR"); +static QString STATE_COMPLETELY_DONE("COMPLETELY_DONE"); + +Gui::Gui(const char *urlbase, const char *uuid, QWidget *parent) + : QDialog(parent) + , _nam(new QNetworkAccessManager(this)) + , _urlStatus(QString(urlbase).append("status/").append(uuid)) + , _urlAbort(QString(urlbase).append("abort/").append(uuid)) + , _urlFinish(QString(urlbase).append("finish/").append(uuid)) + , _denyInteraction(false) + , _tmrStatus(new QTimer(this)) + , _status(nullptr) +{ + _nam->setAutoDeleteReplies(true); + _nam->setTransferTimeout(5000); + setupUi(); + auto flags = windowFlags(); + setWindowFlags(flags & ~(Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint)); + _tmrStatus->start(2500); + QObject::connect(_tmrStatus, &QTimer::timeout, [this]() { + QNetworkReply *reply = _nam->get(QNetworkRequest(_urlStatus)); + QObject::connect(reply, &QNetworkReply::finished, [reply, this]() { + // JSON + auto repData = reply->readAll(); + auto doc = QJsonDocument::fromJson(repData); + if (doc.isEmpty() || !doc.isObject()) { + QString str = QString::fromUtf8(repData); + int code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (code == 404) { + _remoteState = STATE_ERROR; + _status->setText(tr("Sitzung dem Server nicht bekannt")); + updateButtons(); + } else if (str.length() > 0 && str.length() < 160) { + _status->setText(QLatin1String("Server: ") + str); + } else { + _status->setText(tr("Fehler beim Statusabruf")); + } + return; + } + // Got JSON + auto outer = doc.object(); + QJsonValue state = outer[QLatin1String("state")]; + if (state.isString()) { + auto ns = state.toString(); + if (ns != _remoteState) { + _status->setText(ns); + _remoteState = ns; + updateButtons(); + } + } + QJsonValue tasks = outer[QLatin1String("tasks")]; + if (tasks.isArray()) { + auto arr = tasks.toArray(); + for (auto it : arr) { + if (!it.isObject()) + continue; + auto o = it.toObject(); + auto jt = o.value(QLatin1String("title")); + if (!jt.isString()) + continue; + auto jp = o.value(QLatin1String("percent")); + auto je = o.value(QLatin1String("error")); + QString title = jt.toString(); + QString err = je.isString() ? je.toString() : QLatin1String(""); + int percent = err.isEmpty() && jp.isDouble() ? (int)jp.toDouble() : -1; + Progress *item = _items.value(title); + if (item == nullptr) { + if (_items.size() > 10) + continue; + item = new Progress(title, this); + _itemBox->addWidget(item); + _items.insert(title, item); + } + item->setProgress(percent); + item->setCaption(title + QLatin1String(" ") + err); + } + } + }); + }); +} + +Gui::~Gui() +{ + _nam->blockSignals(true); +} + +void Gui::setupUi() +{ + QVBoxLayout *outerVert = new QVBoxLayout(this); + outerVert->setMargin(3); + + _itemBox = new QVBoxLayout; + _itemBox->setObjectName("ItemLayout"); + outerVert->addLayout(_itemBox); + + QLayout *buttons = new QHBoxLayout; + buttons->setObjectName("ButtonLayout"); + outerVert->addLayout(buttons); + + _status = new QLabel("-", this); + _itemBox->addWidget(_status); + + _btnAbort = new QPushButton(tr("Abbrechen/Löschen"), this); + buttons->addWidget(_btnAbort); + QObject::connect(_btnAbort, &QPushButton::clicked, this, &Gui::pushedCancel); + + _btnOk = new QPushButton(tr("Änderungen permanent behalten"), this); + buttons->addWidget(_btnOk); + QObject::connect(_btnOk, &QPushButton::clicked, this, &Gui::pushedOk); +} + +void Gui::updateButtons() +{ + if (_remoteState == STATE_COMPLETELY_DONE || _remoteState == STATE_PROCESSING) { + if (_remoteState == STATE_COMPLETELY_DONE) { + _tmrStatus->stop(); + } + _btnAbort->setEnabled(false); + _btnOk->setEnabled(true); + _btnOk->setText(tr("Schließen")); + } else if (_remoteState == STATE_UPLOAD_DONE) { + _btnAbort->setEnabled(true); + _btnOk->setEnabled(true); + } else if (_remoteState == STATE_ERROR) { + _btnAbort->setEnabled(true); + _btnOk->setEnabled(false); + _tmrStatus->stop(); + _btnAbort->setText(tr("Schließen")); + } else { + _btnAbort->setEnabled(true); + _btnOk->setEnabled(false); + } +} + +void Gui::pushedCancel(bool) +{ + if (_remoteState == STATE_ERROR) { + this->close(); + return; + } + if (_denyInteraction) + return; + if (_remoteState != STATE_ERROR) { + int ret = QMessageBox::question(this, tr("Bestätigung"), tr("Wollen Sie diesen Bearbeitungsvorgang wirklich abbrechen?")); + if (ret == QMessageBox::No) + return; + } + if (_remoteState == STATE_COMPLETELY_DONE) { + _btnAbort->setEnabled(false); + return; + } + _denyInteraction = true; + QNetworkReply *reply = _nam->post(QNetworkRequest(_urlAbort), QByteArray()); + connect(reply, &QNetworkReply::finished, [reply, this]() { + QString msg = QString::fromUtf8(reply->readAll()); + if (reply->error() != QNetworkReply::NoError) { + QMessageBox::critical(this, tr("Fehler"), msg); + _denyInteraction = false; + } else { + _btnAbort->setEnabled(false); + _btnOk->setEnabled(false); + _remoteState = STATE_ERROR; + QMessageBox::information(this, tr("Ergebnis"), msg); + QTimer::singleShot(2000, [this]() { + this->close(); + }); + } + }); +} + +void Gui::pushedOk(bool) +{ + if (_remoteState == STATE_COMPLETELY_DONE || _remoteState == STATE_PROCESSING) { + this->close(); + return; + } + if (_denyInteraction) + return; + if (_remoteState == STATE_UPLOAD_DONE) { + int ret = QMessageBox::question(this, tr("Bestätigung"), tr("Die Änderungen an der VM werden dauerhaft beibehalten. Fortfahren?")); + if (ret == QMessageBox::No) + return; + } + _denyInteraction = true; + QNetworkReply *reply = _nam->get(QNetworkRequest(_urlFinish)); + connect(reply, &QNetworkReply::finished, [reply, this]() { + QString msg = QString::fromUtf8(reply->readAll()); + int code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (reply->error() != QNetworkReply::NoError || (code < 200 || code >= 300)) { + QMessageBox::critical(this, tr("Fehler"), msg); + _denyInteraction = false; + } else { + _remoteState = STATE_PROCESSING; + _status->setText(tr("Finalisiere...")); + updateButtons(); + } + }); +} |