diff options
author | Jan Dalheimer <jan@dalheimer.de> | 2015-02-12 22:01:20 +0100 |
---|---|---|
committer | Jan Dalheimer <jan@dalheimer.de> | 2015-02-19 21:04:27 +0100 |
commit | a53f8d506e212f862f54e5a758fb50666ec7c3ba (patch) | |
tree | 6b2cab86af0e34eabaec17a82893284f20323cd7 /gui | |
parent | f9a17eb9deb559ee01fd7e6c67d80c4f93badf28 (diff) | |
download | MultiMC-a53f8d506e212f862f54e5a758fb50666ec7c3ba.tar MultiMC-a53f8d506e212f862f54e5a758fb50666ec7c3ba.tar.gz MultiMC-a53f8d506e212f862f54e5a758fb50666ec7c3ba.tar.lz MultiMC-a53f8d506e212f862f54e5a758fb50666ec7c3ba.tar.xz MultiMC-a53f8d506e212f862f54e5a758fb50666ec7c3ba.zip |
GH-366: Plain and simple modpack export/import/download
Also removed the in-source QuaZIP in favour of upstream version
Diffstat (limited to 'gui')
-rw-r--r-- | gui/MainWindow.cpp | 169 | ||||
-rw-r--r-- | gui/MainWindow.h | 2 | ||||
-rw-r--r-- | gui/MainWindow.ui | 6 | ||||
-rw-r--r-- | gui/dialogs/NewInstanceDialog.cpp | 75 | ||||
-rw-r--r-- | gui/dialogs/NewInstanceDialog.h | 2 | ||||
-rw-r--r-- | gui/dialogs/NewInstanceDialog.ui | 141 |
6 files changed, 336 insertions, 59 deletions
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 <QWidgetAction> #include <QProgressDialog> #include <QShortcut> +#include <QFileDialog> + +#include <JlCompress.h> #include "osutils.h" #include "userutils.h" @@ -106,6 +109,7 @@ #include <logic/updater/UpdateChecker.h> #include <logic/updater/NotificationChecker.h> #include <logic/tasks/ThreadTask.h> +#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 @@ <addaction name="actionViewSelectedInstFolder"/> <addaction name="actionConfig_Folder"/> <addaction name="separator"/> + <addaction name="actionExportInstance"/> <addaction name="actionDeleteInstance"/> </widget> <widget class="QToolBar" name="newsToolBar"> @@ -495,6 +496,11 @@ <string>Change the settings specific to the instance</string> </property> </action> + <action name="actionExportInstance"> + <property name="text"> + <string>Export Instance</string> + </property> + </action> </widget> <layoutdefault spacing="6" margin="11"/> <resources> 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 <logic/InstanceList.h> +#include "logic/InstanceList.h" #include "gui/Platform.h" #include "VersionSelectDialog.h" @@ -31,6 +31,31 @@ #include <QLayout> #include <QPushButton> +#include <QFileDialog> +#include <QValidator> + +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 @@ <x>0</x> <y>0</y> <width>345</width> - <height>261</height> + <height>343</height> </rect> </property> <property name="windowTitle"> @@ -84,41 +84,24 @@ </item> <item> <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="2"> + <item row="1" column="2"> <widget class="QToolButton" name="btnChangeVersion"> <property name="text"> <string>...</string> </property> </widget> </item> - <item row="0" column="1"> - <widget class="QLineEdit" name="versionTextBox"> - <property name="readOnly"> - <bool>true</bool> - </property> - </widget> - </item> <item row="0" column="0"> - <widget class="QLabel" name="labelVersion"> - <property name="text"> - <string>Version:</string> - </property> - <property name="buddy"> - <cstring>versionTextBox</cstring> - </property> - </widget> - </item> - <item row="1" column="0"> <widget class="QLabel" name="labelVersion_3"> <property name="text"> - <string>Group:</string> + <string>&Group:</string> </property> <property name="buddy"> <cstring>groupBox</cstring> </property> </widget> </item> - <item row="1" column="1" colspan="2"> + <item row="0" column="1" colspan="2"> <widget class="QComboBox" name="groupBox"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> @@ -131,6 +114,50 @@ </property> </widget> </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="modpackEdit"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>http://</string> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QToolButton" name="modpackBtn"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QRadioButton" name="versionBox"> + <property name="text"> + <string>&Version:</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QRadioButton" name="modpackBox"> + <property name="text"> + <string>Import &Modpack:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="versionTextBox"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> </layout> </item> <item> @@ -176,8 +203,8 @@ <slot>accept()</slot> <hints> <hint type="sourcelabel"> - <x>248</x> - <y>254</y> + <x>257</x> + <y>333</y> </hint> <hint type="destinationlabel"> <x>157</x> @@ -192,8 +219,8 @@ <slot>reject()</slot> <hints> <hint type="sourcelabel"> - <x>316</x> - <y>260</y> + <x>325</x> + <y>333</y> </hint> <hint type="destinationlabel"> <x>286</x> @@ -201,5 +228,69 @@ </hint> </hints> </connection> + <connection> + <sender>modpackBox</sender> + <signal>toggled(bool)</signal> + <receiver>modpackEdit</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>81</x> + <y>229</y> + </hint> + <hint type="destinationlabel"> + <x>236</x> + <y>221</y> + </hint> + </hints> + </connection> + <connection> + <sender>modpackBox</sender> + <signal>toggled(bool)</signal> + <receiver>modpackBtn</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>129</x> + <y>225</y> + </hint> + <hint type="destinationlabel"> + <x>328</x> + <y>229</y> + </hint> + </hints> + </connection> + <connection> + <sender>versionBox</sender> + <signal>toggled(bool)</signal> + <receiver>versionTextBox</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>93</x> + <y>195</y> + </hint> + <hint type="destinationlabel"> + <x>213</x> + <y>191</y> + </hint> + </hints> + </connection> + <connection> + <sender>versionBox</sender> + <signal>toggled(bool)</signal> + <receiver>btnChangeVersion</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>104</x> + <y>198</y> + </hint> + <hint type="destinationlabel"> + <x>322</x> + <y>192</y> + </hint> + </hints> + </connection> </connections> </ui> |