diff --git a/launcher/mainwindow_moc.cpp b/launcher/mainwindow_moc.cpp index ca00e192b..4a135f0a0 100644 --- a/launcher/mainwindow_moc.cpp +++ b/launcher/mainwindow_moc.cpp @@ -203,12 +203,6 @@ void MainWindow::on_startGameButton_clicked() switchToStartTab(); } -void MainWindow::on_startEditorButton_clicked() -{ - hide(); - startEditor({}); -} - CModListView * MainWindow::getModView() { return ui->modlistView; @@ -231,6 +225,67 @@ void MainWindow::on_aboutButton_clicked() ui->tabListWidget->setCurrentIndex(TabRows::ABOUT); } +void MainWindow::dragEnterEvent(QDragEnterEvent* event) +{ + if(event->mimeData()->hasUrls()) + for(const auto & url : event->mimeData()->urls()) + for(const auto & ending : QStringList({".zip", ".h3m", ".h3c", ".vmap", ".vcmp", ".json", ".exe"})) + if(url.fileName().endsWith(ending, Qt::CaseInsensitive)) + { + event->acceptProposedAction(); + return; + } +} + +void MainWindow::dropEvent(QDropEvent* event) +{ + const QMimeData* mimeData = event->mimeData(); + + if(mimeData->hasUrls()) + { + const QList urlList = mimeData->urls(); + for (const auto & url : urlList) + manualInstallFile(url.toLocalFile()); + } +} + +void MainWindow::manualInstallFile(QString filePath) +{ + QString fileName = QFileInfo{filePath}.fileName(); + if(filePath.endsWith(".zip", Qt::CaseInsensitive)) + getModView()->downloadFile(fileName.toLower() + // mod name currently comes from zip file -> remove suffixes from github zip download + .replace(QRegularExpression("-[0-9a-f]{40}"), "") + .replace(QRegularExpression("-vcmi-.+\\.zip"), ".zip") + .replace("-main.zip", ".zip") + , QUrl::fromLocalFile(filePath), "mods"); + else if(filePath.endsWith(".json", Qt::CaseInsensitive)) + { + QDir configDir(QString::fromStdString(VCMIDirs::get().userConfigPath().string())); + QStringList configFile = configDir.entryList({fileName}, QDir::Filter::Files); // case insensitive check + if(!configFile.empty()) + { + auto dialogResult = QMessageBox::warning(this, tr("Replace config file?"), tr("Do you want to replace %1?").arg(configFile[0]), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + if(dialogResult == QMessageBox::Yes) + { + const auto configFilePath = configDir.filePath(configFile[0]); + QFile::remove(configFilePath); + QFile::copy(filePath, configFilePath); + + // reload settings + Helper::loadSettings(); + for(const auto widget : qApp->allWidgets()) + if(auto settingsView = qobject_cast(widget)) + settingsView->loadSettings(); + + getModView()->reload(); + } + } + } + else + getModView()->downloadFile(fileName, QUrl::fromLocalFile(filePath), fileName); +} + void MainWindow::updateTranslation() { #ifdef ENABLE_QT_TRANSLATIONS diff --git a/launcher/mainwindow_moc.h b/launcher/mainwindow_moc.h index 52518970b..9a61fb5cd 100644 --- a/launcher/mainwindow_moc.h +++ b/launcher/mainwindow_moc.h @@ -58,6 +58,10 @@ public: void switchToModsTab(); void switchToStartTab(); + void dragEnterEvent(QDragEnterEvent* event) override; + void dropEvent(QDropEvent *event) override; + void manualInstallFile(QString filePath); + protected: void changeEvent(QEvent * event) override; @@ -67,6 +71,5 @@ public slots: private slots: void on_modslistButton_clicked(); void on_settingsButton_clicked(); - void on_startEditorButton_clicked(); void on_aboutButton_clicked(); }; diff --git a/launcher/mainwindow_moc.ui b/launcher/mainwindow_moc.ui index 0e13bd0b7..fb91bf373 100644 --- a/launcher/mainwindow_moc.ui +++ b/launcher/mainwindow_moc.ui @@ -62,7 +62,7 @@ true - true + false true @@ -218,7 +218,7 @@ true - false + true true diff --git a/launcher/modManager/cmodlistview_moc.cpp b/launcher/modManager/cmodlistview_moc.cpp index 64bc7b99f..e8a89eccb 100644 --- a/launcher/modManager/cmodlistview_moc.cpp +++ b/launcher/modManager/cmodlistview_moc.cpp @@ -59,30 +59,6 @@ void CModListView::changeEvent(QEvent *event) QWidget::changeEvent(event); } -void CModListView::dragEnterEvent(QDragEnterEvent* event) -{ - if(event->mimeData()->hasUrls()) - for(const auto & url : event->mimeData()->urls()) - for(const auto & ending : QStringList({".zip", ".h3m", ".h3c", ".vmap", ".vcmp", ".json", ".exe"})) - if(url.fileName().endsWith(ending, Qt::CaseInsensitive)) - { - event->acceptProposedAction(); - return; - } -} - -void CModListView::dropEvent(QDropEvent* event) -{ - const QMimeData* mimeData = event->mimeData(); - - if(mimeData->hasUrls()) - { - const QList urlList = mimeData->urls(); - for (const auto & url : urlList) - manualInstallFile(url.toLocalFile()); - } -} - void CModListView::setupFilterModel() { filterModel = new CModFilterModel(modModel, this); @@ -169,6 +145,12 @@ CModListView::CModListView(QWidget * parent) #endif } +void CModListView::reload() +{ + modStateModel->reloadLocalState(); + modModel->reloadRepositories(); +} + void CModListView::loadRepositories() { QStringList repositories; @@ -642,73 +624,6 @@ void CModListView::on_installButton_clicked() } } -void CModListView::on_installFromFileButton_clicked() -{ - // iOS can't display modal dialogs when called directly on button press - // https://bugreports.qt.io/browse/QTBUG-98651 - QTimer::singleShot(0, this, [this] - { - QString filter = tr("All supported files") + " (*.h3m *.vmap *.h3c *.vcmp *.zip *.json *.exe);;" + - tr("Maps") + " (*.h3m *.vmap);;" + - tr("Campaigns") + " (*.h3c *.vcmp);;" + - tr("Configs") + " (*.json);;" + - tr("Mods") + " (*.zip);;" + - tr("Gog files") + " (*.exe)"; -#if defined(VCMI_MOBILE) - filter = tr("All files (*.*)"); //Workaround for sometimes incorrect mime for some extensions (e.g. for exe) -#endif - QStringList files = QFileDialog::getOpenFileNames(this, tr("Select files (configs, mods, maps, campaigns, gog files) to install..."), QDir::homePath(), filter); - - for(const auto & file : files) - { - manualInstallFile(file); - } - }); -} - -void CModListView::manualInstallFile(QString filePath) -{ - QString fileName = QFileInfo{filePath}.fileName(); - if(filePath.endsWith(".zip", Qt::CaseInsensitive)) - downloadFile(fileName.toLower() - // mod name currently comes from zip file -> remove suffixes from github zip download - .replace(QRegularExpression("-[0-9a-f]{40}"), "") - .replace(QRegularExpression("-vcmi-.+\\.zip"), ".zip") - .replace("-main.zip", ".zip") - , QUrl::fromLocalFile(filePath), "mods"); - else if(filePath.endsWith(".json", Qt::CaseInsensitive)) - { - QDir configDir(QString::fromStdString(VCMIDirs::get().userConfigPath().string())); - QStringList configFile = configDir.entryList({fileName}, QDir::Filter::Files); // case insensitive check - if(!configFile.empty()) - { - auto dialogResult = QMessageBox::warning(this, tr("Replace config file?"), tr("Do you want to replace %1?").arg(configFile[0]), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - if(dialogResult == QMessageBox::Yes) - { - const auto configFilePath = configDir.filePath(configFile[0]); - QFile::remove(configFilePath); - QFile::copy(filePath, configFilePath); - - // reload settings - Helper::loadSettings(); - for(const auto widget : qApp->allWidgets()) - if(auto settingsView = qobject_cast(widget)) - settingsView->loadSettings(); - - modStateModel->reloadLocalState(); - modModel->reloadRepositories(); - } - } - } - else - downloadFile(fileName, QUrl::fromLocalFile(filePath), fileName); -} - -void CModListView::downloadFile(QString file, QString url, QString description, qint64 sizeBytes) -{ - downloadFile(file, QUrl{url}, description, sizeBytes); -} - void CModListView::downloadFile(QString file, QUrl url, QString description, qint64 sizeBytes) { if(!dlManager) diff --git a/launcher/modManager/cmodlistview_moc.h b/launcher/modManager/cmodlistview_moc.h index c958c2aff..5fc52a50b 100644 --- a/launcher/modManager/cmodlistview_moc.h +++ b/launcher/modManager/cmodlistview_moc.h @@ -52,9 +52,6 @@ class CModListView : public QWidget // find mods unknown to mod list (not present in repo and not installed) QStringList findUnavailableMods(QStringList candidates); - void manualInstallFile(QString filePath); - void downloadFile(QString file, QString url, QString description, qint64 sizeBytes = 0); - void downloadFile(QString file, QUrl url, QString description, qint64 sizeBytes = 0); void installMods(QStringList archives); void installMaps(QStringList maps); @@ -64,8 +61,6 @@ class CModListView : public QWidget QString genModInfoText(const ModState & mod); void changeEvent(QEvent *event) override; - void dragEnterEvent(QDragEnterEvent* event) override; - void dropEvent(QDropEvent *event) override; public: explicit CModListView(QWidget * parent = nullptr); @@ -74,6 +69,8 @@ public: void loadScreenshots(); void loadRepositories(); + void reload(); + void disableModInfo(); void selectMod(const QModelIndex & index); @@ -95,6 +92,8 @@ public: /// returns true if mod is currently installed bool isModInstalled(const QString & modName); + void downloadFile(QString file, QUrl url, QString description, qint64 sizeBytes = 0); + public slots: void enableModByName(QString modName); void disableModByName(QString modName); @@ -109,31 +108,17 @@ private slots: void hideProgressBar(); void on_lineEdit_textChanged(const QString & arg1); - void on_comboBox_currentIndexChanged(int index); - void on_enableButton_clicked(); - void on_disableButton_clicked(); - void on_updateButton_clicked(); - void on_uninstallButton_clicked(); - void on_installButton_clicked(); - - void on_installFromFileButton_clicked(); - void on_pushButton_clicked(); - void on_refreshButton_clicked(); - void on_allModsView_activated(const QModelIndex & index); - void on_tabWidget_currentChanged(int index); - void on_screenshotsList_clicked(const QModelIndex & index); - void on_allModsView_doubleClicked(const QModelIndex &index); private: diff --git a/launcher/modManager/cmodlistview_moc.ui b/launcher/modManager/cmodlistview_moc.ui index dd50b6929..2257ba30f 100644 --- a/launcher/modManager/cmodlistview_moc.ui +++ b/launcher/modManager/cmodlistview_moc.ui @@ -191,7 +191,9 @@ <html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } hr { height: 1px; border-width: 0; } -</style></head><body style=" font-family:'.AppleSystemUIFont'; font-size:13pt; font-weight:400; font-style:normal;"> +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:11pt;"><br /></p></body></html> @@ -349,41 +351,6 @@ hr { height: 1px; border-width: 0; } 6 - - - - - 0 - 0 - - - - - 51 - 0 - - - - - 170 - 16777215 - - - - Install from file - - - - icons:mod-download.pngicons:mod-download.png - - - - 20 - 20 - - - - diff --git a/launcher/startGame/StartGameTab.cpp b/launcher/startGame/StartGameTab.cpp index 53055ae3a..b14b2174a 100644 --- a/launcher/startGame/StartGameTab.cpp +++ b/launcher/startGame/StartGameTab.cpp @@ -4,7 +4,10 @@ #include "../mainwindow_moc.h" #include "../main.h" +#include "../modManager/cmodlistview_moc.h" + #include "../../lib/filesystem/Filesystem.h" +#include "../../lib/VCMIDirs.h" StartGameTab::StartGameTab(QWidget * parent) : QWidget(parent) @@ -13,6 +16,8 @@ StartGameTab::StartGameTab(QWidget * parent) ui->setupUi(this); refreshState(); + + ui->buttonGameResume->setVisible(false); // TODO: implement } StartGameTab::~StartGameTab() @@ -29,6 +34,14 @@ MainWindow * StartGameTab::getMainWindow() } void StartGameTab::refreshState() +{ + refreshGameData(); + refreshUpdateStatus(EGameUpdateStatus::NOT_CHECKED);//TODO + refreshTranslation(ETranslationStatus::ACTIVE); + refreshMods(); +} + +void StartGameTab::refreshGameData() { // Some players are using pirated version of the game with some of the files missing // leading to broken town hall menu (and possibly other dialogs) @@ -55,9 +68,6 @@ void StartGameTab::refreshState() "DATA/FOOL", }; - bool updateAvailable = false; - bool checkedForUpdate = false; - bool missingSoundtrack = !CResourceHandler::get()->existsResource(AudioPath::builtin("Music/MainMenu")); bool missingVideoFiles = !CResourceHandler::get()->existsResource(VideoPath::builtin("Video/H3Intro")); bool missingGameFiles = false; @@ -69,13 +79,6 @@ void StartGameTab::refreshState() for (const auto & filename : armaggedonBladeCampaigns) missingCampaings &= !CResourceHandler::get()->existsResource(ResourcePath(filename, EResType::CAMPAIGN)); - ui->buttonEngine->setText("VCMI " VCMI_VERSION_STRING); - ui->buttonUpdateCheck->setVisible(!checkedForUpdate); - ui->labelUpdateAvailable->setVisible(checkedForUpdate && updateAvailable); - ui->labelUpdateNotFound->setVisible(checkedForUpdate && !updateAvailable); - ui->buttonOpenChangelog->setVisible(checkedForUpdate && updateAvailable); - ui->buttonOpenDownloads->setVisible(checkedForUpdate && updateAvailable); - ui->labelMissingCampaigns->setVisible(missingCampaings); ui->labelMissingFiles->setVisible(missingGameFiles); ui->labelMissingVideo->setVisible(missingVideoFiles); @@ -85,6 +88,35 @@ void StartGameTab::refreshState() ui->buttonMissingFilesHelp->setVisible(missingGameFiles); ui->buttonMissingVideoHelp->setVisible(missingVideoFiles); ui->buttonMissingSoundtrackHelp->setVisible(missingSoundtrack); + + // TODO: Chronicles +} + +void StartGameTab::refreshTranslation(ETranslationStatus status) +{ + ui->buttonInstallTranslation->setVisible(status == ETranslationStatus::NOT_INSTALLLED); + ui->buttonInstallTranslationHelp->setVisible(status == ETranslationStatus::NOT_INSTALLLED); + + ui->buttonActivateTranslation->setVisible(status == ETranslationStatus::NOT_INSTALLLED); + ui->buttonActivateTranslationHelp->setVisible(status == ETranslationStatus::NOT_INSTALLLED); +} + +void StartGameTab::refreshMods() +{ + QStringList updateableMods; + + ui->buttonUpdateMods->setVisible(!updateableMods.empty()); + ui->buttonUpdateModsHelp->setVisible(!updateableMods.empty()); +} + +void StartGameTab::refreshUpdateStatus(EGameUpdateStatus status) +{ + ui->buttonEngine->setText("VCMI " VCMI_VERSION_STRING); + ui->buttonUpdateCheck->setVisible(status == EGameUpdateStatus::NOT_CHECKED); + ui->labelUpdateNotFound->setVisible(status == EGameUpdateStatus::NO_UPDATE); + ui->labelUpdateAvailable->setVisible(status == EGameUpdateStatus::UPDATE_AVAILABLE); + ui->buttonOpenChangelog->setVisible(status == EGameUpdateStatus::UPDATE_AVAILABLE); + ui->buttonOpenDownloads->setVisible(status == EGameUpdateStatus::UPDATE_AVAILABLE); } void StartGameTab::on_buttonGameStart_clicked() @@ -93,7 +125,6 @@ void StartGameTab::on_buttonGameStart_clicked() startGame({}); } - void StartGameTab::on_buttonOpenChangelog_clicked() { QDesktopServices::openUrl(QUrl("https://vcmi.eu/ChangeLog/")); @@ -111,3 +142,40 @@ void StartGameTab::on_buttonUpdateCheck_clicked() // TODO: implement } + +void StartGameTab::on_buttonGameEditor_clicked() +{ + getMainWindow()->hide(); + startEditor({}); +} + + +void StartGameTab::on_buttonImportFiles_clicked() +{ + const auto & importFunctor = [this] + { +#ifndef VCMI_MOBILE + QString filter = + tr("All supported files") + " (*.h3m *.vmap *.h3c *.vcmp *.zip *.json *.exe);;" + + tr("Maps") + " (*.h3m *.vmap);;" + + tr("Campaigns") + " (*.h3c *.vcmp);;" + + tr("Configs") + " (*.json);;" + + tr("Mods") + " (*.zip);;" + + tr("Gog files") + " (*.exe)"; +#else + //Workaround for sometimes incorrect mime for some extensions (e.g. for exe) + QString filter = tr("All files (*.*)"); +#endif + QStringList files = QFileDialog::getOpenFileNames(this, tr("Select files (configs, mods, maps, campaigns, gog files) to install..."), QDir::homePath(), filter); + + for(const auto & file : files) + getMainWindow()->manualInstallFile(file); + }; + + // iOS can't display modal dialogs when called directly on button press + // https://bugreports.qt.io/browse/QTBUG-98651 + QTimer::singleShot(0, this, importFunctor); +} + + + diff --git a/launcher/startGame/StartGameTab.h b/launcher/startGame/StartGameTab.h index 5c49599ef..cac8e7e41 100644 --- a/launcher/startGame/StartGameTab.h +++ b/launcher/startGame/StartGameTab.h @@ -8,6 +8,21 @@ namespace Ui class StartGameTab; } +enum class EGameUpdateStatus : int8_t +{ + NOT_CHECKED, + NO_UPDATE, + UPDATE_AVAILABLE +}; + +enum ETranslationStatus +{ + NOT_AVAILABLE, // translation for this language was not found in mod list. Could also happen if player is offline or disabled repository checkout + NOT_INSTALLLED, // translation mod found, but it is not installed + DISABLED, // translation mod found, and installed, but toggled off + ACTIVE // translation mod active OR game is already in specified language (e.g. English H3 for players with English language) +}; + class MainWindow; class StartGameTab : public QWidget @@ -17,6 +32,12 @@ class StartGameTab : public QWidget MainWindow * getMainWindow(); void refreshState(); + + void refreshUpdateStatus(EGameUpdateStatus status); + void refreshTranslation(ETranslationStatus status); + void refreshMods(); + void refreshGameData(); + public: explicit StartGameTab(QWidget * parent = nullptr); ~StartGameTab(); @@ -30,6 +51,10 @@ private slots: void on_buttonUpdateCheck_clicked(); + void on_buttonGameEditor_clicked(); + + void on_buttonImportFiles_clicked(); + private: Ui::StartGameTab * ui; }; diff --git a/launcher/startGame/StartGameTab.ui b/launcher/startGame/StartGameTab.ui index 1e5273a43..d07574af2 100644 --- a/launcher/startGame/StartGameTab.ui +++ b/launcher/startGame/StartGameTab.ui @@ -31,7 +31,7 @@ - Preset %1 + Preset: %1