535 lines
15 KiB
C++
Executable File
535 lines
15 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
*
|
|
* TableModel for dynamic topics
|
|
*
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
*/
|
|
|
|
#include "samplemodel.h"
|
|
#include <QMimeData>
|
|
#include <QStringList>
|
|
#include <QDomDocument>
|
|
#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<quint64>(legatusData.data())
|
|
<< topicName
|
|
<< description
|
|
<< pause;
|
|
|
|
return encodedData;
|
|
}
|
|
|
|
SampleModel::SampleData SampleModel::SampleData::decodeFromMime(const QByteArray &encodedData)
|
|
{
|
|
SampleData ret;
|
|
QDataStream stream(const_cast<QByteArray *>(&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<dds::core::xtypes::DynamicData *>(ptr);
|
|
ret.legatusData = QSharedPointer<dds::core::xtypes::DynamicData>(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<dds::core::xtypes::DynamicData> &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<dds::core::xtypes::DynamicData *>(ptrVal);
|
|
if (ptr->type_kind() != dds::core::xtypes::TypeKind::STRUCTURE_TYPE)
|
|
continue;
|
|
|
|
sample.legatusData = QSharedPointer<dds::core::xtypes::DynamicData>(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<dds::core::xtypes::DynamicData>(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<dds::core::xtypes::DynamicData>(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<SampleModel *>(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<dds::core::xtypes::DynamicData>(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;
|
|
}
|