Browse Source

Logic updated

Ivan Arkhipov 5 years ago
parent
commit
30fb2a67d2

+ 0 - 2
src/Legacy/Legacy.pro

@@ -37,7 +37,6 @@ SOURCES += \
     models/patch/videospatch.cpp \
     models/patch/textspatch.cpp \
     models/patch/graphicspatch.cpp \
-    models/lotrodatmanagerobserver.cpp \
     models/patchlist.cpp \
     legacyapplication.cpp
 
@@ -67,7 +66,6 @@ HEADERS += \
     models/patch/videospatch.h \
     models/patch/textspatch.h \
     models/patch/graphicspatch.h \
-    models/lotrodatmanagerobserver.h \
     models/patchlist.h \
     legacyapplication.h
 

+ 53 - 8
src/Legacy/legacyapplication.cpp

@@ -4,6 +4,50 @@
 #include <QMessageBox>
 #include <QResource>
 #include <QFontDatabase>
+#include <QMessageLogContext>
+#include <QTextStream>
+
+void logMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
+{
+    QString message_type;
+
+    switch (type) {
+    case QtDebugMsg:
+        message_type = "[DEBG]";
+        break;
+    case QtInfoMsg:
+        message_type = "[INFO]";
+        break;
+    case QtWarningMsg:
+        message_type = "[WARN]";
+        break;
+    case QtCriticalMsg:
+        message_type = "[CRIT]";
+        break;
+    case QtFatalMsg:
+        message_type = "[FATL]";
+        break;
+    default:
+        message_type = "[UNKN]";
+        break;
+    }
+
+    QString filename = context.category;
+    QString function = context.function;
+    QString line_number = QString::number(context.line);
+
+    QString s = QString("%1: %2\n")
+                .arg(message_type)
+                .arg(msg);
+
+    QFile log_file("legacy_v2.log");
+    log_file.open(QIODevice::ReadWrite | QIODevice::Append);
+    QTextStream stream(&log_file);
+    stream << s;
+    log_file.close();
+    fprintf(stderr, "%s", qUtf8Printable(s));
+    fflush(stderr);
+}
 
 LegacyApplication::LegacyApplication(int &argc, char **argv)
     : QApplication(argc, argv)
@@ -12,11 +56,12 @@ LegacyApplication::LegacyApplication(int &argc, char **argv)
     , patch_list(nullptr)
     , gui(nullptr)
 {
+    qInstallMessageHandler(logMessageHandler);
 }
 
 bool LegacyApplication::init()
 {
-    qDebug() << __func__ << "Starting initialisation...";
+    qDebug() << "Starting initialisation...";
 
     QCoreApplication::setOrganizationName("LotroLegacy");
     QCoreApplication::setOrganizationDomain("translate.lotros.ru");
@@ -26,11 +71,11 @@ bool LegacyApplication::init()
     QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, QApplication::applicationDirPath());
     QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, QApplication::applicationDirPath());
 
-    qDebug() << __func__ << "Checking if there's another instance of Legacy";
+    qDebug() << "Checking if there's another instance of Legacy";
 
     QLockFile lockFile(QDir::temp().absoluteFilePath("rulotro.lock"));
     if(!lockFile.tryLock(1)){
-        qDebug() << __func__ << "Lock file already exists! Some other application is already running...";
+        qDebug() << "Lock file already exists! Some other application is already running...";
         QMessageBox msgBox;
         msgBox.setIcon(QMessageBox::Warning);
         msgBox.setText("Приложение уже запущено.\nРазрешено запускать только один экземпляр приложения.");
@@ -38,7 +83,7 @@ bool LegacyApplication::init()
         return false;
     }
 
-    qDebug() << __func__ << "Initialising fonts and resources";
+    qDebug() << "Initialising fonts and resources";
 
     QResource::registerResource(QApplication::applicationDirPath() + "/data01.gtr");
     QResource::registerResource(QApplication::applicationDirPath() + "/data02.gtr");
@@ -48,7 +93,7 @@ bool LegacyApplication::init()
     QFontDatabase::addApplicationFont(":/fonts/EBGaramond.ttf");
     QFontDatabase::addApplicationFont(":/fonts/aniron.ttf");
 
-    qDebug() << __func__ << "Starting Lotro Manager initialization...";
+    qDebug() << "Starting Lotro Manager initialization...";
 
     lotro_dat_manager = new LotroDatManager();
     lotro_dat_manager_thread = new QThread();
@@ -56,14 +101,14 @@ bool LegacyApplication::init()
     lotro_dat_manager->moveToThread(lotro_dat_manager_thread);
     lotro_dat_manager_thread->start();
 
-    qDebug() << __func__ << "Starting Patch list initialisation...";
+    qDebug() << "Starting Patch list initialisation...";
 
     patch_list = new PatchList(lotro_dat_manager);
 
-    qDebug() << __func__ << "Starting GUI initialisation...";
+    qDebug() << "Starting GUI initialisation...";
 
     gui = new MainWindow(patch_list);
-    qDebug() << __func__ << "Legacy Initialization finished!";
+    qDebug() << "Legacy Initialization finished!";
 
     patch_list->startAutoUpdate();
     patch_list->initialize();

+ 1 - 0
src/Legacy/main.cpp

@@ -1,5 +1,6 @@
 #include "legacyapplication.h"
 
+
 // Global. Should be updated only by MainWindow!!!
 double window_height = 1000;
 double window_width = 648;

+ 10 - 10
src/Legacy/models/downloader.cpp

@@ -13,7 +13,7 @@ Downloader::Downloader(QObject *parent) :QObject(parent), busy_(false)
 Downloader::~Downloader() {
 }
 
-QUrl Downloader::getUrl()
+QUrl Downloader::getUrl() const
 {
     return url_;
 }
@@ -23,7 +23,7 @@ void Downloader::setUrl(const QUrl &_url)
     url_ = _url;
 }
 
-void Downloader::waitForDownloaded()
+void Downloader::waitForDownloaded() const
 {
     if (!busy_)
         return;
@@ -33,27 +33,27 @@ void Downloader::waitForDownloaded()
     loop.exec();
 }
 
-bool Downloader::isStarted()
+bool Downloader::isStarted() const
 {
     return busy_;
 }
 
-double Downloader::getPercent()
+double Downloader::getPercent() const
 {
     return double(bytes_downloaded_) * 100.0 / double(bytes_total_);
 }
 
-quint64 Downloader::getBytesTotal()
+quint64 Downloader::getBytesTotal() const
 {
     return bytes_total_;
 }
 
-quint64 Downloader::getBytesDownloaded()
+quint64 Downloader::getBytesDownloaded() const
 {
     return bytes_downloaded_;
 }
 
-quint64 Downloader::getElapsedTime()
+quint64 Downloader::getElapsedTime() const
 {
     quint64 delta_size = getBytesTotal() - getBytesDownloaded();
     quint64 avg_speed = qMax(quint64(1), getAverageSpeed());
@@ -66,17 +66,17 @@ quint64 Downloader::getElapsedTime()
     return delta_size / avg_speed;
 }
 
-quint64 Downloader::getCurrentSpeed()
+quint64 Downloader::getCurrentSpeed() const
 {
     return download_speed_;
 }
 
-quint64 Downloader::getAverageSpeed()
+quint64 Downloader::getAverageSpeed() const
 {
     return average_speed_;
 }
 
-Downloader::Status Downloader::getDownloadStatus()
+Downloader::Status Downloader::getDownloadStatus() const
 {
     return {isStarted(), getPercent(), getBytesTotal(), getBytesDownloaded(), getCurrentSpeed(), getAverageSpeed(), getElapsedTime()};
 }

+ 10 - 10
src/Legacy/models/downloader.h

@@ -56,19 +56,19 @@ private:
 public:
     virtual ~Downloader();
 
-    QUrl getUrl();
+    QUrl getUrl() const;
     void setUrl(const QUrl& _url);
-    void waitForDownloaded();
+    void waitForDownloaded() const;
 
-    bool isStarted();
-    double getPercent();
-    quint64 getBytesTotal();
-    quint64 getBytesDownloaded();
-    quint64 getElapsedTime();
-    quint64 getCurrentSpeed();
-    quint64 getAverageSpeed();
+    bool isStarted() const;
+    double getPercent() const;
+    quint64 getBytesTotal() const;
+    quint64 getBytesDownloaded() const;
+    quint64 getElapsedTime() const;
+    quint64 getCurrentSpeed() const;
+    quint64 getAverageSpeed() const;
 
-    Status getDownloadStatus();
+    Status getDownloadStatus() const;
 
     static QString getSizeFormatted(quint64 bytes);
     static QString getSpeedFormatted(quint64 speed_bytes_per_sec);

+ 1 - 24
src/Legacy/models/lotrodatmanager.cpp

@@ -1,7 +1,6 @@
 #include "lotrodatmanager.h"
 #include "models/filesystem.h"
 #include "models/settings.h"
-#include "models/lotrodatmanagerobserver.h"
 
 #include <QDebug>
 #include <QProcess>
@@ -14,13 +13,12 @@ LotroDatManager::LotroDatManager(QObject *parent) :
     QObject(parent) {
 
     qRegisterMetaType<LOTRO_DAT::DatLocaleManager::LOCALE>();
-    observer_ = new LotroDatManagerObserver(this);
+    client_local_file_.GetStatusModule().AddStatusChangedCallbackFunction(file_status_updated_callback_);
 }
 
 LotroDatManager::~LotroDatManager()
 {
     deinitializeManager();
-    delete observer_;
 }
 
 bool LotroDatManager::Initialised()
@@ -33,26 +31,6 @@ bool LotroDatManager::NotPatched()
     return !client_local_file_.GetStatusModule().CheckIfNotPatched() && !client_local_file_.GetStatusModule().CheckIfNotPatched();
 }
 
-LotroDatManager::Status LotroDatManager::getStatus()
-{
-    Status result;
-    result.status = client_local_file_.GetStatusModule().GetStatus();
-    result.percent = client_local_file_.GetStatusModule().GetPercentage();
-    result.dbg_message = QString::fromStdString(client_local_file_.GetStatusModule().GetDebugMessage());
-
-//        client_local_file_.GetStatusModule().GetStatus(),
-//        double(client_local_file_.GetStatusModule().GetPercentage()),
-//        QString::fromStdString(client_local_file_.GetStatusModule().GetDebugMessage())
-//    };
-
-    return result;
-}
-
-LotroDatManagerObserver *LotroDatManager::getObserver()
-{
-    return observer_;
-}
-
 void LotroDatManager::initializeManager()
 {
     emit operationStarted("initializeManager");
@@ -319,4 +297,3 @@ bool LotroDatManager::startLotroLauncherWithParameters(LOTRO_DAT::DatLocaleManag
         return false;
     }
 }
-

+ 10 - 27
src/Legacy/models/lotrodatmanager.h

@@ -11,38 +11,22 @@
 
 using LOTRO_DAT::DatStatus;
 
-class LotroDatManagerObserver;
-
 class LotroDatManager : public QObject
 {
     Q_OBJECT
 
 public:
-    struct Status {
-        DatStatus::DAT_STATUS status;
-        double percent;
-        QString dbg_message;
-
-        Status()
-            : status(DatStatus::DAT_STATUS::E_FREE)
-            , percent(0)
-            , dbg_message("")
-        {
-        }
-    };
-
-    friend bool operator !=(const Status& a, const Status& b) {
-        return a.status != b.status || a.percent != b.percent;
-    }
+    using Status = LOTRO_DAT::DatStatus::ProgressInfo;
 
     enum Category : int {
         E_TEXTS_COMMON = 100,
         E_TEXTS_ITEMS = 101,
         E_TEXTS_EMOTES = 102,
-        E_VIDEOS_PATHS = 103,
+        E_TEXTS_VIDEOS_REFS = 103,
         E_MAPS_COMMON = 200,
         E_TEXTURES_COMMON = 201,
-        E_AUDIOS_COMMON = 300
+        E_AUDIOS_COMMON = 300,
+        E_FONTS_COMMON = 400
     };
 
 public:
@@ -54,9 +38,6 @@ public:
 
     bool NotPatched();
 
-    Status getStatus();
-
-    LotroDatManagerObserver *getObserver();
 
 public slots:
     void initializeManager();
@@ -67,9 +48,9 @@ public slots:
 
     void installPatch(QString patch_name, QString database_path);
 
-    void enableCategory(QString patch_name, Category category);
+    void enableCategory(QString patch_name, LotroDatManager::Category category);
 
-    void disableCategory(QString patch_name, Category category);
+    void disableCategory(QString patch_name, LotroDatManager::Category category);
 
     void createBackup();
 
@@ -84,12 +65,14 @@ signals:
     void operationStarted(QString operation_name, QVector<QVariant> args = {});
     void errorOccured(QString operation_name, QVector<QVariant> args = {}, QString message = "No error message provided");
     void operationFinished(QString operation_name, QVector<QVariant> args = {}, bool successful = true);
+    void statusChanged(Status status);
 
 private:
-    LotroDatManagerObserver* observer_;
-
     LOTRO_DAT::DatFile client_local_file_;
     LOTRO_DAT::DatFile client_general_file_;
+    const LOTRO_DAT::DatStatus::Callback file_status_updated_callback_ = [&](Status status) {
+        emit statusChanged(status);
+    };
 };
 
 #endif // LEGACYAPP_H

+ 105 - 162
src/Legacy/models/patch/graphicspatch.cpp

@@ -1,7 +1,6 @@
 #include "graphicspatch.h"
 #include "LotroDat/LotroDat.h"
 #include "LotroDat/Database.h"
-#include "models/lotrodatmanagerobserver.h"
 #include "models/lotrodatmanager.h"
 
 #include <QUrlQuery>
@@ -11,12 +10,11 @@ GraphicsPatch::GraphicsPatch(LotroDatManager *mgr, QObject *parent) : Patch("Gra
 {
     connect(lotro_mgr_, &LotroDatManager::operationStarted, this, &GraphicsPatch::onLotroDatManagerOperationStarted);
     connect(lotro_mgr_, &LotroDatManager::operationFinished, this, &GraphicsPatch::onLotroDatManagerOperationFinished);
-    connect(lotro_mgr_->getObserver(), &LotroDatManagerObserver::statusChanged, this, &GraphicsPatch::onLotroDatManagerStatusChanged);
+    connect(lotro_mgr_, &LotroDatManager::statusChanged, this, &GraphicsPatch::onLotroDatManagerStatusChanged);
 }
 
 void GraphicsPatch::checkForUpdates()
 {
-    emit operationStarted("checkForUpdates", this);
     QUrlQuery query; // query for building GET-request aka patch-version
 
     foreach (QString db_name, databases_names) {
@@ -35,26 +33,26 @@ void GraphicsPatch::checkForUpdates()
     downloader.waitForDownloaded();
 
     if (target_array.isEmpty()) {
-        qDebug() << __FUNCTION__ << "Cannot download, target_array is empty!";
-        emit errorOccured("checkForUpdates", this, "QueryDownloadFailed");
-        emit operationFinished("checkForUpdates", this, false);
+        qWarning() << *this << "Cannot check for updates, target_array is empty!";
+        emit errorOccured(E_CHECKFORUPDATES, this, "");
+        emit operationFinished(E_CHECKFORUPDATES, this);
         return;
     }
 
     QStringList patch_info = QString(target_array).split('|');
     if (patch_info.size() != databases_names.size()) {
-        qDebug() << __FUNCTION__ << "Incorrect patches number! Data: " << patch_info;
-        emit errorOccured("checkForUpdates", this, "IncorrectQueryResult");
-        emit operationFinished("checkForUpdates", this, false);
+        qCritical() << __FUNCTION__ << "Incorrect patches number! Data: " << patch_info;
+        emit errorOccured(E_CHECKFORUPDATES, this, "");
+        emit operationFinished(E_CHECKFORUPDATES, this);
         return;
     }
 
     for (int i = 0; i < databases_names.size(); ++i) {
         QStringList patch_data = patch_info[i].split(":::");
         if (patch_data.size() != 3) {
-            qDebug() << __FUNCTION__ << "Incorrect patch entry size! Entry: " << patch_data;
-            emit errorOccured("checkForUpdates", this, "IncorrectDbInfo");
-            emit operationFinished("checkForUpdates", this, false);
+            qCritical() << __FUNCTION__ << "Incorrect patch entry size! Entry: " << patch_data;
+            emit errorOccured(E_CHECKFORUPDATES, this, "");
+            emit operationFinished(E_CHECKFORUPDATES, this);
             return;
         }
 
@@ -65,42 +63,32 @@ void GraphicsPatch::checkForUpdates()
         Settings::setValue("PatchDatabases/" + databases_names[i] + "/datetime", patch_data[2]);
         Settings::setValue("PatchDatabases/" + databases_names[i] + "/path", patch_filename);
     }
-    emit operationFinished("checkForUpdates", this, true);
+
+    emit operationFinished(E_CHECKFORUPDATES, this);
 }
 
 void GraphicsPatch::download()
 {
-    if (elapsed_patches_to_download_ != 0) {
-        qDebug() << "Trying to start download of patch set " << patch_name_ << " while download is still active!";
-        return;
-    }
-
-    emit operationStarted("download", this);
-
-    bool download_started = false;
-
     foreach (QString db_name, databases_names) {
         QString settings_prefix = "PatchDatabases/" + db_name;
 
         QString target_filename = QApplication::applicationDirPath() + "/" + Settings::getValue(settings_prefix + "/path").toString();
-        qDebug() << "GraphicsPatch: Checking if there's no need to download file " << target_filename;
+        qDebug() << patch_name_ << ": Checking if file " << target_filename << " matches its hashsum";
 
         if (FileSystem::fileHash(target_filename) == Settings::getValue(settings_prefix + "/hashsum").toString()) {
-            qDebug() << "GraphicsPatch: file " << target_filename << " is fresh, no need to download";
+            qInfo() << *this << ": file " << target_filename << " is up-to-date, no need to download";
             continue;
         }
 
         FileSystem::createFilePath(target_filename);
         QFile* target_file = new QFile(target_filename);
         if (!target_file->open(QIODevice::ReadWrite | QIODevice::Truncate)) {
-            emit errorOccured("download", this, "CantOpenDbFile");
+            qWarning() << *this << "Cannot open file " << target_filename;
             continue;
         }
 
-        qDebug() << "GraphicsPatch: beginning download of file " << target_filename;
-        download_started = true;
+        qInfo() << *this << ": beginning download of file " << target_filename;
 
-        ++elapsed_patches_to_download_;
         Downloader* downloader = new Downloader();
         downloader->setUrl(Settings::getValue(settings_prefix + "/url").toUrl());
         downloader->targetFile = target_file;
@@ -110,77 +98,53 @@ void GraphicsPatch::download()
         downloader->start();
     }
 
-    if (!download_started) {
-        emit operationFinished("download", this, true);
-    } // otherwise will be emitted on the last onDownloaderFinished signal
+    if (downloaders_.empty()) {
+        emit operationFinished(E_DOWNLOAD, this);
+    }
 }
 
 void GraphicsPatch::install()
 {
-    if (elapsed_databases_to_install_ > 0) {
-        emit errorOccured("install", this, "Установка набора графических патчей ещё не закончена");
-        return;
-    }
-
-    emit operationStarted("install", this);
-
-    status_.process = CurrentProcess::E_INSTALL;
-    status_.percent = 0;
-    status_.total_parts = 3;
-    status_.current_part = 0;
+    emit operationFinished(E_INSTALL, this);
+    return;
 
     installLoadscreens();
 
-    status_.process = CurrentProcess::E_AWAITING_INSTALL;
-
     foreach (QString db_name, QStringList({"image", "texture"})) {
-        ++elapsed_databases_to_install_;
-        QMetaObject::invokeMethod(lotro_mgr_,
-                                  "installPatch",
-                                  Qt::QueuedConnection,
-                                  Q_ARG(QString, "GraphicsPatch_" + db_name),
+        ++elapsed_patches_to_install_;
+        QMetaObject::invokeMethod(lotro_mgr_, "installPatch", Qt::QueuedConnection,
+                                  Q_ARG(QString, getPatchName() + "_" + db_name),
                                   Q_ARG(QString, Settings::getValue("PatchDatabases/" + db_name + "/path").toString())
                                   );
     }
-
 }
 
 void GraphicsPatch::activate()
 {
+    emit operationFinished(E_ACTIVATE, this);
+    return;
+
     if (Settings::getValue("Components/loadscreens").toBool()) {
         enableLoadscreens();
     } else {
         disableLoadscreens();
     }
 
-    if (Settings::getValue("Components/maps").toBool()) {
-        QMetaObject::invokeMethod(lotro_mgr_,
-                                  "enableCategory",
-                                  Qt::QueuedConnection,
-                                  Q_ARG(QString, "GraphicsPatch_maps"),
-                                  Q_ARG(LotroDatManager::Category, LotroDatManager::Category::E_MAPS_COMMON)
-                                  );
-    } else {
-        QMetaObject::invokeMethod(lotro_mgr_,
-                                  "disableCategory",
-                                  Q_ARG(QString, "GraphicsPatch_maps"),
+    QString operation_name;
+
+    operation_name = Settings::getValue("Components/maps").toBool() ? "enableCategory" : "disableCategory";
+    ++elapsed_patches_to_install_;
+    QMetaObject::invokeMethod(lotro_mgr_, operation_name.toLocal8Bit().constData(), Qt::QueuedConnection,
+                                  Q_ARG(QString, getPatchName() + "_maps"),
                                   Q_ARG(LotroDatManager::Category, LotroDatManager::Category::E_MAPS_COMMON)
                                   );
-    }
 
-    if (Settings::getValue("Components/textures").toBool()) {
-        QMetaObject::invokeMethod(lotro_mgr_,
-                                  "enableCategory",
-                                  Q_ARG(QString, "GraphicsPatch_maps"),
-                                  Q_ARG(LotroDatManager::Category, LotroDatManager::Category::E_TEXTURES_COMMON)
-                                  );
-    } else {
-        QMetaObject::invokeMethod(lotro_mgr_,
-                                  "disableCategory",
-                                  Q_ARG(QString, "GraphicsPatch_maps"),
+    operation_name = Settings::getValue("Components/textures").toBool() ? "enableCategory" : "disableCategory";
+    ++elapsed_patches_to_install_;
+    QMetaObject::invokeMethod(lotro_mgr_, operation_name.toLocal8Bit().constData(), Qt::QueuedConnection,
+                                  Q_ARG(QString, getPatchName() + "_textures"),
                                   Q_ARG(LotroDatManager::Category, LotroDatManager::Category::E_TEXTURES_COMMON)
                                   );
-    }
 }
 
 void GraphicsPatch::onDownloaderProgressChanged(Downloader *, Downloader::Status)
@@ -190,119 +154,84 @@ void GraphicsPatch::onDownloaderProgressChanged(Downloader *, Downloader::Status
         all_downloads_status = all_downloads_status + downloader->getDownloadStatus();
     }
 
-    emit downloadStatusChanged(this, all_downloads_status);
+    emit progressChanged(OperationProgress(all_downloads_status), this);
 }
 
 void GraphicsPatch::onDownloaderFinished(Downloader *ptr)
 {
+    ptr->targetFile->close();
+    ptr->targetFile->deleteLater();
+
     Downloader::Status all_downloads_status;
-    foreach (Downloader* downloader, downloaders_) {
+    for (const Downloader* downloader : downloaders_) {
         all_downloads_status = all_downloads_status + downloader->getDownloadStatus();
     }
 
-    ptr->targetFile->close();
-    ptr->targetFile->deleteLater();
-
-    if (elapsed_patches_to_download_ == 1) {
-        foreach (Downloader* downloader, downloaders_) {
+    if (!all_downloads_status.running) {
+        for (Downloader* downloader : downloaders_) {
             downloader->deleteLater();
         }
         downloaders_.clear();
-
-        emit downloadStatusChanged(this, all_downloads_status);
-        emit operationFinished("download", this, true);
+        emit operationFinished(E_DOWNLOAD, this);
     }
-
-    --elapsed_patches_to_download_;
 }
 
 void GraphicsPatch::onLotroDatManagerOperationFinished(QString operation_name, QVector<QVariant> args, bool successful)
 {
-    if (operation_name == "installPatch") {
-        if (!args[0].toString().contains("GraphicsPatch_")) {
-            return;
-        }
-
-        status_.process = CurrentProcess::E_AWAITING_INSTALL;
-        status_.percent = 100;
-        emit installStatusChanged(this, status_);
-
-        --elapsed_databases_to_install_;
+    if (args.size() == 0 || !args[0].toString().contains(getPatchName())) {
+        return; // This means, that message from LotroManager is addressed to another patchset
+    }
 
+    if (operation_name.contains("installPatch")) {
         if (!successful) {
-            emit errorOccured("install", this, "Database " + args[0].toString() + " (" + args[1].toString() + ") was not installed due to Legacy core error.");
+            qCritical() << *this << "Database " + args[0].toString() + " (" + args[1].toString() + ") was not installed due to Legacy core error.";
+            emit errorOccured(E_INSTALL, this, "Database " + args[0].toString() + " (" + args[1].toString() + ") was not installed due to Legacy core error.");
         }
 
-        if (elapsed_databases_to_install_ == 0) {
-            status_.process = CurrentProcess::E_FINISHED;
-            emit operationFinished("install", this, true);
+        --elapsed_patches_to_install_;
+        if (!elapsed_patches_to_install_) {
+            is_being_patched_by_lotro_dat_manager_ = false;
+            emit operationFinished(E_INSTALL, this);
         }
     }
 
-    if (operation_name == "enableCategory" || operation_name == "disableCategory") {
-        if (!args[0].toString().contains("GraphicsPatch_")) {
-            return;
-        }
-
-        status_.process = CurrentProcess::E_AWAITING_APPLY;
-        status_.percent = 100;
-        emit installStatusChanged(this, status_);
-
-        --elapsed_categories_to_activate_;
+    if (operation_name.contains("enableCategory") || operation_name.contains("disableCategory")) {
         if (!successful) {
-            emit errorOccured("activate", this, "Error in patch " + args[0].toString() + ": activating category " + QString::number(args[1].toInt()) + " failed.");
+            qCritical() << *this << "Error in patch " + args[0].toString() + ": activating category " + QString::number(args[1].toInt()) + " failed.";
+            emit errorOccured(E_ACTIVATE, this, "Error in patch " + args[0].toString() + ": activating category " + QString::number(args[1].toInt()) + " failed.");
         }
 
-        if (elapsed_categories_to_activate_ == 0) {
-            status_.process = CurrentProcess::E_FINISHED;
-            emit operationFinished("activate", this, true);
+        --elapsed_patches_to_install_;
+        if (!elapsed_patches_to_install_) {
+            is_being_patched_by_lotro_dat_manager_ = false;
+            emit operationFinished(E_ACTIVATE, this);
         }
     }
 }
 
 void GraphicsPatch::onLotroDatManagerOperationStarted(QString operation_name, QVector<QVariant> args)
 {
-    if (operation_name == "installPatch") {
-        if (!args[0].toString().contains("GraphicsPatch_")) {
-            return;
-        }
-        status_.process = CurrentProcess::E_INSTALL;
-        status_.current_part++;
-        status_.percent = 0;
-        emit installStatusChanged(this, status_);
+    if (args.size() == 0 || !args[0].toString().contains(getPatchName())) {
+        return;
     }
 
-    if (operation_name == "enableCategory" || operation_name == "disableCategory") {
-        if (!args[0].toString().contains("GraphicsPatch_")) {
-            return;
-        }
-        status_.process = CurrentProcess::E_APPLY;
-        status_.current_part++;
-        status_.percent = 0;
-        emit installStatusChanged(this, status_);
-    }
+    is_being_patched_by_lotro_dat_manager_ = true;
 }
 
 void GraphicsPatch::onLotroDatManagerStatusChanged(LotroDatManager::Status status)
 {
-    if (status_.process != CurrentProcess::E_INSTALL || status_.process != CurrentProcess::E_APPLY) {
+    if (!is_being_patched_by_lotro_dat_manager_) {
         return;
     }
 
-    status_.percent = status.percent;
-    emit installStatusChanged(this, status_);
+    emit progressChanged(OperationProgress(status), this);
 }
 
 void GraphicsPatch::installLoadscreens()
 {
-    status_.current_part++;
-    status_.process = CurrentProcess::E_INSTALL;
-    status_.percent = 0;
-    emit installStatusChanged(this, status_);
-
     QString locale_prefix = Settings::getValue("Lotro/original_locale").toString();
 
-    QStringList filenames = {
+    const QStringList loadscreens_filenames = {
         locale_prefix == "English" ? "lotro_ad_pregame.jpg" : "lotro_ad_pregame_" + locale_prefix + ".jpg",
         "lotro_generic_teleport_screen_01.jpg",
         "lotro_generic_teleport_screen_02.jpg",
@@ -316,9 +245,13 @@ void GraphicsPatch::installLoadscreens()
         "lotro_generic_teleport_screen_10.jpg"
     };
 
+    OperationProgress progress;
+    progress.install_finished_parts = 0;
+    progress.install_total_parts = loadscreens_filenames.size() * 2;
+
     LOTRO_DAT::Database database;
     if (!database.InitDatabase(Settings::getValue("PatchDatabases/Loadscreen/path").toString().toStdString())) {
-        emit errorOccured("install", this, "installLoadscreensCannotInitDb");
+        qCritical() << *this << "database.InitDatabase() of " << Settings::getValue("PatchDatabases/Loadscreen/path").toString() << " FAILED!";
         return;
     }
 
@@ -326,24 +259,22 @@ void GraphicsPatch::installLoadscreens()
 
     QString logo_path = Settings::getValue("Lotro/game_path").toString() + "/raw/" + (locale_prefix == "English" ? "en" : locale_prefix) + "/logo/";
 
-    for (size_t i = 0; i < qMin(size_t(filenames.size()), database.CountRows()); ++i) {
-        QFile::remove(logo_path + filenames[i] + "_ru");
-        if (!data.binary_data.WriteToFile((logo_path + filenames[i] + "_ru").toStdString())) {
-            emit errorOccured("install", this, "Не могу осуществить запись в файл " + logo_path + filenames[i]);
-        }
-        status_.percent = double(i) * 50.0 / (qMin(double(filenames.size()), double(database.CountRows())));
-        emit installStatusChanged(this, status_);
-    }
+    for (size_t i = 0; i < qMin(size_t(loadscreens_filenames.size()), database.CountRows()); ++i) {
+        QFile::remove(logo_path + loadscreens_filenames[i] + "_ru");
 
+        if (!data.binary_data.WriteToFile((logo_path + loadscreens_filenames[i] + "_ru").toStdString())) {
+            qWarning() << patch_name_ << "Cannot write to file " << logo_path + loadscreens_filenames[i];
+        }
 
-    for (int i = 0; i < filenames.size(); ++i) {
-        QFile::copy(logo_path + filenames[i], logo_path + filenames[i] + "_orig");
-        status_.percent = 50 + double(i) * 50.0 / double(filenames.size());
-        emit installStatusChanged(this, status_);
+        progress.install_finished_parts++;
+        emit progressChanged(progress, this);
     }
 
-    status_.percent = 100;
-    emit installStatusChanged(this, status_);
+    for (int i = 0; i < loadscreens_filenames.size(); ++i) {
+        QFile::copy(logo_path + loadscreens_filenames[i], logo_path + loadscreens_filenames[i] + "_orig"); // Will not copy if _orig file already exists
+        progress.install_finished_parts++;
+        emit progressChanged(progress, this);
+    }
 }
 
 void GraphicsPatch::enableLoadscreens()
@@ -351,7 +282,7 @@ void GraphicsPatch::enableLoadscreens()
     QString locale_prefix = Settings::getValue("Lotro/original_locale").toString();
     QString logo_path = Settings::getValue("Lotro/game_path").toString() + "/raw/" + (locale_prefix == "English" ? "en" : locale_prefix) + "/logo/";
 
-    QStringList filenames = {
+    const QStringList loadscreens_filenames = {
         locale_prefix == "English" ? "lotro_ad_pregame.jpg" : "lotro_ad_pregame_" + locale_prefix + ".jpg",
         "lotro_generic_teleport_screen_01.jpg",
         "lotro_generic_teleport_screen_02.jpg",
@@ -365,9 +296,15 @@ void GraphicsPatch::enableLoadscreens()
         "lotro_generic_teleport_screen_10.jpg"
     };
 
-    for (int i = 0; i < filenames.size(); ++i) {
-        QFile::remove(logo_path + filenames[i]);
-        QFile::copy(logo_path + filenames[i] + "_ru", logo_path + filenames[i]);
+    OperationProgress progress;
+    progress.install_total_parts = loadscreens_filenames.size();
+
+    for (int i = 0; i < loadscreens_filenames.size(); ++i) {
+        QFile::remove(logo_path + loadscreens_filenames[i]);
+        QFile::copy(logo_path + loadscreens_filenames[i] + "_ru", logo_path + loadscreens_filenames[i]);
+
+        progress.install_finished_parts++;
+        emit progressChanged(progress, this);
     }
 }
 
@@ -376,7 +313,7 @@ void GraphicsPatch::disableLoadscreens()
     QString locale_prefix = Settings::getValue("Lotro/original_locale").toString();
     QString logo_path = Settings::getValue("Lotro/game_path").toString() + "/raw/" + (locale_prefix == "English" ? "en" : locale_prefix) + "/logo/";
 
-    QStringList filenames = {
+    const QStringList loadscreens_filenames = {
         locale_prefix == "English" ? "lotro_ad_pregame.jpg" : "lotro_ad_pregame_" + locale_prefix + ".jpg",
         "lotro_generic_teleport_screen_01.jpg",
         "lotro_generic_teleport_screen_02.jpg",
@@ -390,8 +327,14 @@ void GraphicsPatch::disableLoadscreens()
         "lotro_generic_teleport_screen_10.jpg"
     };
 
-    for (int i = 0; i < filenames.size(); ++i) {
-        QFile::remove(logo_path + filenames[i]);
-        QFile::copy(logo_path + filenames[i] + "_orig", logo_path + filenames[i]);
+    OperationProgress progress;
+    progress.install_total_parts = loadscreens_filenames.size();
+
+    for (int i = 0; i < loadscreens_filenames.size(); ++i) {
+        QFile::remove(logo_path + loadscreens_filenames[i]);
+        QFile::copy(logo_path + loadscreens_filenames[i] + "_orig", logo_path + loadscreens_filenames[i]);
+
+        progress.install_finished_parts++;
+        emit progressChanged(progress, this);
     }
 }

+ 3 - 6
src/Legacy/models/patch/graphicspatch.h

@@ -13,7 +13,7 @@ class GraphicsPatch : public Patch
 public:
     GraphicsPatch(LotroDatManager* mgr, QObject* parent = nullptr);
 
-public slots:
+private slots:
     virtual void checkForUpdates() override;
     virtual void download() override;
     virtual void install() override;
@@ -22,8 +22,9 @@ public slots:
 private slots:
     void onDownloaderProgressChanged(Downloader* ptr, Downloader::Status status);
     void onDownloaderFinished(Downloader* ptr);
-    void onLotroDatManagerOperationFinished(QString operation_name, QVector<QVariant> args, bool successful);
+
     void onLotroDatManagerOperationStarted(QString operation_name, QVector<QVariant> args);
+    void onLotroDatManagerOperationFinished(QString operation_name, QVector<QVariant> args, bool successful);
     void onLotroDatManagerStatusChanged(LotroDatManager::Status status);
 
 private:
@@ -36,10 +37,6 @@ private:
     void installLoadscreens();
     void enableLoadscreens();
     void disableLoadscreens();
-
-    quint8 elapsed_patches_to_download_ = 0; // for download process
-    quint8 elapsed_databases_to_install_ = 0; // for install process
-    quint8 elapsed_categories_to_activate_ = 0; // for activate/deactivate process
 };
 
 #endif // IMAGESPATCH_H

+ 144 - 5
src/Legacy/models/patch/patch.cpp

@@ -7,12 +7,16 @@
 
 Patch::Patch(QString patch_name, LotroDatManager* mgr, QObject *parent)
     : QObject(parent)
-    , patch_name_(patch_name)
     , lotro_mgr_(mgr)
+    , patch_name_(patch_name)
 {
-    status_.process = CurrentProcess::E_FINISHED;
-    status_.percent = 0;
-    status_.debug_msg = "";
+    qRegisterMetaType<Operation>("Operation");
+    qRegisterMetaType<QList<Patch::Operation>>("QList<Patch::Operation>");
+    qRegisterMetaType<OperationProgress>("OperationProgress");
+
+
+    connect(this, &Patch::operationStarted, this, &Patch::onOperationStarted);
+    connect(this, &Patch::operationFinished, this, &Patch::onOperationFinished);
 }
 
 bool Patch::needDownloadDatabase(QString db_name)
@@ -58,7 +62,142 @@ bool Patch::isDatabaseDownloadEnabled(QString db_name)
     return false;
 }
 
-QString Patch::getPatchName()
+void Patch::execOperation(Patch::Operation operation)
+{
+    switch (operation) {
+    case E_CHECKFORUPDATES:
+        checkForUpdates();
+        return;
+    case E_DOWNLOAD:
+        download();
+        return;
+    case E_INSTALL:
+        install();
+        return;
+    case E_ACTIVATE:
+        activate();
+        return;
+    default:
+        qCritical() << "Unknown operation on patchset " << patch_name_ << "!";
+    }
+}
+
+QString Patch::getPatchName() const
 {
     return patch_name_;
 }
+
+void Patch::enqueue(const Patch::Operation &operation)
+{
+    qDebug() << "Enqueuing operation " << operation << " in patchset" << *this;
+    operations_queue_.append(operation);
+    if (!running_operation_) {
+        processQueue();
+    }
+}
+
+void Patch::enqueue(const QList<Patch::Operation> &operations)
+{
+    qDebug() << "Enqueuing operations " << operations << " in patchset" << *this;
+    operations_queue_.append(operations);
+    if (!running_operation_) {
+        processQueue();
+    }
+}
+
+void Patch::processQueue()
+{
+    if (operations_queue_.empty() || running_operation_) {
+        return;
+    }
+
+    Operation current_operation = operations_queue_.dequeue();
+    qDebug() << "Processing patch queue: " << *this << ", starting operation " << current_operation;
+    emit operationStarted(current_operation, this);
+    execOperation(current_operation);
+}
+
+void Patch::onOperationStarted(Patch::Operation, Patch*)
+{
+    running_operation_ = true;
+}
+
+void Patch::onOperationFinished(Patch::Operation, Patch*)
+{
+    running_operation_ = false;
+    processQueue();
+}
+
+Patch::OperationProgress::OperationProgress(Downloader::Status status)
+{
+    download_finished_bytes = status.downloaded_bytes;
+    download_total_bytes = status.total_bytes;
+    download_speed = status.current_speed;
+    download_elapsed_time = status.elapsed_time;
+}
+
+Patch::OperationProgress::OperationProgress(LotroDatManager::Status status) {
+    install_finished_parts = status.finished_parts;
+    install_total_parts = status.total_parts;
+}
+
+Patch::OperationProgress Patch::OperationProgress::operator+(const Patch::OperationProgress &other) const
+{
+    Patch::OperationProgress result;
+
+    result.download_elapsed_time = download_elapsed_time + other.download_elapsed_time;
+    result.download_finished_bytes = download_finished_bytes + other.download_finished_bytes;
+    result.download_speed = download_speed + other.download_speed;
+    result.download_total_bytes = download_total_bytes + other.download_total_bytes;
+
+    result.install_finished_parts = install_finished_parts + other.install_finished_parts;
+    result.install_total_parts = install_total_parts + other.install_total_parts;
+
+    return result;
+}
+
+double Patch::OperationProgress::getDownloadPercent() const
+{
+    return double(download_finished_bytes) * 100.0/ double(download_total_bytes);
+}
+
+double Patch::OperationProgress::getInstallPercent() const
+{
+    return double(install_finished_parts) * 100.0 / double(install_total_parts);
+}
+
+QDebug operator<<(QDebug dbg, const Patch::Operation& operation) {
+    switch (operation) {
+    case Patch::Operation::E_CHECKFORUPDATES:
+        dbg << "Patch::Operation::E_CHECKFORUPDATES";
+        return dbg;
+    case Patch::Operation::E_DOWNLOAD:
+        dbg << "Patch::Operation::E_DOWNLOAD";
+        return dbg;
+    case Patch::Operation::E_INSTALL:
+        dbg << "Patch::Operation::E_INSTALL";
+        return dbg;
+    case Patch::Operation::E_ACTIVATE:
+        dbg << "Patch::Operation::E_ACTIVATE";
+        return dbg;
+    default:
+        dbg << "{Unknown operation!}";
+    }
+    return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const Patch::OperationProgress& operation_progress) {
+    dbg.nospace().noquote()
+        << "Downloaded: " << Downloader::getSizeFormatted(operation_progress.download_finished_bytes)
+        << " of " << Downloader::getSizeFormatted(operation_progress.download_total_bytes)
+        << " with speed " << Downloader::getSpeedFormatted(operation_progress.download_speed)
+        << " Elapsed time " << Downloader::getElapsedTimeFormatted(operation_progress.download_elapsed_time)
+        << " Installed: " << operation_progress.install_finished_parts << " of " << operation_progress.install_total_parts << ".";
+    return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const Patch& patch) {
+    dbg.nospace().noquote() << "Patch::" << patch.getPatchName();
+    return dbg;
+}
+

+ 58 - 38
src/Legacy/models/patch/patch.h

@@ -2,6 +2,8 @@
 #define LEGACYPATCH_H
 
 #include <QObject>
+#include <QQueue>
+#include <QTimer>
 
 #include "models/downloader.h"
 #include "models/filesystem.h"
@@ -13,65 +15,83 @@ class Patch : public QObject
     Q_OBJECT
 
 public:
-    enum class CurrentProcess {
-        E_AWAITING_INSTALL,
+    enum Operation {
+        E_CHECKFORUPDATES,
+        E_DOWNLOAD,
         E_INSTALL,
-        E_AWAITING_APPLY,
-        E_APPLY,
-        E_FINISHED
+        E_ACTIVATE
     };
 
-    struct InstallationStatus {
-        CurrentProcess process = CurrentProcess::E_FINISHED;
-        int current_part = 0; // Ex. "installation (current_part/total_parts) percent%"
-        int total_parts = 0;
-        double percent = 0;
-        QString debug_msg = "";
-    };
+    struct OperationProgress {
+
+        OperationProgress() = default;
+        OperationProgress(Downloader::Status status);
+        OperationProgress(LotroDatManager::Status status);
 
-    friend InstallationStatus operator+(const InstallationStatus &a, const InstallationStatus &b) {
-        InstallationStatus result = {
-            CurrentProcess::E_INSTALL,
-            a.current_part + b.current_part,
-            a.total_parts + b.total_parts,
-            (100.0 * double(a.current_part - 1 + b.current_part - 1) + a.percent + b.percent) / double(a.total_parts + b.total_parts),
-            QString()
-        };
+        OperationProgress operator+(const Patch::OperationProgress& other) const;
 
-        return result;
-    }
+        double getDownloadPercent() const;
+        double getInstallPercent() const;
+
+    public:
+        quint64 download_finished_bytes = 0;
+        quint64 download_total_bytes = 0;
+        quint64 download_speed = 0;
+        quint64 download_elapsed_time = 0;
+
+        quint64 install_finished_parts = 0;
+        quint64 install_total_parts = 0;
+    };
 
 public:
-    explicit Patch(QString patch_name, LotroDatManager* mgr, QObject *parent);
+    explicit Patch(QString patch_name, LotroDatManager* mgr, QObject* parent);
 
-    QString getPatchName();
+    QString getPatchName() const;
 
-public slots: // WARNING: THESE CAN BE NON-BLOCKING OPERATIONS! Use them in chain only via operationFinished signal handlers.
+public slots:
+    void enqueue(const Patch::Operation& operation);
 
-    virtual void checkForUpdates() = 0; // Checks for updates. Updates patches info in Settings.
-    virtual void download() = 0; // Downloads patch contents, prepared for installation.
-    virtual void install() = 0; // Installs patch into game resources (need to be enabled after install to be activated)
-    virtual void activate() = 0; // Activates (or deactivates) patch components based on user preferences (patch needs to be installed)
+    void enqueue(const QList<Patch::Operation>& operations);
 
-signals: // each signal brings with it pointer to this class
+    void processQueue();
 
-    void operationStarted(QString operation_name, Patch* patch);
-    void errorOccured(QString operation_name, Patch* patch, QString msg);
-    void operationFinished(QString operation_name, Patch* patch, bool result);
+private slots: // WARNING: THESE CAN BE NON-BLOCKING OPERATIONS! Use them in chain only via operationFinished signal handlers.
+    virtual void checkForUpdates() = 0; // Checks for updates. Updates patches info in Settings.
+    virtual void download() = 0;        // Downloads patch contents, prepared for installation.
+    virtual void install() = 0;         // Installs patch into game resources (needs to be activated in order to be available in-game)
+    virtual void activate() = 0;        // Activates (or deactivates) patch components based on user preferences (patch needs to be installed)
 
-    void downloadStatusChanged(Patch* patch, Downloader::Status status);
-    void installStatusChanged(Patch* patch, InstallationStatus status);
+    void onOperationStarted(Operation operation, Patch* patch);
+    void onOperationFinished(Operation operation, Patch* patch);
+
+signals: // each signal brings with it pointer to class-emitter, operation name is equal to function name
+    void operationStarted(Operation operation, Patch* patch);
+    void progressChanged(OperationProgress progress, Patch* patch);
+    void operationFinished(Operation operation, Patch* patch);
+    void errorOccured(Operation operation, Patch* patch, QString msg);
 
 protected:
-    bool needDownloadDatabase(QString db_name);
+    bool needDownloadDatabase(QString db_name); // Checks if database needs to be downloaded (by checksum comparing)
+
+    static bool isDatabaseDownloadEnabled(QString db_name); // Checks if database is checked for installation by user (via Settings)
 
-    static bool isDatabaseDownloadEnabled(QString db_name); // Checks if this database needs to be downloaded (comparing with user parameters)
+    void execOperation(Operation operation); // Starts operation execution by its "name"
 
 protected:
+    bool running_operation_ = false;
     LotroDatManager* lotro_mgr_;
     QString patch_name_;
     QSet<Downloader*> downloaders_;
-    InstallationStatus status_;
+    QQueue<Operation> operations_queue_;
+    bool is_being_patched_by_lotro_dat_manager_ = false;
+
+    quint32 elapsed_patches_to_install_ = 0;
 };
 
+QDebug operator<<(QDebug dbg, const Patch::Operation& operation);
+
+QDebug operator<<(QDebug dbg, const Patch::OperationProgress& operation_progress);
+
+QDebug operator<<(QDebug dbg, const Patch& patch);
+
 #endif // LEGACYPATCH_H

+ 63 - 126
src/Legacy/models/patch/soundspatch.cpp

@@ -1,7 +1,6 @@
 #include "soundspatch.h"
 #include "LotroDat/LotroDat.h"
 #include "LotroDat/Database.h"
-#include "models/lotrodatmanagerobserver.h"
 
 #include <QUrlQuery>
 #include <QSet>
@@ -11,12 +10,11 @@ SoundsPatch::SoundsPatch(LotroDatManager *mgr, QObject *parent) : Patch("SoundsP
 {
     connect(lotro_mgr_, &LotroDatManager::operationStarted, this, &SoundsPatch::onLotroDatManagerOperationStarted);
     connect(lotro_mgr_, &LotroDatManager::operationFinished, this, &SoundsPatch::onLotroDatManagerOperationFinished);
-    connect(lotro_mgr_->getObserver(), &LotroDatManagerObserver::statusChanged, this, &SoundsPatch::onLotroDatManagerStatusChanged);
+    connect(lotro_mgr_, &LotroDatManager::statusChanged, this, &SoundsPatch::onLotroDatManagerStatusChanged);
 }
 
 void SoundsPatch::checkForUpdates()
 {
-    emit operationStarted("checkForUpdates", this);
     QUrlQuery query; // query for building GET-request aka patch-version
 
     foreach (QString db_name, databases_names) {
@@ -35,26 +33,26 @@ void SoundsPatch::checkForUpdates()
     downloader.waitForDownloaded();
 
     if (target_array.isEmpty()) {
-        qDebug() << __FUNCTION__ << "Cannot download, target_array is empty!";
-        emit errorOccured("checkForUpdates", this, "QueryDownloadFailed");
-        emit operationFinished("checkForUpdates", this, false);
+        qWarning() << *this << "Cannot check for updates, target_array is empty!";
+        emit errorOccured(E_CHECKFORUPDATES, this, "");
+        emit operationFinished(E_CHECKFORUPDATES, this);
         return;
     }
 
-    QStringList patch_info = QString(target_array).split("|");
+    QStringList patch_info = QString(target_array).split('|');
     if (patch_info.size() != databases_names.size()) {
-        qDebug() << __FUNCTION__ << "Incorrect patches number! Data: " << patch_info;
-        emit errorOccured("checkForUpdates", this, "IncorrectQueryResult");
-        emit operationFinished("checkForUpdates", this, false);
+        qCritical() << __FUNCTION__ << "Incorrect patches number! Data: " << patch_info;
+        emit errorOccured(E_CHECKFORUPDATES, this, "");
+        emit operationFinished(E_CHECKFORUPDATES, this);
         return;
     }
 
     for (int i = 0; i < databases_names.size(); ++i) {
         QStringList patch_data = patch_info[i].split(":::");
         if (patch_data.size() != 3) {
-            qDebug() << __FUNCTION__ << "Incorrect patch entry size! Entry: " << patch_data;
-            emit errorOccured("checkForUpdates", this, "IncorrectDbInfo");
-            emit operationFinished("checkForUpdates", this, false);
+            qCritical() << __FUNCTION__ << "Incorrect patch entry size! Entry: " << patch_data;
+            emit errorOccured(E_CHECKFORUPDATES, this, "");
+            emit operationFinished(E_CHECKFORUPDATES, this);
             return;
         }
 
@@ -66,42 +64,31 @@ void SoundsPatch::checkForUpdates()
         Settings::setValue("PatchDatabases/" + databases_names[i] + "/path", patch_filename);
     }
 
-    emit operationFinished("checkForUpdates",  this, true);
+    emit operationFinished(E_CHECKFORUPDATES, this);
 }
 
 void SoundsPatch::download()
 {
-    if (elapsed_patches_to_download_ != 0) {
-        qDebug() << "Trying to start download of patch set " << patch_name_ << " while download is still active!";
-        return;
-    }
-
-    emit operationStarted("download", this);
-
-    bool download_started = false;
-
     foreach (QString db_name, databases_names) {
         QString settings_prefix = "PatchDatabases/" + db_name;
 
         QString target_filename = QApplication::applicationDirPath() + "/" + Settings::getValue(settings_prefix + "/path").toString();
-        qDebug() << "SoundsPatch: Checking if there's no need to download file " << target_filename;
+        qDebug() << patch_name_ << ": Checking if file " << target_filename << " matches its hashsum";
 
         if (FileSystem::fileHash(target_filename) == Settings::getValue(settings_prefix + "/hashsum").toString()) {
-            qDebug() << "SoundsPatch: file " << target_filename << " is fresh, no need to download";
+            qInfo() << *this << ": file " << target_filename << " is up-to-date, no need to download";
             continue;
         }
 
         FileSystem::createFilePath(target_filename);
         QFile* target_file = new QFile(target_filename);
         if (!target_file->open(QIODevice::ReadWrite | QIODevice::Truncate)) {
-            emit errorOccured("download", this, "CantOpenDbFile");
+            qWarning() << *this << "Cannot open file " << target_filename;
             continue;
         }
 
-        qDebug() << "GraphicsPatch: beginning download of file " << target_filename;
-        download_started = true;
+        qInfo() << *this << ": beginning download of file " << target_filename;
 
-        ++elapsed_patches_to_download_;
         Downloader* downloader = new Downloader();
         downloader->setUrl(Settings::getValue(settings_prefix + "/url").toUrl());
         downloader->targetFile = target_file;
@@ -111,56 +98,36 @@ void SoundsPatch::download()
         downloader->start();
     }
 
-    if (!download_started) {
-        emit operationFinished("download", this, true);
-    } // otherwise will be emitted on the last onDownloaderFinished signal
+    if (downloaders_.empty()) {
+        emit operationFinished(E_DOWNLOAD, this);
+    }
 }
 
 void SoundsPatch::install()
 {
-    if (elapsed_databases_to_install_ > 0) {
-        emit errorOccured("install", this, "Установка набора графических патчей ещё не закончена");
-        return;
-    }
-
-    emit operationStarted("install", this);
-
-    status_.process = CurrentProcess::E_AWAITING_INSTALL;
-    status_.percent = 0;
-    status_.total_parts = 1;
-    status_.current_part = 0;
-
-    emit installStatusChanged(this, status_);
+    emit operationFinished(E_INSTALL, this);
+    return;
 
     foreach (QString db_name, QStringList({"sound"})) {
-        ++elapsed_databases_to_install_;
-        QMetaObject::invokeMethod(lotro_mgr_,
-                                  "installPatch",
-                                  Qt::QueuedConnection,
-                                  Q_ARG(QString, "SoundsPatch_" + db_name),
+        ++elapsed_patches_to_install_;
+        QMetaObject::invokeMethod(lotro_mgr_, "installPatch", Qt::QueuedConnection,
+                                  Q_ARG(QString, getPatchName() + "_" + db_name),
                                   Q_ARG(QString, Settings::getValue("PatchDatabases/" + db_name + "/path").toString())
                                   );
     }
-
 }
 
 void SoundsPatch::activate()
 {
-    if (Settings::getValue("Components/sounds").toBool()) {
-        QMetaObject::invokeMethod(lotro_mgr_,
-                                  "enableCategory",
-                                  Qt::QueuedConnection,
-                                  Q_ARG(QString, "SoundsPatch_sounds"),
-                                  Q_ARG(LotroDatManager::Category, LotroDatManager::Category::E_AUDIOS_COMMON)
-                                  );
-    } else {
-        QMetaObject::invokeMethod(lotro_mgr_,
-                                  "disableCategory",
-                                  Qt::QueuedConnection,
-                                  Q_ARG(QString, "SoundsPatch_sounds"),
-                                  Q_ARG(LotroDatManager::Category, LotroDatManager::Category::E_AUDIOS_COMMON)
-                                  );
-    }
+    emit operationFinished(E_ACTIVATE, this);
+    return;
+
+    QString operation_name = Settings::getValue("Components/sounds").toBool() ? "enableCategory" : "disableCategory";
+    ++elapsed_patches_to_install_;
+    QMetaObject::invokeMethod(lotro_mgr_, operation_name.toLocal8Bit().constData(), Qt::QueuedConnection,
+                              Q_ARG(QString, getPatchName() + "_sounds"),
+                              Q_ARG(LotroDatManager::Category, LotroDatManager::Category::E_AUDIOS_COMMON)
+                              );
 }
 
 void SoundsPatch::onDownloaderProgressChanged(Downloader *, Downloader::Status)
@@ -170,105 +137,75 @@ void SoundsPatch::onDownloaderProgressChanged(Downloader *, Downloader::Status)
         all_downloads_status = all_downloads_status + downloader->getDownloadStatus();
     }
 
-    emit downloadStatusChanged(this, all_downloads_status);
+    emit progressChanged(OperationProgress(all_downloads_status), this);
 }
 
 void SoundsPatch::onDownloaderFinished(Downloader *ptr)
 {
+    ptr->targetFile->close();
+    ptr->targetFile->deleteLater();
+
     Downloader::Status all_downloads_status;
-    foreach (Downloader* downloader, downloaders_) {
+    for (const Downloader* downloader : downloaders_) {
         all_downloads_status = all_downloads_status + downloader->getDownloadStatus();
     }
 
-    ptr->targetFile->close();
-    ptr->targetFile->deleteLater();
-
-    if (elapsed_patches_to_download_ == 1) {
-        foreach (Downloader* downloader, downloaders_) {
+    if (!all_downloads_status.running) {
+        for (Downloader* downloader : downloaders_) {
             downloader->deleteLater();
         }
         downloaders_.clear();
-
-        emit downloadStatusChanged(this, all_downloads_status);
-        emit operationFinished("download", this, true);
+        emit operationFinished(E_DOWNLOAD, this);
     }
-
-    --elapsed_patches_to_download_;
 }
 
 void SoundsPatch::onLotroDatManagerOperationFinished(QString operation_name, QVector<QVariant> args, bool successful)
 {
-    if (operation_name == "installPatch") {
-        if (!args[0].toString().contains("SoundsPatch_")) {
-            return;
-        }
-
-        status_.process = CurrentProcess::E_AWAITING_INSTALL;
-        status_.percent = 100;
-        emit installStatusChanged(this, status_);
-
-        --elapsed_databases_to_install_;
+    if (args.size() == 0 || !args[0].toString().contains(getPatchName())) {
+        return; // This means, that message from LotroManager is addressed to another patchset
+    }
 
+    if (operation_name.contains("installPatch")) {
         if (!successful) {
-            emit errorOccured("install", this, "Database " + args[0].toString() + " (" + args[1].toString() + ") was not installed due to Legacy core error.");
+            qCritical() << *this << "Database " + args[0].toString() + " (" + args[1].toString() + ") was not installed due to Legacy core error.";
+            emit errorOccured(E_INSTALL, this, "Database " + args[0].toString() + " (" + args[1].toString() + ") was not installed due to Legacy core error.");
         }
 
-        if (elapsed_databases_to_install_ == 0) {
-            status_.process = CurrentProcess::E_FINISHED;
-            emit operationFinished("install", this, true);
+        --elapsed_patches_to_install_;
+        if (!elapsed_patches_to_install_) {
+            is_being_patched_by_lotro_dat_manager_ = false;
+            emit operationFinished(E_INSTALL, this);
         }
     }
 
-    if (operation_name == "enableCategory" || operation_name == "disableCategory") {
-        if (!args[0].toString().contains("SoundsPatch_")) {
-            return;
-        }
-
-        status_.process = CurrentProcess::E_AWAITING_APPLY;
-        status_.percent = 100;
-        emit installStatusChanged(this, status_);
-
-        --elapsed_categories_to_activate_;
+    if (operation_name.contains("enableCategory") || operation_name.contains("disableCategory")) {
         if (!successful) {
-            emit errorOccured("activate", this, "Error in patch " + args[0].toString() + ": activating category " + QString::number(args[1].toInt()) + " failed.");
+            qCritical() << *this << "Error in patch " + args[0].toString() + ": activating category " + QString::number(args[1].toInt()) + " failed.";
+            emit errorOccured(E_ACTIVATE, this, "Error in patch " + args[0].toString() + ": activating category " + QString::number(args[1].toInt()) + " failed.");
         }
 
-        if (elapsed_categories_to_activate_ == 0) {
-            status_.process = CurrentProcess::E_FINISHED;
-            emit operationFinished("activate", this, true);
+        --elapsed_patches_to_install_;
+        if (!elapsed_patches_to_install_) {
+            is_being_patched_by_lotro_dat_manager_ = false;
+            emit operationFinished(E_ACTIVATE, this);
         }
     }
 }
 
 void SoundsPatch::onLotroDatManagerOperationStarted(QString operation_name, QVector<QVariant> args)
 {
-    if (operation_name == "installPatch") {
-        if (!args[0].toString().contains("SoundsPatch_")) {
-            return;
-        }
-        status_.process = CurrentProcess::E_INSTALL;
-        status_.current_part++;
-        status_.percent = 0;
-        emit installStatusChanged(this, status_);
+    if (args.size() == 0 || !args[0].toString().contains(getPatchName())) {
+        return;
     }
 
-    if (operation_name == "enableCategory" || operation_name == "disableCategory") {
-        if (!args[0].toString().contains("SoundsPatch_")) {
-            return;
-        }
-        status_.process = CurrentProcess::E_APPLY;
-        status_.current_part++;
-        status_.percent = 0;
-        emit installStatusChanged(this, status_);
-    }
+    is_being_patched_by_lotro_dat_manager_ = true;
 }
 
 void SoundsPatch::onLotroDatManagerStatusChanged(LotroDatManager::Status status)
 {
-    if (status_.process != CurrentProcess::E_INSTALL || status_.process != CurrentProcess::E_APPLY) {
+    if (!is_being_patched_by_lotro_dat_manager_) {
         return;
     }
 
-    status_.percent = status.percent;
-    emit installStatusChanged(this, status_);
+    emit progressChanged(OperationProgress(status), this);
 }

+ 3 - 7
src/Legacy/models/patch/soundspatch.h

@@ -12,7 +12,7 @@ class SoundsPatch : public Patch
 public:
     SoundsPatch(LotroDatManager* mgr, QObject* parent = nullptr);
 
-public slots:
+private slots:
     virtual void checkForUpdates() override;
     virtual void download() override;
     virtual void install() override;
@@ -21,19 +21,15 @@ public slots:
 private slots:
     void onDownloaderProgressChanged(Downloader* ptr, Downloader::Status status);
     void onDownloaderFinished(Downloader* ptr);
-    void onLotroDatManagerOperationFinished(QString operation_name, QVector<QVariant> args, bool successful);
+
     void onLotroDatManagerOperationStarted(QString operation_name, QVector<QVariant> args);
+    void onLotroDatManagerOperationFinished(QString operation_name, QVector<QVariant> args, bool successful);
     void onLotroDatManagerStatusChanged(LotroDatManager::Status status);
 
 private:
     const QStringList databases_names = {
         "sound",
     };
-
-    quint8 elapsed_patches_to_download_ = 0; // for download process
-    quint8 elapsed_databases_to_install_ = 0; // for install process
-    quint8 elapsed_categories_to_activate_ = 0; // for activate/deactivate process
-
 };
 
 #endif // SOUNDSPATCH_H

+ 96 - 101
src/Legacy/models/patch/textspatch.cpp

@@ -1,7 +1,6 @@
 #include "textspatch.h"
 #include "LotroDat/LotroDat.h"
 #include "LotroDat/Database.h"
-#include "models/lotrodatmanagerobserver.h"
 
 #include <QUrlQuery>
 #include <QSet>
@@ -11,12 +10,11 @@ TextsPatch::TextsPatch(LotroDatManager *mgr, QObject *parent) : Patch("TextsPatc
 {
     connect(lotro_mgr_, &LotroDatManager::operationStarted, this, &TextsPatch::onLotroDatManagerOperationStarted);
     connect(lotro_mgr_, &LotroDatManager::operationFinished, this, &TextsPatch::onLotroDatManagerOperationFinished);
-    connect(lotro_mgr_->getObserver(), &LotroDatManagerObserver::statusChanged, this, &TextsPatch::onLotroDatManagerStatusChanged);
+    connect(lotro_mgr_, &LotroDatManager::statusChanged, this, &TextsPatch::onLotroDatManagerStatusChanged);
 }
 
 void TextsPatch::checkForUpdates()
 {
-    emit operationStarted("checkForUpdates", this);
     QUrlQuery query; // query for building GET-request aka patch-version
 
     foreach (QString db_name, databases_names) {
@@ -35,26 +33,26 @@ void TextsPatch::checkForUpdates()
     downloader.waitForDownloaded();
 
     if (target_array.isEmpty()) {
-        qDebug() << __FUNCTION__ << "Cannot download, target_array is empty!";
-        emit errorOccured("checkForUpdates", this, "QueryDownloadFailed");
-        emit operationFinished("checkForUpdates", this, false);
+        qWarning() << *this << "Cannot check for updates, target_array is empty!";
+        emit errorOccured(E_CHECKFORUPDATES, this, "");
+        emit operationFinished(E_CHECKFORUPDATES, this);
         return;
     }
 
-    QStringList patch_info = QString(target_array).split("|");
+    QStringList patch_info = QString(target_array).split('|');
     if (patch_info.size() != databases_names.size()) {
-        qDebug() << __FUNCTION__ << "Incorrect patches number! Data: " << patch_info;
-        emit errorOccured("checkForUpdates", this, "IncorrectQueryResult");
-        emit operationFinished("checkForUpdates", this, false);
+        qCritical() << __FUNCTION__ << "Incorrect patches number! Data: " << patch_info;
+        emit errorOccured(E_CHECKFORUPDATES, this, "");
+        emit operationFinished(E_CHECKFORUPDATES, this);
         return;
     }
 
     for (int i = 0; i < databases_names.size(); ++i) {
         QStringList patch_data = patch_info[i].split(":::");
         if (patch_data.size() != 3) {
-            qDebug() << __FUNCTION__ << "Incorrect patch entry size! Entry: " << patch_data;
-            emit errorOccured("checkForUpdates", this, "IncorrectDbInfo");
-            emit operationFinished("checkForUpdates", this, false);
+            qCritical() << __FUNCTION__ << "Incorrect patch entry size! Entry: " << patch_data;
+            emit errorOccured(E_CHECKFORUPDATES, this, "");
+            emit operationFinished(E_CHECKFORUPDATES, this);
             return;
         }
 
@@ -66,42 +64,31 @@ void TextsPatch::checkForUpdates()
         Settings::setValue("PatchDatabases/" + databases_names[i] + "/path", patch_filename);
     }
 
-    emit operationFinished("checkForUpdates",  this, true);
+    emit operationFinished(E_CHECKFORUPDATES, this);
 }
 
 void TextsPatch::download()
 {
-    if (elapsed_patches_to_download_ != 0) {
-        qDebug() << "Trying to start download of patch set " << patch_name_ << " while download is still active!";
-        return;
-    }
-
-    emit operationStarted("download", this);
-
-    bool download_started = false;
-
     foreach (QString db_name, databases_names) {
         QString settings_prefix = "PatchDatabases/" + db_name;
 
         QString target_filename = QApplication::applicationDirPath() + "/" + Settings::getValue(settings_prefix + "/path").toString();
-        qDebug() << "TextsPatch: Checking if there's no need to download file " << target_filename;
+        qDebug() << patch_name_ << ": Checking if file " << target_filename << " matches its hashsum";
 
         if (FileSystem::fileHash(target_filename) == Settings::getValue(settings_prefix + "/hashsum").toString()) {
-            qDebug() << "TextsPatch: file " << target_filename << " is fresh, no need to download";
+            qInfo() << *this << ": file " << target_filename << " is up-to-date, no need to download";
             continue;
         }
 
         FileSystem::createFilePath(target_filename);
         QFile* target_file = new QFile(target_filename);
         if (!target_file->open(QIODevice::ReadWrite | QIODevice::Truncate)) {
-            emit errorOccured("download", this, "CantOpenDbFile");
+            qWarning() << *this << "Cannot open file " << target_filename;
             continue;
         }
 
-        qDebug() << "TextsPatch: beginning download of file " << target_filename;
-        download_started = true;
+        qInfo() << *this << ": beginning download of file " << target_filename;
 
-        ++elapsed_patches_to_download_;
         Downloader* downloader = new Downloader();
         downloader->setUrl(Settings::getValue(settings_prefix + "/url").toUrl());
         downloader->targetFile = target_file;
@@ -111,26 +98,66 @@ void TextsPatch::download()
         downloader->start();
     }
 
-    if (!download_started) {
-        emit operationFinished("download", this, true);
-    } // otherwise will be emitted on the last onDownloaderFinished signal
+    if (downloaders_.empty()) {
+        emit operationFinished(E_DOWNLOAD, this);
+    }
 }
 
 void TextsPatch::install()
 {
-    emit operationStarted("install", this);
-    QThread::sleep(5);
-    emit operationFinished("install", this, true);
+    emit operationFinished(E_INSTALL, this);
+    return;
+
+    foreach (QString db_name, databases_names) {
+        ++elapsed_patches_to_install_;
+        QMetaObject::invokeMethod(lotro_mgr_, "installPatch", Qt::QueuedConnection,
+                                  Q_ARG(QString, getPatchName() + "_" + db_name),
+                                  Q_ARG(QString, Settings::getValue("PatchDatabases/" + db_name + "/path").toString())
+                                  );
+    }
 }
 
 void TextsPatch::activate()
 {
-    emit operationStarted("activate", this);
-    QThread::sleep(5);
-    emit operationFinished("activate", this, true);
+    emit operationFinished(E_ACTIVATE, this);
+    return;
+
+    QString operation_name = Settings::getValue("Components/fonts").toBool() ? "enableCategory" : "disableCategory";
+    ++elapsed_patches_to_install_;
+    QMetaObject::invokeMethod(lotro_mgr_, operation_name.toLocal8Bit().constData(), Qt::QueuedConnection,
+                              Q_ARG(QString, getPatchName() + "_fonts"),
+                              Q_ARG(LotroDatManager::Category, LotroDatManager::Category::E_FONTS_COMMON)
+                              );
+
+    operation_name = Settings::getValue("Components/texts_main").toBool() ? "enableCategory" : "disableCategory";
+    ++elapsed_patches_to_install_;
+    QMetaObject::invokeMethod(lotro_mgr_, operation_name.toLocal8Bit().constData(), Qt::QueuedConnection,
+                              Q_ARG(QString, getPatchName() + "_texts_main"),
+                              Q_ARG(LotroDatManager::Category, LotroDatManager::Category::E_TEXTS_COMMON)
+                              );
+
+    operation_name = Settings::getValue("Components/texts_items").toBool() ? "enableCategory" : "disableCategory";
+    ++elapsed_patches_to_install_;
+    QMetaObject::invokeMethod(lotro_mgr_, operation_name.toLocal8Bit().constData(), Qt::QueuedConnection,
+                              Q_ARG(QString, getPatchName() + "_texts_items"),
+                              Q_ARG(LotroDatManager::Category, LotroDatManager::Category::E_TEXTS_ITEMS)
+                              );
+
+    operation_name = Settings::getValue("Components/texts_emotes").toBool() ? "enableCategory" : "disableCategory";
+    ++elapsed_patches_to_install_;
+    QMetaObject::invokeMethod(lotro_mgr_, operation_name.toLocal8Bit().constData(), Qt::QueuedConnection,
+                              Q_ARG(QString, getPatchName() + "_texts_emotes"),
+                              Q_ARG(LotroDatManager::Category, LotroDatManager::Category::E_TEXTS_EMOTES)
+                              );
+
+    operation_name = Settings::getValue("Components/videos").toBool() ? "enableCategory" : "disableCategory";
+    ++elapsed_patches_to_install_;
+    QMetaObject::invokeMethod(lotro_mgr_, operation_name.toLocal8Bit().constData(), Qt::QueuedConnection,
+                              Q_ARG(QString, getPatchName() + "_texts_videos_refs"),
+                              Q_ARG(LotroDatManager::Category, LotroDatManager::Category::E_TEXTS_VIDEOS_REFS)
+                              );
 }
 
-
 void TextsPatch::onDownloaderProgressChanged(Downloader *, Downloader::Status)
 {
     Downloader::Status all_downloads_status;
@@ -138,107 +165,75 @@ void TextsPatch::onDownloaderProgressChanged(Downloader *, Downloader::Status)
         all_downloads_status = all_downloads_status + downloader->getDownloadStatus();
     }
 
-    emit downloadStatusChanged(this, all_downloads_status);
+    emit progressChanged(OperationProgress(all_downloads_status), this);
 }
 
 void TextsPatch::onDownloaderFinished(Downloader *ptr)
 {
+    ptr->targetFile->close();
+    ptr->targetFile->deleteLater();
+
     Downloader::Status all_downloads_status;
-    foreach (Downloader* downloader, downloaders_) {
+    for (const Downloader* downloader : downloaders_) {
         all_downloads_status = all_downloads_status + downloader->getDownloadStatus();
     }
 
-    ptr->targetFile->close();
-    ptr->targetFile->deleteLater();
-
-    if (elapsed_patches_to_download_ == 1) {
-        foreach (Downloader* downloader, downloaders_) {
+    if (!all_downloads_status.running) {
+        for (Downloader* downloader : downloaders_) {
             downloader->deleteLater();
         }
         downloaders_.clear();
-
-        emit downloadStatusChanged(this, all_downloads_status);
-        emit operationFinished("download", this, true);
+        emit operationFinished(E_DOWNLOAD, this);
     }
-
-    --elapsed_patches_to_download_;
 }
 
 void TextsPatch::onLotroDatManagerOperationFinished(QString operation_name, QVector<QVariant> args, bool successful)
 {
-    if (operation_name == "installPatch") {
-        if (!args[0].toString().contains("SoundsPatch_")) {
-            return;
-        }
-
-        status_.process = CurrentProcess::E_AWAITING_INSTALL;
-        status_.percent = 100;
-        emit installStatusChanged(this, status_);
-
-        --elapsed_databases_to_install_;
+    if (args.size() == 0 || !args[0].toString().contains(getPatchName())) {
+        return; // This means, that message from LotroManager is addressed to another patchset
+    }
 
+    if (operation_name.contains("installPatch")) {
         if (!successful) {
-            emit errorOccured("install", this, "Database " + args[0].toString() + " (" + args[1].toString() + ") was not installed due to Legacy core error.");
+            qCritical() << *this << "Database " + args[0].toString() + " (" + args[1].toString() + ") was not installed due to Legacy core error.";
+            emit errorOccured(E_INSTALL, this, "Database " + args[0].toString() + " (" + args[1].toString() + ") was not installed due to Legacy core error.");
         }
 
-        if (elapsed_databases_to_install_ == 0) {
-            status_.process = CurrentProcess::E_FINISHED;
-            emit operationFinished("install", this, true);
+        --elapsed_patches_to_install_;
+        if (!elapsed_patches_to_install_) {
+            is_being_patched_by_lotro_dat_manager_ = false;
+            emit operationFinished(E_INSTALL, this);
         }
     }
 
-    if (operation_name == "enableCategory" || operation_name == "disableCategory") {
-        if (!args[0].toString().contains("SoundsPatch_")) {
-            return;
-        }
-
-        status_.process = CurrentProcess::E_AWAITING_APPLY;
-        status_.percent = 100;
-        emit installStatusChanged(this, status_);
-
-        --elapsed_categories_to_activate_;
+    if (operation_name.contains("enableCategory") || operation_name.contains("disableCategory")) {
         if (!successful) {
-            emit errorOccured("activate", this, "Error in patch " + args[0].toString() + ": activating category " + QString::number(args[1].toInt()) + " failed.");
+            qCritical() << *this << "Error in patch " + args[0].toString() + ": activating category " + QString::number(args[1].toInt()) + " failed.";
+            emit errorOccured(E_ACTIVATE, this, "Error in patch " + args[0].toString() + ": activating category " + QString::number(args[1].toInt()) + " failed.");
         }
 
-        if (elapsed_categories_to_activate_ == 0) {
-            status_.process = CurrentProcess::E_FINISHED;
-            emit operationFinished("activate", this, true);
+        --elapsed_patches_to_install_;
+        if (!elapsed_patches_to_install_) {
+            is_being_patched_by_lotro_dat_manager_ = false;
+            emit operationFinished(E_ACTIVATE, this);
         }
     }
 }
 
 void TextsPatch::onLotroDatManagerOperationStarted(QString operation_name, QVector<QVariant> args)
 {
-    if (operation_name == "installPatch") {
-        if (!args[0].toString().contains("SoundsPatch_")) {
-            return;
-        }
-        status_.process = CurrentProcess::E_INSTALL;
-        status_.current_part++;
-        status_.percent = 0;
-        emit installStatusChanged(this, status_);
+    if (args.size() == 0 || !args[0].toString().contains(getPatchName())) {
+        return;
     }
 
-    if (operation_name == "enableCategory" || operation_name == "disableCategory") {
-        if (!args[0].toString().contains("SoundsPatch_")) {
-            return;
-        }
-        status_.process = CurrentProcess::E_APPLY;
-        status_.current_part++;
-        status_.percent = 0;
-        emit installStatusChanged(this, status_);
-    }
+    is_being_patched_by_lotro_dat_manager_ = true;
 }
 
 void TextsPatch::onLotroDatManagerStatusChanged(LotroDatManager::Status status)
 {
-    if (status_.process != CurrentProcess::E_INSTALL || status_.process != CurrentProcess::E_APPLY) {
+    if (!is_being_patched_by_lotro_dat_manager_) {
         return;
     }
 
-    status_.percent = status.percent;
-    emit installStatusChanged(this, status_);
+    emit progressChanged(OperationProgress(status), this);
 }
-
-

+ 3 - 6
src/Legacy/models/patch/textspatch.h

@@ -12,7 +12,7 @@ class TextsPatch : public Patch
 public:
     TextsPatch(LotroDatManager* mgr, QObject* parent = nullptr);
 
-public slots:
+private slots:
     virtual void checkForUpdates() override;
     virtual void download() override;
     virtual void install() override;
@@ -21,8 +21,9 @@ public slots:
 private slots:
     void onDownloaderProgressChanged(Downloader* ptr, Downloader::Status status);
     void onDownloaderFinished(Downloader* ptr);
-    void onLotroDatManagerOperationFinished(QString operation_name, QVector<QVariant> args, bool successful);
+
     void onLotroDatManagerOperationStarted(QString operation_name, QVector<QVariant> args);
+    void onLotroDatManagerOperationFinished(QString operation_name, QVector<QVariant> args, bool successful);
     void onLotroDatManagerStatusChanged(LotroDatManager::Status status);
 
 private:
@@ -30,9 +31,5 @@ private:
         "text",
         "font"
     };
-
-    quint8 elapsed_patches_to_download_ = 0; // for download process
-    quint8 elapsed_databases_to_install_ = 0; // for install process
-    quint8 elapsed_categories_to_activate_ = 0; // for activate/deactivate process
 };
 #endif // TEXTPATCH_H

+ 30 - 128
src/Legacy/models/patch/videospatch.cpp

@@ -1,7 +1,6 @@
 #include "videospatch.h"
 #include "LotroDat/LotroDat.h"
 #include "LotroDat/Database.h"
-#include "models/lotrodatmanagerobserver.h"
 
 #include <QUrlQuery>
 #include <QSet>
@@ -9,14 +8,10 @@
 
 VideosPatch::VideosPatch(LotroDatManager *mgr, QObject *parent) : Patch("VideosPatch", mgr, parent)
 {
-    connect(lotro_mgr_, &LotroDatManager::operationStarted, this, &VideosPatch::onLotroDatManagerOperationStarted);
-    connect(lotro_mgr_, &LotroDatManager::operationFinished, this, &VideosPatch::onLotroDatManagerOperationFinished);
-    connect(lotro_mgr_->getObserver(), &LotroDatManagerObserver::statusChanged, this, &VideosPatch::onLotroDatManagerStatusChanged);
 }
 
 void VideosPatch::checkForUpdates()
 {
-    emit operationStarted("checkForUpdates", this);
     QUrlQuery query; // query for building GET-request aka patch-version
 
     foreach (QString db_name, databases_names) {
@@ -35,26 +30,26 @@ void VideosPatch::checkForUpdates()
     downloader.waitForDownloaded();
 
     if (target_array.isEmpty()) {
-        qDebug() << __FUNCTION__ << "Cannot download, target_array is empty!";
-        emit errorOccured("checkForUpdates", this, "QueryDownloadFailed");
-        emit operationFinished("checkForUpdates", this, false);
+        qWarning() << *this << "Cannot check for updates, target_array is empty!";
+        emit errorOccured(E_CHECKFORUPDATES, this, "");
+        emit operationFinished(E_CHECKFORUPDATES, this);
         return;
     }
 
     QStringList patch_info = QString(target_array).split('|');
     if (patch_info.size() != databases_names.size()) {
-        qDebug() << __FUNCTION__ << "Incorrect patches number! Data: " << patch_info;
-        emit errorOccured("checkForUpdates", this, "IncorrectQueryResult");
-        emit operationFinished("checkForUpdates", this, false);
+        qCritical() << __FUNCTION__ << "Incorrect patches number! Data: " << patch_info;
+        emit errorOccured(E_CHECKFORUPDATES, this, "");
+        emit operationFinished(E_CHECKFORUPDATES, this);
         return;
     }
 
     for (int i = 0; i < databases_names.size(); ++i) {
         QStringList patch_data = patch_info[i].split(":::");
         if (patch_data.size() != 3) {
-            qDebug() << __FUNCTION__ << "Incorrect patch entry size! Entry: " << patch_data;
-            emit errorOccured("checkForUpdates", this, "IncorrectDbInfo");
-            emit operationFinished("checkForUpdates", this, false);
+            qCritical() << __FUNCTION__ << "Incorrect patch entry size! Entry: " << patch_data;
+            emit errorOccured(E_CHECKFORUPDATES, this, "");
+            emit operationFinished(E_CHECKFORUPDATES, this);
             return;
         }
 
@@ -65,42 +60,32 @@ void VideosPatch::checkForUpdates()
         Settings::setValue("PatchDatabases/" + databases_names[i] + "/datetime", patch_data[2]);
         Settings::setValue("PatchDatabases/" + databases_names[i] + "/path", patch_filename);
     }
-    emit operationFinished("checkForUpdates", this, true);
+
+    emit operationFinished(E_CHECKFORUPDATES, this);
 }
 
 void VideosPatch::download()
 {
-    if (elapsed_patches_to_download_ != 0) {
-        qDebug() << "Trying to start download of patch set " << patch_name_ << " while download is still active!";
-        return;
-    }
-
-    emit operationStarted("download", this);
-
-    bool download_started = false;
-
     foreach (QString db_name, databases_names) {
         QString settings_prefix = "PatchDatabases/" + db_name;
 
         QString target_filename = QApplication::applicationDirPath() + "/" + Settings::getValue(settings_prefix + "/path").toString();
-        qDebug() << "VideosPatch: Checking if there's no need to download file " << target_filename;
+        qDebug() << patch_name_ << ": Checking if file " << target_filename << " matches its hashsum";
 
         if (FileSystem::fileHash(target_filename) == Settings::getValue(settings_prefix + "/hashsum").toString()) {
-            qDebug() << "VideosPatch: file " << target_filename << " is fresh, no need to download";
+            qInfo() << *this << ": file " << target_filename << " is up-to-date, no need to download";
             continue;
         }
 
         FileSystem::createFilePath(target_filename);
         QFile* target_file = new QFile(target_filename);
         if (!target_file->open(QIODevice::ReadWrite | QIODevice::Truncate)) {
-            emit errorOccured("download", this, "CantOpenDbFile");
+            qWarning() << *this << "Cannot open file " << target_filename;
             continue;
         }
 
-        qDebug() << "VideosPatch: beginning download of file " << target_filename;
-        download_started = true;
+        qInfo() << *this << ": beginning download of file " << target_filename;
 
-        ++elapsed_patches_to_download_;
         Downloader* downloader = new Downloader();
         downloader->setUrl(Settings::getValue(settings_prefix + "/url").toUrl());
         downloader->targetFile = target_file;
@@ -110,23 +95,21 @@ void VideosPatch::download()
         downloader->start();
     }
 
-    if (!download_started) {
-        emit operationFinished("download", this, true);
-    } // otherwise will be emitted on the last onDownloaderFinished signal
+    if (downloaders_.empty()) {
+        emit operationFinished(E_DOWNLOAD, this);
+    }
 }
 
 void VideosPatch::install()
 {
-    emit operationStarted("install", this);
-    QThread::sleep(5);
-    emit operationFinished("install", this, true);
+    QThread::sleep(3);
+    emit operationFinished(E_INSTALL, this);
 }
 
 void VideosPatch::activate()
 {
-    emit operationStarted("activate", this);
-    QThread::sleep(5);
-    emit operationFinished("activate", this, true);
+    QThread::sleep(3);
+    emit operationFinished(E_ACTIVATE, this);
 }
 
 void VideosPatch::onDownloaderProgressChanged(Downloader *, Downloader::Status)
@@ -136,105 +119,24 @@ void VideosPatch::onDownloaderProgressChanged(Downloader *, Downloader::Status)
         all_downloads_status = all_downloads_status + downloader->getDownloadStatus();
     }
 
-    emit downloadStatusChanged(this, all_downloads_status);
+    emit progressChanged(OperationProgress(all_downloads_status), this);
 }
 
 void VideosPatch::onDownloaderFinished(Downloader *ptr)
 {
+    ptr->targetFile->close();
+    ptr->targetFile->deleteLater();
+
     Downloader::Status all_downloads_status;
-    foreach (Downloader* downloader, downloaders_) {
+    for (const Downloader* downloader : downloaders_) {
         all_downloads_status = all_downloads_status + downloader->getDownloadStatus();
     }
 
-    ptr->targetFile->close();
-    ptr->targetFile->deleteLater();
-
-    if (elapsed_patches_to_download_ == 1) {
-        foreach (Downloader* downloader, downloaders_) {
+    if (!all_downloads_status.running) {
+        for (Downloader* downloader : downloaders_) {
             downloader->deleteLater();
         }
         downloaders_.clear();
-
-        emit downloadStatusChanged(this, all_downloads_status);
-        emit operationFinished("download", this, true);
+        emit operationFinished(E_DOWNLOAD, this);
     }
-
-    --elapsed_patches_to_download_;
-}
-
-void VideosPatch::onLotroDatManagerOperationFinished(QString operation_name, QVector<QVariant> args, bool successful)
-{
-    if (operation_name == "installPatch") {
-        if (!args[0].toString().contains("SoundsPatch_")) {
-            return;
-        }
-
-        status_.process = CurrentProcess::E_AWAITING_INSTALL;
-        status_.percent = 100;
-        emit installStatusChanged(this, status_);
-
-        --elapsed_databases_to_install_;
-
-        if (!successful) {
-            emit errorOccured("install", this, "Database " + args[0].toString() + " (" + args[1].toString() + ") was not installed due to Legacy core error.");
-        }
-
-        if (elapsed_databases_to_install_ == 0) {
-            status_.process = CurrentProcess::E_FINISHED;
-            emit operationFinished("install", this, true);
-        }
-    }
-
-    if (operation_name == "enableCategory" || operation_name == "disableCategory") {
-        if (!args[0].toString().contains("SoundsPatch_")) {
-            return;
-        }
-
-        status_.process = CurrentProcess::E_AWAITING_APPLY;
-        status_.percent = 100;
-        emit installStatusChanged(this, status_);
-
-        --elapsed_categories_to_activate_;
-        if (!successful) {
-            emit errorOccured("activate", this, "Error in patch " + args[0].toString() + ": activating category " + QString::number(args[1].toInt()) + " failed.");
-        }
-
-        if (elapsed_categories_to_activate_ == 0) {
-            status_.process = CurrentProcess::E_FINISHED;
-            emit operationFinished("activate", this, true);
-        }
-    }
-}
-
-void VideosPatch::onLotroDatManagerOperationStarted(QString operation_name, QVector<QVariant> args)
-{
-    if (operation_name == "installPatch") {
-        if (!args[0].toString().contains("SoundsPatch_")) {
-            return;
-        }
-        status_.process = CurrentProcess::E_INSTALL;
-        status_.current_part++;
-        status_.percent = 0;
-        emit installStatusChanged(this, status_);
-    }
-
-    if (operation_name == "enableCategory" || operation_name == "disableCategory") {
-        if (!args[0].toString().contains("SoundsPatch_")) {
-            return;
-        }
-        status_.process = CurrentProcess::E_APPLY;
-        status_.current_part++;
-        status_.percent = 0;
-        emit installStatusChanged(this, status_);
-    }
-}
-
-void VideosPatch::onLotroDatManagerStatusChanged(LotroDatManager::Status status)
-{
-    if (status_.process != CurrentProcess::E_INSTALL || status_.process != CurrentProcess::E_APPLY) {
-        return;
-    }
-
-    status_.percent = status.percent;
-    emit installStatusChanged(this, status_);
 }

+ 1 - 8
src/Legacy/models/patch/videospatch.h

@@ -12,7 +12,7 @@ class VideosPatch : public Patch
 public:
     VideosPatch(LotroDatManager* mgr, QObject* parent = nullptr);
 
-public slots:
+private slots:
     virtual void checkForUpdates() override;
     virtual void download() override;
     virtual void install() override;
@@ -21,18 +21,11 @@ public slots:
 private slots:
     void onDownloaderProgressChanged(Downloader* ptr, Downloader::Status status);
     void onDownloaderFinished(Downloader* ptr);
-    void onLotroDatManagerOperationFinished(QString operation_name, QVector<QVariant> args, bool successful);
-    void onLotroDatManagerOperationStarted(QString operation_name, QVector<QVariant> args);
-    void onLotroDatManagerStatusChanged(LotroDatManager::Status status);
 
 private:
     const QStringList databases_names = {
         "video"
     };
-
-    quint8 elapsed_patches_to_download_ = 0; // for download process
-    quint8 elapsed_databases_to_install_ = 0; // for install process
-    quint8 elapsed_categories_to_activate_ = 0; // for activate/deactivate process
 };
 
 #endif // VIDEOSPATCH_H

+ 77 - 77
src/Legacy/models/patchlist.cpp

@@ -6,21 +6,18 @@ PatchList::PatchList(LotroDatManager *mgr, QObject *parent) : QObject(parent)
 {
     lotro_mgr_ = mgr;
 
+    qInfo() << "PatchList: Initialising patchsets";
     texts_patch_ = new TextsPatch(mgr);
     graphics_patch_ = new GraphicsPatch(mgr);
     sounds_patch_ = new SoundsPatch(mgr);
     videos_patch_ = new VideosPatch(mgr);
 
+    qInfo() << "PatchList: Patchsets were initialized, initializing threads";
     texts_patch_thread_ = new QThread(this);
     graphics_patch_thread_ = new QThread(this);
     sounds_patch_thread_ = new QThread(this);
     videos_patch_thread_ = new QThread(this);
 
-    connect(texts_patch_thread_, &QThread::finished, texts_patch_, &TextsPatch::deleteLater);
-    connect(graphics_patch_thread_, &QThread::finished, graphics_patch_, &GraphicsPatch::deleteLater);
-    connect(sounds_patch_thread_, &QThread::finished, sounds_patch_, &SoundsPatch::deleteLater);
-    connect(videos_patch_thread_, &QThread::finished, videos_patch_, &VideosPatch::deleteLater);
-
     texts_patch_->moveToThread(texts_patch_thread_);
     graphics_patch_->moveToThread(graphics_patch_thread_);
     sounds_patch_->moveToThread(sounds_patch_thread_);
@@ -36,94 +33,74 @@ PatchList::PatchList(LotroDatManager *mgr, QObject *parent) : QObject(parent)
     connect(sounds_patch_, &SoundsPatch::operationFinished, this, &PatchList::onPatchOperationFinished, Qt::QueuedConnection);
     connect(videos_patch_, &VideosPatch::operationFinished, this, &PatchList::onPatchOperationFinished, Qt::QueuedConnection);
 
-    connect(texts_patch_, &TextsPatch::downloadStatusChanged, this, &PatchList::onPatchDownloadStatusChanged, Qt::QueuedConnection);
-    connect(graphics_patch_, &GraphicsPatch::downloadStatusChanged, this, &PatchList::onPatchDownloadStatusChanged, Qt::QueuedConnection);
-    connect(sounds_patch_, &SoundsPatch::downloadStatusChanged, this, &PatchList::onPatchDownloadStatusChanged, Qt::QueuedConnection);
-    connect(videos_patch_, &VideosPatch::downloadStatusChanged, this, &PatchList::onPatchDownloadStatusChanged, Qt::QueuedConnection);
-
-    connect(texts_patch_, &TextsPatch::installStatusChanged, this, &PatchList::onPatchInstallStatusChanged, Qt::QueuedConnection);
-    connect(graphics_patch_, &GraphicsPatch::installStatusChanged, this, &PatchList::onPatchInstallStatusChanged, Qt::QueuedConnection);
-    connect(sounds_patch_, &SoundsPatch::installStatusChanged, this, &PatchList::onPatchInstallStatusChanged, Qt::QueuedConnection);
-    connect(videos_patch_, &VideosPatch::installStatusChanged, this, &PatchList::onPatchInstallStatusChanged, Qt::QueuedConnection);
+    connect(texts_patch_, &TextsPatch::progressChanged, this, &PatchList::onPatchOperationProgressChanged, Qt::QueuedConnection);
+    connect(graphics_patch_, &GraphicsPatch::progressChanged, this, &PatchList::onPatchOperationProgressChanged, Qt::QueuedConnection);
+    connect(sounds_patch_, &SoundsPatch::progressChanged, this, &PatchList::onPatchOperationProgressChanged, Qt::QueuedConnection);
+    connect(videos_patch_, &VideosPatch::progressChanged, this, &PatchList::onPatchOperationProgressChanged, Qt::QueuedConnection);
 
     connect(lotro_mgr_, &LotroDatManager::operationFinished, this, &PatchList::onLotroManagerOperationFinished, Qt::QueuedConnection);
 
+    qInfo() << "PatchList: Patchset threads initialized, starting workers";
     texts_patch_thread_->start();
     graphics_patch_thread_->start();
     sounds_patch_thread_->start();
     videos_patch_thread_->start();
-
-    patch_update_timer.setInterval(10 * 60 * 1000); // 10 minutes
-    connect(&patch_update_timer, &QTimer::timeout, this, &PatchList::update);
-}
-
-QList<Patch *> PatchList::getPatchList()
-{
-    return {texts_patch_, graphics_patch_, sounds_patch_, videos_patch_};
 }
 
-void PatchList::onPatchOperationStarted(QString operation_name, Patch *patch)
+PatchList::~PatchList()
 {
-    patch_update_timer.stop();
-    if (active_operations_num_ == 0) {
-        emit patchOperationsStarted();
+    qDebug() << "PatchList: finishing work";
+    texts_patch_thread_->quit();
+    graphics_patch_thread_->quit();
+    sounds_patch_thread_->quit();
+    videos_patch_thread_->quit();
+
+    if (!texts_patch_thread_->wait(500)) {
+        qDebug() << "PatchList: ERROR, TEXTS PATCHSET DIDNT STOP, FORCEFULLY TERMINATING!";
+        texts_patch_thread_->terminate();
+        texts_patch_thread_->wait();
     }
-    ++active_operations_num_;
-
-    qDebug() << "Operation " << operation_name << " started of patchset " << patch->getPatchName();
-}
 
-void PatchList::onPatchOperationFinished(QString operation_name, Patch *patch, bool result)
-{
-    qDebug() << "Operation " << operation_name << " finished of patchset " << patch->getPatchName() << ", result = " << result;
-    --active_operations_num_;
+    if (!graphics_patch_thread_->wait(500)) {
+        qDebug() << "PatchList: ERROR, GRAPHICS PATCHSET DIDNT STOP, FORCEFULLY TERMINATING!";
+        graphics_patch_thread_->terminate();
+        graphics_patch_thread_->wait();
+    }
 
-    if (result && operation_name == "checkForUpdates") {
-        QMetaObject::invokeMethod(patch, &Patch::download, Qt::QueuedConnection);
-        return;
+    if (!sounds_patch_thread_->wait(500)) {
+        qDebug() << "PatchList: ERROR, SOUNDS PATCHSET DIDNT STOP, FORCEFULLY TERMINATING!";
+        sounds_patch_thread_->terminate();
+        sounds_patch_thread_->wait();
     }
 
-//    if (result && operation_name == "download") {
-//        QMetaObject::invokeMethod(patch, &Patch::install, Qt::QueuedConnection);
-//        return;
-//    }
+    if (!videos_patch_thread_->wait(500)) {
+        qDebug() << "PatchList: ERROR, VIDEOS PATCHSET DIDNT STOP, FORCEFULLY TERMINATING!";
+        videos_patch_thread_->terminate();
+        videos_patch_thread_->wait();
+    }
 
-//    if (result && operation_name == "install") {
-//        QMetaObject::invokeMethod(patch, &Patch::activate, Qt::QueuedConnection);
-//        return;
-//    }
+    qDebug() << "Patchlist: all jobs stopped, destroying";
 
-    if (active_operations_num_ == 0) {
-        if (auto_updates_enabled_) {
-            patch_update_timer.start();
-        }
-        qDebug() << __FUNCTION__ << "All patch operations successfully finished!";
-        emit patchOperationsFinished();
-    }
+    delete texts_patch_;
+    delete graphics_patch_;
+    delete sounds_patch_;
+    delete videos_patch_;
 }
 
-void PatchList::onPatchDownloadStatusChanged(Patch *patch, Downloader::Status status)
+QList<Patch *> PatchList::getPatchList()
 {
-    patches_download_status_[patch] = status;
-    Downloader::Status total_status;
-
-    foreach (Downloader::Status st, patches_download_status_) {
-        total_status = total_status + st;
-    }
-
-    emit downloadTotalStatusChanged(total_status);
+    return {texts_patch_, graphics_patch_, sounds_patch_, videos_patch_};
 }
 
-void PatchList::onPatchInstallStatusChanged(Patch *patch, Patch::InstallationStatus status)
+void PatchList::onPatchOperationProgressChanged(Patch::OperationProgress operation_progress, Patch *patch)
 {
-    patches_installation_status_[patch] = status;
-    Patch::InstallationStatus total_status;
+    patch_operations_status_[patch] = operation_progress;
 
-    foreach (Patch::InstallationStatus st, patches_installation_status_) {
+    Patch::OperationProgress total_status;
+    for (const Patch::OperationProgress &st : patch_operations_status_) {
         total_status = total_status + st;
     }
-
-    emit installTotalStatusChanged(total_status);
+    emit progressChanged(total_status);
 }
 
 void PatchList::onLotroManagerOperationFinished(QString operation_name, QVector<QVariant>, bool result)
@@ -135,9 +112,12 @@ void PatchList::onLotroManagerOperationFinished(QString operation_name, QVector<
             emit patchOperationsFinished();
         }
 
-//        if (result == true) {
+        if (result) {
+            startAutoUpdate();
             update();
-//        }
+        } else {
+            qCritical() << "DatManager initialisation error!!!";
+        }
     }
 }
 
@@ -153,16 +133,36 @@ void PatchList::initialize() {
     QMetaObject::invokeMethod(lotro_mgr_, &LotroDatManager::initializeManager, Qt::QueuedConnection);
 }
 
+void PatchList::onPatchOperationStarted(Patch::Operation, Patch*)
+{
+    if (active_operations_num_ == 0) {
+        emit patchOperationsStarted();
+    }
+    ++active_operations_num_;
+}
+
+void PatchList::onPatchOperationFinished(Patch::Operation, Patch*)
+{
+    --active_operations_num_;
+    if (active_operations_num_ == 0) {
+        emit patchOperationsFinished();
+    }
+}
+
 void PatchList::update()
 {
-    if (active_operations_num_ != 0) {
-        qDebug() << __FUNCTION__ << "Tried to start update, while other operations weren't finished yet!";
+    if (active_operations_num_ > 0) {
+        qWarning() << "Tried to start patch update chain, while there are already some operations on them!";
         return;
     }
-
-    qDebug() << __FUNCTION__ << "Starting update!";
-    QMetaObject::invokeMethod(texts_patch_, &TextsPatch::checkForUpdates, Qt::QueuedConnection);
-    QMetaObject::invokeMethod(graphics_patch_, &GraphicsPatch::checkForUpdates, Qt::QueuedConnection);
-    QMetaObject::invokeMethod(sounds_patch_, &SoundsPatch::checkForUpdates, Qt::QueuedConnection);
-    QMetaObject::invokeMethod(videos_patch_, &VideosPatch::checkForUpdates, Qt::QueuedConnection);
+    qInfo() << "PatchList: Starting update chain!";
+    for (Patch* patch : getPatchList()) {
+        QMetaObject::invokeMethod(patch, "enqueue",
+                                  Q_ARG(QList<Patch::Operation>,
+                                        QList<Patch::Operation>({Patch::E_CHECKFORUPDATES,
+                                                                 Patch::E_DOWNLOAD,
+                                                                 Patch::E_INSTALL,
+                                                                 Patch::E_ACTIVATE})));
+    }
 }
+

+ 10 - 15
src/Legacy/models/patchlist.h

@@ -18,16 +18,14 @@ class PatchList : public QObject
 public:
     explicit PatchList(LotroDatManager* mgr, QObject *parent = nullptr);
 
+    ~PatchList();
+
     QList<Patch *> getPatchList();
 
 signals:
-    void patchOperationsFinished();
-
+    void progressChanged(Patch::OperationProgress total_status);
     void patchOperationsStarted();
-
-    void downloadTotalStatusChanged(Downloader::Status status);
-
-    void installTotalStatusChanged(Patch::InstallationStatus status);
+    void patchOperationsFinished();
 
 public slots:
     void startAutoUpdate();
@@ -37,19 +35,17 @@ public slots:
     void initialize();
 
 private slots:
-    void onPatchOperationStarted(QString operation_name, Patch* patch);
-
-    void onPatchOperationFinished(QString operation_name, Patch* patch, bool result);
-
-    void onPatchDownloadStatusChanged(Patch* patch, Downloader::Status status);
-
-    void onPatchInstallStatusChanged(Patch* patch, Patch::InstallationStatus status);
+    void onPatchOperationStarted(Patch::Operation operation, Patch* patch);
+    void onPatchOperationFinished(Patch::Operation operation, Patch* patch);
+    void onPatchOperationProgressChanged(Patch::OperationProgress operation_progress, Patch* patch);
 
+private slots:
     void onLotroManagerOperationFinished(QString operation_name, QVector<QVariant> args, bool result);
 
 private:
     LotroDatManager *lotro_mgr_ = nullptr;
     bool lotro_mgr_initialised_ = false;
+    bool operations_running_ = false;
 
     TextsPatch *texts_patch_ = nullptr;
     GraphicsPatch *graphics_patch_ = nullptr;
@@ -61,8 +57,7 @@ private:
     QThread *sounds_patch_thread_ = nullptr;
     QThread *videos_patch_thread_ = nullptr;
 
-    QMap<Patch*, Downloader::Status> patches_download_status_;
-    QMap<Patch*, Patch::InstallationStatus> patches_installation_status_;
+    QMap<Patch*, Patch::OperationProgress> patch_operations_status_;
 
     quint32 active_operations_num_ = 0;
     bool auto_updates_enabled_ = false;

+ 0 - 2
src/Legacy/object_script.Legacy.Debug

@@ -24,7 +24,6 @@ INPUT(
 ./..\..\build\debug\Legacy\obj\videospatch.o
 ./..\..\build\debug\Legacy\obj\textspatch.o
 ./..\..\build\debug\Legacy\obj\graphicspatch.o
-./..\..\build\debug\Legacy\obj\lotrodatmanagerobserver.o
 ./..\..\build\debug\Legacy\obj\patchlist.o
 ./..\..\build\debug\Legacy\obj\legacyapplication.o
 ./..\..\build\debug\Legacy\obj\legacy_plugin_import.o
@@ -49,6 +48,5 @@ INPUT(
 ./..\..\build\debug\Legacy\obj\moc_videospatch.o
 ./..\..\build\debug\Legacy\obj\moc_textspatch.o
 ./..\..\build\debug\Legacy\obj\moc_graphicspatch.o
-./..\..\build\debug\Legacy\obj\moc_lotrodatmanagerobserver.o
 ./..\..\build\debug\Legacy\obj\moc_patchlist.o
 );

+ 0 - 2
src/Legacy/object_script.Legacy.Release

@@ -22,7 +22,6 @@ INPUT(
 ./..\..\build\release\Legacy\obj\videospatch.o
 ./..\..\build\release\Legacy\obj\textspatch.o
 ./..\..\build\release\Legacy\obj\graphicspatch.o
-./..\..\build\release\Legacy\obj\lotrodatmanagerobserver.o
 ./..\..\build\release\Legacy\obj\patchlist.o
 ./..\..\build\release\Legacy\obj\legacyapplication.o
 ./..\..\build\release\Legacy\obj\legacy_plugin_import.o
@@ -47,6 +46,5 @@ INPUT(
 ./..\..\build\release\Legacy\obj\moc_videospatch.o
 ./..\..\build\release\Legacy\obj\moc_textspatch.o
 ./..\..\build\release\Legacy\obj\moc_graphicspatch.o
-./..\..\build\release\Legacy\obj\moc_lotrodatmanagerobserver.o
 ./..\..\build\release\Legacy\obj\moc_patchlist.o
 );

+ 1 - 1
src/Legacy/widgets/aboutwidget.ui

@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>1000</width>
-    <height>538</height>
+    <height>530</height>
    </rect>
   </property>
   <property name="windowTitle">

+ 1 - 1
src/Legacy/widgets/mainwindow.cpp

@@ -129,7 +129,7 @@ void MainWindow::resizeEvent(QResizeEvent * event)
     ui->menu_widget->resize(width * 650 / default_window_width, height * 53 / default_window_height);
 
     ui->content_area->move(0, height * 110 / default_window_height);
-    ui->content_area->resize(width * 1000 / default_window_width, height * 538 / default_window_height);
+    ui->content_area->resize(width * 1000 / default_window_width, height * 530 / default_window_height);
     setupWindowBackgroundAndMask(current_bg_);
     choose_locale_dialog_->resize(event->size());
 

+ 1 - 1
src/Legacy/widgets/mainwindow.ui

@@ -47,7 +47,7 @@
       <x>0</x>
       <y>110</y>
       <width>1000</width>
-      <height>538</height>
+      <height>530</height>
      </rect>
     </property>
     <property name="sizePolicy">

+ 125 - 113
src/Legacy/widgets/statuswidget.cpp

@@ -19,7 +19,7 @@ StatusWidget::StatusWidget(PatchList *legacy_patches, QWidget *parent)
 {
 
     ui->setupUi(this);
-    last_progress_update_time.start();
+    last_statusbar_update_time_.start();
 
     connect(ui->weekly_code_widget, &WeeklyCodeWidget::showCompletedTooltip, this, &StatusWidget::setToolTipToWeeklyCodeComplete);
     connect(ui->weekly_code_widget, &WeeklyCodeWidget::showHelpTooltip, this, &StatusWidget::setToolTipToWeeklyCodeHelp);
@@ -64,16 +64,14 @@ StatusWidget::StatusWidget(PatchList *legacy_patches, QWidget *parent)
     active_tooltip = nullptr;
     resetToolTip();
 
-    connect(legacy_patches_, &PatchList::downloadTotalStatusChanged, this, &StatusWidget::onDownloadTotalStatusChanged);
-    connect(legacy_patches_, &PatchList::installTotalStatusChanged, this, &StatusWidget::onInstallTotalStatusChanged);
-    connect(legacy_patches_, &PatchList::patchOperationsStarted, this, &StatusWidget::onPatchOperationsStarted);
-    connect(legacy_patches_, &PatchList::patchOperationsFinished, this, &StatusWidget::onPatchOperationsFinished);
+    connect(legacy_patches_, &PatchList::patchOperationsStarted, this, &StatusWidget::onPatchTotalOperationsStarted);
+    connect(legacy_patches_, &PatchList::patchOperationsFinished, this, &StatusWidget::onPatchTotalOperationsFinished);
+    connect(legacy_patches_, &PatchList::progressChanged, this, &StatusWidget::onPatchTotalProgressChanged);
 
     foreach (Patch* patch, legacy_patches_->getPatchList()) {
-        connect(patch, &Patch::downloadStatusChanged, this, &StatusWidget::onPatchDownloadStatusChanged);
-        connect(patch, &Patch::installStatusChanged, this, &StatusWidget::onPatchInstallStatusChanged);
-        connect(patch, &Patch::operationFinished, this, &StatusWidget::onPatchOperationFinished);
+        connect(patch, &Patch::progressChanged, this, &StatusWidget::onPatchProgressChanged);
         connect(patch, &Patch::operationStarted, this, &StatusWidget::onPatchOperationStarted);
+        connect(patch, &Patch::operationFinished, this, &StatusWidget::onPatchOperationFinished);
     }
 }
 
@@ -106,34 +104,34 @@ void StatusWidget::updateFontsSizes()
 void StatusWidget::resizeEvent(QResizeEvent *)
 {
     double coefficient = window_width / default_window_width;
-    ui->game_button->move(QPoint(820, 460) * coefficient);
-    ui->game_button->resize(QSize(150, 60) * coefficient);
-    ui->progressBar->move(QPoint(320, 480) * coefficient);
-    ui->progressBar->resize(QSize(470, 40) * coefficient);
-    ui->progress_label->move(QPoint(330, 390) * coefficient);
-    ui->progress_label->resize(QSize(380, 90) * coefficient);
-    ui->news_label->move(QPoint(45, 33)* coefficient);
-    ui->news_label->resize(QSize(180, 21) * coefficient);
-    ui->news_scroll_area->move(QPoint(40, 75) * coefficient);
-    ui->news_scroll_area->resize(QSize(240, 440) * coefficient);
-    ui->server_status_widget->move(QPoint(820, 90) * coefficient);
-    ui->server_status_widget->resize(QSize(155, 320) * coefficient);
-    ui->weekly_code_widget->move(QPoint(810, 13) * coefficient);
-    ui->weekly_code_widget->resize(QSize(173, 57) * coefficient);
-    ui->galadriel_widget->move(QPoint(320, 20) * coefficient);
-    ui->galadriel_widget->resize(QSize(531, 461) * coefficient);
-
-    ui->news_tooltip->move(QPoint(38, 13) * coefficient);
-    ui->news_tooltip->resize(QSize(365, 114) * coefficient);
-    ui->weekly_code_tooltip_1->move(QPoint(38, 13) * coefficient);
-    ui->weekly_code_tooltip_1->resize(QSize(365, 114) * coefficient);
-    ui->weekly_code_tooltip_2->move(QPoint(38, 13) * coefficient);
-    ui->weekly_code_tooltip_2->resize(QSize(365, 114) * coefficient);
-    ui->server_status_tooltip->move(QPoint(38, 13) * coefficient);
-    ui->server_status_tooltip->resize(QSize(365, 114) * coefficient);
-
-    ui->patches_status->move(QPoint(38, 13) * coefficient);
-    ui->patches_status->resize(QSize(385, 114) * coefficient);
+//    ui->game_button->move(QPoint(820, 460) * coefficient);
+//    ui->game_button->resize(QSize(150, 60) * coefficient);
+//    ui->progressBar->move(QPoint(320, 480) * coefficient);
+//    ui->progressBar->resize(QSize(470, 40) * coefficient);
+//    ui->progress_label->move(QPoint(330, 390) * coefficient);
+//    ui->progress_label->resize(QSize(380, 90) * coefficient);
+//    ui->news_label->move(QPoint(45, 33)* coefficient);
+//    ui->news_label->resize(QSize(180, 21) * coefficient);
+//    ui->news_scroll_area->move(QPoint(40, 75) * coefficient);
+//    ui->news_scroll_area->resize(QSize(240, 440) * coefficient);
+//    ui->server_status_widget->move(QPoint(820, 90) * coefficient);
+//    ui->server_status_widget->resize(QSize(155, 320) * coefficient);
+//    ui->weekly_code_widget->move(QPoint(810, 13) * coefficient);
+//    ui->weekly_code_widget->resize(QSize(173, 57) * coefficient);
+//    ui->galadriel_widget->move(QPoint(320, 20) * coefficient);
+//    ui->galadriel_widget->resize(QSize(531, 461) * coefficient);
+
+//    ui->news_tooltip->move(QPoint(38, 13) * coefficient);
+//    ui->news_tooltip->resize(QSize(365, 114) * coefficient);
+//    ui->weekly_code_tooltip_1->move(QPoint(38, 13) * coefficient);
+//    ui->weekly_code_tooltip_1->resize(QSize(365, 114) * coefficient);
+//    ui->weekly_code_tooltip_2->move(QPoint(38, 13) * coefficient);
+//    ui->weekly_code_tooltip_2->resize(QSize(365, 114) * coefficient);
+//    ui->server_status_tooltip->move(QPoint(38, 13) * coefficient);
+//    ui->server_status_tooltip->resize(QSize(365, 114) * coefficient);
+
+//    ui->patches_status->move(QPoint(38, 13) * coefficient);
+//    ui->patches_status->resize(QSize(385, 114) * coefficient);
 
     updateFontsSizes();
 }
@@ -183,130 +181,144 @@ void StatusWidget::fadeBetweenToolTips(QWidget* next_tooltip)
     active_tooltip = next_tooltip;
 }
 
-void StatusWidget::onPatchOperationsStarted()
+void StatusWidget::onPatchTotalOperationsStarted()
 {
+    all_patch_operations_finished_ = false;
     ui->game_button->setEnabled(false);
-    ui->progress_label->setText("Выполение операций....");
-    ui->progressBar->setValue(0);
 }
 
-void StatusWidget::onPatchOperationsFinished()
+void StatusWidget::onPatchTotalOperationsFinished()
 {
+    all_patch_operations_finished_ = true;
     ui->game_button->setEnabled(true);
-    ui->progress_label->setText("Все операции завершены.");
-    ui->progressBar->setValue(100);
-}
 
-void StatusWidget::onDownloadTotalStatusChanged(Downloader::Status status)
-{
-    last_download_status_ = status;
-    updateStatusBar();
-//        qDebug() << "Download total status: \n" <<
-//                    "    cur_speed = " << Downloader::getSpeedFormatted(status.current_speed) << "\n" <<
-//                    "    avg_speed = " << Downloader::getSpeedFormatted(status.average_speed) << "\n" <<
-//                    "    percent = " << status.percent << "\n" <<
-//                    "    total size = " << Downloader::getSizeFormatted(status.total_bytes) << "\n" <<
-//                    "    downloaded size = " << Downloader::getSizeFormatted(status.downloaded_bytes) << "\n" <<
-//                    "    elapsed time = " << status.elapsed_time << "\n\n";
+    for (Patch* patch : legacy_patches_->getPatchList()) {
+        QString label_name = patch->getPatchName().toLower() + "_status";
+        QLabel *label = findChild<QLabel*>(label_name);
+        if (!label) {
+            return;
+        }
 
-}
+        QString install_date;
+        if (!(install_date = Settings::getValue("UpdateDates/" + patch->getPatchName()).toString()).isEmpty()) {
+            label->setText("Патч версии от " + install_date);
+        } else {
+            label->setText("Все операции с патчем завершены.");
+        }
+    }
 
-void StatusWidget::onInstallTotalStatusChanged(Patch::InstallationStatus status)
-{
-    last_install_status_ = status;
-    updateStatusBar();
+    ui->progress_label->setText("");
 }
 
-void StatusWidget::onPatchOperationStarted(QString operation_name, Patch *patch)
+void StatusWidget::onPatchOperationStarted(Patch::Operation operation, Patch *patch)
 {
-    QLabel* patch_status_label = findChild<QLabel*>(patch->getPatchName().toLower() + "_status");
-    if (!patch_status_label) {
-        qDebug() << "Cannot find status label for patch " << patch->getPatchName();
+    QString label_name = patch->getPatchName().toLower() + "_status";
+    QLabel *label = findChild<QLabel*>(label_name);
+    patch_operations[patch] = operation;
+    if (!label) {
         return;
     }
 
-    QString text = "";
-    if (operation_name == "checkForUpdates") {
-        text = "Поиск и проверка обновлений...";
-    }
-    if (operation_name == "download") {
-        text = "Проверка файлов данных...";
+    switch (operation) {
+    case Patch::E_CHECKFORUPDATES:
+        label->setText("Проверка наличия обновлений...");
+        break;
+    case Patch::E_DOWNLOAD:
+        label->setText("Подготовка к загрузке данных...");
+        break;
+    case Patch::E_INSTALL:
+        label->setText("Ожидание начала установки...");
+        break;
+    case Patch::E_ACTIVATE:
+        label->setText("Ожидание начала активации...");
+    default:
+        break;
     }
-    if (operation_name == "install") {
-        text = "Ожидание начала установки...";
-    }
-    if (operation_name == "apply") {
-        text = "Ожидание начала применения...";
-    }
-
-    patch_status_label->setText(text);
 }
 
-void StatusWidget::onPatchOperationFinished(QString operation_name, Patch *patch, bool result)
+void StatusWidget::onPatchOperationFinished(Patch::Operation operation, Patch *patch)
 {
-    QLabel* patch_status_label = findChild<QLabel*>(patch->getPatchName().toLower() + "_status");
-    if (!patch_status_label) {
-        qDebug() << "Cannot find status label for patch " << patch->getPatchName();
+    if (all_patch_operations_finished_) {
         return;
     }
 
-    if (result) {
-        patch_status_label->setText("Все операции завершены");
-        QString install_date;
-        if (!(install_date = Settings::getValue("UpdateDates/" + patch->getPatchName()).toString()).isEmpty()) {
-            patch_status_label->setText("Патч версии от " + install_date);
-        }
-    } else {
-        patch_status_label->setText("Операция завершилась с ошибкой");
-    }
-}
-
-void StatusWidget::onPatchDownloadStatusChanged(Patch *patch, Downloader::Status status)
-{
     QString label_name = patch->getPatchName().toLower() + "_status";
     QLabel *label = findChild<QLabel*>(label_name);
+
     if (!label) {
-        qDebug() << "Error, cannot find child of StatusWidget with name " + label_name;
         return;
     }
 
-    label->setText("Загрузка " + QString::number(status.percent, 'f', 1) + "% (" + Downloader::getSizeFormatted(status.downloaded_bytes) + " / "
-                   + Downloader::getSizeFormatted(status.total_bytes) + ")");
+    switch (operation) {
+    case Patch::E_CHECKFORUPDATES:
+        label->setText("Проверка обновлений завершена");
+        break;
+    case Patch::E_DOWNLOAD:
+        label->setText("Загрузка данных завершена");
+        break;
+    case Patch::E_INSTALL:
+        label->setText("Установка патча завершена");
+        break;
+    case Patch::E_ACTIVATE:
+        label->setText("Активация патча завершена");
+    default:
+        break;
+    }
+}
+
+void StatusWidget::onPatchTotalProgressChanged(Patch::OperationProgress operation_progress)
+{
+    updateStatusBar(operation_progress);
 }
 
-void StatusWidget::onPatchInstallStatusChanged(Patch *patch, Patch::InstallationStatus status)
+void StatusWidget::onPatchProgressChanged(Patch::OperationProgress progress, Patch *patch)
 {
     QString label_name = patch->getPatchName().toLower() + "_status";
     QLabel *label = findChild<QLabel*>(label_name);
     if (!label) {
-        qDebug() << "Error, cannot find child of StatusWidget with name " + label_name;
         return;
     }
 
-    if (status.process == Patch::CurrentProcess::E_INSTALL) {
-        label->setText("Установка (" + QString::number(status.current_part) + " из " + QString::number(status.total_parts) + ") " + QString::number(status.percent, 'f', 1));
+    switch (patch_operations[patch]) {
+    case Patch::E_CHECKFORUPDATES:
+        label->setText("Проверка наличия обновлений...");
+        break;
+    case Patch::E_DOWNLOAD:
+        label->setText("Загрузка... " + QString::number(progress.getDownloadPercent(), 'f', 1)
+                       + "% (" + Downloader::getSizeFormatted(progress.download_finished_bytes)
+                       + "/" + Downloader::getSizeFormatted(progress.download_total_bytes) + ")");
+        break;
+    case Patch::E_INSTALL:
+        label->setText("Установка... " + QString::number(progress.getInstallPercent(), 'f', 2)
+                       + "% (" + QString::number(progress.install_finished_parts) + "/" + QString::number(progress.install_total_parts) + ")");
+        break;
+    case Patch::E_ACTIVATE:
+        label->setText("Активация... " + QString::number(progress.getInstallPercent(), 'f', 2)
+                       + "% (" + QString::number(progress.install_finished_parts) + "/" + QString::number(progress.install_total_parts) + ")");
+    default:
+        break;
     }
 }
 
-void StatusWidget::updateStatusBar()
+void StatusWidget::updateStatusBar(Patch::OperationProgress progress)
 {
-    if (last_progress_update_time.elapsed() > 1000) {
+    if (last_statusbar_update_time_.elapsed() > 500) {
         QString text = "Выполнение операций...";
-        if (last_download_status_.running) {
-            text += "\nЗагрузка данных: " + QString::number(last_download_status_.percent, 'f', 1) + "% ("
-                    + Downloader::getSizeFormatted(last_download_status_.downloaded_bytes) + "/"
-                    + Downloader::getSizeFormatted(last_download_status_.total_bytes) + ", "
-                    + Downloader::getSpeedFormatted(last_download_status_.current_speed) + ")\n"
-                    + "Осталось примерно: " + Downloader::getElapsedTimeFormatted(last_download_status_.elapsed_time);
+        if (progress.download_total_bytes != 0) {
+            text += "\nЗагрузка данных: " + QString::number(progress.getDownloadPercent(), 'f', 1) + "% ("
+                    + Downloader::getSizeFormatted(progress.download_finished_bytes) + "/"
+                    + Downloader::getSizeFormatted(progress.download_total_bytes) + ", "
+                    + Downloader::getSpeedFormatted(progress.download_speed) + ")\n"
+                    + "До конца загрузки: " + Downloader::getElapsedTimeFormatted(progress.download_elapsed_time);
         }
 
-        if (last_install_status_.process != Patch::CurrentProcess::E_FINISHED) {
-            text += "\nПрименение патчей: " + QString::number(last_install_status_.percent + 100.0 * (last_install_status_.current_part - 1) / (100 * last_install_status_.total_parts), 'f', 1) + "%";
+        if (progress.install_total_parts != 0) {
+            text += "\nПрименение патчей: " + QString::number(progress.getInstallPercent()) + "% "
+                    + "(часть " + QString::number(progress.install_finished_parts + 1) + " из " + QString::number(progress.install_total_parts);
         }
 
         ui->progress_label->setText(text);
-        ui->progressBar->setValue(last_download_status_.percent * 0.85 + last_install_status_.percent * 0.15);
-        last_progress_update_time.restart();
+        last_statusbar_update_time_.restart();
     }
 }
 

+ 13 - 14
src/Legacy/widgets/statuswidget.h

@@ -37,32 +37,31 @@ private slots:
 
     void fadeBetweenToolTips(QWidget* next_tooltip);
 
-    void onPatchOperationsStarted();
-    void onPatchOperationsFinished();
-    void onDownloadTotalStatusChanged(Downloader::Status status);
-    void onInstallTotalStatusChanged(Patch::InstallationStatus status);
-    
-    void onPatchOperationStarted(QString operation_name, Patch* patch);
-    void onPatchOperationFinished(QString operation_name, Patch* patch, bool result);
-    void onPatchDownloadStatusChanged(Patch* patch, Downloader::Status status);
-    void onPatchInstallStatusChanged(Patch* patch, Patch::InstallationStatus status);
+    void onPatchTotalOperationsStarted();
+    void onPatchTotalOperationsFinished();
+
+    void onPatchOperationStarted(Patch::Operation operation, Patch* patch);
+    void onPatchOperationFinished(Patch::Operation operation, Patch* patch);
+
+    void onPatchTotalProgressChanged(Patch::OperationProgress operation_progress);
+    void onPatchProgressChanged(Patch::OperationProgress progress, Patch* patch);
 
 private:
-    void updateStatusBar();
+    void updateStatusBar(Patch::OperationProgress progress);
 
 private:
-    Downloader::Status last_download_status_;
-    Patch::InstallationStatus last_install_status_;
-    
     Ui::StatusWidget *ui;
 
     PatchList *legacy_patches_;
 
+    bool all_patch_operations_finished_ = false;
+
     QMap<QString, QGraphicsOpacityEffect*> tooltip_effects;
     QMap<QString, QPropertyAnimation*> tooltip_animations;
+    QMap<Patch*, Patch::Operation> patch_operations;
     QWidget* active_tooltip;
 
-    QTime last_progress_update_time;
+    QTime last_statusbar_update_time_;
 };
 
 #endif // STATUSWIDGET_H

+ 61 - 108
src/Legacy/widgets/statuswidget.ui

@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>1000</width>
-    <height>538</height>
+    <height>530</height>
    </rect>
   </property>
   <property name="sizePolicy">
@@ -50,7 +50,7 @@ color: white;
      <x>45</x>
      <y>75</y>
      <width>240</width>
-     <height>440</height>
+     <height>453</height>
     </rect>
    </property>
    <property name="styleSheet">
@@ -112,7 +112,7 @@ QScrollBar:vertical {
       <x>0</x>
       <y>0</y>
       <width>240</width>
-      <height>440</height>
+      <height>453</height>
      </rect>
     </property>
    </widget>
@@ -121,7 +121,7 @@ QScrollBar:vertical {
    <property name="geometry">
     <rect>
      <x>820</x>
-     <y>90</y>
+     <y>120</y>
      <width>155</width>
      <height>320</height>
     </rect>
@@ -130,10 +130,10 @@ QScrollBar:vertical {
   <widget class="WeeklyCodeWidget" name="weekly_code_widget" native="true">
    <property name="geometry">
     <rect>
-     <x>810</x>
-     <y>13</y>
-     <width>173</width>
-     <height>57</height>
+     <x>780</x>
+     <y>30</y>
+     <width>201</width>
+     <height>61</height>
     </rect>
    </property>
    <property name="autoFillBackground">
@@ -146,10 +146,10 @@ QScrollBar:vertical {
   <widget class="QWidget" name="galadriel_widget" native="true">
    <property name="geometry">
     <rect>
-     <x>320</x>
-     <y>20</y>
-     <width>531</width>
-     <height>461</height>
+     <x>310</x>
+     <y>36</y>
+     <width>541</width>
+     <height>491</height>
     </rect>
    </property>
    <property name="styleSheet">
@@ -160,9 +160,9 @@ border-image: url(:/characters/galadriel_with_text.png);
    <widget class="QLabel" name="weekly_code_tooltip_1">
     <property name="geometry">
      <rect>
-      <x>38</x>
-      <y>13</y>
-      <width>365</width>
+      <x>29</x>
+      <y>17</y>
+      <width>396</width>
       <height>114</height>
      </rect>
     </property>
@@ -189,9 +189,9 @@ border-image: url(:/characters/galadriel_with_text.png);
    <widget class="QLabel" name="news_tooltip">
     <property name="geometry">
      <rect>
-      <x>38</x>
-      <y>13</y>
-      <width>365</width>
+      <x>29</x>
+      <y>17</y>
+      <width>396</width>
       <height>114</height>
      </rect>
     </property>
@@ -218,9 +218,9 @@ border-image: url(:/characters/galadriel_with_text.png);
    <widget class="QLabel" name="weekly_code_tooltip_2">
     <property name="geometry">
      <rect>
-      <x>38</x>
-      <y>13</y>
-      <width>365</width>
+      <x>29</x>
+      <y>17</y>
+      <width>396</width>
       <height>114</height>
      </rect>
     </property>
@@ -247,9 +247,9 @@ border-image: url(:/characters/galadriel_with_text.png);
    <widget class="QWidget" name="patches_status" native="true">
     <property name="geometry">
      <rect>
-      <x>50</x>
-      <y>13</y>
-      <width>344</width>
+      <x>29</x>
+      <y>17</y>
+      <width>396</width>
       <height>114</height>
      </rect>
     </property>
@@ -442,9 +442,9 @@ border-image: url(:/characters/galadriel_with_text.png);
    <widget class="QLabel" name="server_status_tooltip">
     <property name="geometry">
      <rect>
-      <x>38</x>
-      <y>13</y>
-      <width>365</width>
+      <x>29</x>
+      <y>17</y>
+      <width>396</width>
       <height>114</height>
      </rect>
     </property>
@@ -468,11 +468,45 @@ border-image: url(:/characters/galadriel_with_text.png);
      <bool>true</bool>
     </property>
    </widget>
+   <widget class="QLabel" name="progress_label">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>400</y>
+      <width>381</width>
+      <height>90</height>
+     </rect>
+    </property>
+    <property name="sizePolicy">
+     <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+      <horstretch>0</horstretch>
+      <verstretch>0</verstretch>
+     </sizepolicy>
+    </property>
+    <property name="font">
+     <font>
+      <family>Trajan Pro 3</family>
+      <pointsize>9</pointsize>
+      <weight>50</weight>
+      <bold>false</bold>
+     </font>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+    <property name="alignment">
+     <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+    </property>
+    <property name="wordWrap">
+     <bool>false</bool>
+    </property>
+   </widget>
    <zorder>weekly_code_tooltip_1</zorder>
    <zorder>news_tooltip</zorder>
    <zorder>weekly_code_tooltip_2</zorder>
    <zorder>server_status_tooltip</zorder>
    <zorder>patches_status</zorder>
+   <zorder>progress_label</zorder>
   </widget>
   <widget class="QPushButton" name="game_button">
    <property name="geometry">
@@ -539,91 +573,10 @@ QPushButton#game_button:disabled {
     <bool>true</bool>
    </property>
   </widget>
-  <widget class="QLabel" name="progress_label">
-   <property name="geometry">
-    <rect>
-     <x>329</x>
-     <y>390</y>
-     <width>381</width>
-     <height>90</height>
-    </rect>
-   </property>
-   <property name="sizePolicy">
-    <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
-     <horstretch>0</horstretch>
-     <verstretch>0</verstretch>
-    </sizepolicy>
-   </property>
-   <property name="font">
-    <font>
-     <family>Trajan Pro 3</family>
-     <pointsize>9</pointsize>
-     <weight>50</weight>
-     <bold>false</bold>
-    </font>
-   </property>
-   <property name="text">
-    <string>Все операции выполнены</string>
-   </property>
-   <property name="alignment">
-    <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
-   </property>
-   <property name="wordWrap">
-    <bool>false</bool>
-   </property>
-  </widget>
-  <widget class="QProgressBar" name="progressBar">
-   <property name="geometry">
-    <rect>
-     <x>320</x>
-     <y>480</y>
-     <width>470</width>
-     <height>40</height>
-    </rect>
-   </property>
-   <property name="minimumSize">
-    <size>
-     <width>0</width>
-     <height>40</height>
-    </size>
-   </property>
-   <property name="maximumSize">
-    <size>
-     <width>16777215</width>
-     <height>40</height>
-    </size>
-   </property>
-   <property name="styleSheet">
-    <string notr="true">QProgressBar {
-    border: 0px solid grey;
-	border-image: url(:/per.png);
-}
-
-QProgressBar::chunk {
-background-image: url(:/letters.png);
-margin-left:8px;
-margin-right:8px;
-margin-top:7px;
-margin-bottom:7px;
-}
-</string>
-   </property>
-   <property name="maximum">
-    <number>100</number>
-   </property>
-   <property name="value">
-    <number>1</number>
-   </property>
-   <property name="textVisible">
-    <bool>false</bool>
-   </property>
-  </widget>
   <zorder>galadriel_widget</zorder>
   <zorder>news_label</zorder>
   <zorder>news_scroll_area</zorder>
   <zorder>game_button</zorder>
-  <zorder>progress_label</zorder>
-  <zorder>progressBar</zorder>
   <zorder>server_status_widget</zorder>
   <zorder>weekly_code_widget</zorder>
  </widget>