GH-366: Plain and simple modpack export/import/download
Also removed the in-source QuaZIP in favour of upstream version
This commit is contained in:
@ -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,44 +778,103 @@ 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:
|
||||
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;
|
||||
}
|
||||
|
||||
archivePath = entry->getFullPath();
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
newInstance->setName(newInstDlg.instName());
|
||||
newInstance->setIconKey(newInstDlg.iconKey());
|
||||
newInstance->setGroupInitial(newInstDlg.instGroup());
|
||||
MMC->instances()->add(InstancePtr(newInstance));
|
||||
stringToIntList(MMC->settings()->get("ShownNotifications").toString());
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
@ -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)
|
||||
|
@ -104,6 +104,8 @@ slots:
|
||||
|
||||
void on_actionDeleteInstance_triggered();
|
||||
|
||||
void on_actionExportInstance_triggered();
|
||||
|
||||
void on_actionRenameInstance_triggered();
|
||||
|
||||
void on_actionEditInstance_triggered();
|
||||
|
@ -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>
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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>
|
||||
|
Reference in New Issue
Block a user