/** ** ** @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 #include #include #include 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(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 treeItemSet; treeItemSet.reserve(parentItem->childCount()); // get all children in a QSet for (int i = 0; i < parentItem->childCount(); ++i) { ModelTreeWidgetItem *childItem = static_cast(parentItem->child(i)); if (childItem) treeItemSet += childItem; } // first loop to delete not used item while (node.isValid()) { ConnectionObject *currentConnectionObj = static_cast(node.connected()); if (currentConnectionObj) { ModelTreeWidgetItem *currentItem = nullptr; // node is connected with tree item --> look for currentItem for (QSet::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(node.connected()); ModelTreeWidgetItem *currentItem = nullptr; if (currentConnectionObj) { for (int i = 0; i < parentItem->childCount(); ++i) { ModelTreeWidgetItem *childItem = static_cast(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 symbolResult = symbolGuard->symbolResult(); if (nullptr != symbolResult) currentItem->setIcon(0, QPixmap::fromImage(symbolResult->symbolImage().image())); symbolGuard->deleteLater(); }); QSharedPointer 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(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(node.connected()); if (connectionObj != nullptr) { for (int i = 0; i < parentItem->childCount(); ++i) { ModelTreeWidgetItem *childItem = static_cast(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(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(child(i)); if (treeItem == nullptr) continue; treeItem -> selectionChanged(planEditor, geometryModel); } } if (m_connectionObj == nullptr) return; SituationMap::GeometryModel::Node node = findGeometryNode(geometryModel.getRootNode(), static_cast(m_connectionObj)->m_id); setCheckState(isSelected()); if (!geometryModel.hasUserData(node)) return; bool ok = false; SituationMap::UniqueMapId uid = static_cast(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(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(m_connectionObj)->m_id); geometryModel.setNodeDisplayName(node, text(nameColumn)); } // ModelTreeWidgetItem::userDefinedNameChanged( const SituationMap::GeometryModel & geometryModel ) } // namespace PlanEditor