From 8e44ab2338f4ca63d58de4b3329c384df9d6c053 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Mon, 19 Mar 2018 02:36:12 +0100 Subject: NOISSUE redo new instance dialog --- .../pages/instance/InstanceSettingsPage.cpp | 251 +++++++++ application/pages/instance/InstanceSettingsPage.h | 74 +++ application/pages/instance/InstanceSettingsPage.ui | 393 ++++++++++++++ application/pages/instance/LegacyUpgradePage.cpp | 50 ++ application/pages/instance/LegacyUpgradePage.h | 64 +++ application/pages/instance/LegacyUpgradePage.ui | 47 ++ application/pages/instance/LogPage.cpp | 312 +++++++++++ application/pages/instance/LogPage.h | 86 ++++ application/pages/instance/LogPage.ui | 182 +++++++ application/pages/instance/ModFolderPage.cpp | 210 ++++++++ application/pages/instance/ModFolderPage.h | 108 ++++ application/pages/instance/ModFolderPage.ui | 180 +++++++ application/pages/instance/NotesPage.cpp | 21 + application/pages/instance/NotesPage.h | 60 +++ application/pages/instance/NotesPage.ui | 57 +++ application/pages/instance/OtherLogsPage.cpp | 313 +++++++++++ application/pages/instance/OtherLogsPage.h | 81 +++ application/pages/instance/OtherLogsPage.ui | 150 ++++++ application/pages/instance/ResourcePackPage.h | 21 + application/pages/instance/ScreenshotsPage.cpp | 372 ++++++++++++++ application/pages/instance/ScreenshotsPage.h | 84 +++ application/pages/instance/ScreenshotsPage.ui | 106 ++++ application/pages/instance/TexturePackPage.h | 19 + application/pages/instance/VersionPage.cpp | 570 +++++++++++++++++++++ application/pages/instance/VersionPage.h | 95 ++++ application/pages/instance/VersionPage.ui | 318 ++++++++++++ application/pages/instance/WorldListPage.cpp | 330 ++++++++++++ application/pages/instance/WorldListPage.h | 96 ++++ application/pages/instance/WorldListPage.ui | 168 ++++++ 29 files changed, 4818 insertions(+) create mode 100644 application/pages/instance/InstanceSettingsPage.cpp create mode 100644 application/pages/instance/InstanceSettingsPage.h create mode 100644 application/pages/instance/InstanceSettingsPage.ui create mode 100644 application/pages/instance/LegacyUpgradePage.cpp create mode 100644 application/pages/instance/LegacyUpgradePage.h create mode 100644 application/pages/instance/LegacyUpgradePage.ui create mode 100644 application/pages/instance/LogPage.cpp create mode 100644 application/pages/instance/LogPage.h create mode 100644 application/pages/instance/LogPage.ui create mode 100644 application/pages/instance/ModFolderPage.cpp create mode 100644 application/pages/instance/ModFolderPage.h create mode 100644 application/pages/instance/ModFolderPage.ui create mode 100644 application/pages/instance/NotesPage.cpp create mode 100644 application/pages/instance/NotesPage.h create mode 100644 application/pages/instance/NotesPage.ui create mode 100644 application/pages/instance/OtherLogsPage.cpp create mode 100644 application/pages/instance/OtherLogsPage.h create mode 100644 application/pages/instance/OtherLogsPage.ui create mode 100644 application/pages/instance/ResourcePackPage.h create mode 100644 application/pages/instance/ScreenshotsPage.cpp create mode 100644 application/pages/instance/ScreenshotsPage.h create mode 100644 application/pages/instance/ScreenshotsPage.ui create mode 100644 application/pages/instance/TexturePackPage.h create mode 100644 application/pages/instance/VersionPage.cpp create mode 100644 application/pages/instance/VersionPage.h create mode 100644 application/pages/instance/VersionPage.ui create mode 100644 application/pages/instance/WorldListPage.cpp create mode 100644 application/pages/instance/WorldListPage.h create mode 100644 application/pages/instance/WorldListPage.ui (limited to 'application/pages/instance') diff --git a/application/pages/instance/InstanceSettingsPage.cpp b/application/pages/instance/InstanceSettingsPage.cpp new file mode 100644 index 00000000..71e90a32 --- /dev/null +++ b/application/pages/instance/InstanceSettingsPage.cpp @@ -0,0 +1,251 @@ +#include "InstanceSettingsPage.h" +#include "ui_InstanceSettingsPage.h" + +#include +#include +#include + +#include "dialogs/VersionSelectDialog.h" +#include "JavaCommon.h" +#include "MultiMC.h" + +#include +#include +#include +#include + +InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent) + : QWidget(parent), ui(new Ui::InstanceSettingsPage), m_instance(inst) +{ + m_settings = inst->settings(); + ui->setupUi(this); + auto sysMB = Sys::getSystemRam() / Sys::megabyte; + ui->maxMemSpinBox->setMaximum(sysMB); + loadSettings(); +} + +bool InstanceSettingsPage::shouldDisplay() const +{ + return !m_instance->isRunning(); +} + +InstanceSettingsPage::~InstanceSettingsPage() +{ + delete ui; +} + +bool InstanceSettingsPage::apply() +{ + applySettings(); + return true; +} + +void InstanceSettingsPage::applySettings() +{ + SettingsObject::Lock lock(m_settings); + + // Console + bool console = ui->consoleSettingsBox->isChecked(); + m_settings->set("OverrideConsole", console); + if (console) + { + m_settings->set("ShowConsole", ui->showConsoleCheck->isChecked()); + m_settings->set("AutoCloseConsole", ui->autoCloseConsoleCheck->isChecked()); + m_settings->set("ShowConsoleOnError", ui->showConsoleErrorCheck->isChecked()); + } + else + { + m_settings->reset("ShowConsole"); + m_settings->reset("AutoCloseConsole"); + m_settings->reset("ShowConsoleOnError"); + } + + // Window Size + bool window = ui->windowSizeGroupBox->isChecked(); + m_settings->set("OverrideWindow", window); + if (window) + { + m_settings->set("LaunchMaximized", ui->maximizedCheckBox->isChecked()); + m_settings->set("MinecraftWinWidth", ui->windowWidthSpinBox->value()); + m_settings->set("MinecraftWinHeight", ui->windowHeightSpinBox->value()); + } + else + { + m_settings->reset("LaunchMaximized"); + m_settings->reset("MinecraftWinWidth"); + m_settings->reset("MinecraftWinHeight"); + } + + // Memory + bool memory = ui->memoryGroupBox->isChecked(); + m_settings->set("OverrideMemory", memory); + if (memory) + { + int min = ui->minMemSpinBox->value(); + int max = ui->maxMemSpinBox->value(); + if(min < max) + { + m_settings->set("MinMemAlloc", min); + m_settings->set("MaxMemAlloc", max); + } + else + { + m_settings->set("MinMemAlloc", max); + m_settings->set("MaxMemAlloc", min); + } + m_settings->set("PermGen", ui->permGenSpinBox->value()); + } + else + { + m_settings->reset("MinMemAlloc"); + m_settings->reset("MaxMemAlloc"); + m_settings->reset("PermGen"); + } + + // Java Install Settings + bool javaInstall = ui->javaSettingsGroupBox->isChecked(); + m_settings->set("OverrideJavaLocation", javaInstall); + if (javaInstall) + { + m_settings->set("JavaPath", ui->javaPathTextBox->text()); + } + else + { + m_settings->reset("JavaPath"); + } + + // Java arguments + bool javaArgs = ui->javaArgumentsGroupBox->isChecked(); + m_settings->set("OverrideJavaArgs", javaArgs); + if(javaArgs) + { + m_settings->set("JvmArgs", ui->jvmArgsTextBox->toPlainText().replace("\n", " ")); + JavaCommon::checkJVMArgs(m_settings->get("JvmArgs").toString(), this->parentWidget()); + } + else + { + m_settings->reset("JvmArgs"); + } + + // old generic 'override both' is removed. + m_settings->reset("OverrideJava"); + + // Custom Commands + bool custcmd = ui->customCommands->checked(); + m_settings->set("OverrideCommands", custcmd); + if (custcmd) + { + m_settings->set("PreLaunchCommand", ui->customCommands->prelaunchCommand()); + m_settings->set("WrapperCommand", ui->customCommands->wrapperCommand()); + m_settings->set("PostExitCommand", ui->customCommands->postexitCommand()); + } + else + { + m_settings->reset("PreLaunchCommand"); + m_settings->reset("WrapperCommand"); + m_settings->reset("PostExitCommand"); + } +} + +void InstanceSettingsPage::loadSettings() +{ + // Console + ui->consoleSettingsBox->setChecked(m_settings->get("OverrideConsole").toBool()); + ui->showConsoleCheck->setChecked(m_settings->get("ShowConsole").toBool()); + ui->autoCloseConsoleCheck->setChecked(m_settings->get("AutoCloseConsole").toBool()); + ui->showConsoleErrorCheck->setChecked(m_settings->get("ShowConsoleOnError").toBool()); + + // Window Size + ui->windowSizeGroupBox->setChecked(m_settings->get("OverrideWindow").toBool()); + ui->maximizedCheckBox->setChecked(m_settings->get("LaunchMaximized").toBool()); + ui->windowWidthSpinBox->setValue(m_settings->get("MinecraftWinWidth").toInt()); + ui->windowHeightSpinBox->setValue(m_settings->get("MinecraftWinHeight").toInt()); + + // Memory + ui->memoryGroupBox->setChecked(m_settings->get("OverrideMemory").toBool()); + int min = m_settings->get("MinMemAlloc").toInt(); + int max = m_settings->get("MaxMemAlloc").toInt(); + if(min < max) + { + ui->minMemSpinBox->setValue(min); + ui->maxMemSpinBox->setValue(max); + } + else + { + ui->minMemSpinBox->setValue(max); + ui->maxMemSpinBox->setValue(min); + } + ui->permGenSpinBox->setValue(m_settings->get("PermGen").toInt()); + + // Java Settings + bool overrideJava = m_settings->get("OverrideJava").toBool(); + bool overrideLocation = m_settings->get("OverrideJavaLocation").toBool() || overrideJava; + bool overrideArgs = m_settings->get("OverrideJavaArgs").toBool() || overrideJava; + + ui->javaSettingsGroupBox->setChecked(overrideLocation); + ui->javaPathTextBox->setText(m_settings->get("JavaPath").toString()); + + ui->javaArgumentsGroupBox->setChecked(overrideArgs); + ui->jvmArgsTextBox->setPlainText(m_settings->get("JvmArgs").toString()); + + // Custom commands + ui->customCommands->initialize( + true, + m_settings->get("OverrideCommands").toBool(), + m_settings->get("PreLaunchCommand").toString(), + m_settings->get("WrapperCommand").toString(), + m_settings->get("PostExitCommand").toString() + ); +} + +void InstanceSettingsPage::on_javaDetectBtn_clicked() +{ + JavaInstallPtr java; + + VersionSelectDialog vselect(MMC->javalist().get(), tr("Select a Java version"), this, true); + vselect.setResizeOn(2); + vselect.exec(); + + if (vselect.result() == QDialog::Accepted && vselect.selectedVersion()) + { + java = std::dynamic_pointer_cast(vselect.selectedVersion()); + ui->javaPathTextBox->setText(java->path); + } +} + +void InstanceSettingsPage::on_javaBrowseBtn_clicked() +{ + QString raw_path = QFileDialog::getOpenFileName(this, tr("Find Java executable")); + + // do not allow current dir - it's dirty. Do not allow dirs that don't exist + if(raw_path.isEmpty()) + { + return; + } + QString cooked_path = FS::NormalizePath(raw_path); + + QFileInfo javaInfo(cooked_path);; + if(!javaInfo.exists() || !javaInfo.isExecutable()) + { + return; + } + ui->javaPathTextBox->setText(cooked_path); +} + +void InstanceSettingsPage::on_javaTestBtn_clicked() +{ + if(checker) + { + return; + } + checker.reset(new JavaCommon::TestCheck( + this, ui->javaPathTextBox->text(), ui->jvmArgsTextBox->toPlainText().replace("\n", " "), + ui->minMemSpinBox->value(), ui->maxMemSpinBox->value(), ui->permGenSpinBox->value())); + connect(checker.get(), SIGNAL(finished()), SLOT(checkerFinished())); + checker->run(); +} + +void InstanceSettingsPage::checkerFinished() +{ + checker.reset(); +} diff --git a/application/pages/instance/InstanceSettingsPage.h b/application/pages/instance/InstanceSettingsPage.h new file mode 100644 index 00000000..c5d7d3b6 --- /dev/null +++ b/application/pages/instance/InstanceSettingsPage.h @@ -0,0 +1,74 @@ +/* Copyright 2013-2018 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. + */ + +#pragma once + +#include + +#include "java/JavaChecker.h" +#include "BaseInstance.h" +#include +#include "pages/BasePage.h" +#include "JavaCommon.h" +#include "MultiMC.h" + +class JavaChecker; +namespace Ui +{ +class InstanceSettingsPage; +} + +class InstanceSettingsPage : public QWidget, public BasePage +{ + Q_OBJECT + +public: + explicit InstanceSettingsPage(BaseInstance *inst, QWidget *parent = 0); + virtual ~InstanceSettingsPage(); + virtual QString displayName() const override + { + return tr("Settings"); + } + virtual QIcon icon() const override + { + return MMC->getThemedIcon("instance-settings"); + } + virtual QString id() const override + { + return "settings"; + } + virtual bool apply() override; + virtual QString helpPage() const override + { + return "Instance-settings"; + } + virtual bool shouldDisplay() const override; + +private slots: + void on_javaDetectBtn_clicked(); + void on_javaTestBtn_clicked(); + void on_javaBrowseBtn_clicked(); + + void applySettings(); + void loadSettings(); + + void checkerFinished(); + +private: + Ui::InstanceSettingsPage *ui; + BaseInstance *m_instance; + SettingsObjectPtr m_settings; + unique_qobject_ptr checker; +}; diff --git a/application/pages/instance/InstanceSettingsPage.ui b/application/pages/instance/InstanceSettingsPage.ui new file mode 100644 index 00000000..0c180df3 --- /dev/null +++ b/application/pages/instance/InstanceSettingsPage.ui @@ -0,0 +1,393 @@ + + + InstanceSettingsPage + + + + 0 + 0 + 553 + 522 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QTabWidget::Rounded + + + 0 + + + + Java + + + + + + true + + + Java insta&llation + + + true + + + false + + + + + + + + + Auto-detect... + + + + + + + Browse... + + + + + + + Test + + + + + + + + + + true + + + Memor&y + + + true + + + false + + + + + + Minimum memory allocation: + + + + + + + The maximum amount of memory Minecraft is allowed to use. + + + MB + + + 128 + + + 65536 + + + 128 + + + 1024 + + + + + + + The amount of memory Minecraft is started with. + + + MB + + + 128 + + + 65536 + + + 128 + + + 256 + + + + + + + The amount of memory available to store loaded Java classes. + + + MB + + + 64 + + + 999999999 + + + 8 + + + 64 + + + + + + + PermGen: + + + + + + + Maximum memory allocation: + + + + + + + Note: Permgen is set automatically by Java 8 and later + + + + + + + + + + true + + + Java argumen&ts + + + true + + + false + + + + + + + + + + + + + Game windows + + + + + + true + + + Game Window + + + true + + + false + + + + + + Start Minecraft maximized? + + + + + + + + + Window height: + + + + + + + Window width: + + + + + + + 1 + + + 65536 + + + 1 + + + 854 + + + + + + + 1 + + + 65536 + + + 480 + + + + + + + + + + + + true + + + Conso&le Settings + + + true + + + false + + + + + + Show console while the game is running? + + + + + + + Automatically close console when the game quits? + + + + + + + Show console when the game crashes? + + + + + + + + + + Qt::Vertical + + + + 88 + 125 + + + + + + + + + Custom commands + + + + + + + + + + + + + + CustomCommands + QWidget +
widgets/CustomCommands.h
+ 1 +
+
+ + settingsTabs + javaSettingsGroupBox + javaPathTextBox + javaDetectBtn + javaBrowseBtn + javaTestBtn + memoryGroupBox + minMemSpinBox + maxMemSpinBox + permGenSpinBox + javaArgumentsGroupBox + jvmArgsTextBox + windowSizeGroupBox + maximizedCheckBox + windowWidthSpinBox + windowHeightSpinBox + consoleSettingsBox + showConsoleCheck + autoCloseConsoleCheck + showConsoleErrorCheck + + + +
diff --git a/application/pages/instance/LegacyUpgradePage.cpp b/application/pages/instance/LegacyUpgradePage.cpp new file mode 100644 index 00000000..f808ab88 --- /dev/null +++ b/application/pages/instance/LegacyUpgradePage.cpp @@ -0,0 +1,50 @@ +#include "LegacyUpgradePage.h" +#include "ui_LegacyUpgradePage.h" + +#include "minecraft/legacy/LegacyInstance.h" +#include "minecraft/legacy/LegacyUpgradeTask.h" +#include "MultiMC.h" +#include "FolderInstanceProvider.h" +#include "dialogs/CustomMessageBox.h" +#include "dialogs/ProgressDialog.h" + +LegacyUpgradePage::LegacyUpgradePage(InstancePtr inst, QWidget *parent) + : QWidget(parent), ui(new Ui::LegacyUpgradePage), m_inst(inst) +{ + ui->setupUi(this); +} + +LegacyUpgradePage::~LegacyUpgradePage() +{ + delete ui; +} + +void LegacyUpgradePage::runModalTask(Task *task) +{ + connect(task, &Task::failed, [this](QString reason) + { + CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Warning)->show(); + }); + ProgressDialog loadDialog(this); + loadDialog.setSkipButton(true, tr("Abort")); + if(loadDialog.execWithTask(task) == QDialog::Accepted) + { + m_container->requestClose(); + } +} + +void LegacyUpgradePage::on_upgradeButton_clicked() +{ + QString newName = tr("%1 (Migrated)").arg(m_inst->name()); + auto upgradeTask = new LegacyUpgradeTask(m_inst); + upgradeTask->setName(newName); + upgradeTask->setGroup(m_inst->group()); + upgradeTask->setIcon(m_inst->iconKey()); + std::unique_ptr task(MMC->folderProvider()->wrapInstanceTask(upgradeTask)); + runModalTask(task.get()); +} + +bool LegacyUpgradePage::shouldDisplay() const +{ + return !m_inst->isRunning(); +} diff --git a/application/pages/instance/LegacyUpgradePage.h b/application/pages/instance/LegacyUpgradePage.h new file mode 100644 index 00000000..3e1abe93 --- /dev/null +++ b/application/pages/instance/LegacyUpgradePage.h @@ -0,0 +1,64 @@ +/* Copyright 2013-2018 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. + */ + +#pragma once + +#include + +#include "minecraft/legacy/LegacyInstance.h" +#include "pages/BasePage.h" +#include +#include "tasks/Task.h" + +namespace Ui +{ +class LegacyUpgradePage; +} + +class LegacyUpgradePage : public QWidget, public BasePage +{ + Q_OBJECT + +public: + explicit LegacyUpgradePage(InstancePtr inst, QWidget *parent = 0); + virtual ~LegacyUpgradePage(); + virtual QString displayName() const override + { + return tr("Upgrade"); + } + virtual QIcon icon() const override + { + return MMC->getThemedIcon("checkupdate"); + } + virtual QString id() const override + { + return "upgrade"; + } + virtual QString helpPage() const override + { + return "Legacy-upgrade"; + } + virtual bool shouldDisplay() const override; + +private slots: + void on_upgradeButton_clicked(); + +private: + void runModalTask(Task *task); + +private: + Ui::LegacyUpgradePage *ui; + InstancePtr m_inst; +}; diff --git a/application/pages/instance/LegacyUpgradePage.ui b/application/pages/instance/LegacyUpgradePage.ui new file mode 100644 index 00000000..a94ee039 --- /dev/null +++ b/application/pages/instance/LegacyUpgradePage.ui @@ -0,0 +1,47 @@ + + + LegacyUpgradePage + + + + 0 + 0 + 546 + 405 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + <html><body><h1>Upgrade is required</h1><p>MultiMC now supports old Minecraft versions and all the required features in the new (OneSix) instance format. As a consequence, the old (Legacy) format has been entirely disabled and old instances need to be upgraded.</p><p>The upgrade will create a new instance with the same contents as the current one, in the new format. The original instance will remain untouched, in case anything goes wrong in the process.</p><p>Please report any issues on our <a href="https://github.com/MultiMC/MultiMC5/issues">github issues page</a>.</p><p>There is also a <a href="https://discord.gg/GtPmv93">discord channel for testing here</a>.</p></body></html> + + + true + + + + + + + Upgrade the instance + + + + + + + + diff --git a/application/pages/instance/LogPage.cpp b/application/pages/instance/LogPage.cpp new file mode 100644 index 00000000..0fa1ee67 --- /dev/null +++ b/application/pages/instance/LogPage.cpp @@ -0,0 +1,312 @@ +#include "LogPage.h" +#include "ui_LogPage.h" + +#include "MultiMC.h" + +#include +#include +#include + +#include "launch/LaunchTask.h" +#include +#include "GuiUtil.h" +#include + +class LogFormatProxyModel : public QIdentityProxyModel +{ +public: + LogFormatProxyModel(QObject* parent = nullptr) : QIdentityProxyModel(parent) + { + } + QVariant data(const QModelIndex &index, int role) const override + { + switch(role) + { + case Qt::FontRole: + return m_font; + case Qt::TextColorRole: + { + MessageLevel::Enum level = (MessageLevel::Enum) QIdentityProxyModel::data(index, LogModel::LevelRole).toInt(); + return m_colors->getFront(level); + } + case Qt::BackgroundRole: + { + MessageLevel::Enum level = (MessageLevel::Enum) QIdentityProxyModel::data(index, LogModel::LevelRole).toInt(); + return m_colors->getBack(level); + } + default: + return QIdentityProxyModel::data(index, role); + } + } + + void setFont(QFont font) + { + m_font = font; + } + + void setColors(LogColorCache* colors) + { + m_colors.reset(colors); + } + + QModelIndex find(const QModelIndex &start, const QString &value, bool reverse) const + { + QModelIndex parentIndex = parent(start); + auto compare = [&](int r) -> QModelIndex + { + QModelIndex idx = index(r, start.column(), parentIndex); + if (!idx.isValid() || idx == start) + { + return QModelIndex(); + } + QVariant v = data(idx, Qt::DisplayRole); + QString t = v.toString(); + if (t.contains(value, Qt::CaseInsensitive)) + return idx; + return QModelIndex(); + }; + if(reverse) + { + int from = start.row(); + int to = 0; + + for (int i = 0; i < 2; ++i) + { + for (int r = from; (r >= to); --r) + { + auto idx = compare(r); + if(idx.isValid()) + return idx; + } + // prepare for the next iteration + from = rowCount() - 1; + to = start.row(); + } + } + else + { + int from = start.row(); + int to = rowCount(parentIndex); + + for (int i = 0; i < 2; ++i) + { + for (int r = from; (r < to); ++r) + { + auto idx = compare(r); + if(idx.isValid()) + return idx; + } + // prepare for the next iteration + from = 0; + to = start.row(); + } + } + return QModelIndex(); + } +private: + QFont m_font; + std::unique_ptr m_colors; +}; + +LogPage::LogPage(InstancePtr instance, QWidget *parent) + : QWidget(parent), ui(new Ui::LogPage), m_instance(instance) +{ + ui->setupUi(this); + ui->tabWidget->tabBar()->hide(); + + m_proxy = new LogFormatProxyModel(this); + // set up text colors in the log proxy 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_proxy->setColors(new LogColorCache(origForeground, origBackground)); + } + + // set up fonts in the log proxy + { + QString fontFamily = MMC->settings()->get("ConsoleFont").toString(); + bool conversionOk = false; + int fontSize = MMC->settings()->get("ConsoleFontSize").toInt(&conversionOk); + if(!conversionOk) + { + fontSize = 11; + } + m_proxy->setFont(QFont(fontFamily, fontSize)); + } + + ui->text->setModel(m_proxy); + + // set up instance and launch process recognition + { + auto launchTask = m_instance->getLaunchTask(); + if(launchTask) + { + setInstanceLaunchTaskChanged(launchTask, true); + } + connect(m_instance.get(), &BaseInstance::launchTaskChanged, this, &LogPage::onInstanceLaunchTaskChanged); + } + + auto findShortcut = new QShortcut(QKeySequence(QKeySequence::Find), this); + connect(findShortcut, SIGNAL(activated()), SLOT(findActivated())); + auto findNextShortcut = new QShortcut(QKeySequence(QKeySequence::FindNext), this); + connect(findNextShortcut, SIGNAL(activated()), SLOT(findNextActivated())); + connect(ui->searchBar, SIGNAL(returnPressed()), SLOT(on_findButton_clicked())); + auto findPreviousShortcut = new QShortcut(QKeySequence(QKeySequence::FindPrevious), this); + connect(findPreviousShortcut, SIGNAL(activated()), SLOT(findPreviousActivated())); +} + +LogPage::~LogPage() +{ + delete ui; +} + +void LogPage::modelStateToUI() +{ + if(m_model->wrapLines()) + { + ui->text->setWordWrap(true); + ui->wrapCheckbox->setCheckState(Qt::Checked); + } + else + { + ui->text->setWordWrap(false); + ui->wrapCheckbox->setCheckState(Qt::Unchecked); + } + if(m_model->suspended()) + { + ui->trackLogCheckbox->setCheckState(Qt::Unchecked); + } + else + { + ui->trackLogCheckbox->setCheckState(Qt::Checked); + } +} + +void LogPage::UIToModelState() +{ + if(!m_model) + { + return; + } + m_model->setLineWrap(ui->wrapCheckbox->checkState() == Qt::Checked); + m_model->suspend(ui->trackLogCheckbox->checkState() != Qt::Checked); +} + +void LogPage::setInstanceLaunchTaskChanged(std::shared_ptr proc, bool initial) +{ + m_process = proc; + if(m_process) + { + m_model = proc->getLogModel(); + m_proxy->setSourceModel(m_model.get()); + if(initial) + { + modelStateToUI(); + } + else + { + UIToModelState(); + } + } + else + { + m_proxy->setSourceModel(nullptr); + m_model.reset(); + } +} + +void LogPage::onInstanceLaunchTaskChanged(std::shared_ptr proc) +{ + setInstanceLaunchTaskChanged(proc, false); +} + +bool LogPage::apply() +{ + return true; +} + +bool LogPage::shouldDisplay() const +{ + return m_instance->isRunning() || m_proxy->rowCount() > 0; +} + +void LogPage::on_btnPaste_clicked() +{ + if(!m_model) + return; + + //FIXME: turn this into a proper task and move the upload logic out of GuiUtil! + m_model->append(MessageLevel::MultiMC, tr("MultiMC: Log upload triggered at: %1").arg(QDateTime::currentDateTime().toString(Qt::RFC2822Date))); + auto url = GuiUtil::uploadPaste(m_model->toPlainText(), this); + if(!url.isEmpty()) + { + m_model->append(MessageLevel::MultiMC, tr("MultiMC: Log uploaded to: %1").arg(url)); + } + else + { + m_model->append(MessageLevel::Error, tr("MultiMC: Log upload failed!")); + } +} + +void LogPage::on_btnCopy_clicked() +{ + if(!m_model) + return; + m_model->append(MessageLevel::MultiMC, QString("Clipboard copy at: %1").arg(QDateTime::currentDateTime().toString(Qt::RFC2822Date))); + GuiUtil::setClipboardText(m_model->toPlainText()); +} + +void LogPage::on_btnClear_clicked() +{ + if(!m_model) + return; + m_model->clear(); + m_container->refreshContainer(); +} + +void LogPage::on_btnBottom_clicked() +{ + ui->text->scrollToBottom(); +} + +void LogPage::on_trackLogCheckbox_clicked(bool checked) +{ + if(!m_model) + return; + m_model->suspend(!checked); +} + +void LogPage::on_wrapCheckbox_clicked(bool checked) +{ + ui->text->setWordWrap(checked); + if(!m_model) + return; + m_model->setLineWrap(checked); +} + +void LogPage::on_findButton_clicked() +{ + auto modifiers = QApplication::keyboardModifiers(); + bool reverse = modifiers & Qt::ShiftModifier; + ui->text->findNext(ui->searchBar->text(), reverse); +} + +void LogPage::findNextActivated() +{ + ui->text->findNext(ui->searchBar->text(), false); +} + +void LogPage::findPreviousActivated() +{ + ui->text->findNext(ui->searchBar->text(), true); +} + +void LogPage::findActivated() +{ + // focus the search bar if it doesn't have focus + if (!ui->searchBar->hasFocus()) + { + ui->searchBar->setFocus(); + ui->searchBar->selectAll(); + } +} diff --git a/application/pages/instance/LogPage.h b/application/pages/instance/LogPage.h new file mode 100644 index 00000000..2229418d --- /dev/null +++ b/application/pages/instance/LogPage.h @@ -0,0 +1,86 @@ +/* Copyright 2013-2018 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. + */ + +#pragma once + +#include + +#include "BaseInstance.h" +#include "launch/LaunchTask.h" +#include "pages/BasePage.h" +#include + +namespace Ui +{ +class LogPage; +} +class QTextCharFormat; +class LogFormatProxyModel; + +class LogPage : public QWidget, public BasePage +{ + Q_OBJECT + +public: + explicit LogPage(InstancePtr instance, QWidget *parent = 0); + virtual ~LogPage(); + virtual QString displayName() const override + { + return tr("Minecraft Log"); + } + virtual QIcon icon() const override + { + return MMC->getThemedIcon("log"); + } + virtual QString id() const override + { + return "console"; + } + virtual bool apply() override; + virtual QString helpPage() const override + { + return "Minecraft-Logs"; + } + virtual bool shouldDisplay() const override; + +private slots: + void on_btnPaste_clicked(); + void on_btnCopy_clicked(); + void on_btnClear_clicked(); + void on_btnBottom_clicked(); + + void on_trackLogCheckbox_clicked(bool checked); + void on_wrapCheckbox_clicked(bool checked); + + void on_findButton_clicked(); + void findActivated(); + void findNextActivated(); + void findPreviousActivated(); + + void onInstanceLaunchTaskChanged(std::shared_ptr proc); + +private: + void modelStateToUI(); + void UIToModelState(); + void setInstanceLaunchTaskChanged(std::shared_ptr proc, bool initial); + +private: + Ui::LogPage *ui; + InstancePtr m_instance; + std::shared_ptr m_process; + + LogFormatProxyModel * m_proxy; + shared_qobject_ptr m_model; +}; diff --git a/application/pages/instance/LogPage.ui b/application/pages/instance/LogPage.ui new file mode 100644 index 00000000..4843d7c3 --- /dev/null +++ b/application/pages/instance/LogPage.ui @@ -0,0 +1,182 @@ + + + LogPage + + + + 0 + 0 + 825 + 782 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + Tab 1 + + + + + + false + + + true + + + + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + false + + + + + + + + + Keep updating + + + true + + + + + + + Wrap lines + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Copy the whole log into the clipboard + + + &Copy + + + + + + + Upload the log to paste.ee - it will stay online for a month + + + Upload + + + + + + + Clear the log + + + Clear + + + + + + + + + Search: + + + + + + + Find + + + + + + + + + + Scroll all the way to bottom + + + Bottom + + + + + + + Qt::Vertical + + + + + + + + + + + + LogView + QPlainTextEdit +
widgets/LogView.h
+
+
+ + tabWidget + trackLogCheckbox + wrapCheckbox + btnCopy + btnPaste + btnClear + text + searchBar + findButton + + + +
diff --git a/application/pages/instance/ModFolderPage.cpp b/application/pages/instance/ModFolderPage.cpp new file mode 100644 index 00000000..90155df3 --- /dev/null +++ b/application/pages/instance/ModFolderPage.cpp @@ -0,0 +1,210 @@ +/* Copyright 2013-2018 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 "ModFolderPage.h" +#include "ui_ModFolderPage.h" + +#include +#include +#include +#include + +#include "MultiMC.h" +#include "dialogs/CustomMessageBox.h" +#include "dialogs/ModEditDialogCommon.h" +#include +#include "minecraft/ModList.h" +#include "minecraft/Mod.h" +#include "minecraft/VersionFilterData.h" +#include "minecraft/ComponentList.h" +#include + +ModFolderPage::ModFolderPage(BaseInstance *inst, std::shared_ptr mods, QString id, + QString iconName, QString displayName, QString helpPage, + QWidget *parent) + : QWidget(parent), ui(new Ui::ModFolderPage) +{ + ui->setupUi(this); + ui->tabWidget->tabBar()->hide(); + m_inst = inst; + m_mods = mods; + m_id = id; + m_displayName = displayName; + m_iconName = iconName; + m_helpName = helpPage; + m_fileSelectionFilter = "%1 (*.zip *.jar)"; + m_filterModel = new QSortFilterProxyModel(this); + m_filterModel->setDynamicSortFilter(true); + m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + m_filterModel->setSortCaseSensitivity(Qt::CaseInsensitive); + m_filterModel->setSourceModel(m_mods.get()); + m_filterModel->setFilterKeyColumn(-1); + ui->modTreeView->setModel(m_filterModel); + ui->modTreeView->installEventFilter(this); + ui->modTreeView->sortByColumn(1, Qt::AscendingOrder); + auto smodel = ui->modTreeView->selectionModel(); + connect(smodel, &QItemSelectionModel::currentChanged, this, &ModFolderPage::modCurrent); + connect(ui->filterEdit, &QLineEdit::textChanged, this, &ModFolderPage::on_filterTextChanged ); +} + +void ModFolderPage::openedImpl() +{ + m_mods->startWatching(); +} + +void ModFolderPage::closedImpl() +{ + m_mods->stopWatching(); +} + +void ModFolderPage::on_filterTextChanged(const QString& newContents) +{ + m_viewFilter = newContents; + m_filterModel->setFilterFixedString(m_viewFilter); +} + + +CoreModFolderPage::CoreModFolderPage(BaseInstance *inst, std::shared_ptr mods, + QString id, QString iconName, QString displayName, + QString helpPage, QWidget *parent) + : ModFolderPage(inst, mods, id, iconName, displayName, helpPage, parent) +{ +} + +ModFolderPage::~ModFolderPage() +{ + m_mods->stopWatching(); + delete ui; +} + +bool ModFolderPage::shouldDisplay() const +{ + if (m_inst) + return !m_inst->isRunning(); + return true; +} + +bool CoreModFolderPage::shouldDisplay() const +{ + if (ModFolderPage::shouldDisplay()) + { + auto inst = dynamic_cast(m_inst); + if (!inst) + return true; + auto version = inst->getComponentList(); + if (!version) + return true; + if(!version->getComponent("net.minecraftforge")) + { + return false; + } + if(!version->getComponent("net.minecraft")) + { + return false; + } + if(version->getComponent("net.minecraft")->getReleaseDateTime() < g_VersionFilterData.legacyCutoffDate) + { + return true; + } + } + return false; +} + +bool ModFolderPage::modListFilter(QKeyEvent *keyEvent) +{ + switch (keyEvent->key()) + { + case Qt::Key_Delete: + on_rmModBtn_clicked(); + return true; + case Qt::Key_Plus: + on_addModBtn_clicked(); + return true; + default: + break; + } + return QWidget::eventFilter(ui->modTreeView, keyEvent); +} + +bool ModFolderPage::eventFilter(QObject *obj, QEvent *ev) +{ + if (ev->type() != QEvent::KeyPress) + { + return QWidget::eventFilter(obj, ev); + } + QKeyEvent *keyEvent = static_cast(ev); + if (obj == ui->modTreeView) + return modListFilter(keyEvent); + return QWidget::eventFilter(obj, ev); +} + +void ModFolderPage::on_addModBtn_clicked() +{ + auto list = GuiUtil::BrowseForFiles( + m_helpName, + tr("Select %1", + "Select whatever type of files the page contains. Example: 'Loader Mods'") + .arg(m_displayName), + m_fileSelectionFilter.arg(m_displayName), MMC->settings()->get("CentralModsDir").toString(), + this->parentWidget()); + if (!list.empty()) + { + for (auto filename : list) + { + m_mods->installMod(filename); + } + } +} + +void ModFolderPage::on_enableModBtn_clicked() +{ + auto selection = m_filterModel->mapSelectionToSource(ui->modTreeView->selectionModel()->selection()); + m_mods->enableMods(selection.indexes(), true); +} + +void ModFolderPage::on_disableModBtn_clicked() +{ + auto selection = m_filterModel->mapSelectionToSource(ui->modTreeView->selectionModel()->selection()); + m_mods->enableMods(selection.indexes(), false); +} + +void ModFolderPage::on_rmModBtn_clicked() +{ + auto selection = m_filterModel->mapSelectionToSource(ui->modTreeView->selectionModel()->selection()); + m_mods->deleteMods(selection.indexes()); +} + +void ModFolderPage::on_configFolderBtn_clicked() +{ + DesktopServices::openDirectory(m_inst->instanceConfigFolder(), true); +} + +void ModFolderPage::on_viewModBtn_clicked() +{ + DesktopServices::openDirectory(m_mods->dir().absolutePath(), true); +} + +void ModFolderPage::modCurrent(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (!current.isValid()) + { + ui->frame->clear(); + return; + } + auto sourceCurrent = m_filterModel->mapToSource(current); + int row = sourceCurrent.row(); + Mod &m = m_mods->operator[](row); + ui->frame->updateWithMod(m); +} diff --git a/application/pages/instance/ModFolderPage.h b/application/pages/instance/ModFolderPage.h new file mode 100644 index 00000000..15e728cb --- /dev/null +++ b/application/pages/instance/ModFolderPage.h @@ -0,0 +1,108 @@ +/* Copyright 2013-2018 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. + */ + +#pragma once + +#include + +#include "minecraft/MinecraftInstance.h" +#include "pages/BasePage.h" +#include + +class ModList; +namespace Ui +{ +class ModFolderPage; +} + +class ModFolderPage : public QWidget, public BasePage +{ + Q_OBJECT + +public: + explicit ModFolderPage(BaseInstance *inst, std::shared_ptr mods, QString id, + QString iconName, QString displayName, QString helpPage = "", + QWidget *parent = 0); + virtual ~ModFolderPage(); + + void setFilter(const QString & filter) + { + m_fileSelectionFilter = filter; + } + + virtual QString displayName() const override + { + return m_displayName; + } + virtual QIcon icon() const override + { + return MMC->getThemedIcon(m_iconName); + } + virtual QString id() const override + { + return m_id; + } + virtual QString helpPage() const override + { + return m_helpName; + } + virtual bool shouldDisplay() const override; + + virtual void openedImpl() override; + virtual void closedImpl() override; +protected: + bool eventFilter(QObject *obj, QEvent *ev) override; + bool modListFilter(QKeyEvent *ev); + +protected: + BaseInstance *m_inst; + +protected: + Ui::ModFolderPage *ui; + std::shared_ptr m_mods; + QSortFilterProxyModel *m_filterModel; + QString m_iconName; + QString m_id; + QString m_displayName; + QString m_helpName; + QString m_fileSelectionFilter; + QString m_viewFilter; + +public +slots: + void modCurrent(const QModelIndex ¤t, const QModelIndex &previous); + +private +slots: + void on_filterTextChanged(const QString & newContents); + void on_addModBtn_clicked(); + void on_rmModBtn_clicked(); + void on_viewModBtn_clicked(); + void on_enableModBtn_clicked(); + void on_disableModBtn_clicked(); + void on_configFolderBtn_clicked(); +}; + +class CoreModFolderPage : public ModFolderPage +{ +public: + explicit CoreModFolderPage(BaseInstance *inst, std::shared_ptr mods, QString id, + QString iconName, QString displayName, QString helpPage = "", + QWidget *parent = 0); + virtual ~CoreModFolderPage() + { + } + virtual bool shouldDisplay() const; +}; diff --git a/application/pages/instance/ModFolderPage.ui b/application/pages/instance/ModFolderPage.ui new file mode 100644 index 00000000..b5597bdc --- /dev/null +++ b/application/pages/instance/ModFolderPage.ui @@ -0,0 +1,180 @@ + + + ModFolderPage + + + + 0 + 0 + 723 + 532 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + 0 + 0 + + + + Tab 1 + + + + + + + + &Add + + + + + + + &Remove + + + + + + + Enable + + + + + + + Disable + + + + + + + Open the 'config' folder in the system file manager. + + + View configs + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + &View Folder + + + + + + + + + + 0 + 0 + + + + + + + + + + true + + + + + + + Filter: + + + + + + + + 0 + 0 + + + + true + + + QAbstractItemView::DropOnly + + + + + + + + + + + + + + ModListView + QTreeView +
widgets/ModListView.h
+
+ + MCModInfoFrame + QFrame +
widgets/MCModInfoFrame.h
+ 1 +
+
+ + tabWidget + modTreeView + filterEdit + addModBtn + rmModBtn + enableModBtn + disableModBtn + configFolderBtn + viewModBtn + + + +
diff --git a/application/pages/instance/NotesPage.cpp b/application/pages/instance/NotesPage.cpp new file mode 100644 index 00000000..48bb468c --- /dev/null +++ b/application/pages/instance/NotesPage.cpp @@ -0,0 +1,21 @@ +#include "NotesPage.h" +#include "ui_NotesPage.h" + +NotesPage::NotesPage(BaseInstance *inst, QWidget *parent) + : QWidget(parent), ui(new Ui::NotesPage), m_inst(inst) +{ + ui->setupUi(this); + ui->tabWidget->tabBar()->hide(); + ui->noteEditor->setText(m_inst->notes()); +} + +NotesPage::~NotesPage() +{ + delete ui; +} + +bool NotesPage::apply() +{ + m_inst->setNotes(ui->noteEditor->toPlainText()); + return true; +} diff --git a/application/pages/instance/NotesPage.h b/application/pages/instance/NotesPage.h new file mode 100644 index 00000000..4a25f9b1 --- /dev/null +++ b/application/pages/instance/NotesPage.h @@ -0,0 +1,60 @@ +/* Copyright 2013-2018 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. + */ + +#pragma once + +#include + +#include "BaseInstance.h" +#include "pages/BasePage.h" +#include + +namespace Ui +{ +class NotesPage; +} + +class NotesPage : public QWidget, public BasePage +{ + Q_OBJECT + +public: + explicit NotesPage(BaseInstance *inst, QWidget *parent = 0); + virtual ~NotesPage(); + virtual QString displayName() const override + { + return tr("Notes"); + } + virtual QIcon icon() const override + { + auto icon = MM