#pragma once #include "lts_base.h" #include #include #include #include #include namespace LtsWidgets { template class LtsAbstractTableModel : public QAbstractTableModel { public: LtsAbstractTableModel(const QVector &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 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 &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 HasId { template static std::true_type testSignature(const int &(CLS::*)() const); template static std::true_type testSignature(int (CLS::*)() const); template static decltype(testSignature(&CLS::id)) test(std::nullptr_t); template static std::false_type test(...); public: using type = decltype(test(nullptr)); static const bool value = type::value; }; template ::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::value, int>::type = 0> int getIdInternal(const F &data) const { return getId(data); } template class HasIsDeleted { template static std::true_type testSignature(const bool &(CLS::*)() const); template static std::true_type testSignature(bool (CLS::*)() const); template static decltype(testSignature(&CLS::isDeleted)) test(std::nullptr_t); template static std::false_type test(...); public: using type = decltype(test(nullptr)); static const bool value = type::value; }; template ::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::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 m_vecData; QVector m_vecColumns; }; }