From a53f8d506e212f862f54e5a758fb50666ec7c3ba Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Thu, 12 Feb 2015 22:01:20 +0100 Subject: GH-366: Plain and simple modpack export/import/download Also removed the in-source QuaZIP in favour of upstream version --- gui/MainWindow.cpp | 169 +++++++++++++++++++++++++++++++------- gui/MainWindow.h | 2 + gui/MainWindow.ui | 6 ++ gui/dialogs/NewInstanceDialog.cpp | 75 ++++++++++++++++- gui/dialogs/NewInstanceDialog.h | 2 + gui/dialogs/NewInstanceDialog.ui | 141 +++++++++++++++++++++++++------ 6 files changed, 336 insertions(+), 59 deletions(-) (limited to 'gui') diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index d605c891..fbb575a0 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -36,6 +36,9 @@ #include #include #include +#include + +#include #include "osutils.h" #include "userutils.h" @@ -106,6 +109,7 @@ #include #include #include +#include "logic/net/CacheDownload.h" #include "logic/tools/BaseProfiler.h" @@ -733,6 +737,25 @@ void MainWindow::setCatBackground(bool enabled) } } +static QFileInfo findRecursive(const QString &dir, const QString &name) +{ + for (const auto info : QDir(dir).entryInfoList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files, QDir::DirsLast)) + { + if (info.isFile() && info.fileName() == name) + { + return info; + } + else if (info.isDir()) + { + const QFileInfo res = findRecursive(info.absoluteFilePath(), name); + if (res.isFile() && res.exists()) + { + return res; + } + } + } + return QFileInfo(); +} void MainWindow::on_actionAddInstance_triggered() { if (!MMC->minecraftlist()->isLoaded() && m_versionLoadTask && @@ -755,45 +778,104 @@ void MainWindow::on_actionAddInstance_triggered() QString instancesDir = MMC->settings()->get("InstanceDir").toString(); QString instDirName = DirNameFromString(newInstDlg.instName(), instancesDir); QString instDir = PathCombine(instancesDir, instDirName); - auto &loader = InstanceFactory::get(); - auto error = loader.createInstance(newInstance, newInstDlg.selectedVersion(), instDir); - QString errorMsg = tr("Failed to create instance %1: ").arg(instDirName); - switch (error) + const QUrl modpackUrl = newInstDlg.modpackUrl(); + if (modpackUrl.isValid()) { - case InstanceFactory::NoCreateError: - { - newInstance->setName(newInstDlg.instName()); - newInstance->setIconKey(newInstDlg.iconKey()); - newInstance->setGroupInitial(newInstDlg.instGroup()); - MMC->instances()->add(InstancePtr(newInstance)); - stringToIntList(MMC->settings()->get("ShownNotifications").toString()); - break; - } + QString archivePath; + if (modpackUrl.isLocalFile()) + { + archivePath = modpackUrl.toLocalFile(); + } + else + { + const QString path = modpackUrl.host() + '/' + QString::fromUtf8(modpackUrl.toEncoded()); + auto entry = MMC->metacache()->resolveEntry("general", path); + CacheDownloadPtr dl = CacheDownload::make(modpackUrl, entry); + NetJob job(tr("Modpack download")); + job.addNetAction(dl); + + ProgressDialog dlDialog(this); + if (dlDialog.exec(&job) != QDialog::Accepted) + { + return; + } - case InstanceFactory::InstExists: - { - errorMsg += tr("An instance with the given directory name already exists."); - CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); - return; - } + archivePath = entry->getFullPath(); + } - case InstanceFactory::CantCreateDir: - { - errorMsg += tr("Failed to create the instance directory."); - CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); - return; - } - default: - { - errorMsg += tr("Unknown instance loader error %1").arg(error); - CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); - return; + QTemporaryDir extractTmpDir; + QDir extractDir(extractTmpDir.path()); + QLOG_INFO() << "Attempting to create instance from" << archivePath; + if (JlCompress::extractDir(archivePath, extractDir.absolutePath()).isEmpty()) + { + CustomMessageBox::selectable(this, tr("Error"), + tr("Failed to extract modpack"), QMessageBox::Warning)->show(); + return; + } + const QFileInfo instanceCfgFile = findRecursive(extractDir.absolutePath(), "instance.cfg"); + if (!instanceCfgFile.isFile() || !instanceCfgFile.exists()) + { + CustomMessageBox::selectable(this, tr("Error"), tr("Archive does not contain instance.cfg"))->show(); + return; + } + if (!copyPath(instanceCfgFile.absoluteDir().absolutePath(), instDir)) + { + CustomMessageBox::selectable(this, tr("Error"), tr("Unable to copy instance"))->show(); + return; + } + + auto error = loader.loadInstance(newInstance, instDir); + QString errorMsg = tr("Failed to load instance %1: ").arg(instDirName); + switch (error) + { + case InstanceFactory::UnknownLoadError: + errorMsg += tr("Unkown error"); + CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); + return; + case InstanceFactory::NotAnInstance: + errorMsg += tr("Not an instance"); + CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); + return; + } } + else + { + auto error = loader.createInstance(newInstance, newInstDlg.selectedVersion(), instDir); + QString errorMsg = tr("Failed to create instance %1: ").arg(instDirName); + switch (error) + { + case InstanceFactory::NoCreateError: break; + case InstanceFactory::InstExists: + { + errorMsg += tr("An instance with the given directory name already exists."); + CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); + return; + } + + case InstanceFactory::CantCreateDir: + { + errorMsg += tr("Failed to create the instance directory."); + CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); + return; + } + + default: + { + errorMsg += tr("Unknown instance loader error %1").arg(error); + CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); + return; + } + } } + newInstance->setName(newInstDlg.instName()); + newInstance->setIconKey(newInstDlg.iconKey()); + newInstance->setGroupInitial(newInstDlg.instGroup()); + MMC->instances()->add(InstancePtr(newInstance)); + if (MMC->accounts()->anyAccountIsValid()) { ProgressDialog loadDialog(this); @@ -1056,6 +1138,33 @@ void MainWindow::on_actionDeleteInstance_triggered() } } +void MainWindow::on_actionExportInstance_triggered() +{ + if (m_selectedInstance) + { + const QString output = QFileDialog::getSaveFileName(this, tr("Export %1") + .arg(m_selectedInstance->name()), + QDir::homePath(), "Zip (*.zip)"); + if (output.isNull()) + { + return; + } + if (QFile::exists(output)) + { + int ret = QMessageBox::question(this, tr("Overwrite?"), tr("This file already exists. Do you want to overwrite it?"), + QMessageBox::No, QMessageBox::Yes); + if (ret == QMessageBox::No) + { + return; + } + } + if (!JlCompress::compressDir(output, m_selectedInstance->instanceRoot())) + { + QMessageBox::warning(this, tr("Error"), tr("Unable to export instance")); + } + } +} + void MainWindow::on_actionRenameInstance_triggered() { if (m_selectedInstance) diff --git a/gui/MainWindow.h b/gui/MainWindow.h index 67b0318c..4d417f15 100644 --- a/gui/MainWindow.h +++ b/gui/MainWindow.h @@ -104,6 +104,8 @@ slots: void on_actionDeleteInstance_triggered(); + void on_actionExportInstance_triggered(); + void on_actionRenameInstance_triggered(); void on_actionEditInstance_triggered(); diff --git a/gui/MainWindow.ui b/gui/MainWindow.ui index 69e2c465..800862d1 100644 --- a/gui/MainWindow.ui +++ b/gui/MainWindow.ui @@ -118,6 +118,7 @@ + @@ -495,6 +496,11 @@ Change the settings specific to the instance + + + Export Instance + + diff --git a/gui/dialogs/NewInstanceDialog.cpp b/gui/dialogs/NewInstanceDialog.cpp index e086cf6e..51060ad2 100644 --- a/gui/dialogs/NewInstanceDialog.cpp +++ b/gui/dialogs/NewInstanceDialog.cpp @@ -22,7 +22,7 @@ #include "logic/icons/IconList.h" #include "logic/minecraft/MinecraftVersionList.h" #include "logic/tasks/Task.h" -#include +#include "logic/InstanceList.h" #include "gui/Platform.h" #include "VersionSelectDialog.h" @@ -31,6 +31,31 @@ #include #include +#include +#include + +class UrlValidator : public QValidator +{ +public: + using QValidator::QValidator; + + State validate(QString &in, int &pos) const + { + const QUrl url(in); + if (url.isValid() && !url.isRelative() && !url.isEmpty()) + { + return Acceptable; + } + else if (QFile::exists(in)) + { + return Acceptable; + } + else + { + return Intermediate; + } + } +}; NewInstanceDialog::NewInstanceDialog(QWidget *parent) : QDialog(parent), ui(new Ui::NewInstanceDialog) @@ -39,9 +64,16 @@ NewInstanceDialog::NewInstanceDialog(QWidget *parent) ui->setupUi(this); resize(minimumSizeHint()); layout()->setSizeConstraint(QLayout::SetFixedSize); + setSelectedVersion(MMC->minecraftlist()->getLatestStable(), true); InstIconKey = "infinity"; ui->iconButton->setIcon(MMC->icons()->getIcon(InstIconKey)); + + ui->modpackEdit->setValidator(new UrlValidator(ui->modpackEdit)); + connect(ui->modpackEdit, &QLineEdit::textChanged, this, &NewInstanceDialog::updateDialogState); + connect(ui->modpackBox, &QRadioButton::clicked, this, &NewInstanceDialog::updateDialogState); + connect(ui->versionBox, &QRadioButton::clicked, this, &NewInstanceDialog::updateDialogState); + auto groups = MMC->instances()->getGroups().toSet(); auto groupList = QStringList(groups.toList()); groupList.sort(Qt::CaseInsensitive); @@ -67,7 +99,10 @@ NewInstanceDialog::~NewInstanceDialog() void NewInstanceDialog::updateDialogState() { ui->buttonBox->button(QDialogButtonBox::Ok) - ->setEnabled(!instName().isEmpty() && m_selectedVersion); + ->setEnabled(!instName().isEmpty() + && m_selectedVersion + && (!ui->modpackBox->isChecked() + || ui->modpackEdit->hasAcceptableInput())); } void NewInstanceDialog::setSelectedVersion(BaseVersionPtr version, bool initial) @@ -94,16 +129,33 @@ QString NewInstanceDialog::instName() const { return ui->instNameTextBox->text(); } - QString NewInstanceDialog::instGroup() const { return ui->groupBox->currentText(); } - QString NewInstanceDialog::iconKey() const { return InstIconKey; } +QUrl NewInstanceDialog::modpackUrl() const +{ + if (ui->modpackBox->isChecked()) + { + const QUrl url(ui->modpackEdit->text()); + if (url.isValid() && !url.isRelative() && !url.host().isEmpty()) + { + return url; + } + else + { + return QUrl::fromLocalFile(ui->modpackEdit->text()); + } + } + else + { + return QUrl(); + } +} BaseVersionPtr NewInstanceDialog::selectedVersion() const { @@ -140,3 +192,18 @@ void NewInstanceDialog::on_instNameTextBox_textChanged(const QString &arg1) updateDialogState(); } +void NewInstanceDialog::on_modpackBtn_clicked() +{ + const QUrl url = QFileDialog::getOpenFileUrl(this, tr("Choose modpack"), modpackUrl(), tr("Zip (*.zip)")); + if (url.isValid()) + { + if (url.isLocalFile()) + { + ui->modpackEdit->setText(url.toLocalFile()); + } + else + { + ui->modpackEdit->setText(url.toString()); + } + } +} diff --git a/gui/dialogs/NewInstanceDialog.h b/gui/dialogs/NewInstanceDialog.h index 39f83c89..1e1b0bea 100644 --- a/gui/dialogs/NewInstanceDialog.h +++ b/gui/dialogs/NewInstanceDialog.h @@ -41,12 +41,14 @@ public: QString instName() const; QString instGroup() const; QString iconKey() const; + QUrl modpackUrl() const; BaseVersionPtr selectedVersion() const; private slots: void on_btnChangeVersion_clicked(); void on_iconButton_clicked(); + void on_modpackBtn_clicked(); void on_instNameTextBox_textChanged(const QString &arg1); private: diff --git a/gui/dialogs/NewInstanceDialog.ui b/gui/dialogs/NewInstanceDialog.ui index be4642aa..adb7fa54 100644 --- a/gui/dialogs/NewInstanceDialog.ui +++ b/gui/dialogs/NewInstanceDialog.ui @@ -10,7 +10,7 @@ 0 0 345 - 261 + 343 @@ -84,41 +84,24 @@ - + ... - - - - true - - - - - - Version: - - - versionTextBox - - - - - Group: + &Group: groupBox - + @@ -131,6 +114,50 @@ + + + + false + + + http:// + + + + + + + false + + + ... + + + + + + + &Version: + + + true + + + + + + + Import &Modpack: + + + + + + + true + + + @@ -176,8 +203,8 @@ accept() - 248 - 254 + 257 + 333 157 @@ -192,8 +219,8 @@ reject() - 316 - 260 + 325 + 333 286 @@ -201,5 +228,69 @@ + + modpackBox + toggled(bool) + modpackEdit + setEnabled(bool) + + + 81 + 229 + + + 236 + 221 + + + + + modpackBox + toggled(bool) + modpackBtn + setEnabled(bool) + + + 129 + 225 + + + 328 + 229 + + + + + versionBox + toggled(bool) + versionTextBox + setEnabled(bool) + + + 93 + 195 + + + 213 + 191 + + + + + versionBox + toggled(bool) + btnChangeVersion + setEnabled(bool) + + + 104 + 198 + + + 322 + 192 + + + -- cgit