/** * @RDE_HEADER@ * * Tree model for the TopicSortModel * * $Id$ * $HeadURL$ * $Author$ */ #include "topic_tree_model.h" #include #include #include #include #include #include #include #include #include #include #include #include // taken from AreaEffectSimulation::Icons::imageFromPolygon() QImage imageFromForm(const QPolygonF &poly, const int width) { if (poly.isEmpty()) return QImage(); QPolygonF polyInUtmCoords; polyInUtmCoords.reserve(poly.size()); int zone = LtsCoordTrans::UtmCoordinate::fromGeographic(poly.first()).zone; for (const QPointF &point : poly) { LtsCoordTrans::UtmCoordinate coord = LtsCoordTrans::UtmCoordinate::fromGeographic(point, zone); polyInUtmCoords << QPointF(coord.x, coord.y); } const QPolygonF bounding = polyInUtmCoords.boundingRect(); // center to center / Todo:: Does it always work? const QTransform mTrans(QTransform::fromTranslate(-bounding.boundingRect().bottomLeft().x(), -bounding.boundingRect().bottomLeft().y())); qreal w = bounding.boundingRect().width(); qreal h = bounding.boundingRect().height(); double scale = w > h ? width / w : width / h; w *= scale; h *= scale; scale *= 0.96; // Leave 4% border (i.e. 2% each side) const QTransform mScale(QTransform::fromScale(scale, -scale)); const QTransform mTransSmall(QTransform::fromTranslate(0.02 * width + (width - w) / 2, 0.02 * width + (width - h) / 2)); QImage img(width, width, QImage::Format_ARGB32); img.fill(0); QPainter p(&img); p.fillRect(img.rect(), Qt::black); QPolygonF mPoly = mTrans.map(polyInUtmCoords); mPoly = mScale.map(mPoly); mPoly = mTransSmall.map(mPoly); p.setBrush(QBrush(Qt::red, Qt::SolidPattern)); p.setPen(QPen(p.brush(), 2)); p.drawPolygon(mPoly); p.end(); return img; } TopicDataModel::TopicDataModel(QObject *parent) : QAbstractItemModel(parent) , m_data(nullptr) , m_logger(DdsView::getLogger("TopicDataModel")) {} TopicDataModel::~TopicDataModel() { cleanupModelIndexes(); } void TopicDataModel::setTopicData(const dds::core::xtypes::DynamicData &data) { beginResetModel(); cleanupModelIndexes(); m_data = std::make_shared(data); endResetModel(); } void TopicDataModel::reset() { beginResetModel(); cleanupModelIndexes(); m_data.reset(); endResetModel(); } void TopicDataModel::cleanupModelIndexes() { for (const QModelIndex &idx : qAsConst(m_pathInformations)) { IndexData *id = static_cast(idx.internalPointer()); delete id; } m_pathInformations.clear(); } std::shared_ptr TopicDataModel::parentFromPath(int pathIdx, const QVector &path, const std::shared_ptr &ddParent) const { if (m_logger.isDebugEnabled()) { QStringList sl; sl.reserve(path.size()); for (int i : path) sl += QString::number(i); QString s = sl.join(QLatin1Char(',')); DEBUG_LOG(m_logger) << "parentFromPath: " << s << " - " << pathIdx; } if (pathIdx == path.size() - 1) return ddParent; const int row = path.at(pathIdx); const int memberCount = ddParent->member_count(); if (row < 0 || row >= memberCount) return std::shared_ptr(); DEBUG_LOG(m_logger) << "mfp Trying to get member at idx " << row + 1; try { const rti::core::xtypes::DynamicDataMemberInfo mi = ddParent->member_info(row + 1); DEBUG_LOG(m_logger) << "Member name is " << mi.member_name().c_str(); if (mi.member_kind() != dds::core::xtypes::TypeKind::STRUCTURE_TYPE && mi.member_kind() != dds::core::xtypes::TypeKind::SEQUENCE_TYPE && mi.member_kind() != dds::core::xtypes::TypeKind::ARRAY_TYPE) { WARNING_LOG(m_logger) << "Member is not sequence/structure... something is wrong"; return std::shared_ptr(); } std::shared_ptr child; child.reset(new dds::core::xtypes::DynamicData(ddParent->value(row + 1))); return parentFromPath(pathIdx + 1, path, child); } catch (...) { ERROR_LOG(m_logger) << "Error trying to access child at " << row + 1; } return std::shared_ptr(); } QString TopicDataModel::positionFromStruct(const dds::core::xtypes::DynamicData &data) { QString ret; if (data.type().name() == "Core::GeoPosition2D" || data.type().name() == "Core::GeoPosition" || data.type().name() == "Core::GeoPositionRelativeTime" || data.type().name() == "Core::GeoPositionAbsoluteTime") { const double lon = data.value("lon"); const double lat = data.value("lat"); LtsCoordTrans::UtmCoordinate coord(LtsCoordTrans::UtmCoordinate::fromGeographic(lon, lat)); ret = coord.mgrs(); if (ret.isEmpty()) ret = tr("invalid pos"); } if (data.type().name() == "Core::GeoPosition" || data.type().name() == "Core::GeoPositionRelativeTime" || data.type().name() == "Core::GeoPositionAbsoluteTime") { const double z = data.value("z"); // ret.append( QString(QStringLiteral(" ; z=%1")).arg( QString::number(z, 'f', 2) ) ); ret.append( QString(QStringLiteral(" ; z=%1")).arg( z, 9, 'f', 2, QLatin1Char(' ')) ); } if (data.type().name() == "Core::GeoPositionRelativeTime") { const long long relativeTime = data.value("relativeTime"); ret.append( QString(QStringLiteral(" ; relTime=%1")).arg(relativeTime) ); } else if (data.type().name() == "Core::GeoPositionAbsoluteTime") { const long long absoluteTime = data.value("absoluteTime"); ret.append( QString(QStringLiteral(" ; absTime=%1")).arg(absoluteTime) ); } return ret; } QVariant TopicDataModel::data(const QModelIndex &index, int role) const { try { return dataInternal(index, role); } catch (const std::exception &ex) { ERROR_LOG(m_logger) << "Error retrieving data for " << index.row() << "/" << index.column() << ": " << ex.what(); } return QVariant(); } bool findTypeFromStructType(const dds::core::xtypes::StructType &parentIn, dds::core::xtypes::StructType &parentOut, size_t &row) { if (parentIn.has_parent()) { if (findTypeFromStructType(parentIn.parent(), parentOut, row)) return true; } if (parentIn.member_count() > row) { parentOut = parentIn; return true; } row -= parentIn.member_count(); return false; } const dds::core::xtypes::Member &TopicDataModel::memberFromType(const dds::core::xtypes::StructType &parent, dds::core::xtypes::StructType &stCopy, int row) { size_t sRow = row; findTypeFromStructType(parent, stCopy, sRow); return stCopy.member(sRow); } QVariant TopicDataModel::dataInternal(const QModelIndex &index, int role) const { if (!m_data) return QVariant(); if (role != Qt::DisplayRole && role != Qt::ToolTipRole) return QVariant(); if (index.row() < 0 || index.column() < 0 || index.column() >= columnCount(index)) return QVariant(); IndexData *id = static_cast(index.internalPointer()); std::shared_ptr ddParent = parentFromPath(0, id->path, m_data); if (!ddParent) return QVariant(); const int memberCount = ddParent->member_count(); if (index.row() >= memberCount) return QVariant(); const int row = id->path.last() + 1; if (role == Qt::DisplayRole) { QVariant v = id->displayRole[index.column()]; if (v.isValid()) return v; switch (index.column()) { case 0: { if (ddParent->type_kind() == dds::core::xtypes::TypeKind::SEQUENCE_TYPE) { const dds::core::xtypes::SequenceType dtParent = static_cast(ddParent->type()); const dds::core::xtypes::DynamicType &childType = dtParent.content_type(); if (childType.kind() == dds::core::xtypes::TypeKind::STRUCTURE_TYPE) v = QStringLiteral("%1 [%2]").arg(QString::fromStdString(childType.name())).arg(row - 1); else { // PODs QString typeName; switch (childType.kind().underlying()) { case dds::core::xtypes::TypeKind::CHAR_8_TYPE: typeName = QStringLiteral("char"); break; #if RTI_DDS_VERSION_MAJOR < 6 case dds::core::xtypes::TypeKind::CHAR_32_TYPE: #else case dds::core::xtypes::TypeKind::CHAR_16_TYPE: #endif typeName = QStringLiteral("wchar_t"); break; case dds::core::xtypes::TypeKind::UINT_8_TYPE: typeName = QStringLiteral("uint8_t"); break; case dds::core::xtypes::TypeKind::INT_16_TYPE: typeName = QStringLiteral("int16_t"); break; case dds::core::xtypes::TypeKind::UINT_16_TYPE: typeName = QStringLiteral("uint16_t"); break; case dds::core::xtypes::TypeKind::INT_32_TYPE: typeName = QStringLiteral("int32_t"); break; case dds::core::xtypes::TypeKind::UINT_32_TYPE: typeName = QStringLiteral("uint32_t"); break; case dds::core::xtypes::TypeKind::INT_64_TYPE: typeName = QStringLiteral("int64_t"); break; case dds::core::xtypes::TypeKind::UINT_64_TYPE: typeName = QStringLiteral("uint64_t"); break; case dds::core::xtypes::TypeKind::FLOAT_32_TYPE: typeName = QStringLiteral("float"); break; case dds::core::xtypes::TypeKind::FLOAT_64_TYPE: typeName = QStringLiteral("double"); break; case dds::core::xtypes::TypeKind::BOOLEAN_TYPE: typeName = QStringLiteral("bool"); break; case dds::core::xtypes::TypeKind::STRING_TYPE: typeName = QStringLiteral("string"); break; case dds::core::xtypes::TypeKind::ENUMERATION_TYPE: typeName = QStringLiteral("enum"); break; default: typeName = QStringLiteral("0x") + QString::number(childType.kind().underlying(), 16); break; } v = QStringLiteral("%1 [%2]").arg(typeName).arg(row); } } else { const dds::core::xtypes::StructType &dtParent = static_cast(ddParent->type()); dds::core::xtypes::StructType dtParentCopy = dtParent; const dds::core::xtypes::Member &child = memberFromType(dtParent, dtParentCopy, row - 1); v = QString::fromStdString(child.name()); } break; } case 1: { if (!ddParent->member_exists(row)) return tr(""); const rti::core::xtypes::DynamicDataMemberInfo mi = ddParent->member_info(row); switch (mi.member_kind().underlying()) { case dds::core::xtypes::TypeKind::CHAR_8_TYPE: v = QChar::fromLatin1(ddParent->value(row)); break; #if RTI_DDS_VERSION_MAJOR < 6 case dds::core::xtypes::TypeKind::CHAR_32_TYPE: #else case dds::core::xtypes::TypeKind::CHAR_16_TYPE: #endif v = QChar(ddParent->value(row)); break; case dds::core::xtypes::TypeKind::UINT_8_TYPE: v = QVariant::fromValue(ddParent->value(row)).toUInt(); break; case dds::core::xtypes::TypeKind::INT_16_TYPE: v = QVariant::fromValue(ddParent->value(row)); break; case dds::core::xtypes::TypeKind::UINT_16_TYPE: v = QVariant::fromValue(ddParent->value(row)); break; case dds::core::xtypes::TypeKind::INT_32_TYPE: v = QVariant::fromValue(ddParent->value(row)); break; case dds::core::xtypes::TypeKind::UINT_32_TYPE: v = QVariant::fromValue(ddParent->value(row)); break; case dds::core::xtypes::TypeKind::INT_64_TYPE: v = QVariant::fromValue(qint64(ddParent->value(row))); break; case dds::core::xtypes::TypeKind::UINT_64_TYPE: v = QVariant::fromValue(quint64(ddParent->value(row))); break; case dds::core::xtypes::TypeKind::FLOAT_32_TYPE: v = QString::number(ddParent->value(row), 'f', 4); break; case dds::core::xtypes::TypeKind::FLOAT_64_TYPE: v = QString::number(ddParent->value(row), 'f', 8); break; case dds::core::xtypes::TypeKind::BOOLEAN_TYPE: v = ddParent->value(row) ? tr("true") : tr("false"); break; case dds::core::xtypes::TypeKind::STRING_TYPE: { const dds::core::string tmp(ddParent->value(row)); v = QString::fromUtf8(tmp.c_str(), tmp.size()); break; } case dds::core::xtypes::TypeKind::STRUCTURE_TYPE: { QString ret = positionFromStruct(ddParent->value(row)); if (!ret.isEmpty()) v = ret; else v = tr("[struct]"); break; } case dds::core::xtypes::TypeKind::SEQUENCE_TYPE: case dds::core::xtypes::TypeKind::ARRAY_TYPE: { const dds::core::xtypes::DynamicData childStructData = ddParent->value(row); v = tr("cnt: %1").arg(childStructData.member_count()); break; } case dds::core::xtypes::TypeKind::ENUMERATION_TYPE: { // types are not plain - we have to search for the member in the type-tree dds::core::xtypes::StructType structTypeParent = static_cast(ddParent->type()); const int ordinal = ddParent->value(row); unsigned int rowInType = structTypeParent.find_member_by_name(mi.member_name()); while (rowInType == dds::core::xtypes::EnumType::INVALID_INDEX) { if (!structTypeParent.has_parent()) { WARNING_LOG(m_logger) << "Could not retrieve enum value from Struct Type"; return QStringLiteral("<%1>").arg(ordinal); } structTypeParent = structTypeParent.parent(); rowInType = structTypeParent.find_member_by_name(mi.member_name()); } const dds::core::xtypes::Member &enumMember = structTypeParent.member(rowInType); // index in struct != index in data! const dds::core::xtypes::EnumType &inner = static_cast(enumMember.type()); v = QStringLiteral("%1 (%2)").arg(QString::fromStdString(inner.name())).arg(ordinal); for (size_t i = 0; i < inner.member_count(); ++i) { const auto &member = inner.member(i); if (member.ordinal() == ordinal) { v = QStringLiteral("%1 (%2)").arg(QString::fromStdString(member.name())).arg(ordinal); break; } } break; } default: v = QString(); } break; } case 2: { const dds::core::xtypes::TypeKind tk = childTypeKind(ddParent, row - 1); switch (tk.underlying()) { case dds::core::xtypes::TypeKind::STRUCTURE_TYPE: v = tr("struct"); break; case dds::core::xtypes::TypeKind::SEQUENCE_TYPE: v = tr("sequence"); break; case dds::core::xtypes::TypeKind::ARRAY_TYPE: v = tr("array"); break; default: v = QStringLiteral("-"); break; } break; } case 3: v = ddParent->member_exists(row) ? QStringLiteral("1") : QStringLiteral("0"); break; default: break; } id->displayRole[index.column()] = v; return v; } if (role == Qt::ToolTipRole) { if (!ddParent->member_exists(row)) return QVariant(); const rti::core::xtypes::DynamicDataMemberInfo mi = ddParent->member_info(row); if (mi.member_kind() == dds::core::xtypes::TypeKind::STRUCTURE_TYPE) { const QString ret = positionFromStruct(ddParent->value(row)); if (!ret.isEmpty()) return ret; } else if (mi.member_kind() == dds::core::xtypes::TypeKind::SEQUENCE_TYPE && mi.member_name() == "form") { // assume it is a form from aes with lat/lon values try { const dds::core::xtypes::DynamicData childStructData = ddParent->value(row); QPolygonF poly; poly.reserve(childStructData.member_count()); for (unsigned int i = 1; i <= childStructData.member_count(); ++i) { const auto data = childStructData.value(i); poly += QPointF(data.value("lon"), data.value("lat")); } QImage img = imageFromForm(poly, 64); QBuffer buf; buf.open(QIODevice::ReadWrite); img.save(&buf, "PNG"); return QStringLiteral("").arg(QString::fromLatin1(buf.data().toBase64())); } catch (...) { // maybe no aes item view } } else if (mi.member_kind() == dds::core::xtypes::TypeKind::UINT_8_TYPE) { const qint64 val = ddParent->value(row); return QStringLiteral("0x%1\n0b%2").arg(val, 2, 16, QLatin1Char('0')) .arg(val, 8, 2, QLatin1Char('0')); } else if (mi.member_kind() == dds::core::xtypes::TypeKind::INT_16_TYPE) { const qint64 val = ddParent->value(row); return QStringLiteral("0x%1").arg(val, 8, 16, QLatin1Char('0')); } else if (mi.member_kind() == dds::core::xtypes::TypeKind::UINT_16_TYPE) { const quint64 val = ddParent->value(row); return QStringLiteral("0x%1").arg(val, 8, 16, QLatin1Char('0')); } else if (mi.member_kind() == dds::core::xtypes::TypeKind::INT_32_TYPE) { const qint64 val = ddParent->value(row); return QStringLiteral("0x%1").arg(val, 8, 16, QLatin1Char('0')); } else if (mi.member_kind() == dds::core::xtypes::TypeKind::UINT_32_TYPE) { const quint64 val = ddParent->value(row); return QStringLiteral("0x%1").arg(val, 8, 16, QLatin1Char('0')); } else if (mi.member_kind() == dds::core::xtypes::TypeKind::INT_64_TYPE) { // we assume it's a timestamp when its > 946681200000 (> 01.01.2000) const qint64 val = ddParent->value(row); if (val > 946681200000) { const QDateTime dt(QDateTime::fromMSecsSinceEpoch(val).toUTC()); return dt.toString(QStringLiteral("dd.MM.yy - hh:mm:ss.zzz Z")); } return QStringLiteral("0x%1").arg(val, 8, 16, QLatin1Char('0')); } else if (mi.member_kind() == dds::core::xtypes::TypeKind::UINT_64_TYPE) { const quint64 val = ddParent->value(row); return QStringLiteral("0x%1").arg(val, 8, 16, QLatin1Char('0')); } } return QVariant(); } QVariant TopicDataModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QAbstractItemModel::headerData(section, orientation, role); switch (section) { case 0: return tr("Name"); case 1: return tr("Value"); case 2: return tr("Kind"); case 3: return tr("Exists"); default: break; } return QAbstractItemModel::headerData(section, orientation, role); } int TopicDataModel::rowCount(const QModelIndex &parent) const { if (!m_data) return 0; // root knot if (!parent.isValid()) return m_data->member_count(); const IndexData *id = static_cast(parent.internalPointer()); std::shared_ptr ddParent = parentFromPath(0, id->path, m_data); if (!ddParent) { ERROR_LOG(m_logger) << "rowCount - can not determine count!"; return 0; } try { const int row = id->path.last() + 1; const dds::core::xtypes::TypeKind tk = childTypeKind(ddParent, row - 1); if (tk == dds::core::xtypes::TypeKind::STRUCTURE_TYPE || tk == dds::core::xtypes::TypeKind::SEQUENCE_TYPE || tk == dds::core::xtypes::TypeKind::ARRAY_TYPE) { if (!ddParent->member_exists(row)) // optional, not set return 0; const dds::core::xtypes::DynamicData childStructData = ddParent->value(row); return childStructData.member_count(); } } catch (...) { ERROR_LOG(m_logger) << "Error trying to access child at " << parent.row() + 1; } return 0; } int TopicDataModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) return 4; } QModelIndex TopicDataModel::index(int row, int column, const QModelIndex &parent) const { const PathInformationKey pik({row, column, parent}); auto it = m_pathInformations.find(pik); if (it == m_pathInformations.end()) { IndexData *id = new IndexData; id->index = createIndex(row, column, id); if (parent.isValid()) id->path = static_cast(parent.internalPointer())->path; id->path.push_back(row); id->parent = parent; m_pathInformations.insert(pik, id->index); return id->index; } return it.value(); } QModelIndex TopicDataModel::parent(const QModelIndex &child) const { if (!child.isValid()) return QModelIndex(); const IndexData *id = static_cast(child.internalPointer()); return id->parent; } dds::core::xtypes::TypeKind TopicDataModel::childTypeKind(const std::shared_ptr &ddParent, int typeRow) { switch (ddParent->type_kind().underlying()) { case dds::core::xtypes::TypeKind::SEQUENCE_TYPE: case dds::core::xtypes::TypeKind::ARRAY_TYPE: { const dds::core::xtypes::CollectionType dtParent = static_cast(ddParent->type()); const dds::core::xtypes::DynamicType &childType = dtParent.content_type(); return childType.kind(); } case dds::core::xtypes::TypeKind::STRUCTURE_TYPE: { const dds::core::xtypes::StructType &dtParent = static_cast(ddParent->type()); dds::core::xtypes::StructType dtCopy(dtParent); const dds::core::xtypes::Member &child = memberFromType(dtParent, dtCopy, typeRow); const dds::core::xtypes::DynamicType &childType = child.type(); return childType.kind(); } default: break; } return dds::core::xtypes::TypeKind::NO_TYPE; } // // TopicSortModel // TreeItem *TopicSortModel::addData(const TopicDescriptionEx &data, TreeItem *parent) { return m_sourceModel->addData(data, parent); } TopicDescriptionEx TopicSortModel::getData(const QModelIndex &idx) const { return m_sourceModel->getData(mapToSource(idx)); } void TopicSortModel::setSourceModel(QAbstractItemModel *sourceModel) { m_sourceModel = qobject_cast(sourceModel); QSortFilterProxyModel::setSourceModel(sourceModel); } bool TopicSortModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const { if (source_left.column() != source_right.column()) return QSortFilterProxyModel::lessThan(source_left, source_right); TopicDescriptionEx descriptionLeft; TopicDescriptionEx descriptionRight; // const TopicDescriptionEx &tdL = m_sourceModel->data(source_left, Qt::DisplayRole); // Todo: Korrekte Role // const TopicDescriptionEx &tdR = m_sourceModel->data(source_right, Qt::DisplayRole); // s.o. descriptionLeft.serviceName = m_sourceModel->data(source_left, Qt::DisplayRole).toString(); // Todo: Korrekte Role descriptionRight.serviceName = m_sourceModel->data(source_right, Qt::DisplayRole).toString(); // s.o. const TopicDescriptionEx &tdL = descriptionLeft; const TopicDescriptionEx &tdR = descriptionRight; if (tdL.serviceName == tdR.serviceName) return tdL.name < tdR.name; return tdL.serviceName < tdR.serviceName; } // -------------------------------------------------------------------------------- // | TreeItem | // -------------------------------------------------------------------------------- // Constructor TreeItem::TreeItem(const TopicDescriptionEx &description, TreeItem *parent) : m_data(description) , m_parentItem(parent) {} //Destructor TreeItem::~TreeItem() { qDeleteAll(m_childItems); } void TreeItem::appendChild(TreeItem *child) { m_childItems.append(child); } // Getter TreeItem *TreeItem::child(int row) { if (row < 0 || row >= m_childItems.size()) return nullptr; return m_childItems.value(row); } int TreeItem::childCount() const { return m_childItems.count(); } QVariant TreeItem::itemData(int column) const { if (column == 0) { if (m_data.isEmpty()) return m_data.serviceName; return QString::fromStdString(m_data.name).split(QStringLiteral("::")).last(); } else if (column == 1) { if (m_data.isEmpty()) return QVariant(); return QString::fromStdString(m_data.typeName); } return QVariant(); } TreeItem *TreeItem::parentItem() { return m_parentItem; } int TreeItem::row() const { if (m_parentItem) return m_parentItem->m_childItems.indexOf(const_cast(this)); return 0; } // -------------------------------------------------------------------------------- // | TreeModel | // -------------------------------------------------------------------------------- // Constructor TreeModel::TreeModel(QObject *parent) : QAbstractItemModel(parent) , m_bWithTypeCode(false) { TopicDescriptionEx desc; desc.serviceName = QLatin1String("Title"); m_rootItem = new TreeItem(desc, nullptr); } // Destructor TreeModel::~TreeModel() { delete m_rootItem; } // Getter int TreeModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_bWithTypeCode ? 2 : 1; } QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const { QMimeData *md = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); for (const QModelIndex &idx : indexes) { if (idx.column() != 0) continue; const TopicDescriptionEx td = getData(idx); stream << td.name.c_str(); stream << QByteArray(td.typeName.c_str()); } md->setData(QLatin1String("Text/TopicName+TypeName"), encodedData); return md; } QStringList TreeModel::mimeTypes() const { return QStringList() << QLatin1String("Text/TopicName+TypeName"); } Qt::DropActions TreeModel::supportedDragActions() const { return Qt::CopyAction; } QVariant TreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role != Qt::DisplayRole) return QVariant(); TreeItem *item = static_cast(index.internalPointer()); return item->itemData(index.column()); } Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; Qt::ItemFlags f = QAbstractItemModel::flags(index); if (index.parent().isValid()) f |= Qt::ItemIsDragEnabled; else f &= ~Qt::ItemIsSelectable; return f; } QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) return m_rootItem->itemData(section); return QVariant(); } QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); TreeItem *parentItem; if (!parent.isValid()) parentItem = m_rootItem; else parentItem = static_cast(parent.internalPointer()); TreeItem *childItem = parentItem->child(row); if (childItem) return createIndex(row, column, childItem); else return QModelIndex(); } QModelIndex TreeModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); TreeItem *childItem = static_cast(index.internalPointer()); TreeItem *parentItem = childItem->parentItem(); if (!parentItem || parentItem == m_rootItem) return QModelIndex(); return createIndex(parentItem->row(), 0, parentItem); } int TreeModel::rowCount(const QModelIndex &parent) const { TreeItem *parentItem; if (parent.column() > 0) return 0; if (!parent.isValid()) parentItem = m_rootItem; else parentItem = static_cast(parent.internalPointer()); return parentItem->childCount(); } TreeItem *TreeModel::addData(const TopicDescriptionEx &data, TreeItem *parent) { if (!parent) { parent = m_rootItem; bool createSubKnot = true; for (int i = 0; i < m_rootItem->childCount(); ++i) { if (m_rootItem->child(i)->itemData(0).toString() == data.serviceName) { parent = m_rootItem->child(i); createSubKnot = false; // already there -> no need to create another one break; } } if (createSubKnot) { TopicDescriptionEx description; description.serviceName = data.serviceName; beginInsertRows(QModelIndex(), parent->childCount(), parent->childCount()); parent->appendChild(new TreeItem(description, parent)); endInsertRows(); parent = parent->child(parent->childCount() - 1); } } const QModelIndex parentIdx(createIndex(parent->row(), 0, parent)); beginInsertRows(parentIdx, parent->childCount(), parent->childCount()); TreeItem *ti(new TreeItem(data, parent)); parent->appendChild(ti); endInsertRows(); return ti; } TopicDescriptionEx TreeModel::getData(const QModelIndex &idx) const { TreeItem *item = static_cast(idx.internalPointer()); if (!item) return TopicDescriptionEx(); return item->data(); }