MultiMC5/logic/AbstractCommonModel.h

463 lines
12 KiB
C++

/* 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();
}
};