summaryrefslogtreecommitdiffstats
path: root/logic
diff options
context:
space:
mode:
authorJan Dalheimer <jan@dalheimer.de>2013-12-20 14:47:26 +0100
committerPetr Mrázek <peterix@gmail.com>2013-12-21 23:40:37 +0100
commit82c87aa06f793b9f38e6cb42d284f00695f4bac5 (patch)
tree21f893b0336a90d4e4b9f14cb5312f2c1ea04280 /logic
parent34a3fedf7b115c84ff305bf72fbc9d568682d84a (diff)
downloadMultiMC-82c87aa06f793b9f38e6cb42d284f00695f4bac5.tar
MultiMC-82c87aa06f793b9f38e6cb42d284f00695f4bac5.tar.gz
MultiMC-82c87aa06f793b9f38e6cb42d284f00695f4bac5.tar.lz
MultiMC-82c87aa06f793b9f38e6cb42d284f00695f4bac5.tar.xz
MultiMC-82c87aa06f793b9f38e6cb42d284f00695f4bac5.zip
Initial FTB support. Allows "tracking" of FTB instances.
Diffstat (limited to 'logic')
-rw-r--r--logic/InstanceFactory.cpp86
-rw-r--r--logic/InstanceFactory.h11
-rw-r--r--logic/LegacyFTBInstance.cpp16
-rw-r--r--logic/LegacyFTBInstance.h13
-rw-r--r--logic/OneSixFTBInstance.cpp111
-rw-r--r--logic/OneSixFTBInstance.h20
-rw-r--r--logic/lists/ForgeVersionList.cpp1
-rw-r--r--logic/lists/InstanceList.cpp151
-rw-r--r--logic/lists/InstanceList.h14
-rw-r--r--logic/tasks/SequentialTask.cpp77
-rw-r--r--logic/tasks/SequentialTask.h32
11 files changed, 465 insertions, 67 deletions
diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp
index 66b271d0..31a287dd 100644
--- a/logic/InstanceFactory.cpp
+++ b/logic/InstanceFactory.cpp
@@ -20,7 +20,9 @@
#include "BaseInstance.h"
#include "LegacyInstance.h"
+#include "LegacyFTBInstance.h"
#include "OneSixInstance.h"
+#include "OneSixFTBInstance.h"
#include "NostalgiaInstance.h"
#include "BaseVersion.h"
#include "MinecraftVersion.h"
@@ -60,6 +62,14 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst
{
inst = new NostalgiaInstance(instDir, m_settings, this);
}
+ else if (inst_type == "LegacyFTB")
+ {
+ inst = new LegacyFTBInstance(instDir, m_settings, this);
+ }
+ else if (inst_type == "OneSixFTB")
+ {
+ inst = new OneSixFTBInstance(instDir, m_settings, this);
+ }
else
{
return InstanceFactory::UnknownLoadError;
@@ -69,7 +79,8 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst
InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *&inst,
BaseVersionPtr version,
- const QString &instDir)
+ const QString &instDir,
+ const InstType type)
{
QDir rootDir(instDir);
@@ -85,32 +96,63 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *&
auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg"));
m_settings->registerSetting(new Setting("InstanceType", "Legacy"));
- switch (mcVer->type)
+ if (type == NormalInst)
{
- case MinecraftVersion::Legacy:
- m_settings->set("InstanceType", "Legacy");
- inst = new LegacyInstance(instDir, m_settings, this);
- inst->setIntendedVersionId(version->descriptor());
- inst->setShouldUseCustomBaseJar(false);
- break;
- case MinecraftVersion::OneSix:
- m_settings->set("InstanceType", "OneSix");
- inst = new OneSixInstance(instDir, m_settings, this);
- inst->setIntendedVersionId(version->descriptor());
- inst->setShouldUseCustomBaseJar(false);
- break;
- case MinecraftVersion::Nostalgia:
- m_settings->set("InstanceType", "Nostalgia");
- inst = new NostalgiaInstance(instDir, m_settings, this);
- inst->setIntendedVersionId(version->descriptor());
- inst->setShouldUseCustomBaseJar(false);
- break;
- default:
+ switch (mcVer->type)
+ {
+ case MinecraftVersion::Legacy:
+ m_settings->set("InstanceType", "Legacy");
+ inst = new LegacyInstance(instDir, m_settings, this);
+ inst->setIntendedVersionId(version->descriptor());
+ inst->setShouldUseCustomBaseJar(false);
+ break;
+ case MinecraftVersion::OneSix:
+ m_settings->set("InstanceType", "OneSix");
+ inst = new OneSixInstance(instDir, m_settings, this);
+ inst->setIntendedVersionId(version->descriptor());
+ inst->setShouldUseCustomBaseJar(false);
+ break;
+ case MinecraftVersion::Nostalgia:
+ m_settings->set("InstanceType", "Nostalgia");
+ inst = new NostalgiaInstance(instDir, m_settings, this);
+ inst->setIntendedVersionId(version->descriptor());
+ inst->setShouldUseCustomBaseJar(false);
+ break;
+ default:
+ {
+ delete m_settings;
+ return InstanceFactory::NoSuchVersion;
+ }
+ }
+ }
+ else if (type == FTBInstance)
+ {
+ switch (mcVer->type)
+ {
+ case MinecraftVersion::Legacy:
+ m_settings->set("InstanceType", "LegacyFTB");
+ inst = new LegacyFTBInstance(instDir, m_settings, this);
+ inst->setIntendedVersionId(version->descriptor());
+ inst->setShouldUseCustomBaseJar(false);
+ break;
+ case MinecraftVersion::OneSix:
+ m_settings->set("InstanceType", "OneSixFTB");
+ inst = new OneSixFTBInstance(instDir, m_settings, this);
+ inst->setIntendedVersionId(version->descriptor());
+ inst->setShouldUseCustomBaseJar(false);
+ break;
+ default:
+ {
+ delete m_settings;
+ return InstanceFactory::NoSuchVersion;
+ }
+ }
+ }
+ else
{
delete m_settings;
return InstanceFactory::NoSuchVersion;
}
- }
// FIXME: really, how do you even know?
return InstanceFactory::NoCreateError;
diff --git a/logic/InstanceFactory.h b/logic/InstanceFactory.h
index 01e5af7e..5ff4c7ec 100644
--- a/logic/InstanceFactory.h
+++ b/logic/InstanceFactory.h
@@ -55,18 +55,25 @@ public:
CantCreateDir
};
+ enum InstType
+ {
+ NormalInst,
+ FTBInstance
+ };
+
/*!
* \brief Creates a stub instance
*
* \param inst Pointer to store the created instance in.
- * \param inst Game version to use for the instance
+ * \param version Game version to use for the instance
* \param instDir The new instance's directory.
+ * \param type The type of instance to create
* \return An InstCreateError error code.
* - InstExists if the given instance directory is already an instance.
* - CantCreateDir if the given instance directory cannot be created.
*/
InstCreateError createInstance(BaseInstance *&inst, BaseVersionPtr version,
- const QString &instDir);
+ const QString &instDir, const InstType type = NormalInst);
/*!
* \brief Creates a copy of an existing instance with a new name
diff --git a/logic/LegacyFTBInstance.cpp b/logic/LegacyFTBInstance.cpp
new file mode 100644
index 00000000..84d5a900
--- /dev/null
+++ b/logic/LegacyFTBInstance.cpp
@@ -0,0 +1,16 @@
+#include "LegacyFTBInstance.h"
+
+LegacyFTBInstance::LegacyFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) :
+ LegacyInstance(rootDir, settings, parent)
+{
+}
+
+QString LegacyFTBInstance::getStatusbarDescription()
+{
+ return "Legacy FTB: " + intendedVersionId();
+}
+
+bool LegacyFTBInstance::menuActionEnabled(QString action_name) const
+{
+ return false;
+}
diff --git a/logic/LegacyFTBInstance.h b/logic/LegacyFTBInstance.h
new file mode 100644
index 00000000..2ae72797
--- /dev/null
+++ b/logic/LegacyFTBInstance.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "LegacyInstance.h"
+
+class LegacyFTBInstance : public LegacyInstance
+{
+ Q_OBJECT
+public:
+ explicit LegacyFTBInstance(const QString &rootDir, SettingsObject *settings,
+ QObject *parent = 0);
+ virtual QString getStatusbarDescription();
+ virtual bool menuActionEnabled(QString action_name) const;
+};
diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp
new file mode 100644
index 00000000..567004b9
--- /dev/null
+++ b/logic/OneSixFTBInstance.cpp
@@ -0,0 +1,111 @@
+#include "OneSixFTBInstance.h"
+
+#include "OneSixVersion.h"
+#include "OneSixLibrary.h"
+#include "tasks/SequentialTask.h"
+#include "ForgeInstaller.h"
+#include "lists/ForgeVersionList.h"
+#include "MultiMC.h"
+
+class OneSixFTBInstanceForge : public Task
+{
+ Q_OBJECT
+public:
+ explicit OneSixFTBInstanceForge(const QString &version, OneSixFTBInstance *inst, QObject *parent = 0) :
+ Task(parent), instance(inst), version("Forge " + version)
+ {
+ }
+
+ void executeTask()
+ {
+ for (int i = 0; i < MMC->forgelist()->count(); ++i)
+ {
+ if (MMC->forgelist()->at(i)->name() == version)
+ {
+ forgeVersion = std::dynamic_pointer_cast<ForgeVersion>(MMC->forgelist()->at(i));
+ break;
+ }
+ }
+ if (!forgeVersion)
+ return;
+ entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename);
+ if (entry->stale)
+ {
+ setStatus(tr("Downloading Forge..."));
+ fjob = new NetJob("Forge download");
+ fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry));
+ connect(fjob, &NetJob::failed, [this](){emitFailed(m_failReason);});
+ connect(fjob, &NetJob::succeeded, this, &OneSixFTBInstanceForge::installForge);
+ connect(fjob, &NetJob::progress, [this](qint64 c, qint64 total){ setProgress(100 * c / total); });
+ fjob->start();
+ }
+ else
+ {
+ installForge();
+ }
+ }
+
+private
+slots:
+ void installForge()
+ {
+ setStatus(tr("Installing Forge..."));
+ QString forgePath = entry->getFullPath();
+ ForgeInstaller forge(forgePath, forgeVersion->universal_url);
+ if (!instance->reloadFullVersion())
+ {
+ emitFailed(tr("Couldn't load the version config"));
+ return;
+ }
+ if (!forge.apply(instance->getFullVersion()))
+ {
+ emitFailed(tr("Couldn't install Forge"));
+ return;
+ }
+ emitSucceeded();
+ }
+
+private:
+ OneSixFTBInstance *instance;
+ QString version;
+ ForgeVersionPtr forgeVersion;
+ MetaEntryPtr entry;
+ NetJob *fjob;
+};
+
+OneSixFTBInstance::OneSixFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) :
+ OneSixInstance(rootDir, settings, parent)
+{
+ QFile f(QDir(minecraftRoot()).absoluteFilePath("pack.json"));
+ if (f.open(QFile::ReadOnly))
+ {
+ QString data = QString::fromUtf8(f.readAll());
+ QRegularExpressionMatch match = QRegularExpression("net.minecraftforge:minecraftforge:[\\.\\d]*").match(data);
+ m_forge.reset(new OneSixLibrary(match.captured()));
+ m_forge->finalize();
+ }
+}
+
+QString OneSixFTBInstance::getStatusbarDescription()
+{
+ return "OneSix FTB: " + intendedVersionId();
+}
+bool OneSixFTBInstance::menuActionEnabled(QString action_name) const
+{
+ return false;
+}
+
+std::shared_ptr<Task> OneSixFTBInstance::doUpdate(bool only_prepare)
+{
+ std::shared_ptr<SequentialTask> task;
+ task.reset(new SequentialTask(this));
+ if (!MMC->forgelist()->isLoaded())
+ {
+ task->addTask(std::shared_ptr<Task>(MMC->forgelist()->getLoadTask()));
+ }
+ task->addTask(OneSixInstance::doUpdate(only_prepare));
+ task->addTask(std::shared_ptr<Task>(new OneSixFTBInstanceForge(m_forge->version(), this, this)));
+ return task;
+}
+
+#include "OneSixFTBInstance.moc"
diff --git a/logic/OneSixFTBInstance.h b/logic/OneSixFTBInstance.h
new file mode 100644
index 00000000..7600090c
--- /dev/null
+++ b/logic/OneSixFTBInstance.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "OneSixInstance.h"
+
+class OneSixLibrary;
+
+class OneSixFTBInstance : public OneSixInstance
+{
+ Q_OBJECT
+public:
+ explicit OneSixFTBInstance(const QString &rootDir, SettingsObject *settings,
+ QObject *parent = 0);
+ virtual QString getStatusbarDescription();
+ virtual bool menuActionEnabled(QString action_name) const;
+
+ virtual std::shared_ptr<Task> doUpdate(bool only_prepare) override;
+
+private:
+ std::shared_ptr<OneSixLibrary> m_forge;
+};
diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp
index b5e421af..d6d353da 100644
--- a/logic/lists/ForgeVersionList.cpp
+++ b/logic/lists/ForgeVersionList.cpp
@@ -159,6 +159,7 @@ ForgeListLoadTask::ForgeListLoadTask(ForgeVersionList *vlist) : Task()
void ForgeListLoadTask::executeTask()
{
+ setStatus(tr("Fetching Forge version list"));
auto job = new NetJob("Version index");
// we do not care if the version is stale or not.
auto forgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "list.json");
diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp
index 15fd10ba..539413d8 100644
--- a/logic/lists/InstanceList.cpp
+++ b/logic/lists/InstanceList.cpp
@@ -22,11 +22,14 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
+#include <QXmlStreamReader>
+#include <QRegularExpression>
#include <pathutils.h>
#include "MultiMC.h"
#include "logic/lists/InstanceList.h"
#include "logic/lists/IconList.h"
+#include "logic/lists/MinecraftVersionList.h"
#include "logic/BaseInstance.h"
#include "logic/InstanceFactory.h"
#include "logger/QsLog.h"
@@ -42,6 +45,8 @@ InstanceList::InstanceList(const QString &instDir, QObject *parent)
{
QDir::current().mkpath(m_instDir);
}
+
+ connect(MMC->minecraftlist().get(), &MinecraftVersionList::modelReset, this, &InstanceList::loadList);
}
InstanceList::~InstanceList()
@@ -285,57 +290,87 @@ InstanceList::InstListError InstanceList::loadList()
beginResetModel();
m_instances.clear();
- QDir dir(m_instDir);
- QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable,
- QDirIterator::FollowSymlinks);
- while (iter.hasNext())
+
{
- QString subDir = iter.next();
- if (!QFileInfo(PathCombine(subDir, "instance.cfg")).exists())
- continue;
+ QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable,
+ QDirIterator::FollowSymlinks);
+ while (iter.hasNext())
+ {
+ QString subDir = iter.next();
+ if (!QFileInfo(PathCombine(subDir, "instance.cfg")).exists())
+ continue;
- BaseInstance *instPtr = NULL;
- auto &loader = InstanceFactory::get();
- auto error = loader.loadInstance(instPtr, subDir);
+ BaseInstance *instPtr = NULL;
+ auto error = InstanceFactory::get().loadInstance(instPtr, subDir);
+ continueProcessInstance(instPtr, error, subDir, groupMap);
+ }
+ }
- if (error != InstanceFactory::NoLoadError && error != InstanceFactory::NotAnInstance)
+ if (MMC->settings()->get("TrackFTBInstances").toBool() && MMC->minecraftlist()->isLoaded())
+ {
+ QDir dir = QDir(MMC->settings()->get("FTBLauncherRoot").toString());
+ QDir dataDir = QDir(MMC->settings()->get("FTBRoot").toString());
+ if (!dir.exists())
{
- QString errorMsg = QString("Failed to load instance %1: ")
- .arg(QFileInfo(subDir).baseName())
- .toUtf8();
-
- switch (error)
- {
- default:
- errorMsg += QString("Unknown instance loader error %1").arg(error);
- break;
- }
- QLOG_ERROR() << errorMsg.toUtf8();
+ QLOG_INFO() << "The FTB launcher directory specified does not exist. Please check your settings.";
}
- else if (!instPtr)
+ else if (!dataDir.exists())
{
- QLOG_ERROR() << QString("Error loading instance %1. Instance loader returned null.")
- .arg(QFileInfo(subDir).baseName())
- .toUtf8();
+ QLOG_INFO() << "The FTB directory specified does not exist. Please check your settings";
}
else
{
- std::shared_ptr<BaseInstance> inst(instPtr);
- auto iter = groupMap.find(inst->id());
- if (iter != groupMap.end())
+ dir.cd("ModPacks");
+ QFile f(dir.absoluteFilePath("modpacks.xml"));
+ if (f.open(QFile::ReadOnly))
{
- inst->setGroupInitial((*iter));
+ QXmlStreamReader reader(&f);
+ while (!reader.atEnd())
+ {
+ switch (reader.readNext())
+ {
+ case QXmlStreamReader::StartElement:
+ {
+ if (reader.name() == "modpack")
+ {
+ QXmlStreamAttributes attrs = reader.attributes();
+ const QDir instanceDir = QDir(dataDir.absoluteFilePath(attrs.value("dir").toString()));
+ if (instanceDir.exists())
+ {
+ const QString name = attrs.value("name").toString();
+ const QString iconKey = attrs.value("logo").toString().remove(QRegularExpression("\\..*"));
+ const QString mcVersion = attrs.value("mcVersion").toString();
+ const QString notes = attrs.value("description").toString();
+ QLOG_DEBUG() << dir.absoluteFilePath(attrs.value("logo").toString());
+ MMC->icons()->addIcon(iconKey, iconKey, dir.absoluteFilePath(attrs.value("dir").toString() + QDir::separator() + attrs.value("logo").toString()), true);
+
+ BaseInstance *instPtr = NULL;
+ auto error = InstanceFactory::get().createInstance(instPtr, MMC->minecraftlist()->findVersion(mcVersion), instanceDir.absolutePath(), InstanceFactory::FTBInstance);
+ if (instPtr && error == InstanceFactory::NoCreateError)
+ {
+ instPtr->setGroupInitial("FTB");
+ instPtr->setName(name);
+ instPtr->setIconKey(iconKey);
+ instPtr->setIntendedVersionId(mcVersion);
+ instPtr->setNotes(notes);
+ }
+ continueProcessInstance(instPtr, error, instanceDir, groupMap);
+ }
+ }
+ break;
+ }
+ case QXmlStreamReader::EndElement:
+ break;
+ case QXmlStreamReader::Characters:
+ break;
+ default:
+ break;
+ }
+ }
}
- QLOG_INFO() << "Loaded instance " << inst->name();
- inst->setParent(this);
- m_instances.append(inst);
- connect(instPtr, SIGNAL(propertiesChanged(BaseInstance *)), this,
- SLOT(propertiesChanged(BaseInstance *)));
- connect(instPtr, SIGNAL(groupChanged()), this, SLOT(groupChanged()));
- connect(instPtr, SIGNAL(nuked(BaseInstance *)), this,
- SLOT(instanceNuked(BaseInstance *)));
}
}
+
endResetModel();
emit dataIsInvalid();
return NoError;
@@ -409,6 +444,46 @@ int InstanceList::getInstIndex(BaseInstance *inst) const
return -1;
}
+void InstanceList::continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir, QMap<QString, QString> &groupMap)
+{
+ if (error != InstanceFactory::NoLoadError && error != InstanceFactory::NotAnInstance)
+ {
+ QString errorMsg = QString("Failed to load instance %1: ")
+ .arg(QFileInfo(dir.absolutePath()).baseName())
+ .toUtf8();
+
+ switch (error)
+ {
+ default:
+ errorMsg += QString("Unknown instance loader error %1").arg(error);
+ break;
+ }
+ QLOG_ERROR() << errorMsg.toUtf8();
+ }
+ else if (!instPtr)
+ {
+ QLOG_ERROR() << QString("Error loading instance %1. Instance loader returned null.")
+ .arg(QFileInfo(dir.absolutePath()).baseName())
+ .toUtf8();
+ }
+ else
+ {
+ auto iter = groupMap.find(instPtr->id());
+ if (iter != groupMap.end())
+ {
+ instPtr->setGroupInitial((*iter));
+ }
+ QLOG_INFO() << "Loaded instance " << instPtr->name();
+ instPtr->setParent(this);
+ m_instances.append(std::shared_ptr<BaseInstance>(instPtr));
+ connect(instPtr, SIGNAL(propertiesChanged(BaseInstance *)), this,
+ SLOT(propertiesChanged(BaseInstance *)));
+ connect(instPtr, SIGNAL(groupChanged()), this, SLOT(groupChanged()));
+ connect(instPtr, SIGNAL(nuked(BaseInstance *)), this,
+ SLOT(instanceNuked(BaseInstance *)));
+ }
+}
+
void InstanceList::instanceNuked(BaseInstance *inst)
{
int i = getInstIndex(inst);
diff --git a/logic/lists/InstanceList.h b/logic/lists/InstanceList.h
index f23b7763..b3ee6cfe 100644
--- a/logic/lists/InstanceList.h
+++ b/logic/lists/InstanceList.h
@@ -25,6 +25,8 @@
class BaseInstance;
+class QDir;
+
class InstanceList : public QAbstractListModel
{
Q_OBJECT
@@ -66,11 +68,6 @@ public:
}
/*!
- * \brief Loads the instance list. Triggers notifications.
- */
- InstListError loadList();
-
- /*!
* \brief Get the instance at index
*/
InstancePtr at(int i) const
@@ -108,6 +105,11 @@ public
slots:
void on_InstFolderChanged(const Setting &setting, QVariant value);
+ /*!
+ * \brief Loads the instance list. Triggers notifications.
+ */
+ InstListError loadList();
+
private
slots:
void propertiesChanged(BaseInstance *inst);
@@ -117,6 +119,8 @@ slots:
private:
int getInstIndex(BaseInstance *inst) const;
+ void continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir, QMap<QString, QString> &groupMap);
+
protected:
QString m_instDir;
QList<InstancePtr> m_instances;
diff --git a/logic/tasks/SequentialTask.cpp b/logic/tasks/SequentialTask.cpp
new file mode 100644
index 00000000..63025eee
--- /dev/null
+++ b/logic/tasks/SequentialTask.cpp
@@ -0,0 +1,77 @@
+#include "SequentialTask.h"
+
+SequentialTask::SequentialTask(QObject *parent) :
+ Task(parent), m_currentIndex(-1)
+{
+
+}
+
+QString SequentialTask::getStatus() const
+{
+ if (m_queue.isEmpty() || m_currentIndex >= m_queue.size())
+ {
+ return QString();
+ }
+ return m_queue.at(m_currentIndex)->getStatus();
+}
+
+void SequentialTask::getProgress(qint64 &current, qint64 &total)
+{
+ current = 0;
+ total = 0;
+ for (int i = 0; i < m_queue.size(); ++i)
+ {
+ qint64 subCurrent, subTotal;
+ m_queue.at(i)->getProgress(subCurrent, subTotal);
+ current += subCurrent;
+ total += subTotal;
+ }
+}
+
+void SequentialTask::addTask(std::shared_ptr<Task> task)
+{
+ m_queue.append(task);
+}
+
+void SequentialTask::executeTask()
+{
+ m_currentIndex = -1;
+ startNext();
+}
+
+void SequentialTask::startNext()
+{
+ if (m_currentIndex != -1)
+ {
+ std::shared_ptr<Task> previous = m_queue[m_currentIndex];
+ disconnect(previous.get(), 0, this, 0);
+ }
+ m_currentIndex++;
+ if (m_queue.isEmpty() || m_currentIndex >= m_queue.size())
+ {
+ emitSucceeded();
+ return;
+ }
+ std::shared_ptr<Task> next = m_queue[m_currentIndex];
+ connect(next.get(), SIGNAL(failed(QString)), this, SLOT(subTaskFailed(QString)));
+ connect(next.get(), SIGNAL(status(QString)), this, SLOT(subTaskStatus(QString)));
+ connect(next.get(), SIGNAL(progress(qint64,qint64)), this, SLOT(subTaskProgress()));
+ connect(next.get(), SIGNAL(succeeded()), this, SLOT(startNext()));
+ next->start();
+ emit status(getStatus());
+}
+
+void SequentialTask::subTaskFailed(const QString &msg)
+{
+ emitFailed(msg);
+}
+void SequentialTask::subTaskStatus(const QString &msg)
+{
+ setStatus(msg);
+}
+void SequentialTask::subTaskProgress()
+{
+ qint64 current, total;
+ getProgress(current, total);
+ setProgress(100 * current / total);
+}
diff --git a/logic/tasks/SequentialTask.h b/logic/tasks/SequentialTask.h
new file mode 100644
index 00000000..7f046928
--- /dev/null
+++ b/logic/tasks/SequentialTask.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "Task.h"
+
+#include <QQueue>
+#include <memory>
+
+class SequentialTask : public Task
+{
+ Q_OBJECT
+public:
+ explicit SequentialTask(QObject *parent = 0);
+
+ virtual QString getStatus() const;
+ virtual void getProgress(qint64 &current, qint64 &total);
+
+ void addTask(std::shared_ptr<Task> task);
+
+protected:
+ void executeTask();
+
+private
+slots:
+ void startNext();
+ void subTaskFailed(const QString &msg);
+ void subTaskStatus(const QString &msg);
+ void subTaskProgress();
+
+private:
+ QQueue<std::shared_ptr<Task> > m_queue;
+ int m_currentIndex;
+};