summaryrefslogtreecommitdiffstats
path: root/application
diff options
context:
space:
mode:
Diffstat (limited to 'application')
-rw-r--r--application/CMakeLists.txt5
-rw-r--r--application/ConsoleWindow.cpp260
-rw-r--r--application/InstancePageProvider.h2
-rw-r--r--application/InstanceWindow.cpp226
-rw-r--r--application/InstanceWindow.h (renamed from application/ConsoleWindow.h)32
-rw-r--r--application/LaunchInteraction.cpp33
-rw-r--r--application/LaunchInteraction.h5
-rw-r--r--application/MainWindow.cpp66
-rw-r--r--application/MainWindow.h8
-rw-r--r--application/SettingsUI.cpp9
-rw-r--r--application/SettingsUI.h2
-rw-r--r--application/groupview/InstanceDelegate.cpp4
-rw-r--r--application/pages/LogPage.cpp75
-rw-r--r--application/pages/LogPage.h5
-rw-r--r--application/resources/instances/instances.qrc1
-rw-r--r--application/resources/instances/status-running.pngbin0 -> 1059 bytes
-rw-r--r--application/resources/multimc/16x16/status-running.pngbin0 -> 675 bytes
-rw-r--r--application/resources/multimc/22x22/status-running.pngbin0 -> 957 bytes
-rw-r--r--application/resources/multimc/24x24/status-running.pngbin0 -> 1059 bytes
-rw-r--r--application/resources/multimc/32x32/status-running.pngbin0 -> 1425 bytes
-rw-r--r--application/resources/multimc/48x48/status-running.pngbin0 -> 2288 bytes
-rw-r--r--application/resources/multimc/64x64/status-running.pngbin0 -> 3178 bytes
-rw-r--r--application/resources/multimc/multimc.qrc9
-rw-r--r--application/resources/multimc/scalable/status-running.svg187
-rw-r--r--application/widgets/PageContainer.cpp22
-rw-r--r--application/widgets/PageContainer.h2
26 files changed, 606 insertions, 347 deletions
diff --git a/application/CMakeLists.txt b/application/CMakeLists.txt
index 9d71f977..46c496e2 100644
--- a/application/CMakeLists.txt
+++ b/application/CMakeLists.txt
@@ -103,12 +103,11 @@ SET(MULTIMC_SOURCES
# GUI - windows
MainWindow.h
MainWindow.cpp
- ConsoleWindow.h
- ConsoleWindow.cpp
+ InstanceWindow.h
+ InstanceWindow.cpp
# GUI - settings-specific wrappers for paged dialog
SettingsUI.h
- SettingsUI.cpp
# Processes
LaunchInteraction.h
diff --git a/application/ConsoleWindow.cpp b/application/ConsoleWindow.cpp
deleted file mode 100644
index e620d700..00000000
--- a/application/ConsoleWindow.cpp
+++ /dev/null
@@ -1,260 +0,0 @@
-/* Copyright 2013-2015 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ConsoleWindow.h"
-#include "MultiMC.h"
-
-#include <QScrollBar>
-#include <QMessageBox>
-#include <QSystemTrayIcon>
-#include <QHBoxLayout>
-#include <QPushButton>
-#include <qlayoutitem.h>
-#include <QCloseEvent>
-
-#include <dialogs/CustomMessageBox.h>
-#include <dialogs/ProgressDialog.h>
-#include "widgets/PageContainer.h"
-#include "pages/LogPage.h"
-#include "InstancePageProvider.h"
-
-#include "icons/IconList.h"
-
-class LogPageProvider : public BasePageProvider
-{
-public:
- LogPageProvider(BasePageProviderPtr parent, BasePage * log_page)
- {
- m_parent = parent;
- m_log_page = log_page;
- }
- virtual QString dialogTitle() {return "Fake";};
- virtual QList<BasePage *> getPages()
- {
- auto pages = m_parent->getPages();
- pages.prepend(m_log_page);
- return pages;
- }
-private:
- BasePageProviderPtr m_parent;
- BasePage * m_log_page;
-};
-
-ConsoleWindow::ConsoleWindow(std::shared_ptr<LaunchTask> proc, QWidget *parent)
- : QMainWindow(parent), m_proc(proc)
-{
- setAttribute(Qt::WA_DeleteOnClose);
-
- auto instance = m_proc->instance();
- auto icon = MMC->icons()->getIcon(instance->iconKey());
- QString windowTitle = tr("Console window for ") + instance->name();
-
- // Set window properties
- {
- setWindowIcon(icon);
- setWindowTitle(windowTitle);
- }
-
- // Add page container
- {
- auto mainLayout = new QVBoxLayout;
- auto provider = std::make_shared<InstancePageProvider>(m_proc->instance());
- auto baseprovider = std::dynamic_pointer_cast<BasePageProvider>(provider);
- auto proxy_provider = std::make_shared<LogPageProvider>(baseprovider, new LogPage(m_proc));
- m_container = new PageContainer(proxy_provider, "console", this);
- mainLayout->addWidget(m_container);
- mainLayout->setSpacing(0);
- mainLayout->setContentsMargins(0,0,0,0);
- setLayout(mainLayout);
- setCentralWidget(m_container);
- }
-
- // Add custom buttons to the page container layout.
- {
- auto horizontalLayout = new QHBoxLayout();
- horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
- horizontalLayout->setContentsMargins(6, -1, 6, -1);
-
- auto btnHelp = new QPushButton();
- btnHelp->setText(tr("Help"));
- horizontalLayout->addWidget(btnHelp);
- connect(btnHelp, SIGNAL(clicked(bool)), m_container, SLOT(help()));
-
- auto spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
- horizontalLayout->addSpacerItem(spacer);
-
- m_killButton = new QPushButton();
- m_killButton->setText(tr("Kill Minecraft"));
- horizontalLayout->addWidget(m_killButton);
- connect(m_killButton, SIGNAL(clicked(bool)), SLOT(on_btnKillMinecraft_clicked()));
-
- m_closeButton = new QPushButton();
- m_closeButton->setText(tr("Close"));
- horizontalLayout->addWidget(m_closeButton);
- connect(m_closeButton, SIGNAL(clicked(bool)), SLOT(on_closeButton_clicked()));
-
- m_container->addButtons(horizontalLayout);
- }
-
- // restore window state
- {
- auto base64State = MMC->settings()->get("ConsoleWindowState").toByteArray();
- restoreState(QByteArray::fromBase64(base64State));
- auto base64Geometry = MMC->settings()->get("ConsoleWindowGeometry").toByteArray();
- restoreGeometry(QByteArray::fromBase64(base64Geometry));
- }
-
- // Set up tray icon
- {
- m_trayIcon = new QSystemTrayIcon(icon, this);
- m_trayIcon->setToolTip(windowTitle);
-
- connect(m_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
- SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
- m_trayIcon->show();
- }
-
- // Set up signal connections
- connect(m_proc.get(), &LaunchTask::succeeded, this, &ConsoleWindow::onSucceeded);
- connect(m_proc.get(), &LaunchTask::failed, this, &ConsoleWindow::onFailed);
- connect(m_proc.get(), &LaunchTask::requestProgress, this, &ConsoleWindow::onProgressRequested);
-
- setMayClose(false);
-
- if (m_proc->instance()->settings()->get("ShowConsole").toBool())
- {
- show();
- }
-}
-
-void ConsoleWindow::iconActivated(QSystemTrayIcon::ActivationReason reason)
-{
- switch (reason)
- {
- case QSystemTrayIcon::Trigger:
- {
- toggleConsole();
- }
- default:
- return;
- }
-}
-
-void ConsoleWindow::on_closeButton_clicked()
-{
- close();
-}
-
-void ConsoleWindow::setMayClose(bool mayclose)
-{
- if(mayclose)
- m_closeButton->setText(tr("Close"));
- else
- m_closeButton->setText(tr("Hide"));
- m_mayclose = mayclose;
-}
-
-void ConsoleWindow::toggleConsole()
-{
- if (isVisible())
- {
- if(!isActiveWindow())
- {
- activateWindow();
- return;
- }
- hide();
- }
- else
- {
- show();
- }
-}
-
-void ConsoleWindow::closeEvent(QCloseEvent *event)
-{
- if (!m_mayclose)
- {
- toggleConsole();
- event->ignore();
- }
- else if(m_container->requestClose(event))
- {
- MMC->settings()->set("ConsoleWindowState", saveState().toBase64());
- MMC->settings()->set("ConsoleWindowGeometry", saveGeometry().toBase64());
-
- emit isClosing();
- m_trayIcon->hide();
- event->accept();
- }
-}
-
-void ConsoleWindow::on_btnKillMinecraft_clicked()
-{
- m_killButton->setEnabled(false);
- auto response = CustomMessageBox::selectable(
- this, tr("Kill Minecraft?"),
- tr("This can cause the instance to get corrupted and should only be used if Minecraft "
- "is frozen for some reason"),
- QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec();
- if (response == QMessageBox::Yes)
- m_proc->abort();
- else
- m_killButton->setEnabled(true);
-}
-
-void ConsoleWindow::onSucceeded()
-{
- m_killButton->setEnabled(false);
- setMayClose(true);
- if (m_proc->instance()->settings()->get("AutoCloseConsole").toBool() && m_container->requestClose(nullptr))
- {
- this->close();
- return;
- }
- if (!isVisible())
- {
- show();
- }
- // Raise Window
- if (MMC->settings()->get("RaiseConsole").toBool())
- {
- raise();
- activateWindow();
- }
-}
-
-void ConsoleWindow::onFailed(QString reason)
-{
- m_killButton->setEnabled(false);
- setMayClose(true);
- if (!isVisible())
- {
- show();
- }
-}
-
-void ConsoleWindow::onProgressRequested(Task* task)
-{
- ProgressDialog progDialog(this);
- m_proc->proceed();
- progDialog.execWithTask(task);
-}
-
-
-ConsoleWindow::~ConsoleWindow()
-{
-
-}
diff --git a/application/InstancePageProvider.h b/application/InstancePageProvider.h
index dfc2e4dd..1d6cc5d7 100644
--- a/application/InstancePageProvider.h
+++ b/application/InstancePageProvider.h
@@ -3,6 +3,7 @@
#include "minecraft/legacy/LegacyInstance.h"
#include <FileSystem.h>
#include "pages/BasePage.h"
+#include "pages/LogPage.h"
#include "pages/VersionPage.h"
#include "pages/ModFolderPage.h"
#include "pages/ResourcePackPage.h"
@@ -29,6 +30,7 @@ public:
virtual QList<BasePage *> getPages() override
{
QList<BasePage *> values;
+ values.append(new LogPage(inst));
std::shared_ptr<OneSixInstance> onesix = std::dynamic_pointer_cast<OneSixInstance>(inst);
if(onesix)
{
diff --git a/application/InstanceWindow.cpp b/application/InstanceWindow.cpp
new file mode 100644
index 00000000..dfc7b815
--- /dev/null
+++ b/application/InstanceWindow.cpp
@@ -0,0 +1,226 @@
+/* Copyright 2013-2015 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InstanceWindow.h"
+#include "MultiMC.h"
+
+#include <QScrollBar>
+#include <QMessageBox>
+#include <QHBoxLayout>
+#include <QPushButton>
+#include <qlayoutitem.h>
+#include <QCloseEvent>
+
+#include <dialogs/CustomMessageBox.h>
+#include <dialogs/ProgressDialog.h>
+#include "widgets/PageContainer.h"
+#include "InstancePageProvider.h"
+
+#include "icons/IconList.h"
+
+InstanceWindow::InstanceWindow(InstancePtr instance, QWidget *parent)
+ : QMainWindow(parent), m_instance(instance)
+{
+ setAttribute(Qt::WA_DeleteOnClose);
+
+ auto icon = MMC->icons()->getIcon(m_instance->iconKey());
+ QString windowTitle = tr("Console window for ") + m_instance->name();
+
+ // Set window properties
+ {
+ setWindowIcon(icon);
+ setWindowTitle(windowTitle);
+ }
+
+ // Add page container
+ {
+ auto mainLayout = new QVBoxLayout;
+ auto provider = std::make_shared<InstancePageProvider>(m_instance);
+ m_container = new PageContainer(provider, "console", this);
+ mainLayout->addWidget(m_container);
+ mainLayout->setSpacing(0);
+ mainLayout->setContentsMargins(0,0,0,0);
+ setLayout(mainLayout);
+ setCentralWidget(m_container);
+ }
+
+ // Add custom buttons to the page container layout.
+ {
+ auto horizontalLayout = new QHBoxLayout();
+ horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
+ horizontalLayout->setContentsMargins(6, -1, 6, -1);
+
+ auto btnHelp = new QPushButton();
+ btnHelp->setText(tr("Help"));
+ horizontalLayout->addWidget(btnHelp);
+ connect(btnHelp, SIGNAL(clicked(bool)), m_container, SLOT(help()));
+
+ auto spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
+ horizontalLayout->addSpacerItem(spacer);
+
+ m_killButton = new QPushButton();
+ horizontalLayout->addWidget(m_killButton);
+ setKillButton(m_instance->isRunning());
+ connect(m_killButton, SIGNAL(clicked(bool)), SLOT(on_btnKillMinecraft_clicked()));
+
+ m_closeButton = new QPushButton();
+ m_closeButton->setText(tr("Close"));
+ horizontalLayout->addWidget(m_closeButton);
+ connect(m_closeButton, SIGNAL(clicked(bool)), SLOT(on_closeButton_clicked()));
+
+ m_container->addButtons(horizontalLayout);
+ }
+
+ // restore window state
+ {
+ auto base64State = MMC->settings()->get("ConsoleWindowState").toByteArray();
+ restoreState(QByteArray::fromBase64(base64State));
+ auto base64Geometry = MMC->settings()->get("ConsoleWindowGeometry").toByteArray();
+ restoreGeometry(QByteArray::fromBase64(base64Geometry));
+ }
+
+ // set up instance and launch process recognition
+ {
+ auto launchTask = m_instance->getLaunchTask();
+ on_InstanceLaunchTask_changed(launchTask);
+ connect(m_instance.get(), &BaseInstance::launchTaskChanged,
+ this, &InstanceWindow::on_InstanceLaunchTask_changed);
+ connect(m_instance.get(), &BaseInstance::runningStatusChanged,
+ this, &InstanceWindow::on_RunningState_changed);
+ }
+ show();
+}
+
+void InstanceWindow::setKillButton(bool kill)
+{
+ if(kill)
+ {
+ m_killButton->setText(tr("Kill"));
+ m_killButton->setToolTip(tr("Kill the running instance"));
+ }
+ else
+ {
+ m_killButton->setText(tr("Launch"));
+ m_killButton->setToolTip(tr("Launch the instance"));
+ }
+}
+
+void InstanceWindow::on_InstanceLaunchTask_changed(std::shared_ptr<LaunchTask> proc)
+{
+ if(m_proc)
+ {
+ disconnect(m_proc.get(), &LaunchTask::succeeded, this, &InstanceWindow::onSucceeded);
+ disconnect(m_proc.get(), &LaunchTask::failed, this, &InstanceWindow::onFailed);
+ disconnect(m_proc.get(), &LaunchTask::requestProgress, this, &InstanceWindow::onProgressRequested);
+ }
+
+ m_proc = proc;
+
+ if(m_proc)
+ {
+ // Set up signal connections
+ connect(m_proc.get(), &LaunchTask::succeeded, this, &InstanceWindow::onSucceeded);
+ connect(m_proc.get(), &LaunchTask::failed, this, &InstanceWindow::onFailed);
+ connect(m_proc.get(), &LaunchTask::requestProgress, this, &InstanceWindow::onProgressRequested);
+ }
+}
+
+void InstanceWindow::on_RunningState_changed(bool running)
+{
+ setKillButton(running);
+ m_container->refresh();
+}
+
+void InstanceWindow::on_closeButton_clicked()
+{
+ close();
+}
+
+void InstanceWindow::closeEvent(QCloseEvent *event)
+{
+ MMC->settings()->set("ConsoleWindowState", saveState().toBase64());
+ MMC->settings()->set("ConsoleWindowGeometry", saveGeometry().toBase64());
+
+ if(m_container->requestClose(event))
+ {
+ emit isClosing();
+ event->accept();
+ }
+}
+
+void InstanceWindow::on_btnKillMinecraft_clicked()
+{
+ if(m_instance->isRunning())
+ {
+ auto response = CustomMessageBox::selectable(
+ this, tr("Kill Minecraft?"),
+ tr("This can cause the instance to get corrupted and should only be used if Minecraft "
+ "is frozen for some reason"),
+ QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec();
+ if (response == QMessageBox::Yes)
+ {
+ m_proc->abort();
+ }
+ }
+ else
+ {
+ m_launchController.reset(new LaunchController());
+ m_launchController->setInstance(m_instance);
+ m_launchController->setOnline(true);
+ m_launchController->setParentWidget(this);
+ m_launchController->start();
+ }
+}
+
+void InstanceWindow::onSucceeded()
+{
+ if (m_instance->settings()->get("AutoCloseConsole").toBool() && m_container->requestClose(nullptr))
+ {
+ this->close();
+ return;
+ }
+ // Raise Window
+ if (MMC->settings()->get("RaiseConsole").toBool())
+ {
+ show();
+ raise();
+ activateWindow();
+ }
+}
+
+void InstanceWindow::onFailed(QString reason)
+{
+}
+
+void InstanceWindow::onProgressRequested(Task* task)
+{
+ ProgressDialog progDialog(this);
+ m_proc->proceed();
+ progDialog.execWithTask(task);
+}
+
+QString InstanceWindow::instanceId()
+{
+ return m_instance->id();
+}
+
+bool InstanceWindow::selectPage(QString pageId)
+{
+ return m_container->selectPage(pageId);
+}
+
+InstanceWindow::~InstanceWindow()
+{
+}
diff --git a/application/ConsoleWindow.h b/application/InstanceWindow.h
index ac5a6fd1..7ffc4142 100644
--- a/application/ConsoleWindow.h
+++ b/application/InstanceWindow.h
@@ -16,25 +16,25 @@
#pragma once
#include <QMainWindow>
+#include "LaunchInteraction.h"
+#include <QObjectPtr.h>
#include <QSystemTrayIcon>
#include "launch/LaunchTask.h"
+#include "pages/BasePageContainer.h"
class QPushButton;
class PageContainer;
-class ConsoleWindow : public QMainWindow
+class InstanceWindow : public QMainWindow, public BasePageContainer
{
Q_OBJECT
public:
- explicit ConsoleWindow(std::shared_ptr<LaunchTask> proc, QWidget *parent = 0);
- virtual ~ConsoleWindow();
+ explicit InstanceWindow(InstancePtr proc, QWidget *parent = 0);
+ virtual ~InstanceWindow();
- /**
- * @brief specify if the window is allowed to close
- * @param mayclose
- * used to keep it alive while MC runs
- */
- void setMayClose(bool mayclose);
+ bool selectPage(QString pageId) override;
+
+ QString instanceId();
signals:
void isClosing();
@@ -48,18 +48,20 @@ slots:
void onFailed(QString reason);
void onProgressRequested(Task *task);
- // FIXME: add handlers for the other MinecraftLauncher signals (pre/post launch command
- // failures)
+ void on_InstanceLaunchTask_changed(std::shared_ptr<LaunchTask> proc);
+ void on_RunningState_changed(bool running);
- void iconActivated(QSystemTrayIcon::ActivationReason);
- void toggleConsole();
protected:
- void closeEvent(QCloseEvent *);
+ void closeEvent(QCloseEvent *) override;
+
+private:
+ void setKillButton(bool kill);
private:
std::shared_ptr<LaunchTask> m_proc;
+ unique_qobject_ptr<LaunchController> m_launchController;
+ InstancePtr m_instance;
bool m_mayclose = true;
- QSystemTrayIcon *m_trayIcon = nullptr;
PageContainer *m_container = nullptr;
QPushButton *m_closeButton = nullptr;
QPushButton *m_killButton = nullptr;
diff --git a/application/LaunchInteraction.cpp b/application/LaunchInteraction.cpp
index 1caa054a..80a3368d 100644
--- a/application/LaunchInteraction.cpp
+++ b/application/LaunchInteraction.cpp
@@ -1,11 +1,12 @@
#include "LaunchInteraction.h"
+#include "MainWindow.h"
#include <minecraft/auth/MojangAccountList.h>
#include "MultiMC.h"
#include "dialogs/CustomMessageBox.h"
#include "dialogs/AccountSelectDialog.h"
#include "dialogs/ProgressDialog.h"
#include "dialogs/EditAccountDialog.h"
-#include "ConsoleWindow.h"
+#include "InstanceWindow.h"
#include "BuildConfig.h"
#include "JavaCommon.h"
#include "SettingsUI.h"
@@ -204,13 +205,20 @@ void LaunchController::launchInstance()
return;
}
- if(m_parentWidget)
+ auto mainWindow = qobject_cast<MainWindow *>(m_parentWidget);
+ auto instanceWindow = qobject_cast<InstanceWindow *>(m_parentWidget);
+ if(mainWindow)
{
- m_parentWidget->hide();
+ m_console = mainWindow->showInstanceWindow(m_instance);
+ }
+ else if(instanceWindow)
+ {
+ // NOOP
+ }
+ else
+ {
+ m_console = new InstanceWindow(m_instance);
}
-
- m_console = new ConsoleWindow(m_launcher);
- connect(m_console, &ConsoleWindow::isClosing, this, &LaunchController::instanceEnded);
connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch);
m_launcher->prependStep(std::make_shared<TextPrint>(m_launcher.get(), "MultiMC version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::MultiMC));
@@ -222,6 +230,7 @@ void LaunchController::readyForLaunch()
if (!m_profiler)
{
m_launcher->proceed();
+ emitSucceeded();
return;
}
@@ -230,6 +239,7 @@ void LaunchController::readyForLaunch()
{
m_launcher->abort();
QMessageBox::critical(m_parentWidget, tr("Error"), tr("Couldn't start profiler: %1").arg(error));
+ emitFailed("Profiler startup failed");
return;
}
BaseProfiler *profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
@@ -246,6 +256,7 @@ void LaunchController::readyForLaunch()
msg.setModal(true);
msg.exec();
m_launcher->proceed();
+ emitSucceeded();
});
connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString & message)
{
@@ -257,15 +268,7 @@ void LaunchController::readyForLaunch()
msg.setModal(true);
msg.exec();
m_launcher->abort();
+ emitFailed("Profiler startup failed");
});
profilerInstance->beginProfiling(m_launcher);
}
-
-void LaunchController::instanceEnded()
-{
- if(m_parentWidget)
- {
- m_parentWidget->show();
- }
- emitSucceeded();
-}
diff --git a/application/LaunchInteraction.h b/application/LaunchInteraction.h
index b0932e9b..55cb1e58 100644
--- a/application/LaunchInteraction.h
+++ b/application/LaunchInteraction.h
@@ -3,7 +3,7 @@
#include <BaseInstance.h>
#include <tools/BaseProfiler.h>
-class ConsoleWindow;
+class InstanceWindow;
class LaunchController: public Task
{
Q_OBJECT
@@ -36,14 +36,13 @@ private:
private slots:
void readyForLaunch();
- void instanceEnded();
private:
BaseProfilerFactory *m_profiler = nullptr;
bool m_online = true;
InstancePtr m_instance;
QWidget * m_parentWidget = nullptr;
- ConsoleWindow *m_console = nullptr;
+ InstanceWindow *m_console = nullptr;
AuthSessionPtr m_session;
std::shared_ptr <LaunchTask> m_launcher;
};
diff --git a/application/MainWindow.cpp b/application/MainWindow.cpp
index 06d165da..88c0fc09 100644
--- a/application/MainWindow.cpp
+++ b/application/MainWindow.cpp
@@ -67,6 +67,7 @@
#include <updater/UpdateChecker.h>
#include <DesktopServices.h>
+#include "InstanceWindow.h"
#include "InstancePageProvider.h"
#include "InstanceProxyModel.h"
#include "JavaCommon.h"
@@ -1424,24 +1425,62 @@ 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, this);
+ 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<InstanceWindow *>(QObject::sender());
+ if(!senderWindow)
+ {
+ return;
+ }
+ m_instanceWindows.remove(senderWindow->instanceId());
+}
+
+
void MainWindow::on_actionInstanceSettings_triggered()
{
- SettingsUI::ShowInstancePageDialog(m_selectedInstance, this, "settings");
+ showInstanceWindow(m_selectedInstance, "settings");
}
void MainWindow::on_actionEditInstNotes_triggered()
{
- SettingsUI::ShowInstancePageDialog(m_selectedInstance, this, "notes");
+ showInstanceWindow(m_selectedInstance, "notes");
}
void MainWindow::on_actionEditInstance_triggered()
{
- SettingsUI::ShowInstancePageDialog(m_selectedInstance, this);
+ showInstanceWindow(m_selectedInstance);
}
void MainWindow::on_actionScreenshots_triggered()
{
- SettingsUI::ShowInstancePageDialog(m_selectedInstance, this, "screenshots");
+ showInstanceWindow(m_selectedInstance, "screenshots");
}
void MainWindow::on_actionManageAccounts_triggered()
@@ -1586,16 +1625,19 @@ void MainWindow::on_actionLaunchInstanceOffline_triggered()
void MainWindow::launch(InstancePtr instance, bool online, BaseProfilerFactory *profiler)
{
- if(!instance->canLaunch())
+ if(instance->canLaunch())
{
- 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");
}
- m_launchController.reset(new LaunchController());
- m_launchController->setInstance(instance);
- m_launchController->setOnline(online);
- m_launchController->setParentWidget(this);
- m_launchController->setProfiler(profiler);
- m_launchController->start();
}
void MainWindow::taskEnd()
diff --git a/application/MainWindow.h b/application/MainWindow.h
index e3fb0467..3f0ec6e6 100644
--- a/application/MainWindow.h
+++ b/application/MainWindow.h
@@ -37,6 +37,7 @@ class MinecraftLauncher;
class BaseProfilerFactory;
class GroupView;
class ServerStatus;
+class InstanceWindow;
class MainWindow : public QMainWindow
{
@@ -54,6 +55,8 @@ public:
void checkSetDefaultJava();
void checkInstancePathForProblems();
+ InstanceWindow *showInstanceWindow(InstancePtr instance, QString page = QString());
+
private slots:
void onCatToggled(bool);
@@ -159,6 +162,8 @@ private slots:
*/
void downloadUpdates(GoUpdate::Status status);
+ void on_instanceWindowClose();
+
private:
void setCatBackground(bool enabled);
void updateInstanceToolIcon(QString new_icon);
@@ -195,4 +200,7 @@ private:
// managed by the application object
Task *m_versionLoadTask;
+
+ // map from instance ID to its window
+ QMap<QString, InstanceWindow *> m_instanceWindows;
};
diff --git a/application/SettingsUI.cpp b/application/SettingsUI.cpp
deleted file mode 100644
index 2a2010de..00000000
--- a/application/SettingsUI.cpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "SettingsUI.h"
-namespace SettingsUI
-{
-void ShowInstancePageDialog(InstancePtr instance, QWidget * parent, QString open_page)
-{
- auto provider = std::make_shared<InstancePageProvider>(instance);
- ShowPageDialog(provider, parent, open_page);
-}
-}
diff --git a/application/SettingsUI.h b/application/SettingsUI.h
index 1e6dc8d0..5b8badf2 100644
--- a/application/SettingsUI.h
+++ b/application/SettingsUI.h
@@ -23,6 +23,4 @@ void ShowPageDialog(T raw_provider, QWidget * parent, QString open_page = QStrin
dlg.exec();
}
}
-
-void ShowInstancePageDialog(InstancePtr instance, QWidget * parent, QString open_page = QString());
}
diff --git a/application/groupview/InstanceDelegate.cpp b/application/groupview/InstanceDelegate.cpp
index b9ad353c..359dd3cf 100644
--- a/application/groupview/InstanceDelegate.cpp
+++ b/application/groupview/InstanceDelegate.cpp
@@ -122,6 +122,10 @@ void drawBadges(QPainter *painter, const QStyleOptionViewItemV4 &option, BaseIns
{
pixmaps.append("updateavailable");
}
+ if (instance->isRunning())
+ {
+ pixmaps.append("status-running");
+ }
// begin easter eggs
if (instance->name().contains("btw", Qt::CaseInsensitive) ||
diff --git a/application/pages/LogPage.cpp b/application/pages/LogPage.cpp
index af96148b..de4ed4f3 100644
--- a/application/pages/LogPage.cpp
+++ b/application/pages/LogPage.cpp
@@ -12,40 +12,57 @@
#include "GuiUtil.h"
#include <ColorCache.h>
-LogPage::LogPage(std::shared_ptr<LaunchTask> proc, QWidget *parent)
- : QWidget(parent), ui(new Ui::LogPage), m_process(proc)
+LogPage::LogPage(InstancePtr instance, QWidget *parent)
+ : QWidget(parent), ui(new Ui::LogPage), m_instance(instance)
{
ui->setupUi(this);
ui->tabWidget->tabBar()->hide();
- connect(m_process.get(), SIGNAL(log(QString, MessageLevel::Enum)), this,
- SLOT(write(QString, MessageLevel::Enum)));
// create the format and set its font
- defaultFormat = new QTextCharFormat(ui->text->currentCharFormat());
- QString fontFamily = MMC->settings()->get("ConsoleFont").toString();
- bool conversionOk = false;
- int fontSize = MMC->settings()->get("ConsoleFontSize").toInt(&conversionOk);
- if(!conversionOk)
{
- fontSize = 11;
+ defaultFormat = new QTextCharFormat(ui->text->currentCharFormat());
+ QString fontFamily = MMC->settings()->get("ConsoleFont").toString();
+ bool conversionOk = false;
+ int fontSize = MMC->settings()->get("ConsoleFontSize").toInt(&conversionOk);
+ if(!conversionOk)
+ {
+ fontSize = 11;
+ }
+ defaultFormat->setFont(QFont(fontFamily, fontSize));
}
- defaultFormat->setFont(QFont(fontFamily, fontSize));
// ensure we don't eat all the RAM
- auto lineSetting = MMC->settings()->getSetting("ConsoleMaxLines");
- int maxLines = lineSetting->get().toInt(&conversionOk);
- if(!conversionOk)
{
- maxLines = lineSetting->defValue().toInt();
- qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
+ auto lineSetting = MMC->settings()->getSetting("ConsoleMaxLines");
+ bool conversionOk = false;
+ int maxLines = lineSetting->get().toInt(&conversionOk);
+ if(!conversionOk)
+ {
+ maxLines = lineSetting->defValue().toInt();
+ qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
+ }
+ ui->text->setMaximumBlockCount(maxLines);
+
+ m_stopOnOverflow = MMC->settings()->get("ConsoleOverflowStop").toBool();
}
- ui->text->setMaximumBlockCount(maxLines);
- auto origForeground = ui->text->palette().color(ui->text->foregroundRole());
- auto origBackground = ui->text->palette().color(ui->text->backgroundRole());
- m_colors.reset(new LogColorCache(origForeground, origBackground));
+ // set up instance and launch process recognition
+ {
+ auto launchTask = m_instance->getLaunchTask();
+ if(launchTask)
+ {
+ on_InstanceLaunchTask_changed(launchTask);
+ }
+ connect(m_instance.get(), &BaseInstance::launchTaskChanged,
+ this, &LogPage::on_InstanceLaunchTask_changed);
+ }
- m_stopOnOverflow = MMC->settings()->get("ConsoleOverflowStop").toBool();
+ // set up text colors and adapt them to the current theme foreground and background
+ {
+ auto origForeground = ui->text->palette().color(ui->text->foregroundRole());
+ auto origBackground = ui->text->palette().color(ui->text->backgroundRole());
+ m_colors.reset(new LogColorCache(origForeground, origBackground));
+ }
auto findShortcut = new QShortcut(QKeySequence(QKeySequence::Find), this);
connect(findShortcut, SIGNAL(activated()), SLOT(findActivated()));
@@ -62,6 +79,20 @@ LogPage::~LogPage()
delete defaultFormat;
}
+void LogPage::on_InstanceLaunchTask_changed(std::shared_ptr<LaunchTask> proc)
+{
+ if(m_process)
+ {
+ disconnect(m_process.get(), &LaunchTask::log, this, &LogPage::write);
+ }
+ m_process = proc;
+ if(m_process)
+ {
+ ui->text->clear();
+ connect(m_process.get(), &LaunchTask::log, this, &LogPage::write);
+ }
+}
+
bool LogPage::apply()
{
return true;
@@ -69,7 +100,7 @@ bool LogPage::apply()
bool LogPage::shouldDisplay() const
{
- return m_process->instance()->isRunning();
+ return m_instance->isRunning() || ui->text->blockCount() > 1;
}
void LogPage::on_btnPaste_clicked()
diff --git a/application/pages/LogPage.h b/application/pages/LogPage.h
index 9b694323..e902ad13 100644
--- a/application/pages/LogPage.h
+++ b/application/pages/LogPage.h
@@ -34,7 +34,7 @@ class LogPage : public QWidget, public BasePage
Q_OBJECT
public:
- explicit LogPage(std::shared_ptr<LaunchTask> proc, QWidget *parent = 0);
+ explicit LogPage(InstancePtr instance, QWidget *parent = 0);
virtual ~LogPage();
virtual QString displayName() const override
{
@@ -77,8 +77,11 @@ private slots:
void findNextActivated();
void findPreviousActivated();
+ void on_InstanceLaunchTask_changed(std::shared_ptr<LaunchTask> proc);
+
private:
Ui::LogPage *ui;
+ InstancePtr m_instance;
std::shared_ptr<LaunchTask> m_process;
int m_last_scroll_value = 0;
bool m_scroll_active = true;
diff --git a/application/resources/instances/instances.qrc b/application/resources/instances/instances.qrc
index 09ae25d0..b1f1bb4d 100644
--- a/application/resources/instances/instances.qrc
+++ b/application/resources/instances/instances.qrc
@@ -37,6 +37,7 @@
<file>enderman.png</file>
<file>herobrine.png</file>
<file>derp.png</file>
+ <file>status-running.png</file>
<!-- Update. GPLv2, https://code.google.com/p/gnome-colors/ -->
<file>updateavailable.png</file>
diff --git a/application/resources/instances/status-running.png b/application/resources/instances/status-running.png
new file mode 100644
index 00000000..ecd64451
--- /dev/null
+++ b/application/resources/instances/status-running.png
Binary files differ
diff --git a/application/resources/multimc/16x16/status-running.png b/application/resources/multimc/16x16/status-running.png
new file mode 100644
index 00000000..a4c42e39
--- /dev/null
+++ b/application/resources/multimc/16x16/status-running.png
Binary files differ
diff --git a/application/resources/multimc/22x22/status-running.png b/application/resources/multimc/22x22/status-running.png
new file mode 100644
index 00000000..0dffba18
--- /dev/null
+++ b/application/resources/multimc/22x22/status-running.png
Binary files differ
diff --git a/application/resources/multimc/24x24/status-running.png b/application/resources/multimc/24x24/status-running.png
new file mode 100644
index 00000000..ecd64451
--- /dev/null
+++ b/application/resources/multimc/24x24/status-running.png
Binary files differ
diff --git a/application/resources/multimc/32x32/status-running.png b/application/resources/multimc/32x32/status-running.png
new file mode 100644
index 00000000..f561f01a
--- /dev/null
+++ b/application/resources/multimc/32x32/status-running.png
Binary files differ
diff --git a/application/resources/multimc/48x48/status-running.png b/application/resources/multimc/48x48/status-running.png
new file mode 100644
index 00000000..b8c0bf7c
--- /dev/null
+++ b/application/resources/multimc/48x48/status-running.png
Binary files differ
diff --git a/application/resources/multimc/64x64/status-running.png b/application/resources/multimc/64x64/status-running.png
new file mode 100644
index 00000000..38afda0f
--- /dev/null
+++ b/application/resources/multimc/64x64/status-running.png
Binary files differ
diff --git a/application/resources/multimc/multimc.qrc b/application/resources/multimc/multimc.qrc
index 3f7c5e8f..86b472d8 100644
--- a/application/resources/multimc/multimc.qrc
+++ b/application/resources/multimc/multimc.qrc
@@ -150,6 +150,15 @@
<file>48x48/status-yellow.png</file>
<file>64x64/status-yellow.png</file>
+ <!-- A status icon for things that are in progress/running... Our own. -->
+ <file>16x16/status-running.png</file>
+ <file>24x24/status-running.png</file>
+ <file>22x22/status-running.png</file>
+ <file>32x32/status-running.png</file>
+ <file>48x48/status-running.png</file>
+ <file>64x64/status-running.png</file>
+ <file>scalable/status-running.svg</file>
+
<!-- Plugin (blue recolor), CC-BY-SA 3.0, Oxygen icons. -->
<file>16x16/loadermods.png</file>
<file>24x24/loadermods.png</file>
diff --git a/application/resources/multimc/scalable/status-running.svg b/application/resources/multimc/scalable/status-running.svg
new file mode 100644
index 00000000..18209940
--- /dev/null
+++ b/application/resources/multimc/scalable/status-running.svg
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ width="32"
+ height="32"
+ id="svg2"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="status-running.svg"
+ inkscape:export-filename="/home/peterix/minecraft/src/MultiMC5/application/resources/multimc/64x64/status-running.png"
+ inkscape:export-xdpi="180"
+ inkscape:export-ydpi="180">
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="3840"
+ inkscape:window-height="2125"
+ id="namedview32"
+ showgrid="true"
+ inkscape:snap-bbox="true"
+ inkscape:bbox-nodes="false"
+ inkscape:bbox-paths="false"
+ inkscape:snap-bbox-midpoints="false"
+ inkscape:snap-bbox-edge-midpoints="false"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:object-nodes="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="false"
+ inkscape:zoom="10.429825"
+ inkscape:cx="2.5496161"
+ inkscape:cy="28.936353"
+ inkscape:window-x="1200"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid4160"
+ spacingx="0.5"
+ spacingy="0.5"
+ empspacing="8" />
+ </sodipodi:namedview>
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient4162">
+ <stop
+ offset="0"
+ style="stop-color:#0071f1;stop-opacity:1"
+ id="stop4164" />
+ <stop
+ offset="1"
+ style="stop-color:#007ec3;stop-opacity:1"
+ id="stop4166" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3827">
+ <stop
+ id="stop3829"
+ style="stop-color:#b80000;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop3831"
+ style="stop-color:#600000;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3801">
+ <stop
+ id="stop3803"
+ style="stop-color:#f1ab00;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop3805"
+ style="stop-color:#c39a00;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3005">
+ <stop
+ id="stop3007"
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop3781"
+ style="stop-color:#ffffff;stop-opacity:0.49803922"
+ offset="0.8142857" />
+ <stop
+ id="stop3009"
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <filter
+ color-interpolation-filters="sRGB"
+ id="filter3797">
+ <feGaussianBlur
+ id="feGaussianBlur3799"
+ stdDeviation="0.52592593" />
+ </filter>
+ <radialGradient
+ cx="3.9371533"
+ cy="7.5016646"
+ r="2.5"
+ fx="3.9371533"
+ fy="7.5016646"
+ id="radialGradient3807"
+ xlink:href="#linearGradient4162"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.4496779,1.5407764,-0.90127514,0.84794135,4.9906134,-4.9255796)" />
+ <radialGradient
+ cx="3.9371533"
+ cy="7.5016646"
+ r="2.5"
+ fx="3.9371533"
+ fy="7.5016646"
+ id="radialGradient3823"
+ xlink:href="#linearGradient3827"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.4496779,1.5407764,-0.90127514,0.84794135,4.9906134,-4.9255796)" />
+ <radialGradient
+ cx="3.9371533"
+ cy="7.5016646"
+ r="2.5"
+ fx="3.9371533"
+ fy="7.5016646"
+ id="radialGradient3786"
+ xlink:href="#linearGradient3801"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.4496779,1.5407764,-0.90127514,0.84794135,4.9906134,-4.9255796)" />
+ </defs>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ transform="translate(0,-1020.3622)"
+ id="layer1">
+ <path
+ d="m 8,9.5 a 2.5,2.5 0 1 1 -5,0 2.5,2.5 0 1 1 5,0 z"
+ transform="matrix(6.4,0,0,6.4,-19.2,975.5622)"
+ id="path2997"
+ style="color:#000000;fill:url(#radialGradient3807);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ d="M 31,16 A 15,15 0 1 1 1,16 15,15 0 1 1 31,16 z"
+ transform="matrix(0.93333333,0,0,0.93333444,1.0666666,1021.4288)"
+ id="path2999"
+ style="color:#000000;fill:#009cff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <g
+ id="g4187">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4168"
+ d="m 10.455414,1028.3241 0,16.0761 L 24,1036.3622 Z"
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.95251006px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ style="opacity:0.54887217;fill:#ffffff;stroke:none"
+ id="path3809-3"
+ d="m 10.5,1044.3622 0,-16 13.5,8 z" />
+ </g>
+ </g>
+</svg>
diff --git a/application/widgets/PageContainer.cpp b/application/widgets/PageContainer.cpp
index 90aad26e..04da5c8b 100644
--- a/application/widgets/PageContainer.cpp
+++ b/application/widgets/PageContainer.cpp
@@ -60,7 +60,6 @@ PageContainer::PageContainer(BasePageProviderPtr pageProvider, QString defaultId
createUI();
m_model = new PageModel(this);
m_proxyModel = new PageEntryFilterModel(this);
- int firstIndex = -1;
int counter = 0;
auto pages = pageProvider->getPages();
for (auto page : pages)
@@ -69,10 +68,6 @@ PageContainer::PageContainer(BasePageProviderPtr pageProvider, QString defaultId
page->listIndex = counter;
page->setParentContainer(this);
counter++;
- if (firstIndex == -1)
- {
- firstIndex = page->stackIndex;
- }
}
m_model->setPages(pages);
@@ -111,6 +106,23 @@ bool PageContainer::selectPage(QString pageId)
return false;
}
+void PageContainer::refresh()
+{
+ m_proxyModel->invalidate();
+ if(!m_currentPage->shouldDisplay())
+ {
+ auto index = m_proxyModel->index(0, 0);
+ if(index.isValid())
+ {
+ m_pageList->setCurrentIndex(index);
+ }
+ else
+ {
+ // FIXME: unhandled corner case: what to do when there's no page to select?
+ }
+ }
+}
+
void PageContainer::createUI()
{
m_pageStack = new QStackedLayout;
diff --git a/application/widgets/PageContainer.h b/application/widgets/PageContainer.h
index 381e84e5..84504fb6 100644
--- a/application/widgets/PageContainer.h
+++ b/application/widgets/PageContainer.h
@@ -45,6 +45,8 @@ public:
virtual bool selectPage(QString pageId) override;
+ void refresh();
+
private:
void createUI();
private