287 lines
6.3 KiB
C++
Executable File
287 lines
6.3 KiB
C++
Executable File
|
|
|
|
|
|
#pragma once
|
|
|
|
#include "lts_base.h"
|
|
|
|
#include <LtsUtilsCore/data_modification_enum.h>
|
|
|
|
#include <QVector>
|
|
#include <QAbstractTableModel>
|
|
|
|
#include <memory>
|
|
#include <algorithm>
|
|
|
|
|
|
namespace LtsWidgets
|
|
{
|
|
|
|
template<class T>
|
|
class LtsAbstractTableModel : public QAbstractTableModel
|
|
{
|
|
public:
|
|
LtsAbstractTableModel(const QVector<QString> &header, QObject *parent)
|
|
: QAbstractTableModel(parent)
|
|
, m_vecColumns(header)
|
|
{}
|
|
|
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
|
|
{
|
|
if (role == Qt::DisplayRole && orientation == Qt::Horizontal && section>= 0 && section < m_vecColumns.size())
|
|
return m_vecColumns[section];
|
|
|
|
return QAbstractTableModel::headerData(section, orientation, role);
|
|
}
|
|
|
|
bool isEmpty(const QModelIndex &parent = QModelIndex())
|
|
{
|
|
return parent.isValid() ? true : m_vecData.isEmpty();
|
|
}
|
|
|
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override
|
|
{
|
|
return parent.isValid() ? 0 : m_vecData.size();
|
|
}
|
|
|
|
int columnCount(const QModelIndex &parent = QModelIndex()) const override
|
|
{
|
|
return parent.isValid() ? 0 : m_vecColumns.size();
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
beginResetModel();
|
|
m_vecData.clear();
|
|
endResetModel();
|
|
}
|
|
|
|
bool indexIsValid(const QModelIndex &idx) const
|
|
{
|
|
if (!idx.isValid())
|
|
return false;
|
|
if (idx.row() < 0 || idx.row() >= m_vecData.size())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
virtual bool removeRow(int row)
|
|
{
|
|
return removeRows(row, 1);
|
|
}
|
|
|
|
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
|
|
{
|
|
if (parent.isValid())
|
|
return false;
|
|
|
|
if (row < 0 || row >= m_vecData.size())
|
|
return false;
|
|
|
|
int range = std::min(row + count, m_vecData.size());
|
|
|
|
beginRemoveRows(QModelIndex(), row, range - 1);
|
|
m_vecData.erase(m_vecData.begin() + row, m_vecData.begin() + range);
|
|
endRemoveRows();
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual bool insertRow(const T &item)
|
|
{
|
|
if (isFiltered(item, DataModification::eAdded) || isDeletedInternal(item))
|
|
return false;
|
|
beginInsertRows(QModelIndex(), m_vecData.size(), m_vecData.size());
|
|
m_vecData.append(item);
|
|
endInsertRows();
|
|
return true;
|
|
}
|
|
|
|
template <typename CONT>
|
|
void insertRows(const CONT &items)
|
|
{
|
|
if (items.isEmpty())
|
|
return;
|
|
const int cntBefore = m_vecData.size();
|
|
for (const auto &item : items)
|
|
{
|
|
if (isFiltered(item, DataModification::eAdded) || isDeletedInternal(item))
|
|
continue;
|
|
m_vecData += item;
|
|
}
|
|
const int cntAfter = m_vecData.size();
|
|
if (cntBefore == cntAfter)
|
|
return;
|
|
beginInsertRows(QModelIndex(), cntBefore, cntAfter - 1);
|
|
endInsertRows();
|
|
}
|
|
|
|
void setData(const QVector<T> &vecData)
|
|
{
|
|
clear();
|
|
insertRows(vecData);
|
|
}
|
|
|
|
|
|
|
|
void update()
|
|
{
|
|
if (!m_vecData.isEmpty())
|
|
emit dataChanged(index(0, 0), index(m_vecData.size() - 1, columnCount() - 1));
|
|
}
|
|
|
|
T getRowData(int row) const
|
|
{
|
|
if (row < 0 || row >= m_vecData.size())
|
|
return T();
|
|
return m_vecData.at(row);
|
|
}
|
|
|
|
bool setRowData(int row, const T &data)
|
|
{
|
|
if (row < 0 || row >= m_vecData.size())
|
|
return false;
|
|
|
|
m_vecData[row] = data;
|
|
emit dataChanged(index(row, 0), index(row, columnCount() - 1));
|
|
return true;
|
|
}
|
|
|
|
QModelIndex findIndexForId(int id, int column = 0) const
|
|
{
|
|
const int row = findRowForId(id);
|
|
if (row < 0)
|
|
return QModelIndex();
|
|
return index(row, column);
|
|
}
|
|
|
|
int getId(const QModelIndex &mi) const
|
|
{
|
|
if (!indexIsValid(mi))
|
|
return -1;
|
|
return getIdInternal(m_vecData.at(mi.row()));
|
|
}
|
|
|
|
protected:
|
|
template <class C>
|
|
class HasId
|
|
{
|
|
template <class CLS>
|
|
static std::true_type testSignature(const int &(CLS::*)() const);
|
|
template <class CLS>
|
|
static std::true_type testSignature(int (CLS::*)() const);
|
|
|
|
template <class CLS>
|
|
static decltype(testSignature(&CLS::id)) test(std::nullptr_t);
|
|
|
|
template <class CLS>
|
|
static std::false_type test(...);
|
|
|
|
public:
|
|
using type = decltype(test<C>(nullptr));
|
|
static const bool value = type::value;
|
|
};
|
|
|
|
|
|
template <typename F = T, typename std::enable_if<HasId<F>::value, int>::type = 0>
|
|
int getIdInternal(const F &data) const
|
|
{
|
|
return data.id();
|
|
}
|
|
|
|
|
|
virtual int getId(const T &data) const
|
|
{
|
|
Q_ASSERT(false);
|
|
Q_UNUSED(data);
|
|
return -1;
|
|
}
|
|
|
|
template < typename F = T, typename std::enable_if < !HasId<F>::value, int>::type = 0>
|
|
int getIdInternal(const F &data) const
|
|
{
|
|
return getId(data);
|
|
}
|
|
|
|
template <class C>
|
|
class HasIsDeleted
|
|
{
|
|
template <class CLS>
|
|
static std::true_type testSignature(const bool &(CLS::*)() const);
|
|
template <class CLS>
|
|
static std::true_type testSignature(bool (CLS::*)() const);
|
|
|
|
template <class CLS>
|
|
static decltype(testSignature(&CLS::isDeleted)) test(std::nullptr_t);
|
|
|
|
template <class CLS>
|
|
static std::false_type test(...);
|
|
|
|
public:
|
|
using type = decltype(test<C>(nullptr));
|
|
static const bool value = type::value;
|
|
};
|
|
|
|
template <typename F = T, typename std::enable_if<HasIsDeleted<F>::value, int>::type = 0>
|
|
bool isDeletedInternal(const F &data) const
|
|
{
|
|
return data.isDeleted();
|
|
}
|
|
|
|
virtual int isDeleted(const T &data) const
|
|
{
|
|
Q_UNUSED(data);
|
|
return false;
|
|
}
|
|
template < typename F = T, typename std::enable_if < !HasIsDeleted<F>::value, int>::type = 0>
|
|
bool isDeletedInternal(const F &data) const
|
|
{
|
|
return isDeleted(data);
|
|
}
|
|
|
|
|
|
virtual bool isFiltered(const T &data, DataModification modType) const
|
|
{
|
|
Q_UNUSED(data);
|
|
Q_UNUSED(modType);
|
|
return false;
|
|
}
|
|
|
|
|
|
virtual bool onDataChanged(const T &data, DataModification modType)
|
|
{
|
|
if (isFiltered(data, modType))
|
|
return false;
|
|
|
|
const int id = getIdInternal(data);
|
|
const int row = findRowForId(id);
|
|
|
|
|
|
if (modType == DataModification::eRemoved || isDeletedInternal(data))
|
|
{
|
|
if (row >= 0)
|
|
return removeRow(row);
|
|
return false;
|
|
}
|
|
|
|
if (modType == DataModification::eAdded && row < 0)
|
|
return insertRow(data);
|
|
|
|
return setRowData(row, data);
|
|
}
|
|
|
|
int findRowForId(int id) const
|
|
{
|
|
for (int i = 0; i < m_vecData.size(); ++i)
|
|
{
|
|
if (getIdInternal(m_vecData.at(i)) == id)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
QVector<T> m_vecData;
|
|
QVector<QString> m_vecColumns;
|
|
};
|
|
}
|