pollymc/launcher/ui/MainWindow.cpp
Kenneth Chew a549828655
Remove the Edit menu bar menu
It wouldn't bring much utility.

- The keyboard shortcuts for copy/paste/etc. already work and are well-known. The menu bar likely doesn't need to advertise them.
- There's not very many places you would be able to use these options in the main window (because there's not many places to type stuff in the main window). It would only be applicable on systems with a native menu bar that shows in all other windows as well (but again, the keyboard shortcuts still work).

Also, rename `actionWiki` -> `actionOpenWiki` to match the corresponding `on_actionOpenWiki_triggered`
2022-04-16 13:17:34 -04:00

2172 lines
75 KiB
C++

/* Copyright 2013-2021 MultiMC Contributors
*
* Authors: Andrew Okin
* Peterix
* Orochimarufan <orochimarufan.x3@gmail.com>
*
* 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 "Application.h"
#include "BuildConfig.h"
#include "MainWindow.h"
#include <QtCore/QVariant>
#include <QtCore/QUrl>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtGui/QKeyEvent>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QToolBar>
#include <QtWidgets/QWidget>
#include <QtWidgets/QMenu>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QLabel>
#include <QtWidgets/QToolButton>
#include <QtWidgets/QWidgetAction>
#include <QtWidgets/QProgressDialog>
#include <QtWidgets/QShortcut>
#include <BaseInstance.h>
#include <InstanceList.h>
#include <MMCZip.h>
#include <icons/IconList.h>
#include <java/JavaUtils.h>
#include <java/JavaInstallList.h>
#include <launch/LaunchTask.h>
#include <minecraft/auth/AccountList.h>
#include <SkinUtils.h>
#include <BuildConfig.h>
#include <net/NetJob.h>
#include <net/Download.h>
#include <news/NewsChecker.h>
#include <tools/BaseProfiler.h>
#include <updater/DownloadTask.h>
#include <updater/UpdateChecker.h>
#include <DesktopServices.h>
#include "InstanceWindow.h"
#include "InstancePageProvider.h"
#include "JavaCommon.h"
#include "LaunchController.h"
#include "ui/instanceview/InstanceProxyModel.h"
#include "ui/instanceview/InstanceView.h"
#include "ui/instanceview/InstanceDelegate.h"
#include "ui/widgets/LabeledToolButton.h"
#include "ui/dialogs/NewInstanceDialog.h"
#include "ui/dialogs/ProgressDialog.h"
#include "ui/dialogs/AboutDialog.h"
#include "ui/dialogs/VersionSelectDialog.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/IconPickerDialog.h"
#include "ui/dialogs/CopyInstanceDialog.h"
#include "ui/dialogs/UpdateDialog.h"
#include "ui/dialogs/EditAccountDialog.h"
#include "ui/dialogs/ExportInstanceDialog.h"
#include "UpdateController.h"
#include "KonamiCode.h"
#include "InstanceImportTask.h"
#include "InstanceCopyTask.h"
#include "MMCTime.h"
namespace {
QString profileInUseFilter(const QString & profile, bool used)
{
if(used)
{
return QObject::tr("%1 (in use)").arg(profile);
}
else
{
return profile;
}
}
}
// WHY: to hold the pre-translation strings together with the T pointer, so it can be retranslated without a lot of ugly code
template <typename T>
class Translated
{
public:
Translated(){}
Translated(QWidget *parent)
{
m_contained = new T(parent);
}
void setTooltipId(const char * tooltip)
{
m_tooltip = tooltip;
}
void setTextId(const char * text)
{
m_text = text;
}
operator T*()
{
return m_contained;
}
T * operator->()
{
return m_contained;
}
void retranslate()
{
if(m_text)
{
QString result;
result = QApplication::translate("MainWindow", m_text);
if(result.contains("%1")) {
result = result.arg(BuildConfig.LAUNCHER_NAME);
}
m_contained->setText(result);
}
if(m_tooltip)
{
QString result;
result = QApplication::translate("MainWindow", m_tooltip);
if(result.contains("%1")) {
result = result.arg(BuildConfig.LAUNCHER_NAME);
}
m_contained->setToolTip(result);
}
}
private:
T * m_contained = nullptr;
const char * m_text = nullptr;
const char * m_tooltip = nullptr;
};
using TranslatedAction = Translated<QAction>;
using TranslatedToolButton = Translated<QToolButton>;
class TranslatedToolbar
{
public:
TranslatedToolbar(){}
TranslatedToolbar(QWidget *parent)
{
m_contained = new QToolBar(parent);
}
void setWindowTitleId(const char * title)
{
m_title = title;
}
operator QToolBar*()
{
return m_contained;
}
QToolBar * operator->()
{
return m_contained;
}
void retranslate()
{
if(m_title)
{
m_contained->setWindowTitle(QApplication::translate("MainWindow", m_title));
}
}
private:
QToolBar * m_contained = nullptr;
const char * m_title = nullptr;
};
class MainWindow::Ui
{
public:
TranslatedAction actionAddInstance;
//TranslatedAction actionRefresh;
TranslatedAction actionCheckUpdate;
TranslatedAction actionSettings;
TranslatedAction actionMoreNews;
TranslatedAction actionManageAccounts;
TranslatedAction actionLaunchInstance;
TranslatedAction actionRenameInstance;
TranslatedAction actionChangeInstGroup;
TranslatedAction actionChangeInstIcon;
TranslatedAction actionEditInstNotes;
TranslatedAction actionEditInstance;
TranslatedAction actionWorlds;
TranslatedAction actionMods;
TranslatedAction actionViewSelectedInstFolder;
TranslatedAction actionViewSelectedMCFolder;
TranslatedAction actionViewSelectedModsFolder;
TranslatedAction actionDeleteInstance;
TranslatedAction actionConfig_Folder;
TranslatedAction actionCAT;
TranslatedAction actionCopyInstance;
TranslatedAction actionLaunchInstanceOffline;
TranslatedAction actionScreenshots;
TranslatedAction actionExportInstance;
QVector<TranslatedAction *> all_actions;
LabeledToolButton *renameButton = nullptr;
LabeledToolButton *changeIconButton = nullptr;
QMenu * foldersMenu = nullptr;
TranslatedToolButton foldersMenuButton;
TranslatedAction actionViewInstanceFolder;
TranslatedAction actionViewCentralModsFolder;
QMenu * helpMenu = nullptr;
TranslatedToolButton helpMenuButton;
TranslatedAction actionReportBug;
TranslatedAction actionDISCORD;
TranslatedAction actionMATRIX;
TranslatedAction actionREDDIT;
TranslatedAction actionAbout;
QVector<TranslatedToolButton *> all_toolbuttons;
QWidget *centralWidget = nullptr;
QHBoxLayout *horizontalLayout = nullptr;
QStatusBar *statusBar = nullptr;
QMenuBar *menuBar = nullptr;
QMenu *fileMenu;
QMenu *viewMenu;
QMenu *profileMenu;
QAction *actionCloseWindow;
QAction *actionOpenWiki;
QAction *actionNewsMenuBar;
TranslatedToolbar mainToolBar;
TranslatedToolbar instanceToolBar;
TranslatedToolbar newsToolBar;
QVector<TranslatedToolbar *> all_toolbars;
bool m_kill = false;
void updateLaunchAction()
{
if(m_kill)
{
actionLaunchInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Kill"));
actionLaunchInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Kill the running instance"));
}
else
{
actionLaunchInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Launch"));
actionLaunchInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Launch the selected instance."));
}
actionLaunchInstance.retranslate();
}
void setLaunchAction(bool kill)
{
m_kill = kill;
updateLaunchAction();
}
void createMainToolbarActions(QMainWindow *MainWindow)
{
actionAddInstance = TranslatedAction(MainWindow);
actionAddInstance->setObjectName(QStringLiteral("actionAddInstance"));
actionAddInstance->setIcon(APPLICATION->getThemedIcon("new"));
actionAddInstance->setIconVisibleInMenu(false);
actionAddInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Add Instanc&e..."));
actionAddInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Add a new instance."));
actionAddInstance->setShortcut(QKeySequence::New);
all_actions.append(&actionAddInstance);
actionViewInstanceFolder = TranslatedAction(MainWindow);
actionViewInstanceFolder->setObjectName(QStringLiteral("actionViewInstanceFolder"));
actionViewInstanceFolder->setIcon(APPLICATION->getThemedIcon("viewfolder"));
actionViewInstanceFolder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "View Instance Folder"));
actionViewInstanceFolder.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the instance folder in a file browser."));
all_actions.append(&actionViewInstanceFolder);
actionViewCentralModsFolder = TranslatedAction(MainWindow);
actionViewCentralModsFolder->setObjectName(QStringLiteral("actionViewCentralModsFolder"));
actionViewCentralModsFolder->setIcon(APPLICATION->getThemedIcon("centralmods"));
actionViewCentralModsFolder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "View Central Mods Folder"));
actionViewCentralModsFolder.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the central mods folder in a file browser."));
all_actions.append(&actionViewCentralModsFolder);
foldersMenu = new QMenu(MainWindow);
foldersMenu->setTitle(tr("F&olders"));
foldersMenu->setToolTipsVisible(true);
foldersMenu->addAction(actionViewInstanceFolder);
foldersMenu->addAction(actionViewCentralModsFolder);
foldersMenuButton = TranslatedToolButton(MainWindow);
foldersMenuButton.setTextId(QT_TRANSLATE_NOOP("MainWindow", "F&olders"));
foldersMenuButton.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open one of the folders shared between instances."));
foldersMenuButton->setMenu(foldersMenu);
foldersMenuButton->setPopupMode(QToolButton::InstantPopup);
foldersMenuButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
foldersMenuButton->setIcon(APPLICATION->getThemedIcon("viewfolder"));
foldersMenuButton->setFocusPolicy(Qt::NoFocus);
all_toolbuttons.append(&foldersMenuButton);
actionSettings = TranslatedAction(MainWindow);
actionSettings->setObjectName(QStringLiteral("actionSettings"));
actionSettings->setIcon(APPLICATION->getThemedIcon("settings"));
actionSettings->setMenuRole(QAction::PreferencesRole);
actionSettings.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Settings..."));
actionSettings.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Change settings."));
actionSettings->setShortcut(QKeySequence::Preferences);
all_actions.append(&actionSettings);
if (!BuildConfig.BUG_TRACKER_URL.isEmpty()) {
actionReportBug = TranslatedAction(MainWindow);
actionReportBug->setObjectName(QStringLiteral("actionReportBug"));
actionReportBug->setIcon(APPLICATION->getThemedIcon("bug"));
actionReportBug.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Report a &Bug..."));
actionReportBug.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the bug tracker to report a bug with %1."));
all_actions.append(&actionReportBug);
}
if(!BuildConfig.MATRIX_URL.isEmpty()) {
actionMATRIX = TranslatedAction(MainWindow);
actionMATRIX->setObjectName(QStringLiteral("actionMATRIX"));
actionMATRIX->setIcon(APPLICATION->getThemedIcon("matrix"));
actionMATRIX.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Matrix Space"));
actionMATRIX.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open %1 Matrix space"));
all_actions.append(&actionMATRIX);
}
if (!BuildConfig.DISCORD_URL.isEmpty()) {
actionDISCORD = TranslatedAction(MainWindow);
actionDISCORD->setObjectName(QStringLiteral("actionDISCORD"));
actionDISCORD->setIcon(APPLICATION->getThemedIcon("discord"));
actionDISCORD.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Discord Guild"));
actionDISCORD.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open %1 Discord guild."));
all_actions.append(&actionDISCORD);
}
if (!BuildConfig.SUBREDDIT_URL.isEmpty()) {
actionREDDIT = TranslatedAction(MainWindow);
actionREDDIT->setObjectName(QStringLiteral("actionREDDIT"));
actionREDDIT->setIcon(APPLICATION->getThemedIcon("reddit-alien"));
actionREDDIT.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Sub&reddit"));
actionREDDIT.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open %1 subreddit."));
all_actions.append(&actionREDDIT);
}
actionAbout = TranslatedAction(MainWindow);
actionAbout->setObjectName(QStringLiteral("actionAbout"));
actionAbout->setIcon(APPLICATION->getThemedIcon("about"));
actionAbout->setMenuRole(QAction::AboutRole);
actionAbout.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&About %1"));
actionAbout.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "View information about %1."));
all_actions.append(&actionAbout);
if(BuildConfig.UPDATER_ENABLED)
{
actionCheckUpdate = TranslatedAction(MainWindow);
actionCheckUpdate->setObjectName(QStringLiteral("actionCheckUpdate"));
actionCheckUpdate->setIcon(APPLICATION->getThemedIcon("checkupdate"));
actionCheckUpdate.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Update..."));
actionCheckUpdate.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Check for new updates for %1."));
actionCheckUpdate->setMenuRole(QAction::ApplicationSpecificRole);
all_actions.append(&actionCheckUpdate);
}
actionCAT = TranslatedAction(MainWindow);
actionCAT->setObjectName(QStringLiteral("actionCAT"));
actionCAT->setCheckable(true);
actionCAT->setIcon(APPLICATION->getThemedIcon("cat"));
actionCAT.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Meow"));
actionCAT.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "It's a fluffy kitty :3"));
actionCAT->setPriority(QAction::LowPriority);
all_actions.append(&actionCAT);
// profile menu and its actions
actionManageAccounts = TranslatedAction(MainWindow);
actionManageAccounts->setObjectName(QStringLiteral("actionManageAccounts"));
actionManageAccounts.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Manage Accounts..."));
// FIXME: no tooltip!
actionManageAccounts->setCheckable(false);
actionManageAccounts->setIcon(APPLICATION->getThemedIcon("accounts"));
all_actions.append(&actionManageAccounts);
}
void createMainToolbar(QMainWindow *MainWindow)
{
mainToolBar = TranslatedToolbar(MainWindow);
mainToolBar->setVisible(menuBar->isNativeMenuBar() || !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool());
mainToolBar->setObjectName(QStringLiteral("mainToolBar"));
mainToolBar->setMovable(true);
mainToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
mainToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
mainToolBar->setFloatable(false);
mainToolBar.setWindowTitleId(QT_TRANSLATE_NOOP("MainWindow", "Main Toolbar"));
mainToolBar->addAction(actionAddInstance);
mainToolBar->addSeparator();
QWidgetAction* foldersButtonAction = new QWidgetAction(MainWindow);
foldersButtonAction->setDefaultWidget(foldersMenuButton);
mainToolBar->addAction(foldersButtonAction);
mainToolBar->addAction(actionSettings);
helpMenu = new QMenu(MainWindow);
helpMenu->setToolTipsVisible(true);
if (!BuildConfig.BUG_TRACKER_URL.isEmpty()) {
helpMenu->addAction(actionReportBug);
}
if(!BuildConfig.MATRIX_URL.isEmpty()) {
helpMenu->addAction(actionMATRIX);
}
if (!BuildConfig.DISCORD_URL.isEmpty()) {
helpMenu->addAction(actionDISCORD);
}
if (!BuildConfig.SUBREDDIT_URL.isEmpty()) {
helpMenu->addAction(actionREDDIT);
}
helpMenu->addAction(actionAbout);
helpMenuButton = TranslatedToolButton(MainWindow);
helpMenuButton.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Help"));
helpMenuButton.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Get help with %1 or Minecraft."));
helpMenuButton->setMenu(helpMenu);
helpMenuButton->setPopupMode(QToolButton::InstantPopup);
helpMenuButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
helpMenuButton->setIcon(APPLICATION->getThemedIcon("help"));
helpMenuButton->setFocusPolicy(Qt::NoFocus);
all_toolbuttons.append(&helpMenuButton);
QWidgetAction* helpButtonAction = new QWidgetAction(MainWindow);
helpButtonAction->setDefaultWidget(helpMenuButton);
mainToolBar->addAction(helpButtonAction);
if(BuildConfig.UPDATER_ENABLED)
{
mainToolBar->addAction(actionCheckUpdate);
}
mainToolBar->addSeparator();
mainToolBar->addAction(actionCAT);
all_toolbars.append(&mainToolBar);
MainWindow->addToolBar(Qt::TopToolBarArea, mainToolBar);
}
void createMenuBar(MainWindow *MainWindow)
{
menuBar = new QMenuBar(MainWindow);
menuBar->setVisible(APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool());
createMenuActions(MainWindow);
fileMenu = menuBar->addMenu(tr("&File"));
fileMenu->addAction(actionAddInstance);
fileMenu->addAction(actionLaunchInstance);
fileMenu->addAction(actionLaunchInstanceOffline);
fileMenu->addAction(actionCloseWindow);
fileMenu->addSeparator();
fileMenu->addAction(actionEditInstance);
fileMenu->addAction(actionEditInstNotes);
fileMenu->addAction(actionMods);
fileMenu->addAction(actionWorlds);
fileMenu->addAction(actionScreenshots);
fileMenu->addAction(actionChangeInstGroup);
fileMenu->addSeparator();
fileMenu->addAction(actionViewSelectedMCFolder);
fileMenu->addAction(actionConfig_Folder);
fileMenu->addAction(actionViewSelectedInstFolder);
fileMenu->addSeparator();
fileMenu->addAction(actionExportInstance);
fileMenu->addAction(actionDeleteInstance);
fileMenu->addAction(actionCopyInstance);
fileMenu->addSeparator();
fileMenu->addAction(actionSettings);
viewMenu = menuBar->addMenu(tr("&View"));
viewMenu->addAction(actionCAT);
viewMenu->addSeparator();
menuBar->addMenu(foldersMenu);
profileMenu = menuBar->addMenu(tr("&Profiles"));
profileMenu->addAction(actionManageAccounts);
helpMenu = menuBar->addMenu(tr("&Help"));
helpMenu->addAction(actionAbout);
helpMenu->addAction(actionOpenWiki);
helpMenu->addAction(actionNewsMenuBar);
helpMenu->addSeparator();
if (!BuildConfig.BUG_TRACKER_URL.isEmpty())
helpMenu->addAction(actionReportBug);
if (!BuildConfig.MATRIX_URL.isEmpty())
helpMenu->addAction(actionMATRIX);
if (!BuildConfig.DISCORD_URL.isEmpty())
helpMenu->addAction(actionDISCORD);
if (!BuildConfig.SUBREDDIT_URL.isEmpty())
helpMenu->addAction(actionREDDIT);
helpMenu->addSeparator();
if(BuildConfig.UPDATER_ENABLED)
helpMenu->addAction(actionCheckUpdate);
MainWindow->setMenuBar(menuBar);
}
void createMenuActions(MainWindow *MainWindow)
{
actionCloseWindow = new QAction(tr("Close &Window"), MainWindow);
actionCloseWindow->setShortcut(QKeySequence::Close);
actionCloseWindow->setStatusTip(tr("Close the current window"));
connect(actionCloseWindow, &QAction::triggered, APPLICATION, &Application::closeCurrentWindow);
actionOpenWiki = new QAction(tr("%1 He&lp").arg(BuildConfig.LAUNCHER_NAME), MainWindow);
actionOpenWiki->setStatusTip(tr("Open the %1 wiki").arg(BuildConfig.LAUNCHER_NAME));
connect(actionOpenWiki, &QAction::triggered, MainWindow, &MainWindow::on_actionOpenWiki_triggered);
actionNewsMenuBar = new QAction(tr("&%1 &News").arg(BuildConfig.LAUNCHER_NAME), MainWindow);
actionNewsMenuBar->setStatusTip(tr("Open the development blog to read more news about %1.").arg(BuildConfig.LAUNCHER_NAME));
connect(actionNewsMenuBar, &QAction::triggered, MainWindow, &MainWindow::on_actionMoreNews_triggered);
}
// "Instance actions" are actions that require an instance to be selected (i.e. "new instance" is not here)
void setInstanceActionsEnabled(bool enabled)
{
actionLaunchInstance->setEnabled(enabled);
actionLaunchInstanceOffline->setEnabled(enabled);
actionEditInstance->setEnabled(enabled);
actionEditInstNotes->setEnabled(enabled);
actionMods->setEnabled(enabled);
actionWorlds->setEnabled(enabled);
actionScreenshots->setEnabled(enabled);
actionChangeInstGroup->setEnabled(enabled);
actionViewSelectedMCFolder->setEnabled(enabled);
actionConfig_Folder->setEnabled(enabled);
actionViewSelectedInstFolder->setEnabled(enabled);
actionExportInstance->setEnabled(enabled);
actionDeleteInstance->setEnabled(enabled);
actionCopyInstance->setEnabled(enabled);
}
void createStatusBar(QMainWindow *MainWindow)
{
statusBar = new QStatusBar(MainWindow);
statusBar->setObjectName(QStringLiteral("statusBar"));
MainWindow->setStatusBar(statusBar);
}
void createNewsToolbar(QMainWindow *MainWindow)
{
newsToolBar = TranslatedToolbar(MainWindow);
newsToolBar->setObjectName(QStringLiteral("newsToolBar"));
newsToolBar->setMovable(true);
newsToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
newsToolBar->setIconSize(QSize(16, 16));
newsToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
newsToolBar->setFloatable(false);
newsToolBar->setWindowTitle(QT_TRANSLATE_NOOP("MainWindow", "News Toolbar"));
actionMoreNews = TranslatedAction(MainWindow);
actionMoreNews->setObjectName(QStringLiteral("actionMoreNews"));
actionMoreNews->setIcon(APPLICATION->getThemedIcon("news"));
actionMoreNews.setTextId(QT_TRANSLATE_NOOP("MainWindow", "More news..."));
actionMoreNews.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the development blog to read more news about %1."));
all_actions.append(&actionMoreNews);
newsToolBar->addAction(actionMoreNews);
all_toolbars.append(&newsToolBar);
MainWindow->addToolBar(Qt::BottomToolBarArea, newsToolBar);
}
void createInstanceActions(QMainWindow *MainWindow)
{
// NOTE: not added to toolbar, but used for instance context menu (right click)
actionChangeInstIcon = TranslatedAction(MainWindow);
actionChangeInstIcon->setObjectName(QStringLiteral("actionChangeInstIcon"));
actionChangeInstIcon->setIcon(QIcon(":/icons/instances/grass"));
actionChangeInstIcon->setIconVisibleInMenu(true);
actionChangeInstIcon.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Change Icon"));
actionChangeInstIcon.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Change the selected instance's icon."));
all_actions.append(&actionChangeInstIcon);
changeIconButton = new LabeledToolButton(MainWindow);
changeIconButton->setObjectName(QStringLiteral("changeIconButton"));
changeIconButton->setIcon(APPLICATION->getThemedIcon("news"));
changeIconButton->setToolTip(actionChangeInstIcon->toolTip());
changeIconButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
// NOTE: not added to toolbar, but used for instance context menu (right click)
actionRenameInstance = TranslatedAction(MainWindow);
actionRenameInstance->setObjectName(QStringLiteral("actionRenameInstance"));
actionRenameInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Rename"));
actionRenameInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Rename the selected instance."));
all_actions.append(&actionRenameInstance);
// the rename label is inside the rename tool button
renameButton = new LabeledToolButton(MainWindow);
renameButton->setObjectName(QStringLiteral("renameButton"));
renameButton->setToolTip(actionRenameInstance->toolTip());
renameButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
actionLaunchInstance = TranslatedAction(MainWindow);
actionLaunchInstance->setObjectName(QStringLiteral("actionLaunchInstance"));
all_actions.append(&actionLaunchInstance);
actionLaunchInstanceOffline = TranslatedAction(MainWindow);
actionLaunchInstanceOffline->setObjectName(QStringLiteral("actionLaunchInstanceOffline"));
actionLaunchInstanceOffline.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Launch &Offline"));
actionLaunchInstanceOffline.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Launch the selected instance in offline mode."));
all_actions.append(&actionLaunchInstanceOffline);
actionEditInstance = TranslatedAction(MainWindow);
actionEditInstance->setObjectName(QStringLiteral("actionEditInstance"));
actionEditInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Edit Inst&ance..."));
actionEditInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Change the instance settings, mods and versions."));
actionEditInstance->setShortcut(QKeySequence(tr("Ctrl+I")));
all_actions.append(&actionEditInstance);
actionEditInstNotes = TranslatedAction(MainWindow);
actionEditInstNotes->setObjectName(QStringLiteral("actionEditInstNotes"));
actionEditInstNotes.setTextId(QT_TRANSLATE_NOOP("MainWindow", "E&dit Notes..."));
actionEditInstNotes.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Edit the notes for the selected instance."));
all_actions.append(&actionEditInstNotes);
actionMods = TranslatedAction(MainWindow);
actionMods->setObjectName(QStringLiteral("actionMods"));
actionMods.setTextId(QT_TRANSLATE_NOOP("MainWindow", "View &Mods"));
actionMods.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "View the mods of this instance."));
all_actions.append(&actionMods);
actionWorlds = TranslatedAction(MainWindow);
actionWorlds->setObjectName(QStringLiteral("actionWorlds"));
actionWorlds.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&View Worlds"));
actionWorlds.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "View the worlds of this instance."));
all_actions.append(&actionWorlds);
actionScreenshots = TranslatedAction(MainWindow);
actionScreenshots->setObjectName(QStringLiteral("actionScreenshots"));
actionScreenshots.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Manage &Screenshots"));
actionScreenshots.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "View and upload screenshots for this instance."));
all_actions.append(&actionScreenshots);
actionChangeInstGroup = TranslatedAction(MainWindow);
actionChangeInstGroup->setObjectName(QStringLiteral("actionChangeInstGroup"));
actionChangeInstGroup.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Change Group..."));
actionChangeInstGroup.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Change the selected instance's group."));
actionChangeInstGroup->setShortcut(QKeySequence(tr("Ctrl+G")));
all_actions.append(&actionChangeInstGroup);
actionViewSelectedMCFolder = TranslatedAction(MainWindow);
actionViewSelectedMCFolder->setObjectName(QStringLiteral("actionViewSelectedMCFolder"));
actionViewSelectedMCFolder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Minec&raft Folder"));
actionViewSelectedMCFolder.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the selected instance's Minecraft folder in a file browser."));
actionViewSelectedMCFolder->setShortcut(QKeySequence(tr("Ctrl+M")));
all_actions.append(&actionViewSelectedMCFolder);
/*
actionViewSelectedModsFolder = TranslatedAction(MainWindow);
actionViewSelectedModsFolder->setObjectName(QStringLiteral("actionViewSelectedModsFolder"));
actionViewSelectedModsFolder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Mods Folder"));
actionViewSelectedModsFolder.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the selected instance's mods folder in a file browser."));
all_actions.append(&actionViewSelectedModsFolder);
*/
actionConfig_Folder = TranslatedAction(MainWindow);
actionConfig_Folder->setObjectName(QStringLiteral("actionConfig_Folder"));
actionConfig_Folder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Confi&g Folder"));
actionConfig_Folder.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the instance's config folder."));
// Qt on macOS is "smart" and will eat up this action when added to the menu bar because it starts with the word "config"...
// Docs: https://doc.qt.io/qt-5/qmenubar.html#qmenubar-as-a-global-menu-bar
actionConfig_Folder->setMenuRole(QAction::NoRole);
all_actions.append(&actionConfig_Folder);
actionViewSelectedInstFolder = TranslatedAction(MainWindow);
actionViewSelectedInstFolder->setObjectName(QStringLiteral("actionViewSelectedInstFolder"));
actionViewSelectedInstFolder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Instance Folder"));
actionViewSelectedInstFolder.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the selected instance's root folder in a file browser."));
all_actions.append(&actionViewSelectedInstFolder);
actionExportInstance = TranslatedAction(MainWindow);
actionExportInstance->setObjectName(QStringLiteral("actionExportInstance"));
actionExportInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "E&xport Instance..."));
actionExportInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Export the selected instance as a zip file."));
actionExportInstance->setShortcut(QKeySequence(tr("Ctrl+E")));
all_actions.append(&actionExportInstance);
actionDeleteInstance = TranslatedAction(MainWindow);
actionDeleteInstance->setObjectName(QStringLiteral("actionDeleteInstance"));
actionDeleteInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Dele&te Instance..."));
actionDeleteInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Delete the selected instance."));
actionDeleteInstance->setShortcuts({QKeySequence(tr("Backspace")), QKeySequence::Delete});
all_actions.append(&actionDeleteInstance);
actionCopyInstance = TranslatedAction(MainWindow);
actionCopyInstance->setObjectName(QStringLiteral("actionCopyInstance"));
actionCopyInstance->setIcon(APPLICATION->getThemedIcon("copy"));
actionCopyInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Cop&y Instance..."));
actionCopyInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Copy the selected instance."));
actionCopyInstance->setShortcut(QKeySequence(tr("Ctrl+D")));
all_actions.append(&actionCopyInstance);
}
void createInstanceToolbar(QMainWindow *MainWindow)
{
instanceToolBar = TranslatedToolbar(MainWindow);
instanceToolBar->setObjectName(QStringLiteral("instanceToolBar"));
// disabled until we have an instance selected
instanceToolBar->setEnabled(false);
instanceToolBar->setMovable(true);
instanceToolBar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);
instanceToolBar->setToolButtonStyle(Qt::ToolButtonTextOnly);
instanceToolBar->setFloatable(false);
instanceToolBar->setWindowTitle(QT_TRANSLATE_NOOP("MainWindow", "Instance Toolbar"));
instanceToolBar->addWidget(changeIconButton);
instanceToolBar->addWidget(renameButton);
instanceToolBar->addSeparator();
instanceToolBar->addAction(actionLaunchInstance);
instanceToolBar->addAction(actionLaunchInstanceOffline);
instanceToolBar->addSeparator();
instanceToolBar->addAction(actionEditInstance);
instanceToolBar->addAction(actionEditInstNotes);
instanceToolBar->addAction(actionMods);
instanceToolBar->addAction(actionWorlds);
instanceToolBar->addAction(actionScreenshots);
instanceToolBar->addAction(actionChangeInstGroup);
instanceToolBar->addSeparator();
instanceToolBar->addAction(actionViewSelectedMCFolder);
/*
instanceToolBar->addAction(actionViewSelectedModsFolder);
*/
instanceToolBar->addAction(actionConfig_Folder);
instanceToolBar->addAction(actionViewSelectedInstFolder);
instanceToolBar->addSeparator();
instanceToolBar->addAction(actionExportInstance);
instanceToolBar->addAction(actionDeleteInstance);
instanceToolBar->addAction(actionCopyInstance);
all_toolbars.append(&instanceToolBar);
MainWindow->addToolBar(Qt::RightToolBarArea, instanceToolBar);
}
void setupUi(QMainWindow *MainWindow)
{
if (MainWindow->objectName().isEmpty())
{
MainWindow->setObjectName(QStringLiteral("MainWindow"));
}
MainWindow->resize(800, 600);
MainWindow->setWindowIcon(APPLICATION->getThemedIcon("logo"));
MainWindow->setWindowTitle(BuildConfig.LAUNCHER_DISPLAYNAME);
#ifndef QT_NO_ACCESSIBILITY
MainWindow->setAccessibleName(BuildConfig.LAUNCHER_NAME);
#endif
createMainToolbarActions(MainWindow);
createInstanceActions(MainWindow);
createMenuBar(dynamic_cast<class MainWindow *>(MainWindow));
createMainToolbar(MainWindow);
centralWidget = new QWidget(MainWindow);
centralWidget->setObjectName(QStringLiteral("centralWidget"));
horizontalLayout = new QHBoxLayout(centralWidget);
horizontalLayout->setSpacing(0);
horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
horizontalLayout->setSizeConstraint(QLayout::SetDefaultConstraint);
horizontalLayout->setContentsMargins(0, 0, 0, 0);
MainWindow->setCentralWidget(centralWidget);
createStatusBar(MainWindow);
createNewsToolbar(MainWindow);
createInstanceToolbar(MainWindow);
retranslateUi(MainWindow);
QMetaObject::connectSlotsByName(MainWindow);
} // setupUi
void retranslateUi(QMainWindow *MainWindow)
{
QString winTitle = tr("%1 - Version %2", "Launcher - Version X").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString());
if (!BuildConfig.BUILD_PLATFORM.isEmpty())
{
winTitle += tr(" on %1", "on platform, as in operating system").arg(BuildConfig.BUILD_PLATFORM);
}
MainWindow->setWindowTitle(winTitle);
// all the actions
for(auto * item: all_actions)
{
item->retranslate();
}
for(auto * item: all_toolbars)
{
item->retranslate();
}
for(auto * item: all_toolbuttons)
{
item->retranslate();
}
// submenu buttons
foldersMenuButton->setText(tr("Folders"));
helpMenuButton->setText(tr("Help"));
} // retranslateUi
};
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow::Ui)
{
ui->setupUi(this);
// OSX magic.
setUnifiedTitleAndToolBarOnMac(true);
// Global shortcuts
{
// FIXME: This is kinda weird. and bad. We need some kind of managed shutdown.
auto q = new QShortcut(QKeySequence::Quit, this);
connect(q, SIGNAL(activated()), qApp, SLOT(quit()));
}
// Konami Code
{
secretEventFilter = new KonamiCode(this);
connect(secretEventFilter, &KonamiCode::triggered, this, &MainWindow::konamiTriggered);
}
// Add the news label to the news toolbar.
{
m_newsChecker.reset(new NewsChecker(APPLICATION->network(), BuildConfig.NEWS_RSS_URL));
newsLabel = new QToolButton();
newsLabel->setIcon(APPLICATION->getThemedIcon("news"));
newsLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
newsLabel->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
newsLabel->setFocusPolicy(Qt::NoFocus);
ui->newsToolBar->insertWidget(ui->actionMoreNews, newsLabel);
QObject::connect(newsLabel, &QAbstractButton::clicked, this, &MainWindow::newsButtonClicked);
QObject::connect(m_newsChecker.get(), &NewsChecker::newsLoaded, this, &MainWindow::updateNewsLabel);
updateNewsLabel();
}
// Create the instance list widget
{
view = new InstanceView(ui->centralWidget);
view->setSelectionMode(QAbstractItemView::SingleSelection);
// FIXME: leaks ListViewDelegate
view->setItemDelegate(new ListViewDelegate(this));
view->setFrameShape(QFrame::NoFrame);
// do not show ugly blue border on the mac
view->setAttribute(Qt::WA_MacShowFocusRect, false);
view->installEventFilter(this);
view->setContextMenuPolicy(Qt::CustomContextMenu);
connect(view, &QWidget::customContextMenuRequested, this, &MainWindow::showInstanceContextMenu);
connect(view, &InstanceView::droppedURLs, this, &MainWindow::droppedURLs, Qt::QueuedConnection);
proxymodel = new InstanceProxyModel(this);
proxymodel->setSourceModel(APPLICATION->instances().get());
proxymodel->sort(0);
connect(proxymodel, &InstanceProxyModel::dataChanged, this, &MainWindow::instanceDataChanged);
view->setModel(proxymodel);
view->setSourceOfGroupCollapseStatus([](const QString & groupName)->bool {
return APPLICATION->instances()->isGroupCollapsed(groupName);
});
connect(view, &InstanceView::groupStateChanged, APPLICATION->instances().get(), &InstanceList::on_GroupStateChanged);
ui->horizontalLayout->addWidget(view);
}
// The cat background
{
bool cat_enable = APPLICATION->settings()->get("TheCat").toBool();
ui->actionCAT->setChecked(cat_enable);
// NOTE: calling the operator like that is an ugly hack to appease ancient gcc...
connect(ui->actionCAT.operator->(), SIGNAL(toggled(bool)), SLOT(onCatToggled(bool)));
setCatBackground(cat_enable);
}
// start instance when double-clicked
connect(view, &InstanceView::activated, this, &MainWindow::instanceActivated);
// track the selection -- update the instance toolbar
connect(view->selectionModel(), &QItemSelectionModel::currentChanged, this, &MainWindow::instanceChanged);
// track icon changes and update the toolbar!
connect(APPLICATION->icons().get(), &IconList::iconUpdated, this, &MainWindow::iconUpdated);
// model reset -> selection is invalid. All the instance pointers are wrong.
connect(APPLICATION->instances().get(), &InstanceList::dataIsInvalid, this, &MainWindow::selectionBad);
// handle newly added instances
connect(APPLICATION->instances().get(), &InstanceList::instanceSelectRequest, this, &MainWindow::instanceSelectRequest);
// When the global settings page closes, we want to know about it and update our state
connect(APPLICATION, &Application::globalSettingsClosed, this, &MainWindow::globalSettingsClosed);
m_statusLeft = new QLabel(tr("No instance selected"), this);
m_statusCenter = new QLabel(tr("Total playtime: 0s"), this);
statusBar()->addPermanentWidget(m_statusLeft, 1);
statusBar()->addPermanentWidget(m_statusCenter, 0);
// Add "manage accounts" button, right align
QWidget *spacer = new QWidget();
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
ui->mainToolBar->addWidget(spacer);
accountMenu = new QMenu(this);
repopulateAccountsMenu();
accountMenuButton = new QToolButton(this);
accountMenuButton->setMenu(accountMenu);
accountMenuButton->setPopupMode(QToolButton::InstantPopup);
accountMenuButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
accountMenuButton->setIcon(APPLICATION->getThemedIcon("noaccount"));
QWidgetAction *accountMenuButtonAction = new QWidgetAction(this);
accountMenuButtonAction->setDefaultWidget(accountMenuButton);
ui->mainToolBar->addAction(accountMenuButtonAction);
// Update the menu when the active account changes.
// Shouldn't have to use lambdas here like this, but if I don't, the compiler throws a fit.
// Template hell sucks...
connect(
APPLICATION->accounts().get(),
&AccountList::defaultAccountChanged,
[this] {
defaultAccountChanged();
}
);
connect(
APPLICATION->accounts().get(),
&AccountList::listChanged,
[this]
{
repopulateAccountsMenu();
}
);
// Show initial account
defaultAccountChanged();
// TODO: refresh accounts here?
// auto accounts = APPLICATION->accounts();
// load the news
{
m_newsChecker->reloadNews();
updateNewsLabel();
}
if(BuildConfig.UPDATER_ENABLED)
{
bool updatesAllowed = APPLICATION->updatesAreAllowed();
updatesAllowedChanged(updatesAllowed);
// NOTE: calling the operator like that is an ugly hack to appease ancient gcc...
connect(ui->actionCheckUpdate.operator->(), &QAction::triggered, this, &MainWindow::checkForUpdates);
// set up the updater object.
auto updater = APPLICATION->updateChecker();
connect(updater.get(), &UpdateChecker::updateAvailable, this, &MainWindow::updateAvailable);
connect(updater.get(), &UpdateChecker::noUpdateFound, this, &MainWindow::updateNotAvailable);
// if automatic update checks are allowed, start one.
if (APPLICATION->settings()->get("AutoUpdate").toBool() && updatesAllowed)
{
updater->checkForUpdate(APPLICATION->settings()->get("UpdateChannel").toString(), false);
}
}
setSelectedInstanceById(APPLICATION->settings()->get("SelectedInstance").toString());
// removing this looks stupid
view->setFocus();
retranslateUi();
}
// macOS always has a native menu bar, so these fixes are not applicable
// Other systems may or may not have a native menu bar (most do not - it seems like only Ubuntu Unity does)
#ifndef Q_OS_MAC
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
if(event->key()==Qt::Key_Alt && !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool())
ui->menuBar->setVisible(!ui->menuBar->isVisible());
else
QMainWindow::keyReleaseEvent(event);
}
#endif
void MainWindow::retranslateUi()
{
auto accounts = APPLICATION->accounts();
MinecraftAccountPtr defaultAccount = accounts->defaultAccount();
if(defaultAccount) {
auto profileLabel = profileInUseFilter(defaultAccount->profileName(), defaultAccount->isInUse());
accountMenuButton->setText(profileLabel);
}
else {
accountMenuButton->setText(tr("Profiles"));
}
if (m_selectedInstance) {
m_statusLeft->setText(m_selectedInstance->getStatusbarDescription());
} else {
m_statusLeft->setText(tr("No instance selected"));
}
ui->retranslateUi(this);
}
MainWindow::~MainWindow()
{
}
QMenu * MainWindow::createPopupMenu()
{
QMenu* filteredMenu = QMainWindow::createPopupMenu();
filteredMenu->removeAction( ui->mainToolBar->toggleViewAction() );
return filteredMenu;
}
void MainWindow::konamiTriggered()
{
qDebug() << "Super Secret Mode ACTIVATED!";
}
void MainWindow::showInstanceContextMenu(const QPoint &pos)
{
QList<QAction *> actions;
QAction *actionSep = new QAction("", this);
actionSep->setSeparator(true);
bool onInstance = view->indexAt(pos).isValid();
if (onInstance)
{
actions = ui->instanceToolBar->actions();
// replace the change icon widget with an actual action
actions.replace(0, ui->actionChangeInstIcon);
// replace the rename widget with an actual action
actions.replace(1, ui->actionRenameInstance);
// add header
actions.prepend(actionSep);
QAction *actionVoid = new QAction(m_selectedInstance->name(), this);
actionVoid->setEnabled(false);
actions.prepend(actionVoid);
}
else
{
auto group = view->groupNameAt(pos);
QAction *actionVoid = new QAction(BuildConfig.LAUNCHER_NAME, this);
actionVoid->setEnabled(false);
QAction *actionCreateInstance = new QAction(tr("Create instance"), this);
actionCreateInstance->setToolTip(ui->actionAddInstance->toolTip());
if(!group.isNull())
{
QVariantMap data;
data["group"] = group;
actionCreateInstance->setData(data);
}
connect(actionCreateInstance, SIGNAL(triggered(bool)), SLOT(on_actionAddInstance_triggered()));
actions.prepend(actionSep);
actions.prepend(actionVoid);
actions.append(actionCreateInstance);
if(!group.isNull())
{
QAction *actionDeleteGroup = new QAction(tr("Delete group '%1'").arg(group), this);
QVariantMap data;
data["group"] = group;
actionDeleteGroup->setData(data);
connect(actionDeleteGroup, SIGNAL(triggered(bool)), SLOT(deleteGroup()));
actions.append(actionDeleteGroup);
}
}
QMenu myMenu;
myMenu.addActions(actions);
/*
if (onInstance)
myMenu.setEnabled(m_selectedInstance->canLaunch());
*/
myMenu.exec(view->mapToGlobal(pos));
}
void MainWindow::updateMainToolBar()
{
ui->menuBar->setVisible(APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool());
ui->mainToolBar->setVisible(ui->menuBar->isNativeMenuBar() || !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool());
}
void MainWindow::updateToolsMenu()
{
QToolButton *launchButton = dynamic_cast<QToolButton*>(ui->instanceToolBar->widgetForAction(ui->actionLaunchInstance));
QToolButton *launchOfflineButton = dynamic_cast<QToolButton*>(ui->instanceToolBar->widgetForAction(ui->actionLaunchInstanceOffline));
if(!m_selectedInstance || m_selectedInstance->isRunning())
{
ui->actionLaunchInstance->setMenu(nullptr);
ui->actionLaunchInstanceOffline->setMenu(nullptr);
launchButton->setPopupMode(QToolButton::InstantPopup);
launchOfflineButton->setPopupMode(QToolButton::InstantPopup);
return;
}
QMenu *launchMenu = ui->actionLaunchInstance->menu();
QMenu *launchOfflineMenu = ui->actionLaunchInstanceOffline->menu();
launchButton->setPopupMode(QToolButton::MenuButtonPopup);
launchOfflineButton->setPopupMode(QToolButton::MenuButtonPopup);
if (launchMenu)
{
launchMenu->clear();
}
else
{
launchMenu = new QMenu(this);
}
if (launchOfflineMenu) {
launchOfflineMenu->clear();
}
else
{
launchOfflineMenu = new QMenu(this);
}
QAction *normalLaunch = launchMenu->addAction(tr("Launch"));
normalLaunch->setShortcut(QKeySequence::Open);
QAction *normalLaunchOffline = launchOfflineMenu->addAction(tr("Launch Offline"));
normalLaunchOffline->setShortcut(QKeySequence(tr("Ctrl+Shift+O")));
connect(normalLaunch, &QAction::triggered, [this]()
{
APPLICATION->launch(m_selectedInstance, true);
});
connect(normalLaunchOffline, &QAction::triggered, [this]()
{
APPLICATION->launch(m_selectedInstance, false);
});
QString profilersTitle = tr("Profilers");
launchMenu->addSeparator()->setText(profilersTitle);
launchOfflineMenu->addSeparator()->setText(profilersTitle);
for (auto profiler : APPLICATION->profilers().values())
{
QAction *profilerAction = launchMenu->addAction(profiler->name());
QAction *profilerOfflineAction = launchOfflineMenu->addAction(profiler->name());
QString error;
if (!profiler->check(&error))
{
profilerAction->setDisabled(true);
profilerOfflineAction->setDisabled(true);
QString profilerToolTip = tr("Profiler not setup correctly. Go into settings, \"External Tools\".");
profilerAction->setToolTip(profilerToolTip);
profilerOfflineAction->setToolTip(profilerToolTip);
}
else
{
connect(profilerAction, &QAction::triggered, [this, profiler]()
{
APPLICATION->launch(m_selectedInstance, true, profiler.get());
});
connect(profilerOfflineAction, &QAction::triggered, [this, profiler]()
{
APPLICATION->launch(m_selectedInstance, false, profiler.get());
});
}
}
ui->actionLaunchInstance->setMenu(launchMenu);
ui->actionLaunchInstanceOffline->setMenu(launchOfflineMenu);
}
void MainWindow::repopulateAccountsMenu()
{
accountMenu->clear();
ui->profileMenu->clear();
auto accounts = APPLICATION->accounts();
MinecraftAccountPtr defaultAccount = accounts->defaultAccount();
QString active_profileId = "";
if (defaultAccount)
{
// this can be called before accountMenuButton exists
if (accountMenuButton)
{
auto profileLabel = profileInUseFilter(defaultAccount->profileName(), defaultAccount->isInUse());
accountMenuButton->setText(profileLabel);
}
}
if (accounts->count() <= 0)
{
QAction *action = new QAction(tr("No accounts added!"), this);
action->setEnabled(false);
accountMenu->addAction(action);
ui->profileMenu->addAction(action);
}
else
{
// TODO: Nicer way to iterate?
for (int i = 0; i < accounts->count(); i++)
{
MinecraftAccountPtr account = accounts->at(i);
auto profileLabel = profileInUseFilter(account->profileName(), account->isInUse());
QAction *action = new QAction(profileLabel, this);
action->setData(i);
action->setCheckable(true);
if (defaultAccount == account)
{
action->setChecked(true);
}
auto face = account->getFace();
if(!face.isNull()) {
action->setIcon(face);
}
else {
action->setIcon(APPLICATION->getThemedIcon("noaccount"));
}
const int highestNumberKey = 9;
if(i<highestNumberKey)
{
action->setShortcut(QKeySequence(tr("Ctrl+%1").arg(i + 1)));
}
accountMenu->addAction(action);
ui->profileMenu->addAction(action);
connect(action, SIGNAL(triggered(bool)), SLOT(changeActiveAccount()));
}
}
accountMenu->addSeparator();
ui->profileMenu->addSeparator();
QAction *action = new QAction(tr("No Default Account"), this);
action->setCheckable(true);
action->setIcon(APPLICATION->getThemedIcon("noaccount"));
action->setData(-1);
action->setShortcut(QKeySequence(tr("Ctrl+0")));
if (!defaultAccount) {
action->setChecked(true);
}
accountMenu->addAction(action);
ui->profileMenu->addAction(action);
connect(action, SIGNAL(triggered(bool)), SLOT(changeActiveAccount()));
accountMenu->addSeparator();
ui->profileMenu->addSeparator();
accountMenu->addAction(ui->actionManageAccounts);
ui->profileMenu->addAction(ui->actionManageAccounts);
}
void MainWindow::updatesAllowedChanged(bool allowed)
{
if(!BuildConfig.UPDATER_ENABLED)
{
return;
}
ui->actionCheckUpdate->setEnabled(allowed);
}
/*
* Assumes the sender is a QAction
*/
void MainWindow::changeActiveAccount()
{
QAction *sAction = (QAction *)sender();
// Profile's associated Mojang username
if (sAction->data().type() != QVariant::Type::Int)
return;
QVariant data = sAction->data();
bool valid = false;
int index = data.toInt(&valid);
if(!valid) {
index = -1;
}
auto accounts = APPLICATION->accounts();
accounts->setDefaultAccount(index == -1 ? nullptr : accounts->at(index));
defaultAccountChanged();
}
void MainWindow::defaultAccountChanged()
{
repopulateAccountsMenu();
MinecraftAccountPtr account = APPLICATION->accounts()->defaultAccount();
// FIXME: this needs adjustment for MSA
if (account && account->profileName() != "")
{
auto profileLabel = profileInUseFilter(account->profileName(), account->isInUse());
accountMenuButton->setText(profileLabel);
auto face = account->getFace();
if(face.isNull()) {
accountMenuButton->setIcon(APPLICATION->getThemedIcon("noaccount"));
}
else {
accountMenuButton->setIcon(face);
}
return;
}
// Set the icon to the "no account" icon.
accountMenuButton->setIcon(APPLICATION->getThemedIcon("noaccount"));
accountMenuButton->setText(tr("Profiles"));
}
bool MainWindow::eventFilter(QObject *obj, QEvent *ev)
{
if (obj == view)
{
if (ev->type() == QEvent::KeyPress)
{
secretEventFilter->input(ev);
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
switch (keyEvent->key())
{
/*
case Qt::Key_Enter:
case Qt::Key_Return:
activateInstance(m_selectedInstance);
return true;
*/
case Qt::Key_Delete:
on_actionDeleteInstance_triggered();
return true;
case Qt::Key_F5:
refreshInstances();
return true;
case Qt::Key_F2:
on_actionRenameInstance_triggered();
return true;
default:
break;
}
}
}
return QMainWindow::eventFilter(obj, ev);
}
void MainWindow::updateNewsLabel()
{
if (m_newsChecker->isLoadingNews())
{
newsLabel->setText(tr("Loading news..."));
newsLabel->setEnabled(false);
}
else
{
QList<NewsEntryPtr> entries = m_newsChecker->getNewsEntries();
if (entries.length() > 0)
{
newsLabel->setText(entries[0]->title);
newsLabel->setEnabled(true);
}
else
{
newsLabel->setText(tr("No news available."));
newsLabel->setEnabled(false);
}
}
}
void MainWindow::updateAvailable(GoUpdate::Status status)
{
if(!APPLICATION->updatesAreAllowed())
{
updateNotAvailable();
return;
}
UpdateDialog dlg(true, this);
UpdateAction action = (UpdateAction)dlg.exec();
switch (action)
{
case UPDATE_LATER:
qDebug() << "Update will be installed later.";
break;
case UPDATE_NOW:
downloadUpdates(status);
break;
}
}
void MainWindow::updateNotAvailable()
{
UpdateDialog dlg(false, this);
dlg.exec();
}
QList<int> stringToIntList(const QString &string)
{
QStringList split = string.split(',', QString::SkipEmptyParts);
QList<int> out;
for (int i = 0; i < split.size(); ++i)
{
out.append(split.at(i).toInt());
}
return out;
}
QString intListToString(const QList<int> &list)
{
QStringList slist;
for (int i = 0; i < list.size(); ++i)
{
slist.append(QString::number(list.at(i)));
}
return slist.join(',');
}
void MainWindow::downloadUpdates(GoUpdate::Status status)
{
if(!APPLICATION->updatesAreAllowed())
{
return;
}
qDebug() << "Downloading updates.";
ProgressDialog updateDlg(this);
status.rootPath = APPLICATION->root();
auto dlPath = FS::PathCombine(APPLICATION->root(), "update", "XXXXXX");
if (!FS::ensureFilePathExists(dlPath))
{
CustomMessageBox::selectable(this, tr("Error"), tr("Couldn't create folder for update downloads:\n%1").arg(dlPath), QMessageBox::Warning)->show();
}
GoUpdate::DownloadTask updateTask(APPLICATION->network(), status, dlPath, &updateDlg);
// If the task succeeds, install the updates.
if (updateDlg.execWithTask(&updateTask))
{
/**
* NOTE: This disables launching instances until the update either succeeds (and this process exits)
* or the update fails (and the control leaves this scope).
*/
APPLICATION->updateIsRunning(true);
UpdateController update(this, APPLICATION->root(), updateTask.updateFilesDir(), updateTask.operations());
update.installUpdates();
APPLICATION->updateIsRunning(false);
}
else
{
CustomMessageBox::selectable(this, tr("Error"), updateTask.failReason(), QMessageBox::Warning)->show();
}
}
void MainWindow::onCatToggled(bool state)
{
setCatBackground(state);
APPLICATION->settings()->set("TheCat", state);
}
namespace {
template <typename T>
T non_stupid_abs(T in)
{
if (in < 0)
return -in;
return in;
}
}
void MainWindow::setCatBackground(bool enabled)
{
if (enabled)
{
QDateTime now = QDateTime::currentDateTime();
QDateTime birthday(QDate(now.date().year(), 11, 30), QTime(0, 0));
QDateTime xmas(QDate(now.date().year(), 12, 25), QTime(0, 0));
QString cat;
if(non_stupid_abs(now.daysTo(xmas)) <= 4) {
cat = "catmas";
}
else if (non_stupid_abs(now.daysTo(birthday)) <= 12) {
cat = "cattiversary";
}
else {
cat = "kitteh";
}
view->setStyleSheet(QString(R"(
InstanceView
{
background-image: url(:/backgrounds/%1);
background-attachment: fixed;
background-clip: padding;
background-position: top right;
background-repeat: none;
background-color:palette(base);
})").arg(cat));
}
else
{
view->setStyleSheet(QString());
}
}
void MainWindow::runModalTask(Task *task)
{
connect(task, &Task::failed, [this](QString reason)
{
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
});
connect(task, &Task::succeeded, [this, task]()
{
QStringList warnings = task->warnings();
if(warnings.count())
{
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show();
}
});
ProgressDialog loadDialog(this);
loadDialog.setSkipButton(true, tr("Abort"));
loadDialog.execWithTask(task);
}
void MainWindow::instanceFromInstanceTask(InstanceTask *rawTask)
{
unique_qobject_ptr<Task> task(APPLICATION->instances()->wrapInstanceTask(rawTask));
runModalTask(task.get());
}
void MainWindow::on_actionCopyInstance_triggered()
{
if (!m_selectedInstance)
return;
CopyInstanceDialog copyInstDlg(m_selectedInstance, this);
if (!copyInstDlg.exec())
return;
auto copyTask = new InstanceCopyTask(m_selectedInstance, copyInstDlg.shouldCopySaves(), copyInstDlg.shouldKeepPlaytime());
copyTask->setName(copyInstDlg.instName());
copyTask->setGroup(copyInstDlg.instGroup());
copyTask->setIcon(copyInstDlg.iconKey());
unique_qobject_ptr<Task> task(APPLICATION->instances()->wrapInstanceTask(copyTask));
runModalTask(task.get());
}
void MainWindow::finalizeInstance(InstancePtr inst)
{
view->updateGeometries();
setSelectedInstanceById(inst->id());
if (APPLICATION->accounts()->anyAccountIsValid())
{
ProgressDialog loadDialog(this);
auto update = inst->createUpdateTask(Net::Mode::Online);
connect(update.get(), &Task::failed, [this](QString reason)
{
QString error = QString("Instance load failed: %1").arg(reason);
CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show();
});
if(update)
{
loadDialog.setSkipButton(true, tr("Abort"));
loadDialog.execWithTask(update.get());
}
}
else
{
CustomMessageBox::selectable(
this,
tr("Error"),
tr("The launcher cannot download Minecraft or update instances unless you have at least "
"one account added.\nPlease add your Mojang or Minecraft account."),
QMessageBox::Warning
)->show();
}
}
void MainWindow::addInstance(QString url)
{
QString groupName;
do
{
QObject* obj = sender();
if(!obj)
break;
QAction *action = qobject_cast<QAction *>(obj);
if(!action)
break;
auto map = action->data().toMap();
if(!map.contains("group"))
break;
groupName = map["group"].toString();
} while(0);
if(groupName.isEmpty())
{
groupName = APPLICATION->settings()->get("LastUsedGroupForNewInstance").toString();
}
NewInstanceDialog newInstDlg(groupName, url, this);
if (!newInstDlg.exec())
return;
APPLICATION->settings()->set("LastUsedGroupForNewInstance", newInstDlg.instGroup());
InstanceTask * creationTask = newInstDlg.extractTask();
if(creationTask)
{
instanceFromInstanceTask(creationTask);
}
}
void MainWindow::on_actionAddInstance_triggered()
{
addInstance();
}
void MainWindow::droppedURLs(QList<QUrl> urls)
{
for(auto & url:urls)
{
if(url.isLocalFile())
{
addInstance(url.toLocalFile());
}
else
{
addInstance(url.toString());
}
// Only process one dropped file...
break;
}
}
void MainWindow::on_actionREDDIT_triggered()
{
DesktopServices::openUrl(QUrl(BuildConfig.SUBREDDIT_URL));
}
void MainWindow::on_actionDISCORD_triggered()
{
DesktopServices::openUrl(QUrl(BuildConfig.DISCORD_URL));
}
void MainWindow::on_actionMATRIX_triggered()
{
DesktopServices::openUrl(QUrl(BuildConfig.MATRIX_URL));
}
void MainWindow::on_actionChangeInstIcon_triggered()
{
if (!m_selectedInstance)
return;
IconPickerDialog dlg(this);
dlg.execWithSelection(m_selectedInstance->iconKey());
if (dlg.result() == QDialog::Accepted)
{
m_selectedInstance->setIconKey(dlg.selectedIconKey);
auto icon = APPLICATION->icons()->getIcon(dlg.selectedIconKey);
ui->actionChangeInstIcon->setIcon(icon);
ui->changeIconButton->setIcon(icon);
}
}
void MainWindow::iconUpdated(QString icon)
{
if (icon == m_currentInstIcon)
{
auto icon = APPLICATION->icons()->getIcon(m_currentInstIcon);
ui->actionChangeInstIcon->setIcon(icon);
ui->changeIconButton->setIcon(icon);
}
}
void MainWindow::updateInstanceToolIcon(QString new_icon)
{
m_currentInstIcon = new_icon;
auto icon = APPLICATION->icons()->getIcon(m_currentInstIcon);
ui->actionChangeInstIcon->setIcon(icon);
ui->changeIconButton->setIcon(icon);
}
void MainWindow::setSelectedInstanceById(const QString &id)
{
if (id.isNull())
return;
const QModelIndex index = APPLICATION->instances()->getInstanceIndexById(id);
if (index.isValid())
{
QModelIndex selectionIndex = proxymodel->mapFromSource(index);
view->selectionModel()->setCurrentIndex(selectionIndex, QItemSelectionModel::ClearAndSelect);
updateStatusCenter();
}
}
void MainWindow::on_actionChangeInstGroup_triggered()
{
if (!m_selectedInstance)
return;
bool ok = false;
InstanceId instId = m_selectedInstance->id();
QString name(APPLICATION->instances()->getInstanceGroup(instId));
auto groups = APPLICATION->instances()->getGroups();
groups.insert(0, "");
groups.sort(Qt::CaseInsensitive);
int foo = groups.indexOf(name);
name = QInputDialog::getItem(this, tr("Group name"), tr("Enter a new group name."), groups, foo, true, &ok);
name = name.simplified();
if (ok)
{
APPLICATION->instances()->setInstanceGroup(instId, name);
}
}
void MainWindow::deleteGroup()
{
QObject* obj = sender();
if(!obj)
return;
QAction *action = qobject_cast<QAction *>(obj);
if(!action)
return;
auto map = action->data().toMap();
if(!map.contains("group"))
return;
QString groupName = map["group"].toString();
if(!groupName.isEmpty())
{
auto reply = QMessageBox::question(this, tr("Delete group"), tr("Are you sure you want to delete the group %1?")
.arg(groupName), QMessageBox::Yes | QMessageBox::No);
if(reply == QMessageBox::Yes)
{
APPLICATION->instances()->deleteGroup(groupName);
}
}
}
void MainWindow::on_actionViewInstanceFolder_triggered()
{
QString str = APPLICATION->settings()->get("InstanceDir").toString();
DesktopServices::openDirectory(str);
}
void MainWindow::refreshInstances()
{
APPLICATION->instances()->loadList();
}
void MainWindow::on_actionViewCentralModsFolder_triggered()
{
DesktopServices::openDirectory(APPLICATION->settings()->get("CentralModsDir").toString(), true);
}
void MainWindow::on_actionConfig_Folder_triggered()
{
if (m_selectedInstance)
{
QString str = m_selectedInstance->instanceConfigFolder();
DesktopServices::openDirectory(QDir(str).absolutePath());
}
}
void MainWindow::checkForUpdates()
{
if(BuildConfig.UPDATER_ENABLED)
{
auto updater = APPLICATION->updateChecker();
updater->checkForUpdate(APPLICATION->settings()->get("UpdateChannel").toString(), true);
}
else
{
qWarning() << "Updater not set up. Cannot check for updates.";
}
}
void MainWindow::on_actionSettings_triggered()
{
APPLICATION->ShowGlobalSettings(this, "global-settings");
}
void MainWindow::globalSettingsClosed()
{
// FIXME: quick HACK to make this work. improve, optimize.
APPLICATION->instances()->loadList();
proxymodel->invalidate();
proxymodel->sort(0);
updateMainToolBar();
updateToolsMenu();
updateStatusCenter();
update();
}
void MainWindow::on_actionInstanceSettings_triggered()
{
APPLICATION->showInstanceWindow(m_selectedInstance, "settings");
}
void MainWindow::on_actionEditInstNotes_triggered()
{
APPLICATION->showInstanceWindow(m_selectedInstance, "notes");
}
void MainWindow::on_actionWorlds_triggered()
{
APPLICATION->showInstanceWindow(m_selectedInstance, "worlds");
}
void MainWindow::on_actionMods_triggered()
{
APPLICATION->showInstanceWindow(m_selectedInstance, "mods");
}
void MainWindow::on_actionEditInstance_triggered()
{
APPLICATION->showInstanceWindow(m_selectedInstance);
}
void MainWindow::on_actionScreenshots_triggered()
{
APPLICATION->showInstanceWindow(m_selectedInstance, "screenshots");
}
void MainWindow::on_actionManageAccounts_triggered()
{
APPLICATION->ShowGlobalSettings(this, "accounts");
}
void MainWindow::on_actionReportBug_triggered()
{
DesktopServices::openUrl(QUrl(BuildConfig.BUG_TRACKER_URL));
}
void MainWindow::on_actionOpenWiki_triggered()
{
DesktopServices::openUrl(QUrl(BuildConfig.HELP_URL.arg("")));
}
void MainWindow::on_actionMoreNews_triggered()
{
DesktopServices::openUrl(QUrl(BuildConfig.NEWS_OPEN_URL));
}
void MainWindow::newsButtonClicked()
{
QList<NewsEntryPtr> entries = m_newsChecker->getNewsEntries();
if (entries.count() > 0)
{
DesktopServices::openUrl(QUrl(entries[0]->link));
}
else
{
DesktopServices::openUrl(QUrl(BuildConfig.NEWS_OPEN_URL));
}
}
void MainWindow::on_actionAbout_triggered()
{
AboutDialog dialog(this);
dialog.exec();
}
void MainWindow::on_actionDeleteInstance_triggered()
{
if (!m_selectedInstance)
{
return;
}
auto id = m_selectedInstance->id();
auto response = CustomMessageBox::selectable(
this,
tr("CAREFUL!"),
tr("About to delete: %1\nThis is permanent and will completely delete the instance.\n\nAre you sure?").arg(m_selectedInstance->name()),
QMessageBox::Warning,
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No
)->exec();
if (response == QMessageBox::Yes)
{
APPLICATION->instances()->deleteInstance(id);
}
}
void MainWindow::on_actionExportInstance_triggered()
{
if (m_selectedInstance)
{
ExportInstanceDialog dlg(m_selectedInstance, this);
dlg.exec();
}
}
void MainWindow::on_actionRenameInstance_triggered()
{
if (m_selectedInstance)
{
view->edit(view->currentIndex());
}
}
void MainWindow::on_actionViewSelectedInstFolder_triggered()
{
if (m_selectedInstance)
{
QString str = m_selectedInstance->instanceRoot();
DesktopServices::openDirectory(QDir(str).absolutePath());
}
}
void MainWindow::on_actionViewSelectedMCFolder_triggered()
{
if (m_selectedInstance)
{
QString str = m_selectedInstance->gameRoot();
if (!FS::ensureFilePathExists(str))
{
// TODO: report error
return;
}
DesktopServices::openDirectory(QDir(str).absolutePath());
}
}
void MainWindow::on_actionViewSelectedModsFolder_triggered()
{
if (m_selectedInstance)
{
QString str = m_selectedInstance->modsRoot();
if (!FS::ensureFilePathExists(str))
{
// TODO: report error
return;
}
DesktopServices::openDirectory(QDir(str).absolutePath());
}
}
void MainWindow::closeEvent(QCloseEvent *event)
{
// Save the window state and geometry.
APPLICATION->settings()->set("MainWindowState", saveState().toBase64());
APPLICATION->settings()->set("MainWindowGeometry", saveGeometry().toBase64());
event->accept();
emit isClosing();
}
void MainWindow::changeEvent(QEvent* event)
{
if (event->type() == QEvent::LanguageChange)
{
retranslateUi();
}
QMainWindow::changeEvent(event);
}
void MainWindow::instanceActivated(QModelIndex index)
{
if (!index.isValid())
return;
QString id = index.data(InstanceList::InstanceIDRole).toString();
InstancePtr inst = APPLICATION->instances()->getInstanceById(id);
if (!inst)
return;
activateInstance(inst);
}
void MainWindow::on_actionLaunchInstance_triggered()
{
if (!m_selectedInstance)
{
return;
}
if(m_selectedInstance->isRunning())
{
APPLICATION->kill(m_selectedInstance);
}
else
{
APPLICATION->launch(m_selectedInstance);
}
}
void MainWindow::activateInstance(InstancePtr instance)
{
APPLICATION->launch(instance);
}
void MainWindow::on_actionLaunchInstanceOffline_triggered()
{
if (m_selectedInstance)
{
APPLICATION->launch(m_selectedInstance, false);
}
}
void MainWindow::taskEnd()
{
QObject *sender = QObject::sender();
if (sender == m_versionLoadTask)
m_versionLoadTask = NULL;
sender->deleteLater();
}
void MainWindow::startTask(Task *task)
{
connect(task, SIGNAL(succeeded()), SLOT(taskEnd()));
connect(task, SIGNAL(failed(QString)), SLOT(taskEnd()));
task->start();
}
void MainWindow::instanceChanged(const QModelIndex &current, const QModelIndex &previous)
{
if (!current.isValid())
{
APPLICATION->settings()->set("SelectedInstance", QString());
selectionBad();
return;
}
QString id = current.data(InstanceList::InstanceIDRole).toString();
m_selectedInstance = APPLICATION->instances()->getInstanceById(id);
if (m_selectedInstance)
{
ui->instanceToolBar->setEnabled(true);
ui->setInstanceActionsEnabled(true);
if(m_selectedInstance->isRunning())
{
ui->actionLaunchInstance->setEnabled(true);
ui->setLaunchAction(true);
}
else
{
ui->actionLaunchInstance->setEnabled(m_selectedInstance->canLaunch());
ui->setLaunchAction(false);
}
ui->actionLaunchInstanceOffline->setEnabled(m_selectedInstance->canLaunch());
ui->actionExportInstance->setEnabled(m_selectedInstance->canExport());
ui->renameButton->setText(m_selectedInstance->name());
m_statusLeft->setText(m_selectedInstance->getStatusbarDescription());
updateStatusCenter();
updateInstanceToolIcon(m_selectedInstance->iconKey());
updateToolsMenu();
APPLICATION->settings()->set("SelectedInstance", m_selectedInstance->id());
}
else
{
ui->instanceToolBar->setEnabled(false);
ui->setInstanceActionsEnabled(false);
APPLICATION->settings()->set("SelectedInstance", QString());
selectionBad();
return;
}
}
void MainWindow::instanceSelectRequest(QString id)
{
setSelectedInstanceById(id);
}
void MainWindow::instanceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
auto current = view->selectionModel()->currentIndex();
QItemSelection test(topLeft, bottomRight);
if (test.contains(current))
{
instanceChanged(current, current);
}
}
void MainWindow::selectionBad()
{
// start by reseting everything...
m_selectedInstance = nullptr;
statusBar()->clearMessage();
ui->instanceToolBar->setEnabled(false);
ui->setInstanceActionsEnabled(false);
ui->renameButton->setText(tr("Rename Instance"));
updateInstanceToolIcon("grass");
// ...and then see if we can enable the previously selected instance
setSelectedInstanceById(APPLICATION->settings()->get("SelectedInstance").toString());
}
void MainWindow::checkInstancePathForProblems()
{
QString instanceFolder = APPLICATION->settings()->get("InstanceDir").toString();
if (FS::checkProblemticPathJava(QDir(instanceFolder)))
{
QMessageBox warning(this);
warning.setText(tr("Your instance folder contains \'!\' and this is known to cause Java problems!"));
warning.setInformativeText(
tr(
"You have now two options: <br/>"
" - change the instance folder in the settings <br/>"
" - move this installation of %1 to a different folder"
).arg(BuildConfig.LAUNCHER_NAME)
);
warning.setDefaultButton(QMessageBox::Ok);
warning.exec();
}
auto tempFolderText = tr("This is a problem: <br/>"
" - The launcher will likely be deleted without warning by the operating system <br/>"
" - close the launcher now and extract it to a real location, not a temporary folder");
QString pathfoldername = QDir(instanceFolder).absolutePath();
if (pathfoldername.contains("Rar$", Qt::CaseInsensitive))
{
QMessageBox warning(this);
warning.setText(tr("Your instance folder contains \'Rar$\' - that means you haven't extracted the launcher archive!"));
warning.setInformativeText(tempFolderText);
warning.setDefaultButton(QMessageBox::Ok);
warning.exec();
}
else if (pathfoldername.startsWith(QDir::tempPath()) || pathfoldername.contains("/TempState/"))
{
QMessageBox warning(this);
warning.setText(tr("Your instance folder is in a temporary folder: \'%1\'!").arg(QDir::tempPath()));
warning.setInformativeText(tempFolderText);
warning.setDefaultButton(QMessageBox::Ok);
warning.exec();
}
}
void MainWindow::updateStatusCenter()
{
m_statusCenter->setVisible(APPLICATION->settings()->get("ShowGlobalGameTime").toBool());
int timePlayed = APPLICATION->instances()->getTotalPlayTime();
if (timePlayed > 0) {
m_statusCenter->setText(tr("Total playtime: %1").arg(Time::prettifyDuration(timePlayed)));
}
}