#include "tablemodel.h" #include "../formats/jsonparser.h" #include "commands/edititemcommand.h" #include "commands/insertrowscommand.h" #include "commands/removerowscommand.h" #include "metadata.h" #include "modelitem.h" #include #include #include QByteArray TableModel::generateExampleItems() { QJsonDocument doc = QJsonDocument(); QJsonObject rootObject; QJsonArray array; for (int row = 0; row < 5; ++row) { QJsonObject itemObject; // itemObject.insert("uuid", m_uuid.toString()); // itemObject.insert("entryDateUTC", m_entryDateUTC.toString(Qt::ISODate)); itemObject.insert(ROLE_NAMES.value(NameRole), QString("Item %1").arg(row)); itemObject.insert(ROLE_NAMES.value(DescriptionRole), QString("This is item %1").arg(row)); itemObject.insert(ROLE_NAMES.value(InfoRole), QString("Info of item %1").arg(row)); itemObject.insert(ROLE_NAMES.value(AmountRole), row); itemObject.insert(ROLE_NAMES.value(FactorRole), row * 1.1); array.append(itemObject); } rootObject.insert("items", array); doc.setObject(rootObject); return doc.toJson(); } TableModel::TableModel(QUndoStack* undoStack, QObject* parent) : QAbstractTableModel{parent} , m_undoStack(undoStack) {} Qt::ItemFlags TableModel::flags(const QModelIndex& index) const { return Qt::ItemIsEditable | QAbstractTableModel::flags(index); } QHash TableModel::roleNames() const { return ROLE_NAMES; } int TableModel::rowCount(const QModelIndex& parent) const { Q_UNUSED(parent); return m_items.size(); } int TableModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return ROLE_NAMES.size(); } QVariant TableModel::data(const QModelIndex& index, int role) const { const int row = index.row(); const int column = index.column(); if (!index.isValid()) { return QVariant(); } if (row >= rowCount(QModelIndex()) || column >= columnCount(QModelIndex())) { return QVariant(); } int roleForColumn = getRoleForColumn(column); switch (role) { case Qt::DisplayRole: case Qt::EditRole: return m_items.at(row)->data(roleForColumn); case NameRole: case DescriptionRole: case InfoRole: case AmountRole: case FactorRole: return m_items.at(row)->data(role); } return QVariant(); } QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole) { if (orientation == Qt::Horizontal) { const int columnRole = getRoleForColumn(section); const QString headerName = ROLE_NAMES.value(columnRole); return QString("%1").arg(headerName); } else { return QString("%1").arg(section); } } return QVariant(); } bool TableModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (role == Qt::EditRole && checkIndex(index)) { const int column = index.column(); const int roleForColumn = getRoleForColumn(column); return setItemData(index, {{roleForColumn, value}}); } return false; } bool TableModel::setItemData(const QModelIndex& index, const QMap& roles) { if (!checkIndex(index)) { return false; } // if (isRoleReadOnly(roleForColumn)) { // return false; // } QMap changedValues = onlyChangedValues(index, roles); if (changedValues.size() > 0) { EditItemCommand* editCommand = new EditItemCommand(this, index, changedValues); m_undoStack->push(editCommand); return true; } return false; } QJsonDocument TableModel::getAllItemsAsJsonDoc() const { QJsonDocument doc = QJsonDocument(); QJsonObject rootObject; QJsonArray array; foreach (shared_ptr item, m_items) { QJsonObject itemObject = item->toJsonObject(); array.append(itemObject); } rootObject.insert("items", array); doc.setObject(rootObject); return doc; } bool TableModel::removeRows(int firstRow, int nRows, const QModelIndex& parentIndex) { if (parentIndex != QModelIndex()) { qWarning() << "Removing of child rows is not supported yet!"; return false; } const int lastRow = firstRow + nRows - 1; if (firstRow < 0 || lastRow >= m_items.size()) { qWarning() << "Trying to remove rows is out of bounds!"; return false; } RemoveRowsCommand* removeCommand = new RemoveRowsCommand(this, firstRow, nRows); m_undoStack->push(removeCommand); return true; } void TableModel::appendItems(const QByteArray& jsonDoc) { insertItems(-1, jsonDoc, QModelIndex()); } void TableModel::insertItems(int startPosition, const QByteArray& jsonDoc, const QModelIndex& parentIndex) { qInfo() << "Inserting item(s) into model..."; if (parentIndex != QModelIndex()) { qWarning() << "Using invalid parent index (no child support for now)"; } if (startPosition == -1 || startPosition > m_items.size()) { /// Appending item(s) startPosition = m_items.size(); } QList> valueList = JsonParser::toItemValuesList(jsonDoc, "items"); InsertRowsCommand* insertCommand = new InsertRowsCommand(this, startPosition, valueList); m_undoStack->push(insertCommand); } void TableModel::execInsertItems(const int firstRow, const QList> valueList) { const int nRows = valueList.size(); qDebug() << "Inserting" << nRows << "items..."; const int lastRow = firstRow + nRows - 1; beginInsertRows(QModelIndex(), firstRow, lastRow); for (int row = 0; row < nRows; ++row) { const int rowPosition = firstRow + row; shared_ptr item = make_unique(valueList.at(row)); m_items.insert(rowPosition, std::move(item)); } endInsertRows(); } void TableModel::execRemoveItems(const int firstRow, const int nRows) { const int lastRow = firstRow + nRows - 1; beginRemoveRows(QModelIndex(), firstRow, lastRow); m_items.remove(firstRow, nRows); endRemoveRows(); } void TableModel::execEditItemData(const int row, const QMap& changedValues) { shared_ptr item = m_items.at(row); bool isDataChanged = item->setItemData(changedValues); if (isDataChanged) { /// FIXME due to the mapping from roles to the DisplayRole of different columns the complete row /// is getting notified about (potential) data changes; dataChanged should be called only for /// the affected columns const QModelIndex firstIndex = this->index(row, 0); const QModelIndex lastIndex = this->index(row, ROLE_NAMES.size() - 1); QList roles = changedValues.keys(); roles.insert(0, Qt::DisplayRole); emit dataChanged(firstIndex, lastIndex, roles.toVector()); } } QMap TableModel::onlyChangedValues(const QModelIndex& index, const QMap& roleValueMap) const { QMap result; QMap::const_iterator i; for (i = roleValueMap.constBegin(); i != roleValueMap.constEnd(); ++i) { const int role = i.key(); const QVariant newValue = i.value(); const QVariant oldValue = index.data(role); // TODO check if role is a editable role? if (oldValue != newValue) { bool emptyValueIsEqualToZero = isEmptyValueEqualToZero(role); if (emptyValueIsEqualToZero && oldValue == QVariant() && newValue == 0) { qDebug() << "oldValue:" << oldValue << "& newValue:" << newValue << "mean the same. Ignoring..."; continue; } qDebug() << "oldValue:" << oldValue << "!= newValue:" << newValue; result.insert(role, newValue); } else { qInfo() << "oldValue is already the same as newValue:" << oldValue << "-> ignoring..."; } } return result; } bool TableModel::isEmptyValueEqualToZero(const int role) const { const QString roleName = ROLE_NAMES.value(role); if (intColumns.contains(roleName)) { return true; } return false; }