mirror-chatterino2/src/util/signalvectormodel.hpp
2018-05-28 08:34:54 +02:00

314 lines
8.9 KiB
C++

#pragma once
#include <QAbstractTableModel>
#include <QStandardItem>
#include <boost/optional.hpp>
#include <util/signalvector2.hpp>
#include <pajlada/signals/signalholder.hpp>
namespace chatterino {
namespace util {
template <typename TVectorItem>
class SignalVectorModel : public QAbstractTableModel, pajlada::Signals::SignalHolder
{
public:
SignalVectorModel(int columnCount, QObject *parent = nullptr)
: QAbstractTableModel(parent)
, _columnCount(columnCount)
{
for (int i = 0; i < columnCount; i++) {
this->_headerData.emplace_back();
}
}
void init(util::BaseSignalVector<TVectorItem> *vec)
{
this->vector = vec;
auto insert = [this](const SignalVectorItemArgs<TVectorItem> &args) {
// get row index
int index = this->getModelIndexFromVectorIndex(args.index);
assert(index >= 0 && index <= this->rows.size());
// get row items
std::vector<QStandardItem *> row = this->createRow();
this->getRowFromItem(args.item, row);
// insert row
index = this->beforeInsert(args.item, row, index);
this->beginInsertRows(QModelIndex(), index, index);
this->rows.insert(this->rows.begin() + index, Row(row, args.item));
this->endInsertRows();
};
int i = 0;
for (const TVectorItem &item : vec->getVector()) {
SignalVectorItemArgs<TVectorItem> args{item, i++, 0};
insert(args);
}
this->managedConnect(vec->itemInserted, insert);
this->managedConnect(vec->itemRemoved, [this](auto args) {
int row = this->getModelIndexFromVectorIndex(args.index);
assert(row >= 0 && row <= this->rows.size());
// remove row
std::vector<QStandardItem *> items = std::move(this->rows[row].items);
this->beginRemoveRows(QModelIndex(), row, row);
this->rows.erase(this->rows.begin() + row);
this->endRemoveRows();
this->afterRemoved(args.item, items, row);
for (QStandardItem *item : items) {
delete item;
}
});
this->afterInit();
}
virtual ~SignalVectorModel()
{
for (Row &row : this->rows) {
for (QStandardItem *item : row.items) {
delete item;
}
}
}
virtual int rowCount(const QModelIndex &parent) const
{
return this->rows.size();
}
virtual int columnCount(const QModelIndex &parent) const
{
return this->_columnCount;
}
virtual QVariant data(const QModelIndex &index, int role) const
{
int row = index.row(), column = index.column();
assert(row >= 0 && row < this->rows.size() && column >= 0 && column < this->_columnCount);
return rows[row].items[column]->data(role);
}
virtual bool setData(const QModelIndex &index, const QVariant &value, int role)
{
int row = index.row(), column = index.column();
assert(row >= 0 && row < this->rows.size() && column >= 0 && column < this->_columnCount);
Row &rowItem = this->rows[row];
rowItem.items[column]->setData(value, role);
if (rowItem.isCustomRow) {
this->customRowSetData(rowItem.items, column, value, role);
} else {
int vecRow = this->getVectorIndexFromModelIndex(row);
this->vector->removeItem(vecRow, this);
assert(this->rows[row].original);
TVectorItem item =
this->getItemFromRow(this->rows[row].items, this->rows[row].original.get());
this->vector->insertItem(item, vecRow, this);
}
return true;
}
QVariant headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation != Qt::Horizontal) {
return QVariant();
}
auto it = this->_headerData[section].find(role);
if (it == this->_headerData[section].end()) {
return QVariant();
} else {
return it.value();
}
}
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value,
int role = Qt::DisplayRole)
{
if (orientation != Qt::Horizontal) {
return false;
}
this->_headerData[section][role] = value;
emit this->headerDataChanged(Qt::Horizontal, section, section);
return true;
}
Qt::ItemFlags flags(const QModelIndex &index) const
{
int row = index.row(), column = index.column();
assert(row >= 0 && row < this->rows.size() && column >= 0 && column < this->_columnCount);
return this->rows[index.row()].items[index.column()]->flags();
}
QStandardItem *getItem(int row, int column)
{
assert(row >= 0 && row < this->rows.size() && column >= 0 && column < this->_columnCount);
return rows[row].items[column];
}
void deleteRow(int row)
{
int signalVectorRow = this->getVectorIndexFromModelIndex(row);
this->vector->removeItem(signalVectorRow);
}
virtual bool removeRows(int row, int count, const QModelIndex &parent) override
{
if (count != 1) {
return false;
}
assert(row >= 0 && row < this->rows.size());
int signalVectorRow = this->getVectorIndexFromModelIndex(row);
this->vector->removeItem(signalVectorRow, this);
return true;
}
protected:
virtual void afterInit()
{
}
// turn a vector item into a model row
virtual TVectorItem getItemFromRow(std::vector<QStandardItem *> &row,
const TVectorItem &original) = 0;
// turns a row in the model into a vector item
virtual void getRowFromItem(const TVectorItem &item, std::vector<QStandardItem *> &row) = 0;
virtual int beforeInsert(const TVectorItem &item, std::vector<QStandardItem *> &row,
int proposedIndex)
{
return proposedIndex;
}
virtual void afterRemoved(const TVectorItem &item, std::vector<QStandardItem *> &row, int index)
{
}
virtual void customRowSetData(const std::vector<QStandardItem *> &row, int column,
const QVariant &value, int role)
{
}
void insertCustomRow(std::vector<QStandardItem *> row, int index)
{
assert(index >= 0 && index <= this->rows.size());
this->beginInsertRows(QModelIndex(), index, index);
this->rows.insert(this->rows.begin() + index, Row(std::move(row), true));
this->endInsertRows();
}
void removeCustomRow(int index)
{
assert(index >= 0 && index <= this->rows.size());
assert(this->rows[index].isCustomRow);
this->beginRemoveRows(QModelIndex(), index, index);
this->rows.erase(this->rows.begin() + index);
this->endRemoveRows();
}
std::vector<QStandardItem *> createRow()
{
std::vector<QStandardItem *> row;
for (int i = 0; i < this->_columnCount; i++) {
row.push_back(new QStandardItem());
}
return row;
}
struct Row {
std::vector<QStandardItem *> items;
boost::optional<TVectorItem> original;
bool isCustomRow;
Row(std::vector<QStandardItem *> _items, bool _isCustomRow = false)
: items(std::move(_items))
, isCustomRow(_isCustomRow)
{
}
Row(std::vector<QStandardItem *> _items, const TVectorItem &_original,
bool _isCustomRow = false)
: items(std::move(_items))
, original(_original)
, isCustomRow(_isCustomRow)
{
}
};
std::vector<Row> rows;
private:
std::vector<QMap<int, QVariant>> _headerData;
BaseSignalVector<TVectorItem> *vector;
int _columnCount;
// returns the related index of the SignalVector
int getVectorIndexFromModelIndex(int index)
{
int i = 0;
for (auto &row : this->rows) {
if (row.isCustomRow) {
index--;
continue;
}
if (i == index) {
return i;
}
i++;
}
return i;
}
// returns the related index of the model
int getModelIndexFromVectorIndex(int index)
{
int i = 0;
for (auto &row : this->rows) {
if (row.isCustomRow) {
index++;
}
if (i == index) {
return i;
}
i++;
}
return i;
}
};
} // namespace util
} // namespace chatterino