mirror-chatterino2/src/common/SignalVectorModel.hpp

335 lines
9.5 KiB
C++
Raw Normal View History

#pragma once
#include "common/SignalVector.hpp"
2018-06-26 14:09:39 +02:00
#include <QAbstractTableModel>
#include <QStandardItem>
#include <boost/optional.hpp>
#include <pajlada/signals/signalholder.hpp>
namespace chatterino {
template <typename TVectorItem>
2018-08-06 21:17:03 +02:00
class SignalVectorModel : public QAbstractTableModel,
pajlada::Signals::SignalHolder
{
public:
2018-04-30 00:41:58 +02:00
SignalVectorModel(int columnCount, QObject *parent = nullptr)
: QAbstractTableModel(parent)
, columnCount_(columnCount)
{
2018-04-30 00:41:58 +02:00
for (int i = 0; i < columnCount; i++) {
this->headerData_.emplace_back();
2018-04-30 00:41:58 +02:00
}
}
2018-06-26 17:06:17 +02:00
void init(BaseSignalVector<TVectorItem> *vec)
2018-04-30 00:41:58 +02:00
{
this->vector_ = vec;
2018-04-30 00:41:58 +02:00
2018-05-27 03:17:33 +02:00
auto insert = [this](const SignalVectorItemArgs<TVectorItem> &args) {
2018-06-06 10:46:23 +02:00
if (args.caller == this) {
return;
}
// 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);
2018-08-06 21:17:03 +02:00
this->rows_.insert(this->rows_.begin() + index,
Row(row, args.item));
this->endInsertRows();
2018-04-30 00:41:58 +02:00
};
int i = 0;
for (const TVectorItem &item : vec->getVector()) {
2018-05-27 03:17:33 +02:00
SignalVectorItemArgs<TVectorItem> args{item, i++, 0};
2018-04-30 00:41:58 +02:00
insert(args);
}
this->managedConnect(vec->itemInserted, insert);
this->managedConnect(vec->itemRemoved, [this](auto args) {
2018-06-06 10:46:23 +02:00
if (args.caller == this) {
return;
}
int row = this->getModelIndexFromVectorIndex(args.index);
assert(row >= 0 && row <= this->rows_.size());
// remove row
2018-08-06 21:17:03 +02:00
std::vector<QStandardItem *> items =
std::move(this->rows_[row].items);
2018-05-28 08:34:54 +02:00
this->beginRemoveRows(QModelIndex(), row, row);
this->rows_.erase(this->rows_.begin() + row);
this->endRemoveRows();
2018-05-28 08:34:54 +02:00
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;
}
}
}
2018-07-03 19:42:28 +02:00
int rowCount(const QModelIndex &parent) const override
{
return this->rows_.size();
}
2018-07-03 19:42:28 +02:00
int columnCount(const QModelIndex &parent) const override
{
return this->columnCount_;
}
2018-07-03 19:42:28 +02:00
QVariant data(const QModelIndex &index, int role) const override
{
int row = index.row(), column = index.column();
2018-08-06 21:17:03 +02:00
assert(row >= 0 && row < this->rows_.size() && column >= 0 &&
column < this->columnCount_);
return rows_[row].items[column]->data(role);
}
2018-08-06 21:17:03 +02:00
bool setData(const QModelIndex &index, const QVariant &value,
int role) override
{
int row = index.row(), column = index.column();
2018-08-06 21:17:03 +02:00
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, row);
} else {
int vecRow = this->getVectorIndexFromModelIndex(row);
this->vector_->removeItem(vecRow, this);
assert(this->rows_[row].original);
2018-08-06 21:17:03 +02:00
TVectorItem item = this->getItemFromRow(
this->rows_[row].items, this->rows_[row].original.get());
this->vector_->insertItem(item, vecRow, this);
}
return true;
}
2018-08-06 21:17:03 +02:00
QVariant headerData(int section, Qt::Orientation orientation,
int role) const override
2018-04-30 00:41:58 +02:00
{
if (orientation != Qt::Horizontal) {
return QVariant();
}
auto it = this->headerData_[section].find(role);
if (it == this->headerData_[section].end()) {
2018-04-30 00:41:58 +02:00
return QVariant();
} else {
return it.value();
}
}
2018-08-06 21:17:03 +02:00
bool setHeaderData(int section, Qt::Orientation orientation,
const QVariant &value,
2018-07-03 19:42:28 +02:00
int role = Qt::DisplayRole) override
2018-04-30 00:41:58 +02:00
{
if (orientation != Qt::Horizontal) {
return false;
}
this->headerData_[section][role] = value;
emit this->headerDataChanged(Qt::Horizontal, section, section);
2018-04-30 00:41:58 +02:00
return true;
}
2018-07-03 19:42:28 +02:00
Qt::ItemFlags flags(const QModelIndex &index) const override
{
int row = index.row(), column = index.column();
2018-08-06 21:17:03 +02:00
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)
{
2018-08-06 21:17:03 +02:00
assert(row >= 0 && row < this->rows_.size() && column >= 0 &&
column < this->columnCount_);
return rows_[row].items[column];
2018-04-30 00:41:58 +02:00
}
void deleteRow(int row)
{
int signalVectorRow = this->getVectorIndexFromModelIndex(row);
this->vector_->removeItem(signalVectorRow);
}
2018-07-03 19:42:28 +02:00
bool removeRows(int row, int count, const QModelIndex &parent) override
2018-04-30 00:41:58 +02:00
{
if (count != 1) {
return false;
}
assert(row >= 0 && row < this->rows_.size());
2018-04-30 00:41:58 +02:00
int signalVectorRow = this->getVectorIndexFromModelIndex(row);
this->vector_->removeItem(signalVectorRow);
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
2018-08-06 21:17:03 +02:00
virtual void getRowFromItem(const TVectorItem &item,
std::vector<QStandardItem *> &row) = 0;
2018-08-06 21:17:03 +02:00
virtual int beforeInsert(const TVectorItem &item,
std::vector<QStandardItem *> &row,
int proposedIndex)
{
return proposedIndex;
}
2018-08-06 21:17:03 +02:00
virtual void afterRemoved(const TVectorItem &item,
std::vector<QStandardItem *> &row, int index)
{
}
2018-08-06 21:17:03 +02:00
virtual void customRowSetData(const std::vector<QStandardItem *> &row,
int column, const QVariant &value, int role,
int rowIndex)
{
}
void insertCustomRow(std::vector<QStandardItem *> row, int index)
{
assert(index >= 0 && index <= this->rows_.size());
this->beginInsertRows(QModelIndex(), index, index);
2018-08-06 21:17:03 +02:00
this->rows_.insert(this->rows_.begin() + index,
Row(std::move(row), true));
this->endInsertRows();
}
2018-05-28 08:34:54 +02:00
void removeCustomRow(int index)
{
assert(index >= 0 && index <= this->rows_.size());
assert(this->rows_[index].isCustomRow);
2018-05-28 08:34:54 +02:00
this->beginRemoveRows(QModelIndex(), index, index);
this->rows_.erase(this->rows_.begin() + index);
2018-05-28 08:34:54 +02:00
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)
{
}
};
private:
std::vector<QMap<int, QVariant>> headerData_;
BaseSignalVector<TVectorItem> *vector_;
std::vector<Row> rows_;
2018-04-30 00:41:58 +02:00
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 chatterino