mainwindow_moc.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. /*
  2. * mainwindow_moc.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "mainwindow_moc.h"
  12. #include "ui_mainwindow_moc.h"
  13. #include <QDir>
  14. #include "../lib/CConfigHandler.h"
  15. #include "../lib/VCMIDirs.h"
  16. #include "../lib/filesystem/Filesystem.h"
  17. #include "../lib/logging/CBasicLogConfigurator.h"
  18. #include "../lib/texts/Languages.h"
  19. #include "../lib/ExceptionsCommon.h"
  20. #include "updatedialog_moc.h"
  21. #include "main.h"
  22. #include "helper.h"
  23. void MainWindow::load()
  24. {
  25. // Set current working dir to executable folder.
  26. // This is important on Mac for relative paths to work inside DMG.
  27. QDir::setCurrent(QApplication::applicationDirPath());
  28. #ifndef VCMI_MOBILE
  29. console = std::make_unique<CConsoleHandler>();
  30. CBasicLogConfigurator logConfig(VCMIDirs::get().userLogsPath() / "VCMI_Launcher_log.txt", console.get());
  31. #else
  32. CBasicLogConfigurator logConfig(VCMIDirs::get().userLogsPath() / "VCMI_Launcher_log.txt", nullptr);
  33. #endif
  34. logConfig.configureDefault();
  35. try
  36. {
  37. CResourceHandler::initialize();
  38. CResourceHandler::load("config/filesystem.json");
  39. }
  40. catch (const DataLoadingException & e)
  41. {
  42. QMessageBox::critical(this, tr("Error starting executable"), QString::fromStdString(e.what()));
  43. }
  44. Helper::loadSettings();
  45. }
  46. void MainWindow::computeSidePanelSizes()
  47. {
  48. QVector<QToolButton*> widgets = {
  49. ui->modslistButton,
  50. ui->settingsButton,
  51. ui->aboutButton,
  52. ui->startGameButton
  53. };
  54. for(auto & widget : widgets)
  55. {
  56. QFontMetrics metrics(widget->font());
  57. QSize iconSize = widget->iconSize();
  58. // this is minimal space that is needed for our button to avoid text clipping
  59. int buttonHeight = iconSize.height() + metrics.height() + 4;
  60. widget->setMinimumHeight(buttonHeight);
  61. widget->setMaximumHeight(buttonHeight * 1.2);
  62. }
  63. }
  64. MainWindow::MainWindow(QWidget * parent)
  65. : QMainWindow(parent), ui(new Ui::MainWindow)
  66. {
  67. load(); // load FS before UI
  68. bool setupCompleted = settings["launcher"]["setupCompleted"].Bool();
  69. if (!setupCompleted)
  70. detectPreferredLanguage();
  71. updateTranslation(); // load translation
  72. ui->setupUi(this);
  73. setAcceptDrops(true);
  74. setWindowIcon(QIcon{":/icons/menu-game.png"});
  75. ui->modslistButton->setIcon(QIcon{":/icons/menu-mods.png"});
  76. ui->settingsButton->setIcon(QIcon{":/icons/menu-settings.png"});
  77. ui->aboutButton->setIcon(QIcon{":/icons/about-project.png"});
  78. ui->startGameButton->setIcon(QIcon{":/icons/menu-game.png"});
  79. #ifndef VCMI_MOBILE
  80. //load window settings
  81. QSettings s(Ui::teamName, Ui::appName);
  82. auto size = s.value("MainWindow/Size").toSize();
  83. if(size.isValid())
  84. {
  85. resize(size);
  86. }
  87. auto position = s.value("MainWindow/Position").toPoint();
  88. if(!position.isNull())
  89. {
  90. move(position);
  91. }
  92. #endif
  93. computeSidePanelSizes();
  94. bool h3DataFound = CResourceHandler::get()->existsResource(ResourcePath("DATA/GENRLTXT.TXT"));
  95. if (h3DataFound && setupCompleted)
  96. ui->tabListWidget->setCurrentIndex(TabRows::START);
  97. else
  98. enterSetup();
  99. ui->settingsView->setDisplayList();
  100. if(settings["launcher"]["updateOnStartup"].Bool())
  101. UpdateDialog::showUpdateDialog(false);
  102. }
  103. void MainWindow::detectPreferredLanguage()
  104. {
  105. auto preferredLanguages = QLocale::system().uiLanguages();
  106. std::string selectedLanguage;
  107. for (auto const & userLang : preferredLanguages)
  108. {
  109. logGlobal->info("Preferred language: %s", userLang.toStdString());
  110. for (auto const & vcmiLang : Languages::getLanguageList())
  111. if (vcmiLang.tagIETF == userLang.toStdString() && vcmiLang.selectable)
  112. selectedLanguage = vcmiLang.identifier;
  113. if (!selectedLanguage.empty())
  114. {
  115. logGlobal->info("Selected language: %s", selectedLanguage);
  116. Settings node = settings.write["general"]["language"];
  117. node->String() = selectedLanguage;
  118. return;
  119. }
  120. }
  121. }
  122. void MainWindow::enterSetup()
  123. {
  124. ui->startGameButton->setEnabled(false);
  125. ui->settingsButton->setEnabled(false);
  126. ui->aboutButton->setEnabled(false);
  127. ui->modslistButton->setEnabled(false);
  128. ui->tabListWidget->setCurrentIndex(TabRows::SETUP);
  129. }
  130. void MainWindow::exitSetup(bool goToMods)
  131. {
  132. Settings writer = settings.write["launcher"]["setupCompleted"];
  133. writer->Bool() = true;
  134. ui->startGameButton->setEnabled(true);
  135. ui->settingsButton->setEnabled(true);
  136. ui->aboutButton->setEnabled(true);
  137. ui->modslistButton->setEnabled(true);
  138. if (goToMods)
  139. ui->tabListWidget->setCurrentIndex(TabRows::MODS);
  140. else
  141. ui->tabListWidget->setCurrentIndex(TabRows::START);
  142. }
  143. void MainWindow::switchToStartTab()
  144. {
  145. ui->startGameButton->setEnabled(true);
  146. ui->startGameButton->setChecked(true);
  147. ui->tabListWidget->setCurrentIndex(TabRows::START);
  148. auto* startGameTabWidget = qobject_cast<StartGameTab*>(ui->tabListWidget->widget(TabRows::START));
  149. if(startGameTabWidget)
  150. startGameTabWidget->refreshState();
  151. }
  152. void MainWindow::switchToModsTab()
  153. {
  154. ui->startGameButton->setEnabled(true);
  155. ui->modslistButton->setChecked(true);
  156. ui->tabListWidget->setCurrentIndex(TabRows::MODS);
  157. }
  158. void MainWindow::changeEvent(QEvent * event)
  159. {
  160. if(event->type() == QEvent::LanguageChange)
  161. {
  162. ui->retranslateUi(this);
  163. }
  164. QMainWindow::changeEvent(event);
  165. }
  166. MainWindow::~MainWindow()
  167. {
  168. #ifndef VCMI_MOBILE
  169. //save window settings
  170. QSettings s(Ui::teamName, Ui::appName);
  171. s.setValue("MainWindow/Size", size());
  172. s.setValue("MainWindow/Position", pos());
  173. #endif
  174. delete ui;
  175. }
  176. void MainWindow::on_startGameButton_clicked()
  177. {
  178. switchToStartTab();
  179. }
  180. CModListView * MainWindow::getModView()
  181. {
  182. return ui->modlistView;
  183. }
  184. void MainWindow::on_modslistButton_clicked()
  185. {
  186. switchToModsTab();
  187. }
  188. void MainWindow::on_settingsButton_clicked()
  189. {
  190. ui->startGameButton->setEnabled(true);
  191. ui->tabListWidget->setCurrentIndex(TabRows::SETTINGS);
  192. }
  193. void MainWindow::on_aboutButton_clicked()
  194. {
  195. ui->startGameButton->setEnabled(true);
  196. ui->tabListWidget->setCurrentIndex(TabRows::ABOUT);
  197. }
  198. void MainWindow::dragEnterEvent(QDragEnterEvent* event)
  199. {
  200. if(event->mimeData()->hasUrls())
  201. for(const auto & url : event->mimeData()->urls())
  202. for(const auto & ending : QStringList({".zip", ".h3m", ".h3c", ".vmap", ".vcmp", ".json", ".exe"}))
  203. if(url.fileName().endsWith(ending, Qt::CaseInsensitive))
  204. {
  205. event->acceptProposedAction();
  206. return;
  207. }
  208. }
  209. void MainWindow::dropEvent(QDropEvent* event)
  210. {
  211. const QMimeData* mimeData = event->mimeData();
  212. if(mimeData->hasUrls())
  213. {
  214. const QList<QUrl> urlList = mimeData->urls();
  215. for (const auto & url : urlList)
  216. manualInstallFile(url.toLocalFile());
  217. }
  218. }
  219. void MainWindow::manualInstallFile(QString filePath)
  220. {
  221. QString realFilePath = Helper::getRealPath(filePath);
  222. if(realFilePath.endsWith(".zip", Qt::CaseInsensitive) || realFilePath.endsWith(".exe", Qt::CaseInsensitive))
  223. switchToModsTab();
  224. QString fileName = QFileInfo{filePath}.fileName();
  225. if(realFilePath.endsWith(".zip", Qt::CaseInsensitive))
  226. {
  227. QString filenameClean = fileName.toLower()
  228. // mod name currently comes from zip file -> remove suffixes from github zip download
  229. .replace(QRegularExpression("-[0-9a-f]{40}"), "")
  230. .replace(QRegularExpression("-vcmi-.+\\.zip"), ".zip")
  231. .replace("-main.zip", ".zip");
  232. getModView()->downloadFile(filenameClean, QUrl::fromLocalFile(filePath), "mods");
  233. }
  234. else if(realFilePath.endsWith(".json", Qt::CaseInsensitive))
  235. {
  236. QDir configDir(QString::fromStdString(VCMIDirs::get().userConfigPath().string()));
  237. QStringList configFile = configDir.entryList({fileName}, QDir::Filter::Files); // case insensitive check
  238. if(!configFile.empty())
  239. {
  240. 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);
  241. if(dialogResult == QMessageBox::Yes)
  242. {
  243. const auto configFilePath = configDir.filePath(configFile[0]);
  244. QFile::remove(configFilePath);
  245. QFile::copy(filePath, configFilePath);
  246. // reload settings
  247. Helper::loadSettings();
  248. for(const auto widget : qApp->allWidgets())
  249. if(auto settingsView = qobject_cast<CSettingsView *>(widget))
  250. settingsView->loadSettings();
  251. getModView()->reload();
  252. }
  253. }
  254. }
  255. else
  256. getModView()->installFiles(QStringList{filePath});
  257. }
  258. ETranslationStatus MainWindow::getTranslationStatus()
  259. {
  260. QString preferredlanguage = QString::fromStdString(settings["general"]["language"].String());
  261. QString installedlanguage = QString::fromStdString(settings["session"]["language"].String());
  262. if (preferredlanguage == installedlanguage)
  263. return ETranslationStatus::ACTIVE;
  264. QString modName = getModView()->getTranslationModName(preferredlanguage);
  265. if (modName.isEmpty())
  266. return ETranslationStatus::NOT_AVAILABLE;
  267. if (!getModView()->isModInstalled(modName))
  268. return ETranslationStatus::NOT_INSTALLLED;
  269. if (!getModView()->isModEnabled(modName))
  270. return ETranslationStatus::DISABLED;
  271. return ETranslationStatus::ACTIVE;
  272. }
  273. void MainWindow::updateTranslation()
  274. {
  275. #ifdef ENABLE_QT_TRANSLATIONS
  276. const std::string translationFile = settings["general"]["language"].String()+ ".qm";
  277. QString translationFileResourcePath = QString{":/translation/%1"}.arg(translationFile.c_str());
  278. logGlobal->info("Loading translation %s", translationFile);
  279. if(!QFile::exists(translationFileResourcePath))
  280. {
  281. logGlobal->debug("Translation file %s does not exist", translationFileResourcePath.toStdString());
  282. return;
  283. }
  284. if (!translator.load(translationFileResourcePath))
  285. {
  286. logGlobal->error("Failed to load translation file %s", translationFileResourcePath.toStdString());
  287. return;
  288. }
  289. if(translationFile == "english.qm")
  290. {
  291. // translator doesn't need to be installed for English
  292. return;
  293. }
  294. if (!qApp->installTranslator(&translator))
  295. {
  296. logGlobal->error("Failed to install translator for translation file %s", translationFileResourcePath.toStdString());
  297. }
  298. #endif
  299. }