Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+950
View File
@@ -0,0 +1,950 @@
/**
* @RDE_HEADER@
*
* Tree model for the TopicSortModel
*
* $Id$
* $HeadURL$
* $Author$
*/
#include "topic_tree_model.h"
#include <QBuffer>
#include <QDataStream>
#include <QDateTime>
#include <QImage>
#include <QMimeData>
#include <QPainter>
#include <QPolygonF>
#include <QSet>
#include <QTransform>
#include <rti/core/xtypes/DynamicDataMemberInfo.hpp>
#include <dds/core/xtypes/EnumType.hpp>
#include <LtsCoordTrans/lts_coord_trans.h>
// 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<dds::core::xtypes::DynamicData>(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<IndexData *>(idx.internalPointer());
delete id;
}
m_pathInformations.clear();
}
std::shared_ptr<dds::core::xtypes::DynamicData> TopicDataModel::parentFromPath(int pathIdx, const QVector<int> &path,
const std::shared_ptr<dds::core::xtypes::DynamicData> &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<dds::core::xtypes::DynamicData>();
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<dds::core::xtypes::DynamicData>();
}
std::shared_ptr<dds::core::xtypes::DynamicData> child;
child.reset(new dds::core::xtypes::DynamicData(ddParent->value<dds::core::xtypes::DynamicData>(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<dds::core::xtypes::DynamicData>();
}
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<double>("lon");
const double lat = data.value<double>("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<double>("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<long long>("relativeTime");
ret.append( QString(QStringLiteral(" ; relTime=%1")).arg(relativeTime) );
} else if (data.type().name() == "Core::GeoPositionAbsoluteTime")
{
const long long absoluteTime = data.value<long long>("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<IndexData *>(index.internalPointer());
std::shared_ptr<dds::core::xtypes::DynamicData> 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<const dds::core::xtypes::SequenceType &>(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<const dds::core::xtypes::StructType &>(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("<not set>");
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<char>(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<DDS_Wchar>(row));
break;
case dds::core::xtypes::TypeKind::UINT_8_TYPE:
v = QVariant::fromValue(ddParent->value<uint8_t>(row)).toUInt();
break;
case dds::core::xtypes::TypeKind::INT_16_TYPE:
v = QVariant::fromValue(ddParent->value<int16_t>(row));
break;
case dds::core::xtypes::TypeKind::UINT_16_TYPE:
v = QVariant::fromValue(ddParent->value<uint16_t>(row));
break;
case dds::core::xtypes::TypeKind::INT_32_TYPE:
v = QVariant::fromValue(ddParent->value<int32_t>(row));
break;
case dds::core::xtypes::TypeKind::UINT_32_TYPE:
v = QVariant::fromValue(ddParent->value<uint32_t>(row));
break;
case dds::core::xtypes::TypeKind::INT_64_TYPE:
v = QVariant::fromValue(qint64(ddParent->value<int64_t>(row)));
break;
case dds::core::xtypes::TypeKind::UINT_64_TYPE:
v = QVariant::fromValue(quint64(ddParent->value<uint64_t>(row)));
break;
case dds::core::xtypes::TypeKind::FLOAT_32_TYPE:
v = QString::number(ddParent->value<float>(row), 'f', 4);
break;
case dds::core::xtypes::TypeKind::FLOAT_64_TYPE:
v = QString::number(ddParent->value<double>(row), 'f', 8);
break;
case dds::core::xtypes::TypeKind::BOOLEAN_TYPE:
v = ddParent->value<bool>(row) ? tr("true") : tr("false");
break;
case dds::core::xtypes::TypeKind::STRING_TYPE:
{
const dds::core::string tmp(ddParent->value<dds::core::string>(row));
v = QString::fromUtf8(tmp.c_str(), tmp.size());
break;
}
case dds::core::xtypes::TypeKind::STRUCTURE_TYPE:
{
QString ret = positionFromStruct(ddParent->value<dds::core::xtypes::DynamicData>(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<dds::core::xtypes::DynamicData>(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<const dds::core::xtypes::StructType &>(ddParent->type());
const int ordinal = ddParent->value<int32_t>(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<const dds::core::xtypes::EnumType &>(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<dds::core::xtypes::DynamicData>(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<dds::core::xtypes::DynamicData>(row);
QPolygonF poly;
poly.reserve(childStructData.member_count());
for (unsigned int i = 1; i <= childStructData.member_count(); ++i)
{
const auto data = childStructData.value<dds::core::xtypes::DynamicData>(i);
poly += QPointF(data.value<double>("lon"), data.value<double>("lat"));
}
QImage img = imageFromForm(poly, 64);
QBuffer buf;
buf.open(QIODevice::ReadWrite);
img.save(&buf, "PNG");
return QStringLiteral("<img src='data:image/png;base64,%1'>").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<uint8_t>(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<int16_t>(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<uint16_t>(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<int32_t>(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<uint32_t>(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<int64_t>(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<uint64_t>(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<const IndexData *>(parent.internalPointer());
std::shared_ptr<dds::core::xtypes::DynamicData> 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<dds::core::xtypes::DynamicData>(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<IndexData *>(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<const IndexData *>(child.internalPointer());
return id->parent;
}
dds::core::xtypes::TypeKind TopicDataModel::childTypeKind(const std::shared_ptr<dds::core::xtypes::DynamicData> &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<const dds::core::xtypes::CollectionType &>(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<const dds::core::xtypes::StructType &>(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<TreeModel *>(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<TreeItem *>(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<TreeItem *>(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<TreeItem *>(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<TreeItem *>(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<TreeItem *>(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<TreeItem *>(idx.internalPointer());
if (!item)
return TopicDescriptionEx();
return item->data();
}