diff --git a/api/logic/translations/TranslationsModel.cpp b/api/logic/translations/TranslationsModel.cpp index 1d44484a9..868aa98f2 100644 --- a/api/logic/translations/TranslationsModel.cpp +++ b/api/logic/translations/TranslationsModel.cpp @@ -193,6 +193,10 @@ QString TranslationsModel::selectedLanguage() void TranslationsModel::downloadIndex() { + if(d->m_index_job || d->m_dl_job) + { + return; + } qDebug() << "Downloading Translations Index..."; d->m_index_job.reset(new NetJob("Translations Index")); MetaEntryPtr entry = ENV.metacache()->resolveEntry("translations", "index"); diff --git a/application/CMakeLists.txt b/application/CMakeLists.txt index e51e6eb10..2814d6be1 100644 --- a/application/CMakeLists.txt +++ b/application/CMakeLists.txt @@ -114,6 +114,13 @@ SET(MULTIMC_SOURCES # GUI - setup wizard setupwizard/SetupWizard.h setupwizard/SetupWizard.cpp + setupwizard/AnalyticsWizardPage.cpp + setupwizard/AnalyticsWizardPage.h + setupwizard/BaseWizardPage.h + setupwizard/JavaWizardPage.cpp + setupwizard/JavaWizardPage.h + setupwizard/LanguageWizardPage.cpp + setupwizard/LanguageWizardPage.h # GUI - themes themes/FusionTheme.cpp @@ -246,6 +253,8 @@ SET(MULTIMC_SOURCES widgets/ServerStatus.h widgets/VersionListView.cpp widgets/VersionListView.h + widgets/VersionSelectWidget.cpp + widgets/VersionSelectWidget.h widgets/ProgressWidget.h widgets/ProgressWidget.cpp @@ -289,7 +298,6 @@ SET(MULTIMC_UIS dialogs/CopyInstanceDialog.ui dialogs/NewInstanceDialog.ui dialogs/AboutDialog.ui - dialogs/VersionSelectDialog.ui dialogs/ProgressDialog.ui dialogs/IconPickerDialog.ui dialogs/ProfileSelectDialog.ui diff --git a/application/dialogs/VersionSelectDialog.cpp b/application/dialogs/VersionSelectDialog.cpp index 50b543db4..8290d6d6b 100644 --- a/application/dialogs/VersionSelectDialog.cpp +++ b/application/dialogs/VersionSelectDialog.cpp @@ -14,9 +14,12 @@ */ #include "VersionSelectDialog.h" -#include "ui_VersionSelectDialog.h" -#include +#include +#include +#include +#include +#include #include #include "CustomMessageBox.h" @@ -27,165 +30,102 @@ #include #include "MultiMC.h" #include +#include -VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent, - bool cancelable) - : QDialog(parent), ui(new Ui::VersionSelectDialog) +VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent, bool cancelable) + : QDialog(parent) { - ui->setupUi(this); + setObjectName(QStringLiteral("VersionSelectDialog")); + resize(400, 347); + m_verticalLayout = new QVBoxLayout(this); + m_verticalLayout->setObjectName(QStringLiteral("verticalLayout")); + + m_versionWidget = new VersionSelectWidget(vlist, parent); + m_verticalLayout->addWidget(m_versionWidget); + + m_horizontalLayout = new QHBoxLayout(); + m_horizontalLayout->setObjectName(QStringLiteral("horizontalLayout")); + + m_refreshButton = new QPushButton(this); + m_refreshButton->setObjectName(QStringLiteral("refreshButton")); + m_horizontalLayout->addWidget(m_refreshButton); + + m_buttonBox = new QDialogButtonBox(this); + m_buttonBox->setObjectName(QStringLiteral("buttonBox")); + m_buttonBox->setOrientation(Qt::Horizontal); + m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + m_horizontalLayout->addWidget(m_buttonBox); + + m_verticalLayout->addLayout(m_horizontalLayout); + + retranslate(); + + QObject::connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + QObject::connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + QMetaObject::connectSlotsByName(this); setWindowModality(Qt::WindowModal); setWindowTitle(title); m_vlist = vlist; - m_proxyModel = new VersionProxyModel(this); - m_proxyModel->setSourceModel(vlist); - - ui->listView->setModel(m_proxyModel); - ui->listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch); - ui->sneakyProgressBar->setHidden(true); - if (!cancelable) { - ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); + m_buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); } } +void VersionSelectDialog::retranslate() +{ + // FIXME: overrides custom title given in constructor! + setWindowTitle(QApplication::translate("VersionSelectDialog", "Choose Version", Q_NULLPTR)); + m_refreshButton->setToolTip(QApplication::translate("VersionSelectDialog", "Reloads the version list.", Q_NULLPTR)); + m_refreshButton->setText(QApplication::translate("VersionSelectDialog", "&Refresh", Q_NULLPTR)); +} + void VersionSelectDialog::setEmptyString(QString emptyString) { - ui->listView->setEmptyString(emptyString); + m_versionWidget->setEmptyString(emptyString); } void VersionSelectDialog::setEmptyErrorString(QString emptyErrorString) { - ui->listView->setEmptyErrorString(emptyErrorString); -} - -VersionSelectDialog::~VersionSelectDialog() -{ - delete ui; + m_versionWidget->setEmptyErrorString(emptyErrorString); } void VersionSelectDialog::setResizeOn(int column) { - ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::ResizeToContents); - resizeOnColumn = column; - ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch); + m_versionWidget->setResizeOn(column); } int VersionSelectDialog::exec() { QDialog::open(); - if (!m_vlist->isLoaded()) - { - loadList(); - } - else - { - if (m_proxyModel->rowCount() == 0) - { - ui->listView->setEmptyMode(VersionListView::String); - } - preselect(); - } + m_versionWidget->initialize(); return QDialog::exec(); } -void VersionSelectDialog::closeEvent(QCloseEvent * event) -{ - if(loadTask) - { - loadTask->abort(); - loadTask->deleteLater(); - loadTask = nullptr; - } - QDialog::closeEvent(event); -} - -void VersionSelectDialog::loadList() -{ - if(loadTask) - { - return; - } - loadTask = m_vlist->getLoadTask(); - if (!loadTask) - { - return; - } - connect(loadTask, &Task::finished, this, &VersionSelectDialog::onTaskFinished); - connect(loadTask, &Task::progress, this, &VersionSelectDialog::changeProgress); - loadTask->start(); - ui->sneakyProgressBar->setHidden(false); -} - -void VersionSelectDialog::onTaskFinished() -{ - if (!loadTask->successful()) - { - CustomMessageBox::selectable(this, tr("Error"), - tr("List update failed:\n%1").arg(loadTask->failReason()), - QMessageBox::Warning)->show(); - if (m_proxyModel->rowCount() == 0) - { - ui->listView->setEmptyMode(VersionListView::ErrorString); - } - } - else if (m_proxyModel->rowCount() == 0) - { - ui->listView->setEmptyMode(VersionListView::String); - } - ui->sneakyProgressBar->setHidden(true); - loadTask->deleteLater(); - loadTask = nullptr; - preselect(); -} - -void VersionSelectDialog::changeProgress(qint64 current, qint64 total) -{ - ui->sneakyProgressBar->setMaximum(total); - ui->sneakyProgressBar->setValue(current); -} - -void VersionSelectDialog::preselect() -{ - if(preselectedAlready) - return; - preselectedAlready = true; - selectRecommended(); -} - void VersionSelectDialog::selectRecommended() { - auto idx = m_proxyModel->getRecommended(); - if(idx.isValid()) - { - ui->listView->selectionModel()->setCurrentIndex(idx,QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); - ui->listView->scrollTo(idx, QAbstractItemView::PositionAtCenter); - } + m_versionWidget->selectRecommended(); } BaseVersionPtr VersionSelectDialog::selectedVersion() const { - auto currentIndex = ui->listView->selectionModel()->currentIndex(); - auto variant = m_proxyModel->data(currentIndex, BaseVersionList::VersionPointerRole); - return variant.value(); + return m_versionWidget->selectedVersion(); } void VersionSelectDialog::on_refreshButton_clicked() { - loadList(); + m_versionWidget->loadList(); } void VersionSelectDialog::setExactFilter(BaseVersionList::ModelRoles role, QString filter) { - m_proxyModel->setFilter(role, filter, true); + m_versionWidget->setExactFilter(role, filter); } void VersionSelectDialog::setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter) { - m_proxyModel->setFilter(role, filter, false); + m_versionWidget->setFuzzyFilter(role, filter); } - -#include "VersionSelectDialog.moc" diff --git a/application/dialogs/VersionSelectDialog.h b/application/dialogs/VersionSelectDialog.h index 6b83535ed..8916ce598 100644 --- a/application/dialogs/VersionSelectDialog.h +++ b/application/dialogs/VersionSelectDialog.h @@ -18,8 +18,15 @@ #include #include + #include "BaseVersionList.h" +class QVBoxLayout; +class QHBoxLayout; +class QDialogButtonBox; +class VersionSelectWidget; +class QPushButton; + namespace Ui { class VersionSelectDialog; @@ -32,14 +39,10 @@ class VersionSelectDialog : public QDialog Q_OBJECT public: - explicit VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent = 0, - bool cancelable = true); - ~VersionSelectDialog(); + explicit VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent = 0, bool cancelable = true); + virtual ~VersionSelectDialog() {}; - virtual int exec(); - - //! Starts a task that loads the list. - void loadList(); + int exec() override; BaseVersionPtr selectedVersion() const; @@ -50,22 +53,19 @@ public: void setResizeOn(int column); void setUseLatest(const bool useLatest); -protected: - virtual void closeEvent ( QCloseEvent* ); - -private -slots: +private slots: void on_refreshButton_clicked(); - void onTaskFinished(); - void changeProgress(qint64 current, qint64 total); - private: - void preselect(); + void retranslate(); void selectRecommended(); private: - Ui::VersionSelectDialog *ui = nullptr; + VersionSelectWidget *m_versionWidget = nullptr; + QVBoxLayout *m_verticalLayout = nullptr; + QHBoxLayout *m_horizontalLayout = nullptr; + QPushButton *m_refreshButton = nullptr; + QDialogButtonBox *m_buttonBox = nullptr; BaseVersionList *m_vlist = nullptr; @@ -74,6 +74,4 @@ private: int resizeOnColumn = 0; Task * loadTask = nullptr; - - bool preselectedAlready = false; }; diff --git a/application/dialogs/VersionSelectDialog.ui b/application/dialogs/VersionSelectDialog.ui deleted file mode 100644 index 420e853d2..000000000 --- a/application/dialogs/VersionSelectDialog.ui +++ /dev/null @@ -1,120 +0,0 @@ - - - VersionSelectDialog - - - - 0 - 0 - 400 - 347 - - - - Choose Version - - - - - - Qt::ScrollBarAlwaysOff - - - true - - - false - - - false - - - true - - - true - - - false - - - - - - - 24 - - - %p% - - - - - - - - - Reloads the version list. - - - &Refresh - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - - VersionListView - QTreeView -
widgets/VersionListView.h
-
-
- - - - buttonBox - accepted() - VersionSelectDialog - accept() - - - 257 - 290 - - - 157 - 274 - - - - - buttonBox - rejected() - VersionSelectDialog - reject() - - - 325 - 290 - - - 286 - 274 - - - - -
diff --git a/application/setupwizard/AnalyticsWizardPage.cpp b/application/setupwizard/AnalyticsWizardPage.cpp new file mode 100644 index 000000000..40fb0b887 --- /dev/null +++ b/application/setupwizard/AnalyticsWizardPage.cpp @@ -0,0 +1,79 @@ +#include "AnalyticsWizardPage.h" +#include + +#include +#include +#include + +#include + +AnalyticsWizardPage::AnalyticsWizardPage(QWidget *parent) + : BaseWizardPage(parent) +{ + setObjectName(QStringLiteral("analyticsPage")); + verticalLayout_3 = new QVBoxLayout(this); + verticalLayout_3->setObjectName(QStringLiteral("verticalLayout_3")); + textBrowser = new QTextBrowser(this); + textBrowser->setObjectName(QStringLiteral("textBrowser")); + textBrowser->setAcceptRichText(false); + textBrowser->setOpenExternalLinks(true); + verticalLayout_3->addWidget(textBrowser); + + checkBox = new QCheckBox(this); + checkBox->setObjectName(QStringLiteral("checkBox")); + checkBox->setChecked(true); + verticalLayout_3->addWidget(checkBox); + retranslate(); +} + +AnalyticsWizardPage::~AnalyticsWizardPage() +{ +} + +bool AnalyticsWizardPage::validatePage() +{ + auto settings = MMC->settings(); + auto analytics = MMC->analytics(); + auto status = checkBox->isChecked(); + settings->set("AnalyticsSeen", analytics->version()); + settings->set("Analytics", status); + return true; +} + +bool AnalyticsWizardPage::isRequired() +{ + auto settings = MMC->settings(); + auto analytics = MMC->analytics(); + if (!settings->get("Analytics").toBool()) + { + return false; + } + if (settings->get("AnalyticsSeen").toInt() < analytics->version()) + { + return true; + } + return false; +} + +void AnalyticsWizardPage::retranslate() +{ + setTitle(QApplication::translate("AnalyticsWizardPage", "Analytics", Q_NULLPTR)); + setSubTitle(QApplication::translate("AnalyticsWizardPage", "We track some anonymous statistics about users.", Q_NULLPTR)); + textBrowser->setHtml(QApplication::translate( + "AnalyticsWizardPage", + "" + "

MultiMC sends anonymous usage statistics on every start of the application. This helps us decide what platforms and issues to focus on.

" + "

The data is processed by Google Analytics, see their article on the " + "matter.

" + "

The following data is collected:

" + "
  • A random unique ID of the MultiMC installation.
    It is stored in the application settings (multimc.cfg).
  • " + "
  • Anonymized (partial) IP address.
  • " + "
  • MultiMC version.
  • " + "
  • Operating system name, version and architecture.
  • " + "
  • CPU architecture (kernel architecture on linux).
  • " + "
  • Size of system memory.
  • " + "
  • Java version, architecture and memory settings.
" + "

If we change the tracked information, you will see this page again.

", + Q_NULLPTR)); + checkBox->setText(QApplication::translate("AnalyticsWizardPage", "Enable Analytics", Q_NULLPTR)); +} diff --git a/application/setupwizard/AnalyticsWizardPage.h b/application/setupwizard/AnalyticsWizardPage.h new file mode 100644 index 000000000..88fbed011 --- /dev/null +++ b/application/setupwizard/AnalyticsWizardPage.h @@ -0,0 +1,26 @@ +#pragma once + +#include "BaseWizardPage.h" + +class QVBoxLayout; +class QTextBrowser; +class QCheckBox; + +class AnalyticsWizardPage : public BaseWizardPage +{ + Q_OBJECT; +public: + explicit AnalyticsWizardPage(QWidget *parent = Q_NULLPTR); + virtual ~AnalyticsWizardPage(); + + bool validatePage() override; + static bool isRequired(); + +protected: + void retranslate() override; + +private: + QVBoxLayout *verticalLayout_3 = nullptr; + QTextBrowser *textBrowser = nullptr; + QCheckBox *checkBox = nullptr; +}; \ No newline at end of file diff --git a/application/setupwizard/BaseWizardPage.h b/application/setupwizard/BaseWizardPage.h new file mode 100644 index 000000000..9ad54e094 --- /dev/null +++ b/application/setupwizard/BaseWizardPage.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +class BaseWizardPage : public QWizardPage +{ +public: + explicit BaseWizardPage(QWidget *parent = Q_NULLPTR) + : QWizardPage(parent) + { + } + virtual ~BaseWizardPage() {}; + + virtual bool wantsRefreshButton() + { + return false; + } + virtual void refresh() + { + } + +protected: + virtual void retranslate() = 0; + void changeEvent(QEvent * event) override + { + if (event->type() == QEvent::LanguageChange) + { + retranslate(); + } + QWizardPage::changeEvent(event); + } +}; diff --git a/application/setupwizard/JavaWizardPage.cpp b/application/setupwizard/JavaWizardPage.cpp new file mode 100644 index 000000000..31b8ff715 --- /dev/null +++ b/application/setupwizard/JavaWizardPage.cpp @@ -0,0 +1,327 @@ +#include "JavaWizardPage.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +JavaWizardPage::JavaWizardPage(QWidget *parent) + :BaseWizardPage(parent) +{ + m_availableMemory = Sys::getSystemRam() / (1024ull * 1024ull); + + goodIcon = MMC->getThemedIcon("status-good"); + yellowIcon = MMC->getThemedIcon("status-yellow"); + badIcon = MMC->getThemedIcon("status-bad"); + setupUi(); + + connect(m_minMemSpinBox, SIGNAL(valueChanged(int)), this, SLOT(memoryValueChanged(int))); + connect(m_maxMemSpinBox, SIGNAL(valueChanged(int)), this, SLOT(memoryValueChanged(int))); + connect(m_permGenSpinBox, SIGNAL(valueChanged(int)), this, SLOT(memoryValueChanged(int))); + connect(m_versionWidget, &VersionSelectWidget::selectedVersionChanged, this, &JavaWizardPage::javaVersionSelected); + connect(m_javaBrowseBtn, &QPushButton::clicked, this, &JavaWizardPage::on_javaBrowseBtn_clicked); + connect(m_javaPathTextBox, &QLineEdit::textEdited, this, &JavaWizardPage::checkJavaPath); +} + +void JavaWizardPage::setupUi() +{ + setObjectName(QStringLiteral("javaPage")); + m_verticalLayout = new QVBoxLayout(this); + m_verticalLayout->setObjectName(QStringLiteral("verticalLayout")); + + m_versionWidget = new VersionSelectWidget(MMC->javalist().get(), this); + m_versionWidget->setResizeOn(2); + m_verticalLayout->addWidget(m_versionWidget); + + m_horizontalLayout = new QHBoxLayout(); + m_horizontalLayout->setObjectName(QStringLiteral("horizontalLayout")); + m_javaPathTextBox = new QLineEdit(this); + m_javaPathTextBox->setObjectName(QStringLiteral("javaPathTextBox")); + + m_horizontalLayout->addWidget(m_javaPathTextBox); + + m_javaBrowseBtn = new QPushButton(this); + m_javaBrowseBtn->setObjectName(QStringLiteral("javaBrowseBtn")); + /* + QSizePolicy sizePolicy2(QSizePolicy::Fixed, QSizePolicy::Fixed); + sizePolicy2.setHorizontalStretch(0); + sizePolicy2.setVerticalStretch(0); + sizePolicy2.setHeightForWidth(m_javaBrowseBtn->sizePolicy().hasHeightForWidth()); + m_javaBrowseBtn->setSizePolicy(sizePolicy2); + m_javaBrowseBtn->setMaximumSize(QSize(28, 16777215)); + */ + m_javaBrowseBtn->setText(QStringLiteral("...")); + m_horizontalLayout->addWidget(m_javaBrowseBtn); + + m_javaStatusLabel = new IconLabel(this, badIcon, QSize(16, 16)); + m_horizontalLayout->addWidget(m_javaStatusLabel); + + m_verticalLayout->addLayout(m_horizontalLayout); + + m_memoryGroupBox = new QGroupBox(this); + m_memoryGroupBox->setObjectName(QStringLiteral("memoryGroupBox")); + m_gridLayout_2 = new QGridLayout(m_memoryGroupBox); + m_gridLayout_2->setObjectName(QStringLiteral("gridLayout_2")); + + m_labelMinMem = new QLabel(m_memoryGroupBox); + m_labelMinMem->setObjectName(QStringLiteral("labelMinMem")); + m_gridLayout_2->addWidget(m_labelMinMem, 0, 0, 1, 1); + + m_minMemSpinBox = new QSpinBox(m_memoryGroupBox); + m_minMemSpinBox->setObjectName(QStringLiteral("minMemSpinBox")); + m_minMemSpinBox->setSuffix(QStringLiteral(" MB")); + m_minMemSpinBox->setMinimum(256); + m_minMemSpinBox->setMaximum(m_availableMemory); + m_minMemSpinBox->setSingleStep(128); + m_minMemSpinBox->setValue(256); + m_gridLayout_2->addWidget(m_minMemSpinBox, 0, 1, 1, 1); + + m_labelMaxMem = new QLabel(m_memoryGroupBox); + m_labelMaxMem->setObjectName(QStringLiteral("labelMaxMem")); + m_gridLayout_2->addWidget(m_labelMaxMem, 1, 0, 1, 1); + + m_maxMemSpinBox = new QSpinBox(m_memoryGroupBox); + m_maxMemSpinBox->setObjectName(QStringLiteral("maxMemSpinBox")); + m_maxMemSpinBox->setSuffix(QStringLiteral(" MB")); + m_maxMemSpinBox->setMinimum(512); + m_maxMemSpinBox->setMaximum(m_availableMemory); + m_maxMemSpinBox->setSingleStep(128); + m_maxMemSpinBox->setValue(1024); + m_gridLayout_2->addWidget(m_maxMemSpinBox, 1, 1, 1, 1); + + m_labelPermGen = new QLabel(m_memoryGroupBox); + m_labelPermGen->setObjectName(QStringLiteral("labelPermGen")); + m_labelPermGen->setText(QStringLiteral("PermGen:")); + m_gridLayout_2->addWidget(m_labelPermGen, 2, 0, 1, 1); + m_labelPermGen->setVisible(false); + + m_permGenSpinBox = new QSpinBox(m_memoryGroupBox); + m_permGenSpinBox->setObjectName(QStringLiteral("permGenSpinBox")); + m_permGenSpinBox->setSuffix(QStringLiteral(" MB")); + m_permGenSpinBox->setMinimum(64); + m_permGenSpinBox->setMaximum(m_availableMemory); + m_permGenSpinBox->setSingleStep(8); + m_permGenSpinBox->setValue(128); + m_gridLayout_2->addWidget(m_permGenSpinBox, 2, 1, 1, 1); + m_permGenSpinBox->setVisible(false); + + m_verticalLayout->addWidget(m_memoryGroupBox); + + retranslate(); +} + +void JavaWizardPage::refresh() +{ + m_versionWidget->loadList(); +} + +void JavaWizardPage::initializePage() +{ + m_versionWidget->initialize(); + auto s = MMC->settings(); + // Memory + m_minMemSpinBox->setValue(s->get("MinMemAlloc").toInt()); + m_maxMemSpinBox->setValue(s->get("MaxMemAlloc").toInt()); + m_permGenSpinBox->setValue(s->get("PermGen").toInt()); +} + +bool JavaWizardPage::validatePage() +{ + auto settings = MMC->settings(); + auto path = m_javaPathTextBox->text(); + switch(javaStatus) + { + case JavaStatus::Bad: + { + int button = CustomMessageBox::selectable( + this, + tr("No Java version selected"), + tr("You didn't select a Java version or selected something that doesn't work.\n" + "MultiMC will not be able to start Minecraft.\n" + "Do you wish to proceed without any Java?" + "\n\n" + "You can change the Java version in the settings later.\n" + ), + QMessageBox::Warning, + QMessageBox::Yes | QMessageBox::No, + QMessageBox::NoButton + )->exec(); + if(button == QMessageBox::No) + { + return false; + } + } + break; + case JavaStatus::Pending: + { + return false; + } + case JavaStatus::Good: + { + settings->set("JavaPath", path); + } + } + + // Memory + auto s = MMC->settings(); + s->set("MinMemAlloc", m_minMemSpinBox->value()); + s->set("MaxMemAlloc", m_maxMemSpinBox->value()); + if (m_permGenSpinBox->isVisible()) + { + s->set("PermGen", m_permGenSpinBox->value()); + } + else + { + s->reset("PermGen"); + } + return true; +} + +bool JavaWizardPage::isRequired() +{ + QString currentHostName = QHostInfo::localHostName(); + QString oldHostName = MMC->settings()->get("LastHostname").toString(); + if (currentHostName != oldHostName) + { + MMC->settings()->set("LastHostname", currentHostName); + return true; + } + QString currentJavaPath = MMC->settings()->get("JavaPath").toString(); + QString actualPath = FS::ResolveExecutable(currentJavaPath); + if (actualPath.isNull()) + { + return true; + } + return false; +} + +bool JavaWizardPage::wantsRefreshButton() +{ + return true; +} + +void JavaWizardPage::memoryValueChanged(int) +{ + int min = m_minMemSpinBox->value(); + int max = m_maxMemSpinBox->value(); + QObject *obj = sender(); + if (obj == m_minMemSpinBox) + { + if (min > max) + { + m_maxMemSpinBox->setValue(min); + } + } + else if (obj == m_maxMemSpinBox) + { + if (min > max) + { + m_minMemSpinBox->setValue(max); + } + } + checkJavaPath(m_javaPathTextBox->text()); +} + +void JavaWizardPage::javaVersionSelected(BaseVersionPtr version) +{ + auto java = std::dynamic_pointer_cast(version); + if(!java) + { + return; + } + auto visible = java->id.requiresPermGen(); + m_labelPermGen->setVisible(visible); + m_permGenSpinBox->setVisible(visible); + m_javaPathTextBox->setText(java->path); + checkJavaPath(java->path); +} + +void JavaWizardPage::on_javaBrowseBtn_clicked() +{ + QString raw_path = QFileDialog::getOpenFileName(this, tr("Find Java executable")); + if(raw_path.isNull()) + { + return; + } + QString cooked_path = FS::NormalizePath(raw_path); + m_javaPathTextBox->setText(cooked_path); + checkJavaPath(cooked_path); +} + +void JavaWizardPage::setJavaStatus(JavaWizardPage::JavaStatus status) +{ + javaStatus = status; + switch(javaStatus) + { + case JavaStatus::Good: + m_javaStatusLabel->setIcon(goodIcon); + break; + case JavaStatus::Pending: + m_javaStatusLabel->setIcon(yellowIcon); + break; + default: + case JavaStatus::Bad: + m_javaStatusLabel->setIcon(badIcon); + break; + } +} + +void JavaWizardPage::checkJavaPath(const QString &path) +{ + auto realPath = FS::ResolveExecutable(path); + if(realPath.isNull()) + { + setJavaStatus(JavaStatus::Bad); + return; + } + setJavaStatus(JavaStatus::Pending); + m_checker.reset(new JavaChecker()); + m_checker->m_path = path; + m_checker->m_minMem = m_minMemSpinBox->value(); + m_checker->m_maxMem = m_maxMemSpinBox->value(); + if(m_permGenSpinBox->isVisible()) + { + m_checker->m_permGen = m_permGenSpinBox->value(); + } + connect(m_checker.get(), &JavaChecker::checkFinished, this, &JavaWizardPage::checkFinished); + m_checker->performCheck(); +} + +void JavaWizardPage::checkFinished(JavaCheckResult result) +{ + if(result.valid) + { + setJavaStatus(JavaStatus::Good); + } + else + { + setJavaStatus(JavaStatus::Bad); + } + m_checker.reset(); +} + +void JavaWizardPage::retranslate() +{ + setTitle(QApplication::translate("JavaWizardPage", "Java", Q_NULLPTR)); + setSubTitle(QApplication::translate("JavaWizardPage", "You do not have a working Java set up yet or it went missing.\n" + "Please select one of the following or browse for a java executable.", Q_NULLPTR)); + m_memoryGroupBox->setTitle(QApplication::translate("JavaPage", "Memory", Q_NULLPTR)); + m_maxMemSpinBox->setToolTip(QApplication::translate("JavaPage", "The maximum amount of memory Minecraft is allowed to use.", Q_NULLPTR)); + m_labelMinMem->setText(QApplication::translate("JavaPage", "Minimum memory allocation:", Q_NULLPTR)); + m_labelMaxMem->setText(QApplication::translate("JavaPage", "Maximum memory allocation:", Q_NULLPTR)); + m_minMemSpinBox->setToolTip(QApplication::translate("JavaPage", "The amount of memory Minecraft is started with.", Q_NULLPTR)); + m_permGenSpinBox->setToolTip(QApplication::translate("JavaPage", "The amount of memory available to store loaded Java classes.", Q_NULLPTR)); +} diff --git a/application/setupwizard/JavaWizardPage.h b/application/setupwizard/JavaWizardPage.h new file mode 100644 index 000000000..5a61ff02f --- /dev/null +++ b/application/setupwizard/JavaWizardPage.h @@ -0,0 +1,79 @@ +#pragma once + +#include "BaseWizardPage.h" +#include +#include +#include +#include + +class QLineEdit; +class VersionSelectWidget; +class QSpinBox; +class QPushButton; +class QVBoxLayout; +class QHBoxLayout; +class QGroupBox; +class QGridLayout; +class QLabel; +class IconLabel; + +class JavaWizardPage : public BaseWizardPage +{ + Q_OBJECT; +public: + explicit JavaWizardPage(QWidget *parent = Q_NULLPTR); + + virtual ~JavaWizardPage() + { + }; + + bool wantsRefreshButton() override; + void refresh() override; + void initializePage() override; + bool validatePage() override; + static bool isRequired(); + + enum class JavaStatus + { + Pending, + Good, + Bad + } javaStatus; + +protected slots: + void memoryValueChanged(int); + void javaVersionSelected(BaseVersionPtr version); + void on_javaBrowseBtn_clicked(); + void checkFinished(JavaCheckResult result); + +protected: /* methods */ + void checkJavaPath(const QString &path); + void setJavaStatus(JavaStatus status); + void setupUi(); + void retranslate() override; + +private: /* data */ + VersionSelectWidget *m_versionWidget = nullptr; + QVBoxLayout *m_verticalLayout = nullptr; + + QLineEdit * m_javaPathTextBox = nullptr; + QPushButton * m_javaBrowseBtn = nullptr; + IconLabel * m_javaStatusLabel = nullptr; + QHBoxLayout *m_horizontalLayout = nullptr; + + QGroupBox *m_memoryGroupBox = nullptr; + QGridLayout *m_gridLayout_2 = nullptr; + QSpinBox *m_maxMemSpinBox = nullptr; + QLabel *m_labelMinMem = nullptr; + QLabel *m_labelMaxMem = nullptr; + QSpinBox *m_minMemSpinBox = nullptr; + QLabel *m_labelPermGen = nullptr; + QSpinBox *m_permGenSpinBox = nullptr; + QIcon goodIcon; + QIcon yellowIcon; + QIcon badIcon; + + uint64_t m_availableMemory = 0ull; + shared_qobject_ptr m_checker; +}; + diff --git a/application/setupwizard/LanguageWizardPage.cpp b/application/setupwizard/LanguageWizardPage.cpp new file mode 100644 index 000000000..cf427a0da --- /dev/null +++ b/application/setupwizard/LanguageWizardPage.cpp @@ -0,0 +1,74 @@ +#include "LanguageWizardPage.h" +#include +#include + +#include +#include + +LanguageWizardPage::LanguageWizardPage(QWidget *parent) + : BaseWizardPage(parent) +{ + setObjectName(QStringLiteral("languagePage")); + verticalLayout = new QVBoxLayout(this); + verticalLayout->setObjectName(QStringLiteral("verticalLayout")); + languageView = new QListView(this); + languageView->setObjectName(QStringLiteral("languageView")); + verticalLayout->addWidget(languageView); + retranslate(); + + auto translations = MMC->translations(); + auto index = translations->selectedIndex(); + languageView->setModel(translations.get()); + languageView->setCurrentIndex(index); + connect(languageView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &LanguageWizardPage::languageRowChanged); +} + +LanguageWizardPage::~LanguageWizardPage() +{ +} + +bool LanguageWizardPage::wantsRefreshButton() +{ + return true; +} + +void LanguageWizardPage::refresh() +{ + auto translations = MMC->translations(); + translations->downloadIndex(); +} + +bool LanguageWizardPage::validatePage() +{ + auto settings = MMC->settings(); + auto translations = MMC->translations(); + QString key = translations->data(languageView->currentIndex(), Qt::UserRole).toString(); + settings->set("Language", key); + return true; +} + +bool LanguageWizardPage::isRequired() +{ + auto settings = MMC->settings(); + if (settings->get("Language").toString().isEmpty()) + return true; + return false; +} + +void LanguageWizardPage::retranslate() +{ + setTitle(QApplication::translate("LanguageWizardPage", "Language", Q_NULLPTR)); + setSubTitle(QApplication::translate("LanguageWizardPage", "Select the language to use in MultiMC", Q_NULLPTR)); +} + +void LanguageWizardPage::languageRowChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (current == previous) + { + return; + } + auto translations = MMC->translations(); + QString key = translations->data(current, Qt::UserRole).toString(); + translations->selectLanguage(key); + translations->updateLanguage(key); +} diff --git a/application/setupwizard/LanguageWizardPage.h b/application/setupwizard/LanguageWizardPage.h new file mode 100644 index 000000000..9c24b29d0 --- /dev/null +++ b/application/setupwizard/LanguageWizardPage.h @@ -0,0 +1,33 @@ +#pragma once + +#include "BaseWizardPage.h" + +class QVBoxLayout; +class QListView; + +class LanguageWizardPage : public BaseWizardPage +{ + Q_OBJECT; +public: + explicit LanguageWizardPage(QWidget *parent = Q_NULLPTR); + + virtual ~LanguageWizardPage(); + + bool wantsRefreshButton() override; + + void refresh() override; + + bool validatePage() override; + + static bool isRequired(); + +protected: + void retranslate() override; + +protected slots: + void languageRowChanged(const QModelIndex ¤t, const QModelIndex &previous); + +private: + QVBoxLayout *verticalLayout = nullptr; + QListView *languageView = nullptr; +}; diff --git a/application/setupwizard/SetupWizard.cpp b/application/setupwizard/SetupWizard.cpp index 18b8aff9b..fc5b2555e 100644 --- a/application/setupwizard/SetupWizard.cpp +++ b/application/setupwizard/SetupWizard.cpp @@ -1,306 +1,21 @@ #include "SetupWizard.h" + +#include "LanguageWizardPage.h" +#include "JavaWizardPage.h" +#include "AnalyticsWizardPage.h" + #include "translations/TranslationsModel.h" #include #include #include +#include + enum Page { Language, Java, Analytics, - // Themes, - // Accounts -}; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class BaseWizardPage : public QWizardPage -{ -public: - explicit BaseWizardPage(QWidget *parent = Q_NULLPTR) - : QWizardPage(parent) - { - } - virtual ~BaseWizardPage() {}; - -protected: - virtual void retranslate() = 0; - void changeEvent(QEvent * event) override - { - if (event->type() == QEvent::LanguageChange) - { - retranslate(); - } - QWizardPage::changeEvent(event); - } -}; - -class LanguageWizardPage : public BaseWizardPage -{ - Q_OBJECT; -public: - explicit LanguageWizardPage(QWidget *parent = Q_NULLPTR) - : BaseWizardPage(parent) - { - setObjectName(QStringLiteral("languagePage")); - verticalLayout = new QVBoxLayout(this); - verticalLayout->setObjectName(QStringLiteral("verticalLayout")); - languageView = new QListView(this); - languageView->setObjectName(QStringLiteral("languageView")); - verticalLayout->addWidget(languageView); - retranslate(); - - auto translations = MMC->translations(); - auto index = translations->selectedIndex(); - languageView->setModel(translations.get()); - languageView->setCurrentIndex(index); - connect(languageView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &LanguageWizardPage::languageRowChanged); - } - - virtual ~LanguageWizardPage() - { - }; - - bool validatePage() override - { - auto settings = MMC->settings(); - auto translations = MMC->translations(); - QString key = translations->data(languageView->currentIndex(), Qt::UserRole).toString(); - settings->set("Language", key); - return true; - } - - static bool isRequired() - { - auto settings = MMC->settings(); - if (settings->get("Language").toString().isEmpty()) - return true; - return false; - } - -protected: - void retranslate() override - { - setTitle(QApplication::translate("LanguageWizardPage", "Language", Q_NULLPTR)); - setSubTitle(QApplication::translate("LanguageWizardPage", "Select the language to use in MultiMC", Q_NULLPTR)); - } - -protected slots: - void languageRowChanged(const QModelIndex ¤t, const QModelIndex &previous) - { - if (current == previous) - { - return; - } - auto translations = MMC->translations(); - QString key = translations->data(current, Qt::UserRole).toString(); - translations->selectLanguage(key); - translations->updateLanguage(key); - } - -private: - QVBoxLayout *verticalLayout = nullptr; - QListView *languageView = nullptr; -}; - -#include -#include -#include -#include - -class JavaWizardPage : public BaseWizardPage -{ - Q_OBJECT; -public: - explicit JavaWizardPage(QWidget *parent = Q_NULLPTR) - : BaseWizardPage(parent) - { - setObjectName(QStringLiteral("javaPage")); - // FIXME: this is dumb and ugly. - qDebug() << "Java path needs resetting, showing Java selection dialog..."; - JavaInstallPtr java; - - VersionSelectDialog vselect(MMC->javalist().get(), tr("Select a Java version"), this, false); - vselect.setResizeOn(2); - vselect.exec(); - - if (vselect.selectedVersion()) - { - java = std::dynamic_pointer_cast(vselect.selectedVersion()); - } - else - { - CustomMessageBox::selectable(this, - tr("Invalid version selected"), - tr("You didn't select a valid Java version, so MultiMC will select the default. You can change this in the settings dialog."), - QMessageBox::Warning)->show(); - JavaUtils ju; - java = ju.GetDefaultJava(); - } - if (java) - { - MMC->settings()->set("JavaPath", java->path); - } - else - { - MMC->settings()->set("JavaPath", QString("java")); - } - retranslate(); - } - - virtual ~JavaWizardPage() - { - }; - - bool validatePage() override - { - auto settings = MMC->settings(); - /* - auto analytics = MMC->analytics(); - auto status = checkBox->isChecked(); - settings->set("AnalyticsSeen", analytics->version()); - settings->set("Analytics", status); - */ - return true; - } - - static bool isRequired() - { - QString currentHostName = QHostInfo::localHostName(); - QString oldHostName = MMC->settings()->get("LastHostname").toString(); - if (currentHostName != oldHostName) - { - MMC->settings()->set("LastHostname", currentHostName); - return true; - } - QString currentJavaPath = MMC->settings()->get("JavaPath").toString(); - QString actualPath = FS::ResolveExecutable(currentJavaPath); - if (actualPath.isNull()) - { - return true; - } - return false; - } - -protected: - void retranslate() override - { - setTitle(QApplication::translate("JavaWizardPage", "Java", Q_NULLPTR)); - setSubTitle(QApplication::translate("JavaWizardPage", "You need to set up Java to proceed.", Q_NULLPTR)); - /* - textBrowser->setHtml(QApplication::translate("JavaWizardPage", - "" - "

MultiMC sends anonymous usage statistics on every start of the application. This helps us decide what platforms and issues to focus on.

" - "

The data is processed by Google Analytics, see their article on the matter.

" - "

The following data is collected:

" - "
  • A random unique ID of the MultiMC installation.
    It is stored in the application settings (multimc.cfg).
  • " - "
  • Anonymized IP address.
    Last octet is set to 0 by Google and not stored.
  • " - "
  • MultiMC version.
  • " - "
  • Operating system name, version and architecture.
  • " - "
  • CPU architecture (kernel architecture on linux).
  • " - "
  • Size of system memory.
  • " - "
  • Java version, architecture and memory settings.
" - "

The analytics will activate on next start, unless you disable them in the settings.

" - "

If we change the tracked information, analytics won't be active for the first run and you will see this page again.

", Q_NULLPTR)); - checkBox->setText(QApplication::translate("JavaWizardPage", "Enable Analytics", Q_NULLPTR)); - */ - } -private: - /* - QVBoxLayout *verticalLayout_3 = nullptr; - QTextBrowser *textBrowser = nullptr; - QCheckBox *checkBox = nullptr; - */ -}; - -class AnalyticsWizardPage : public BaseWizardPage -{ - Q_OBJECT; -public: - explicit AnalyticsWizardPage(QWidget *parent = Q_NULLPTR) - : BaseWizardPage(parent) - { - setObjectName(QStringLiteral("analyticsPage")); - verticalLayout_3 = new QVBoxLayout(this); - verticalLayout_3->setObjectName(QStringLiteral("verticalLayout_3")); - textBrowser = new QTextBrowser(this); - textBrowser->setObjectName(QStringLiteral("textBrowser")); - textBrowser->setAcceptRichText(false); - textBrowser->setOpenExternalLinks(true); - verticalLayout_3->addWidget(textBrowser); - - checkBox = new QCheckBox(this); - checkBox->setObjectName(QStringLiteral("checkBox")); - checkBox->setChecked(true); - verticalLayout_3->addWidget(checkBox); - retranslate(); - } - - virtual ~AnalyticsWizardPage() - { - }; - - bool validatePage() override - { - auto settings = MMC->settings(); - auto analytics = MMC->analytics(); - auto status = checkBox->isChecked(); - settings->set("AnalyticsSeen", analytics->version()); - settings->set("Analytics", status); - return true; - } - - static bool isRequired() - { - auto settings = MMC->settings(); - auto analytics = MMC->analytics(); - if(!settings->get("Analytics").toBool()) - { - return false; - } - if(settings->get("AnalyticsSeen").toInt() < analytics->version()) - { - return true; - } - return false; - } - -protected: - void retranslate() override - { - setTitle(QApplication::translate("AnalyticsWizardPage", "Analytics", Q_NULLPTR)); - setSubTitle(QApplication::translate("AnalyticsWizardPage", "We track some anonymous statistics about users.", Q_NULLPTR)); - textBrowser->setHtml(QApplication::translate("AnalyticsWizardPage", - "" - "

MultiMC sends anonymous usage statistics on every start of the application. This helps us decide what platforms and issues to focus on.

" - "

The data is processed by Google Analytics, see their article on the matter.

" - "

The following data is collected:

" - "
  • A random unique ID of the MultiMC installation.
    It is stored in the application settings (multimc.cfg).
  • " - "
  • Anonymized IP address.
    Last octet is set to 0 by Google and not stored.
  • " - "
  • MultiMC version.
  • " - "
  • Operating system name, version and architecture.
  • " - "
  • CPU architecture (kernel architecture on linux).
  • " - "
  • Size of system memory.
  • " - "
  • Java version, architecture and memory settings.
" - "

The analytics will activate on next start, unless you disable them in the settings.

" - "

If we change the tracked information, analytics won't be active for the first run and you will see this page again.

", Q_NULLPTR)); - checkBox->setText(QApplication::translate("AnalyticsWizardPage", "Enable Analytics", Q_NULLPTR)); - } -private: - QVBoxLayout *verticalLayout_3 = nullptr; - QTextBrowser *textBrowser = nullptr; - QCheckBox *checkBox = nullptr; }; SetupWizard::SetupWizard(QWidget *parent) : QWizard(parent) @@ -309,7 +24,12 @@ SetupWizard::SetupWizard(QWidget *parent) : QWizard(parent) resize(615, 659); // make it ugly everywhere to avoid variability in theming setWizardStyle(QWizard::ClassicStyle); - setOptions(QWizard::NoCancelButton | QWizard::IndependentPages); + setOptions(QWizard::NoCancelButton | QWizard::IndependentPages | QWizard::HaveCustomButton1); + + retranslate(); + + connect(this, &QWizard::currentIdChanged, this, &SetupWizard::pageChanged); + if (LanguageWizardPage::isRequired()) { setPage(Page::Language, new LanguageWizardPage(this)); @@ -326,12 +46,54 @@ SetupWizard::SetupWizard(QWidget *parent) : QWizard(parent) void SetupWizard::retranslate() { - setButtonText(QWizard::NextButton, tr("Next >")); - setButtonText(QWizard::BackButton, tr("< Back")); - setButtonText(QWizard::FinishButton, tr("Finish")); + setButtonText(QWizard::NextButton, tr("&Next >")); + setButtonText(QWizard::BackButton, tr("< &Back")); + setButtonText(QWizard::FinishButton, tr("&Finish")); + setButtonText(QWizard::CustomButton1, tr("&Refresh")); setWindowTitle(QApplication::translate("SetupWizard", "MultiMC Quick Setup", Q_NULLPTR)); } +BaseWizardPage * SetupWizard::getBasePage(int id) +{ + if(id == -1) + return nullptr; + auto pagePtr = page(id); + if(!pagePtr) + return nullptr; + return dynamic_cast(pagePtr); +} + +BaseWizardPage * SetupWizard::getCurrentBasePage() +{ + return getBasePage(currentId()); +} + +void SetupWizard::pageChanged(int id) +{ + auto basePagePtr = getBasePage(id); + if(!basePagePtr) + { + return; + } + if(basePagePtr->wantsRefreshButton()) + { + setButtonLayout({QWizard::CustomButton1, QWizard::Stretch, QWizard::BackButton, QWizard::NextButton, QWizard::FinishButton}); + auto customButton = button(QWizard::CustomButton1); + connect(customButton, &QAbstractButton::pressed, [&](){ + auto basePagePtr = getCurrentBasePage(); + if(basePagePtr) + { + basePagePtr->refresh(); + } + }); + } + else + { + setButtonLayout({QWizard::Stretch, QWizard::BackButton, QWizard::NextButton, QWizard::FinishButton}); + } +} + + void SetupWizard::changeEvent(QEvent *event) { if (event->type() == QEvent::LanguageChange) @@ -356,4 +118,3 @@ bool SetupWizard::isRequired() return false; } -#include "SetupWizard.moc" diff --git a/application/setupwizard/SetupWizard.h b/application/setupwizard/SetupWizard.h index adf254c78..a1e757d8e 100644 --- a/application/setupwizard/SetupWizard.h +++ b/application/setupwizard/SetupWizard.h @@ -22,6 +22,8 @@ namespace Ui class SetupWizard; } +class BaseWizardPage; + class SetupWizard : public QWizard { Q_OBJECT @@ -31,6 +33,11 @@ public: /* con/destructors */ virtual ~SetupWizard(); void changeEvent(QEvent * event) override; + BaseWizardPage *getBasePage(int id); + BaseWizardPage *getCurrentBasePage(); + +private slots: + void pageChanged(int id); public: /* methods */ static bool isRequired(); diff --git a/application/widgets/ProgressWidget.cpp b/application/widgets/ProgressWidget.cpp index 7b51eca02..8b6709160 100644 --- a/application/widgets/ProgressWidget.cpp +++ b/application/widgets/ProgressWidget.cpp @@ -1,7 +1,23 @@ // Licensed under the Apache-2.0 license. See README.md for details. #include "ProgressWidget.h" - + /* + textBrowser->setHtml(QApplication::translate("JavaWizardPage", + "" + "

MultiMC sends anonymous usage statistics on every start of the application. This helps us decide what platforms and issues to focus on.

" + "

The data is processed by Google Analytics, see their article on the matter.

" + "

The following data is collected:

" + "
  • A random unique ID of the MultiMC installation.
    It is stored in the application settings (multimc.cfg).
  • " + "
  • Anonymized IP address.
    Last octet is set to 0 by Google and not stored.
  • " + "
  • MultiMC version.
  • " + "
  • Operating system name, version and architecture.
  • " + "
  • CPU architecture (kernel architecture on linux).
  • " + "
  • Size of system memory.
  • " + "
  • Java version, architecture and memory settings.
" + "

The analytics will activate on next start, unless you disable them in the settings.

" + "

If we change the tracked information, analytics won't be active for the first run and you will see this page again.

", Q_NULLPTR)); + checkBox->setText(QApplication::translate("JavaWizardPage", "Enable Analytics", Q_NULLPTR)); + */ #include #include #include diff --git a/application/widgets/VersionSelectWidget.cpp b/application/widgets/VersionSelectWidget.cpp new file mode 100644 index 000000000..18284a910 --- /dev/null +++ b/application/widgets/VersionSelectWidget.cpp @@ -0,0 +1,181 @@ +#include "VersionSelectWidget.h" +#include +#include +#include "VersionListView.h" +#include +#include +#include + +VersionSelectWidget::VersionSelectWidget(BaseVersionList* vlist, QWidget* parent) + : QWidget(parent), m_vlist(vlist) +{ + setObjectName(QStringLiteral("VersionSelectWidget")); + verticalLayout = new QVBoxLayout(this); + verticalLayout->setObjectName(QStringLiteral("verticalLayout")); + verticalLayout->setContentsMargins(0, 0, 0, 0); + + m_proxyModel = new VersionProxyModel(this); + m_proxyModel->setSourceModel(vlist); + + listView = new VersionListView(this); + listView->setObjectName(QStringLiteral("listView")); + listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + listView->setAlternatingRowColors(true); + listView->setRootIsDecorated(false); + listView->setItemsExpandable(false); + listView->setWordWrap(true); + listView->header()->setCascadingSectionResizes(true); + listView->header()->setStretchLastSection(false); + listView->setModel(m_proxyModel); + listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch); + verticalLayout->addWidget(listView); + + sneakyProgressBar = new QProgressBar(this); + sneakyProgressBar->setObjectName(QStringLiteral("sneakyProgressBar")); + sneakyProgressBar->setFormat(QStringLiteral("%p%")); + verticalLayout->addWidget(sneakyProgressBar); + sneakyProgressBar->setHidden(true); + connect(listView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &VersionSelectWidget::currentRowChanged); + + QMetaObject::connectSlotsByName(this); +} + +void VersionSelectWidget::setEmptyString(QString emptyString) +{ + listView->setEmptyString(emptyString); +} + +void VersionSelectWidget::setEmptyErrorString(QString emptyErrorString) +{ + listView->setEmptyErrorString(emptyErrorString); +} + +VersionSelectWidget::~VersionSelectWidget() +{ +} + +void VersionSelectWidget::setResizeOn(int column) +{ + listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::ResizeToContents); + resizeOnColumn = column; + listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch); +} + +void VersionSelectWidget::initialize() +{ + if (!m_vlist->isLoaded()) + { + loadList(); + } + else + { + if (m_proxyModel->rowCount() == 0) + { + listView->setEmptyMode(VersionListView::String); + } + preselect(); + } +} + +void VersionSelectWidget::closeEvent(QCloseEvent * event) +{ + if(loadTask) + { + loadTask->abort(); + loadTask->deleteLater(); + loadTask = nullptr; + } + QWidget::closeEvent(event); +} + +void VersionSelectWidget::loadList() +{ + if(loadTask) + { + return; + } + loadTask = m_vlist->getLoadTask(); + if (!loadTask) + { + return; + } + connect(loadTask, &Task::finished, this, &VersionSelectWidget::onTaskFinished); + connect(loadTask, &Task::progress, this, &VersionSelectWidget::changeProgress); + loadTask->start(); + sneakyProgressBar->setHidden(false); +} + +void VersionSelectWidget::onTaskFinished() +{ + if (!loadTask->successful()) + { + CustomMessageBox::selectable(this, tr("Error"), + tr("List update failed:\n%1").arg(loadTask->failReason()), + QMessageBox::Warning)->show(); + if (m_proxyModel->rowCount() == 0) + { + listView->setEmptyMode(VersionListView::ErrorString); + } + } + else if (m_proxyModel->rowCount() == 0) + { + listView->setEmptyMode(VersionListView::String); + } + sneakyProgressBar->setHidden(true); + loadTask->deleteLater(); + loadTask = nullptr; + preselect(); +} + +void VersionSelectWidget::changeProgress(qint64 current, qint64 total) +{ + sneakyProgressBar->setMaximum(total); + sneakyProgressBar->setValue(current); +} + +void VersionSelectWidget::currentRowChanged(const QModelIndex& current, const QModelIndex&) +{ + auto variant = m_proxyModel->data(current, BaseVersionList::VersionPointerRole); + emit selectedVersionChanged(variant.value()); +} + +void VersionSelectWidget::preselect() +{ + if(preselectedAlready) + return; + preselectedAlready = true; + selectRecommended(); +} + +void VersionSelectWidget::selectRecommended() +{ + auto idx = m_proxyModel->getRecommended(); + if(idx.isValid()) + { + listView->selectionModel()->setCurrentIndex(idx,QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + listView->scrollTo(idx, QAbstractItemView::PositionAtCenter); + } +} + +bool VersionSelectWidget::hasVersions() const +{ + return m_proxyModel->rowCount(QModelIndex()) != 0; +} + +BaseVersionPtr VersionSelectWidget::selectedVersion() const +{ + auto currentIndex = listView->selectionModel()->currentIndex(); + auto variant = m_proxyModel->data(currentIndex, BaseVersionList::VersionPointerRole); + return variant.value(); +} + +void VersionSelectWidget::setExactFilter(BaseVersionList::ModelRoles role, QString filter) +{ + m_proxyModel->setFilter(role, filter, true); +} + +void VersionSelectWidget::setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter) +{ + m_proxyModel->setFilter(role, filter, false); +} \ No newline at end of file diff --git a/application/widgets/VersionSelectWidget.h b/application/widgets/VersionSelectWidget.h new file mode 100644 index 000000000..0fc9f2e69 --- /dev/null +++ b/application/widgets/VersionSelectWidget.h @@ -0,0 +1,76 @@ +/* Copyright 2013-2017 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 +#include "BaseVersionList.h" + +class VersionProxyModel; +class VersionListView; +class QVBoxLayout; +class QProgressBar; + +class VersionSelectWidget: public QWidget +{ + Q_OBJECT +public: + explicit VersionSelectWidget(BaseVersionList *vlist, QWidget *parent = 0); + ~VersionSelectWidget(); + + //! loads the list if needed. + void initialize(); + + //! Starts a task that loads the list. + void loadList(); + + bool hasVersions() const; + BaseVersionPtr selectedVersion() const; + void selectRecommended(); + + void setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter); + void setExactFilter(BaseVersionList::ModelRoles role, QString filter); + void setEmptyString(QString emptyString); + void setEmptyErrorString(QString emptyErrorString); + void setResizeOn(int column); + void setUseLatest(const bool useLatest); + +signals: + void selectedVersionChanged(BaseVersionPtr version); + +protected: + virtual void closeEvent ( QCloseEvent* ); + +private slots: + void onTaskFinished(); + void changeProgress(qint64 current, qint64 total); + void currentRowChanged(const QModelIndex ¤t, const QModelIndex &); + +private: + void preselect(); + +private: + BaseVersionList *m_vlist = nullptr; + VersionProxyModel *m_proxyModel = nullptr; + int resizeOnColumn = 0; + Task * loadTask = nullptr; + bool preselectedAlready = false; + +private: + QVBoxLayout *verticalLayout = nullptr; + VersionListView *listView = nullptr; + QProgressBar *sneakyProgressBar = nullptr; +};