summaryrefslogtreecommitdiffstats
path: root/gui
diff options
context:
space:
mode:
Diffstat (limited to 'gui')
-rw-r--r--gui/MainWindow.cpp169
-rw-r--r--gui/MainWindow.h2
-rw-r--r--gui/MainWindow.ui6
-rw-r--r--gui/dialogs/NewInstanceDialog.cpp75
-rw-r--r--gui/dialogs/NewInstanceDialog.h2
-rw-r--r--gui/dialogs/NewInstanceDialog.ui141
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>&amp;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>&amp;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 &amp;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>