From e5b4dee1c05d0d6ebc0d7b2dd802b93cb8765e63 Mon Sep 17 00:00:00 2001
From: Jan Dalheimer <jan@dalheimer.de>
Date: Fri, 14 Mar 2014 19:51:56 +0100
Subject: [PATCH 1/3] Move version stuff to the model and reimplement
 reordering

---
 gui/dialogs/OneSixModEditDialog.cpp |  60 +++++++----
 gui/dialogs/OneSixModEditDialog.ui  |   9 --
 logic/OneSixVersionBuilder.cpp      |  13 +--
 logic/VersionFinal.cpp              | 153 ++++++++++++++++++++++++++--
 logic/VersionFinal.h                |  16 ++-
 5 files changed, 203 insertions(+), 48 deletions(-)

diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp
index 78585a05..b5a0d5aa 100644
--- a/gui/dialogs/OneSixModEditDialog.cpp
+++ b/gui/dialogs/OneSixModEditDialog.cpp
@@ -42,16 +42,6 @@
 #include "logic/LiteLoaderInstaller.h"
 #include "logic/OneSixVersionBuilder.h"
 
-template <typename A, typename B> QMap<A, B> invert(const QMap<B, A> &in)
-{
-	QMap<A, B> out;
-	for (auto it = in.begin(); it != in.end(); ++it)
-	{
-		out.insert(it.value(), it.key());
-	}
-	return out;
-}
-
 OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
 	: QDialog(parent), ui(new Ui::OneSixModEditDialog), m_inst(inst)
 {
@@ -164,25 +154,57 @@ void OneSixModEditDialog::on_removeLibraryBtn_clicked()
 
 void OneSixModEditDialog::on_resetLibraryOrderBtn_clicked()
 {
-	// FIXME: IMPLEMENT LOGIC IN MODEL. SEE LEGACY DIALOG FOR EXAMPLE(S).
+	try
+	{
+		m_version->resetOrder();
+	}
+	catch (MMCError &e)
+	{
+		QMessageBox::critical(this, tr("Error"), e.cause());
+	}
 }
 
 void OneSixModEditDialog::on_moveLibraryUpBtn_clicked()
 {
-	// FIXME: IMPLEMENT LOGIC IN MODEL. SEE LEGACY DIALOG FOR EXAMPLE(S).
+	if (ui->libraryTreeView->selectionModel()->selectedRows().isEmpty())
+	{
+		return;
+	}
+	try
+	{
+		const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row();
+		const int newRow = 0;m_version->move(row, VersionFinal::MoveUp);
+		//ui->libraryTreeView->selectionModel()->setCurrentIndex(m_version->index(newRow), QItemSelectionModel::ClearAndSelect);
+	}
+	catch (MMCError &e)
+	{
+		QMessageBox::critical(this, tr("Error"), e.cause());
+	}
 }
 
 void OneSixModEditDialog::on_moveLibraryDownBtn_clicked()
 {
-	// FIXME: IMPLEMENT LOGIC IN MODEL. SEE LEGACY DIALOG FOR EXAMPLE(S).
+	if (ui->libraryTreeView->selectionModel()->selectedRows().isEmpty())
+	{
+		return;
+	}
+	try
+	{
+		const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row();
+		const int newRow = 0;m_version->move(row, VersionFinal::MoveDown);
+		//ui->libraryTreeView->selectionModel()->setCurrentIndex(m_version->index(newRow), QItemSelectionModel::ClearAndSelect);
+	}
+	catch (MMCError &e)
+	{
+		QMessageBox::critical(this, tr("Error"), e.cause());
+	}
 }
 
 void OneSixModEditDialog::on_forgeBtn_clicked()
 {
 	// FIXME: use actual model, not reloading. Move logic to model.
 
-	// FIXME: model::isCustom();
-	if (QDir(m_inst->instanceRoot()).exists("custom.json"))
+	if (m_version->isCustom())
 	{
 		if (QMessageBox::question(this, tr("Revert?"),
 								  tr("This action will remove your custom.json. Continue?")) !=
@@ -190,8 +212,7 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
 		{
 			return;
 		}
-		// FIXME: model::revertToBase();
-		QDir(m_inst->instanceRoot()).remove("custom.json");
+		m_version->revertToBase();
 		reloadInstanceVersion();
 	}
 	VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
@@ -242,8 +263,7 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
 
 void OneSixModEditDialog::on_liteloaderBtn_clicked()
 {
-	// FIXME: model...
-	if (QDir(m_inst->instanceRoot()).exists("custom.json"))
+	if (m_version->isCustom())
 	{
 		if (QMessageBox::question(this, tr("Revert?"),
 								  tr("This action will remove your custom.json. Continue?")) !=
@@ -251,7 +271,7 @@ void OneSixModEditDialog::on_liteloaderBtn_clicked()
 		{
 			return;
 		}
-		QDir(m_inst->instanceRoot()).remove("custom.json");
+		m_version->revertToBase();
 		reloadInstanceVersion();
 	}
 	VersionSelectDialog vselect(MMC->liteloaderlist().get(), tr("Select LiteLoader version"),
diff --git a/gui/dialogs/OneSixModEditDialog.ui b/gui/dialogs/OneSixModEditDialog.ui
index b606dcd2..2c9f70bb 100644
--- a/gui/dialogs/OneSixModEditDialog.ui
+++ b/gui/dialogs/OneSixModEditDialog.ui
@@ -99,9 +99,6 @@
          </item>
          <item>
           <widget class="QPushButton" name="moveLibraryUpBtn">
-           <property name="enabled">
-            <bool>false</bool>
-           </property>
            <property name="toolTip">
             <string>This isn't implemented yet.</string>
            </property>
@@ -112,9 +109,6 @@
          </item>
          <item>
           <widget class="QPushButton" name="moveLibraryDownBtn">
-           <property name="enabled">
-            <bool>false</bool>
-           </property>
            <property name="toolTip">
             <string>This isn't implemented yet.</string>
            </property>
@@ -125,9 +119,6 @@
          </item>
          <item>
           <widget class="QPushButton" name="resetLibraryOrderBtn">
-           <property name="enabled">
-            <bool>false</bool>
-           </property>
            <property name="toolTip">
             <string>This isn't implemented yet.</string>
            </property>
diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp
index 8eacbce4..35d01a46 100644
--- a/logic/OneSixVersionBuilder.cpp
+++ b/logic/OneSixVersionBuilder.cpp
@@ -58,13 +58,15 @@ void OneSixVersionBuilder::readJsonAndApplyToVersion(VersionFinal *version,
 
 void OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringList &external)
 {
-	m_version->clear();
+	m_version->versionFiles.clear();
 
 	QDir root(m_instance->instanceRoot());
 	QDir patches(root.absoluteFilePath("patches/"));
 
 	// if we do external files, do just those.
 	if (!external.isEmpty())
+	{
+		int externalOrder = -1;
 		for (auto fileName : external)
 		{
 			QLOG_INFO() << "Reading" << fileName;
@@ -72,11 +74,12 @@ void OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringLi
 				parseJsonFile(QFileInfo(fileName), false, fileName.endsWith("pack.json"));
 			file->name = QFileInfo(fileName).fileName();
 			file->fileId = "org.multimc.external." + file->name;
+			file->order = (externalOrder += 1);
 			file->version = QString();
 			file->mcVersion = QString();
-			file->applyTo(m_version);
 			m_version->versionFiles.append(file);
 		}
+	}
 	// else, if there's custom json, we just do that.
 	else if (QFile::exists(root.absoluteFilePath("custom.json")))
 	{
@@ -85,8 +88,8 @@ void OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringLi
 		file->name = "custom.json";
 		file->filename = "custom.json";
 		file->fileId = "org.multimc.custom.json";
+		file->order = -1;
 		file->version = QString();
-		file->applyTo(m_version);
 		m_version->versionFiles.append(file);
 		// QObject::tr("The version descriptors of this instance are not compatible with the
 		// current version of MultiMC"));
@@ -101,9 +104,9 @@ void OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringLi
 			auto file = parseJsonFile(QFileInfo(root.absoluteFilePath("version.json")), false);
 			file->name = "Minecraft";
 			file->fileId = "org.multimc.version.json";
+			file->order = -1;
 			file->version = m_instance->intendedVersionId();
 			file->mcVersion = m_instance->intendedVersionId();
-			file->applyTo(m_version);
 			m_version->versionFiles.append(file);
 			// QObject::tr("Error while applying %1. Please check MultiMC-0.log for more
 			// info.").arg(root.absoluteFilePath("version.json")));
@@ -128,9 +131,7 @@ void OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringLi
 			}
 			for (auto order : files.keys())
 			{
-				QLOG_DEBUG() << "Applying file with order" << order;
 				auto &filePair = files[order];
-				filePair.second->applyTo(m_version);
 				m_version->versionFiles.append(filePair.second);
 			}
 		} while (0);
diff --git a/logic/VersionFinal.cpp b/logic/VersionFinal.cpp
index a057ecdd..2901fcf9 100644
--- a/logic/VersionFinal.cpp
+++ b/logic/VersionFinal.cpp
@@ -17,8 +17,20 @@
 
 #include <QDebug>
 #include <QFile>
+#include <QDir>
 
 #include "OneSixVersionBuilder.h"
+#include "OneSixInstance.h"
+
+template <typename A, typename B> QMap<A, B> invert(const QMap<B, A> &in)
+{
+	QMap<A, B> out;
+	for (auto it = in.begin(); it != in.end(); ++it)
+	{
+		out.insert(it.value(), it.key());
+	}
+	return out;
+}
 
 VersionFinal::VersionFinal(OneSixInstance *instance, QObject *parent)
 	: QAbstractListModel(parent), m_instance(instance)
@@ -31,12 +43,12 @@ bool VersionFinal::reload(const bool onlyVanilla, const QStringList &external)
 	//FIXME: source of epic failure.
 	beginResetModel();
 	OneSixVersionBuilder::build(this, m_instance, onlyVanilla, external);
+	reapply(true);
 	endResetModel();
 }
 
 void VersionFinal::clear()
 {
-	beginResetModel();
 	id.clear();
 	time.clear();
 	releaseTime.clear();
@@ -48,8 +60,6 @@ void VersionFinal::clear()
 	mainClass.clear();
 	libraries.clear();
 	tweakers.clear();
-	versionFiles.clear();
-	endResetModel();
 }
 
 bool VersionFinal::canRemove(const int index) const
@@ -60,6 +70,14 @@ bool VersionFinal::canRemove(const int index) const
 	}
 	return false;
 }
+bool VersionFinal::remove(const int index)
+{
+	if (canRemove(index))
+	{
+		return QFile::remove(versionFiles.at(index)->filename);
+	}
+	return false;
+}
 
 QString VersionFinal::versionFileId(const int index) const
 {
@@ -69,14 +87,16 @@ QString VersionFinal::versionFileId(const int index) const
 	}
 	return versionFiles.at(index)->fileId;
 }
-
-bool VersionFinal::remove(const int index)
+VersionFilePtr VersionFinal::versionFile(const QString &id)
 {
-	if (canRemove(index))
+	for (auto file : versionFiles)
 	{
-		return QFile::remove(versionFiles.at(index)->filename);
+		if (file->fileId == id)
+		{
+			return file;
+		}
 	}
-	return false;
+	return 0;
 }
 
 QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNormalLibs()
@@ -91,7 +111,6 @@ QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNormalLibs()
 	}
 	return output;
 }
-
 QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNativeLibs()
 {
 	QList<std::shared_ptr<OneSixLibrary> > output;
@@ -144,7 +163,6 @@ QVariant VersionFinal::data(const QModelIndex &index, int role) const
 	}
 	return QVariant();
 }
-
 QVariant VersionFinal::headerData(int section, Qt::Orientation orientation, int role) const
 {
 	if (orientation == Qt::Horizontal)
@@ -164,7 +182,6 @@ QVariant VersionFinal::headerData(int section, Qt::Orientation orientation, int
 	}
 	return QVariant();
 }
-
 Qt::ItemFlags VersionFinal::flags(const QModelIndex &index) const
 {
 	if (!index.isValid())
@@ -181,3 +198,117 @@ int VersionFinal::columnCount(const QModelIndex &parent) const
 {
 	return 2;
 }
+
+bool VersionFinal::isCustom()
+{
+	return QDir(m_instance->instanceRoot()).exists("custom.json");
+}
+bool VersionFinal::revertToBase()
+{
+	return QDir(m_instance->instanceRoot()).remove("custom.json");
+}
+
+QMap<QString, int> VersionFinal::getExistingOrder() const
+{
+
+	QMap<QString, int> order;
+	// default
+	{
+		for (auto file : versionFiles)
+		{
+			order.insert(file->fileId, file->order);
+		}
+	}
+	// overriden
+	{
+		QMap<QString, int> overridenOrder = OneSixVersionBuilder::readOverrideOrders(m_instance);
+		for (auto id : order.keys())
+		{
+			if (overridenOrder.contains(id))
+			{
+				order[id] = overridenOrder[id];
+			}
+		}
+	}
+	return order;
+}
+
+void VersionFinal::move(const int index, const MoveDirection direction)
+{
+	int theirIndex;
+	if (direction == MoveUp)
+	{
+		theirIndex = index - 1;
+	}
+	else
+	{
+		theirIndex = index + 1;
+	}
+	if (theirIndex < 0 || theirIndex >= versionFiles.size())
+	{
+		return;
+	}
+	const QString ourId = versionFileId(index);
+	const QString theirId = versionFileId(theirIndex);
+	if (ourId.isNull() || ourId.startsWith("org.multimc.") ||
+			theirId.isNull() || theirId.startsWith("org.multimc."))
+	{
+		return;
+	}
+
+	VersionFilePtr we = versionFiles[index];
+	VersionFilePtr them = versionFiles[theirIndex];
+	if (!we || !them)
+	{
+		return;
+	}
+	beginMoveRows(QModelIndex(), index, index, QModelIndex(), theirIndex);
+	versionFiles.replace(theirIndex, we);
+	versionFiles.replace(index, them);
+	endMoveRows();
+
+	auto order = getExistingOrder();
+	order[ourId] = theirIndex;
+	order[theirId] = index;
+
+	if (!OneSixVersionBuilder::writeOverrideOrders(order, m_instance))
+	{
+		throw MMCError(tr("Couldn't save the new order"));
+	}
+	else
+	{
+		reapply();
+	}
+}
+void VersionFinal::resetOrder()
+{
+	QDir(m_instance->instanceRoot()).remove("order.json");
+	reapply();
+}
+
+void VersionFinal::reapply(const bool alreadyReseting)
+{
+	if (!alreadyReseting)
+	{
+		beginResetModel();
+	}
+
+	clear();
+
+	auto existingOrders = getExistingOrder();
+	QList<int> orders = existingOrders.values();
+	std::sort(orders.begin(), orders.end());
+	QList<VersionFilePtr> newVersionFiles;
+	for (auto order : orders)
+	{
+		auto file = versionFile(existingOrders.key(order));
+		newVersionFiles.append(file);
+		file->applyTo(this);
+	}
+	versionFiles.swap(newVersionFiles);
+
+	if (!alreadyReseting)
+	{
+		endResetModel();
+	}
+}
diff --git a/logic/VersionFinal.h b/logic/VersionFinal.h
index 99fd5ff0..fcffb3c3 100644
--- a/logic/VersionFinal.h
+++ b/logic/VersionFinal.h
@@ -41,12 +41,22 @@ public:
 	bool reload(const bool onlyVanilla = false, const QStringList &external = QStringList());
 	void clear();
 
-	void dump() const;
-
 	bool canRemove(const int index) const;
 
 	QString versionFileId(const int index) const;
 
+	// does this instance have an all overriding custom.json
+	bool isCustom();
+	// remove custom.json
+	bool revertToBase();
+
+	enum MoveDirection { MoveUp, MoveDown };
+	void move(const int index, const MoveDirection direction);
+	void resetOrder();
+
+	// clears and reapplies all version files
+	void reapply(const bool alreadyReseting = false);
+
 public
 slots:
 	bool remove(const int index);
@@ -120,7 +130,9 @@ public:
 	// QList<Rule> rules;
 
 	QList<VersionFilePtr> versionFiles;
+	VersionFilePtr versionFile(const QString &id);
 
 private:
 	OneSixInstance *m_instance;
+	QMap<QString, int> getExistingOrder() const;
 };

From e95619fa67239b709522a66b8bb7403bb7b84098 Mon Sep 17 00:00:00 2001
From: Jan Dalheimer <jan@dalheimer.de>
Date: Fri, 14 Mar 2014 20:48:57 +0100
Subject: [PATCH 2/3] Pull in BaseInstaller related changes from quickmod

---
 gui/dialogs/OneSixModEditDialog.cpp |  56 ++-------------
 logic/BaseInstaller.h               |   6 ++
 logic/ForgeInstaller.cpp            | 105 +++++++++++++++++++++++++++-
 logic/ForgeInstaller.h              |   5 +-
 logic/LiteLoaderInstaller.cpp       |  68 +++++++++++++++++-
 logic/LiteLoaderInstaller.h         |   5 +-
 6 files changed, 186 insertions(+), 59 deletions(-)

diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp
index b5a0d5aa..7dac68e5 100644
--- a/gui/dialogs/OneSixModEditDialog.cpp
+++ b/gui/dialogs/OneSixModEditDialog.cpp
@@ -221,44 +221,9 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
 						   m_inst->currentVersionId());
 	if (vselect.exec() && vselect.selectedVersion())
 	{
-		ForgeVersionPtr forgeVersion =
-			std::dynamic_pointer_cast<ForgeVersion>(vselect.selectedVersion());
-		if (!forgeVersion)
-			return;
-		auto entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename);
-		if (entry->stale)
-		{
-			NetJob *fjob = new NetJob("Forge download");
-			fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry));
-			ProgressDialog dlg(this);
-			dlg.exec(fjob);
-			if (dlg.result() == QDialog::Accepted)
-			{
-				// install
-				QString forgePath = entry->getFullPath();
-				ForgeInstaller forge(forgePath, forgeVersion->universal_url);
-				if (!forge.add(m_inst))
-				{
-					QLOG_ERROR() << "Failure installing forge";
-				}
-			}
-			else
-			{
-				// failed to download forge :/
-			}
-		}
-		else
-		{
-			// install
-			QString forgePath = entry->getFullPath();
-			ForgeInstaller forge(forgePath, forgeVersion->universal_url);
-			if (!forge.add(m_inst))
-			{
-				QLOG_ERROR() << "Failure installing forge";
-			}
-		}
+		ProgressDialog dialog(this);
+		dialog.exec(ForgeInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this));
 	}
-	reloadInstanceVersion();
 }
 
 void OneSixModEditDialog::on_liteloaderBtn_clicked()
@@ -281,21 +246,8 @@ void OneSixModEditDialog::on_liteloaderBtn_clicked()
 						   m_inst->currentVersionId());
 	if (vselect.exec() && vselect.selectedVersion())
 	{
-		LiteLoaderVersionPtr liteloaderVersion =
-			std::dynamic_pointer_cast<LiteLoaderVersion>(vselect.selectedVersion());
-		if (!liteloaderVersion)
-			return;
-		LiteLoaderInstaller liteloader(liteloaderVersion);
-		if (!liteloader.add(m_inst))
-		{
-			QMessageBox::critical(this, tr("LiteLoader"),
-								  tr("For reasons unknown, the LiteLoader installation failed. "
-									 "Check your MultiMC log files for details."));
-		}
-		else
-		{
-			reloadInstanceVersion();
-		}
+		ProgressDialog dialog(this);
+		dialog.exec(LiteLoaderInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this));
 	}
 }
 
diff --git a/logic/BaseInstaller.h b/logic/BaseInstaller.h
index c572e004..d59833cc 100644
--- a/logic/BaseInstaller.h
+++ b/logic/BaseInstaller.h
@@ -20,6 +20,10 @@
 class OneSixInstance;
 class QDir;
 class QString;
+class QObject;
+class ProgressProvider;
+class BaseVersion;
+typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
 
 class BaseInstaller
 {
@@ -31,6 +35,8 @@ public:
 	virtual bool add(OneSixInstance *to);
 	virtual bool remove(OneSixInstance *from);
 
+	virtual ProgressProvider *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) = 0;
+
 protected:
 	virtual QString id() const = 0;
 	QString filename(const QString &root) const;
diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp
index 6f238c21..48bfb8a3 100644
--- a/logic/ForgeInstaller.cpp
+++ b/logic/ForgeInstaller.cpp
@@ -24,17 +24,24 @@
 #include <QRegularExpression>
 #include <QRegularExpressionMatch>
 #include "MultiMC.h"
+#include "tasks/Task.h"
 #include "OneSixInstance.h"
+#include "lists/ForgeVersionList.h"
+#include "gui/dialogs/ProgressDialog.h"
 
 #include <QJsonDocument>
 #include <QJsonArray>
 #include <QSaveFile>
 #include <QCryptographicHash>
 
-ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
+ForgeInstaller::ForgeInstaller()
+	: BaseInstaller()
+{
+}
+void ForgeInstaller::prepare(const QString &filename, const QString &universalUrl)
 {
 	std::shared_ptr<VersionFinal> newVersion;
-	m_universal_url = universal_url;
+	m_universal_url = universalUrl;
 
 	QuaZip zip(filename);
 	if (!zip.open(QuaZip::mdUnzip))
@@ -111,7 +118,6 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
 	m_forge_version = newVersion;
 	realVersionId = m_forge_version->id = installObj.value("minecraft").toString();
 }
-
 bool ForgeInstaller::add(OneSixInstance *to)
 {
 	if (!BaseInstaller::add(to))
@@ -226,3 +232,96 @@ bool ForgeInstaller::add(OneSixInstance *to)
 
 	return true;
 }
+
+class ForgeInstallTask : public Task
+{
+	Q_OBJECT
+public:
+	ForgeInstallTask(ForgeInstaller *installer, OneSixInstance *instance, BaseVersionPtr version, QObject *parent = 0)
+		: Task(parent), m_installer(installer), m_instance(instance), m_version(version)
+	{
+	}
+
+protected:
+	void executeTask() override
+	{
+		{
+			setStatus(tr("Installing forge..."));
+			ForgeVersionPtr forgeVersion =
+				std::dynamic_pointer_cast<ForgeVersion>(m_version);
+			if (!forgeVersion)
+			{
+				emitFailed(tr("Unknown error occured"));
+				return;
+			}
+			auto entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename);
+			if (entry->stale)
+			{
+				NetJob *fjob = new NetJob("Forge download");
+				fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry));
+				connect(fjob, &NetJob::progress, [this](qint64 current, qint64 total){setProgress(100 * current / qMax((qint64)1, total));});
+				connect(fjob, &NetJob::status, [this](const QString &msg){setStatus(msg);});
+				connect(fjob, &NetJob::failed, [this](){emitFailed(tr("Failure to download forge"));});
+				connect(fjob, &NetJob::succeeded, [this, entry, forgeVersion]()
+				{
+					if (!install(entry, forgeVersion))
+					{
+						QLOG_ERROR() << "Failure installing forge";
+						emitFailed(tr("Failure to install forge"));
+					}
+					else
+					{
+						reload();
+					}
+				});
+			}
+			else
+			{
+				if (!install(entry, forgeVersion))
+				{
+					QLOG_ERROR() << "Failure installing forge";
+					emitFailed(tr("Failure to install forge"));
+				}
+				else
+				{
+					reload();
+				}
+			}
+		}
+	}
+
+	bool install(const std::shared_ptr<MetaEntry> &entry, const ForgeVersionPtr &forgeVersion)
+	{
+		QString forgePath = entry->getFullPath();
+		m_installer->prepare(forgePath, forgeVersion->universal_url);
+		return m_installer->add(m_instance);
+	}
+	void reload()
+	{
+		try
+		{
+			m_instance->reloadVersion();
+			emitSucceeded();
+		}
+		catch (MMCError &e)
+		{
+			emitFailed(e.cause());
+		}
+		catch (...)
+		{
+			emitFailed(tr("Failed to load the version description file for reasons unknown."));
+		}
+	}
+
+private:
+	ForgeInstaller *m_installer;
+	OneSixInstance *m_instance;
+	BaseVersionPtr m_version;
+};
+
+ProgressProvider *ForgeInstaller::createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent)
+{
+	return new ForgeInstallTask(this, instance, version, parent);
+}
+
+#include "ForgeInstaller.moc"
diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h
index df029f38..05cc994b 100644
--- a/logic/ForgeInstaller.h
+++ b/logic/ForgeInstaller.h
@@ -25,12 +25,15 @@ class VersionFinal;
 class ForgeInstaller : public BaseInstaller
 {
 public:
-	ForgeInstaller(QString filename, QString universal_url);
+	ForgeInstaller();
 
+	void prepare(const QString &filename, const QString &universalUrl);
 	bool add(OneSixInstance *to) override;
 
 	QString id() const override { return "net.minecraftforge"; }
 
+	ProgressProvider *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) override;
+
 private:
 	// the version, read from the installer
 	std::shared_ptr<VersionFinal> m_forge_version;
diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp
index bb4b07ca..99cc5643 100644
--- a/logic/LiteLoaderInstaller.cpp
+++ b/logic/LiteLoaderInstaller.cpp
@@ -23,12 +23,17 @@
 #include "VersionFinal.h"
 #include "OneSixLibrary.h"
 #include "OneSixInstance.h"
+#include "MultiMC.h"
+#include "lists/LiteLoaderVersionList.h"
 
-LiteLoaderInstaller::LiteLoaderInstaller(LiteLoaderVersionPtr version)
-	: BaseInstaller(), m_version(version)
+LiteLoaderInstaller::LiteLoaderInstaller() : BaseInstaller()
 {
 }
 
+void LiteLoaderInstaller::prepare(LiteLoaderVersionPtr version)
+{
+	m_version = version;
+}
 bool LiteLoaderInstaller::add(OneSixInstance *to)
 {
 	if (!BaseInstaller::add(to))
@@ -84,3 +89,62 @@ bool LiteLoaderInstaller::add(OneSixInstance *to)
 
 	return true;
 }
+
+class LiteLoaderInstallTask : public Task
+{
+	Q_OBJECT
+public:
+	LiteLoaderInstallTask(LiteLoaderInstaller *installer, OneSixInstance *instance,
+						  BaseVersionPtr version, QObject *parent)
+		: Task(parent), m_installer(installer), m_instance(instance), m_version(version)
+	{
+	}
+
+protected:
+	void executeTask() override
+	{
+		LiteLoaderVersionPtr liteloaderVersion =
+			std::dynamic_pointer_cast<LiteLoaderVersion>(m_version);
+		if (!liteloaderVersion)
+		{
+			return;
+		}
+		m_installer->prepare(liteloaderVersion);
+		if (!m_installer->add(m_instance))
+		{
+			emitFailed(tr("For reasons unknown, the LiteLoader installation failed. Check your "
+						  "MultiMC log files for details."));
+		}
+		else
+		{
+			try
+			{
+				m_instance->reloadVersion();
+				emitSucceeded();
+			}
+			catch (MMCError &e)
+			{
+				emitFailed(e.cause());
+			}
+			catch (...)
+			{
+				emitFailed(
+					tr("Failed to load the version description file for reasons unknown."));
+			}
+		}
+	}
+
+private:
+	LiteLoaderInstaller *m_installer;
+	OneSixInstance *m_instance;
+	BaseVersionPtr m_version;
+};
+
+ProgressProvider *LiteLoaderInstaller::createInstallTask(OneSixInstance *instance,
+														 BaseVersionPtr version,
+														 QObject *parent)
+{
+	return new LiteLoaderInstallTask(this, instance, version, parent);
+}
+
+#include "LiteLoaderInstaller.moc"
diff --git a/logic/LiteLoaderInstaller.h b/logic/LiteLoaderInstaller.h
index 2e0de64a..3ab5acb2 100644
--- a/logic/LiteLoaderInstaller.h
+++ b/logic/LiteLoaderInstaller.h
@@ -25,10 +25,13 @@
 class LiteLoaderInstaller : public BaseInstaller
 {
 public:
-	LiteLoaderInstaller(LiteLoaderVersionPtr version);
+	LiteLoaderInstaller();
 
+	void prepare(LiteLoaderVersionPtr version);
 	bool add(OneSixInstance *to) override;
 
+	ProgressProvider *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) override;
+
 private:
 	virtual QString id() const override
 	{

From 42a85def60036a5788a9dd862d0c6f835b62337c Mon Sep 17 00:00:00 2001
From: Jan Dalheimer <jan@dalheimer.de>
Date: Fri, 14 Mar 2014 21:18:17 +0100
Subject: [PATCH 3/3] Get rid of one reloadInstanceVersion

---
 gui/dialogs/OneSixModEditDialog.cpp | 4 ----
 logic/VersionFinal.cpp              | 8 ++++++--
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp
index 7dac68e5..2d459001 100644
--- a/gui/dialogs/OneSixModEditDialog.cpp
+++ b/gui/dialogs/OneSixModEditDialog.cpp
@@ -145,10 +145,6 @@ void OneSixModEditDialog::on_removeLibraryBtn_clicked()
 		{
 			QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file"));
 		}
-		else
-		{
-			reloadInstanceVersion();
-		}
 	}
 }
 
diff --git a/logic/VersionFinal.cpp b/logic/VersionFinal.cpp
index 2901fcf9..d0e85c15 100644
--- a/logic/VersionFinal.cpp
+++ b/logic/VersionFinal.cpp
@@ -72,9 +72,13 @@ bool VersionFinal::canRemove(const int index) const
 }
 bool VersionFinal::remove(const int index)
 {
-	if (canRemove(index))
+	if (canRemove(index) && QFile::remove(versionFiles.at(index)->filename))
 	{
-		return QFile::remove(versionFiles.at(index)->filename);
+		beginResetModel();
+		versionFiles.removeAt(index);
+		reapply(true);
+		endResetModel();
+		return true;
 	}
 	return false;
 }