/** * @RDE_HEADER@ * * TableModel for dynamic topics * * $Id$ * $HeadURL$ * $Author$ */ #include "samplemodel.h" #include #include #include #include "detailviewdlg.h" #include "nglsettings.h" #include "xmlhelper.h" // // SampleModel::SampleData // QByteArray SampleModel::SampleData::encodeForMime() const { QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); stream << QString::fromStdString(typeName) << reinterpret_cast(legatusData.data()) << topicName << description << pause; return encodedData; } SampleModel::SampleData SampleModel::SampleData::decodeFromMime(const QByteArray &encodedData) { SampleData ret; QDataStream stream(const_cast(&encodedData), QIODevice::ReadOnly); QString tn; quint64 ptr; dds::core::xtypes::DynamicData *dynDataPtr; stream >> tn >> ptr >> ret.topicName >> ret.description >> ret.pause; ret.typeName = tn.toStdString(); dynDataPtr = reinterpret_cast(ptr); ret.legatusData = QSharedPointer(new dds::core::xtypes::DynamicData(*dynDataPtr)); return ret; } // // SampleModel // SampleModel::SampleModel(QObject *parent) : QAbstractTableModel(parent) , m_logger(DdsView::getLogger("SampleModel")) {} SampleModel::~SampleModel() { for (const SampleData &sd : qAsConst(m_data)) delete sd.dialog; } int SampleModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_data.size(); } int SampleModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return Column::Maximum + 1; } QVariant SampleModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (index.column() >= columnCount(QModelIndex()) || index.row() >= rowCount(QModelIndex())) return QVariant(); const SampleData &data = m_data[index.row()]; if (role == Qt::ToolTipRole && index.column() == Column::TopicName) return QString::fromStdString(data.typeName); if (role != Qt::DisplayRole && role != Qt::EditRole) return QVariant(); if (data.typeName.empty()) return QVariant(); switch (index.column()) { case Column::TopicName: return data.topicName; case Column::Description: return data.description; case Column::TypeName: return QString::fromStdString(data.typeName); case Column::Pause: return data.pause; } return QVariant(); } bool SampleModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || role != Qt::EditRole) return false; if (index.column() >= columnCount(QModelIndex()) || index.row() >= rowCount(QModelIndex())) return false; SampleData &data = m_data[index.row()]; bool retVal = false; switch (index.column()) { // Non editable! case Column::TopicName: case Column::TypeName: break; case Column::Description: data.description = value.toString(); retVal = true; if (data.dialog) data.dialog->setWindowTitle(QString::fromLatin1("%1 (%2)").arg(data.description, data.topicName)); break; case Column::Pause: { int i = value.toInt(&retVal); if (retVal) data.pause = i; break; } } if (retVal) emit dataChanged(index, index); return retVal; } bool SampleModel::updateRow(const QModelIndex &index, const QSharedPointer &data, const QString &topicName) { int row = index.row(); if (row < 0 || row >= rowCount(QModelIndex())) return false; SampleData &sample = m_data[row]; if (sample.legatusData->type() != data->type()) return false; sample.legatusData = data; sample.topicName = topicName; return true; } Qt::DropActions SampleModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions SampleModel::supportedDragActions() const { return Qt::MoveAction | Qt::CopyAction; } QVariant SampleModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case Column::TopicName: return tr("Topic Name"); case Column::Description: return tr("Description"); case Column::TypeName: return tr("Type Name"); case Column::Pause: return tr("Pause"); } } return QAbstractTableModel::headerData(section, orientation, role); } std::string SampleModel::typeName(const QModelIndex &index) const { int row = index.row(); if (row < 0 || row >= m_data.size()) return std::string(); return m_data[row].typeName; } bool SampleModel::addRow(const SampleData &sample, int position) { if (position < 0 || position > m_data.size()) position = m_data.size(); beginInsertRows(QModelIndex(), position, position); m_data.insert(position, sample); endInsertRows(); return true; } bool SampleModel::deleteRow(const QModelIndex &index) { int position = index.row(); if (position < 0 || position > m_data.size()) return false; beginRemoveRows(QModelIndex(), position, position); delete m_data[position].dialog; m_data.remove(position); endRemoveRows(); return true; } void SampleModel::onClear() { beginResetModel(); auto it = m_data.begin(); while (it != m_data.end()) { delete (*it).dialog; it = m_data.erase(it); } endResetModel(); } Qt::ItemFlags SampleModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = QAbstractTableModel::flags(index) | Qt::ItemIsDropEnabled; if (index.isValid()) flags |= Qt::ItemIsDragEnabled; if (index.column() == Column::Description || index.column() == Column::Pause) flags |= Qt::ItemIsEditable; return flags; } QMimeData *SampleModel::mimeData(const QModelIndexList &indexes) const { QMimeData *mimeData = new QMimeData(); QModelIndex mi = indexes.value(0); if (!mi.isValid()) return mimeData; const SampleData &sample = m_data[mi.row()]; mimeData->setData(QLatin1String("SampleModel/SampleData"), sample.encodeForMime()); return mimeData; } bool SampleModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(column) if (action == Qt::IgnoreAction) return true; if ((action != Qt::MoveAction) && (action != Qt::CopyAction)) return false; if (data->hasFormat(QLatin1String("SampleModel/SampleData"))) { const QByteArray encodedData = data->data(QLatin1String("SampleModel/SampleData")); SampleData sample = SampleData::decodeFromMime(encodedData); int beginRow; if (row != -1) beginRow = row; else if (parent.isValid()) beginRow = parent.row(); else beginRow = rowCount(QModelIndex()); addRow(sample, beginRow); } else if (data->hasFormat(QLatin1String("dds::core::xtypes::DynamicData/Pointer+TopicName"))) { QByteArray encodedData = data->data(QLatin1String("dds::core::xtypes::DynamicData/Pointer+TopicName")); QDataStream stream(&encodedData, QIODevice::ReadOnly); while (!stream.atEnd()) { SampleData sample; quint64 ptrVal; stream >> ptrVal; stream >> sample.topicName; dds::core::xtypes::DynamicData *ptr = reinterpret_cast(ptrVal); if (ptr->type_kind() != dds::core::xtypes::TypeKind::STRUCTURE_TYPE) continue; sample.legatusData = QSharedPointer(new dds::core::xtypes::DynamicData(*ptr)); sample.typeName = ptr->type().name(); sample.description = sample.topicName; int beginRow; if (row != -1) beginRow = row; else if (parent.isValid()) beginRow = parent.row(); else beginRow = rowCount(QModelIndex()); addRow(sample, beginRow); } } else if (data->hasFormat(QLatin1String("Text/TopicName+TypeName"))) { QByteArray topicName, typeName; QByteArray encodedData = data->data(QLatin1String("Text/TopicName+TypeName")); QDataStream stream(&encodedData, QIODevice::ReadOnly); stream >> topicName; stream >> typeName; SampleData sample; // TODO: set topicName sample.typeName = typeName.toStdString(); sample.topicName = QString::fromLatin1(topicName.data()); sample.description = sample.topicName; sample.legatusData = QSharedPointer(new dds::core::xtypes::DynamicData(NGLSettings::instance()->dataTopic(sample.topicName).type)); int beginRow; if (row != -1) beginRow = row; else if (parent.isValid()) beginRow = parent.row(); else beginRow = rowCount(QModelIndex()); addRow(sample, beginRow); } return true; } QStringList SampleModel::mimeTypes() const { QStringList sl; sl << QLatin1String("SampleModel/SampleData"); // (SampleModel::SampleData) sl << QLatin1String("dds::core::xtypes::DynamicData/Pointer+TopicName"); // (dds::core::xtypes::DynamicData*) & topicName sl << QLatin1String("Text/TopicName+TypeName"); // e.g. "LTS_ExerciseParticipant" & "ParticipantManagement::ExerciseParticipant" return sl; } bool SampleModel::addRow(const dds::core::xtypes::DynamicData &data, const QString &topicName, const QString &description) { SampleData id; id.typeName = data.type().name(); id.topicName = topicName; id.description = description.isEmpty() ? topicName : description; id.legatusData = QSharedPointer(new dds::core::xtypes::DynamicData(data.type())); return addRow(id); } SampleModel::SampleData SampleModel::sampleData(const QModelIndex &index) const { int row = index.row(); if (row < 0 || row >= m_data.size()) return SampleData(); SampleData data = m_data.value(row); //DEBUG_LOG(m_logger) << "intern: " << data.legatusData.data()->type().name(); return data; } void SampleModel::setDialog(const QModelIndex &index, DetailViewDlg *dlg) { // Dialog will not be destructed! It will be hidden and reused on second usage. // ~SampleModel takes care tp destruct anyways. int row = index.row(); if (row < 0 || row >= m_data.size()) return; m_data[row].dialog = dlg; } QModelIndex SampleModel::indexByDialog(const DetailViewDlg *dlg) const { for (int i = 0; i < m_data.size(); ++i) if (m_data[i].dialog == dlg) return index(i, 0); return QModelIndex(); } // // SampleSortModel // bool SampleSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { if (left.column() != right.column()) return QSortFilterProxyModel::lessThan(left, right); SampleModel *srcModel = qobject_cast(sourceModel()); if (!srcModel) return QSortFilterProxyModel::lessThan(left, right); SampleModel::SampleData dataLeft = srcModel->sampleData(left); SampleModel::SampleData dataRight = srcModel->sampleData(right); switch (left.column()) { case SampleModel::Column::TopicName: return dataLeft.topicName < dataRight.topicName; case SampleModel::Column::Description: return dataLeft.description < dataRight.description; case SampleModel::Column::TypeName: return dataLeft.typeName < dataRight.typeName; case SampleModel::Column::Pause: return dataLeft.pause < dataRight.pause; } return QSortFilterProxyModel::lessThan(left, right); } void SampleSortModel::sort(int column, Qt::SortOrder order) { QModelIndex ind = index(m_currentRow, 0); int sourceRow = mapToSource(ind).row(); QSortFilterProxyModel::sort(column, order); QModelIndex dex = sourceModel()->index(sourceRow, 0); m_currentRow = mapFromSource(dex).row(); emit headerDataChanged(Qt::Vertical, m_currentRow, m_currentRow); } QVariant SampleSortModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QSortFilterProxyModel::headerData(section, orientation, role); if (orientation == Qt::Vertical) { if (section == m_currentRow) return QChar(0x21D2); return QString(); } return sourceModel()->headerData(section, orientation, role); } void SampleModel::SampleData::toXml(QDomDocument &doc, QDomElement &elem) const { QDomElement deSample = doc.createElement(QLatin1String("Sample")); elem.appendChild(deSample); QDomElement deTopicName = doc.createElement(QLatin1String("TopicName")); deSample.appendChild(deTopicName); QDomText deTopicNameContents = doc.createTextNode(topicName); deTopicName.appendChild(deTopicNameContents); QDomElement deTypeName = doc.createElement(QLatin1String("TypeName")); deSample.appendChild(deTypeName); QDomText deTypeNameContents = doc.createTextNode(QString::fromStdString(typeName)); deTypeName.appendChild(deTypeNameContents); QDomElement dePause = doc.createElement(QLatin1String("Pause")); deSample.appendChild(dePause); QDomText dePauseContents = doc.createTextNode(QString::number(pause)); dePause.appendChild(dePauseContents); XmlHelper::dds2Xml(*legatusData, topicName, doc, deSample); } bool SampleModel::SampleData::fromXml(QDomElement &elem) { if (elem.nodeName().compare(QLatin1String("Sample"), Qt::CaseInsensitive) != 0) return false; bool succ = false; QDomNodeList dnl = elem.childNodes(); for (int i = 0; i < dnl.size(); ++i) { QDomNode dn = dnl.at(i); if (!dn.isElement()) continue; QDomElement de = dn.toElement(); if (de.nodeName() == QLatin1String("TopicName")) topicName = de.text(); else if (de.nodeName() == QLatin1String("Type")) typeName = de.text().toStdString(); else if (de.nodeName() == QLatin1String("Pause")) { bool ok; pause = de.text().toInt(&ok); if (!ok) pause = 1000; } else if (de.nodeName() == QLatin1String("DdsData")) { const QString topicName = XmlHelper::topicNameFromXml(de); const TopicDescriptionEx td = NGLSettings::instance()->topicFromTopicName(topicName); const dds::core::xtypes::DynamicData data = XmlHelper::xml2Dds(de, td); dds::core::xtypes::TypeKind kind = dds::core::xtypes::TypeKind::PRIMITIVE_TYPE; try { kind = data.type_kind(); } catch (dds::core::Exception &e) { qWarning("dds exception: %s", e.what()); continue; } if (kind != dds::core::xtypes::TypeKind::STRUCTURE_TYPE) continue; legatusData = QSharedPointer(new dds::core::xtypes::DynamicData(data)); typeName = data.type().name(); succ = (typeName != "Unknown"); } } if (topicName.isEmpty()) topicName = QString::fromStdString(typeName); if (description.isEmpty()) description = topicName; return succ; }