465 lines
17 KiB
C++
Executable File
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
|