Browse Source

Ver 1.2.0

Ivan Arkhipov 6 years ago
parent
commit
5437a7f25c
13 changed files with 411 additions and 88 deletions
  1. 24 7
      anetwork.cpp
  2. 2 1
      anetwork.h
  3. 99 33
      downloadmanager.cpp
  4. 16 2
      downloadmanager.h
  5. 68 1
      helper.cpp
  6. 3 0
      helper.h
  7. 104 15
      lotromanager.cpp
  8. 12 0
      lotromanager.h
  9. 1 1
      main.cpp
  10. 75 25
      mainwindow.cpp
  11. 4 0
      mainwindow.h
  12. 2 2
      mainwindow.ui
  13. 1 1
      ui_mainwindow.h

+ 24 - 7
anetwork.cpp

@@ -156,8 +156,17 @@ QString ANetwork::getServers(){
     return content;
 }
 
-void ANetwork::UpdatePatches(){
+void ANetwork::UpdatePatches() {
     App *app = &App::getInstance();
+    if (app->state == "busy") {
+        qInfo("%s:%i: %s", __FILE__, __LINE__, "Проверка актуальности патчей запущена во время выполения операции. Прерываю проверку");
+        return;
+    }
+
+    app->helper->setState("busy");
+    emit changeHint("Проверка обновлений патчей", "Выполняется поиск обновлений патчей...");
+    app->downloader->resetDownloadedCount();
+
     QUrlQuery query;
     QStringList names;
     QString version;
@@ -167,9 +176,11 @@ void ANetwork::UpdatePatches(){
 
     QString datafolder = QApplication::applicationDirPath() + "/data";
     QDir dir(datafolder);
-    if(!dir.exists()) QDir().mkdir(datafolder);
+    if (!dir.exists())
+        QDir().mkdir(datafolder);
 
     foreach(QString s, names) {
+        emit changeHint("Проверка обновлений патчей", "Проверка патча " + LotroManager::getInstance().patchTitleFromName(s) + "...");
 
         if(app->config->getValue("Editor", s) == "false") {
             emit changePatchStatus(s, "Не выбран");
@@ -177,6 +188,8 @@ void ANetwork::UpdatePatches(){
         }
 
         QStringList paths = dir.entryList(QStringList(s + "*"));
+        qDebug("%s:%i: %s%s", __FILE__, __LINE__, "Найденные патчи: ", QString(paths.join(' ')).toLocal8Bit().data());
+
 
         QString dateline = app->config->getValue("Datetime", s);
         if (dateline != "-1") {
@@ -188,8 +201,7 @@ void ANetwork::UpdatePatches(){
         if (paths.empty()) {
             qDebug("%s:%i: %s%s", __FILE__, __LINE__, "Патч отсутствует: ", s.toLocal8Bit().data());
             version = "100";
-            emit changePatchStatus(s, "Отсутствует");
-
+            emit changePatchStatus(s, "В очереди");
             query.addQueryItem(s.left(s.length() - 1), version);
             continue;
         }
@@ -207,9 +219,8 @@ void ANetwork::UpdatePatches(){
             base.remove();
             version="100";
 
-            emit changePatchStatus(s, "Ошибка хэша");
+            emit changePatchStatus(s, "В очереди");
         }
-
         query.addQueryItem(s.left(s.length() - 1), version);
     }
 
@@ -220,17 +231,22 @@ void ANetwork::UpdatePatches(){
     if(content == "error"){
         qInfo("%s:%i: %s", __FILE__, __LINE__, "Отсутствует связь с сервером. Прервано.");
         app->logSectionEnd();
+        emit changeHint("Ошибка проверки обновлений", "Отсутствует связь с сервером. Проверка обновлений прервана.");
+        app->helper->setState("free");
         return;
     }
 
     qDebug() << "\nОтвет сервера: " << content;
     if (content == "Запросы GET или POST отсутствуют!!!!") {
         qInfo("%s:%i: %s", __FILE__, __LINE__, "Отсутствуют запросы к серверу.");
+        app->helper->setState("free");
         return;
     }
 
     if (content == "") {
         qInfo("%s:%i: %s", __FILE__, __LINE__, "Все версии патчей соответствуют актуальным.");
+        emit changeHint("Проверка завершена", "Все версии патчей соответствуют актуальным.");
+        app->helper->setState("free");
         return;
     }
 
@@ -258,7 +274,8 @@ void ANetwork::UpdatePatches(){
         app->downloader->append(url);
     }
     qInfo("%s:%i: %s", __FILE__, __LINE__, "Начинаем загрузку патчей.");
-    app->downloader->startNextDownload();
+    app->helper->setState("free");
+    app->downloader->startDownloads();
 }
 
 QString ANetwork::getMicroPath(int timestamp){

+ 2 - 1
anetwork.h

@@ -36,8 +36,9 @@ public:
 
 signals:
     void changePatchStatus(QString patch_name, QString new_status);
+    void changeHint(QString header, QString hint);
 
-//private slots:
+private slots:
 //    void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
 //    void downloadFinished();
 };

+ 99 - 33
downloadmanager.cpp

@@ -1,5 +1,6 @@
 #include "app.h"
 #include "filesystem.h"
+#include "lotromanager.h"
 
 #include <QFileInfo>
 #include <QApplication>
@@ -13,6 +14,7 @@
 DownloadManager::DownloadManager(QObject *parent)
     : QObject(parent), downloadedCount(0), totalCount(0)
 {
+    busy = false;
 }
 
 void DownloadManager::append(const QStringList &urlList){
@@ -24,22 +26,45 @@ void DownloadManager::append(const QStringList &urlList){
 }
 
 void DownloadManager::append(const QUrl &url){
-    qDebug("%s:%i: %s%s", __FILE__, __LINE__, "Добавлен: ", url.fileName().toLocal8Bit().data());
-    if (downloadQueue.isEmpty())
-        QTimer::singleShot(0, this, SLOT(startNextDownload()));
+    qDebug("%s:%i: %s%s", __FILE__, __LINE__, "Добавлен файл для скачивания: ", url.fileName().toLocal8Bit().data());
 
-    downloadQueue.enqueue(url);
-    ++totalCount;
+    if (downloadQueue.count(url) == 0) {
+        downloadQueue.enqueue(url);
+        ++totalCount;
+    } else {
+        qDebug("%s:%i: %s %s%s", __FILE__, __LINE__, "Не добавляю закачку "
+               , url.fileName().toLocal8Bit().data()
+               , ", так как она уже находится в очереди закачек");
+    }
     qDebug("%s:%i: %s%i", __FILE__, __LINE__, "Загрузок в списке: ", totalCount);
+
+    //if (!busy)
+    //    QTimer::singleShot(0, this, SLOT(startNextDownload()));
 }
 
-void DownloadManager::startNextDownload()
-{
+void DownloadManager::startDownloads() {
+    if (!busy)
+        QTimer::singleShot(0, this, SLOT(startNextDownload()));
+}
+
+void DownloadManager::startNextDownload() {
+    current_speed = "";
+    bytesReceivedBeforeSecond = 0;
+    timeElapsedBeforeSecond = 0;
+
+    if (busy) {
+        qWarning("%s:%i: %s", __FILE__, __LINE__, "Ошибка! Обнаружена попытка начала загрузки, когда не завершилась другая загрузка");
+        return;
+    }
+
     App *app = &App::getInstance();
+    app->helper->setState("busy");
+    busy = true;
 
     if (downloadQueue.isEmpty()) {
         app->helper->setState("free");// говорим что приложение освободилось
         qDebug("%s:%i: %s%d/%d", __FILE__, __LINE__, "Загрузка завершена. Загружено файлов: ", downloadedCount, totalCount);
+        busy = false;
         emit finished();
         return;
     }
@@ -48,6 +73,7 @@ void DownloadManager::startNextDownload()
     QString filename = QFileInfo(url.path()).fileName();
     output.setFileName(QApplication::applicationDirPath() + "/data/" + filename);
     qDebug("%s:%i: %s%s", __FILE__, __LINE__, "Начата загрузка файла: ", filename.toLocal8Bit().data());
+
     // Проверяем целостность файла и игнорируем в случае если он цел
     QString hash = FileSystem::fileHash(QApplication::applicationDirPath() + "/data/" + filename, QCryptographicHash::Md5);
     QStringList pname = output.fileName().split("/");
@@ -57,7 +83,8 @@ void DownloadManager::startNextDownload()
     if(keyname == "loadscreens") keyname = "screens";
     if(hash == app->config->getValue("Hashes", ptype.first()) && app->config->getValue("Editor", keyname) == "true"){
         qDebug("%s:%i: %s%s", __FILE__, __LINE__, "Проверка хэша успешно завершена: ", filename.toLocal8Bit().data());
-        startNextDownload();
+        busy = false;
+        QTimer::singleShot(0, this, SLOT(startNextDownload()));
         return;
     }
 
@@ -65,38 +92,44 @@ void DownloadManager::startNextDownload()
     QStringList parsename = filename.split("_");
     QString name = parsename[0] + "Status";
     download_name = parsename[0];
-    app->writtenLabel = app->window->ui->mainbox->findChild<QLabel *>(name);
+
+    // Проверка, что файл не был отменён
+    if(app->config->getValue("Editor", download_name) == "false") {
+        emit changePatchStatus(download_name, "Отменён");
+        busy = false;
+        QTimer::singleShot(0, this, SLOT(startNextDownload()));
+        return;
+    }
 
     if (!output.open(QIODevice::WriteOnly)) {
-        qWarning("%s:%i: %s%s", __FILE__, __LINE__, "Произошла остановка скачивания ", filename.toLocal8Bit().data());
-        if(app->writtenLabel != nullptr) app->writtenLabel->setText("Не удалась");
-        startNextDownload();
+        qWarning("%s:%i: %s%s", __FILE__, __LINE__, "Произошла остановка скачивания (не могу открыть файл) "
+                 , filename.toLocal8Bit().data());
+        emit changePatchStatus(download_name, "Ошибка записи");
+        busy = false;
+        QTimer::singleShot(0, this, SLOT(startNextDownload()));
         return;
     }
 
     qInfo("%s:%i: %s%s", __FILE__, __LINE__, "Начинаем скачивание ", url.fileName().toLocal8Bit().data());
 
-    app->helper->setState("busy");// говорим что приложение занято
-
     QNetworkRequest request(url);
     currentDownload = manager.get(request);
     connect(this, SIGNAL(cancelDownload()), currentDownload, SLOT(abort()));
-    connect(currentDownload, SIGNAL(downloadProgress(qint64,qint64)),
-            SLOT(downloadProgress(qint64,qint64)));
-    connect(currentDownload, SIGNAL(finished()),
-            SLOT(downloadFinished()));
-    connect(currentDownload, SIGNAL(readyRead()),
-            SLOT(downloadReadyRead()));
+    connect(currentDownload, SIGNAL(downloadProgress(qint64,qint64)), SLOT(downloadProgress(qint64,qint64)));
+    connect(currentDownload, SIGNAL(finished()), SLOT(downloadFinished()));
+    connect(currentDownload, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
 
     // prepare the output
 
     downloadTime.start();
 }
 
-void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
-{
-    double speed = bytesReceived * 1000.0 / downloadTime.elapsed();
+void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) {    
+    App* app = &App::getInstance();
+
+    double speed = (bytesReceived - bytesReceivedBeforeSecond) * 1000.0 / (downloadTime.elapsed() - timeElapsedBeforeSecond);
     double percent =  double(std::ceil(((double)bytesReceived/ bytesTotal) * 100 * 10)) / 10.0;
+
     QString unit;
     if (speed < 1024) {
         unit = "bytes/sec";
@@ -108,28 +141,50 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
         unit = "MB/s";
     }
     QString speedtext = QString::fromLatin1("%1 %2").arg(speed, 3, 'f', 1).arg(unit);
-    QString percenttext = QString::fromLatin1("%1").arg(percent, 3);
+    QString percenttext = QString::fromLatin1("%1").arg(percent, 3, 'f', 1);
 
-    App *app = &App::getInstance();
-    if(app->writtenLabel != nullptr) app->writtenLabel->setText("Загрузка ... <br/>" + percenttext+ "% (" + speedtext + ")");
+    if (downloadTime.elapsed() - timeElapsedBeforeSecond >= 1000) {
+        timeElapsedBeforeSecond = downloadTime.elapsed();
+        bytesReceivedBeforeSecond = bytesReceived;
+        current_speed = speedtext;
+    }
+
+    if (current_speed == "")
+        current_speed = speedtext;
+
+    QString elapsed_time = app->helper->countFormattedElapsedTime(bytesTotal - bytesReceived, bytesReceived * 1000.0 / downloadTime.elapsed());
+
+    emit changePatchStatus(download_name, "Загрузка ... <br/>" + percenttext + "% (" + current_speed + ")");
+    emit changeHint("Загрузка патча " + LotroManager::getInstance().patchTitleFromName(download_name),
+                    "Завершено " + percenttext + "% (" + current_speed + "). Осталось примерно " + elapsed_time);
 }
 
 void DownloadManager::downloadFinished() {
+    busy = false;
     output.close();
+    qDebug() << "Finished download: " << output.fileName().split('/').last().split('_')[0];
+
+    downloaded_list.append(output.fileName().split('/').last().split('_')[0]);
+
     App *app = &App::getInstance();
     app->helper->setState("free");// говорим что приложение освободилось
 
     if (currentDownload->error()) {
-         qWarning("%s:%i: %s%s", __FILE__, __LINE__, "Загрузка не удалась: ", currentDownload->errorString().toLocal8Bit().data());
-         if(app->writtenLabel != nullptr) app->writtenLabel->setText("Не удалась.");
-    } else {       
-        if(app->writtenLabel != nullptr) app->writtenLabel->setText("Готово");
-        qInfo("%s:%i: %s", __FILE__, __LINE__, "Все загрузки завершены. Загрузчик завершил свою работу.");
+        qWarning("%s:%i: %s%s", __FILE__, __LINE__, "Загрузка не удалась: ", currentDownload->errorString().toLocal8Bit().data());
+        emit changePatchStatus(download_name, "Не удалась");
+    } else {
+        emit changePatchStatus(download_name, "Готово");
         ++downloadedCount;
     }
 
+    if (downloadQueue.isEmpty()) {
+        qInfo("%s:%i: %s", __FILE__, __LINE__, "Все загрузки завершены. Загрузчик завершил свою работу.");
+        emit allDownloadsFinished(downloaded_list);
+    } else {
+        QTimer::singleShot(0, this, SLOT(startNextDownload()));
+    }
+
     currentDownload->deleteLater();
-    startNextDownload();
 }
 
 void DownloadManager::downloadReadyRead(){
@@ -142,8 +197,19 @@ void DownloadManager::abortDownload(QString name){
        if(app->state != "free" &&  currentDownload != NULL && currentDownload->isOpen()) {
             qDebug() << "Прерываем закачку " + download_name;
             qInfo("%s:%i: %s%s", __FILE__, __LINE__, "Пользователь прервал закачку файла ", download_name.toLocal8Bit().data());
-            if(app->writtenLabel != nullptr) app->writtenLabel->setText("Не выбран");
+            emit changePatchStatus(name, "Отменён");
             currentDownload->abort();
        }
     }
+    downloadQueue.removeAll(name);
+}
+
+int DownloadManager::getDownloadedCount() {
+    return downloadedCount;
+}
+
+void DownloadManager::resetDownloadedCount() {
+    totalCount = 0;
+    downloadedCount = 0;
+    downloaded_list.clear();
 }

+ 16 - 2
downloadmanager.h

@@ -20,14 +20,21 @@ public:
 
     void append(const QUrl &url);
     void append(const QStringList &urlList);
-    void startNextDownload();
     void abortDownload(QString name);
 
+    int getDownloadedCount();
+    void resetDownloadedCount();
+    void startDownloads();
+
 signals:
+    void changePatchStatus(QString name, QString status);
+    void changeHint(QString title, QString hint);
     void finished();
     void cancelDownload();
-private slots:
+    void allDownloadsFinished(QStringList downloads);
 
+private slots:
+    void startNextDownload();
     void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
     void downloadFinished();
     void downloadReadyRead();
@@ -40,8 +47,15 @@ private:
     QTime downloadTime;
     QString download_name;
 
+    QStringList downloaded_list;
+
+    int bytesReceivedBeforeSecond; // Нужны для подсчёта текущей скорости скачивания, а не
+    int timeElapsedBeforeSecond;   // средней за всё время.
+    QString current_speed;
+
     int downloadedCount;
     int totalCount;
+    bool busy;
 };
 
 #endif // DOWNLOADMANAGER_H

+ 68 - 1
helper.cpp

@@ -69,6 +69,7 @@ void Helper::loadNews(){
         title->setText(html);
         teaser->setText(item[2]);
         counter = counter+1;
+        qInfo("%s:%i: %s %s %s", __FILE__, __LINE__, "Размещена новость:", html.toLocal8Bit().data(), QString(item[2]).toUtf8().data());
     }
     qInfo("%s:%i: %s", __FILE__, __LINE__, "Выполнено.");
     app->logSectionEnd();
@@ -298,7 +299,7 @@ void Helper::setState(QString state){
         app->window->ui->loader->hide();
     }
 
-    if(state == "runfirst"){
+    if (state == "runfirst") {
         app->window->ui->enterButton->setEnabled(false);
         app->window->ui->repairButton->setEnabled(false);
         app->state = "runfirst";
@@ -363,3 +364,69 @@ void Helper::replaceSkin(QString name){
     movie->setScaledSize(QSize(app->window->ui->loader->width(), app->window->ui->loader->height()));
     movie->start();
 }
+
+QString Helper::countFormattedElapsedTime(qint64 ticks_elapsed, double average_tick_speed) {
+    if (average_tick_speed < 0.01) {
+        return " очень много (низкая скорость)";
+    }
+
+    qint64 secs = ticks_elapsed / average_tick_speed;
+    qint64 mins = 0;
+    qint64 hours = 0;
+    qint64 days = 0;
+
+    if (secs > 60) {
+        mins = secs / 60;
+        secs %= 60;
+    }
+
+    if (mins > 60) {
+        hours = mins / 60;
+        mins %= 60;
+    }
+
+    if (hours > 24) {
+        days = hours / 24;
+        hours %= 24;
+    }
+
+    if (days > 0)
+        return " очень много (низкая скорость)";
+
+
+    QString result = "";
+
+    if (hours > 0) {
+        result += QString::number(hours);
+        if (hours % 10 == 1 && hours / 10 != 1)
+            result += " час ";
+        else if (hours % 10 > 1 && hours % 10 < 5 && hours / 10 != 1)
+            result += " часа ";
+        else
+            result += " часов ";
+    }
+
+    if (mins > 0) {
+        result += QString::number(mins);
+        if (mins % 10 == 1 && mins / 10 != 1)
+            result += " минута ";
+        else if (mins % 10 > 1 && mins % 10 < 5 && mins / 10 != 1)
+            result += " минуты ";
+        else
+            result += " минут ";
+    }
+
+    if (secs > 0 && hours == 0) {
+        result += QString::number(secs);
+        if (secs % 10 == 1 && secs / 10 != 1)
+            result += " секунда ";
+        else if (secs % 10 > 1 && secs % 10 < 5 && secs / 10 != 1)
+            result += " секунды ";
+        else
+            result += " секунд ";
+    }
+
+    if (result == "")
+        result = "совсем чуть-чуть";
+    return result;
+}

+ 3 - 0
helper.h

@@ -35,6 +35,9 @@ class Helper:public QObject {
 
     void replaceSkin(QString name);
 
+    QString countFormattedElapsedTime(qint64 ticks_elapsed, double average_tick_speed);
+
+
     QString dialog_ok_answer;
     QString dialog_cancel_answer;
 

+ 104 - 15
lotromanager.cpp

@@ -3,6 +3,9 @@
 #include "filesystem.h"
 
 #include <QStringList>
+#include <QDateTime>
+#include <QDate>
+#include <QTime>
 
 LotroManager::LotroManager(QObject *parent) : QObject(parent){
     dat_files_.resize(5);
@@ -72,6 +75,15 @@ bool LotroManager::execute(QString command, QString args, QString name) {
         return true;
     }
 
+    if (command == "ApplyPatchList") {
+        QtConcurrent::run([=]() {
+            emit dat_operation_started(command, args, name);
+            applyPatch(args.split('|'));
+            emit dat_operation_finished(command, args, name);
+        });
+        return true;
+    }
+
     if (command == "ApplyGlobal") {
         QtConcurrent::run([=]() {
             emit dat_operation_started(command, args, name);
@@ -79,6 +91,7 @@ bool LotroManager::execute(QString command, QString args, QString name) {
             applyPatch("sounds");
             applyPatch("texts");
             applyPatch("images");
+            applyPatch("textures");
             applyLoadscreens();
             applyMicroPatch();
             emit dat_operation_finished(command, args, name);
@@ -222,7 +235,16 @@ bool LotroManager::openDatFile(int id) {
     return true;
 }
 
-void LotroManager::applyPatch(QString name){
+void LotroManager::applyPatch(QString name) {
+    if (name == "loadscreens") {
+        applyLoadscreens();
+        return;
+    }
+
+    if (name == "videos") {
+        return;
+    }
+
     App *app = &App::getInstance();
     emit changeHint("Применение патчей", "Ожидайте. Идёт применение выбранных патчей");
 
@@ -235,28 +257,77 @@ void LotroManager::applyPatch(QString name){
     }
 
     QStringList paths = dir.entryList(QStringList(name + "*"));
+    qInfo("%s:%i: %s%s%s%s", __FILE__, __LINE__, "Для патча ", name.toLocal8Bit().data(), " найдены пути:", QString(paths.join(',')).toLocal8Bit().data());
+
+    if (paths.empty()) {
+        emit changePatchStatus(name, "Не найден");
+        emit changeHint("Патч " + patchTitleFromName(name) + " не найден", "Чтобы закачать его перейдите на вкладку \"Патчи\", когда все загрузки будут завершены.");
+        return;
+    }
+
     qDebug() << QString("data/" + paths.first()).toLocal8Bit().data();
+    qInfo("%s:%i: %s%s", __FILE__, __LINE__, "Инициализируем базу данных: ", QString("data/" + paths.first()).toLocal8Bit().data());
 
-    if(!paths.empty()) {
-        qInfo("%s:%i: %s%s", __FILE__, __LINE__, "Инициализируем базу данных: ", QString("data/" + paths.first()).toLocal8Bit().data());
-        database_.InitDatabase(QString("data/" + paths.first()).toLocal8Bit().data());
+    if (!database_.InitDatabase(QString("data/" + paths.first()).toLocal8Bit().data())) {
+        emit changePatchStatus(name, "Ошибка патча!");
+        emit changeHint("Применение патча завершено", "Патч " + patchTitleFromName(name) + " НЕ установлен (ошибка чтения патча)");
+        return;
+    }
 
-        int indb = database_.CountRows();
-        qInfo("%s:%i: %s%d", __FILE__, __LINE__, "Файлов в обновлении: ", indb);
+    int indb = database_.CountRows();
+    qInfo("%s:%i: %s%d", __FILE__, __LINE__, "Файлов в обновлении: ", indb);
+
+    last_second_files_count = 0;
+    last_second_time = 0;
+    start_time = QDateTime::currentDateTime().toMSecsSinceEpoch();
+
+    for(int i = 0; i < indb; i++) {
+        qint64 current_timestamp = QDateTime::currentDateTime().toMSecsSinceEpoch();
 
-        for(int i = 0; i<=indb; i++){
-            processFile();
-            emit changePatchStatus(name, QString("Применение ... ")
-                                         + QString::fromLatin1("%1").arg(i*100.0/indb, 3, 'f', 1)
-                                         + QString("%"));
+        QString elapsed_time;
+        if (current_timestamp - start_time < 1000)
+            elapsed_time = " высчитывается...";
+        else
+            elapsed_time = app->helper->countFormattedElapsedTime(indb - i, double(i * 1000.0) / double(current_timestamp - start_time));
+
+        if (current_timestamp - last_second_time >= 1000) {
+            last_second_time = current_timestamp;
+            current_elapsed_time = elapsed_time;
+            last_second_files_count = i;
         }
 
-        emit changePatchStatus(name, "Готово");
+        processFile();
+        emit changePatchStatus(name, QString("Применение ... ")
+                                     + QString::fromLatin1("%1").arg(i*100.0/indb, 3, 'f', 1)
+                                     + QString("%"));
+        emit changeHint("Применение патча " + patchTitleFromName(name)
+                        , "Завершено " + QString::fromLatin1("%1").arg(i*100.0/indb, 3, 'f', 1)
+                        + "% Осталось примерно " + current_elapsed_time);
 
-        app->config->setValue("Applied", name, paths.first());
-        database_.CloseDatabase();
+    }
 
-        emit changeHint("Применение патча завершено", "Патч " + name + " успешно установлен");
+    emit changePatchStatus(name, "Готово");
+
+    app->config->setValue("Applied", name, paths.first());
+    database_.CloseDatabase();
+
+    emit changeHint("Применение патча завершено", "Патч " + patchTitleFromName(name) + " успешно установлен");
+}
+
+bool LotroManager::correctPatchName(QString patch_name) {
+    return patch_name == "texts" ||
+           patch_name == "fonts" ||
+           patch_name == "sounds" ||
+           patch_name == "images" ||
+           patch_name == "textures" ||
+           patch_name == "loadscreens" ||
+           patch_name == "videos";
+}
+
+void LotroManager::applyPatch(QStringList patches_list) {
+    for (QString patch : patches_list) {
+        if (correctPatchName(patch))
+            applyPatch(patch);
     }
 }
 
@@ -457,3 +528,21 @@ bool LotroManager::setGameLocale(QString locale){
 //void LotroManager::handleFinisheddd(){
 //   installMicroPatch();
 //}
+
+QString LotroManager::patchTitleFromName(QString name) {
+    if (name == "sounds")
+        return "\"Звуки\"";
+    if (name == "texts")
+        return "\"Тексты\"";
+    if (name == "fonts")
+        return "\"Шрифты\"";
+    if (name == "images")
+        return "\"Карты (1)\"";
+    if (name == "textures")
+        return "\"Карты (2)\"";
+    if (name == "videos")
+        return "\"Видеоролики\"";
+    if (name == "loadscreens")
+        return "\"Загрузочные экраны\"";
+    return "\"Неизвестный патч\"";
+}

+ 12 - 0
lotromanager.h

@@ -25,6 +25,8 @@ public:
 
     bool isBusy();
 
+    QString patchTitleFromName(QString name);
+
 private:
 
     void startGame();
@@ -33,6 +35,10 @@ private:
 
     void applyPatch(QString name);
 
+    void applyPatch(QStringList patches_list);
+
+    bool correctPatchName(QString patch_name);
+
     void applyGlobal();
 
     bool isDatReady();
@@ -58,6 +64,11 @@ private:
 private:
     bool busy_;
 
+    qint64 last_second_files_count;
+    qint64 last_second_time;
+    qint64 start_time;
+    QString current_elapsed_time;
+
     LOTRO_DAT::LOCALE dat_locale_;
     std::vector<LOTRO_DAT::DatFile> dat_files_;
     LOTRO_DAT::Database database_;
@@ -69,6 +80,7 @@ private:
 
         "ApplyGlobal",
         "ApplyPatch",
+        "ApplyPatchList",
         "ApplyMicroPatch",
         "ApplyLoadScreens",
 

+ 1 - 1
main.cpp

@@ -16,7 +16,7 @@
 
 void myMessageOutput(QtMsgType type, const QMessageLogContext &, const QString &msg)
 {
-    QByteArray localMsg = msg.toUtf8();
+    QByteArray localMsg = msg.toLocal8Bit().data();
     const char * format = "[" + QDate::currentDate().toString(Qt::ISODate).toUtf8() + " " + QTime::currentTime().toString(Qt::ISODate).toUtf8() + "]";
     switch (type) {
     case QtDebugMsg:

+ 75 - 25
mainwindow.cpp

@@ -74,33 +74,49 @@ MainWindow::MainWindow( double scale_factor, QMainWindow* parent) :
     connect(std::addressof(LotroManager::getInstance()),
             SIGNAL(dat_operation_finished(QString, QString, QString, QString)),
             this,
-            SLOT(on_lotro_manager_finished(QString, QString, QString, QString))
-            );
+            SLOT(on_lotro_manager_finished(QString, QString, QString, QString)));
 
     connect(std::addressof(LotroManager::getInstance()),
             SIGNAL(dat_operation_started(QString, QString, QString)),
             this,
-            SLOT(on_lotro_manager_started(QString, QString, QString))
-            );
+            SLOT(on_lotro_manager_started(QString, QString, QString)));
 
 
     connect(std::addressof(LotroManager::getInstance()),
             SIGNAL(changePatchStatus(QString, QString)),
             this,
-            SLOT(on_change_patch_status(QString, QString))
-            );
+            SLOT(on_change_patch_status(QString, QString)));
 
     connect(std::addressof(LotroManager::getInstance()),
             SIGNAL(changeHint(QString, QString)),
             this,
-            SLOT(on_change_hint(QString, QString))
-            );
+            SLOT(on_change_hint(QString, QString)));
 
     connect(app->network,
             SIGNAL(changePatchStatus(QString, QString)),
             this,
-            SLOT(on_change_patch_status(QString, QString))
-            );
+            SLOT(on_change_patch_status(QString, QString)));
+
+    connect(app->network,
+            SIGNAL(changeHint(QString,QString)),
+            this,
+            SLOT(on_change_hint(QString,QString)));
+
+
+    connect(app->downloader,
+            SIGNAL(changePatchStatus(QString, QString)),
+            this,
+            SLOT(on_change_patch_status(QString, QString)));
+
+    connect(app->downloader,
+            SIGNAL(changeHint(QString,QString)),
+            this,
+            SLOT(on_change_hint(QString,QString)));
+
+    connect(app->downloader,
+            SIGNAL(allDownloadsFinished(QStringList)),
+            this,
+            SLOT(on_patches_updated(QStringList)));
 
     app->helper->loadNews();
     app->network->getFootMessage();
@@ -153,6 +169,13 @@ void MainWindow::on_lotro_manager_finished(QString command, QString args, QStrin
     app->helper->setState("free");
 }
 
+void MainWindow::on_patches_updated(QStringList patches) {
+    App* app = &App::getInstance();
+    if (app->downloader->getDownloadedCount() == 0)
+        return;
+    LotroManager::getInstance().execute("ApplyPatchList", QString(patches.join('|')), "");
+}
+
 void MainWindow::on_change_patch_status(QString patch_name, QString new_status) {
     QLabel* obj = ui->mainbox->findChild<QLabel*>(patch_name + "Status");
     if (obj != nullptr){
@@ -170,7 +193,7 @@ void MainWindow::PostDatFileCheckTransactions(int check_result) {
     ui->exthintLabel->setText("Успешно!");
 
     App *app = &App::getInstance();
-    app->helper->setState("free");
+    //app->helper->setState("free");
 
     if (check_result == 1) {
         app->ready = false;
@@ -193,7 +216,7 @@ void MainWindow::PostDatFileCheckTransactions(int check_result) {
     }
 
     // Если это первый запуск то показываем мастер
-    if(app->config->getValue("Local", "runfirst") == "-1"){
+    if(app->config->getValue("Local", "runfirst") == "-1") {
         qInfo("%s:%i: %s", __FILE__, __LINE__, "Показываем мастер начальных настроек.");
         app->helper->setState("runfirst");
     } else {
@@ -204,7 +227,7 @@ void MainWindow::PostDatFileCheckTransactions(int check_result) {
 
 
     // Если приготовления выполнены загружаем патчи
-    if(app->state == "free" && app->ready == true){
+    if(app->state == "free" && app->ready == true) {
         app->logSectionStart("Загрузка патчей");
         app->network->UpdatePatches();
         qInfo("%s:%i: %s", __FILE__, __LINE__, "Выполнено.");
@@ -241,19 +264,28 @@ void MainWindow::mouseMoveEvent( QMouseEvent* e ) {
 }
 
 void MainWindow::mousePressEvent( QMouseEvent* e ) {
-    if( e->button() == Qt::LeftButton ) {
+    QPoint pt=mapFromGlobal(QCursor::pos());
+    QWidget* child=childAt(pt);
+
+    QString cname = child->metaObject()->className();
+    if(e->button() == Qt::LeftButton || (cname != "QPushButton" && cname != "QComboBox" && cname != "QLabel")) {
         dx = e->x();
         dy = e->y();
-        setCursor( Qt::OpenHandCursor );
+        setCursor(Qt::OpenHandCursor);
     }
 }
 
 void MainWindow::mouseReleaseEvent( QMouseEvent* e ) {
-    if( e->button() == Qt::LeftButton ) {
-        setCursor( Qt::ArrowCursor );
+    QPoint pt=mapFromGlobal(QCursor::pos());
+    QWidget* child=childAt(pt);
+
+    QString cname = child->metaObject()->className();
+    if(e->button() == Qt::LeftButton || (cname != "QPushButton" && cname != "QComboBox" && cname != "QLabel")) {
         dx = e->x();
         dy = e->y();
     }
+
+    setCursor( Qt::ArrowCursor);
 }
 
 void MainWindow::minimize() {
@@ -299,7 +331,10 @@ void MainWindow::on_mainButton_clicked(){
     App *app = &App::getInstance();
     app->helper->checkTab("mainbox");
     app->window->ui->titleLabel->setText("Патчи");
-    if (app->state != "busy") app->network->UpdatePatches();
+
+    if (app->state != "busy") {
+        QtConcurrent::run(app->network, &app->network->UpdatePatches);
+    }
 }
 
 void MainWindow::on_optButton_clicked(){
@@ -444,26 +479,43 @@ void MainWindow::saveSkin(){
 void MainWindow::on_checkFonts_stateChanged(int arg1){
     App *app = &App::getInstance();
     app->config->setValue("Editor", "fonts", arg1 ? "true" : "false");
+    if (!arg1) {
+       app->downloader->abortDownload("fonts");
+    }
 }
 
 void MainWindow::on_checkTexts_stateChanged(int arg1){
     App *app = &App::getInstance();
-    app->config->setValue("Editor", "texts", arg1 ? "true" : "false");;
+    app->config->setValue("Editor", "texts", arg1 ? "true" : "false");
+    if (!arg1) {
+       app->downloader->abortDownload("texts");
+    }
 }
 
 void MainWindow::on_checkSounds_stateChanged(int arg1){
     App *app = &App::getInstance();
     app->config->setValue("Editor", "sounds", arg1 ? "true" : "false");
+    if (!arg1) {
+       app->downloader->abortDownload("sounds");
+    }
 }
 
 void MainWindow::on_checkMaps_stateChanged(int arg1){
     App *app = &App::getInstance();
     app->config->setValue("Editor", "images", arg1 ? "true" : "false");
+    app->config->setValue("Editor", "textures", arg1 ? "true" : "false");
+    if (!arg1) {
+       app->downloader->abortDownload("images");
+       app->downloader->abortDownload("textures");
+    }
 }
 
 void MainWindow::on_checkScreens_stateChanged(int arg1){
     App *app = &App::getInstance();
-    app->config->setValue("Editor", "screens", arg1 ? "true" : "false");
+    app->config->setValue("Editor", "loadscreens", arg1 ? "true" : "false");
+    if (!arg1) {
+       app->downloader->abortDownload("loadscreens");
+    }
 }
 
 void MainWindow::on_checkVideos_stateChanged(int arg1){
@@ -510,9 +562,9 @@ void MainWindow::on_okButton_clicked(){
             this->ui->dialogbox->hide();
         }
 
-        if(command == "gamefind"){
+        if(command == "gamefind") {
             QStringList folders = app->config->getLotroPath();
-            if(folders.count() > 1){
+            if(folders.count() > 1) {
                 QString text = "Результаты поиска";
                 QString info = "Найдены следующие папки в которых установлена игра:<br/><br/><br/><br/><br/>Выберите нужную папку и нажмите «Далее»<br/>";
                 app->helper->myDialogBox(text, info, "Далее", "Отмена", "gandalf.png", "writefolder", "", 530, 200, true, false);
@@ -522,7 +574,6 @@ void MainWindow::on_okButton_clicked(){
 
                 foreach(QString f, folders) new QListWidgetItem(f, app->window->ui->dialogList);
                 app->window->ui->dialogList->show();
-
             } else {
                 if(folders.count() == 1){
                     app->window->ui->lotropathLabel->setText(folders.first());
@@ -567,13 +618,12 @@ void MainWindow::on_okButton_clicked(){
         if(command == "gotooptions"){
             app->helper->checkTab("optbox");
             app->config->setValue("Local", "runfirst", "1");
-            app->helper->setState("free");
         }
 
         if(command == "applypaths"){
             app->ready = true;
             app->config->deleteSection("Applied");
-            app->network->UpdatePatches();
+            LotroManager::getInstance().execute("ApplyGlobal", "", "");
             app->helper->checkTab("mainbox");
         }
 

+ 4 - 0
mainwindow.h

@@ -26,9 +26,13 @@ public:
 
 signals:
     void datFileChecked(int result);
+
 private slots:
     void on_lotro_manager_finished(QString command, QString args, QString name, QString result = "");
     void on_lotro_manager_started(QString command, QString args, QString name);
+
+    void on_patches_updated(QStringList patches);
+
     void on_change_patch_status(QString patch_name, QString new_status);
     void on_change_hint(QString title, QString hint);
 

+ 2 - 2
mainwindow.ui

@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>848</width>
-    <height>387</height>
+    <width>526</width>
+    <height>306</height>
    </rect>
   </property>
   <property name="windowTitle">

+ 1 - 1
ui_mainwindow.h

@@ -151,7 +151,7 @@ public:
     {
         if (MainWindow->objectName().isEmpty())
             MainWindow->setObjectName(QStringLiteral("MainWindow"));
-        MainWindow->resize(848, 387);
+        MainWindow->resize(526, 306);
         MainWindow->setWindowOpacity(1);
         MainWindow->setStyleSheet(QStringLiteral(""));
         centralWidget = new QWidget(MainWindow);