diff options
Diffstat (limited to 'gui/pages')
28 files changed, 3251 insertions, 0 deletions
diff --git a/gui/pages/BasePage.h b/gui/pages/BasePage.h new file mode 100644 index 00000000..09af3a59 --- /dev/null +++ b/gui/pages/BasePage.h @@ -0,0 +1,48 @@ +/* Copyright 2014 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 <QString> +#include <QIcon> +#include <memory> + +class BasePage +{ +public: + virtual ~BasePage(){}; + virtual QString id() = 0; + virtual QString displayName() = 0; + virtual QIcon icon() = 0; + virtual bool apply() + { + return true; + } + virtual bool shouldDisplay() + { + return true; + } + virtual QString helpPage() + { + return QString(); + } + virtual void opened() + { + + } + int stackIndex = -1; + int listIndex = -1; +}; + +typedef std::shared_ptr<BasePage> BasePagePtr; diff --git a/gui/pages/BasePageProvider.h b/gui/pages/BasePageProvider.h new file mode 100644 index 00000000..cff9c8e7 --- /dev/null +++ b/gui/pages/BasePageProvider.h @@ -0,0 +1,28 @@ +/* Copyright 2014 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 "BasePage.h" +#include <memory> + +class BasePageProvider +{ +public: + virtual QList<BasePage *> getPages() = 0; + virtual QString dialogTitle() = 0; +}; + +typedef std::shared_ptr<BasePageProvider> BasePageProviderPtr; diff --git a/gui/pages/InstanceSettingsPage.cpp b/gui/pages/InstanceSettingsPage.cpp new file mode 100644 index 00000000..6e2ce238 --- /dev/null +++ b/gui/pages/InstanceSettingsPage.cpp @@ -0,0 +1,229 @@ +#include "InstanceSettingsPage.h" +#include <gui/dialogs/VersionSelectDialog.h> +#include "logic/NagUtils.h" +#include <logic/java/JavaVersionList.h> +#include "MultiMC.h" +#include <QDialog> +#include <QFileDialog> +#include <QMessageBox> +#include "ui_InstanceSettingsPage.h" + +QString InstanceSettingsPage::displayName() +{ + return tr("Settings"); +} + +QIcon InstanceSettingsPage::icon() +{ + return QIcon::fromTheme("settings"); +} + +QString InstanceSettingsPage::id() +{ + return "settings"; +} + +InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent) + : QWidget(parent), ui(new Ui::InstanceSettingsPage), m_instance(inst) +{ + m_settings = &(inst->settings()); + ui->setupUi(this); + loadSettings(); +} + +bool InstanceSettingsPage::shouldDisplay() +{ + return !m_instance->isRunning(); +} + +InstanceSettingsPage::~InstanceSettingsPage() +{ + delete ui; +} + +bool InstanceSettingsPage::apply() +{ + applySettings(); + return true; +} + +void InstanceSettingsPage::applySettings() +{ + // 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()); + } + else + { + m_settings->reset("ShowConsole"); + m_settings->reset("AutoCloseConsole"); + } + + // 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) + { + m_settings->set("MinMemAlloc", ui->minMemSpinBox->value()); + m_settings->set("MaxMemAlloc", ui->maxMemSpinBox->value()); + 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", " ")); + NagUtils::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->customCommandsGroupBox->isChecked(); + m_settings->set("OverrideCommands", custcmd); + if (custcmd) + { + m_settings->set("PreLaunchCommand", ui->preLaunchCmdTextBox->text()); + m_settings->set("PostExitCommand", ui->postExitCmdTextBox->text()); + } + else + { + m_settings->reset("PreLaunchCommand"); + 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()); + + // 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()); + ui->minMemSpinBox->setValue(m_settings->get("MinMemAlloc").toInt()); + ui->maxMemSpinBox->setValue(m_settings->get("MaxMemAlloc").toInt()); + 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->customCommandsGroupBox->setChecked(m_settings->get("OverrideCommands").toBool()); + ui->preLaunchCmdTextBox->setText(m_settings->get("PreLaunchCommand").toString()); + ui->postExitCmdTextBox->setText(m_settings->get("PostExitCommand").toString()); +} + +void InstanceSettingsPage::on_javaDetectBtn_clicked() +{ + JavaVersionPtr 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<JavaVersion>(vselect.selectedVersion()); + ui->javaPathTextBox->setText(java->path); + } +} + +void InstanceSettingsPage::on_javaBrowseBtn_clicked() +{ + QString dir = QFileDialog::getOpenFileName(this, tr("Find Java executable")); + if (!dir.isNull()) + { + ui->javaPathTextBox->setText(dir); + } +} + +void InstanceSettingsPage::on_javaTestBtn_clicked() +{ + checker.reset(new JavaChecker()); + connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, + SLOT(checkFinished(JavaCheckResult))); + checker->path = ui->javaPathTextBox->text(); + checker->performCheck(); +} + +void InstanceSettingsPage::checkFinished(JavaCheckResult result) +{ + if (result.valid) + { + QString text; + text += "Java test succeeded!\n"; + if (result.is_64bit) + text += "Using 64bit java.\n"; + text += "\n"; + text += "Platform reported: " + result.realPlatform; + QMessageBox::information(this, tr("Java test success"), text); + } + else + { + QMessageBox::warning( + this, tr("Java test failure"), + tr("The specified java binary didn't work. You should use the auto-detect feature, " + "or set the path to the java executable.")); + } +} diff --git a/gui/pages/InstanceSettingsPage.h b/gui/pages/InstanceSettingsPage.h new file mode 100644 index 00000000..2447168f --- /dev/null +++ b/gui/pages/InstanceSettingsPage.h @@ -0,0 +1,59 @@ +/* Copyright 2014 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 <QWidget> + +#include <logic/OneSixInstance.h> +#include <logic/net/NetJob.h> +#include <logic/java/JavaChecker.h> +#include "BasePage.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() override; + virtual QIcon icon() override; + virtual QString id() override; + virtual bool apply(); + virtual QString helpPage() override { return "Instance-settings"; } + virtual bool shouldDisplay(); +private slots: + void on_javaDetectBtn_clicked(); + + void on_javaTestBtn_clicked(); + + void on_javaBrowseBtn_clicked(); + + void checkFinished(JavaCheckResult result); + + void applySettings(); + void loadSettings(); +private: + Ui::InstanceSettingsPage *ui; + BaseInstance *m_instance; + SettingsObject *m_settings; + std::shared_ptr<JavaChecker> checker; +}; diff --git a/gui/pages/InstanceSettingsPage.ui b/gui/pages/InstanceSettingsPage.ui new file mode 100644 index 00000000..b8af6c60 --- /dev/null +++ b/gui/pages/InstanceSettingsPage.ui @@ -0,0 +1,432 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>InstanceSettingsPage</class> + <widget class="QWidget" name="InstanceSettingsPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>458</width> + <height>426</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QTabWidget" name="settingsTabs"> + <property name="tabShape"> + <enum>QTabWidget::Rounded</enum> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="minecraftTab"> + <attribute name="title"> + <string>Java</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <widget class="QGroupBox" name="javaSettingsGroupBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Java installation</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0" colspan="3"> + <widget class="QLineEdit" name="javaPathTextBox"/> + </item> + <item row="1" column="0"> + <widget class="QPushButton" name="javaDetectBtn"> + <property name="text"> + <string>Auto-detect...</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QPushButton" name="javaBrowseBtn"> + <property name="text"> + <string>Browse...</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="javaTestBtn"> + <property name="text"> + <string>Test</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="memoryGroupBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Memory</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="1" column="1"> + <widget class="QSpinBox" name="maxMemSpinBox"> + <property name="toolTip"> + <string>The maximum amount of memory Minecraft is allowed to use.</string> + </property> + <property name="suffix"> + <string> MB</string> + </property> + <property name="minimum"> + <number>512</number> + </property> + <property name="maximum"> + <number>65536</number> + </property> + <property name="singleStep"> + <number>128</number> + </property> + <property name="value"> + <number>1024</number> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="labelMinMem"> + <property name="text"> + <string>Minimum memory allocation:</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="labelMaxMem"> + <property name="text"> + <string>Maximum memory allocation:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QSpinBox" name="minMemSpinBox"> + <property name="toolTip"> + <string>The amount of memory Minecraft is started with.</string> + </property> + <property name="suffix"> + <string> MB</string> + </property> + <property name="minimum"> + <number>256</number> + </property> + <property name="maximum"> + <number>65536</number> + </property> + <property name="singleStep"> + <number>128</number> + </property> + <property name="value"> + <number>256</number> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QSpinBox" name="permGenSpinBox"> + <property name="toolTip"> + <string>The amount of memory available to store loaded Java classes.</string> + </property> + <property name="suffix"> + <string> MB</string> + </property> + <property name="minimum"> + <number>64</number> + </property> + <property name="maximum"> + <number>999999999</number> + </property> + <property name="singleStep"> + <number>8</number> + </property> + <property name="value"> + <number>64</number> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="labelPermGen"> + <property name="text"> + <string>PermGen:</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="javaArgumentsGroupBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Java arguments</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout_5"> + <item row="1" column="1"> + <widget class="QPlainTextEdit" name="jvmArgsTextBox"/> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacerMinecraft"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="javaTab"> + <attribute name="title"> + <string>Game windows</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QGroupBox" name="windowSizeGroupBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Game Window</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QCheckBox" name="maximizedCheckBox"> + <property name="text"> + <string>Start Minecraft maximized?</string> + </property> + </widget> + </item> + <item> + <layout class="QGridLayout" name="gridLayoutWindowSize"> + <item row="1" column="0"> + <widget class="QLabel" name="labelWindowHeight"> + <property name="text"> + <string>Window height:</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="labelWindowWidth"> + <property name="text"> + <string>Window width:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QSpinBox" name="windowWidthSpinBox"> + <property name="minimum"> + <number>854</number> + </property> + <property name="maximum"> + <number>65536</number> + </property> + <property name="singleStep"> + <number>1</number> + </property> + <property name="value"> + <number>854</number> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QSpinBox" name="windowHeightSpinBox"> + <property name="minimum"> + <number>480</number> + </property> + <property name="maximum"> + <number>65536</number> + </property> + <property name="value"> + <number>480</number> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="consoleSettingsBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Console Settings</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QCheckBox" name="showConsoleCheck"> + <property name="text"> + <string>Show console while the game is running?</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="autoCloseConsoleCheck"> + <property name="text"> + <string>Automatically close console when the game quits?</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacerMinecraft_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>88</width> + <height>125</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Custom commands</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <item> + <widget class="QGroupBox" name="customCommandsGroupBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Custom Commands</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="1"> + <widget class="QLineEdit" name="preLaunchCmdTextBox"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="labelPostExitCmd"> + <property name="text"> + <string>Post-exit command:</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="labelPreLaunchCmd"> + <property name="text"> + <string>Pre-launch command:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="postExitCmdTextBox"/> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QLabel" name="labelCustomCmdsDescription"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Pre-launch command runs before the instance launches and post-exit command runs after it exits. Both will be run in MultiMC's working directory with INST_ID, INST_DIR, and INST_NAME as environment variables.</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacerMinecraft_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>88</width> + <height>186</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/gui/pages/LegacyJarModPage.cpp b/gui/pages/LegacyJarModPage.cpp new file mode 100644 index 00000000..b1c0d49a --- /dev/null +++ b/gui/pages/LegacyJarModPage.cpp @@ -0,0 +1,208 @@ +/* Copyright 2013 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 "LegacyJarModPage.h" +#include "ui_LegacyJarModPage.h" +#include "gui/dialogs/VersionSelectDialog.h" +#include "gui/dialogs/ProgressDialog.h" +#include "gui/dialogs/ModEditDialogCommon.h" +#include "logic/ModList.h" +#include "logic/LegacyInstance.h" +#include "logic/forge/ForgeVersion.h" +#include "logic/forge/ForgeVersionList.h" +#include "MultiMC.h" +#include <pathutils.h> +#include <QtGui/QKeyEvent> +#include <QFileDialog> +#include <QKeyEvent> + +LegacyJarModPage::LegacyJarModPage(LegacyInstance *inst, QWidget *parent) + : QWidget(parent), ui(new Ui::LegacyJarModPage), m_inst(inst) +{ + ui->setupUi(this); + m_jarmods = m_inst->jarModList(); + ui->jarModsTreeView->setModel(m_jarmods.get()); + ui->jarModsTreeView->setDragDropMode(QAbstractItemView::DragDrop); + ui->jarModsTreeView->setSelectionMode(QAbstractItemView::SingleSelection); + ui->jarModsTreeView->installEventFilter(this); + m_jarmods->startWatching(); + auto smodel = ui->jarModsTreeView->selectionModel(); + connect(smodel, SIGNAL(currentChanged(QModelIndex, QModelIndex)), + SLOT(jarCurrent(QModelIndex, QModelIndex))); +} + +LegacyJarModPage::~LegacyJarModPage() +{ + m_jarmods->stopWatching(); + delete ui; +} + +QString LegacyJarModPage::displayName() +{ + return tr("Jar Mods"); +} + +bool LegacyJarModPage::shouldDisplay() +{ + return !m_inst->isRunning(); +} + +QIcon LegacyJarModPage::icon() +{ + return QIcon::fromTheme("plugin-red"); +} + +QString LegacyJarModPage::id() +{ + return "jarmods"; +} + +bool LegacyJarModPage::eventFilter(QObject *obj, QEvent *ev) +{ + if (ev->type() != QEvent::KeyPress || obj != ui->jarModsTreeView) + { + return QWidget::eventFilter(obj, ev); + } + + QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev); + switch (keyEvent->key()) + { + case Qt::Key_Up: + { + if (keyEvent->modifiers() & Qt::ControlModifier) + { + on_moveJarUpBtn_clicked(); + return true; + } + break; + } + case Qt::Key_Down: + { + if (keyEvent->modifiers() & Qt::ControlModifier) + { + on_moveJarDownBtn_clicked(); + return true; + } + break; + } + case Qt::Key_Delete: + on_rmJarBtn_clicked(); + return true; + case Qt::Key_Plus: + on_addJarBtn_clicked(); + return true; + default: + break; + } + return QWidget::eventFilter(obj, ev); +} + +void LegacyJarModPage::on_addForgeBtn_clicked() +{ + VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this); + vselect.setExactFilter(1, m_inst->intendedVersionId()); + if (vselect.exec() && vselect.selectedVersion()) + { + ForgeVersionPtr forge = + std::dynamic_pointer_cast<ForgeVersion>(vselect.selectedVersion()); + if (!forge) + return; + auto entry = MMC->metacache()->resolveEntry("minecraftforge", forge->filename()); + if (entry->stale) + { + NetJob *fjob = new NetJob("Forge download"); + fjob->addNetAction(CacheDownload::make(forge->universal_url, entry)); + ProgressDialog dlg(this); + dlg.exec(fjob); + if (dlg.result() == QDialog::Accepted) + { + m_jarmods->stopWatching(); + m_jarmods->installMod(QFileInfo(entry->getFullPath())); + m_jarmods->startWatching(); + } + else + { + // failed to download forge :/ + } + } + else + { + m_jarmods->stopWatching(); + m_jarmods->installMod(QFileInfo(entry->getFullPath())); + m_jarmods->startWatching(); + } + } +} +void LegacyJarModPage::on_addJarBtn_clicked() +{ + //: Title of jar mod selection dialog + QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Jar Mods")); + for (auto filename : fileNames) + { + m_jarmods->stopWatching(); + m_jarmods->installMod(QFileInfo(filename)); + m_jarmods->startWatching(); + } +} + +void LegacyJarModPage::on_moveJarDownBtn_clicked() +{ + int first, last; + auto list = ui->jarModsTreeView->selectionModel()->selectedRows(); + + if (!lastfirst(list, first, last)) + return; + + m_jarmods->moveModsDown(first, last); +} + +void LegacyJarModPage::on_moveJarUpBtn_clicked() +{ + int first, last; + auto list = ui->jarModsTreeView->selectionModel()->selectedRows(); + + if (!lastfirst(list, first, last)) + return; + m_jarmods->moveModsUp(first, last); +} + +void LegacyJarModPage::on_rmJarBtn_clicked() +{ + int first, last; + auto list = ui->jarModsTreeView->selectionModel()->selectedRows(); + + if (!lastfirst(list, first, last)) + return; + m_jarmods->stopWatching(); + m_jarmods->deleteMods(first, last); + m_jarmods->startWatching(); +} + +void LegacyJarModPage::on_viewJarBtn_clicked() +{ + openDirInDefaultProgram(m_inst->jarModsDir(), true); +} + +void LegacyJarModPage::jarCurrent(QModelIndex current, QModelIndex previous) +{ + if (!current.isValid()) + { + ui->jarMIFrame->clear(); + return; + } + int row = current.row(); + Mod &m = m_jarmods->operator[](row); + ui->jarMIFrame->updateWithMod(m); +} diff --git a/gui/pages/LegacyJarModPage.h b/gui/pages/LegacyJarModPage.h new file mode 100644 index 00000000..016f4a8f --- /dev/null +++ b/gui/pages/LegacyJarModPage.h @@ -0,0 +1,63 @@ +/* Copyright 2013 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 <QDialog> +#include <logic/net/NetJob.h> +#include "BasePage.h" + +class ModList; +class LegacyInstance; +namespace Ui +{ +class LegacyJarModPage; +} + +class LegacyJarModPage : public QWidget, public BasePage +{ + Q_OBJECT + +public: + explicit LegacyJarModPage(LegacyInstance *inst, QWidget *parent = 0); + virtual ~LegacyJarModPage(); + + virtual QString displayName(); + virtual QIcon icon(); + virtual QString id(); + virtual QString helpPage() override { return "Legacy-jar-mods"; }; + virtual bool shouldDisplay(); + +private +slots: + + void on_addJarBtn_clicked(); + void on_rmJarBtn_clicked(); + void on_addForgeBtn_clicked(); + void on_moveJarUpBtn_clicked(); + void on_moveJarDownBtn_clicked(); + void on_viewJarBtn_clicked(); + + void jarCurrent(QModelIndex current, QModelIndex previous); + +protected: + virtual bool eventFilter(QObject *obj, QEvent *ev) override; + +private: + Ui::LegacyJarModPage *ui; + std::shared_ptr<ModList> m_jarmods; + LegacyInstance *m_inst; + NetJobPtr forgeJob; +}; diff --git a/gui/pages/LegacyJarModPage.ui b/gui/pages/LegacyJarModPage.ui new file mode 100644 index 00000000..a1da2b20 --- /dev/null +++ b/gui/pages/LegacyJarModPage.ui @@ -0,0 +1,158 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>LegacyJarModPage</class> + <widget class="QWidget" name="LegacyJarModPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>378</width> + <height>324</height> + </rect> + </property> + <property name="windowTitle"> + <string>LegacyJarModPage</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="ModListView" name="jarModsTreeView"> + <property name="verticalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOn</enum> + </property> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="jarModsButtonBox"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Selection</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="rmJarBtn"> + <property name="text"> + <string>&Remove</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="moveJarUpBtn"> + <property name="text"> + <string>Move &Up</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="moveJarDownBtn"> + <property name="text"> + <string>Move &Down</string> + </property> + </widget> + </item> + <item> + <widget class="LineSeparator" name="separator" native="true"/> + </item> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Install</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="addJarBtn"> + <property name="text"> + <string>&Add jar mod</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="addForgeBtn"> + <property name="text"> + <string>Install Forge</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="viewJarBtn"> + <property name="text"> + <string>&View Folder</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <widget class="MCModInfoFrame" name="jarMIFrame"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>ModListView</class> + <extends>QTreeView</extends> + <header>gui/widgets/ModListView.h</header> + </customwidget> + <customwidget> + <class>MCModInfoFrame</class> + <extends>QFrame</extends> + <header>gui/widgets/MCModInfoFrame.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>LineSeparator</class> + <extends>QWidget</extends> + <header>gui/widgets/LineSeparator.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/gui/pages/LegacyUpgradePage.cpp b/gui/pages/LegacyUpgradePage.cpp new file mode 100644 index 00000000..bb54210c --- /dev/null +++ b/gui/pages/LegacyUpgradePage.cpp @@ -0,0 +1,39 @@ +#include "LegacyUpgradePage.h" +#include <logic/LegacyInstance.h> +#include "ui_LegacyUpgradePage.h" + +QString LegacyUpgradePage::displayName() +{ + return tr("Upgrade"); +} + +QIcon LegacyUpgradePage::icon() +{ + return QIcon::fromTheme("checkupdate"); +} + +QString LegacyUpgradePage::id() +{ + return "upgrade"; +} + +LegacyUpgradePage::LegacyUpgradePage(LegacyInstance *inst, QWidget *parent) + : QWidget(parent), ui(new Ui::LegacyUpgradePage), m_inst(inst) +{ + ui->setupUi(this); +} + +LegacyUpgradePage::~LegacyUpgradePage() +{ + delete ui; +} + +void LegacyUpgradePage::on_upgradeButton_clicked() +{ + // now what? +} + +bool LegacyUpgradePage::shouldDisplay() +{ + return !m_inst->isRunning(); +}
\ No newline at end of file diff --git a/gui/pages/LegacyUpgradePage.h b/gui/pages/LegacyUpgradePage.h new file mode 100644 index 00000000..eb816a7a --- /dev/null +++ b/gui/pages/LegacyUpgradePage.h @@ -0,0 +1,48 @@ +/* Copyright 2014 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 <QWidget> + +#include <logic/OneSixInstance.h> +#include <logic/net/NetJob.h> +#include "BasePage.h" + +class EnabledItemFilter; +namespace Ui +{ +class LegacyUpgradePage; +} + +class LegacyUpgradePage : public QWidget, public BasePage +{ + Q_OBJECT + +public: + explicit LegacyUpgradePage(LegacyInstance *inst, QWidget *parent = 0); + virtual ~LegacyUpgradePage(); + virtual QString displayName() override; + virtual QIcon icon() override; + virtual QString id() override; + virtual QString helpPage() override { return "Legacy-upgrade"; }; + virtual bool shouldDisplay(); +private +slots: + void on_upgradeButton_clicked(); + +private: + Ui::LegacyUpgradePage *ui; + LegacyInstance *m_inst; +}; diff --git a/gui/pages/LegacyUpgradePage.ui b/gui/pages/LegacyUpgradePage.ui new file mode 100644 index 00000000..8d676eae --- /dev/null +++ b/gui/pages/LegacyUpgradePage.ui @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>LegacyUpgradePage</class> + <widget class="QWidget" name="LegacyUpgradePage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>546</width> + <height>405</height> + </rect> + </property> + <property name="windowTitle"> + <string>Upgrade</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QTextBrowser" name="textBrowser"> + <property name="html"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Bitstream Vera Sans'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:18pt; font-weight:600;">New format is available</span> </p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">MultiMC now supports old Minecraft versions in the new (OneSix) instance format. The old format won't be getting any new features and only the most critical bugfixes. As a consequence, you should upgrade this instance. </p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">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 style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Please report any issues on our <a href="https://github.com/MultiMC/MultiMC5/issues"><img src=":/icons/multimc/22x22/bug.png" /></a><a href="https://github.com/MultiMC/MultiMC5/issues"><span style=" text-decoration: underline; color:#68a0df;">github issues page</span></a>.</p></body></html></string> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QCommandLinkButton" name="upgradeButton"> + <property name="text"> + <string>Start the upgrade! (Not Yet Implemented, Coming Soonâ„¢)</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/gui/pages/LogPage.cpp b/gui/pages/LogPage.cpp new file mode 100644 index 00000000..dd088862 --- /dev/null +++ b/gui/pages/LogPage.cpp @@ -0,0 +1,137 @@ +#include "LogPage.h" +#include <gui/dialogs/CustomMessageBox.h> +#include <gui/dialogs/ProgressDialog.h> +#include <logic/MinecraftProcess.h> +#include <QtGui/QIcon> +#include "ui_LogPage.h" +#include "logic/net/PasteUpload.h" +#include <QScrollBar> + +QString LogPage::displayName() +{ + return tr("Minecraft Log"); +} + +QIcon LogPage::icon() +{ + return QIcon::fromTheme("refresh"); +} + +QString LogPage::id() +{ + return "console"; +} + +LogPage::LogPage(MinecraftProcess *proc, QWidget *parent) + : QWidget(parent), ui(new Ui::LogPage), m_process(proc) +{ + ui->setupUi(this); + connect(m_process, SIGNAL(log(QString, MessageLevel::Enum)), this, + SLOT(write(QString, MessageLevel::Enum))); +} + +LogPage::~LogPage() +{ + delete ui; +} + +bool LogPage::apply() +{ + return true; +} + +bool LogPage::shouldDisplay() +{ + return m_process->instance()->isRunning(); +} + +void LogPage::on_btnPaste_clicked() +{ + auto text = ui->text->toPlainText(); + ProgressDialog dialog(this); + PasteUpload *paste = new PasteUpload(this, text); + dialog.exec(paste); + if (!paste->successful()) + { + CustomMessageBox::selectable(this, "Upload failed", paste->failReason(), + QMessageBox::Critical)->exec(); + } +} + +void LogPage::writeColor(QString text, const char *color, const char * background) +{ + // append a paragraph + QString newtext; + newtext += "<span style=\""; + { + if (color) + newtext += QString("color:") + color + ";"; + if (background) + newtext += QString("background-color:") + background + ";"; + newtext += "font-family: monospace;"; + } + newtext += "\">"; + newtext += text.toHtmlEscaped(); + newtext += "</span>"; + ui->text->appendHtml(newtext); +} + +void LogPage::write(QString data, MessageLevel::Enum mode) +{ + QScrollBar *bar = ui->text->verticalScrollBar(); + int max_bar = bar->maximum(); + int val_bar = bar->value(); + if(isVisible()) + { + if (m_scroll_active) + { + m_scroll_active = (max_bar - val_bar) <= 1; + } + else + { + m_scroll_active = val_bar == max_bar; + } + } + if (data.endsWith('\n')) + data = data.left(data.length() - 1); + QStringList paragraphs = data.split('\n'); + QStringList filtered; + for (QString ¶graph : paragraphs) + { + // Quick hack for + if(paragraph.contains("Detected an attempt by a mod null to perform game activity during mod construction")) + continue; + filtered.append(paragraph.trimmed()); + } + QListIterator<QString> iter(filtered); + if (mode == MessageLevel::MultiMC) + while (iter.hasNext()) + writeColor(iter.next(), "blue", 0); + else if (mode == MessageLevel::Error) + while (iter.hasNext()) + writeColor(iter.next(), "red", 0); + else if (mode == MessageLevel::Warning) + while (iter.hasNext()) + writeColor(iter.next(), "orange", 0); + else if (mode == MessageLevel::Fatal) + while (iter.hasNext()) + writeColor(iter.next(), "red", "black"); + else if (mode == MessageLevel::Debug) + while (iter.hasNext()) + writeColor(iter.next(), "green", 0); + else if (mode == MessageLevel::PrePost) + while (iter.hasNext()) + writeColor(iter.next(), "grey", 0); + // TODO: implement other MessageLevels + else + while (iter.hasNext()) + writeColor(iter.next(), 0, 0); + if(isVisible()) + { + if (m_scroll_active) + { + bar->setValue(bar->maximum()); + } + m_last_scroll_value = bar->value(); + } +} diff --git a/gui/pages/LogPage.h b/gui/pages/LogPage.h new file mode 100644 index 00000000..7cdea2c1 --- /dev/null +++ b/gui/pages/LogPage.h @@ -0,0 +1,72 @@ +/* Copyright 2014 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 <QWidget> + +#include <logic/BaseInstance.h> +#include <logic/net/NetJob.h> +#include <logic/MinecraftProcess.h> +#include "BasePage.h" + +class EnabledItemFilter; +class MinecraftProcess; +namespace Ui +{ +class LogPage; +} + +class LogPage : public QWidget, public BasePage +{ + Q_OBJECT + +public: + explicit LogPage(MinecraftProcess *proc, QWidget *parent = 0); + virtual ~LogPage(); + virtual QString displayName() override; + virtual QIcon icon() override; + virtual QString id() override; + virtual bool apply(); + virtual QString helpPage() override { return "Minecraft-Log"; }; + virtual bool shouldDisplay(); + +private: + /** + * @brief write a colored paragraph + * @param data the string + * @param color the css color name + * this will only insert a single paragraph. + * \n are ignored. a real \n is always appended. + */ + void writeColor(QString text, const char *color, const char *background); + +private slots: + /** + * @brief write a string + * @param data the string + * @param mode the WriteMode + * lines have to be put through this as a whole! + */ + void write(QString data, MessageLevel::Enum level = MessageLevel::MultiMC); + void on_btnPaste_clicked(); + +private: + Ui::LogPage *ui; + MinecraftProcess *m_process; + int m_last_scroll_value = 0; + bool m_scroll_active = true; + int m_saved_offset = 0; +}; diff --git a/gui/pages/LogPage.ui b/gui/pages/LogPage.ui new file mode 100644 index 00000000..00b611b5 --- /dev/null +++ b/gui/pages/LogPage.ui @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>LogPage</class> + <widget class="QWidget" name="LogPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>831</width> + <height>596</height> + </rect> + </property> + <property name="windowTitle"> + <string>Log</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QPlainTextEdit" name="text"> + <property name="undoRedoEnabled"> + <bool>false</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + <property name="plainText"> + <string notr="true"/> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + <property name="centerOnScroll"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QPushButton" name="btnPaste"> + <property name="text"> + <string>Upload Log</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/gui/pages/ModFolderPage.cpp b/gui/pages/ModFolderPage.cpp new file mode 100644 index 00000000..2035e57a --- /dev/null +++ b/gui/pages/ModFolderPage.cpp @@ -0,0 +1,149 @@ +/* Copyright 2014 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 "MultiMC.h" + +#include <pathutils.h> +#include <QFileDialog> +#include <QMessageBox> +#include <QDebug> +#include <QEvent> +#include <QKeyEvent> +#include <QDesktopServices> +#include <QAbstractItemModel> + +#include "ModFolderPage.h" +#include "ui_ModFolderPage.h" + +#include "gui/dialogs/CustomMessageBox.h" +#include "gui/dialogs/ModEditDialogCommon.h" + +#include "logic/ModList.h" +#include "logic/Mod.h" + +QString ModFolderPage::displayName() +{ + return m_displayName; +} + +QIcon ModFolderPage::icon() +{ + return QIcon::fromTheme(m_iconName); +} + +QString ModFolderPage::id() +{ + return m_id; +} + +ModFolderPage::ModFolderPage(BaseInstance * inst, std::shared_ptr<ModList> mods, QString id, QString iconName, + QString displayName, QString helpPage, QWidget *parent) + : QWidget(parent), ui(new Ui::ModFolderPage) +{ + ui->setupUi(this); + m_inst = inst; + m_mods = mods; + m_id = id; + m_displayName = displayName; + m_iconName = iconName; + m_helpName = helpPage; + ui->modTreeView->setModel(m_mods.get()); + ui->modTreeView->installEventFilter(this); + m_mods->startWatching(); + auto smodel = ui->modTreeView->selectionModel(); + connect(smodel, SIGNAL(currentChanged(QModelIndex, QModelIndex)), + SLOT(modCurrent(QModelIndex, QModelIndex))); +} + +ModFolderPage::~ModFolderPage() +{ + m_mods->stopWatching(); + delete ui; +} + +bool ModFolderPage::shouldDisplay() +{ + if(m_inst) + return !m_inst->isRunning(); + return true; +} + +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<QKeyEvent *>(ev); + if (obj == ui->modTreeView) + return modListFilter(keyEvent); + return QWidget::eventFilter(obj, ev); +} + +void ModFolderPage::on_addModBtn_clicked() +{ + QStringList fileNames = QFileDialog::getOpenFileNames( + this, QApplication::translate("ModFolderPage", "Select Loader Mods")); + for (auto filename : fileNames) + { + m_mods->stopWatching(); + m_mods->installMod(QFileInfo(filename)); + m_mods->startWatching(); + } +} +void ModFolderPage::on_rmModBtn_clicked() +{ + int first, last; + auto list = ui->modTreeView->selectionModel()->selectedRows(); + + if (!lastfirst(list, first, last)) + return; + m_mods->stopWatching(); + m_mods->deleteMods(first, last); + m_mods->startWatching(); +} + +void ModFolderPage::on_viewModBtn_clicked() +{ + openDirInDefaultProgram(m_mods->dir().absolutePath(), true); +} + +void ModFolderPage::modCurrent(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (!current.isValid()) + { + ui->frame->clear(); + return; + } + int row = current.row(); + Mod &m = m_mods->operator[](row); + ui->frame->updateWithMod(m); +} diff --git a/gui/pages/ModFolderPage.h b/gui/pages/ModFolderPage.h new file mode 100644 index 00000000..c193f4c1 --- /dev/null +++ b/gui/pages/ModFolderPage.h @@ -0,0 +1,63 @@ +/* Copyright 2014 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 <QWidget> + +#include <logic/OneSixInstance.h> +#include <logic/net/NetJob.h> +#include "BasePage.h" + +class EnabledItemFilter; +class ModList; +namespace Ui +{ +class ModFolderPage; +} + +class ModFolderPage : public QWidget, public BasePage +{ + Q_OBJECT + +public: + explicit ModFolderPage(BaseInstance * inst, std::shared_ptr<ModList> mods, QString id, QString iconName, + QString displayName, QString helpPage = "" , QWidget *parent = 0); + virtual ~ModFolderPage(); + virtual QString displayName() override; + virtual QIcon icon() override; + virtual QString id() override; + virtual QString helpPage() override { return m_helpName; }; + virtual bool shouldDisplay(); +protected: + bool eventFilter(QObject *obj, QEvent *ev); + bool modListFilter(QKeyEvent *ev); +protected: + BaseInstance * m_inst; +private: + Ui::ModFolderPage *ui; + std::shared_ptr<ModList> m_mods; + QString m_iconName; + QString m_id; + QString m_displayName; + QString m_helpName; + +public slots: + void modCurrent(const QModelIndex ¤t, const QModelIndex &previous); + +private slots: + void on_addModBtn_clicked(); + void on_rmModBtn_clicked(); + void on_viewModBtn_clicked(); +}; diff --git a/gui/pages/ModFolderPage.ui b/gui/pages/ModFolderPage.ui new file mode 100644 index 00000000..eb29a2c0 --- /dev/null +++ b/gui/pages/ModFolderPage.ui @@ -0,0 +1,118 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ModFolderPage</class> + <widget class="QWidget" name="ModFolderPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>723</width> + <height>532</height> + </rect> + </property> + <property name="windowTitle"> + <string>Mods</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="ModListView" name="modTreeView"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="acceptDrops"> + <bool>true</bool> + </property> + <property name="dragDropMode"> + <enum>QAbstractItemView::DropOnly</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QPushButton" name="addModBtn"> + <property name="text"> + <string>&Add</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="rmModBtn"> + <property name="text"> + <string>&Remove</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="viewModBtn"> + <property name="text"> + <string>&View Folder</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <widget class="MCModInfoFrame" name="frame"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>ModListView</class> + <extends>QTreeView</extends> + <header>gui/widgets/ModListView.h</header> + </customwidget> + <customwidget> + <class>MCModInfoFrame</class> + <extends>QFrame</extends> + <header>gui/widgets/MCModInfoFrame.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/gui/pages/NotesPage.cpp b/gui/pages/NotesPage.cpp new file mode 100644 index 00000000..b4746a77 --- /dev/null +++ b/gui/pages/NotesPage.cpp @@ -0,0 +1,35 @@ +#include "NotesPage.h" +#include "ui_NotesPage.h" + +QString NotesPage::displayName() +{ + return tr("Notes"); +} + +QIcon NotesPage::icon() +{ + return QIcon::fromTheme("news"); +} + +QString NotesPage::id() +{ + return "notes"; +} + +NotesPage::NotesPage(BaseInstance *inst, QWidget *parent) + : QWidget(parent), ui(new Ui::NotesPage), m_inst(inst) +{ + ui->setupUi(this); + ui->noteEditor->setText(m_inst->notes()); +} + +NotesPage::~NotesPage() +{ + delete ui; +} + +bool NotesPage::apply() +{ + m_inst->setNotes(ui->noteEditor->toPlainText()); + return true; +} diff --git a/gui/pages/NotesPage.h b/gui/pages/NotesPage.h new file mode 100644 index 00000000..fe916f21 --- /dev/null +++ b/gui/pages/NotesPage.h @@ -0,0 +1,45 @@ +/* Copyright 2014 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 <QWidget> + +#include <logic/BaseInstance.h> +#include <logic/net/NetJob.h> +#include "BasePage.h" + +class EnabledItemFilter; +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() override; + virtual QIcon icon() override; + virtual QString id() override; + virtual bool apply(); + virtual QString helpPage() override { return "Notes"; }; + +private: + Ui::NotesPage *ui; + BaseInstance *m_inst; +}; diff --git a/gui/pages/NotesPage.ui b/gui/pages/NotesPage.ui new file mode 100644 index 00000000..ab33ffd3 --- /dev/null +++ b/gui/pages/NotesPage.ui @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>NotesPage</class> + <widget class="QWidget" name="NotesPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QTextEdit" name="noteEditor"> + <property name="verticalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOn</enum> + </property> + <property name="acceptRichText"> + <bool>false</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/gui/pages/ResourcePackPage.h b/gui/pages/ResourcePackPage.h new file mode 100644 index 00000000..06367905 --- /dev/null +++ b/gui/pages/ResourcePackPage.h @@ -0,0 +1,19 @@ +#pragma once +#include "ModFolderPage.h" + +class ResourcePackPage : public ModFolderPage +{ +public: + explicit ResourcePackPage(BaseInstance *instance, QWidget *parent = 0) + : ModFolderPage(instance, instance->resourcePackList(), "resourcepacks", "resourcepacks", + tr("Resource packs"), "Resource-packs", parent) + { + } + + virtual ~ResourcePackPage() {}; + virtual bool shouldDisplay() override + { + return !m_inst->traits().contains("no-texturepacks") && + !m_inst->traits().contains("texturepacks"); + } +}; diff --git a/gui/pages/ScreenshotsPage.cpp b/gui/pages/ScreenshotsPage.cpp new file mode 100644 index 00000000..051bc12d --- /dev/null +++ b/gui/pages/ScreenshotsPage.cpp @@ -0,0 +1,270 @@ +#include "ScreenshotsPage.h" +#include "ui_ScreenshotsPage.h" + +#include <QModelIndex> +#include <QMutableListIterator> +#include <QFileIconProvider> +#include <QFileSystemModel> +#include <QStyledItemDelegate> +#include <QLineEdit> +#include <QtGui/qevent.h> + +#include <pathutils.h> + +#include "gui/dialogs/ProgressDialog.h" +#include "gui/dialogs/CustomMessageBox.h" +#include "logic/net/NetJob.h" +#include "logic/screenshots/ImgurUpload.h" +#include "logic/screenshots/ImgurAlbumCreation.h" +#include "logic/tasks/SequentialTask.h" + +class FilterModel : public QIdentityProxyModel +{ +public: + virtual QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const + { + auto model = sourceModel(); + if (!model) + return QVariant(); + if (role == Qt::DisplayRole || role == Qt::EditRole) + { + QVariant result = sourceModel()->data(mapToSource(proxyIndex), role); + return result.toString().remove(QRegExp("\\.png$")); + } + if (role == Qt::DecorationRole) + { + QVariant result = sourceModel()->data(mapToSource(proxyIndex), QFileSystemModel::FilePathRole); + QString filePath = result.toString(); + if(thumbnailCache.contains(filePath)) + { + return thumbnailCache[filePath]; + } + bool failed = false; + QFileInfo info(filePath); + failed |= info.isDir(); + failed |= (info.suffix().compare("png", Qt::CaseInsensitive) != 0); + // WARNING: really an IF! this is purely for using break instead of goto... + while(!failed) + { + QImage image(info.absoluteFilePath()); + if (image.isNull()) + { + // TODO: schedule a retry. + failed = true; + break; + } + QImage thumbnail = image.scaledToWidth(512).scaledToWidth(256, Qt::SmoothTransformation); + QIcon icon(QPixmap::fromImage(thumbnail)); + // the casts are a hack for the stupid method being const. + ((QMap<QString, QIcon> &)thumbnailCache).insert(filePath, icon); + return icon; + } + // we failed anyway... + return sourceModel()->data(mapToSource(proxyIndex), QFileSystemModel::FileIconRole); + } + else + { + QVariant result = sourceModel()->data(mapToSource(proxyIndex), role); + return result; + } + } + virtual bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole) + { + auto model = sourceModel(); + if (!model) + return false; + if (role != Qt::EditRole) + return false; + // FIXME: this is a workaround for a bug in QFileSystemModel, where it doesn't + // sort after renames + { + ((QFileSystemModel *)model)->setNameFilterDisables(true); + ((QFileSystemModel *)model)->setNameFilterDisables(false); + } + return model->setData(mapToSource(index), value.toString() + ".png", role); + } +private: + QMap<QString, QIcon> thumbnailCache; +}; + +class CenteredEditingDelegate : public QStyledItemDelegate +{ +public: + explicit CenteredEditingDelegate(QObject *parent = 0) : QStyledItemDelegate(parent) + { + } + virtual ~CenteredEditingDelegate() + { + } + virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const + { + auto widget = QStyledItemDelegate::createEditor(parent, option, index); + auto foo = dynamic_cast<QLineEdit *>(widget); + if (foo) + { + foo->setAlignment(Qt::AlignHCenter); + foo->setFrame(true); + foo->setMaximumWidth(192); + } + return widget; + } +}; + +QString ScreenshotsPage::displayName() +{ + return tr("Screenshots"); +} + +QIcon ScreenshotsPage::icon() +{ + return QIcon::fromTheme("screenshots"); +} + +QString ScreenshotsPage::id() +{ + return "screenshots"; +} + +ScreenshotsPage::ScreenshotsPage(BaseInstance *instance, QWidget *parent) + : QWidget(parent), ui(new Ui::ScreenshotsPage) +{ + m_model.reset(new QFileSystemModel()); + m_filterModel.reset(new FilterModel()); + m_filterModel->setSourceModel(m_model.get()); + m_model->setFilter(QDir::Files | QDir::Writable | QDir::Readable); + m_model->setReadOnly(false); + m_folder = PathCombine(instance->minecraftRoot(), "screenshots"); + m_valid = ensureFolderPathExists(m_folder); + + ui->setupUi(this); + ui->listView->setModel(m_filterModel.get()); + ui->listView->setIconSize(QSize(128, 128)); + ui->listView->setGridSize(QSize(192, 128)); + ui->listView->setSpacing(9); + // ui->listView->setUniformItemSizes(true); + ui->listView->setLayoutMode(QListView::Batched); + ui->listView->setViewMode(QListView::IconMode); + ui->listView->setResizeMode(QListView::Adjust); + ui->listView->installEventFilter(this); + ui->listView->setEditTriggers(0); + ui->listView->setItemDelegate(new CenteredEditingDelegate(this)); + connect(ui->listView, SIGNAL(activated(QModelIndex)), SLOT(onItemActivated(QModelIndex))); +} + +bool ScreenshotsPage::eventFilter(QObject *obj, QEvent *evt) +{ + if (obj != ui->listView) + return QWidget::eventFilter(obj, evt); + if (evt->type() != QEvent::KeyPress) + { + return QWidget::eventFilter(obj, evt); + } + QKeyEvent *keyEvent = static_cast<QKeyEvent *>(evt); + switch (keyEvent->key()) + { + case Qt::Key_Delete: + on_deleteBtn_clicked(); + return true; + case Qt::Key_F2: + on_renameBtn_clicked(); + return true; + default: + break; + } + return QWidget::eventFilter(obj, evt); +} + +ScreenshotsPage::~ScreenshotsPage() +{ + delete ui; +} + +void ScreenshotsPage::onItemActivated(QModelIndex index) +{ + if (!index.isValid()) + return; + auto info = m_model->fileInfo(index); + QString fileName = info.absoluteFilePath(); + openFileInDefaultProgram(info.absoluteFilePath()); +} + +void ScreenshotsPage::on_viewFolderBtn_clicked() +{ + openDirInDefaultProgram(m_folder, true); +} + +void ScreenshotsPage::on_uploadBtn_clicked() +{ + auto selection = ui->listView->selectionModel()->selectedIndexes(); + if (selection.isEmpty()) + return; + + QList<ScreenshotPtr> uploaded; + auto job = std::make_shared<NetJob>("Screenshot Upload"); + for (auto item : selection) + { + auto info = m_model->fileInfo(item); + auto screenshot = std::make_shared<ScreenShot>(info); + uploaded.push_back(screenshot); + job->addNetAction(ImgurUpload::make(screenshot)); + } + SequentialTask task; + auto albumTask = std::make_shared<NetJob>("Imgur Album Creation"); + auto imgurAlbum = ImgurAlbumCreation::make(uploaded); + albumTask->addNetAction(imgurAlbum); + task.addTask(job); + task.addTask(albumTask); + ProgressDialog prog(this); + if (prog.exec(&task) != QDialog::Accepted) + { + CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), + tr("Unknown error"), QMessageBox::Warning)->exec(); + } + else + { + CustomMessageBox::selectable( + this, tr("Upload finished"), + tr("<a href=\"https://imgur.com/a/%1\">Visit album</a><br/>Delete hash: %2 (save " + "this if you want to be able to edit/delete the album)") + .arg(imgurAlbum->id(), imgurAlbum->deleteHash()), + QMessageBox::Information)->exec(); + } +} + +void ScreenshotsPage::on_deleteBtn_clicked() +{ + auto mbox = CustomMessageBox::selectable( + this, tr("Are you sure?"), tr("This will delete all selected screenshots."), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No); + std::unique_ptr<QMessageBox> box(mbox); + + if (box->exec() != QMessageBox::Yes) + return; + + auto selected = ui->listView->selectionModel()->selectedIndexes(); + for (auto item : selected) + { + m_model->remove(item); + } +} + +void ScreenshotsPage::on_renameBtn_clicked() +{ + auto selection = ui->listView->selectionModel()->selectedIndexes(); + if (selection.isEmpty()) + return; + ui->listView->edit(selection[0]); + // TODO: mass renaming +} + +void ScreenshotsPage::opened() +{ + if (m_valid) + { + QString path = QDir(m_folder).absolutePath(); + m_model->setRootPath(path); + ui->listView->setRootIndex(m_filterModel->mapFromSource(m_model->index(path))); + } +} diff --git a/gui/pages/ScreenshotsPage.h b/gui/pages/ScreenshotsPage.h new file mode 100644 index 00000000..4098e5e4 --- /dev/null +++ b/gui/pages/ScreenshotsPage.h @@ -0,0 +1,68 @@ +/* Copyright 2014 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 <QWidget> +#include <logic/OneSixInstance.h> +#include "BasePage.h" +#include <QIcon> +#include <QEvent> + +class QFileSystemModel; +class QIdentityProxyModel; +namespace Ui +{ +class ScreenshotsPage; +} + +class ScreenShot; +class ScreenshotList; +class ImgurAlbumCreation; + +class ScreenshotsPage : public QWidget, public BasePage +{ + Q_OBJECT + +public: + explicit ScreenshotsPage(BaseInstance *instance, QWidget *parent = 0); + virtual ~ScreenshotsPage(); + + virtual void opened() override; + + enum + { + NothingDone = 0x42 + }; + + virtual bool eventFilter(QObject *, QEvent *); + virtual QString displayName() override; + virtual QIcon icon() override; + virtual QString id() override; + virtual QString helpPage() override { return "Screenshots-management"; }; +private +slots: + void on_uploadBtn_clicked(); + void on_deleteBtn_clicked(); + void on_renameBtn_clicked(); + void on_viewFolderBtn_clicked(); + void onItemActivated(QModelIndex); + +private: + Ui::ScreenshotsPage *ui; + std::shared_ptr<QFileSystemModel> m_model; + std::shared_ptr<QIdentityProxyModel> m_filterModel; + QString m_folder; + bool m_valid = false; +}; diff --git a/gui/pages/ScreenshotsPage.ui b/gui/pages/ScreenshotsPage.ui new file mode 100644 index 00000000..5951ab02 --- /dev/null +++ b/gui/pages/ScreenshotsPage.ui @@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ScreenshotsPage</class> + <widget class="QWidget" name="ScreenshotsPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>723</width> + <height>532</height> + </rect> + </property> + <property name="windowTitle"> + <string>Mods</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QListView" name="listView"> + <property name="selectionMode"> + <enum>QAbstractItemView::ExtendedSelection</enum> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectItems</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QPushButton" name="uploadBtn"> + <property name="text"> + <string>&Upload</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="deleteBtn"> + <property name="text"> + <string>&Delete</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="renameBtn"> + <property name="text"> + <string>&Rename</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="viewFolderBtn"> + <property name="text"> + <string>&View Folder</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + <tabstops> + <tabstop>listView</tabstop> + <tabstop>uploadBtn</tabstop> + <tabstop>deleteBtn</tabstop> + <tabstop>renameBtn</tabstop> + <tabstop>viewFolderBtn</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/gui/pages/TexturePackPage.h b/gui/pages/TexturePackPage.h new file mode 100644 index 00000000..69a47204 --- /dev/null +++ b/gui/pages/TexturePackPage.h @@ -0,0 +1,17 @@ +#pragma once +#include "ModFolderPage.h" + +class TexturePackPage : public ModFolderPage +{ +public: + explicit TexturePackPage(BaseInstance *instance, QWidget *parent = 0) + : ModFolderPage(instance, instance->texturePackList(), "texturepacks", "resourcepacks", + tr("Texture packs"), "Texture-packs", parent) + { + } + virtual ~TexturePackPage() {}; + virtual bool shouldDisplay() override + { + return m_inst->traits().contains("texturepacks"); + } +}; diff --git a/gui/pages/VersionPage.cpp b/gui/pages/VersionPage.cpp new file mode 100644 index 00000000..34599111 --- /dev/null +++ b/gui/pages/VersionPage.cpp @@ -0,0 +1,394 @@ +/* Copyright 2014 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 "MultiMC.h" + +#include <pathutils.h> +#include <QFileDialog> +#include <QMessageBox> +#include <QDebug> +#include <QEvent> +#include <QKeyEvent> + +#include "VersionPage.h" +#include "ui_VersionPage.h" + +#include "gui/Platform.h" +#include "gui/dialogs/CustomMessageBox.h" +#include "gui/dialogs/VersionSelectDialog.h" +#include "gui/dialogs/ModEditDialogCommon.h" + +#include "gui/dialogs/ProgressDialog.h" + +#include "logic/ModList.h" +#include "logic/minecraft/InstanceVersion.h" +#include "logic/EnabledItemFilter.h" +#include "logic/forge/ForgeVersionList.h" +#include "logic/forge/ForgeInstaller.h" +#include "logic/liteloader/LiteLoaderVersionList.h" +#include "logic/liteloader/LiteLoaderInstaller.h" +#include "logic/minecraft/VersionBuilder.h" +#include "logic/auth/MojangAccountList.h" + +#include <QAbstractItemModel> +#include <logic/Mod.h> +#include <logic/icons/IconList.h> + +#include <QMessageBox> +#include <QListView> +#include <QString> +#include <QUrl> + +QString VersionPage::displayName() +{ + return tr("Version"); +} + +QIcon VersionPage::icon() +{ + return MMC->icons()->getIcon(m_inst->iconKey()); +} + +QString VersionPage::id() +{ + return "version"; +} + +bool VersionPage::shouldDisplay() +{ + return !m_inst->isRunning(); +} + +VersionPage::VersionPage(OneSixInstance *inst, QWidget *parent) + : QWidget(parent), ui(new Ui::VersionPage), m_inst(inst) +{ + ui->setupUi(this); + // libraries! + + m_version = m_inst->getFullVersion(); + if (m_version) + { + main_model = new EnabledItemFilter(this); + main_model->setActive(true); + main_model->setSourceModel(m_version.get()); + ui->libraryTreeView->setModel(main_model); + ui->libraryTreeView->installEventFilter(this); + ui->libraryTreeView->setSelectionMode(QAbstractItemView::SingleSelection); + connect(ui->libraryTreeView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &VersionPage::versionCurrent); + updateVersionControls(); + // select first item. + auto index = main_model->index(0,0); + if(index.isValid()) + ui->libraryTreeView->setCurrentIndex(index); + } + else + { + disableVersionControls(); + } + connect(m_inst, &OneSixInstance::versionReloaded, this, + &VersionPage::updateVersionControls); +} + +VersionPage::~VersionPage() +{ + delete ui; +} + +void VersionPage::updateVersionControls() +{ + ui->forgeBtn->setEnabled(true); + ui->liteloaderBtn->setEnabled(true); +} + +void VersionPage::disableVersionControls() +{ + ui->forgeBtn->setEnabled(false); + ui->liteloaderBtn->setEnabled(false); + ui->reloadLibrariesBtn->setEnabled(false); + ui->removeLibraryBtn->setEnabled(false); +} + +bool VersionPage::reloadInstanceVersion() +{ + try + { + m_inst->reloadVersion(); + return true; + } + catch (MMCError &e) + { + QMessageBox::critical(this, tr("Error"), e.cause()); + return false; + } + catch (...) + { + QMessageBox::critical( + this, tr("Error"), + tr("Failed to load the version description file for reasons unknown.")); + return false; + } +} + +void VersionPage::on_reloadLibrariesBtn_clicked() +{ + reloadInstanceVersion(); +} + +void VersionPage::on_removeLibraryBtn_clicked() +{ + if (ui->libraryTreeView->currentIndex().isValid()) + { + // FIXME: use actual model, not reloading. + if (!m_version->remove(ui->libraryTreeView->currentIndex().row())) + { + QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file")); + } + } +} + +void VersionPage::on_jarmodBtn_clicked() +{ + QFileDialog w; + QSet<QString> locations; + QString modsFolder = MMC->settings()->get("CentralModsDir").toString(); + auto f = [&](QStandardPaths::StandardLocation l) + { + QString location = QStandardPaths::writableLocation(l); + QFileInfo finfo(location); + if (!finfo.exists()) + return; + locations.insert(location); + }; + f(QStandardPaths::DesktopLocation); + f(QStandardPaths::DocumentsLocation); + f(QStandardPaths::DownloadLocation); + f(QStandardPaths::HomeLocation); + QList<QUrl> urls; + for (auto location : locations) + { + urls.append(QUrl::fromLocalFile(location)); + } + urls.append(QUrl::fromLocalFile(modsFolder)); + + w.setFileMode(QFileDialog::ExistingFiles); + w.setAcceptMode(QFileDialog::AcceptOpen); + w.setNameFilter(tr("Minecraft jar mods (*.zip *.jar)")); + w.setDirectory(modsFolder); + w.setSidebarUrls(urls); + + if (w.exec()) + m_version->installJarMods(w.selectedFiles()); +} + +void VersionPage::on_resetLibraryOrderBtn_clicked() +{ + try + { + m_version->resetOrder(); + } + catch (MMCError &e) + { + QMessageBox::critical(this, tr("Error"), e.cause()); + } +} + +void VersionPage::on_moveLibraryUpBtn_clicked() +{ + if (ui->libraryTreeView->selectionModel()->selectedRows().isEmpty()) + { + return; + } + try + { + const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row(); + const int newRow = 0; + m_version->move(row, InstanceVersion::MoveUp); + // ui->libraryTreeView->selectionModel()->setCurrentIndex(m_version->index(newRow), + // QItemSelectionModel::ClearAndSelect); + } + catch (MMCError &e) + { + QMessageBox::critical(this, tr("Error"), e.cause()); + } +} + +void VersionPage::on_moveLibraryDownBtn_clicked() +{ + if (ui->libraryTreeView->selectionModel()->selectedRows().isEmpty()) + { + return; + } + try + { + const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row(); + const int newRow = 0; + m_version->move(row, InstanceVersion::MoveDown); + // ui->libraryTreeView->selectionModel()->setCurrentIndex(m_version->index(newRow), + // QItemSelectionModel::ClearAndSelect); + } + catch (MMCError &e) + { + QMessageBox::critical(this, tr("Error"), e.cause()); + } +} + +void VersionPage::on_changeMCVersionBtn_clicked() +{ + VersionSelectDialog vselect(m_inst->versionList().get(), tr("Change Minecraft version"), + this); + if (!vselect.exec() || !vselect.selectedVersion()) + return; + + if (!MMC->accounts()->anyAccountIsValid()) + { + CustomMessageBox::selectable( + this, tr("Error"), + tr("MultiMC cannot download Minecraft or update instances unless you have at least " + "one account added.\nPlease add your Mojang or Minecraft account."), + QMessageBox::Warning)->show(); + return; + } + + if (m_inst->versionIsCustom()) + { + auto result = CustomMessageBox::selectable( + this, tr("Are you sure?"), + tr("This will remove any library/version customization you did previously. " + "This includes things like Forge install and similar."), + QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Abort, + QMessageBox::Abort)->exec(); + + if (result != QMessageBox::Ok) + return; + m_version->revertToVanilla(); + reloadInstanceVersion(); + } + m_inst->setIntendedVersionId(vselect.selectedVersion()->descriptor()); + + auto updateTask = m_inst->doUpdate(); + if (!updateTask) + { + return; + } + ProgressDialog tDialog(this); + connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString))); + tDialog.exec(updateTask.get()); +} + +void VersionPage::on_forgeBtn_clicked() +{ + // FIXME: use actual model, not reloading. Move logic to model. + if (m_version->hasFtbPack()) + { + if (QMessageBox::question( + this, tr("Revert?"), + tr("This action will remove the FTB pack version patch. Continue?")) != + QMessageBox::Yes) + { + return; + } + m_version->removeFtbPack(); + reloadInstanceVersion(); + } + if (m_version->hasDeprecatedVersionFiles()) + { + if (QMessageBox::question(this, tr("Revert?"), + tr("This action will remove deprecated version files " + "(custom.json and version.json). Continue?")) != + QMessageBox::Yes) + { + return; + } + m_version->removeDeprecatedVersionFiles(); + reloadInstanceVersion(); + } + VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this); + vselect.setExactFilter(1, m_inst->currentVersionId()); + vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") + + m_inst->currentVersionId()); + if (vselect.exec() && vselect.selectedVersion()) + { + ProgressDialog dialog(this); + dialog.exec( + ForgeInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this)); + } +} + +void VersionPage::on_liteloaderBtn_clicked() +{ + if (m_version->hasFtbPack()) + { + if (QMessageBox::question( + this, tr("Revert?"), + tr("This action will remove the FTB pack version patch. Continue?")) != + QMessageBox::Yes) + { + return; + } + m_version->removeFtbPack(); + reloadInstanceVersion(); + } + if (m_version->hasDeprecatedVersionFiles()) + { + if (QMessageBox::question(this, tr("Revert?"), + tr("This action will remove deprecated version files " + "(custom.json and version.json). Continue?")) != + QMessageBox::Yes) + { + return; + } + m_version->removeDeprecatedVersionFiles(); + reloadInstanceVersion(); + } + VersionSelectDialog vselect(MMC->liteloaderlist().get(), tr("Select LiteLoader version"), + this); + vselect.setExactFilter(1, m_inst->currentVersionId()); + vselect.setEmptyString(tr("No LiteLoader versions are currently available for Minecraft ") + + m_inst->currentVersionId()); + if (vselect.exec() && vselect.selectedVersion()) + { + ProgressDialog dialog(this); + dialog.exec( + LiteLoaderInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this)); + } +} + +void VersionPage::versionCurrent(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (!current.isValid()) + { + ui->removeLibraryBtn->setDisabled(true); + ui->moveLibraryDownBtn->setDisabled(true); + ui->moveLibraryUpBtn->setDisabled(true); + } + else + { + bool enabled = m_version->canRemove(current.row()); + ui->removeLibraryBtn->setEnabled(enabled); + ui->moveLibraryDownBtn->setEnabled(enabled); + ui->moveLibraryUpBtn->setEnabled(enabled); + } + QString selectedId = m_version->versionFileId(current.row()); + if (selectedId == "net.minecraft" || selectedId == "org.multimc.custom.json" || + selectedId == "org.multimc.version.json") + { + ui->changeMCVersionBtn->setEnabled(true); + } + else + { + ui->changeMCVersionBtn->setEnabled(false); + } +} diff --git a/gui/pages/VersionPage.h b/gui/pages/VersionPage.h new file mode 100644 index 00000000..dfbb6741 --- /dev/null +++ b/gui/pages/VersionPage.h @@ -0,0 +1,72 @@ +/* Copyright 2014 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 <QWidget> + +#include <logic/OneSixInstance.h> +#include <logic/net/NetJob.h> +#include "BasePage.h" + +class EnabledItemFilter; +namespace Ui +{ +class VersionPage; +} + +class VersionPage : public QWidget, public BasePage +{ + Q_OBJECT + +public: + explicit VersionPage(OneSixInstance *inst, QWidget *parent = 0); + virtual ~VersionPage(); + virtual QString displayName() override; + virtual QIcon icon() override; + virtual QString id() override; + virtual QString helpPage() override { return "Instance-version"; }; + virtual bool shouldDisplay(); +private +slots: + + // version tab + void on_forgeBtn_clicked(); + void on_liteloaderBtn_clicked(); + void on_reloadLibrariesBtn_clicked(); + void on_removeLibraryBtn_clicked(); + void on_resetLibraryOrderBtn_clicked(); + void on_moveLibraryUpBtn_clicked(); + void on_moveLibraryDownBtn_clicked(); + void on_jarmodBtn_clicked(); + + void updateVersionControls(); + void disableVersionControls(); + void on_changeMCVersionBtn_clicked(); + +protected: + /// FIXME: this shouldn't be necessary! + bool reloadInstanceVersion(); + +private: + Ui::VersionPage *ui; + std::shared_ptr<InstanceVersion> m_version; + EnabledItemFilter *main_model; + OneSixInstance *m_inst; + NetJobPtr forgeJob; + +public +slots: + void versionCurrent(const QModelIndex ¤t, const QModelIndex &previous); +}; diff --git a/gui/pages/VersionPage.ui b/gui/pages/VersionPage.ui new file mode 100644 index 00000000..f770df55 --- /dev/null +++ b/gui/pages/VersionPage.ui @@ -0,0 +1,197 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>VersionPage</class> + <widget class="QWidget" name="VersionPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>475</height> + </rect> + </property> + <property name="windowTitle"> + <string>Version</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QVBoxLayout" name="verticalLayout_10"> + <item> + <widget class="ModListView" name="libraryTreeView"> + <property name="verticalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOn</enum> + </property> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="headerHidden"> + <bool>false</bool> + </property> + <attribute name="headerVisible"> + <bool>true</bool> + </attribute> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Selection</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="changeMCVersionBtn"> + <property name="text"> + <string>Change version</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="moveLibraryUpBtn"> + <property name="toolTip"> + <string>This isn't implemented yet.</string> + </property> + <property name="text"> + <string>Move up</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="moveLibraryDownBtn"> + <property name="toolTip"> + <string>This isn't implemented yet.</string> + </property> + <property name="text"> + <string>Move down</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="removeLibraryBtn"> + <property name="text"> + <string>Remove</string> + </property> + </widget> + </item> + <item> + <widget class="LineSeparator" name="separator" native="true"/> + </item> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Install</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="forgeBtn"> + <property name="toolTip"> + <string>Replace any current custom version with Minecraft Forge</string> + </property> + <property name="text"> + <string>Install Forge</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="liteloaderBtn"> + <property name="text"> + <string>Install LiteLoader</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="jarmodBtn"> + <property name="text"> + <string>Add jar mod</string> + </property> + </widget> + </item> + <item> + <widget class="LineSeparator" name="widget" native="true"/> + </item> + <item> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>List</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="resetLibraryOrderBtn"> + <property name="toolTip"> + <string>This isn't implemented yet.</string> + </property> + <property name="text"> + <string>Reset order</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="reloadLibrariesBtn"> + <property name="text"> + <string>Reload</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_7"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>ModListView</class> + <extends>QTreeView</extends> + <header>gui/widgets/ModListView.h</header> + </customwidget> + <customwidget> + <class>LineSeparator</class> + <extends>QWidget</extends> + <header>gui/widgets/LineSeparator.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> |