463 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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();
 | 
						|
	}
 | 
						|
};
 |