aboutsummaryrefslogtreecommitdiff
path: root/logic
diff options
context:
space:
mode:
authorJan Dalheimer <jan@dalheimer.de>2015-05-28 19:38:29 +0200
committerPetr Mrázek <peterix@gmail.com>2015-06-06 21:23:05 +0200
commit3a8b238052163952831fb5924b2483a375e86ebd (patch)
treeab120b4fac3a5345a20e7a09e1e7477e67d9ed6f /logic
parent161dc66c2c8d5f973ee69dab36c3969a7efd7495 (diff)
downloadPrismLauncher-3a8b238052163952831fb5924b2483a375e86ebd.tar.gz
PrismLauncher-3a8b238052163952831fb5924b2483a375e86ebd.tar.bz2
PrismLauncher-3a8b238052163952831fb5924b2483a375e86ebd.zip
NOISSUE Various changes from multiauth that are unrelated to it
Diffstat (limited to 'logic')
-rw-r--r--logic/AbstractCommonModel.cpp133
-rw-r--r--logic/AbstractCommonModel.h462
-rw-r--r--logic/BaseConfigObject.cpp119
-rw-r--r--logic/BaseConfigObject.h50
-rw-r--r--logic/CMakeLists.txt30
-rw-r--r--logic/Env.cpp3
-rw-r--r--logic/Exception.h41
-rw-r--r--logic/FileSystem.cpp56
-rw-r--r--logic/FileSystem.h13
-rw-r--r--logic/Json.cpp278
-rw-r--r--logic/Json.h239
-rw-r--r--logic/MMCError.h25
-rw-r--r--logic/MMCJson.cpp142
-rw-r--r--logic/MMCJson.h101
-rw-r--r--logic/QObjectPtr.h5
-rw-r--r--logic/forge/ForgeInstaller.cpp3
-rw-r--r--logic/liteloader/LiteLoaderInstaller.cpp3
-rw-r--r--logic/liteloader/LiteLoaderVersionList.cpp4
-rw-r--r--logic/minecraft/JarMod.cpp4
-rw-r--r--logic/minecraft/MinecraftProfile.cpp7
-rw-r--r--logic/minecraft/MinecraftVersionList.cpp31
-rw-r--r--logic/minecraft/OneSixInstance.cpp3
-rw-r--r--logic/minecraft/OneSixProfileStrategy.cpp4
-rw-r--r--logic/minecraft/OneSixUpdate.cpp3
-rw-r--r--logic/minecraft/ParseUtils.cpp1
-rw-r--r--logic/minecraft/ProfileUtils.cpp10
-rw-r--r--logic/minecraft/RawLibrary.cpp6
-rw-r--r--logic/minecraft/VersionBuildError.h8
-rw-r--r--logic/minecraft/VersionFile.cpp4
-rw-r--r--logic/minecraft/VersionFile.h3
-rw-r--r--logic/net/CacheDownload.h29
-rw-r--r--logic/resources/IconResourceHandler.cpp60
-rw-r--r--logic/resources/IconResourceHandler.h22
-rw-r--r--logic/resources/Resource.cpp121
-rw-r--r--logic/resources/Resource.h116
-rw-r--r--logic/resources/ResourceHandler.cpp28
-rw-r--r--logic/resources/ResourceHandler.h33
-rw-r--r--logic/resources/ResourceObserver.cpp55
-rw-r--r--logic/resources/ResourceObserver.h67
-rw-r--r--logic/resources/ResourceProxyModel.cpp103
-rw-r--r--logic/resources/ResourceProxyModel.h36
-rw-r--r--logic/resources/WebResourceHandler.cpp67
-rw-r--r--logic/resources/WebResourceHandler.h23
-rw-r--r--logic/tasks/StandardTask.cpp120
-rw-r--r--logic/tasks/StandardTask.h43
-rw-r--r--logic/tasks/Task.cpp1
-rw-r--r--logic/tasks/Task.h2
47 files changed, 2398 insertions, 319 deletions
diff --git a/logic/AbstractCommonModel.cpp b/logic/AbstractCommonModel.cpp
new file mode 100644
index 00000000..71d75829
--- /dev/null
+++ b/logic/AbstractCommonModel.cpp
@@ -0,0 +1,133 @@
+/* Copyright 2015 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AbstractCommonModel.h"
+
+BaseAbstractCommonModel::BaseAbstractCommonModel(const Qt::Orientation orientation, QObject *parent)
+ : QAbstractListModel(parent), m_orientation(orientation)
+{
+}
+
+int BaseAbstractCommonModel::rowCount(const QModelIndex &parent) const
+{
+ return m_orientation == Qt::Horizontal ? entryCount() : size();
+}
+int BaseAbstractCommonModel::columnCount(const QModelIndex &parent) const
+{
+ return m_orientation == Qt::Horizontal ? size() : entryCount();
+}
+QVariant BaseAbstractCommonModel::data(const QModelIndex &index, int role) const
+{
+ if (!hasIndex(index.row(), index.column(), index.parent()))
+ {
+ return QVariant();
+ }
+ const int i = m_orientation == Qt::Horizontal ? index.column() : index.row();
+ const int entry = m_orientation == Qt::Horizontal ? index.row() : index.column();
+ return formatData(i, role, get(i, entry, role));
+}
+QVariant BaseAbstractCommonModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation != m_orientation && role == Qt::DisplayRole)
+ {
+ return entryTitle(section);
+ }
+ else
+ {
+ return QVariant();
+ }
+}
+bool BaseAbstractCommonModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ const int i = m_orientation == Qt::Horizontal ? index.column() : index.row();
+ const int entry = m_orientation == Qt::Horizontal ? index.row() : index.column();
+ const bool result = set(i, entry, role, sanetizeData(i, role, value));
+ if (result)
+ {
+ emit dataChanged(index, index, QVector<int>() << role);
+ }
+ return result;
+}
+Qt::ItemFlags BaseAbstractCommonModel::flags(const QModelIndex &index) const
+{
+ if (!hasIndex(index.row(), index.column(), index.parent()))
+ {
+ return Qt::NoItemFlags;
+ }
+
+ const int entry = m_orientation == Qt::Horizontal ? index.row() : index.column();
+ if (canSet(entry))
+ {
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEnabled;
+ }
+ else
+ {
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+ }
+}
+
+void BaseAbstractCommonModel::notifyAboutToAddObject(const int at)
+{
+ if (m_orientation == Qt::Horizontal)
+ {
+ beginInsertColumns(QModelIndex(), at, at);
+ }
+ else
+ {
+ beginInsertRows(QModelIndex(), at, at);
+ }
+}
+void BaseAbstractCommonModel::notifyObjectAdded()
+{
+ if (m_orientation == Qt::Horizontal)
+ {
+ endInsertColumns();
+ }
+ else
+ {
+ endInsertRows();
+ }
+}
+void BaseAbstractCommonModel::notifyAboutToRemoveObject(const int at)
+{
+ if (m_orientation == Qt::Horizontal)
+ {
+ beginRemoveColumns(QModelIndex(), at, at);
+ }
+ else
+ {
+ beginRemoveRows(QModelIndex(), at, at);
+ }
+}
+void BaseAbstractCommonModel::notifyObjectRemoved()
+{
+ if (m_orientation == Qt::Horizontal)
+ {
+ endRemoveColumns();
+ }
+ else
+ {
+ endRemoveRows();
+ }
+}
+
+void BaseAbstractCommonModel::notifyBeginReset()
+{
+ beginResetModel();
+}
+void BaseAbstractCommonModel::notifyEndReset()
+{
+ endResetModel();
+}
diff --git a/logic/AbstractCommonModel.h b/logic/AbstractCommonModel.h
new file mode 100644
index 00000000..31b86a23
--- /dev/null
+++ b/logic/AbstractCommonModel.h
@@ -0,0 +1,462 @@
+/* Copyright 2015 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <QAbstractListModel>
+#include <type_traits>
+#include <functional>
+#include <memory>
+
+class BaseAbstractCommonModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ explicit BaseAbstractCommonModel(const Qt::Orientation orientation, QObject *parent = nullptr);
+
+ // begin QAbstractItemModel interface
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+ bool setData(const QModelIndex &index, const QVariant &value, int role) override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+ // end QAbstractItemModel interface
+
+ virtual int size() const = 0;
+ virtual int entryCount() const = 0;
+
+ virtual QVariant formatData(const int index, int role, const QVariant &data) const { return data; }
+ virtual QVariant sanetizeData(const int index, int role, const QVariant &data) const { return data; }
+
+protected:
+ virtual QVariant get(const int index, const int entry, const int role) const = 0;
+ virtual bool set(const int index, const int entry, const int role, const QVariant &value) = 0;
+ virtual bool canSet(const int entry) const = 0;
+ virtual QString entryTitle(const int entry) const = 0;
+
+ void notifyAboutToAddObject(const int at);
+ void notifyObjectAdded();
+ void notifyAboutToRemoveObject(const int at);
+ void notifyObjectRemoved();
+ void notifyBeginReset();
+ void notifyEndReset();
+
+ const Qt::Orientation m_orientation;
+};
+
+template<typename Object>
+class AbstractCommonModel : public BaseAbstractCommonModel
+{
+public:
+ explicit AbstractCommonModel(const Qt::Orientation orientation)
+ : BaseAbstractCommonModel(orientation) {}
+ virtual ~AbstractCommonModel() {}
+
+ int size() const override { return m_objects.size(); }
+ int entryCount() const override { return m_entries.size(); }
+
+ void append(const Object &object)
+ {
+ notifyAboutToAddObject(size());
+ m_objects.append(object);
+ notifyObjectAdded();
+ }
+ void prepend(const Object &object)
+ {
+ notifyAboutToAddObject(0);
+ m_objects.prepend(object);
+ notifyObjectAdded();
+ }
+ void insert(const Object &object, const int index)
+ {
+ if (index >= size())
+ {
+ prepend(object);
+ }
+ else if (index <= 0)
+ {
+ append(object);
+ }
+ else
+ {
+ notifyAboutToAddObject(index);
+ m_objects.insert(index, object);
+ notifyObjectAdded();
+ }
+ }
+ void remove(const int index)
+ {
+ notifyAboutToRemoveObject(index);
+ m_objects.removeAt(index);
+ notifyObjectRemoved();
+ }
+ Object get(const int index) const
+ {
+ return m_objects.at(index);
+ }
+
+private:
+ friend class CommonModel;
+ QVariant get(const int index, const int entry, const int role) const override
+ {
+ if (m_entries.size() < entry || !m_entries[entry].second.contains(role))
+ {
+ return QVariant();
+ }
+ return m_entries[entry].second.value(role)->get(m_objects.at(index));
+ }
+ bool set(const int index, const int entry, const int role, const QVariant &value) override
+ {
+ if (m_entries.size() < entry || !m_entries[entry].second.contains(role))
+ {
+ return false;
+ }
+ IEntry *e = m_entries[entry].second.value(role);
+ if (!e->canSet())
+ {
+ return false;
+ }
+ e->set(m_objects[index], value);
+ return true;
+ }
+ bool canSet(const int entry) const override
+ {
+ if (m_entries.size() < entry || !m_entries[entry].second.contains(Qt::EditRole))
+ {
+ return false;
+ }
+ IEntry *e = m_entries[entry].second.value(Qt::EditRole);
+ return e->canSet();
+ }
+
+ QString entryTitle(const int entry) const override
+ {
+ return m_entries.at(entry).first;
+ }
+
+private:
+ struct IEntry
+ {
+ virtual ~IEntry() {}
+ virtual void set(Object &object, const QVariant &value) = 0;
+ virtual QVariant get(const Object &object) const = 0;
+ virtual bool canSet() const = 0;
+ };
+ template<typename T>
+ struct VariableEntry : public IEntry
+ {
+ typedef T (Object::*Member);
+
+ explicit VariableEntry(Member member)
+ : m_member(member) {}
+
+ void set(Object &object, const QVariant &value) override
+ {
+ object.*m_member = value.value<T>();
+ }
+ QVariant get(const Object &object) const override
+ {
+ return QVariant::fromValue<T>(object.*m_member);
+ }
+ bool canSet() const override { return true; }
+
+ private:
+ Member m_member;
+ };
+ template<typename T>
+ struct FunctionEntry : public IEntry
+ {
+ typedef T (Object::*Getter)() const;
+ typedef void (Object::*Setter)(T);
+
+ explicit FunctionEntry(Getter getter, Setter setter)
+ : m_getter(m_getter), m_setter(m_setter) {}
+
+ void set(Object &object, const QVariant &value) override
+ {
+ object.*m_setter(value.value<T>());
+ }
+ QVariant get(const Object &object) const override
+ {
+ return QVariant::fromValue<T>(object.*m_getter());
+ }
+ bool canSet() const override { return !!m_setter; }
+
+ private:
+ Getter m_getter;
+ Setter m_setter;
+ };
+
+ QList<Object> m_objects;
+ QVector<QPair<QString, QMap<int, IEntry *>>> m_entries;
+
+ void addEntryInternal(IEntry *e, const int entry, const int role)
+ {
+ if (m_entries.size() <= entry)
+ {
+ m_entries.resize(entry + 1);
+ }
+ m_entries[entry].second.insert(role, e);
+ }
+
+protected:
+ template<typename Getter, typename Setter>
+ typename std::enable_if<std::is_member_function_pointer<Getter>::value && std::is_member_function_pointer<Getter>::value, void>::type
+ addEntry(Getter getter, Setter setter, const int entry, const int role)
+ {
+ addEntryInternal(new FunctionEntry<typename std::result_of<Getter>::type>(getter, setter), entry, role);
+ }
+ template<typename Getter>
+ typename std::enable_if<std::is_member_function_pointer<Getter>::value, void>::type
+ addEntry(Getter getter, const int entry, const int role)
+ {
+ addEntryInternal(new FunctionEntry<typename std::result_of<Getter>::type>(getter, nullptr), entry, role);
+ }
+ template<typename T>
+ typename std::enable_if<!std::is_member_function_pointer<T (Object::*)>::value, void>::type
+ addEntry(T (Object::*member), const int entry, const int role)
+ {
+ addEntryInternal(new VariableEntry<T>(member), entry, role);
+ }
+
+ void setEntryTitle(const int entry, const QString &title)
+ {
+ m_entries[entry].first = title;
+ }
+};
+template<typename Object>
+class AbstractCommonModel<Object *> : public BaseAbstractCommonModel
+{
+public:
+ explicit AbstractCommonModel(const Qt::Orientation orientation)
+ : BaseAbstractCommonModel(orientation) {}
+ virtual ~AbstractCommonModel()
+ {
+ qDeleteAll(m_objects);
+ }
+
+ int size() const override { return m_objects.size(); }
+ int entryCount() const override { return m_entries.size(); }
+
+ void append(Object *object)
+ {
+ notifyAboutToAddObject(size());
+ m_objects.append(object);
+ notifyObjectAdded();
+ }
+ void prepend(Object *object)
+ {
+ notifyAboutToAddObject(0);
+ m_objects.prepend(object);
+ notifyObjectAdded();
+ }
+ void insert(Object *object, const int index)
+ {
+ if (index >= size())
+ {
+ prepend(object);
+ }
+ else if (index <= 0)
+ {
+ append(object);
+ }
+ else
+ {
+ notifyAboutToAddObject(index);
+ m_objects.insert(index, object);
+ notifyObjectAdded();
+ }
+ }
+ void remove(const int index)
+ {
+ notifyAboutToRemoveObject(index);
+ m_objects.removeAt(index);
+ notifyObjectRemoved();
+ }
+ Object *get(const int index) const
+ {
+ return m_objects.at(index);
+ }
+ int find(Object * const obj) const
+ {
+ return m_objects.indexOf(obj);
+ }
+
+ QList<Object *> getAll() const
+ {
+ return m_objects;
+ }
+
+private:
+ friend class CommonModel;
+ QVariant get(const int index, const int entry, const int role) const override
+ {
+ if (m_entries.size() < entry || !m_entries[entry].second.contains(role))
+ {
+ return QVariant();
+ }
+ return m_entries[entry].second.value(role)->get(m_objects.at(index));
+ }
+ bool set(const int index, const int entry, const int role, const QVariant &value) override
+ {
+ if (m_entries.size() < entry || !m_entries[entry].second.contains(role))
+ {
+ return false;
+ }
+ IEntry *e = m_entries[entry].second.value(role);
+ if (!e->canSet())
+ {
+ return false;
+ }
+ e->set(m_objects[index], value);
+ return true;
+ }
+ bool canSet(const int entry) const override
+ {
+ if (m_entries.size() < entry || !m_entries[entry].second.contains(Qt::EditRole))
+ {
+ return false;
+ }
+ IEntry *e = m_entries[entry].second.value(Qt::EditRole);
+ return e->canSet();
+ }
+
+ QString entryTitle(const int entry) const override
+ {
+ return m_entries.at(entry).first;
+ }
+
+private:
+ struct IEntry
+ {
+ virtual ~IEntry() {}
+ virtual void set(Object *object, const QVariant &value) = 0;
+ virtual QVariant get(Object *object) const = 0;
+ virtual bool canSet() const = 0;
+ };
+ template<typename T>
+ struct VariableEntry : public IEntry
+ {
+ typedef T (Object::*Member);
+
+ explicit VariableEntry(Member member)
+ : m_member(member) {}
+
+ void set(Object *object, const QVariant &value) override
+ {
+ object->*m_member = value.value<T>();
+ }
+ QVariant get(Object *object) const override
+ {
+ return QVariant::fromValue<T>(object->*m_member);
+ }
+ bool canSet() const override { return true; }
+
+ private:
+ Member m_member;
+ };
+ template<typename T>
+ struct FunctionEntry : public IEntry
+ {
+ typedef T (Object::*Getter)() const;
+ typedef void (Object::*Setter)(T);
+
+ explicit FunctionEntry(Getter getter, Setter setter)
+ : m_getter(getter), m_setter(setter) {}
+
+ void set(Object *object, const QVariant &value) override
+ {
+ (object->*m_setter)(value.value<T>());
+ }
+ QVariant get(Object *object) const override
+ {
+ return QVariant::fromValue<T>((object->*m_getter)());
+ }
+ bool canSet() const override { return !!m_setter; }
+
+ private:
+ Getter m_getter;
+ Setter m_setter;
+ };
+ template<typename T>
+ struct LambdaEntry : public IEntry
+ {
+ using Getter = std::function<T(Object *)>;
+
+ explicit LambdaEntry(Getter getter)
+ : m_getter(getter) {}
+
+ void set(Object *object, const QVariant &value) override {}
+ QVariant get(Object *object) const override
+ {
+ return QVariant::fromValue<T>(m_getter(object));
+ }
+ bool canSet() const override { return false; }
+
+ private:
+ Getter m_getter;
+ };
+
+ QList<Object *> m_objects;
+ QVector<QPair<QString, QMap<int, IEntry *>>> m_entries;
+
+ void addEntryInternal(IEntry *e, const int entry, const int role)
+ {
+ if (m_entries.size() <= entry)
+ {
+ m_entries.resize(entry + 1);
+ }
+ m_entries[entry].second.insert(role, e);
+ }
+
+protected:
+ template<typename Getter, typename Setter>
+ typename std::enable_if<std::is_member_function_pointer<Getter>::value && std::is_member_function_pointer<Getter>::value, void>::type
+ addEntry(const int entry, const int role, Getter getter, Setter setter)
+ {
+ addEntryInternal(new FunctionEntry<typename std::result_of<Getter>::type>(getter, setter), entry, role);
+ }
+ template<typename T>
+ typename std::enable_if<std::is_member_function_pointer<typename FunctionEntry<T>::Getter>::value, void>::type
+ addEntry(const int entry, const int role, typename FunctionEntry<T>::Getter getter)
+ {
+ addEntryInternal(new FunctionEntry<T>(getter, nullptr), entry, role);
+ }
+ template<typename T>
+ typename std::enable_if<!std::is_member_function_pointer<T (Object::*)>::value, void>::type
+ addEntry(const int entry, const int role, T (Object::*member))
+ {
+ addEntryInternal(new VariableEntry<T>(member), entry, role);
+ }
+ template<typename T>
+ void addEntry(const int entry, const int role, typename LambdaEntry<T>::Getter lambda)
+ {
+ addEntryInternal(new LambdaEntry<T>(lambda), entry, role);
+ }
+
+ void setEntryTitle(const int entry, const QString &title)
+ {
+ m_entries[entry].first = title;
+ }
+
+ void setAll(const QList<Object *> objects)
+ {
+ notifyBeginReset();
+ qDeleteAll(m_objects);
+ m_objects = objects;
+ notifyEndReset();
+ }
+};
diff --git a/logic/BaseConfigObject.cpp b/logic/BaseConfigObject.cpp
new file mode 100644
index 00000000..ff698ad0
--- /dev/null
+++ b/logic/BaseConfigObject.cpp
@@ -0,0 +1,119 @@
+/* Copyright 2015 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BaseConfigObject.h"
+
+#include <QTimer>
+#include <QSaveFile>
+#include <QFile>
+#include <QCoreApplication>
+#include <QDebug>
+
+#include "Exception.h"
+
+BaseConfigObject::BaseConfigObject(const QString &filename)
+ : m_filename(filename)
+{
+ m_saveTimer = new QTimer;
+ m_saveTimer->setSingleShot(true);
+ // cppcheck-suppress pureVirtualCall
+ QObject::connect(m_saveTimer, &QTimer::timeout, [this](){saveNow();});
+ setSaveTimeout(250);
+
+ m_initialReadTimer = new QTimer;
+ m_initialReadTimer->setSingleShot(true);
+ QObject::connect(m_initialReadTimer, &QTimer::timeout, [this]()
+ {
+ loadNow();
+ m_initialReadTimer->deleteLater();
+ m_initialReadTimer = 0;
+ });
+ m_initialReadTimer->start(0);
+
+ // cppcheck-suppress pureVirtualCall
+ m_appQuitConnection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [this](){saveNow();});
+}
+BaseConfigObject::~BaseConfigObject()
+{
+ delete m_saveTimer;
+ if (m_initialReadTimer)
+ {
+ delete m_initialReadTimer;
+ }
+ QObject::disconnect(m_appQuitConnection);
+}
+
+void BaseConfigObject::setSaveTimeout(int msec)
+{
+ m_saveTimer->setInterval(msec);
+}
+
+void BaseConfigObject::scheduleSave()
+{
+ m_saveTimer->stop();
+ m_saveTimer->start();
+}
+void BaseConfigObject::saveNow()
+{
+ if (m_saveTimer->isActive())
+ {
+ m_saveTimer->stop();
+ }
+ if (m_disableSaving)
+ {
+ return;
+ }
+
+ QSaveFile file(m_filename);
+ if (!file.open(QFile::WriteOnly))
+ {
+ qWarning() << "Couldn't open" << m_filename << "for writing:" << file.errorString();
+ return;
+ }
+ // cppcheck-suppress pureVirtualCall
+ file.write(doSave());
+
+ if (!file.commit())
+ {
+ qCritical() << "Unable to commit the file" << file.fileName() << ":" << file.errorString();
+ file.cancelWriting();
+ }
+}
+void BaseConfigObject::loadNow()
+{
+ if (m_saveTimer->isActive())
+ {
+ saveNow();
+ }
+
+ QFile file(m_filename);
+ if (!file.exists())
+ {
+ return;
+ }
+ if (!file.open(QFile::ReadOnly))
+ {
+ qWarning() << "Couldn't open" << m_filename << "for reading:" << file.errorString();
+ return;
+ }
+ try
+ {
+ doLoad(file.readAll());
+ }
+ catch (Exception &e)
+ {
+ qWarning() << "Error loading" << m_filename << ":" << e.cause();
+ }
+}
diff --git a/logic/BaseConfigObject.h b/logic/BaseConfigObject.h
new file mode 100644
index 00000000..1c96b3d1
--- /dev/null
+++ b/logic/BaseConfigObject.h
@@ -0,0 +1,50 @@
+/* Copyright 2015 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <QObject>
+
+class QTimer;
+
+class BaseConfigObject
+{
+public:
+ void setSaveTimeout(int msec);
+
+protected:
+ explicit BaseConfigObject(const QString &filename);
+ virtual ~BaseConfigObject();
+
+ // cppcheck-suppress pureVirtualCall
+ virtual