From 412855ae3d967ff81a383688397c4d9448a4ee15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 30 Oct 2016 02:37:38 +0100 Subject: NOISSUE refactor window management and launch, make MultiMC a single instance application. --- application/CMakeLists.txt | 2 +- application/InstanceWindow.cpp | 11 +-- application/InstanceWindow.h | 7 -- application/LaunchController.cpp | 17 +---- application/MainWindow.cpp | 88 +++------------------- application/MainWindow.h | 10 --- application/MultiMC.cpp | 159 +++++++++++++++++++++++++++++++++++++-- application/MultiMC.h | 57 ++++++++------ application/main.cpp | 37 +-------- 9 files changed, 204 insertions(+), 184 deletions(-) (limited to 'application') diff --git a/application/CMakeLists.txt b/application/CMakeLists.txt index 59a51ce7..4a7d644d 100644 --- a/application/CMakeLists.txt +++ b/application/CMakeLists.txt @@ -322,7 +322,7 @@ qt5_add_resources(MULTIMC_RESOURCES ${MULTIMC_QRCS}) # Add executable add_executable(MultiMC MACOSX_BUNDLE WIN32 ${MULTIMC_SOURCES} ${MULTIMC_UI} ${MULTIMC_RESOURCES} ${MULTIMC_RCS}) -target_link_libraries(MultiMC MultiMC_gui ${QUAZIP_LIBRARIES} hoedown rainbow) +target_link_libraries(MultiMC MultiMC_gui ${QUAZIP_LIBRARIES} hoedown rainbow LocalPeer) if(APPLE) find_library(OSX_CORE_FOUNDATION CoreFoundation) diff --git a/application/InstanceWindow.cpp b/application/InstanceWindow.cpp index 7b3e1d10..1c7731bd 100644 --- a/application/InstanceWindow.cpp +++ b/application/InstanceWindow.cpp @@ -174,11 +174,6 @@ void InstanceWindow::closeEvent(QCloseEvent *event) MMC->settings()->set("ConsoleWindowGeometry", saveGeometry().toBase64()); emit isClosing(); event->accept(); - if(m_shouldQuit) - { - // this needs to be delayed so we don't do horrible things - QMetaObject::invokeMethod(MMC, "quit", Qt::QueuedConnection); - } } bool InstanceWindow::saveAll() @@ -203,11 +198,7 @@ void InstanceWindow::on_btnKillMinecraft_clicked() // FIXME: duplicate logic between MainWindow and InstanceWindow else if(saveAll()) { - m_launchController.reset(new LaunchController()); - m_launchController->setInstance(m_instance); - m_launchController->setOnline(true); - m_launchController->setParentWidget(this); - m_launchController->start(); + MMC->launch(m_instance, true, nullptr); } } diff --git a/application/InstanceWindow.h b/application/InstanceWindow.h index 1a6bc8c3..5cbced03 100644 --- a/application/InstanceWindow.h +++ b/application/InstanceWindow.h @@ -36,11 +36,6 @@ public: QString instanceId(); - void setQuitOnClose(bool shouldQuit = false) - { - m_shouldQuit = shouldQuit; - } - // save all settings and changes (prepare for launch) bool saveAll(); @@ -68,9 +63,7 @@ private: private: std::shared_ptr m_proc; - unique_qobject_ptr m_launchController; InstancePtr m_instance; - bool m_shouldQuit = false; bool m_doNotSave = false; PageContainer *m_container = nullptr; QPushButton *m_closeButton = nullptr; diff --git a/application/LaunchController.cpp b/application/LaunchController.cpp index bc7f976a..4dce6708 100644 --- a/application/LaunchController.cpp +++ b/application/LaunchController.cpp @@ -205,21 +205,10 @@ void LaunchController::launchInstance() return; } - auto mainWindow = qobject_cast(m_parentWidget); - auto instanceWindow = qobject_cast(m_parentWidget); - if(mainWindow) + auto console = qobject_cast(m_parentWidget); + if(!console) { - m_console = mainWindow->showInstanceWindow(m_instance); - } - else if(instanceWindow) - { - // NOOP - } - else - { - // this is used when launching directly from command line - m_console = new InstanceWindow(m_instance); - m_console->setQuitOnClose(true); + MMC->showInstanceWindow(m_instance); } connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch); diff --git a/application/MainWindow.cpp b/application/MainWindow.cpp index e07686dd..85484700 100644 --- a/application/MainWindow.cpp +++ b/application/MainWindow.cpp @@ -691,7 +691,7 @@ void MainWindow::updateToolsMenu() QAction *normalLaunch = launchMenu->addAction(tr("Launch")); connect(normalLaunch, &QAction::triggered, [this]() { - launch(m_selectedInstance); + MMC->launch(m_selectedInstance); }); launchMenu->addSeparator()->setText(tr("Profilers")); for (auto profiler : MMC->profilers().values()) @@ -707,7 +707,7 @@ void MainWindow::updateToolsMenu() { connect(profilerAction, &QAction::triggered, [this, profiler]() { - launch(m_selectedInstance, true, profiler.get()); + MMC->launch(m_selectedInstance, true, profiler.get()); }); } } @@ -953,7 +953,7 @@ void MainWindow::downloadUpdates(GoUpdate::Status status) { qDebug() << "Downloading updates."; ProgressDialog updateDlg(this); - status.rootPath = MMC->rootPath; + status.rootPath = MMC->root(); auto dlPath = FS::PathCombine(MMC->root(), "update", "XXXXXX"); if (!FS::ensureFilePathExists(dlPath)) @@ -1004,8 +1004,8 @@ void MainWindow::waitForMinecraftVersions() if (!MMC->minecraftlist()->isLoaded() && m_versionLoadTask && m_versionLoadTask->isRunning()) { QEventLoop waitLoop; - waitLoop.connect(m_versionLoadTask, SIGNAL(failed(QString)), SLOT(quit())); - waitLoop.connect(m_versionLoadTask, SIGNAL(succeeded()), SLOT(quit())); + waitLoop.connect(m_versionLoadTask, &Task::failed, &waitLoop, &QEventLoop::quit); + waitLoop.connect(m_versionLoadTask, &Task::succeeded, &waitLoop, &QEventLoop::quit); waitLoop.exec(); } } @@ -1261,62 +1261,24 @@ void MainWindow::on_actionSettings_triggered() update(); } -InstanceWindow *MainWindow::showInstanceWindow(InstancePtr instance, QString page) -{ - if(!instance) - return nullptr; - auto id = instance->id(); - InstanceWindow * window = nullptr; - - auto iter = m_instanceWindows.find(id); - if(iter != m_instanceWindows.end()) - { - window = *iter; - window->raise(); - window->activateWindow(); - } - else - { - window = new InstanceWindow(instance); - m_instanceWindows[id] = window; - connect(window, &InstanceWindow::isClosing, this, &MainWindow::on_instanceWindowClose); - } - if(!page.isEmpty()) - { - window->selectPage(page); - } - return window; -} - -void MainWindow::on_instanceWindowClose() -{ - auto senderWindow = qobject_cast(QObject::sender()); - if(!senderWindow) - { - return; - } - m_instanceWindows.remove(senderWindow->instanceId()); -} - - void MainWindow::on_actionInstanceSettings_triggered() { - showInstanceWindow(m_selectedInstance, "settings"); + MMC->showInstanceWindow(m_selectedInstance, "settings"); } void MainWindow::on_actionEditInstNotes_triggered() { - showInstanceWindow(m_selectedInstance, "notes"); + MMC->showInstanceWindow(m_selectedInstance, "notes"); } void MainWindow::on_actionEditInstance_triggered() { - showInstanceWindow(m_selectedInstance); + MMC->showInstanceWindow(m_selectedInstance); } void MainWindow::on_actionScreenshots_triggered() { - showInstanceWindow(m_selectedInstance, "screenshots"); + MMC->showInstanceWindow(m_selectedInstance, "screenshots"); } void MainWindow::on_actionManageAccounts_triggered() @@ -1440,14 +1402,14 @@ void MainWindow::instanceActivated(QModelIndex index) if (!inst) return; - launch(inst); + MMC->launch(inst); } void MainWindow::on_actionLaunchInstance_triggered() { if (m_selectedInstance) { - launch(m_selectedInstance); + MMC->launch(m_selectedInstance); } } @@ -1455,33 +1417,7 @@ void MainWindow::on_actionLaunchInstanceOffline_triggered() { if (m_selectedInstance) { - launch(m_selectedInstance, false); - } -} - -void MainWindow::launch(InstancePtr instance, bool online, BaseProfilerFactory *profiler) -{ - if(instance->canLaunch()) - { - // FIXME: duplicate logic between MainWindow and InstanceWindow - auto window = m_instanceWindows.find(instance->id()); - if(window != m_instanceWindows.end()) - { - if(!(*window)->saveAll()) - { - return; - } - } - m_launchController.reset(new LaunchController()); - m_launchController->setInstance(instance); - m_launchController->setOnline(online); - m_launchController->setParentWidget(this); - m_launchController->setProfiler(profiler); - m_launchController->start(); - } - else if (instance->isRunning()) - { - showInstanceWindow(instance, "console"); + MMC->launch(m_selectedInstance, false); } } diff --git a/application/MainWindow.h b/application/MainWindow.h index d0660676..e499f162 100644 --- a/application/MainWindow.h +++ b/application/MainWindow.h @@ -37,7 +37,6 @@ class MinecraftLauncher; class BaseProfilerFactory; class GroupView; class ServerStatus; -class InstanceWindow; class MainWindow : public QMainWindow { @@ -55,8 +54,6 @@ public: void checkSetDefaultJava(); void checkInstancePathForProblems(); - InstanceWindow *showInstanceWindow(InstancePtr instance, QString page = QString()); - private slots: void onCatToggled(bool); @@ -162,8 +159,6 @@ private slots: */ void downloadUpdates(GoUpdate::Status status); - void on_instanceWindowClose(); - private: void setCatBackground(bool enabled); void updateInstanceToolIcon(QString new_icon); @@ -174,7 +169,6 @@ private: void instanceFromVersion(QString instName, QString instGroup, QString instIcon, BaseVersionPtr version); void instanceFromZipPack(QString instName, QString instGroup, QString instIcon, QUrl url); void finalizeInstance(InstancePtr inst); - void launch(InstancePtr instance, bool online = true, BaseProfilerFactory *profiler = nullptr); private: std::unique_ptr ui; @@ -194,14 +188,10 @@ private: unique_qobject_ptr skin_download_job; unique_qobject_ptr m_newsChecker; unique_qobject_ptr m_notificationChecker; - unique_qobject_ptr m_launchController; InstancePtr m_selectedInstance; QString m_currentInstIcon; // managed by the application object Task *m_versionLoadTask; - - // map from instance ID to its window - QMap m_instanceWindows; }; diff --git a/application/MultiMC.cpp b/application/MultiMC.cpp index 927468b6..41f5281c 100644 --- a/application/MultiMC.cpp +++ b/application/MultiMC.cpp @@ -1,5 +1,7 @@ #include "MultiMC.h" #include "BuildConfig.h" +#include "MainWindow.h" +#include "InstanceWindow.h" #include "pages/BasePageProvider.h" #include "pages/global/MultiMCPage.h" #include "pages/global/MinecraftPage.h" @@ -59,6 +61,7 @@ #include #include #include +#include #if defined Q_OS_WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -159,6 +162,7 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) return; } } + m_instanceIdToLaunch = args["launch"].toString(); QString origcwdPath = QDir::currentPath(); QString binPath = applicationDirPath(); @@ -179,21 +183,32 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) adjustedBy += "Fallback to binary path " + dataPath; } - instanceIdToLaunch = args["launch"].toString(); - if (!FS::ensureFolderPathExists(dataPath) || !QDir::setCurrent(dataPath)) { // BAD STUFF. WHAT DO? - initLogger(); - qCritical() << "Failed to set work path. Will exit. NOW."; m_status = MultiMC::Failed; return; } + m_peerInstance = new LocalPeer(this, ApplicationId::fromPathAndVersion(dataPath, BuildConfig.printableVersionString())); + connect(m_peerInstance, &LocalPeer::messageReceived, this, &MultiMC::messageReceived); + if(m_peerInstance->isClient()) + { + if(m_instanceIdToLaunch.isEmpty()) + { + m_peerInstance->sendMessage("activate", 2000); + } + else + { + m_peerInstance->sendMessage(m_instanceIdToLaunch, 2000); + } + quit(); + return; + } // in test mode, root path is the same as the binary path. #ifdef Q_OS_LINUX QDir foo(FS::PathCombine(binPath, "..")); - rootPath = foo.absolutePath(); + m_rootPath = foo.absolutePath(); #elif defined(Q_OS_WIN32) rootPath = binPath; #elif defined(Q_OS_MAC) @@ -219,10 +234,10 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) qDebug() << "Work dir : " << QDir::currentPath(); } qDebug() << "Binary path : " << binPath; - qDebug() << "Application root path : " << rootPath; - if(!instanceIdToLaunch.isEmpty()) + qDebug() << "Application root path : " << m_rootPath; + if(!m_instanceIdToLaunch.isEmpty()) { - qDebug() << "ID of instance to launch : " << instanceIdToLaunch; + qDebug() << "ID of instance to launch : " << m_instanceIdToLaunch; } // load settings @@ -309,7 +324,22 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) } connect(this, SIGNAL(aboutToQuit()), SLOT(onExit())); + m_status = MultiMC::Initialized; + + setIconTheme(settings()->get("IconTheme").toString()); + setApplicationTheme(settings()->get("ApplicationTheme").toString()); + if(!m_instanceIdToLaunch.isEmpty()) + { + auto inst = instances()->getInstanceById(m_instanceIdToLaunch); + if(inst) + { + minecraftlist(); + launch(inst, true, nullptr); + return; + } + } + showMainWindow(); } MultiMC::~MultiMC() @@ -333,6 +363,22 @@ MultiMC::~MultiMC() #endif } +void MultiMC::messageReceived(const QString& message) +{ + if(message == "activate") + { + showMainWindow(); + } + else + { + auto inst = instances()->getInstanceById(message); + if(inst) + { + launch(inst, true, nullptr); + } + } +} + #ifdef Q_OS_MAC #include "CertWorkaround.h" #endif @@ -1024,4 +1070,101 @@ bool MultiMC::openJsonEditor(const QString &filename) } } +void MultiMC::launch(InstancePtr instance, bool online, BaseProfilerFactory *profiler) +{ + if(instance->canLaunch()) + { + m_launchController.reset(new LaunchController()); + m_launchController->setInstance(instance); + m_launchController->setOnline(online); + m_launchController->setProfiler(profiler); + auto windowIter = m_instanceWindows.find(instance->id()); + if(windowIter != m_instanceWindows.end()) + { + auto window = *windowIter; + if(!window->saveAll()) + { + return; + } + m_launchController->setParentWidget(window); + } + if(m_mainWindow) + { + m_launchController->setParentWidget(m_mainWindow); + } + m_launchController->start(); + } + else if (instance->isRunning()) + { + showInstanceWindow(instance, "console"); + } +} + +MainWindow * MultiMC::showMainWindow() +{ + if(m_mainWindow) + { + m_mainWindow->setWindowState(m_mainWindow->windowState() & ~Qt::WindowMinimized); + m_mainWindow->raise(); + m_mainWindow->activateWindow(); + } + else + { + m_mainWindow = new MainWindow(); + m_mainWindow->restoreState(QByteArray::fromBase64(MMC->settings()->get("MainWindowState").toByteArray())); + m_mainWindow->restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("MainWindowGeometry").toByteArray())); + m_mainWindow->show(); + m_mainWindow->checkSetDefaultJava(); + m_mainWindow->checkInstancePathForProblems(); + } + return m_mainWindow; +} + +InstanceWindow *MultiMC::showInstanceWindow(InstancePtr instance, QString page) +{ + if(!instance) + return nullptr; + auto id = instance->id(); + InstanceWindow * window = nullptr; + + auto iter = m_instanceWindows.find(id); + if(iter != m_instanceWindows.end()) + { + window = *iter; + window->raise(); + window->activateWindow(); + } + else + { + window = new InstanceWindow(instance); + m_instanceWindows[id] = window; + connect(window, &InstanceWindow::isClosing, this, &MultiMC::on_windowClose); + } + if(!page.isEmpty()) + { + window->selectPage(page); + } + return window; +} + +void MultiMC::on_windowClose() +{ + auto instWindow = qobject_cast(QObject::sender()); + if(instWindow) + { + m_instanceWindows.remove(instWindow->instanceId()); + return; + } + auto mainWindow = qobject_cast(QObject::sender()); + if(mainWindow) + { + m_mainWindow = nullptr; + } + // quit when there are no more windows. + if(m_instanceWindows.isEmpty() && !m_mainWindow) + { + quit(); + } +} + #include "MultiMC.moc" diff --git a/application/MultiMC.h b/application/MultiMC.h index 070447fc..663da1a1 100644 --- a/application/MultiMC.h +++ b/application/MultiMC.h @@ -7,8 +7,14 @@ #include #include #include -class FolderInstanceProvider; +#include + +class LaunchController; +class LocalPeer; +class InstanceWindow; +class MainWindow; +class FolderInstanceProvider; class GenericPageProvider; class QFile; class MinecraftVersionList; @@ -36,8 +42,6 @@ class ITheme; class MultiMC : public QApplication { // friends for the purpose of limiting access to deprecated stuff - friend class MultiMCPage; - friend class MainWindow; Q_OBJECT public: enum Status @@ -51,13 +55,12 @@ public: MultiMC(int &argc, char **argv); virtual ~MultiMC(); - // InstanceList, IconList, OneSixFTBInstance, LegacyUpdate, LegacyInstance, MCEditTool, JVisualVM, MinecraftInstance, JProfiler, BaseInstance - std::shared_ptr settings() + std::shared_ptr settings() const { return m_settings; } - std::shared_ptr globalSettingsPages() + std::shared_ptr globalSettingsPages() const { return m_globalSettingsProvider; } @@ -72,6 +75,7 @@ public: void setIconTheme(const QString& name); std::vector getValidApplicationThemes(); + void setApplicationTheme(const QString& name); // DownloadUpdateTask @@ -86,7 +90,6 @@ public: std::shared_ptr liteloaderlist(); std::shared_ptr javalist(); - // APPLICATION ONLY std::shared_ptr instances() const { return m_instances; @@ -102,46 +105,44 @@ public: return m_icons; } - // APPLICATION ONLY std::shared_ptr accounts() const { return m_accounts; } - // APPLICATION ONLY Status status() const { return m_status; } - // APPLICATION ONLY const QMap> &profilers() const { return m_profilers; } - // APPLICATION ONLY const QMap> &tools() const { return m_tools; } - // APPLICATION ONLY + /// this is the root of the 'installation'. Used for automatic updates + const QString &root() + { + return m_rootPath; + } + + // install updates now. void installUpdates(const QString updateFilesDir, GoUpdate::OperationList operations); /*! - * Opens a json file using either a system default editor, or, if note empty, the editor + * Opens a json file using either a system default editor, or, if not empty, the editor * specified in the settings */ bool openJsonEditor(const QString &filename); -protected: /* to be removed! */ - // FIXME: remove. used by MainWindow to create application update tasks - /// this is the root of the 'installation'. Used for automatic updates - const QString &root() - { - return rootPath; - } + InstanceWindow *showInstanceWindow(InstancePtr instance, QString page = QString()); + MainWindow *showMainWindow(); + void launch(InstancePtr instance, bool online = true, BaseProfilerFactory *profiler = nullptr); private slots: /** @@ -149,6 +150,10 @@ private slots: */ void onExit(); + void on_windowClose(); + + void messageReceived(const QString & message); + private: void initLogger(); void initIcons(); @@ -160,6 +165,7 @@ private: private: QDateTime startTime; + unique_qobject_ptr m_launchController; std::shared_ptr m_qt_translator; std::shared_ptr m_mmc_translator; std::shared_ptr m_settings; @@ -180,12 +186,19 @@ private: QMap> m_profilers; QMap> m_tools; - QString rootPath; + QString m_rootPath; Status m_status = MultiMC::Failed; + // used on Windows to attach the standard IO streams bool consoleAttached = false; + + // map from instance ID to its window + QMap m_instanceWindows; + // main window, if any + MainWindow * m_mainWindow = nullptr; + LocalPeer * m_peerInstance = nullptr; public: - QString instanceIdToLaunch; + QString m_instanceIdToLaunch; std::unique_ptr logFile; }; diff --git a/application/main.cpp b/application/main.cpp index c6847c5d..fde5eba7 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -4,41 +4,6 @@ #include #include -int launchMainWindow(MultiMC &app) -{ - MainWindow mainWin; - mainWin.restoreState(QByteArray::fromBase64(MMC->settings()->get("MainWindowState").toByteArray())); - mainWin.restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("MainWindowGeometry").toByteArray())); - mainWin.show(); - mainWin.checkSetDefaultJava(); - mainWin.checkInstancePathForProblems(); - return app.exec(); -} - -int launchInstance(MultiMC &app, InstancePtr inst) -{ - app.minecraftlist(); - LaunchController launchController; - launchController.setInstance(inst); - launchController.setOnline(true); - QMetaObject::invokeMethod(&launchController, "start", Qt::QueuedConnection); - return app.exec(); -} - -int main_gui(MultiMC &app) -{ - app.setIconTheme(MMC->settings()->get("IconTheme").toString()); - app.setApplicationTheme(MMC->settings()->get("ApplicationTheme").toString()); - - // show main window - auto inst = app.instances()->getInstanceById(app.instanceIdToLaunch); - if(inst) - { - return launchInstance(app, inst); - } - return launchMainWindow(app); -} - int main(int argc, char *argv[]) { // initialize Qt @@ -59,7 +24,7 @@ int main(int argc, char *argv[]) switch (app.status()) { case MultiMC::Initialized: - return main_gui(app); + return app.exec(); case MultiMC::Failed: return 1; case MultiMC::Succeeded: -- cgit v1.2.3