// SPDX-License-Identifier: MIT #include "dbc_tree_model.h" #include #include #include #include namespace { QVariantList ToVariantList(const ValueDescriptions &values) { QVariantList descriptions; for (const ValueDescriptions::value_type &entry : values) { QVariantMap pair; if (const std::int64_t *number = std::get_if(&entry.first)) pair.insert("value", QVariant::fromValue(static_cast(*number))); else pair.insert("value", QVariant::fromValue( static_cast(std::get(entry.first)))); pair.insert("description", QString::fromStdString(entry.second)); descriptions.append(pair); } return descriptions; } } // namespace DbcTreeModel::DbcTreeModel(QObject *parent) : QAbstractItemModel(parent) {} void DbcTreeModel::SetTree(std::unique_ptr root) { if (root && (root->GetType() != NodeType::Root || root->GetParent())) throw std::invalid_argument("Model requires a detached root node"); beginResetModel(); m_root = std::move(root); endResetModel(); } const TreeNode *DbcTreeModel::Node(const QModelIndex &index) const { if (!index.isValid() || index.model() != this || index.column() < 0 || index.column() >= ColumnCount || index.row() < 0) return nullptr; return static_cast(index.internalPointer()); } QModelIndex DbcTreeModel::index(int row, int column, const QModelIndex &parentIndex) const { if (row < 0 || column < 0 || column >= ColumnCount || (parentIndex.isValid() && parentIndex.column() != 0)) return {}; const TreeNode *owner = parentIndex.isValid() ? Node(parentIndex) : m_root.get(); if (!owner) return {}; const TreeNode *child = owner->GetChild(static_cast(row)); return child ? createIndex(row, column, const_cast(child)) : QModelIndex(); } QModelIndex DbcTreeModel::parent(const QModelIndex &childIndex) const { const TreeNode *child = Node(childIndex); const TreeNode *owner = child ? child->GetParent() : nullptr; if (!owner || owner == m_root.get()) return {}; return createIndex(static_cast(owner->GetRow()), 0, const_cast(owner)); } int DbcTreeModel::rowCount(const QModelIndex &parentIndex) const { if (parentIndex.isValid() && parentIndex.column() != 0) return 0; const TreeNode *owner = parentIndex.isValid() ? Node(parentIndex) : m_root.get(); if (!owner) return 0; return static_cast(qMin(owner->GetChildCount(), static_cast(std::numeric_limits::max()))); } int DbcTreeModel::columnCount(const QModelIndex &parentIndex) const { return parentIndex.isValid() && !Node(parentIndex) ? 0 : ColumnCount; } QVariant DbcTreeModel::data(const QModelIndex &item, int role) const { const TreeNode *node = Node(item); if (!node) return {}; const FrameInfo *frame = node->GetFrame(); const SignalInfo *signal = node->GetSignal(); QString comment; if (frame != nullptr) comment = QString::fromStdString(frame->comment); else if (signal != nullptr) comment = QString::fromStdString(signal->comment); if (role == Qt::ToolTipRole || role == CommentRole) return comment; if (role == NodeTypeRole) return static_cast(node->GetType()); if (role == ValueDescriptionsRole && signal) { return ToVariantList(signal->valueDescriptions); } if (frame) { if (role == CanIdRole) return frame->canId; if (role == PayloadLengthRole) return static_cast(frame->dlc); if (role == ExtendedRole) return frame->isExtended; } if (signal) { if (role == StartBitRole) return signal->startBit; if (role == BitLengthRole) return signal->length; if (role == LittleEndianRole) return signal->isLittleEndian; if (role == SignedRole) return signal->isSigned; if (role == UnitRole) return QString::fromStdString(signal->unit); } if (role != Qt::DisplayRole) return {}; switch (item.column()) { case Name: return QString::fromStdString(node->GetName()); case CanId: return frame ? QVariant(QString("0x%1").arg(frame->canId, 0, 16).toUpper()) : QVariant(); case PayloadLength: return frame ? QVariant(static_cast(frame->dlc)) : QVariant(); case StartBit: return signal ? QVariant(signal->startBit) : QVariant(); case BitLength: return signal ? QVariant(signal->length) : QVariant(); case ByteOrder: return signal ? QVariant(signal->isLittleEndian ? "Intel" : "Motorola") : QVariant(); case Signed: return signal ? QVariant(signal->isSigned ? "Signed" : "Unsigned") : QVariant(); case Unit: return signal ? QVariant(QString::fromStdString(signal->unit)) : QVariant(); case Comment: return comment; default: return {}; } } QVariant DbcTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { static const char *const headers[] = {"Name", "CAN ID", "Payload (bytes)", "Start bit", "Length (bits)", "Byte order", "Signedness", "Unit", "Comment"}; if (orientation != Qt::Horizontal || role != Qt::DisplayRole || section < 0 || section >= ColumnCount) return {}; return QString::fromLatin1(headers[section]); } Qt::ItemFlags DbcTreeModel::flags(const QModelIndex &item) const { return Node(item) ? Qt::ItemIsEnabled | Qt::ItemIsSelectable : Qt::NoItemFlags; }