Files
qt5-legatus-legacy/Lts/Plugins/PlanEditor/model_tree_widget_item.cpp
T
2026-08-13 18:33:10 +02:00

465 lines
17 KiB
C++
Executable File

/**
**
** @RDE_HEADER@
** PlanEditor.
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 09.12.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#include "model_tree_widget_item.h"
#include "model_tree_widget.h"
#include "plan_editor.h"
#include "plan_editor_internal.h"
#include "plan_editor_utils.h"
#include <Map/geometry_model_renderer.h>
#include <Common/referenced.h>
#include <Symbology/sidc_codec.h>
#include <QtWidgets/QHeaderView>
namespace PlanEditor
{
ModelTreeWidgetItem::ModelTreeWidgetItem(QTreeWidget *parent, const QString &text) : QTreeWidgetItem(parent), m_connectionObj(nullptr)
{
setText(itemColumn, text);
initItem();
} // ModelTreeWidgetItem(ModelTreeWidget * treeWidget, const QString & text)
ModelTreeWidgetItem::ModelTreeWidgetItem(QTreeWidgetItem *parent) : QTreeWidgetItem(parent), m_connectionObj(nullptr)
{
initItem();
} // ModelTreeWidgetItem::ModelTreeWidgetItem( TreeWidgetItem * parent )
void ModelTreeWidgetItem::initItem()
{
setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled);
setCheckState(false);
} // void ModelTreeWidgetItem::initItem()
void ModelTreeWidgetItem::setCheckState(bool selected)
{
QTreeWidget *widget = treeWidget();
if (widget)
{
widget->blockSignals(true);
if (parent()) QTreeWidgetItem::setData(itemColumn, Qt::CheckStateRole, QVariant()); // remove checkbox
else QTreeWidgetItem::setData(itemColumn, Qt::CheckStateRole, selected ? Qt::Checked : Qt::Unchecked);
widget->blockSignals(false);
}
} // void ModelTreeWidgetItem::setCheckState( bool selected )
QVariant ModelTreeWidgetItem::data(int column, int role) const
{
if ((Qt::SizeHintRole == role) && (nameColumn == column))
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Their seems to be a small bug in Qt. According to documentation pages of QTreeWidget
* the height should be adjusted to size of editor widget automatically. But in practice
* the height of row is to low. Ok do some calculations ...
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int neededHeight = 2 * treeWidget()->frameWidth() + treeWidget()->header()->height();
return QVariant(QSize(-1, neededHeight));
}
return QTreeWidgetItem::data(column, role);
} // QVariant ModelTreeWidgetItem::data(int column, int role) const
class ConnectionObject : public SituationMap::Referenced
{
public:
uint m_id;
ConnectionObject() = default;
~ConnectionObject() = default;
ConnectionObject(const ConnectionObject &) = delete;
ConnectionObject &operator = (const ConnectionObject &) = delete;
}; // ConnectionObject
static inline void removeConnectionRecursive(ModelTreeWidgetItem *parent)
{
if (!parent)
return;
parent->connectionRemoved();
const int rowCount = parent->childCount();
for (int i = 0; i < rowCount; ++i)
removeConnectionRecursive(static_cast<ModelTreeWidgetItem*>(parent->child(i)));
}
void ModelTreeWidgetItem::updateTree( ModelTreeWidget *modelTreeWidget
, QTreeWidgetItem *parentItem
, SituationMap::Map *map
, const SituationMap::GeometryModel &geoModel
, SituationMap::GeometryModel::Node node)
{
if (nullptr == parentItem) return;
if (nullptr == modelTreeWidget) return;
modelTreeWidget->haveChanges( geoModel.modificationCounter() > 0 );
if (!node.isValid())
{
node = geoModel.getRootNode();
if (!node.hasChildNodes())
{
const auto &childList = parentItem->takeChildren();
for (auto child : childList)
delete child;
return;
}
node = node.firstChildNode();
}
SituationMap::GeometryModel::Node firstChildNode = node;
QSet<ModelTreeWidgetItem *> treeItemSet;
treeItemSet.reserve(parentItem->childCount());
// get all children in a QSet
for (int i = 0; i < parentItem->childCount(); ++i)
{
ModelTreeWidgetItem *childItem = static_cast<ModelTreeWidgetItem *>(parentItem->child(i));
if (childItem) treeItemSet += childItem;
}
// first loop to delete not used item
while (node.isValid())
{
ConnectionObject *currentConnectionObj = static_cast<ConnectionObject *>(node.connected());
if (currentConnectionObj)
{
ModelTreeWidgetItem *currentItem = nullptr;
// node is connected with tree item --> look for currentItem
for (QSet<ModelTreeWidgetItem *>::Iterator it = treeItemSet.begin(); it != treeItemSet.end(); ++it)
{
ModelTreeWidgetItem *childItem = *it;
if (childItem->m_connectionObj == currentConnectionObj)
{
currentItem = childItem;
treeItemSet.erase(it); // remove processed item from item set
break;
}
} // for
if (currentItem == nullptr)
node.unlinkConnection();
}
node = node.next(); // go to next sibling
} // while
// they were for sure removed with unlinkConnection but we are not connected to
// GeometryModel::connectionCleanup() so do it this hacky way
// otherwise we will work on uninitialized memory in ModelTreeWidgetItem::selectionChanged()
for (auto childItem : qAsConst(treeItemSet))
removeConnectionRecursive(childItem);
for (auto childItem : qAsConst(treeItemSet))
delete childItem; // automatically removes from tree and parent
// Second loop to update items and create new one
node = firstChildNode;
while (node.isValid())
{
ConnectionObject *currentConnectionObj = static_cast<ConnectionObject *>(node.connected());
ModelTreeWidgetItem *currentItem = nullptr;
if (currentConnectionObj)
{
for (int i = 0; i < parentItem->childCount(); ++i)
{
ModelTreeWidgetItem *childItem = static_cast<ModelTreeWidgetItem *>(parentItem->child(i));
if (childItem->m_connectionObj == currentConnectionObj)
{
currentItem = childItem;
break;
}
} // for
}
if (currentItem == nullptr)
{
// build connection node to new tree-item
currentConnectionObj = new ConnectionObject();
currentItem = new ModelTreeWidgetItem(parentItem);
currentItem -> m_connectionObj = currentConnectionObj;
currentConnectionObj->m_id = node.makeConnection(currentItem -> m_connectionObj); // transfer ownership of currentItem -> m_connectionObj to node
parentItem -> setExpanded(true);
}
SituationMap::GeometryModel::PrimitiveType primitiveType = geoModel.getType(node);
QString itemText;
const QString &itemName = geoModel.getNodeDisplayName(node);
const QString &itemCount = QStringLiteral(" <%1>").arg(node.connectedId());
auto requestSymbolIcon = [=](const SituationMap::GeometryModel *geoModel, QString itemText, bool isXmlSymbol) -> QString
{
QString sidc;
const AdvancedSymbology::SymbolDictionary &symbolDict = map->symbolDictionary();
if (isXmlSymbol)
{
int symbolId = geoModel->getAttribute(node, SituationMap::GeometryModel::Attribute::SymbolId).toInt();
sidc = AdvancedSymbology::SidcCodec::encodeCivilXmlSymbolId(symbolId);
}
else
{
sidc = geoModel->getAttribute(node, SituationMap::GeometryModel::Attribute::SIDC).toString();
if (SituationMap::GeometryModel::PrimitiveType::ParticipantSymbol == primitiveType) itemText = QObject::tr("Participant");
else itemText = QObject::tr("Symbol");
}
itemText += QLatin1String(" : ") + sidc;
AdvancedSymbology::SymbolResultWatcher *symbolGuard = new AdvancedSymbology::SymbolResultWatcher(parentItem->treeWidget());
QObject::connect(symbolGuard, &AdvancedSymbology::SymbolResultWatcher::resultReady, parentItem->treeWidget(), [currentItem, symbolGuard]()
{
QSharedPointer<AdvancedSymbology::SymbolResult> symbolResult = symbolGuard->symbolResult();
if (nullptr != symbolResult)
currentItem->setIcon(0, QPixmap::fromImage(symbolResult->symbolImage().image()));
symbolGuard->deleteLater();
});
QSharedPointer<AdvancedSymbology::SymbolResult> symbolResult = symbolDict.findBaseSymbol(sidc);
symbolGuard->setSymbolResult(symbolResult);
return itemText;
}; // requestSymbolIcon
switch (primitiveType)
{
case SituationMap::GeometryModel::PrimitiveType::Symbol : itemText = requestSymbolIcon( &geoModel, QObject::tr("XML Symbol") , true ); break;
case SituationMap::GeometryModel::PrimitiveType::ParticipantSymbol : itemText = requestSymbolIcon( &geoModel, QObject::tr("Participant"), false ); break;
case SituationMap::GeometryModel::PrimitiveType::DictionarySymbol : itemText = requestSymbolIcon( &geoModel, QObject::tr("Symbol") , false ); break;
case SituationMap::GeometryModel::PrimitiveType::Ellipse : currentItem->setIcon(itemColumn, Utils::getIcon(QLatin1String("draw-ellipse"))); itemText = QObject::tr("Ellipse"); break;
case SituationMap::GeometryModel::PrimitiveType::Line : currentItem->setIcon(itemColumn, Utils::getIcon(QLatin1String("draw-line"))); itemText = QObject::tr("Line"); break;
case SituationMap::GeometryModel::PrimitiveType::Polyline : currentItem->setIcon(itemColumn, Utils::getIcon(QLatin1String("draw-polyline"))); itemText = QObject::tr("Polyline"); break;
case SituationMap::GeometryModel::PrimitiveType::Rectangle : currentItem->setIcon(itemColumn, Utils::getIcon(QLatin1String("draw-rectangle"))); itemText = QObject::tr("Rectangle"); break;
case SituationMap::GeometryModel::PrimitiveType::Spline : currentItem->setIcon(itemColumn, Utils::getIcon(QLatin1String("draw-bezier-curves"))); itemText = QObject::tr("Spline"); break;
case SituationMap::GeometryModel::PrimitiveType::Text :
{
const QString &textData = geoModel.getTextData(node).trimmed();
const QString &textLine = textData.section(QLatin1Char('\n'), 0, 0);
currentItem->setIcon(itemColumn, Utils::getIcon(QLatin1String("draw-text")));
itemText = QObject::tr("Text");
itemText += QLatin1String(" : ");
itemText += textLine;
if (textData.size() > textLine.size()) itemText += QStringLiteral(" ...");
} break;
case SituationMap::GeometryModel::PrimitiveType::Node :
{
itemText = QObject::tr("Group");
if (node.hasChildNodes())
updateTree( modelTreeWidget, currentItem, map, geoModel, node.firstChildNode() );
else
{
// Group-Node doesn't has any child nodes --> remove
delete currentItem;
currentItem = nullptr;
}
}
break;
case SituationMap::GeometryModel::PrimitiveType::RootNode :
case SituationMap::GeometryModel::PrimitiveType::Unknown :
default : break; // Do we need an error here?
}
if (currentItem)
{
itemText += itemCount;
currentItem->setText(itemColumn, itemText);
currentItem->setText(nameColumn, itemName);
if (currentItem->parent())
currentItem->initItem();
else
{
currentItem->setFlags(currentItem->flags() | Qt::ItemIsUserCheckable);
currentItem->setCheckState(currentItem->isSelected());
}
}
node = node.next(); // go to next sibling
} // while
} //void ModelTreeWidgetItem::updateTree( ModelTreeWidget *modelTreeWidget, QTreeWidgetItem *parentItem, SituationMap::Map *map, const SituationMap::GeometryModel&, SituationMap::GeometryModel::Node )
void ModelTreeWidgetItem::clearSelection(QTreeWidgetItem *parentItem)
{
if (parentItem == nullptr) return;
for (int i = 0; i < parentItem->childCount(); ++i)
{
ModelTreeWidgetItem *childItem = static_cast<ModelTreeWidgetItem *>(parentItem->child(i));
childItem->setSelected(false);
childItem->setCheckState(false);
if (childItem->childCount() > 0) clearSelection(childItem);
}
} // TreeWidgetItem::clearSelection(TreeWidgetItem * parentItem)
void ModelTreeWidgetItem::selectItem(QTreeWidgetItem *parentItem, const SituationMap::GeometryModel::Node &node, bool on)
{
if (nullptr == parentItem) return;
QTreeWidget *treeWidget = parentItem->treeWidget();
if (nullptr == treeWidget) return;
ConnectionObject *connectionObj = static_cast<ConnectionObject *>(node.connected());
if (connectionObj != nullptr)
{
for (int i = 0; i < parentItem->childCount(); ++i)
{
ModelTreeWidgetItem *childItem = static_cast<ModelTreeWidgetItem *>(parentItem->child(i));
if (childItem->m_connectionObj == connectionObj)
{
if (childItem->isSelected() != on)
{
// change selection only if necessary
if (childItem->isSelected() != on)
{
// check the complete group here
while (childItem->parent())
childItem = static_cast<ModelTreeWidgetItem *>(childItem->parent());
childItem->setGroupSelected(on);
}
}
return;
}
if (childItem->childCount() > 0) selectItem(childItem, node, on);
}
}
} // ModelTreeWidgetItem::selectItem( QTreeWidgetItem * parentItem, const SituationMap::GeometryModel::Node & node, bool on )
void ModelTreeWidgetItem::selectionChanged(PlanEditor &planEditor
, const SituationMap::GeometryModel &geometryModel)
{
if (childCount() > 0)
{
for (int i = 0; i < childCount(); ++i)
{
ModelTreeWidgetItem *treeItem = dynamic_cast<ModelTreeWidgetItem *>(child(i));
if (treeItem == nullptr) continue;
treeItem -> selectionChanged(planEditor, geometryModel);
}
}
if (m_connectionObj == nullptr) return;
SituationMap::GeometryModel::Node node = findGeometryNode(geometryModel.getRootNode(), static_cast<ConnectionObject *>(m_connectionObj)->m_id);
setCheckState(isSelected());
if (!geometryModel.hasUserData(node)) return;
bool ok = false;
SituationMap::UniqueMapId uid = static_cast<SituationMap::UniqueMapId>(geometryModel.getUserData(node, &ok));
if (!ok) return;
if (planEditor.isSelected(uid) != isSelected())
planEditor.setSelected(uid, isSelected());
} // ModelTreeWidgetItem::selectionChanged( const SituationMap::Map ...
void ModelTreeWidgetItem::setGroupSelected(bool selectionState)
{
if ((childCount() > 0) && (selectionState)) setExpanded(true);
setSelected(selectionState);
setCheckState(selectionState);
for (int i = 0; i < childCount(); ++i)
{
ModelTreeWidgetItem *treeItem = dynamic_cast<ModelTreeWidgetItem *>(child(i));
if (treeItem == nullptr) continue;
treeItem -> setGroupSelected(selectionState);
}
} // ModelTreeWidgetItem::setGroupSelected( bool selectionState )
SituationMap::GeometryModel::Node ModelTreeWidgetItem::findGeometryNode(SituationMap::GeometryModel::Node node, uint id)
{
if (node.isValid())
{
if (node.connectedId() == id) return node;
if (node.hasChildNodes())
{
node = node.firstChildNode();
while (node.isValid())
{
SituationMap::GeometryModel::Node found = findGeometryNode(node, id);
if (found.isValid()) return found;
node = node.next();
}
}
}
return SituationMap::GeometryModel::Node();
} // ModelTreeWidgetItem::findGeometryNode( const SituationMap::GeometryModel::Node & node, uint id )
void ModelTreeWidgetItem::userDefinedNameChanged(SituationMap::GeometryModel &geometryModel)
{
if (m_connectionObj == nullptr) return;
SituationMap::GeometryModel::Node node = findGeometryNode(geometryModel.getRootNode(), static_cast<ConnectionObject *>(m_connectionObj)->m_id);
geometryModel.setNodeDisplayName(node, text(nameColumn));
} // ModelTreeWidgetItem::userDefinedNameChanged( const SituationMap::GeometryModel & geometryModel )
} // namespace PlanEditor