/* ** ** @RDE_HEADER@ ** plan_editor.cpp ** $Id$ ** $HeadURL$ ** $Author$ ** ** ----------------------------------------------------------------------------------------------- ** ** Hardware-Environment: Intel-PC ** Software-Environment: Windows 7, Qt 5.9, ArcGIS 100.2.1 ** ** Language ........: C++ 11 ** ** ----------------------------------------------------------------------------------------------- ** ** Author ...........: Jörg Turla, SPD G, Tel. 3132 ** ** ----------------------------------------------------------------------------------------------- ** ** Notes .........: ** ** 09.12.2015, JT : First edition for use with Qt5 ** ** ------------------------------------------------------------------------------------------------ */ const char* const REVISION = "$Revision$"; #include "dialog.h" #include "drop_event_manager.h" #include "icon_names.h" #include "lookup_symbol_dlg.h" #include "geometry_placer.h" #include "selection_worker.h" #include "plan_editor_utils.h" #include "metadata_dialog.h" #include "model_tree_widget.h" #include "model_tree_widget_item.h" #include "symbol_favourites.h" #include #include #include #include #include #include #include #include #include #include #include #include // just for testing sight fan #include // just for testing parallel translation #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "plan_editor_internal.h" #include "setting_keys.h" #include // just for testing namespace PlanEditor { const double PasteDistance = 20.0; const int PLAN_EDITOR_TEXTBOX_PARENT_ID = -123; class KeyPressDeleteFilter : public QObject { public: explicit KeyPressDeleteFilter(PlanEditor *planEditor) : QObject(planEditor) , m_planEditor(planEditor) {} protected: bool eventFilter(QObject *obj, QEvent *event) override { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if ((keyEvent->key() == Qt::Key_Delete) && m_planEditor) m_planEditor->onDelete(); return true; } // standard event processing return QObject::eventFilter(obj, event); } QPointer m_planEditor; }; void PlanEditor::onGeoModelModified(unsigned int modifyCount) { if (!m_internal || !m_internal->m_modelTreeWidget) return; m_internal->m_modelTreeWidget->haveChanges((modifyCount > 0)); } // void PlanEditor::onGeoModelModified(unsigned int modifyCount) void PlanEditor::onNoticeGeoModelTypeError(const QString &errorMsg) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " Geometry Model type error: " << errorMsg; } // PlanEditor::onNoticeGeoModelTypeError( const QString & ) void PlanEditor::onNoticeGeoModelAttributeError(const QString &errorMsg) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " Geometry Model attribute error: " << errorMsg; } // PlanEditor::onNoticeGeoModelAttributeError( const QString & ) void PlanEditor::onNoticeGeoModelModelError(const QString &errorMsg) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " Geometry Model error: " << errorMsg; } // PlanEditor::onNoticeGeoModelModelError( const QString & ) void PlanEditor::onNoticeGeoModelError(const QString &errorMsg) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " Geometry Model error: " << errorMsg; QString msg = tr("Critical internal failure detected: %1").arg(errorMsg); msg += QStringLiteral("
"); msg += tr("It is recommended to save the plan and restart the editor to avoid data loss"); QMessageBox::critical(topLevelWidget(), tr("Critical Internal Failure"), msg); } // PlanEditor::onNoticeGeoModelError( const QString & ) void PlanEditor::onNoticeGeoModelUpdate(const SituationMap::GeometryModel::Node &node , SituationMap::GeometryModel::Attribute att , bool coordsChanged) { Q_UNUSED(node); Q_UNUSED(att); Q_UNUSED(coordsChanged); const QString &nodename = m_internal->m_geoModel->getNodeDisplayName(node); DEBUG_LOG(m_internal->m_logger) << LOGFUNCTION << " Geometry Model has changed: " << " nodeDisplayName='" << nodename << "'" << " connectedId=" << node.connectedId() << " att=" << static_cast(att) << " coordsChanged=" << coordsChanged; switch (m_internal->m_updateViewMode) { case UpdateViewMode::Scheduled : m_internal->m_delayedUpdateTimer->start(10); break; case UpdateViewMode::Immediately : onUpdateView(); break; case UpdateViewMode::Manually : break; } // [GUEZE-2947] Undo,Redo fuer PixelBasedTextBoxen // update TextBoxes (transfer data from model to TextBoxes) if ((att == SituationMap::GeometryModel::Attribute::MetaPixelBasedTextBoxes) || (att == SituationMap::GeometryModel::Attribute::MaxAttributes) ) { // // initializePlanTextBoxPixel(); // does not really work, to rough // Assumption: // - we create one PixelBasedTextBox (implicit an EditorDialog is opened) // - enter some characters (PixelBasedTextBox is updated) // - we press apply // within LtsTextEditDialog::apply() there will be two signals emitted // - first 'signalTextApplied' // this will update the geometryModel and via changed and onNoticeGeoModelUpdate // the LtsTextEditDialog will be deleted // - second 'signalTextAppliedWithBackGroundColor' // will crash since object does not exist anymore.... // Note: most of the times, ptbdCont.size() == pbtbIdVec.size() are equal... // Model const QString &strRef = m_internal->m_geoModel->getModelAttribute(SituationMap::GeometryModel::Attribute::MetaPixelBasedTextBoxes).toString(); const QVector &ptbdCont = SituationMap::PixelTextBoxData::pixelTextBoxContainerFromJsonStr(strRef); // displayed TextBoxes SituationMap::PixelBasedTextBoxHandler &pixelBasedTextBoxHandler = m_internal->m_map->pixelBasedTextBoxHandler(); const SituationMap::PBTB_ID pbtbIdAsParent(PLAN_EDITOR_TEXTBOX_PARENT_ID); const QVector pbtbIdVec = pixelBasedTextBoxHandler.getAllIdsOfParent(pbtbIdAsParent); if (ptbdCont.size() == pbtbIdVec.size()) { // this case is typically entered by pressing: undo, redo, LtsTextEditDialog::apply() QVectorIterator pbtbIdVecIt(pbtbIdVec); for (const SituationMap::PixelTextBoxData &ptbd : qAsConst(ptbdCont)) { pbtbIdVecIt.hasNext(); const SituationMap::PBTB_ID &pbtbId = pbtbIdVecIt.next(); DEBUG_LOG(m_internal->m_logger) << LOGFUNCTION << " -------- ptbd.toDebugString = " << ptbd.toDebugString() << " UPDATE pbtbId = " << pbtbId; pixelBasedTextBoxHandler.updateTextBox(pbtbId, ptbd); } } else { // this case is typically entered by pressing undo, redo; but no by LtsTextEditDialog::apply() initializePlanTextBoxPixel(); } // if (ptbdCont.size() == pbtbIdVec.size()) } // NLDEVCTRL-807: if (m_internal && m_internal->m_btnUndo) m_internal->m_btnUndo->setEnabled((m_internal->m_geoModel->undoSteps() > 0)); if (m_internal && m_internal->m_btnRedo) m_internal->m_btnRedo->setEnabled((m_internal->m_geoModel->redoSteps() > 0)); } // PlanEditor::onNoticeGeoModelUpdate( const SituationMap::GeometryModel::Node & node, SituationMap::GeometryModel::Attribute att, bool coordsChanged ); void PlanEditor::onUpdateView() { m_internal->m_delayedUpdateTimer->stop(); if (m_internal->m_teDebug) m_internal->m_teDebug->setPlainText(QString::fromUtf8(m_internal->m_geoModel->toByteArrayDebug())); ModelTreeWidgetItem::updateTree(m_internal->m_modelTreeWidget, m_internal->m_modelTreeWidget->rootItem(), m_internal->m_map, *(m_internal->m_geoModel)); // m_internal->m_map->getQGraphicsView() seems to be nullptr , mostly during slotReplaySessionForPlanEditorStarted if (m_internal->m_map && m_internal->m_map->getQGraphicsView() && m_internal->m_map->getQGraphicsView()->viewport()) m_internal->m_map->getQGraphicsView()->viewport()->update(); m_internal->m_modelTreeWidget->showNeedSaveFlag( (m_internal->m_geoModel->modificationCounter() > 0) ); } // PlanEditor::onUpdateView() QString PlanEditor::planDefaultDirectory() const { return m_internal->m_defaultPlanDirectory; } // PlanEditor::planDefaultDirectory() const void PlanEditor::setPlanDefaultDirectory(const QString &dir) { QFileInfo fileInfo(dir); if ((fileInfo.isDir()) || (dir.isEmpty())) m_internal->m_defaultPlanDirectory = dir; else ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " failed: \"" << dir << "\" is not a directory."; } // PlanEditor::setPlanDefaultDirectory( const QString & dir ) QWidget *PlanEditor::topLevelWidget() const { return m_internal->m_session.topLevelWidget(); } void PlanEditor::setParticipantsSidcsInUse(const QSet &sidcs) { // DEBUG_LOG(m_internal->m_logger) << LOGFUNCTION << " sidcs: " << sidcs.values().join(QLatin1String(";")); if (!m_internal->m_lookupSymbolManager) return; m_internal->m_lookupSymbolManager->setParticipantsSidcsInUse(sidcs); } static void importGuezXMLSymbolsFromFile(const AdvancedSymbology::SymbolDictionary &symbolDict); PlanEditor::PlanEditor(SituationMap::Map *map, LtsSession::LtsSession *ltsSession) : QObject() , m_internal(new PlanEditor::InternalData(map, PlanEditorSession(ltsSession, this), this)) { init(); } PlanEditor::PlanEditor(SituationMap::Map *map, LtsWidgets::LtsBaseLayoutWidget *ltsbaseWidget) : QObject() , m_internal(new PlanEditor::InternalData(map, PlanEditorSession(ltsbaseWidget, this), this)) { init(); } void PlanEditor::init() { if (!m_internal->m_map) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "SituationMap::Map instance is nullptr."; return; } // Qt-doc: event filters are automatically removed when destroyed KeyPressDeleteFilter *kpFilter = new KeyPressDeleteFilter(this); m_internal->m_map->installEventFilter(kpFilter); SituationMap::RB::RubberbandFactory *rbndFactory = &m_internal->m_map->rubberbandFactory(); connect(rbndFactory, &SituationMap::RB::RubberbandFactory::rubberbandCreated, this, &PlanEditor::onRubberbandAction); connect(rbndFactory, &SituationMap::RB::RubberbandFactory::rubberbandDestroyed, this, &PlanEditor::onRubberbandAction); AdvancedSymbology::SymbolDictionary &symbolDict = m_internal->m_map->symbolDictionary(); // make symbol dictionary if doesn't exist AdvancedSymbology::LoadStatus dictStatus = symbolDict.loadStatus(); if ((AdvancedSymbology::LoadStatus::Loaded != dictStatus) && (AdvancedSymbology::LoadStatus::Loading != dictStatus)) symbolDict.load(); importGuezXMLSymbolsFromFile(symbolDict); auto &layerFac = m_internal->m_map->layerFactory(); layerFac.setLayersVisible({SituationMap::LayerType::Undefined, SituationMap::LayerType::JumpPoint, SituationMap::LayerType::SnapshotOverlay, SituationMap::LayerType::Overlay, SituationMap::LayerType::Operational }, false); if (m_internal->m_session.isSnapshot()) // ToDo: check if partitionType is sufficiant { m_internal->initLogicalLayernameAndRenderer(m_internal->m_map->layerFactory().snapshotOverlayLayername(), this); layerFac.addOverlayLayer(m_internal->m_logicalLayername, m_internal->m_logicalLayername, true); } else layerFac.addOperationalLayer(m_internal->m_logicalLayername, tr("PlanEditor")); // ToDo: removeOperationalLayer if (nullptr == topLevelWidget()) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "top level widget is nullptr."; } buildFileBox(m_internal->m_session); buildSelectionModeBox(m_internal->m_session); buildEditBox(m_internal->m_session); buildCreateBox(m_internal->m_session); buildHelpBox(m_internal->m_session); m_internal -> m_modelTreeWidget = new ModelTreeWidget(this, topLevelWidget()); #ifdef LTS_DEBUG m_internal -> m_dlgDebug = buildDebugBox(topLevelWidget()); m_internal->m_session.addWidget(m_internal -> m_dlgDebug.data(), title(), QLatin1String("Debug"), LtsWidgets::LayoutArea::eInfoTab, false); #endif m_internal->m_session.addWidget(m_internal -> m_modelTreeWidget.data(), title(), m_internal->m_modelTreeWidget->objectName(), LtsWidgets::LayoutArea::eInfoTab, false); m_internal->m_session.makeCurrentWidget(m_internal->m_modelTreeWidget, LtsWidgets::LayoutArea::eInfoTab); onHideDialogTab(); if (m_internal->m_map) { connect(m_internal->m_map, &SituationMap::Map::mapObjectsClicked , this, &PlanEditor::onMapObjectClicked); connect(m_internal->m_map, &SituationMap::Map::mapObjectsPickedByArea, this, &PlanEditor::onMapObjectClicked); connect(m_internal->m_map, &SituationMap::Map::mapReady, this, &PlanEditor::onEnablePlanEditor); if (!m_internal->m_session.isLtsSession()) { connect(m_internal->m_map, &SituationMap::Map::mapReady, this, [this]() { connect(m_internal->m_map.data() , &SituationMap::Map::contextMenuClicked , this , &PlanEditor::onContextMenuRequested); // siehe auch sit_map.cpp Zeile 421 ff }); } if (!m_internal->m_map->isReady()) { onDisablePlanEditor(); } connect( qApp, &QCoreApplication::aboutToQuit, this, &PlanEditor::onQuit ); m_internal->m_map->installViewPortEventFilter(this); } else onDisablePlanEditor(); m_internal->m_favouritesActionManager->initMainMenu(); // look for autosaved files const QString &tmp = QStringLiteral("/tmp/plan_") + QString::number(LtsSessionData::LtsSessionData::getInstance()->getOperatorId()); if (QFile::exists(tmp)) { if (LtsWidgets::Button::Yes == LtsWidgets::LtsMessageDialog::showMessage( topLevelWidget() , tr("Autosaved file exists. Do you want to load it?") , title() + QStringLiteral(" : ") + tr("Open autosaved file") , LtsWidgets::Behaviour::Modal , LtsWidgets::Button::Yes | LtsWidgets::Button::No)) loadFromFile(tmp); } m_internal->m_autosaveTimer->start(300000); // 5 mins = 300000 } // void PlanEditor::init( SituationMap::Map * map ) QString PlanEditor::title() const { return m_internal->m_title; } void PlanEditor::onEnableSymbolWidgets() { if (m_internal->m_symbolDictLoaded) return; m_internal->m_symbolDictLoaded = true; m_internal->m_favouritesActionManager->initFromDefaultSettings(); for (QWidget *w : qAsConst(m_internal->m_symbolWidgets)) w->setEnabled(true); } // PlanEditor::onEnableSymbolWidgets void PlanEditor::onEnablePlanEditor() { m_internal->m_session.setTabLayoutAreaEnabled(LtsWidgets::LayoutArea::eInfoTab, true); m_internal->m_session.setTabLayoutAreaEnabled(LtsWidgets::LayoutArea::eDialogTab, true); m_internal->m_session.setTabLayoutAreaEnabled(LtsWidgets::LayoutArea::eSpecialTab, true); m_internal->m_session.setMenuEnabled(true); m_internal->m_session.setToolBarEnabled(true); } // void PlanEditor::onEnablePlanEditor() void PlanEditor::onDisablePlanEditor() { if (nullptr == m_internal->m_currentBboxRubberband) { m_internal->m_session.setTabLayoutAreaEnabled(LtsWidgets::LayoutArea::eInfoTab, false); m_internal->m_session.setTabLayoutAreaEnabled(LtsWidgets::LayoutArea::eDialogTab, false); m_internal->m_session.setTabLayoutAreaEnabled(LtsWidgets::LayoutArea::eSpecialTab, false); m_internal->m_session.setMenuEnabled(false); m_internal->m_session.setToolBarEnabled(false); } } // void PlanEditor::onDisablePlanEditor() bool PlanEditor::readyForUse() const { return ((m_internal->m_map) && (m_internal->m_map->isReady())); } PlanEditor::~PlanEditor() { if (m_internal->m_map) m_internal->m_map->removeViewPortEventFilter(this); if (m_internal->m_textBoxNotifyTimer) m_internal->m_textBoxNotifyTimer->stop(); } // PlanEditor::~PlanEditor() bool PlanEditor::event(QEvent *e) { if (e->type() == QEvent::LanguageChange) { buildFileBox(m_internal->m_session,true); buildSelectionModeBox(m_internal->m_session,true); buildEditBox(m_internal->m_session,true); buildCreateBox(m_internal->m_session,true); buildHelpBox(m_internal->m_session,true); m_internal->m_favouritesActionManager->initMainMenu(true); } return QObject::event(e); } // bool PlanEditor::event(QEvent *e) void PlanEditor::buildSelectionModeBox(const PlanEditorSession &session, bool retranslate) { LtsWidgets::PluginData pluginAction; LtsWidgets::LtsDragIcon *currentDragIcon = nullptr; QButtonGroup *selectButtonGroup = new QButtonGroup(this); selectButtonGroup -> setExclusive(true); pluginAction.menuGroup = tr("Selection"); pluginAction.identifier = QStringLiteral("Mouse_Pick_Selection"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(SELECT_PICK_ICON_NAME)); pluginAction.menuText = tr("Mouse Pick Selection"); pluginAction.tooltip = tr("Selection with single mouse clicks"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { m_internal->m_selectionMode = SelectionMode::Picking; }; currentDragIcon = session.addMenuAction(pluginAction); if (!retranslate) m_internal->m_ltsItems += currentDragIcon; if (currentDragIcon) { currentDragIcon->setCheckable(true); currentDragIcon->setChecked(m_internal->m_selectionMode == SelectionMode::Picking); currentDragIcon->addToButtonGroup(selectButtonGroup, int(SelectionMode::Picking)); } pluginAction.identifier = QStringLiteral("Line_Selection"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(SELECT_LINE_ICON_NAME)); pluginAction.menuText = tr("Line Selection"); pluginAction.tooltip = tr("Selection with a line"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { m_internal->m_selectionMode = SelectionMode::Line; }; currentDragIcon = session.addMenuAction(pluginAction); if (!retranslate) m_internal->m_ltsItems += currentDragIcon; if (currentDragIcon) { currentDragIcon->setCheckable(true); currentDragIcon->setChecked(m_internal->m_selectionMode == SelectionMode::Line); currentDragIcon->addToButtonGroup(selectButtonGroup, int(SelectionMode::Line)); } pluginAction.identifier = QStringLiteral("Lasso_Selection"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(SELECT_LASSO_EDIT_ICON_NAME)); pluginAction.menuText = tr("Lasso Selection"); pluginAction.tooltip = tr("Selection with a lasso"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { m_internal->m_selectionMode = SelectionMode::Lasso; }; currentDragIcon = session.addMenuAction(pluginAction); if (!retranslate) m_internal->m_ltsItems += currentDragIcon; if (currentDragIcon) { currentDragIcon->setCheckable(true); currentDragIcon->setChecked(m_internal->m_selectionMode == SelectionMode::Lasso); currentDragIcon->addToButtonGroup(selectButtonGroup, int(SelectionMode::Lasso)); } pluginAction.identifier = QStringLiteral("Rectangle_Selection"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(SELECT_RECT_EDIT_ICON_NAME)); pluginAction.menuText = tr("Rectangle Selection"); pluginAction.tooltip = tr("Selection with a rectangle"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { m_internal->m_selectionMode = SelectionMode::Rectangle; }; currentDragIcon = session.addMenuAction(pluginAction); if (!retranslate) m_internal->m_ltsItems += currentDragIcon; if (currentDragIcon) { currentDragIcon->setCheckable(true); currentDragIcon->setChecked(m_internal->m_selectionMode == SelectionMode::Rectangle); currentDragIcon->addToButtonGroup(selectButtonGroup, int(SelectionMode::Rectangle)); } pluginAction.identifier = QStringLiteral("Mouse_Pick_Selection_and_Edit_Geometry"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(SELECT_PICK_EDIT_ICON_NAME)); pluginAction.menuText = tr("Mouse Pick Selection and Edit Geometry"); pluginAction.tooltip = tr("Selection with single mouse clicks and starts proper rubberband to edit geometry"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { m_internal->m_selectionMode = SelectionMode::PickingAndEdit; }; currentDragIcon = session.addMenuAction(pluginAction); if (!retranslate) m_internal->m_ltsItems += currentDragIcon; if (currentDragIcon) { currentDragIcon->setCheckable(true); currentDragIcon->setChecked(m_internal->m_selectionMode == SelectionMode::PickingAndEdit); currentDragIcon->addToButtonGroup(selectButtonGroup, int(SelectionMode::PickingAndEdit)); } /* Dialog * dialog = new LtsWidgets::LtsDialog( parent ); QHBoxLayout * layout = new QHBoxLayout( dialog ); QToolButton * tb = nullptr; dialog->setTitle("Picking Mode"); tb = new QToolButton(dialog); { tb->setText( tr("Picking") ); tb->setIcon( Utils::getIcon(QLatin1String(SELECT_PICK_ICON_NAME)) ); tb->setToolTip( tr("Selection with single mouse clicks") ); connect( tb, &QToolButton::clicked, this, &PlanEditor::onSelectionActivatePicking ); } layout->addWidget( tb, 0 ); tb = new QToolButton(dialog); { tb->setText( tr("Line Select") ); tb->setIcon( Utils::getIcon(QLatin1String(SELECT_LINE_ICON_NAME)) ); tb->setToolTip( tr("Selection with intersection line") ); connect( tb, &QToolButton::clicked, this, &PlanEditor::onSelectionActivatePicking ); } layout->addWidget( tb, 0 ); layout->addStretch(3); return dialog; */ } // PlanEditor::buildSelectionModeBox( LtsWidgets::LtsBaseLayoutWidget * ltsbaseWidget ) void PlanEditor::buildFileBox(const PlanEditorSession &session, bool retranslate) { if (m_internal->m_session.isLtsSession()) { return; // skip if running as plugin } LtsWidgets::PluginData pluginAction; pluginAction.menuGroup = tr("File"); pluginAction.identifier = QStringLiteral("New_Plan_File"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DOCUMENT_NEW_ICON_NAME)); pluginAction.menuText = tr("New Plan"); pluginAction.tooltip = tr("Create empty plan"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onPlanCreate(); }; if (retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Open_Plan_File"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DOCUMENT_OPEN_ICON_NAME)); pluginAction.menuText = tr("Open Plan"); pluginAction.tooltip = tr("Load plan from file"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onPlanLoad(); }; if (retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Import_Plan_File"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DOCUMENT_IMPORT_ICON_NAME)); pluginAction.menuText = tr("Import Plan"); pluginAction.tooltip = tr("Import plan from file"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onPlanImport(); }; if (retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Save_Plan_File"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DOCUMENT_SAVE_ICON_NAME)); pluginAction.menuText = tr("Save"); pluginAction.tooltip = tr("Save plan to file"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onPlanSave(); }; if (retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("SaveAs_Plan_File"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DOCUMENT_SAVEAS_ICON_NAME)); pluginAction.menuText = tr("Save As"); pluginAction.tooltip = tr("Save plan to file as"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onPlanSaveAs(); }; if (retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Export_Plan_File"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DOCUMENT_SAVEAS_ICON_NAME)); pluginAction.menuText = tr("Export Selected"); pluginAction.tooltip = tr("Save selected plan parts to file as"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onPlanSaveSelected(); }; if (retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Print_Plan"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DOCUMENT_PRINT_ICON_NAME)); pluginAction.menuText = tr("Print"); pluginAction.tooltip = tr("Print plan"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onPlanPrint(); }; if (retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); #ifdef HAS_CLOSE_BUTTON pluginAction.identifier = QStringLiteral("Close"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DOCUMENT_CLOSE_ICON_NAME)); pluginAction.menuText = tr("Close"); pluginAction.tooltip = tr("Close Plan Editor"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onQuit(); }; if (retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); #endif //#define TEST_AGGREGATION 1 #ifdef TEST_AGGREGATION pluginAction.identifier = QStringLiteral("ParticipantAggregation"); pluginAction.menuIcon = QIcon(QLatin1String("icon:overlays.svg")); pluginAction.menuText = tr("Participant Aggregation"); pluginAction.tooltip = tr("Aggregation settings for participants"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { QWidget *parent = this->topLevelWidget(); SituationMap::ParticipantAggregationDialog *pad = new SituationMap::ParticipantAggregationDialog(m_internal->m_map, parent); m_internal->m_session.addWidget(pad, title(), tr("Participant Aggregation"), LtsWidgets::LayoutArea::eDialogTab); }; if (retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); #endif #ifdef TEST_RUBBERBAND_RECTANGLE_5X5 pluginAction.identifier = QStringLiteral("Make_Rectangle_Rubberband"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DOCUMENT_CLOSE_ICON_NAME)); pluginAction.menuText = tr("Make Rectangle Rubberband"); pluginAction.tooltip = tr("Make Rectangle Rubberband"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { SituationMap::RB::RubberbandRectangle *rubberband = m_internal->m_map->rubberbandFactory().create(m_internal.get()); if (rubberband) { rubberband->setAutoDelete(true); rubberband->buildRectangleRelativeToBottomLeftCorner( m_internal->m_map->centerPoint(), 5, 5, QPointF(0,1) ); } }; if (retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); #endif // #define TEST_SIGHT_FAN 1 #ifdef TEST_SIGHT_FAN pluginAction.identifier = QStringLiteral("Make_SIGHT_FAN"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DOCUMENT_CLOSE_ICON_NAME)); pluginAction.menuText = tr("Make Sight Fan"); pluginAction.tooltip = tr("Make Sight Fan"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { SituationMap::RB::RubberbandEllipse *rubberband = m_internal->m_map->rubberbandFactory().create(m_internal.get()); if (rubberband) { rubberband->setAutoDelete(true); rubberband->constrainQuadratic(true); rubberband->constrainOrthogonal(true); rubberband->enableSector(true); static SituationMap::RefPointer sightFan; std::function updateSightFan = [this](SituationMap::RB::RubberbandBase* rbBase) -> bool { SituationMap::RB::RubberbandEllipse *rubberElli = dynamic_cast(rbBase); if (rubberElli) { QPointF center; double diameter; double dummy; QPointF direction; double startAngle = 0.0; double dAngle = 2.0 / 180.0*MathConst::pi; unsigned int noBeams = 180; rubberElli->rectangleParameter(center, diameter, dummy, direction); if (diameter > 0.0) { double radius = diameter * 0.5; if (rubberElli->hasSector()) { startAngle = SituationMap::MathUtils::getMathAngle(rubberElli->sectorStart()); double endAngle = SituationMap::MathUtils::getMathAngle(rubberElli->sectorEnd()); noBeams = floor((endAngle - startAngle) / dAngle); } if (nullptr == sightFan.get()) { SituationMap::ObjectFactory &objFac = m_internal->m_map->objectFactory(); sightFan = objFac.create(QLatin1String("Tools")); if (sightFan) { sightFan->initFan( SituationMap::Point3D(center.x(),center.y(),0.0), 1.5, radius, radius / 1000.0, startAngle, dAngle, noBeams, 0.6 ); } } else { sightFan->setInnerRadius(radius/1000.0); sightFan->setRadius(radius); sightFan->setStartAngle(startAngle); sightFan->setNoOfFans(noBeams); sightFan->move(center); } } } return true; }; // std::function rubberband->installExternalUpdate(updateSightFan); QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, this, [this](SituationMap::RB::RubberbandBase *rubberband) { if (nullptr != sightFan.get()) { sightFan->removeMySelf(); sightFan = nullptr; } SituationMap::RB::RubberbandEllipse *rubberElli = dynamic_cast(rubberband); if (rubberElli) { SituationMap::ObjectFactory &objFac = m_internal->m_map->objectFactory(); SituationMap::RefPointer sightFan = objFac.create(QLatin1String("Tools")); if (sightFan) { QPointF center = rubberElli->centerPoint(); double diameter; double dummy; QPointF direction; double startAngle = 0.0; double dAngle = 2.0/180.0*MathConst::pi; unsigned int noBeams = 180; rubberElli->rectangleParameter(center, diameter, dummy, direction); if (rubberElli->hasSector()) { startAngle = SituationMap::MathUtils::getMathAngle(rubberElli->sectorStart()); double endAngle = SituationMap::MathUtils::getMathAngle(rubberElli->sectorEnd()); noBeams = ceil(((startAngle > endAngle) ? (endAngle - startAngle + MathConst::pi_two) : (endAngle - startAngle)) / dAngle); } double radius = diameter * 0.5; sightFan->initFan(SituationMap::Point3D(center.x(),center.y(),0.0), 1.5, radius, radius / 1000.0, startAngle, dAngle, noBeams, 0.6 ); } } }); QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, this, [this](SituationMap::RB::RubberbandBase *rubberband) { if (nullptr != sightFan.get()) { sightFan->removeMySelf(); sightFan = nullptr; } onRubberbandAbort(rubberband); }); } }; if (retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); #endif //#define TEST_SIGHT_FAN_DIALOG 1 #ifdef TEST_SIGHT_FAN_DIALOG pluginAction.identifier = QStringLiteral("Make_SIGHT_FAN_DIALOG"); pluginAction.menuIcon = QIcon(); pluginAction.menuText = tr("Make Sight Fan Dialog"); pluginAction.tooltip = tr("Make Sight Fan Dialog"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { ViewshedDialog *dlg = new ViewshedDialog(m_internal->m_session.topLevelWidget()); static double viewshedAlpha = 1.0; static double viewshedInnerRadius = 100.0; static double viewshedObserverHeight = 2.0; static double viewshedTargetHeight = 2.0; static double viewshedBeamAngle = 2.0; dlg->initValues(viewshedAlpha, viewshedInnerRadius, viewshedObserverHeight, viewshedTargetHeight, viewshedBeamAngle); connect(dlg, &ViewshedDialog::createViewshed, this, [this, dlg]() { viewshedAlpha = dlg->alpha(); viewshedInnerRadius = dlg->innerRadius(); viewshedObserverHeight = dlg->observerHeight(); viewshedTargetHeight = dlg->targetHeight(); viewshedBeamAngle = dlg->beamAngle(); this->m_internal->m_map->viewshed().create(viewshedAlpha, viewshedInnerRadius, viewshedObserverHeight, viewshedTargetHeight, viewshedBeamAngle); }); connect(dlg, &ViewshedDialog::removeViewshed, this, [this]() { this->m_internal->m_map->viewshed().remove(); }); connect(dlg, &ViewshedDialog::parameterChanged, this, [this, dlg]() { viewshedAlpha = dlg->alpha(); viewshedInnerRadius = dlg->innerRadius(); viewshedObserverHeight = dlg->observerHeight(); viewshedTargetHeight = dlg->targetHeight(); viewshedBeamAngle = dlg->beamAngle(); this->m_internal->m_map->viewshed().modify(-1,viewshedAlpha, viewshedInnerRadius, viewshedObserverHeight, viewshedTargetHeight, viewshedBeamAngle); }); m_internal->m_session.addWidget( dlg, tr("Situation Map"), QLatin1String("ViewshedDialog"), LtsWidgets::LayoutArea::eDialogTab, true ); }; if (retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); #endif //#define TEST_PARTICIPANT_LIST #ifdef TEST_PARTICIPANT_LIST pluginAction.identifier = QStringLiteral("setParticipantsSidcList"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DOCUMENT_CLOSE_ICON_NAME)); pluginAction.menuText = tr("Test setParticipantsSidcList"); pluginAction.tooltip = tr("setParticipantsSidcList"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { if (isSymbolModelValid()) { QStringList sidcList; sidcList << AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::MIL2525D, QStringLiteral("10010100001205000000")); sidcList << AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::MIL2525D, QStringLiteral("10010100001106000000")); sidcList << AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::MIL2525D, QStringLiteral("10011500001202030000")); setParticipantsSidcList(sidcList); } }; if (retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("resetParticipantsSidcList"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DOCUMENT_CLOSE_ICON_NAME)); pluginAction.menuText = tr("Test resetParticipantsSidcList"); pluginAction.tooltip = tr("resetParticipantsSidcList"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { if (isSymbolModelValid()) { setParticipantsSidcList(QStringList()); } }; if (retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); #endif //#define TEST_PARALLEL_TRANSLATION #ifdef TEST_PARALLEL_TRANSLATION pluginAction.identifier = QStringLiteral("ParallelTranslation"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DOCUMENT_CLOSE_ICON_NAME)); pluginAction.menuText = tr("Test Parallel Translation"); pluginAction.tooltip = tr("Test Parallel Translation"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { SituationMap::RB::RubberbandCorridor *rubberband = m_internal->m_map->rubberbandFactory().create(m_internal.get()); if (rubberband) { static std::function testParallel = [this](SituationMap::RB::RubberbandBase *r) -> bool { const QVector &line = static_cast(r)->coordinateVector(); if (line.size() > 2) { double distance = static_cast(r)->corridorWidth(); const QVector &pLine = SituationMap::MathUtils::GeometryEngine::parallelTranslation( m_internal->m_map->coordTranslationFunctor(), line, distance ); static SituationMap::Polyline *pLineObjPtr = nullptr; if (nullptr == pLineObjPtr) { SituationMap::ObjectFactory &objFac = m_internal->m_map->objectFactory(); pLineObjPtr = objFac.create( SituationMap::INVALID_MAP_ID , QStringLiteral("PlanEditorLayer") , QLatin1String("Test")).get(); } if (pLineObjPtr) { if (pLine.size() > 1) { SituationMap::LineStyle lineStyle; lineStyle.setLineWidth(5); lineStyle.setLineColor(QColor(Qt::green)); pLineObjPtr->setLineStyle(lineStyle); pLineObjPtr->setPositions(SituationMap::convert2D3D(pLine)); pLineObjPtr->updateView(); } else { pLineObjPtr->removeMySelf(); pLineObjPtr = nullptr; } } // and now back again if (1) { const QVector &rLine = SituationMap::MathUtils::GeometryEngine::parallelTranslation( m_internal->m_map->coordTranslationFunctor(), pLine, -distance ); static SituationMap::Polyline *rLineObjPtr = nullptr; if (nullptr == rLineObjPtr) { SituationMap::ObjectFactory &objFac = m_internal->m_map->objectFactory(); rLineObjPtr = objFac.create(SituationMap::INVALID_MAP_ID , QStringLiteral("PlanEditorLayer") , QLatin1String("Test")).get(); } if (rLineObjPtr) { if (rLine.size() > 1) { SituationMap::LineStyle lineStyle; lineStyle.setLineWidth(10); lineStyle.setLineColor(QColor(Qt::red)); rLineObjPtr->setLineStyle(lineStyle); rLineObjPtr->setPositions(SituationMap::convert2D3D(rLine)); rLineObjPtr->updateView(); } else { rLineObjPtr->removeMySelf(); rLineObjPtr = nullptr; } } } } return true; }; rubberband->setAutoDelete(true); rubberband->installExternalUpdate(testParallel); } }; if (retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); #endif } // PlanEditor::buildFileBox( LtsWidgets::LtsBaseLayoutWidget * ltsbaseWidget ) void PlanEditor::buildEditBox(const PlanEditorSession &session, bool retranslate) { LtsWidgets::PluginData pluginAction; pluginAction.menuGroup = tr("Edit"); pluginAction.identifier = QStringLiteral("Undo"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(UNDO_ICON_NAME)); pluginAction.menuText = tr("Undo"); pluginAction.tooltip = tr("Undo last actions. Undo is limited to the last %1 actions").arg(m_internal->m_geoModel->maxUndoSteps()); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onUndo(); }; LtsWidgets::LtsDragIcon *ltsButton = session.addMenuAction(pluginAction); QMenu *undoMenu = new QMenu(); QAction *tmpAction = undoMenu->addAction(tr("Show Undo History")); connect(tmpAction, &QAction::triggered, this, &PlanEditor::onUndoHistory); if (ltsButton) { ltsButton -> setMenu(undoMenu); ltsButton -> setEnabled(false); if (!retranslate) { m_internal->m_ltsItems += ltsButton; m_internal->m_btnUndo = ltsButton; } } pluginAction.identifier = QStringLiteral("Redo"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(REDO_ICON_NAME)); pluginAction.menuText = tr("Redo"); pluginAction.tooltip = tr("Redo last Undo"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onRedo(); }; ltsButton = session.addMenuAction(pluginAction); QMenu *redoMenu = new QMenu(ltsButton); tmpAction = redoMenu->addAction(tr("Show Redo History")); connect(tmpAction, &QAction::triggered, this, &PlanEditor::onRedoHistory); if (ltsButton) { ltsButton -> setMenu(redoMenu); ltsButton -> setEnabled(false); if (!retranslate) { m_internal->m_ltsItems += ltsButton; m_internal->m_btnRedo = ltsButton; } } pluginAction.identifier = QStringLiteral("Copy_Feature"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(COPY_ICON_NAME)); pluginAction.menuText = tr("Copy"); pluginAction.tooltip = tr("Copy selected feature"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onCopy(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Cut_Feature"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(CUT_ICON_NAME)); pluginAction.menuText = tr("Cut"); pluginAction.tooltip = tr("Cut selected feature"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onCut(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Paste_Feature"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(PASTE_ICON_NAME)); pluginAction.menuText = tr("Paste"); pluginAction.tooltip = tr("Paste selected feature"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onPaste(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("MetaData"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DOCUMENT_METADATA_ICON_NAME)); pluginAction.menuText = tr("Meta Data"); pluginAction.tooltip = tr("Show Meta Data of Plan"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onMetaData(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Group_Feature"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(GROUP_SELECTED_ICON_NAME)); pluginAction.menuText = tr("Group"); pluginAction.tooltip = tr("Group selected feature"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onGroup(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Ungroup_Feature"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(UNGROUP_SELECTED_ICON_NAME)); pluginAction.menuText = tr("Ungroup"); pluginAction.tooltip = tr("Ungroup selected feature"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onUngroup(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Delete_Feature"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DELETE_SELECTED_ICON_NAME)); pluginAction.menuText = tr("Delete"); pluginAction.tooltip = tr("Delete selected feature"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onDelete(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("GraphicAttributes"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(ATTR_EDIT_SELECTED_ICON_NAME)); pluginAction.menuText = tr("Graphic Attributes"); pluginAction.tooltip = tr("Edit Attributes of selected feature"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onEditAttributes(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Edit_Feature"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(EDIT_SELECTED_ICON_NAME)); pluginAction.menuText = tr("Edit"); pluginAction.tooltip = tr("Edit geometry of selected feature"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onEditGraphicGeometry(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Move_Feature"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(MOVE_SELECTED_ICON_NAME)); pluginAction.menuText = tr("Move"); pluginAction.tooltip = tr("Move geometry of selected features"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onMoveGraphicGeometry(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Rotate_Feature"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(ROTATE_SELECTED_ICON_NAME)); pluginAction.menuText = tr("Rotate"); pluginAction.tooltip = tr("Rotate geometry of selected feature"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onRotateGraphicGeometry(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Scale_Feature"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(SCALE_SELECTED_ICON_NAME)); pluginAction.menuText = tr("Scale"); pluginAction.tooltip = tr("Scale geometry of selected feature"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onScaleGraphicGeometry(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); } // void PlanEditor::buildEditBox( LtsWidgets::LtsBaseLayoutWidget * ltsbaseWidget ) void PlanEditor::buildCreateBox(const PlanEditorSession &session, bool retranslate) { LtsWidgets::PluginData pluginAction; pluginAction.menuGroup = tr("Create Graphics"); pluginAction.identifier = QStringLiteral("Draw_Line"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DRAW_LINE_ICON_NAME)); pluginAction.menuText = tr("Line"); pluginAction.tooltip = tr("Draw line"); pluginAction.dropHandlerKey = m_internal->m_dropEventManager->add(new DropCreateLine(this, m_internal.get())); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onLineCreateRubberband(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Draw_Polyline"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DRAW_POLYLINE_ICON_NAME)); pluginAction.menuText = tr("Polyline"); pluginAction.tooltip = tr("Draw polyline"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onPolylineCreateRubberband(); }; pluginAction.dropHandlerKey = m_internal->m_dropEventManager->add(new DropCreatePolyLine(this, m_internal.get())); if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Draw_Polygon"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DRAW_POLYGON_ICON_NAME)); pluginAction.menuText = tr("Polygon"); pluginAction.tooltip = tr("Draw polygon"); pluginAction.dropHandlerKey = m_internal->m_dropEventManager->add(new DropCreatePolygon(this, m_internal.get())); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onPolygonCreateRubberband(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Draw_Rectangle"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DRAW_RECTANGLE_ICON_NAME)); pluginAction.menuText = tr("Rectangle"); pluginAction.tooltip = tr("Draw rectangle"); pluginAction.dropHandlerKey = m_internal->m_dropEventManager->add(new DropCreateRectangle(this, m_internal.get())); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onRectangleCreateRubberband(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Draw_SmoothLiné"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DRAW_SPLINE_ICON_NAME)); pluginAction.menuText = tr("Smooth Line"); pluginAction.tooltip = tr("Draw spline based smooth line"); pluginAction.dropHandlerKey = m_internal->m_dropEventManager->add(new DropCreatePolyLine(this, m_internal.get(), true)); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onSmoothLineCreateRubberband(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Draw_Ellipse"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DRAW_ELLIPSE_ICON_NAME)); pluginAction.menuText = tr("Ellipse"); pluginAction.tooltip = tr("Draw ellipse"); pluginAction.dropHandlerKey = m_internal->m_dropEventManager->add(new DropCreateRectangle(this, m_internal.get(), true)); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onEllipseCreateRubberband(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Draw_Symbol"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DRAW_SYMBOL_ICON_NAME)); pluginAction.menuText = tr("Symbol"); pluginAction.tooltip = tr("Draw symbol from symbol dictionary"); pluginAction.dropHandlerKey = m_internal->m_dropEventManager->add(new DropCreateRectangle(this, m_internal.get(), true)); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { onChooseSymbol(); }; LtsWidgets::LtsDragIcon *pbCreateSymbol = session.addMenuAction(pluginAction); pbCreateSymbol->setEnabled(m_internal->m_symbolDictLoaded); if (!retranslate) { m_internal->m_ltsItems += pbCreateSymbol; m_internal->m_symbolWidgets += pbCreateSymbol; // set button to symbolWidgets list to enable the button if dictionary is loaded } pluginAction.identifier = QStringLiteral("Draw_Participants"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DRAW_PARTICIPANT_ICON_NAME)); pluginAction.menuText = tr("Participants"); pluginAction.tooltip = tr("Draw participants from symbol dictionary"); pluginAction.dropHandlerKey = m_internal->m_dropEventManager->add(new DropCreateRectangle(this, m_internal.get(), true)); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { onClearSelection(); if (m_internal->m_symbolDictLoaded) m_internal->m_lookupSymbolManager -> onOpenParticipantDialog(); }; LtsWidgets::LtsDragIcon *pbCreateParticipants = session.addMenuAction(pluginAction); pbCreateParticipants->setEnabled(m_internal->m_symbolDictLoaded); if (!retranslate) { m_internal->m_ltsItems += pbCreateParticipants; m_internal->m_symbolWidgets += pbCreateParticipants; // set button to symbolWidgets list to enable the button if dictionary is loaded } pluginAction.identifier = QStringLiteral("Draw_Text"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DRAW_TEXT_ICON_NAME)); pluginAction.menuText = tr("Text"); pluginAction.tooltip = tr("Draw single text line"); pluginAction.dropHandlerKey = m_internal->m_dropEventManager->add(new DropCreateTextLine(this, m_internal.get())); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onTextLineCreateRubberband(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); pluginAction.identifier = QStringLiteral("Draw_TextBoxPixel"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(DRAW_TEXT_ICON_NAME)); pluginAction.menuText = tr("Pixel-based Text"); pluginAction.tooltip = tr("Draw textbox"); pluginAction.dropHandlerKey = m_internal->m_dropEventManager->add(new DropCreateTextLine(this, m_internal.get())); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onTextBoxPixelCreateRubberband(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); } // void PlanEditor::buildCreateBox( LtsWidgets::LtsBaseLayoutWidget * ltsbaseWidget ) void PlanEditor::buildHelpBox(const PlanEditorSession &session, bool retranslate) { LtsWidgets::PluginData pluginAction; pluginAction.menuGroup = tr("Help"); if (!m_internal->m_session.isLtsSession()) { if (m_internal->m_viewHelp != nullptr) { pluginAction.identifier = QStringLiteral("Help"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(HELP_ICON_NAME)); pluginAction.menuText = tr("View Online Documentation"); pluginAction.tooltip = tr("Shows online documentation"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onViewHelp(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); } pluginAction.identifier = QStringLiteral("About"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(ABOUT_ICON_NAME)); pluginAction.menuText = tr("About"); pluginAction.tooltip = tr("Shows about information"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { PlanEditor::onViewAbout(); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); } //#define TestSymbolDialog #ifdef TestSymbolDialog pluginAction.identifier = QStringLiteral("SymbolDialog"); pluginAction.menuIcon = Utils::getIcon(QLatin1String(ABOUT_ICON_NAME)); pluginAction.menuText = tr("Symbol Dialog"); pluginAction.tooltip = tr("Selected a symbol"); pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { qCritical() << "********************************* show symbol dialog ***************************************************************"; AdvancedSymbology::Gui::BaseSymbolLookupDialog *symbolDialog = new AdvancedSymbology::Gui::BaseSymbolLookupDialog(m_internal->m_map->symbolDictionary(), true, nullptr); connect(symbolDialog, &AdvancedSymbology::Gui::BaseSymbolLookupDialog::lookupApplied, symbolDialog, [this](const QString & sidc) { qCritical() << "********************************* Selected Symbol : " << sidc << " ************************************************"; }); m_internal->m_session.addWidget(symbolDialog , title() , tr("Select Symbol") , LtsWidgets::LayoutArea::eDialogTab false); }; if (!retranslate) session.addMenuAction(pluginAction); else m_internal->m_ltsItems += session.addMenuAction(pluginAction); #endif } // void PlanEditor::buildHelpBox( LtsWidgets::LtsBaseLayoutWidget * ltsbaseWidget ) LtsWidgets::LtsDialog *PlanEditor::buildDebugBox(QWidget *parent) { Dialog *gbLayer = new Dialog(parent, LtsWidgets::ButtonCombination::SeamlessDialog); QVBoxLayout *tabLayout = new QVBoxLayout(gbLayer); gbLayer -> setObjectName(QLatin1String("PlanEditor::PlanDebug")); gbLayer -> setTitle(tr("Plan Debug")); m_internal->m_teDebug = new QTextEdit(gbLayer); m_internal->m_teDebug -> setReadOnly(true); tabLayout->addWidget(m_internal->m_teDebug, 2); return gbLayer; } // PlanEditor::buildDebugBox( QWidget * parent ) bool PlanEditor::eventFilter(QObject *obj, QEvent *event) { if (QEvent::MouseButtonPress == event->type()) { QMouseEvent *mouseEvent = static_cast(event); if (Qt::LeftButton == mouseEvent->button()) { if (SituationMap::RB::Rubberband::activeRubberbands(m_internal->m_map.data()).isEmpty()) { if (!(QGuiApplication::keyboardModifiers().testFlag(Qt::KeyboardModifier::ShiftModifier) || QGuiApplication::keyboardModifiers().testFlag(Qt::KeyboardModifier::ControlModifier))) { switch (m_internal->m_selectionMode) { case SelectionMode::Picking: break; case SelectionMode::PickingAndEdit: break; case SelectionMode::Line: case SelectionMode::Lasso: case SelectionMode::Rectangle: new SelectionWorker(this, m_internal->m_geoModel, m_internal->m_map.data(), m_internal->m_selectionMode, mouseEvent->pos()); return true; } } } } } return QObject::eventFilter(obj, event); } // bool PlanEditor::eventFilter( QObject * obj, QEvent * event ) void PlanEditor::onRubberbandAction() { if (nullptr == m_internal) return; // PlanEditor is expiring const QList &activeRbndList = SituationMap::RB::Rubberband::activeRubberbands(m_internal->m_map.data()); (activeRbndList.isEmpty()) ? onEnablePlanEditor() : onDisablePlanEditor(); } // void PlanEditor::onRubberbandAction() void PlanEditor::onHideDialogTab() { m_internal->m_lookupSymbolManager -> onCloseDialog(); m_internal->m_attributeDialogManager -> onCloseDialog(); } // void PlanEditor::onHideDialogTab void PlanEditor::onShowDialogTab(const SituationMap::GeometryModel::Node &node) { onEnablePlanEditor(); m_internal->m_attributeDialogManager->onOpenDialog(node); // 28.10.2019, Norbert Thoden // RP 553 Schnappschuss: in einem bereits angelegtem Textfeld können keine Änderungen vorgenommen werden.. // getStyleAttributes wird fuer alle PrimitiveTypen aufgerufen: m_internal->m_attributeDialogManager->getStyleAttributes(node); } // PlanEditor::onShowDialogTab void PlanEditor::saveToFile(const QString &filePath) { QFile file(filePath); if (file.open(QIODevice::WriteOnly)) { file.write(m_internal->m_geoModel->toByteArray()); m_internal->m_geoModel->resetModificationCounter(); } else { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Save plan to file \"%1\"failed").arg(filePath), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Saving plan model to file - " << filePath << " - failed."; } } // PlanEditor::saveToFile( const QString & filePath ) void PlanEditor::initializePlan() { ModelTreeWidgetItem::updateTree(m_internal->m_modelTreeWidget, m_internal->m_modelTreeWidget->rootItem(), m_internal->m_map.data(), *(m_internal->m_geoModel)); m_internal->m_geoModelRenderer->render(); initializePlanTextBoxPixel(); // m_internal->m_map->getQGraphicsView() seems to be nullptr , mostly during slotReplaySessionForPlanEditorStarted if (m_internal->m_map && m_internal->m_map->getQGraphicsView() && m_internal->m_map->getQGraphicsView()->viewport()) m_internal->m_map->getQGraphicsView()->viewport()->update(); } // void PlanEditor::initializePlan() void PlanEditor::initializePlanTextBoxPixel() { const QString &strRef = m_internal->m_geoModel->getModelAttribute(SituationMap::GeometryModel::Attribute::MetaPixelBasedTextBoxes).toString(); const QVector &ptbdCont = SituationMap::PixelTextBoxData::pixelTextBoxContainerFromJsonStr(strRef); SituationMap::PixelBasedTextBoxHandler &pixelBasedTextBoxHandler = m_internal->m_map->pixelBasedTextBoxHandler(); const int parentId = PLAN_EDITOR_TEXTBOX_PARENT_ID; pixelBasedTextBoxHandler.removeAllTextBoxesOfParent(parentId); m_internal->resetTextBoxData(); m_internal->m_ourTextBoxIds.clear(); m_internal->m_ourTextBoxIds.reserve(ptbdCont.size()); for (const SituationMap::PixelTextBoxData &ptbd : qAsConst(ptbdCont)) { DEBUG_LOG(m_internal->m_logger) << LOGFUNCTION << " -------- ptbd.toDebugString = " << ptbd.toDebugString(); if (!ptbd.isEmpty()) { SituationMap::PBTB_ID pbtbId(parentId); QTextEdit *newTextEdit = pixelBasedTextBoxHandler.createTextBox(pbtbId, ptbd, true); if (newTextEdit) DEBUG_LOG(m_internal->m_logger) << LOGFUNCTION << " new TextBox created: newTextEdit = " << newTextEdit << ", pbtbId=" << pbtbId << ", ptbd=" << ptbd; else ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " createUpdateTextBox failed" << ", pbtbId=" << pbtbId << ", ptbd=" << ptbd; // add to mgnt: if (!m_internal->m_ourTextBoxIds.contains(pbtbId)) m_internal->m_ourTextBoxIds.append(pbtbId); } } // for // add connect to open TextEditDialog: connect(&pixelBasedTextBoxHandler, &SituationMap::PixelBasedTextBoxHandler::pixelBasedTextBoxDoubleClickEvent, this, &PlanEditor::onTextEditDialogRequested, Qt::UniqueConnection); connect(&pixelBasedTextBoxHandler, &SituationMap::PixelBasedTextBoxHandler::pixelBasedTextBoxGeometryChanged, this, &PlanEditor::onTextEditGeometryChanged, Qt::UniqueConnection); connect(&pixelBasedTextBoxHandler, &SituationMap::PixelBasedTextBoxHandler::pixelBasedTextBoxRemoveRequestedEvent, this, &PlanEditor::onTextEditRemoveRequested, Qt::UniqueConnection); } void PlanEditor::checkFromEmptyPlan() { if (m_internal->m_modelTreeWidget->isEmpty()) { #ifdef SHOW_MODAL_DIALOG_WHEN_OTHER_MODAL_DIALOG_IS_ACTIVE /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * REGG-4219: * On Linux systemes some times a new LtsMessageDialog will appear behind an already existing other modal dialog. As * result the user can't confirm with "Ok" and the progarm seems to hang up * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Warning loading empty plan to PlanEditor."), title()); #endif WARNING_LOG(m_internal->m_logger) << LOGFUNCTION << "Warning loading empty plan to PlanEditor."; } } // void PlanEditor::checkFromEmptyPlan() void PlanEditor::setNameOfPlan(const QString & planName) { m_internal->m_modelTreeWidget->setNameOfPlan(planName); } // void PlanEditor::setNameOfPlan(const QString & planName) bool PlanEditor::setPlan(const QByteArray &xmlContent) { TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " START, xmlContent.size()" << xmlContent.size(); if (xmlContent.isEmpty()) { DEBUG_LOG(m_internal->m_logger) << LOGFUNCTION << " xmlContent is Empty"; onPlanCreate(); return true; } if (!m_internal->m_geoModel->fromByteArray(xmlContent)) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " fromByteArray failed... -> clearModel"; m_internal->m_geoModel->clearModel(); return false; } initializePlan(); // not here, we are inside a Request // checkFromEmptyPlan(); return true; } // void PlanEditor::setPlan( const QByteArray & xmlContent ) bool PlanEditor::importPlan(const QByteArray &xmlContent) { bool importPlanEmpty = xmlContent.isEmpty(); bool status = true; SituationMap::GeometryModel *importedModel = SituationMap::GeometryModelFactory::singleton()->create(); if (!importedModel) { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Memory out of range"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Memory allocation failure. Can't create GeometryModel to import file"; return false; } if (!importPlanEmpty) { importedModel->cleanModel(); status = importedModel->fromByteArray(xmlContent); if (status) { importPlanEmpty = importedModel->isEmpty(); if (!importPlanEmpty) { m_internal->m_geoModel->startUndoBlock(); m_internal->m_geoModel->merge(importedModel, true, true); m_internal->m_geoModel->endUndoBlock(tr("Import from file")); ModelTreeWidgetItem::updateTree(m_internal->m_modelTreeWidget, m_internal->m_modelTreeWidget->rootItem(), m_internal->m_map.data(), *(m_internal->m_geoModel)); m_internal->m_geoModelRenderer->render(); m_internal->m_map->getQGraphicsView()->viewport()->update(); } } } delete importedModel; if (importPlanEmpty) { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Imported plan is empty."), title()); } return status; } // bool PlanEditor::importPlan(const QByteArray &xmlContent) void PlanEditor::loadFromFile(const QString &filePath) { QFile file(filePath); m_internal->m_geoModel->clearModel(); if (file.open(QIODevice::ReadOnly)) { if (m_internal->m_geoModel->fromDevice(&file)) { initializePlan(); checkFromEmptyPlan(); } else { m_internal->m_geoModel->clearModel(); LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Read plan from file '%1' failed").arg(filePath), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Parsing plan model data - " << filePath << " - failed."; } } else { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Open file '%1' failed").arg(filePath), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Open file for reading plan model - " << filePath << " - failed."; } } // PlanEditor::loadFromFile( const QString & filePath ) void PlanEditor::saveSelectedToFile(const QString &filePath) { QFile file(filePath); if (file.open(QIODevice::WriteOnly)) { SituationMap::GeometryModel * exportModel = SituationMap::GeometryModelFactory::singleton()->clone( m_internal->m_geoModel ); Q_ASSERT(exportModel); // ToDo // 1. make copy from m_internal->m_geoModel // 2. remove all node which are not selected // 3. call cleanModel() // 4. write to file class MapObjectsVisitor : public SituationMap::GeometryNodeVisitor { QList m_exportedNodes; QList m_removeNodeList; QList m_removeGroupList; public: MapObjectsVisitor(SituationMap::GeometryModel *geoModel, const QList &exportedNodes) : SituationMap::GeometryNodeVisitor(geoModel) , m_exportedNodes(exportedNodes) { } // MapObjectsVisitor( SituationMap::GeometryModel & geoModel, const QList & exportedNodes) bool apply(SituationMap::GeometryModel *geoModel , const SituationMap::GeometryModel::Node &node , SituationMap::GeometryModel::PrimitiveType primitiveType) override { switch (primitiveType) { case SituationMap::GeometryModel::PrimitiveType::RootNode : break; case SituationMap::GeometryModel::PrimitiveType::Node: { if (!node.hasChildNodes()) { m_removeGroupList << node; } } break; default: { if (geoModel->hasUserData(node)) { bool ok = false; SituationMap::UniqueMapId uid = geoModel->getUserData(node, &ok); if (ok && m_exportedNodes.contains(uid)) { return true; } } m_removeNodeList << node; } break; } return true; } // bool apply( SituationMap::GeometryModel*, const SituationMap::GeometryModel::Node &, SituationMap::GeometryModel::PrimitiveType void cleanup() { for (const auto &node : qAsConst(m_removeNodeList)) m_geometryModel->removePrimitive(node); for (const auto &group : qAsConst(m_removeGroupList)) m_geometryModel->deleteGroup(group); } // void cleanup() }; // MapObjectsVisitor MapObjectsVisitor exportVisitor(exportModel, m_internal->m_selectionList); exportVisitor.traverse(exportModel->getRootNode()); exportVisitor.cleanup(); exportModel->cleanModel(); file.write(exportModel->toByteArray()); file.close(); delete exportModel; } else ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Saving selected plan parts to file - " << filePath << " - failed."; } // PlanEditor::saveSelectedToFile( const QString & filePath ) void PlanEditor::importFromFile(const QString &filePath) { QFile file(filePath); if (file.open(QIODevice::ReadOnly)) { const QByteArray & xmlContent = file.readAll(); if (!importPlan(xmlContent)) { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Parsing plan model '%1' failed").arg(filePath), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Parsing plan model data - " << filePath << " - failed."; } } else { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Open plan file '%1' failed").arg(filePath), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Open file for reading plan model - " << filePath << " - failed."; } } // PlanEditor::importFromFile( const QString & filePath ) bool PlanEditor::isSymbolModelValid() const { // if (m_internal->m_lookupSymbolManager->getBaseSymbolModelEsri()) // return (!m_internal->m_lookupSymbolManager->getBaseSymbolModelEsri()->isEmpty()); // return false; return true; } void PlanEditor::setAllSymbolFavourites(const QString &xmlContent) { m_internal->m_favouritesActionManager->symbolFavourites()->contentFromString(xmlContent); } // void PlanEditor::setAllSymbolFavourites( const QString & xmlContent ) QString PlanEditor::allSymbolFavouritesToString() const { return m_internal->m_favouritesActionManager->symbolFavourites()->toString(); } // QString PlanEditor::allSymbolFavouritesToString() const SituationMap::UniqueMapId PlanEditor::getIdFromNode(const SituationMap::GeometryModel::Node &p) const { return m_internal->m_geoModelRenderer->getIdFromNode(p); } // SituationMap::UniqueMapId PlanEditor::getIdFromNode(const SituationMap::GeometryModel::Node & p) const void PlanEditor::setSelected(SituationMap::UniqueMapId uid, bool on, bool enableShowDialogTab) { SituationMap::AbstractMapObject *mapObj = m_internal->m_map->objectFactory().idToMapObject(uid); if (!mapObj) return; DEBUG_LOG(m_internal->m_logger) << LOGFUNCTION << " setSelected... uid=" << uid << ", on=" << on << ", enableShowDialogTab=" << enableShowDialogTab; mapObj->setSelected(on); mapObj->updateView(); SituationMap::GeometryModel::Node modelPrimitive = m_internal->m_geoModelRenderer->getAssociatedNode(mapObj); if (on) { if (enableShowDialogTab) onShowDialogTab(modelPrimitive); if (!m_internal->m_selectionList.contains(uid)) m_internal->m_selectionList.append(uid); } else { if (m_internal->m_selectionList.contains(uid)) m_internal->m_selectionList.removeOne(uid); if (m_internal->m_selectionList.size() == 1) { // Can this even happen? mapObj = m_internal->m_map->objectFactory().idToMapObject(m_internal->m_selectionList.first()); if (mapObj) { modelPrimitive = m_internal->m_geoModelRenderer->getAssociatedNode(mapObj); onShowDialogTab(modelPrimitive); } } if (m_internal->m_selectionList.isEmpty()) { onHideDialogTab(); } } ModelTreeWidgetItem::selectItem(m_internal->m_modelTreeWidget->rootItem(), modelPrimitive, on); } // PlanEditor::setSelected(SituationMap::UniqueMapId, bool on ) void PlanEditor::setSelected(const SituationMap::GeometryModel::Node &node, bool on, bool enableShowDialogTab) { setSelected(node, on, true, enableShowDialogTab); } void PlanEditor::setSelected(const SituationMap::GeometryModel::Node &node, bool on, bool checkParents, bool enableShowDialogTab) { if (!node.isValid()) return; if (checkParents) { const SituationMap::GeometryModel::Node &parent = m_internal->m_geoModel->isInGroup(node); if (parent.isValid()) setSelected(parent, on, true, enableShowDialogTab); } SituationMap::GeometryModel::PrimitiveType nodeType = m_internal->m_geoModel->getType(node); if ((SituationMap::GeometryModel::PrimitiveType::Node == nodeType) || (SituationMap::GeometryModel::PrimitiveType::RootNode == nodeType)) { const SituationMap::GeometryModel::NodeList &childNodes = m_internal->m_geoModel->getGroupPrimitives(node); for (const SituationMap::GeometryModel::Node &childNode : childNodes) setSelected(childNode, on, false, enableShowDialogTab); } else { bool ok = false; SituationMap::UniqueMapId uid = m_internal->m_geoModel->getUserData(node, &ok); if (ok) setSelected(uid, on, enableShowDialogTab); } } // PlanEditor::setSelected(const SituationMap::GeometryModel::Node & node) bool PlanEditor::isSelected(SituationMap::UniqueMapId uid) { return m_internal->m_selectionList.contains(uid); } // bool PlanEditor::isSelected( SituationMap::UniqueMapId ) bool PlanEditor::isSelected(const SituationMap::GeometryModel::Node &node) { SituationMap::GeometryModel::PrimitiveType nodeType = m_internal->m_geoModel->getType(node); if ((SituationMap::GeometryModel::PrimitiveType::Node == nodeType) || (SituationMap::GeometryModel::PrimitiveType::RootNode == nodeType)) { const SituationMap::GeometryModel::NodeList &childNodes = m_internal->m_geoModel->getGroupPrimitives(node); for (const SituationMap::GeometryModel::Node &childNode : childNodes) { if (isSelected(childNode)) return true; } return false; } else { bool ok = false; SituationMap::UniqueMapId uid = m_internal->m_geoModel->getUserData(node, &ok); if (ok) return isSelected(uid); } return false; } // bool PlanEditor::isSelected( const SituationMap::GeometryModel::Node & node ) void PlanEditor::addStyleAttributes(const SituationMap::GeometryModel::Node &node, bool prepareUndo, bool changedAttrOnly) { m_internal->m_attributeDialogManager->addStyleAttributes(node, prepareUndo, changedAttrOnly); } // PlanEditor::addStyleAttributes( const SituationMap::GeometryModel::Node & node, bool prepareUndo ) void PlanEditor::getSymbolAttributes(const SituationMap::GeometryModel::Node &node) { SituationMap::GeometryModel::PrimitiveType primitiveType = m_internal->m_geoModel->getType(node); const QList &supportedStyleAttributes = m_internal->m_geoModel->supportedAttributes(primitiveType); for (auto attr : supportedStyleAttributes) { QVariant attrValue = m_internal->m_geoModel->getAttribute(node, attr); switch (attr) { case SituationMap::GeometryModel::Attribute::SIDC : { m_internal->m_symbolCurrentSidc = attrValue.toString(); // ToDo : Attribute Fenster aktualisieren } break; default : break; } } } // PlanEditor::getSymbolAttributes( const SituationMap::GeometryModel::Node & node ) SituationMap::GeometryModel::Node PlanEditor::addGeoModelPrimitive(SituationMap::GeometryModel::PrimitiveType type , const QList &points , const QList> &attributes , int userData , const QString &textData , SituationMap::GeometryModel::HorizontalAlign textAlign) { const SituationMap::GeometryModel::Node &node = m_internal->m_geoModel->addPrimitive(type , points , attributes , userData , textData , textAlign); addStyleAttributes(node, false, false); return node; } // SituationMap::GeometryModel::Node PlanEditor::addGeoModelPrimitive(...) void PlanEditor::removeFeature(SituationMap::UniqueMapId itemId, bool prepareUndo) { const SituationMap::GeometryModel::Node &node = m_internal->m_geoModel->findPrimitive(itemId); if (!node.isValid()) return; if (prepareUndo) { m_internal->m_geoModel->startUndoBlock(); { m_internal->m_geoModel->removePrimitive(node); } m_internal->m_geoModel->endUndoBlock(tr("delete single feature", TR_UNDO_TOKEN)); } else m_internal->m_geoModel->removePrimitive(node); } // PlanEditor::removeFeature( SituationMap::UniqueMapId id, bool prepareUndo ) void PlanEditor::updateSelectedFeature(const QString &undoToken) { if (m_internal->m_readStyleAttributesFromModel) return; if (m_internal->m_selectionList.isEmpty()) return; m_internal->m_geoModel->startUndoBlock(); { for (SituationMap::UniqueMapId uid : qAsConst(m_internal->m_selectionList)) { const SituationMap::GeometryModel::Node &modelPrimitive = m_internal->m_geoModel->findPrimitive(uid); if (modelPrimitive.isValid()) addStyleAttributes(modelPrimitive, false); } } m_internal->m_geoModel->endUndoBlock(undoToken.isEmpty() ? tr("modify feature style", TR_UNDO_TOKEN) : undoToken); emit modifyFeatureStyle(undoToken); } // PlanEditor::updateSelectedFeature( const QString & undoToken ) void PlanEditor::abortCurrentBboxRubberband() { if (m_internal->m_currentBboxRubberband) { m_internal->m_currentBboxRubberband->deleteMySelf(); m_internal->m_currentBboxRubberband = nullptr; } } // void PlanEditor::abortCurrentBboxRubberband() template void PlanEditor::createTransformRubberband() { // start translate rubberband for move, scale and rotate if possible // but first check the bounding box of selection SituationMap::RB::RubberbandTransform *geoPlacerRB = GeometryPlacer::create(this, m_internal->m_map.data(), m_internal->m_geoModel, m_internal->m_selectionList, this); if (geoPlacerRB) { // Ok, bounding box has a valid size and GeometryPlacer is valid and has a rubberband onHideDialogTab(); // get more size on map widget for the map m_internal->m_currentBboxRubberband = geoPlacerRB; SituationMap::LayerParameter lp = m_internal->m_map->layerFactory().layerParameter(m_internal->m_logicalLayername); int opacity = lp.opacity; lp.opacity *= 0.3; m_internal->m_map->layerFactory().setLayerParameter(m_internal->m_logicalLayername, lp); lp.opacity = opacity; connect(geoPlacerRB, &SituationMap::RB::Rubberband::finished, [this, lp](SituationMap::RB::RubberbandBase *) { onEditAttributes(); if (m_internal) { m_internal->m_currentBboxRubberband = nullptr; m_internal->m_map->layerFactory().setLayerParameter(m_internal->m_logicalLayername, lp); } // 17.09.19, N. Thoden, (Qt 5.12.3) // Maybe calling setFocus here is to early. // Race condition between dialog/widget created by former onEditAttributes() // and this setFocus(): m_internal->m_map->setFocus(); }); connect(this, &PlanEditor::modifyFeatureStyle, geoPlacerRB, [geoPlacerRB](const QString &) { GeometryPlacer *geoPlacer = static_cast*>(geoPlacerRB->userData()); if (nullptr == geoPlacer) return; geoPlacer->changeStyleAttributes(); }); } } // template void PlanEditor::createTransformRubberband() void PlanEditor::onTransformRubberband() { createTransformRubberband(); } void PlanEditor::onTextEditDialogRequested(const SituationMap::PBTB_ID &pbtbId) { DEBUG_LOG(m_internal->m_logger) << LOGFUNCTION << ", clickedObj = pbtbId " << pbtbId; // reuse or create EditDialog QWidget *parent = m_internal->m_session.topLevelWidget(); if (m_internal->m_textEditorDialog && m_internal->m_textEditorDialog->hasUnsavedChanges()) { const LtsWidgets::Button answer = LtsWidgets::LtsMessageDialog::askApplyDiscardCancel(m_internal->m_textEditorDialog); switch (answer) { case LtsWidgets::Button::Apply: m_internal->m_textEditorDialog->apply(); break; case LtsWidgets::Button::Cancel: // do not continue return; case LtsWidgets::Button::Discard: if (m_internal->m_textBoxTextEdit) m_internal->m_textBoxTextEdit->setHtml(m_internal->m_htmlTextForCancelation); break; default: break; } // switch } // if if (!m_internal->m_textEditorDialog) { m_internal->m_textEditorDialog = new LtsWidgets::LtsTextEditDialog(parent); m_internal->m_textEditorDialog->enableBackgroundColor(true); m_internal->m_textEditorDialog->setOnlineHelpFilename(QStringLiteral("Plan_Editor-Create_Graphics")); m_internal->m_session.addOrReplaceWidget(m_internal->m_textEditorDialog, QStringLiteral("PlanEditor"), // this->title()??? QStringLiteral("PixelBasedTextBoxAttributes"), LtsWidgets::LayoutArea::eDialogTab, true); //setClosable } m_internal->m_session.focusTab(m_internal->m_textEditorDialog, LtsWidgets::LayoutArea::eDialogTab, QStringLiteral("PlanEditor"), QStringLiteral("PixelBasedTextBoxAttributes")); SituationMap::PixelBasedTextBoxHandler &pixelBasedTextBoxHandler = m_internal->m_map->pixelBasedTextBoxHandler(); // // NOW store WHICH pbtbId and textEdit is linked to LtsTextEditDialog: // QTextEdit *currentTe = pixelBasedTextBoxHandler.getTextBox(pbtbId); m_internal->m_textBoxIdInEditing = pbtbId; m_internal->m_textBoxTextEdit = currentTe; if (!pixelBasedTextBoxHandler.setSelected(m_internal->m_textBoxIdInEditing)) ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " setSelected failed "; // transfer current htmlText to EditDialog: // m_internal->m_textBoxTextEdit may be null, use pixelBasedTextBoxHandler: const SituationMap::PixelTextBoxData &data = pixelBasedTextBoxHandler.getPixelTextBoxData(pbtbId); if (!data.isEmpty()) { const QString ¤tText = data.m_htmlText; // m_htmlText ist updated during getPixelTextBoxData() m_internal->m_textEditorDialog->setHtml(currentText); m_internal->m_textEditorDialog->setInitialBackgroundColor(data.m_backgroundColor); m_internal->m_textEditorDialog->__unchange__(); m_internal->m_htmlTextForCancelation = currentText; } // connections: connect(m_internal->m_textEditorDialog, &LtsWidgets::LtsTextEditDialog::signalTextChanged, this, &PlanEditor::onTextBoxPixelTextChanged, Qt::UniqueConnection); connect(m_internal->m_textEditorDialog, &LtsWidgets::LtsTextEditDialog::signalBackgroundColorChanged, this, &PlanEditor::onTextBoxPixelBackgroundColorChanged, Qt::UniqueConnection); connect(m_internal->m_textEditorDialog, &LtsWidgets::LtsTextEditDialog::signalTextApplied, this, &PlanEditor::onTextBoxPixelTextApplied, Qt::UniqueConnection); connect(m_internal->m_textEditorDialog, &LtsWidgets::LtsTextEditDialog::signalDialogCanceled, this, &PlanEditor::onTextBoxPixelCanceled, Qt::UniqueConnection); connect(m_internal->m_textEditorDialog, &LtsWidgets::LtsTextEditDialog::destroyed, this, &PlanEditor::onTextBoxPixelClosed, Qt::UniqueConnection); // we use the QTextEdit::destroyed signal as signal when the pixelbased textbox is closed connect(currentTe, &QTextEdit::destroyed, this, [this /*, pbtbId*/] (QObject *) { // [GUEZE-2947] Undo,Redo fuer PixelBasedTextBoxen delete m_internal->m_textEditorDialog; // [GUEZE-2947] Undo,Redo fuer PixelBasedTextBoxen // if (!m_internal->m_textEditorDialog.isNull()) // { // if (pbtbId == m_internal->m_textBoxIdInEditing) // if it is the focused textbox we close the text editor // { // m_internal->m_textEditorDialog->__unchange__(); // unchange, so we close it, even if there are unsaved changes // m_internal->m_textEditorDialog->closeRequest(); // } // } }); } void PlanEditor::onTextEditGeometryChanged(const SituationMap::PBTB_ID &pbtbId) { // will be called by events: a) geometryChanged or b) positionChanged TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " START " << pbtbId; if (!m_internal->m_textBoxNotifyTimer) { m_internal->m_textBoxNotifyTimer = new QTimer(this); m_internal->m_textBoxNotifyTimer->setSingleShot(true); m_internal->m_textBoxNotifyTimer->setInterval(100); connect(m_internal->m_textBoxNotifyTimer, &QTimer::timeout, this, [this] () { writeTextBoxDataToModel(); }); } m_internal->m_textBoxNotifyTimer->start(); TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " END"; } void PlanEditor::onTextEditRemoveRequested(const SituationMap::PBTB_ID &pbtbId) { TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " START " << pbtbId; if (!pbtbId.isValid()) return; // to ensure, that button save", save as" will save the correct data: m_internal->m_ourTextBoxIds.removeAll(pbtbId); QTimer::singleShot(100, this, [this, pbtbId] () { SituationMap::PixelBasedTextBoxHandler &pixelBasedTextBoxHandler = m_internal->m_map->pixelBasedTextBoxHandler(); if (!pixelBasedTextBoxHandler.removeTextBox(pbtbId)) ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " removeTextBox failed: pbtbId=" << pbtbId; writeTextBoxDataToModel(); }); TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " END"; } void PlanEditor::onMapObjectClicked(const SituationMap::Map::RefPointerMapObjectList &mapObjList) { if (mapObjList.isEmpty()) onHideDialogTab(); if (!m_internal->m_geoModel) return; if (!m_internal->m_geoModelRenderer) return; if (m_internal->m_currentBboxRubberband) return; if (!SituationMap::RB::Rubberband::activeRubberbands(m_internal->m_map.data()).isEmpty()) return; if (!(QGuiApplication::keyboardModifiers().testFlag(Qt::KeyboardModifier::ShiftModifier) || QGuiApplication::keyboardModifiers().testFlag(Qt::KeyboardModifier::ControlModifier))) { if (mapObjList.isEmpty()) onClearSelection(); } if ((SelectionMode::Picking == m_internal->m_selectionMode) || QGuiApplication::keyboardModifiers().testFlag(Qt::KeyboardModifier::ShiftModifier) || QGuiApplication::keyboardModifiers().testFlag(Qt::KeyboardModifier::ControlModifier)) { doPicking(mapObjList); return; } if (SelectionMode::PickingAndEdit == m_internal->m_selectionMode) { doPicking(mapObjList); onEditGraphicGeometry(); } } // void PlanEditor::onMapObjectClicked( const SituationMap::Map::RefPointerMapObjectList & mapObjList, const QPoint & ) void PlanEditor::doPicking(const SituationMap::Map::RefPointerMapObjectList &mapObjList) { DEBUG_LOG(m_internal->m_logger) << LOGFUNCTION << " XXXX mapObjList.size() = " << mapObjList.size() << ", m_internal->m_selectionList.size() = " << m_internal->m_selectionList.size(); // Szenario: // click an Ets symbol on the map: PlanEditor::doPicking open an EditDialog (occurance#1), // PlanEditor::setSelected renews the same dialog (occurance#2), finally createTransformRubberband destroys the occurance#2 // Rbd::finished opens the same dialog (occurance#3), setSidc recognize an Ets symbol -> dialog will be closed.... // Reduce the construction/destruction to rblock seduce flickering, by adding bool enableShowDialogTab to setSelected. // N. Thoden, 08/2019: bool bAddSelection = (QGuiApplication::keyboardModifiers().testFlag(Qt::KeyboardModifier::ShiftModifier) || QGuiApplication::keyboardModifiers().testFlag(Qt::KeyboardModifier::ControlModifier)); // do we have to createTransformRubberband? bool bCreateTransformRubberband = !bAddSelection && (m_internal->m_selectionMode != SelectionMode::PickingAndEdit) && mapObjList.size() >= 1; // Maybe: // Do the *de*selection first: // If we change on item to be selected now, a some kind of edit dialog will be opened. // Unfortunately they emit a huge number of signals (e.g. SymbolModificationWidget::changed) // If there ist still an item in the m_internal->m_selectionList, this will be infected by mistake) // But: The selectionChanged of the ModelTreeWidgetItem is ordered be the model // So if the selection (by clicking the ModelView) changes from a 3rd obj to the 1st, // the selection=true will be set to the 1st AND THEN the selection=false will be set to the 3rd. // N. Thoden, 08/2019: for (auto mapObjPtr : mapObjList) { // NOTICE_LOG(m_internal->m_logger) << LOGFUNCTION << " XXXX mapObjPtr->debugInfo()... " << mapObjPtr->debugInfo() << " m_internal->m_selectionList.isEmpty() = " << m_internal->m_selectionList.isEmpty(); const SituationMap::GeometryModel::Node &modelPrimitive = m_internal->m_geoModelRenderer->getAssociatedNode(mapObjPtr.get()); if (modelPrimitive.isValid()) { // if (m_internal->m_selectionList.isEmpty()) // { // NOTICE_LOG(m_internal->m_logger) << LOGFUNCTION << " XXXX onShowDialogTab.."; // onShowDialogTab(modelPrimitive); // } setSelected(modelPrimitive, true, !bCreateTransformRubberband); // enableShowDialogTab = !bCreateTransformRubberband; } } DEBUG_LOG(m_internal->m_logger) << LOGFUNCTION << " XXXX bCreateTransformRubberband= " << bCreateTransformRubberband; if (bCreateTransformRubberband) createTransformRubberband(); } // void PlanEditor::doPicking( const SituationMap::Map::RefPointerMapObjectList & mapObjList ) void PlanEditor::onContextMenuRequested( const SituationMap::Map::RefPointerMapObjectList&, const QPointF &viewportPosition) { if (m_internal->m_session.isLtsSession()) return; // context is handled by SituationMapGuiPlugin if (nullptr == m_internal->m_map) return; QMenu contextMenu; QAction *selectionByArea = contextMenu.addAction(tr("Area Selection")); QAction *scaleByArea = contextMenu.addAction(tr("Area Scale")); QAction *selectedItem = contextMenu.exec(m_internal->m_map->mapToGlobal(viewportPosition.toPoint())); if (selectedItem == selectionByArea) { if (nullptr != m_internal->m_map) { m_internal->m_map->pickMapObjectsArea(); } return; } if (selectedItem == scaleByArea) { if (nullptr != m_internal->m_map) { m_internal->m_map->scaleByArea(); } return; } } // void PlanEditor::onContextMenuRequested( const SituationMap::Map::RefPointerMapObjectList &mapObjList, const QPointF &viewportPosition) void PlanEditor::onViewAbout() { abortCurrentBboxRubberband(); QString aboutText = tr("Plan Editor application to create and modify excercise plans."); aboutText = QStringLiteral("%1\nBuild: %2\nESRI ArcGISRuntime Release %3").arg(aboutText).arg( QLatin1String(REVISION) ).arg(QLatin1String(ACRGIS_VERSION_STR)); QMessageBox::about(m_internal->m_session.topLevelWidget(), title(), aboutText); // ToDo: Implement me } // PlanEditor::onViewAbout() void PlanEditor::onViewHelp() { abortCurrentBboxRubberband(); if (nullptr == m_internal->m_viewHelp) return; m_internal->m_viewHelp->show(); } // PlanEditor::onViewHelp() void PlanEditor::onShowHelp() { if (m_internal->m_session.ltsSession()) { m_internal->m_session.ltsSession()->slotShowOnlineHelp(QStringLiteral("Plan_Editor-Application")); } } // PlanEditor::onShowHelp() void PlanEditor::installSaveFunction(const std::function &saveFunction) { m_internal->m_saveFunction = saveFunction; } // void PlanEditor::installSaveFunction(std::function * saveFunction) void PlanEditor::onQuit() { onAskSaveChanges(); emit quitPlanEditor(); } // PlanEditor::onQuit void PlanEditor::onAskSaveChanges() { if (m_internal->m_geoModel->modificationCounter() < 1) return; if (LtsWidgets::Button::Yes == LtsWidgets::LtsMessageDialog::showMessage( topLevelWidget() , tr("PlanEditor has unsaved changes. Do you want to save the plan?") , title() + QStringLiteral(" : ") + tr("Save unsaved Changes") , LtsWidgets::Behaviour::Modal , LtsWidgets::Button::Yes | LtsWidgets::Button::No)) { if (nullptr == m_internal->m_saveFunction) { onPlanSave(); if (m_internal->m_geoModel->modificationCounter() < 1) { const QString & msg = tr("Saving plan successfully."); LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), msg, title()); } } else m_internal->m_saveFunction(); } else m_internal->m_geoModel->resetModificationCounter(); } // PlanEditor::onAskSaveChanges void PlanEditor::onAskRejectChanges(bool &reject) { INFO_LOG(m_internal->m_logger) << LOGFUNCTION << " START"; reject = true; if (m_internal->m_geoModel->modificationCounter() < 1) return; reject = (LtsWidgets::Button::Yes == LtsWidgets::LtsMessageDialog::showMessage( topLevelWidget() , tr("PlanEditor has unsaved changes. Do you want to discard this changes?") , title() + QStringLiteral(" : ") + tr("Unsaved Changes") , LtsWidgets::Behaviour::Modal , LtsWidgets::Button::Yes | LtsWidgets::Button::No)); if (reject) { // discard: m_internal->m_geoModel->resetModificationCounter(); m_internal->removeAutosaveFile(); } return; } void PlanEditor::onSignalClose() { INFO_LOG(m_internal->m_logger) << LOGFUNCTION << " START, isSnapshot=" << m_internal->m_session.isSnapshot(); if (m_internal->m_session.isSnapshot()) { // PlanEditorData is stored in OPERATIONAL_Layer, but SnapshotData is stored in OVERLAY_Layer // see PlanEditor::init(). // Avoid, that the GraphicsLayer are destroyed via MapImpl::fini() _before_ the AbstractGraphicalObjects are destroyed. // That would lead to segFault.... // cleanModel and destroy the coresponding MapObjects/GraphicalObjects: if (m_internal->m_geoModel) m_internal->m_geoModel->cleanModel(); } INFO_LOG(m_internal->m_logger) << LOGFUNCTION << " END, isSnapshot=" << m_internal->m_session.isSnapshot(); } void PlanEditor::onMetaData() { MetaDataDialog *dlg = new MetaDataDialog(this, m_internal->m_geoModel, m_internal->m_map.data(), m_internal->m_map); m_internal->m_session.addWidget(dlg, title(), QLatin1String("MetaData"), LtsWidgets::LayoutArea::eDialogTab, false); } // void PlanEditor::onMetaData() void PlanEditor::onPlanPrint() { abortCurrentBboxRubberband(); QSharedPointer printer(new QPrinter(QPrinter::PrinterMode::HighResolution)); QPrintDialog printDialog(printer.data(), topLevelWidget()); // choose a printer printer->setOrientation(QPrinter::Orientation::Landscape); if (printDialog.exec() == QDialog::Accepted) { QEventLoop eventLoop; QMetaObject::Connection slotMetaObj = connect(m_internal->m_map.data() , &SituationMap::Map::imageCompleted , this , [this, &printer, &eventLoop](const QUuid &, const QImage & image) { // now we have a nice image of our plan, next use use Qt Printer Support to send it to a printer const QString &title = m_internal->m_geoModel->getModelAttribute(SituationMap::GeometryModel::Attribute::MetaDataTitle).toString(); const QString &author = m_internal->m_geoModel->getModelAttribute(SituationMap::GeometryModel::Attribute::MetaDataAuthor).toString(); const QString &leftFooter = author; const QString &middleFooter = QString(); const QString &rightFooter = QDateTime::currentDateTime().toString(QStringLiteral("d.M.yyyy h:mm")); // current date and time Q_UNUSED(title); Q_UNUSED(author); Q_UNUSED(leftFooter); Q_UNUSED(middleFooter); Q_UNUSED(rightFooter); QTextCharFormat defaultFontFormat; defaultFontFormat.setForeground(QBrush(QColor("black"))); defaultFontFormat.setFontPointSize(14); QTextCharFormat largeFontFormat = defaultFontFormat; largeFontFormat.setFontWeight(QFont::Bold); largeFontFormat.setFontPointSize(28); const QFont &defaultFont = defaultFontFormat.font(); int marginInt = defaultFont.pixelSize(); const QSizeF pageSize = printer->paperRect().size(); const QSizeF drawingSize = pageSize - QSizeF(2 * marginInt, 5 * marginInt) * 1.5; QMarginsF margins; margins.setLeft(marginInt); margins.setTop(marginInt); printer->setPageMargins(margins); printer->setCreator(QStringLiteral("RDE-") + qAppName()); printer->setDocName(title); QTextDocument document; // use a QTextDocument to add some text and place the map image QPainter painter(printer.data()); // init painter to the choosen printer const QUrl imageUrl(QLatin1String("PlanEditor://sitMapScreenshot.png")); document.addResource(QTextDocument::ImageResource, imageUrl, QVariant(image)); qreal width = drawingSize.width() * 0.9; qreal height = drawingSize.height() * 0.9; QString html; html += QStringLiteral("\n"); html += QStringLiteral("
\n"); html += QStringLiteral("

\n"); html += title; html += QStringLiteral("

\n"); html += QStringLiteral("

\n").arg(width); html += QStringLiteral("
\n"); html += QStringLiteral(" \n").arg(QStringLiteral("PlanEditor://sitMapScreenshot.png")).arg(width).arg(height); html += QStringLiteral("

\n"); html += QStringLiteral("
\n").arg(width); html += QStringLiteral(" \n").arg(width); html += QStringLiteral(" \n"); html += QStringLiteral(" \n"); html += QStringLiteral(" \n"); html += QStringLiteral(" \n"); html += QStringLiteral(" \n"); html += QStringLiteral("
\n"); html += leftFooter; html += QStringLiteral(" \n"); html += middleFooter; html += QStringLiteral(" \n"); html += rightFooter; html += QStringLiteral("
\n"); html += QStringLiteral("\n"); document.setHtml(html); // finally: document.drawContents(&painter); // #define DEBUG_MSGBOX #ifdef DEBUG_MSGBOX html = QStringLiteral("\n"); html += QStringLiteral("
\n"); html += QStringLiteral("

\n"); html += title + tr("Jörg"); html += QStringLiteral("

\n"); html += QStringLiteral("

\n"); html += QStringLiteral("

Alle Kühe essen Heu Cälber mögen besseres Futter. \n"); html += QStringLiteral("Teigwaren heißen Teigwaren weil Teigwaren Teig waren.

\n"); static QMessageBox *box = new QMessageBox(); box->setAttribute(Qt::WA_DeleteOnClose, true); box->setTextFormat(Qt::RichText); box->addButton(new QPushButton(tr("close"), box), QMessageBox::AcceptRole); box->setText(html); box->show(); #endif eventLoop.quit(); }); // lambda [this,&printer,&eventLoop,&slotMetaObj]( const QString & pdfFilePath ) if (m_internal->m_map->toImage().isNull()) { disconnect(slotMetaObj); QString msg = QObject::tr("Creating map image for printing failed"); LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), msg, this->title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << msg; return; } eventLoop.exec(); // wait for finish printing disconnect(slotMetaObj); } } // PlanEditor::onPlanPrint void PlanEditor::onPlanReset() { qDebug() << LOGFUNCTION << " START"; if (m_internal->m_geoModel == nullptr) return; m_internal->m_geoModel->clearModel(); setNameOfPlan(QString()); initializePlan(); } // PlanEditor::onPlanReset() void PlanEditor::onPlanCreate() { abortCurrentBboxRubberband(); onAskSaveChanges(); onPlanReset(); onMetaData(); } // PlanEditor::onPlanCreate static QString addPlanFileSuffix(const QString &filePath, const QString &selectedNameFilter) { QString defaultSuffix(QStringLiteral(".plan")); // qCritical() << selectedNameFilter; QStringRef suffix; int suffixDefStart = selectedNameFilter.lastIndexOf(QLatin1Char('(')) + 1; int suffixDefEnd = selectedNameFilter.lastIndexOf(QLatin1Char(')')) - 1; if (suffixDefStart < suffixDefEnd) { const QStringRef &suffixFilter = selectedNameFilter.midRef(suffixDefStart, suffixDefEnd - suffixDefStart + 1); const QVector &suffixVector = suffixFilter.split(QLatin1Char(' '), QString::SkipEmptyParts); if (!suffixVector.isEmpty()) { const QStringRef &filter = suffixVector.first(); int indexOfDot = filter.lastIndexOf(QLatin1Char('.')); if (indexOfDot >= 0) suffix = filter.right(filter.length() - indexOfDot); } } // qCritical() << suffix; if (suffix.isEmpty()) suffix = QStringRef(&defaultSuffix); if ((suffix == QStringLiteral(".*")) || (filePath.endsWith(suffix))) return filePath; return filePath + suffix; } // static QString addPlanFileSuffix( const QString & filePath, const QString & selectedNameFilter ) void PlanEditor::onPlanLoad() { abortCurrentBboxRubberband(); if (m_internal->m_geoModel == nullptr) return; onAskSaveChanges(); m_internal -> m_fileDialog -> setWindowTitle(tr("Plan Editor - Select File to Open")); m_internal -> m_fileDialog -> setAcceptMode(QFileDialog::AcceptMode::AcceptOpen); m_internal -> m_fileDialog -> setDirectory(planDefaultDirectory()); if (!m_internal->m_currentFileName.isEmpty()) m_internal -> m_fileDialog -> selectFile(m_internal->m_currentFileName); if (m_internal->m_fileDialog->exec() == QDialog::DialogCode::Accepted) { const QStringList &fileList = m_internal->m_fileDialog->selectedFiles(); if (!fileList.isEmpty()) { m_internal->m_currentFileName = fileList.first(); loadFromFile(m_internal->m_currentFileName); QFileInfo fileInfo(m_internal->m_currentFileName); setNameOfPlan(fileInfo.baseName()); } } } // PlanEditor::onPlanLoad void PlanEditor::onPlanSave() { abortCurrentBboxRubberband(); if (m_internal->m_geoModel == nullptr) return; if (!m_internal->m_currentFileName.isEmpty()) { QFileInfo fileInfo(m_internal->m_currentFileName); if (fileInfo.exists()) { saveToFile(fileInfo.absoluteFilePath()); return; } } onPlanSaveAs(); } // PlanEditor::onPlanSave void PlanEditor::onPlanSaveAs() { abortCurrentBboxRubberband(); if (m_internal->m_geoModel == nullptr) return; m_internal -> m_fileDialog -> setWindowTitle(tr("Plan Editor - Select File to Store current Plan")); m_internal -> m_fileDialog -> setAcceptMode(QFileDialog::AcceptMode::AcceptSave); m_internal -> m_fileDialog -> setDirectory(planDefaultDirectory()); if (!m_internal->m_currentFileName.isEmpty()) m_internal -> m_fileDialog -> selectFile(m_internal->m_currentFileName); if (m_internal->m_fileDialog->exec() == QDialog::DialogCode::Accepted) { const QStringList &fileList = m_internal->m_fileDialog->selectedFiles(); if (!fileList.isEmpty()) { m_internal->m_currentFileName = addPlanFileSuffix(fileList.first(), m_internal->m_fileDialog->selectedNameFilter()); saveToFile(m_internal->m_currentFileName); QFileInfo fileInfo(m_internal->m_currentFileName); setNameOfPlan(fileInfo.baseName()); m_internal->removeAutosaveFile(); } } } // PlanEditor::onPlanSaveAs void PlanEditor::onPlanSaveSelected() { abortCurrentBboxRubberband(); if (m_internal->m_geoModel == nullptr) return; m_internal -> m_fileDialog -> setWindowTitle(tr("Plan Editor - Select File to Store Selected Plan Items")); m_internal -> m_fileDialog -> setAcceptMode(QFileDialog::AcceptMode::AcceptSave); m_internal -> m_fileDialog -> setDirectory(planDefaultDirectory()); m_internal -> m_fileDialog -> selectFile(QString()); if (m_internal->m_fileDialog->exec() == QDialog::DialogCode::Accepted) { const QStringList &fileList = m_internal->m_fileDialog->selectedFiles(); if (!fileList.isEmpty()) { m_internal->m_currentFileName = addPlanFileSuffix(fileList.first(), m_internal->m_fileDialog->selectedNameFilter()); saveSelectedToFile(m_internal->m_currentFileName); } } } // PlanEditor::onPlanSaveSelected void PlanEditor::onPlanAutoSave() { if (m_internal->m_geoModel == nullptr) return; if (m_internal->m_geoModel->modificationCounter() < 1) return; // no modifications since last save const QString &crt = QStringLiteral("/tmp/plan_") + QString::number(LtsSessionData::LtsSessionData::getInstance()->getOperatorId()); QFile file(crt); if (file.open(QIODevice::WriteOnly)) file.write(m_internal->m_geoModel->toByteArray()); } // PlanEditor::onPlanAutoSave void PlanEditor::onPlanImport() { abortCurrentBboxRubberband(); if (m_internal->m_geoModel == nullptr) return; m_internal -> m_fileDialog -> setWindowTitle(tr("Plan Editor - Select File to Import")); m_internal -> m_fileDialog -> setAcceptMode(QFileDialog::AcceptMode::AcceptOpen); m_internal -> m_fileDialog -> setDirectory(planDefaultDirectory()); if (!m_internal->m_currentFileName.isEmpty()) m_internal -> m_fileDialog -> selectFile(m_internal->m_currentFileName); if (m_internal->m_fileDialog->exec() == QDialog::DialogCode::Accepted) { const QStringList &fileList = m_internal->m_fileDialog->selectedFiles(); if (!fileList.isEmpty()) { const QString & fileName = addPlanFileSuffix(fileList.first(), m_internal->m_fileDialog->selectedNameFilter()); importFromFile(fileName); } } } // PlanEditor::onPlanImport void PlanEditor::onCenterViewSelected() { const qreal eps = 0.00000000001; abortCurrentBboxRubberband(); if (m_internal->m_selectionList.isEmpty()) return; qreal viewAreaMinX = std::numeric_limits::max(); qreal viewAreaMinY = std::numeric_limits::max(); qreal viewAreaMaxX = -std::numeric_limits::max(); qreal viewAreaMaxY = -std::numeric_limits::max(); for (SituationMap::UniqueMapId uid : qAsConst(m_internal->m_selectionList)) { SituationMap::AbstractMapObject *mapObj = m_internal->m_map->objectFactory().idToMapObject(uid); if (!mapObj) continue; SituationMap::BoundingBox objBbox; mapObj->boundingBox(objBbox); switch (objBbox.extentType()) { case SituationMap::BoundingBox::ExtentType::Sim : { const SituationMap::PositionCont &bboxVerticies = objBbox.positions(); for (const SituationMap::Point3D &v : bboxVerticies) { if (viewAreaMinX > v.x()) viewAreaMinX = v.x(); if (viewAreaMinY > v.y()) viewAreaMinY = v.y(); if (viewAreaMaxX < v.x()) viewAreaMaxX = v.x(); if (viewAreaMaxY < v.y()) viewAreaMaxY = v.y(); } } break; case SituationMap::BoundingBox::ExtentType::Pixel : { const SituationMap::Point3D &v = objBbox.center(); if (viewAreaMinX > v.x()) viewAreaMinX = v.x(); if (viewAreaMinY > v.y()) viewAreaMinY = v.y(); if (viewAreaMaxX < v.x()) viewAreaMaxX = v.x(); if (viewAreaMaxY < v.y()) viewAreaMaxY = v.y(); } break; default : break; } } // for if (viewAreaMinX < std::numeric_limits::max()) { // ViewAreaMin and viewAreaMax are defined, we can setup the view now // add 10 % extra space qreal dx = (viewAreaMaxX - viewAreaMinX) * 0.5; qreal dy = (viewAreaMaxY - viewAreaMinY) * 0.5; qreal cx = viewAreaMinX + dx; qreal cy = viewAreaMinY + dy; dx *= 1.05; dy *= 1.05; // setup values if ((fabs(dx) < eps) || (fabs(dy) < eps)) { QRectF extent = m_internal->m_map->extent(); dx = extent.width() * 0.5; dy = extent.height() * 0.5; } viewAreaMinX = cx - dx; viewAreaMinY = cy - dy; viewAreaMaxX = cx + dx; viewAreaMaxY = cy + dy; m_internal->m_map->setExtent(viewAreaMinX, viewAreaMinY, viewAreaMaxX, viewAreaMaxY); } } // PlanEditor::onCenterViewSelected() void PlanEditor::onClearSelection() { abortCurrentBboxRubberband(); const auto selList = m_internal->m_selectionList; // setSelected modifes m_selectionList for (SituationMap::UniqueMapId uid : selList) setSelected(uid, false); m_internal->m_selectionList.clear(); ModelTreeWidgetItem::clearSelection(m_internal->m_modelTreeWidget->rootItem()); onHideDialogTab(); } // PlanEditor::onClearSelection() void PlanEditor::onRubberbandAbort(SituationMap::RB::RubberbandBase *rubberband) { if (rubberband != nullptr) { if (!m_internal->m_rbBackupNodeCoordinates.isEmpty()) { SituationMap::AbstractMapObject *mapObj = (SituationMap::AbstractMapObject *)rubberband->userData(); SituationMap::GeometryModel::Node node = m_internal -> m_geoModelRenderer -> getAssociatedNode(mapObj); m_internal->m_geoModel->setPrimitivePoints(node, m_internal->m_rbBackupNodeCoordinates); } if (!rubberband->autoDelete()) rubberband->deleteMySelf(); } m_internal->m_rbBackupNodeCoordinates.clear(); // ToDo: Status-Zeile } // PlanEditor::onRubberbandAbort(SituationMap::RB::RubberbandBase * rubberband) class DropCreateSymbol : public LtsWidgets::LtsDropHandler { PlanEditor *m_planEditor; PlanEditor::InternalData *m_planEditorInternal; QString m_sidc; mutable bool m_createParticipant; public: DropCreateSymbol(PlanEditor *planEditor, PlanEditor::InternalData *planEditorInternal, const QString &sidc, bool createParticipant = false ) : LtsWidgets::LtsDropHandler() , m_planEditor(planEditor) , m_planEditorInternal(planEditorInternal) , m_sidc(sidc) , m_createParticipant(createParticipant) {;} virtual ~DropCreateSymbol() = default; void doDrop(int x, int y, const QMimeData * mimeData) const override { m_planEditor->onClearSelection(); if (nullptr == m_planEditorInternal) return; if (nullptr == m_planEditorInternal->m_geoModel) return; const AdvancedSymbology::SymbolDictionary &symbolDict = m_planEditorInternal->m_map->symbolDictionary(); const QString &baseCode = AdvancedSymbology::SidcBuilderBase::concreteSidcToBaseSymbolCode(m_sidc); const AdvancedSymbology::SymbolProperties &symbolProps = symbolDict.symbolProperties(baseCode); if ((nullptr != mimeData) && mimeData->hasFormat(QStringLiteral("Participant"))) { const QByteArray & participantInfo = mimeData->data(QStringLiteral("Participant")); m_createParticipant = m_createParticipant || (participantInfo.toInt() > 0); } if (symbolProps.key() == AdvancedSymbology::SidcCodec::decodeRawSidc(baseCode)) { m_planEditorInternal->m_symbolCurrentSidc = m_sidc; SituationMap::RB::Rubberband *rubberband = m_planEditorInternal->m_map->rubberbandFactory().createSymbol(m_sidc, QPoint(x, y), m_planEditorInternal); if (rubberband != nullptr) { SituationMap::RB::RubberbandPointFeature *pointFeatureRB = dynamic_cast(rubberband); if ((pointFeatureRB) && (pointFeatureRB->getMaximumPointCount() < 2)) { // It is a point feature to start a rubberband makes no sense here because the coordinate is already // known. We can use the coordinate of drop event to place the symbol. QVector pointList; pointList += m_planEditorInternal->m_map->viewPortWindowPointToMapCoordinate(QPointF(x, y)); pointFeatureRB->setCoordinateVector(pointList, SituationMap::RB::CoordinateSystem::Map); m_planEditor->onSymbolCreate(rubberband,m_createParticipant); pointFeatureRB -> deleteMySelf(); // bye bye rubberband return; } m_planEditorInternal->m_session.hideAnimated(); rubberband -> setAutoDelete(true); rubberband -> setPreviewSize(ICON_SIZE); QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, m_planEditor, [this](SituationMap::RB::RubberbandBase *rubberband) { m_planEditor->onSymbolCreate(rubberband, m_createParticipant); }); QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, m_planEditor, &PlanEditor::onRubberbandAbort); } else { QString msg = QObject::tr("Creating rubberband failed, may be wrong sidc (%1)").arg(m_planEditorInternal->m_symbolCurrentSidc); LtsWidgets::LtsMessageDialog::showMessage(m_planEditor->topLevelWidget(), msg, m_planEditor->title()); ERROR_LOG(m_planEditorInternal->m_logger) << LOGFUNCTION << msg; } } } // doDrop(int x, int y, const QMimeData * mimeData) }; // DropCreateSymbol qulonglong PlanEditor::createSymbolDropEvent(const QString &sidc) { bool createParticipant = false; if (m_internal->m_lookupSymbolManager) { // createParticipant = m_internal->m_lookupSymbolManager->getBaseSymbolModel()->isParticipant(sidc); createParticipant = m_internal->m_lookupSymbolManager->isParticipantDialog(); } return m_internal->m_dropEventManager->add(new DropCreateSymbol(this, m_internal.get(), sidc, createParticipant)); } // int PlanEditor::createSymbolDropEvent(const QString & sidc) SituationMap::RB::Rubberband *PlanEditor::onSymbolCreateRubberband(const QString ¤tSidc, bool createParticipant ) { // AdvancedSymbology::Gui::BaseSymbolModel *symbolModel = m_internal->m_lookupSymbolManager->getBaseSymbolModel(); // bool bCreateParticipant = createParticipant && (m_internal->m_lookupSymbolManager) && (m_internal->m_lookupSymbolManager->isParticipant(currentSidc)); bool bCreateParticipant = createParticipant && (m_internal->m_lookupSymbolManager) && (m_internal->m_lookupSymbolManager->isParticipantDialog()); onClearSelection(); const AdvancedSymbology::SymbolDictionary &symbolDict = m_internal->m_map->symbolDictionary(); const QString &baseKey = AdvancedSymbology::SidcBuilderBase::concreteSidcToBaseSymbolCode(currentSidc); const AdvancedSymbology::SymbolProperties &symbolProps = symbolDict.symbolProperties(baseKey); if (symbolProps.key() == AdvancedSymbology::SidcCodec::decodeRawSidc(baseKey)) { m_internal->m_symbolCurrentSidc = currentSidc; SituationMap::RB::Rubberband *rubberband = m_internal->m_map->rubberbandFactory().createSymbol(currentSidc, m_internal.get()); if (rubberband != nullptr) { m_internal->m_session.hideAnimated(); rubberband -> setAutoDelete(true); rubberband -> setPreviewSize(ICON_SIZE); QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, this, [this, bCreateParticipant](SituationMap::RB::RubberbandBase *rubberband) { onSymbolCreate(rubberband, bCreateParticipant); }); QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &PlanEditor::onRubberbandAbort); } else { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Creating rubberband failed! May there is another active rubberband"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Creating rubberband failed, may be wrong sidc (" << m_internal->m_symbolCurrentSidc << ")"; } return rubberband; } return nullptr; } // PlanEditor::onSymbolCreateRubberband() void PlanEditor::onChooseSymbol() { onClearSelection(); if (m_internal->m_symbolDictLoaded) m_internal->m_lookupSymbolManager->onOpenSymbolDialog(); }; // PlanEditor::onChooseSymbol() void PlanEditor::onSymbolCreate(SituationMap::RB::RubberbandBase *rubberband, bool createParticipant) { SituationMap::RB::Rubberband *rbnd = dynamic_cast(rubberband); if (nullptr == m_internal->m_geoModel) return; if (nullptr == rbnd) return; SituationMap::RB::RubberbandRectangle *rbRectangle = dynamic_cast(rbnd); SituationMap::RB::RubberbandEllipse *rbEllipse = dynamic_cast(rbnd); SituationMap::RB::RubberbandCorridor *rbCorridor = dynamic_cast(rbnd); bool isAttributedSymbol = (rbRectangle || rbEllipse || rbCorridor); const QString &sidc = rbnd->getPreviewSidc(); QList points; QList > attributes; QString sidcAttributes; SituationMap::AbstractMapObject *mapObj = (SituationMap::AbstractMapObject *)rbnd->userData(); SituationMap::GeometryModel::Node node = m_internal -> m_geoModelRenderer -> getAssociatedNode(mapObj); if (isAttributedSymbol) { if (rbRectangle && (nullptr == rbEllipse)) { QPointF center; qreal length; qreal width; QPointF dir; rbRectangle->rectangleParameter(center, length, width, dir); #ifdef SPEC_MIL2525D_RELATED points += center; #else points = rbRectangle->coordinateVector().toList(); #endif QMap attrMap; attrMap[QStringLiteral("length")] = length; attrMap[QStringLiteral("width")] = width; attrMap[QStringLiteral("attitude")] = SituationMap::MathUtils::getAngleDegreeNorth(QPointF(0, 0), dir); sidcAttributes = m_internal->m_geoModel->buildSymbolAttributeString(attrMap); } if (rbEllipse) { QPointF center; qreal diameter; qreal d2; QPointF dir; rbEllipse->rectangleParameter(center, diameter, d2, dir); points += center; QMap attrMap; attrMap[QStringLiteral("radius")] = diameter * 0.5; sidcAttributes = m_internal->m_geoModel->buildSymbolAttributeString(attrMap); } if (rbCorridor) { qreal corridorWidth = rbCorridor->corridorWidth() * 0.5; const auto coords = rbCorridor->centerLine(); points.reserve(coords.size()); for (const QPointF &coord : coords) points += coord; QMap attrMap; attrMap[QStringLiteral("width")] = corridorWidth; sidcAttributes = m_internal->m_geoModel->buildSymbolAttributeString(attrMap); } } else { const auto coords = rubberband->coordinateVector(); points.reserve(coords.size()); for (const QPointF &coord : coords) points += coord; } DEBUG_LOG(m_internal->m_logger) << LOGFUNCTION << "Creating symbol sidc='" << sidc << "'; Attributes='" << sidcAttributes << "'"; if (!node.isValid()) { m_internal->m_geoModel->startUndoBlock(); { if (!sidcAttributes.isEmpty()) attributes += QPair(SituationMap::GeometryModel::Attribute::SIDC_Attributes, sidcAttributes); SituationMap::GeometryModel::PrimitiveType symbolType = SituationMap::GeometryModel::PrimitiveType::Symbol; if (AdvancedSymbology::SidcCodec::SidcType::CivilXml != AdvancedSymbology::SidcCodec::decodeSidcType(sidc)) { symbolType = createParticipant ? SituationMap::GeometryModel::PrimitiveType::ParticipantSymbol : SituationMap::GeometryModel::PrimitiveType::DictionarySymbol; attributes += QPair(SituationMap::GeometryModel::Attribute::SIDC, sidc); } else { const QString &symbolId = AdvancedSymbology::SidcCodec::decodeRawSidc(sidc); attributes += QPair(SituationMap::GeometryModel::Attribute::SymbolId, symbolId ); } node = addGeoModelPrimitive(symbolType, points, attributes); addStyleAttributes(node, false); } m_internal->m_geoModel->endUndoBlock(tr("create symbol", TR_UNDO_TOKEN)); onUpdateView(); setSelected(node); } else { if (!m_internal->m_rbBackupNodeCoordinates.isEmpty()) { // restore last coordinate before rubberband was running m_internal->m_geoModel->setPrimitivePoints(node, m_internal->m_rbBackupNodeCoordinates); } m_internal->m_geoModel->startUndoBlock(); { addStyleAttributes(node, false); m_internal->m_geoModel->setPrimitiveAttribute(node, SituationMap::GeometryModel::Attribute::SIDC, sidc); if (!sidcAttributes.isEmpty()) m_internal->m_geoModel->setPrimitiveAttribute(node, SituationMap::GeometryModel::Attribute::SIDC_Attributes, sidcAttributes); m_internal->m_geoModel->setPrimitivePoints(node, points); } m_internal->m_geoModel->endUndoBlock(tr("modifiy symbol geometry", TR_UNDO_TOKEN)); } m_internal->m_rbBackupNodeCoordinates.clear(); } // PlanEditor::onSymbolCreate( SituationMap::RB::RubberbandBase * rubberband ) SituationMap::RB::Rubberband *PlanEditor::onLineCreateRubberband() { onClearSelection(); if (m_internal->m_geoModel == nullptr) { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Creating rubberband failed! May there is another active rubberband"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Creating rubberband failed! May there is another active rubberband"; return nullptr; } SituationMap::RB::RubberbandMultiPointFeature *rubberband = m_internal->m_map->rubberbandFactory().create(m_internal.get()); if (rubberband != nullptr) { m_internal->m_session.hideAnimated(); rubberband -> setAutoDelete(true); rubberband -> enableClosed(false); rubberband -> maximumPointCount(2); rubberband -> enableFinishAfterConstruction(true); QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &PlanEditor::onLineCreate); QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &PlanEditor::onRubberbandAbort); } else { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Creating rubberband failed! May there is another active rubberband"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Creating rubberband for multi points features failed."; } return rubberband; } // PlanEditor::onLineCreateRubberband() void DropCreateLine::doDrop(int x, int y, const QMimeData *) const { m_planEditor->onClearSelection(); if (nullptr == m_planEditorInternal) return; if (nullptr == m_planEditorInternal->m_geoModel) return; SituationMap::RB::RubberbandMultiPointFeature *rubberband = m_planEditorInternal->m_map->rubberbandFactory().create(m_planEditorInternal); if (rubberband != nullptr) { m_planEditorInternal->m_session.hideAnimated(); rubberband -> setAutoDelete(true); rubberband -> enableClosed(false); rubberband -> maximumPointCount(2); double dx = m_planEditorInternal->m_map->width() * 0.25; double dy = m_planEditorInternal->m_map->height() * 0.25; QVector lineCoordinates; lineCoordinates += m_planEditorInternal->m_map->viewPortWindowPointToGeoCoordinate(QPointF(x - dx, y - dy)); lineCoordinates += m_planEditorInternal->m_map->viewPortWindowPointToGeoCoordinate(QPointF(x + dx, y + dy)); rubberband->setCoordinateVector(lineCoordinates); QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, m_planEditor, &PlanEditor::onLineCreate); QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, m_planEditor, &PlanEditor::onRubberbandAbort); } else { LtsWidgets::LtsMessageDialog::showMessage(m_planEditor->topLevelWidget(), QObject::tr("Creating rubberband failed! May there is another active rubberband"), m_planEditor->title()); ERROR_LOG(m_planEditorInternal->m_logger) << LOGFUNCTION << "Creating rubberband for multi points features failed."; } } // DropCreateLine::doDrop( int x, int y ) const void PlanEditor::onLineCreate(SituationMap::RB::RubberbandBase *rubberband) { if (m_internal->m_geoModel == nullptr) return; if (rubberband == nullptr) return; SituationMap::RB::RubberbandMultiPointFeature *lineRubberband = dynamic_cast(rubberband); if (lineRubberband) { QList points; QList > attributes; SituationMap::AbstractMapObject *mapObj = (SituationMap::AbstractMapObject *)lineRubberband->userData(); SituationMap::GeometryModel::Node node = m_internal -> m_geoModelRenderer -> getAssociatedNode(mapObj); const auto coords = lineRubberband->coordinateVector(); points.reserve(coords.size()); for (const QPointF &coord : coords) points += coord; if (!node.isValid()) { m_internal->m_geoModel->startUndoBlock(); { node = addGeoModelPrimitive(SituationMap::GeometryModel::PrimitiveType::Line, points, attributes); // addStyleAttributes will be done in addGeoModelPrimitives } m_internal->m_geoModel->endUndoBlock(tr("create line", TR_UNDO_TOKEN)); onUpdateView(); setSelected(node); } else { m_internal->m_geoModel->startUndoBlock(); { m_internal->m_geoModel->setPrimitivePoints(node, points); addStyleAttributes(node, false); } m_internal->m_geoModel->endUndoBlock(tr("modifiy line geometry", TR_UNDO_TOKEN)); } } } // PlanEditor::onLineCreate( SituationMap::RB::RubberbandBase * rubberband ) SituationMap::RB::Rubberband *PlanEditor::onPolylineCreateRubberband() { onClearSelection(); if (nullptr == m_internal->m_geoModel) { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Internal critical failure"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "GeometryModel doesn't exist any more!"; return nullptr; } SituationMap::RB::RubberbandMultiPointFeature *rubberband = m_internal->m_map->rubberbandFactory().create(m_internal.get()); if (rubberband != nullptr) { m_internal->m_session.hideAnimated(); rubberband -> setAutoDelete(true); rubberband -> enableClosed(false); rubberband -> minimumPointCount(3); rubberband -> enableFinishAfterConstruction(true); QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &PlanEditor::onPolylineCreate); QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &PlanEditor::onRubberbandAbort); } else { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Creating rubberband failed! May there is another active rubberband"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Creating rubberband for multi points features failed."; } return rubberband; } // PlanEditor::onPolylineCreateRubberband() void DropCreatePolyLine::doDrop(int x, int y, const QMimeData *) const { m_planEditor->onClearSelection(); if (nullptr == m_planEditorInternal) return; if (nullptr == m_planEditorInternal->m_geoModel) return; SituationMap::RB::RubberbandMultiPointFeature *rubberband; if (m_smooth) rubberband = m_planEditorInternal->m_map->rubberbandFactory().create(m_planEditorInternal); else rubberband = m_planEditorInternal->m_map->rubberbandFactory().create(m_planEditorInternal); if (rubberband != nullptr) { m_planEditorInternal->m_session.hideAnimated(); rubberband -> setAutoDelete(true); rubberband -> enableClosed(false); double dx = m_planEditorInternal->m_map->width() * 0.25; double dy = m_planEditorInternal->m_map->height() * 0.25; QVector lineCoordinates; lineCoordinates += m_planEditorInternal->m_map->viewPortWindowPointToGeoCoordinate(QPointF(x - dx, y - dy)); lineCoordinates += m_planEditorInternal->m_map->viewPortWindowPointToGeoCoordinate(QPointF(x + dx, y + dy)); rubberband->setCoordinateVector(lineCoordinates); QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, m_planEditor, &PlanEditor::onPolylineCreate); QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, m_planEditor, &PlanEditor::onRubberbandAbort); } else { LtsWidgets::LtsMessageDialog::showMessage(m_planEditor->topLevelWidget(), QObject::tr("Creating rubberband failed! May there is another active rubberband"), m_planEditor->title()); ERROR_LOG(m_planEditorInternal->m_logger) << LOGFUNCTION << "Creating rubberband for multi points features failed."; } } // DropCreatePolyLine::doDrop( int x, int y ) const void PlanEditor::onPolylineCreate(SituationMap::RB::RubberbandBase *rubberband) { if (m_internal->m_geoModel == nullptr) return; if (rubberband == nullptr) return; SituationMap::RB::RubberbandMultiPointFeature *lineRubberband = dynamic_cast(rubberband); SituationMap::RB::RubberbandSmoothLine *smoothLine = dynamic_cast(rubberband); if (lineRubberband) { QList points; QList > attributes; SituationMap::AbstractMapObject *mapObj = (SituationMap::AbstractMapObject *)lineRubberband->userData(); SituationMap::GeometryModel::Node node = m_internal -> m_geoModelRenderer -> getAssociatedNode(mapObj); const auto coords = lineRubberband->coordinateVector(); points.reserve(coords.size()); for (const QPointF &coord : coords) points += coord; attributes += QPair(SituationMap::GeometryModel::Attribute::Close, QVariant(false)); attributes += QPair(SituationMap::GeometryModel::Attribute::Fill, QVariant(false)); if (!node.isValid()) { m_internal->m_geoModel->startUndoBlock(); { if (smoothLine) node = addGeoModelPrimitive(SituationMap::GeometryModel::PrimitiveType::Spline, points, attributes); // addStyleAttributes will be done in addGeoModelPrimitives else node = addGeoModelPrimitive(SituationMap::GeometryModel::PrimitiveType::Polyline, points, attributes); } m_internal->m_geoModel->endUndoBlock((smoothLine) ? tr("create new smooth line", TR_UNDO_TOKEN) : tr("create new polyline", TR_UNDO_TOKEN)); onUpdateView(); setSelected(node); } else { m_internal->m_geoModel->startUndoBlock(); { m_internal->m_geoModel->setPrimitivePoints(node, points); addStyleAttributes(node, false); } m_internal->m_geoModel->endUndoBlock(tr("modifiy polyline geometry", TR_UNDO_TOKEN)); } } } // PlanEditor::onPolylineCreate( SituationMap::RB::RubberbandBase * rubberband ) SituationMap::RB::Rubberband *PlanEditor::onSmoothLineCreateRubberband() { onClearSelection(); if (m_internal->m_geoModel == nullptr) { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Internal critical failure"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "GeometryModel doesn't exist any more!"; return nullptr; } SituationMap::RB::RubberbandMultiPointFeature *rubberband = m_internal->m_map->rubberbandFactory().create(m_internal.get()); if (rubberband != nullptr) { m_internal->m_session.hideAnimated(); rubberband -> setAutoDelete(true); rubberband -> enableClosed(false); rubberband -> minimumPointCount(3); rubberband -> enableFinishAfterConstruction(true); QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &PlanEditor::onPolylineCreate); QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &PlanEditor::onRubberbandAbort); } else { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Creating rubberband failed! May there is another active rubberband"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Creating rubberband for multi points features failed."; } return rubberband; } // PlanEditor::onSmoothLineCreateRubberband() SituationMap::RB::Rubberband *PlanEditor::onPolygonCreateRubberband() { onClearSelection(); if (m_internal->m_geoModel == nullptr) { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Internal critical failure"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "GeometryModel doesn't exist any more!"; return nullptr; } SituationMap::RB::RubberbandMultiPointFeature *rubberband = m_internal->m_map->rubberbandFactory().create(m_internal.get()); if (rubberband != nullptr) { m_internal->m_session.hideAnimated(); rubberband -> setAutoDelete(true); rubberband -> enableClosed(true); rubberband -> enableFinishAfterConstruction(true); QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &PlanEditor::onPolygonCreate); QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &PlanEditor::onRubberbandAbort); } else { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Creating rubberband failed! May there is another active rubberband"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Creating rubberband for multi points features failed."; } return rubberband; } // PlanEditor::onPolygonCreateRubberband() void DropCreatePolygon::doDrop(int x, int y, const QMimeData *) const { m_planEditor->onClearSelection(); if (nullptr == m_planEditorInternal) return; if (nullptr == m_planEditorInternal->m_geoModel) return; SituationMap::RB::RubberbandMultiPointFeature *rubberband; if (m_smooth) rubberband = m_planEditorInternal->m_map->rubberbandFactory().create(m_planEditorInternal); else rubberband = m_planEditorInternal->m_map->rubberbandFactory().create(m_planEditorInternal); if (rubberband != nullptr) { m_planEditorInternal->m_session.hideAnimated(); rubberband -> setAutoDelete(true); rubberband -> enableClosed(true); double dx = m_planEditorInternal->m_map->width() * 0.25; double dy = m_planEditorInternal->m_map->height() * 0.25; QVector lineCoordinates; lineCoordinates += m_planEditorInternal->m_map->viewPortWindowPointToGeoCoordinate(QPointF(x - dx, y - dy)); lineCoordinates += m_planEditorInternal->m_map->viewPortWindowPointToGeoCoordinate(QPointF(x + dx, y)); lineCoordinates += m_planEditorInternal->m_map->viewPortWindowPointToGeoCoordinate(QPointF(x + dx, y + dy)); rubberband->setCoordinateVector(lineCoordinates); QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, m_planEditor, &PlanEditor::onPolygonCreate); QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, m_planEditor, &PlanEditor::onRubberbandAbort); } else { LtsWidgets::LtsMessageDialog::showMessage(m_planEditor->topLevelWidget(), QObject::tr("Creating rubberband failed! May there is another active rubberband"), m_planEditor->title()); ERROR_LOG(m_planEditorInternal->m_logger) << LOGFUNCTION << "Creating rubberband for multi points features failed."; } } // DropCreatePolygon::doDrop( int x, int y ) const void PlanEditor::onPolygonCreate(SituationMap::RB::RubberbandBase *rubberband) { if (m_internal->m_geoModel == nullptr) return; if (rubberband == nullptr) return; SituationMap::RB::RubberbandMultiPointFeature *lineRubberband = dynamic_cast(rubberband); SituationMap::RB::RubberbandSmoothLine *smoothLine = dynamic_cast(rubberband); if (lineRubberband) { QList points; QList > attributes; SituationMap::AbstractMapObject *mapObj = (SituationMap::AbstractMapObject *)lineRubberband->userData(); SituationMap::GeometryModel::Node node = m_internal -> m_geoModelRenderer -> getAssociatedNode(mapObj); const auto coords = lineRubberband->coordinateVector(); points.reserve(coords.size()); for (const QPointF &coord : coords) points += coord; attributes += QPair(SituationMap::GeometryModel::Attribute::Close, QVariant(true)); // ToDo: fill attributes if (!node.isValid()) { m_internal->m_geoModel->startUndoBlock(); { if (smoothLine) node = addGeoModelPrimitive(SituationMap::GeometryModel::PrimitiveType::Spline, points, attributes); // addStyleAttributes will be done in addGeoModelPrimitives else node = addGeoModelPrimitive(SituationMap::GeometryModel::PrimitiveType::Polyline, points, attributes); } m_internal->m_geoModel->endUndoBlock(tr("create polygon", TR_UNDO_TOKEN)); onUpdateView(); setSelected(node); } else { m_internal->m_geoModel->startUndoBlock(); { m_internal->m_geoModel->setPrimitivePoints(node, points); addStyleAttributes(node, false); } m_internal->m_geoModel->endUndoBlock(tr("modify polygon geoemtry", TR_UNDO_TOKEN)); } } } // PlanEditor::onPolygonCreate( SituationMap::RB::RubberbandBase * rubberband ) SituationMap::RB::Rubberband *PlanEditor::onRectangleCreateRubberband() { onClearSelection(); if (m_internal->m_geoModel == nullptr) { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Internal critical failure"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "GeometryModel doesn't exist any more!"; return nullptr; } SituationMap::RB::RubberbandRectangle *rubberband = m_internal->m_map->rubberbandFactory().create(m_internal.get()); if (rubberband != nullptr) { m_internal->m_session.hideAnimated(); rubberband -> setAutoDelete(true); QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &PlanEditor::onRectangleCreate); QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &PlanEditor::onRubberbandAbort); } else { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Creating rubberband failed! May there is another active rubberband"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Creating rubberband for rectangles failed."; } return rubberband; } // PlanEditor::onRectangleCreateRubberband() void DropCreateRectangle::doDrop(int x, int y, const QMimeData *) const { m_planEditor->onClearSelection(); if (nullptr == m_planEditorInternal) return; if (nullptr == m_planEditorInternal->m_geoModel) return; SituationMap::RB::RubberbandRectangle *rubberband; if (m_ellipse) rubberband = m_planEditorInternal->m_map->rubberbandFactory().create(m_planEditorInternal); else rubberband = m_planEditorInternal->m_map->rubberbandFactory().create(m_planEditorInternal); if (rubberband != nullptr) { m_planEditorInternal->m_session.hideAnimated(); rubberband -> setAutoDelete(true); double dx = m_planEditorInternal->m_map->width() * 0.4; double dy = m_planEditorInternal->m_map->height() * 0.25; QVector coordinates; coordinates += m_planEditorInternal->m_map->viewPortWindowPointToGeoCoordinate(QPointF(x - dx, y - dy)); coordinates += m_planEditorInternal->m_map->viewPortWindowPointToGeoCoordinate(QPointF(x + dx, y - dy)); coordinates += m_planEditorInternal->m_map->viewPortWindowPointToGeoCoordinate(QPointF(x + dx, y + dy)); coordinates += m_planEditorInternal->m_map->viewPortWindowPointToGeoCoordinate(QPointF(x - dx, y + dy)); rubberband->setCoordinateVector(coordinates); QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, m_planEditor, &PlanEditor::onRectangleCreate); QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, m_planEditor, &PlanEditor::onRubberbandAbort); } else { LtsWidgets::LtsMessageDialog::showMessage(m_planEditor->topLevelWidget(), QObject::tr("Creating rubberband failed! May there is another active rubberband"), m_planEditor->title()); ERROR_LOG(m_planEditorInternal->m_logger) << LOGFUNCTION << "Creating rubberband for rectangles failed."; } } // DropCreateRectangle::doDrop( int x, int y ) const void PlanEditor::onRectangleCreate(SituationMap::RB::RubberbandBase *rubberband) { if (m_internal->m_geoModel == nullptr) return; if (rubberband == nullptr) return; SituationMap::RB::RubberbandRectangle *rband = dynamic_cast(rubberband); SituationMap::RB::RubberbandEllipse *relli = dynamic_cast(rubberband); if (rband) { const QList &points = rband->coordinateVector().toList(); QList > attributes; QPointF rectCenter; QPointF rectOrientation; qreal rectWidth; qreal rectHeight; SituationMap::AbstractMapObject *mapObj = (SituationMap::AbstractMapObject *)rband->userData(); SituationMap::GeometryModel::Node node = m_internal -> m_geoModelRenderer -> getAssociatedNode(mapObj); rband->rectangleParameter(rectCenter, rectWidth, rectHeight, rectOrientation, SituationMap::RB::CoordinateSystem::Geo); qreal cosPhi = rectOrientation.x(); qreal phi = acos(cosPhi) * 180.0 / MathConst::pi; attributes += QPair(SituationMap::GeometryModel::Attribute::Center, QVariant(rectCenter)); attributes += QPair(SituationMap::GeometryModel::Attribute::Width, QVariant(rectWidth)); attributes += QPair(SituationMap::GeometryModel::Attribute::Height, QVariant(rectHeight)); attributes += QPair(SituationMap::GeometryModel::Attribute::Rotate, QVariant(phi)); if (relli) { if (relli->hasSector()) { auto getAngle = [](qreal sinPhi, qreal cosPhi) -> qreal { qreal phi = acos(cosPhi); if (sinPhi < 0) phi = MathConst::pi_two - phi; return (phi * 180.0 / MathConst::pi); }; // getAngle QPointF sectorStartVec = relli->sectorStart(); QPointF sectorEndVec = relli->sectorEnd(); qreal sectorStart = getAngle(sectorStartVec.y(), sectorStartVec.x()); qreal sectorEnd = getAngle(sectorEndVec.y(), sectorEndVec.x()); qreal sectorExtent = sectorEnd - sectorStart; attributes += QPair(SituationMap::GeometryModel::Attribute::Start, QVariant(sectorStart)); attributes += QPair(SituationMap::GeometryModel::Attribute::Extent, QVariant(sectorExtent)); } else attributes += QPair(SituationMap::GeometryModel::Attribute::Extent, QVariant(360)); } if (!node.isValid()) { const char *undoToken = (relli) ? "create ellipse" : "create rectangle"; m_internal->m_geoModel->startUndoBlock(); { if (relli) node = addGeoModelPrimitive(SituationMap::GeometryModel::PrimitiveType::Ellipse, points, attributes); // addStyleAttributes will be done in addGeoModelPrimitives else node = addGeoModelPrimitive(SituationMap::GeometryModel::PrimitiveType::Rectangle, points, attributes); } m_internal->m_geoModel->endUndoBlock(tr(undoToken, TR_UNDO_TOKEN)); onUpdateView(); setSelected(node); } else { const char *undoToken = (relli) ? "modify ellipse geometry" : "modify rectangle geoemtry"; m_internal->m_geoModel->startUndoBlock(); { m_internal->m_geoModel->setPrimitivePoints(node, points); for (const auto &attrPair : qAsConst(attributes)) m_internal->m_geoModel->setPrimitiveAttribute(node, attrPair.first, attrPair.second); addStyleAttributes(node, false); } m_internal->m_geoModel->endUndoBlock(tr(undoToken, TR_UNDO_TOKEN)); } } } // PlanEditor::onRectangleCreate( SituationMap::RB::RubberbandBase * rubberband ) SituationMap::RB::Rubberband *PlanEditor::onEllipseCreateRubberband() { onClearSelection(); if (m_internal->m_geoModel == nullptr) { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Internal critical failure"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "GeometryModel doesn't exist any more!"; return nullptr; } SituationMap::RB::RubberbandEllipse *rubberband = m_internal->m_map->rubberbandFactory().create(m_internal.get()); if (rubberband != nullptr) { m_internal->m_session.hideAnimated(); rubberband -> setAutoDelete(true); rubberband -> enableSector(); QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &PlanEditor::onRectangleCreate); QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &PlanEditor::onRubberbandAbort); } else { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Creating rubberband failed! May there is another active rubberband"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Creating rubberband for rectangles failed."; } return rubberband; } // PlanEditor::onEllipseCreateRubberband() SituationMap::RB::Rubberband *PlanEditor::onTextLineCreateRubberband() { onClearSelection(); if (m_internal->m_geoModel == nullptr) { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Internal critical failure"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "GeometryModel doesn't exist any more!"; return nullptr; } SituationMap::RB::RubberbandPointFeature *rubberband = m_internal->m_map->rubberbandFactory().create(m_internal.get()); if (rubberband != nullptr) { m_internal->m_session.hideAnimated(); rubberband -> setAutoDelete(true); rubberband -> enableFinishAfterConstruction(true); QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &PlanEditor::onTextLineCreate); QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &PlanEditor::onRubberbandAbort); } else { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Creating rubberband failed! May there is another active rubberband"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Creating rubberband for multi points features failed."; } return rubberband; } SituationMap::RB::Rubberband *PlanEditor::onTextBoxPixelCreateRubberband() { onClearSelection(); if (m_internal->m_geoModel == nullptr) { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Internal critical failure"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "GeometryModel doesn't exist any more!"; return nullptr; } SituationMap::RB::RubberbandPointFeature *rubberband = m_internal->m_map->rubberbandFactory().create(m_internal.get()); if (rubberband) { m_internal->m_session.hideAnimated(); rubberband -> setAutoDelete(true); rubberband -> enableFinishAfterConstruction(true); QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &PlanEditor::onTextBoxPixelCreate); QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &PlanEditor::onRubberbandAbort); } else { LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), tr("Creating rubberband failed! May there is another active rubberband"), title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << "Creating rubberband for multi points features failed."; } return rubberband; } SituationMap::GeometryModel::Node PlanEditor::buildTextNode(const QPointF &loc) { QList > attributes; QList points; SituationMap::GeometryModel::Node node; points += loc; m_internal->m_geoModel->startUndoBlock(); { node = addGeoModelPrimitive(SituationMap::GeometryModel::PrimitiveType::Text , points , attributes , SituationMap::IdHandler::BadId , m_internal->m_attributeDialogManager->textLines()); } m_internal->m_geoModel->endUndoBlock(tr("create text", TR_UNDO_TOKEN)); return node; } // PlanEditor::buildTextNode( const QPointF & loc ) void PlanEditor::buildTextBoxPixelNode(const QPointF &locPixel) { // // m_internal->m_geoModel->startUndoBlock(); // // cleanup 'current'... // m_internal->resetCurrentTextBox(); // // get in touch with TextBox at QGraphicsScene: // SituationMap::PixelBasedTextBoxHandler &pixelBasedTextBoxHandler = m_internal->m_map->pixelBasedTextBoxHandler(); SituationMap::PBTB_ID pbtbId(PLAN_EDITOR_TEXTBOX_PARENT_ID); SituationMap::PixelTextBoxData ptbd; ptbd.m_position = locPixel.toPoint(); QTextEdit *newTextEdit = pixelBasedTextBoxHandler.createTextBox(pbtbId, ptbd, true); // // !! pbtbId maybe updated by createUpdateTextBox !! // INFO_LOG(m_internal->m_logger) << " new TextBox created: newTextEdit = " << newTextEdit << ", pbtbId=" << pbtbId << ", ptbd=" << ptbd; // add to mgnt: if (!m_internal->m_ourTextBoxIds.contains(pbtbId)) m_internal->m_ourTextBoxIds.append(pbtbId); // connect slot to open TextEditDialog: connect(&pixelBasedTextBoxHandler, &SituationMap::PixelBasedTextBoxHandler::pixelBasedTextBoxDoubleClickEvent, this, &PlanEditor::onTextEditDialogRequested, Qt::UniqueConnection); connect(&pixelBasedTextBoxHandler, &SituationMap::PixelBasedTextBoxHandler::pixelBasedTextBoxGeometryChanged, this, &PlanEditor::onTextEditGeometryChanged, Qt::UniqueConnection); connect(&pixelBasedTextBoxHandler, &SituationMap::PixelBasedTextBoxHandler::pixelBasedTextBoxRemoveRequestedEvent, this, &PlanEditor::onTextEditRemoveRequested, Qt::UniqueConnection); // // reuse or create EditDialog // onTextEditDialogRequested(pbtbId); // // m_internal->m_geoModel->endUndoBlock(tr("create textBoxPixel", TR_UNDO_TOKEN)); } void PlanEditor::writeTextBoxDataToModel() { INFO_LOG(m_internal->m_logger) << LOGFUNCTION << " START" << ", m_internal->m_textBoxIds.size = " << m_internal->m_ourTextBoxIds.size(); QVector ptbdVec; ptbdVec.reserve(m_internal->m_ourTextBoxIds.size()); // Read text, bgColor, pos, size from SitMap (pixelBasedTextBoxHandler): const SituationMap::PBTB_ID pbtbIdAsParent(PLAN_EDITOR_TEXTBOX_PARENT_ID); const SituationMap::PixelBasedTextBoxHandler &pixelBasedTextBoxHandler = m_internal->m_map->pixelBasedTextBoxHandler(); const QVector pbtbIdVec = pixelBasedTextBoxHandler.getAllIdsOfParent(pbtbIdAsParent); for (const SituationMap::PBTB_ID &pbtbId : qAsConst(pbtbIdVec)) { const SituationMap::PixelTextBoxData &data = pixelBasedTextBoxHandler.getPixelTextBoxData(pbtbId); if (data.isEmpty()) continue; ptbdVec.append(data); } // for // convert to Json (Container): const QString &str = SituationMap::PixelTextBoxData::pixelTextBoxContainerToJsonStr(ptbdVec, QJsonDocument::JsonFormat::Compact); // to model m_internal->m_geoModel->setModelAttribute(SituationMap::GeometryModel::Attribute::MetaPixelBasedTextBoxes, str); TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " END"; } void DropCreateTextLine::doDrop(int x, int y, const QMimeData *) const { m_planEditor->onClearSelection(); if (nullptr == m_planEditorInternal) return; if (nullptr == m_planEditorInternal->m_geoModel) return; const QPointF loc = m_planEditorInternal->m_map->viewPortWindowPointToGeoCoordinate(QPointF(x, y)); const SituationMap::GeometryModel::Node node = m_planEditor->buildTextNode(loc); m_planEditor->onUpdateView(); m_planEditor->setSelected(node); } // DropCreateTextLine::doDrop(int x, int y, const QMimeData*) const void PlanEditor::onTextLineCreate(SituationMap::RB::RubberbandBase *rubberband) { if (m_internal->m_geoModel == nullptr) return; if (rubberband == nullptr) return; SituationMap::RB::RubberbandPointFeature *pointRubberband = dynamic_cast(rubberband); if (pointRubberband) { const QPointF &loc = pointRubberband->singleCoordinate(); SituationMap::AbstractMapObject *mapObj = (SituationMap::AbstractMapObject *)pointRubberband->userData(); SituationMap::GeometryModel::Node node = m_internal -> m_geoModelRenderer -> getAssociatedNode(mapObj); if (!node.isValid()) { node = buildTextNode(loc); onUpdateView(); setSelected(node); } else { // restore last coordinate before rubberband was running m_internal->m_geoModel->setPrimitivePoints(node, m_internal->m_rbBackupNodeCoordinates); m_internal->m_geoModel->startUndoBlock(); { m_internal->m_geoModel->setPrimitivePoints(node, QList() << loc); addStyleAttributes(node, false); } m_internal->m_geoModel->endUndoBlock(tr("modifiy text position", TR_UNDO_TOKEN)); } } m_internal->m_rbBackupNodeCoordinates.clear(); } void PlanEditor::onTextBoxPixelCreate(SituationMap::RB::RubberbandBase *rubberband) { if (!m_internal->m_geoModel || !rubberband) return; SituationMap::RB::RubberbandPointFeature *pointRubberband = dynamic_cast(rubberband); if (!pointRubberband) return; const QVector &coordsRef = pointRubberband->coordinateVectorPixel(); const QPointF &locPixel = coordsRef.first(); // create TextEdit and it´s dialog: buildTextBoxPixelNode(locPixel); } void PlanEditor::onTextBoxPixelTextChanged(const QString &htmlText) { TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " START"; if (!m_internal->m_map) return; // slight change -> no UndoBlock const SituationMap::PBTB_ID currentPbtbId = m_internal->m_textBoxIdInEditing; if (!currentPbtbId.isValid()) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " m_internal->m_textBoxIdInEditing invalid: " << currentPbtbId.toString(); return; } if (!m_internal->m_ourTextBoxIds.contains(currentPbtbId)) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " m_internal->m_textBoxIds does not have currentPbtbId = " << currentPbtbId; return; } if (!m_internal->m_textBoxTextEdit) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " m_textBoxTextEdit invalid: " << m_internal->m_textBoxTextEdit; return; } // Update gui: m_internal->m_textBoxTextEdit->setHtml(htmlText); // to ensure, that button "save" and "save as" will save the correct data: // [GUEZE-2947] Undo,Redo fuer PixelBasedTextBoxen // on apply: // writeTextBoxDataToModel(); TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " END"; } void PlanEditor::onTextBoxPixelBackgroundColorChanged(const QColor &backgroundColor) { TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " START, backgroundColor= " << backgroundColor; if (!backgroundColor.isValid()) return; const SituationMap::PBTB_ID currentPbtbId = m_internal->m_textBoxIdInEditing; if (!currentPbtbId.isValid()) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " m_internal->m_textBoxIdInEditing invalid: " << currentPbtbId.toString(); return; } if (!m_internal->m_ourTextBoxIds.contains(currentPbtbId)) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " m_internal->m_textBoxIds does not have currentPbtbId = " << currentPbtbId; return; } SituationMap::PixelBasedTextBoxHandler &pixelBasedTextBoxHandler = m_internal->m_map->pixelBasedTextBoxHandler(); SituationMap::PixelTextBoxData data = pixelBasedTextBoxHandler.getPixelTextBoxData(currentPbtbId); if (data.isEmpty()) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " m_textBoxTextEdit invalid: " << m_internal->m_textBoxTextEdit; return; } // Update gui: data.m_backgroundColor = backgroundColor; pixelBasedTextBoxHandler.updateTextBox(currentPbtbId, data); // to ensure, that button save", save as" will save the correct data: writeTextBoxDataToModel(); TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " END, backgroundColor= " << backgroundColor; } void PlanEditor::onTextBoxPixelTextApplied(const QString &htmlText) { TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " START"; // // m_internal->m_geoModel->startUndoBlock(); m_internal->m_htmlTextForCancelation = htmlText; // update writeTextBoxDataToModel(); // // m_internal->m_geoModel->endUndoBlock(tr("update textBoxes", TR_UNDO_TOKEN)); TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " END"; } void PlanEditor::onTextBoxPixelCanceled() { TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " START"; if (!m_internal->m_textBoxTextEdit) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " m_textBoxTextEdit invalid: " << m_internal->m_textBoxTextEdit; return; } // restore saved text: m_internal->m_textBoxTextEdit->setHtml(m_internal->m_htmlTextForCancelation); writeTextBoxDataToModel(); TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " END"; } void PlanEditor::onTextBoxPixelClosed() { TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " START"; SituationMap::PixelBasedTextBoxHandler &pixelBasedTextBoxHandler = m_internal->m_map->pixelBasedTextBoxHandler(); pixelBasedTextBoxHandler.removeSelection(); TRACE_LOG(m_internal->m_logger) << LOGFUNCTION << " END"; } void PlanEditor::onTransformMove(SituationMap::RB::RubberbandBase *rubberband) { SituationMap::RB::RubberbandTransformMove *rMove = dynamic_cast(rubberband); if (rMove == nullptr) return; QList *featureToTransform = static_cast*>(rMove->userData()); // Nothing to do here because all work has been done in preview rMove->setUserData(nullptr); delete featureToTransform; m_internal->m_geoModel->endUndoBlock(tr("move feature(s)", TR_UNDO_TOKEN)); } // PlanEditor::onTransformMove( SituationMap::RB::RubberbandBase * rubberband ) void PlanEditor::onTransformRotate(SituationMap::RB::RubberbandBase *rubberband) { SituationMap::RB::RubberbandTransformRotate *rRotate = dynamic_cast(rubberband); if (rRotate == nullptr) return; // const QPointF & rCenter = rRotate -> centerPoint(); // qreal deltaAngle = rRotate -> getRelativeRotation(); for (auto uid : qAsConst(m_internal->m_selectionList)) { const SituationMap::GeometryModel::Node &modelPrimitive = m_internal->m_geoModel->findPrimitive(uid); if (modelPrimitive.isValid()) { QList vertexList = m_internal->m_geoModel->getPrimitivePoints(modelPrimitive); m_internal->m_geoModel->setPrimitivePoints(modelPrimitive, vertexList); } } } // PlanEditor::onTransformRotate( SituationMap::RB::RubberbandBase * rubberband ) void PlanEditor::onTransformScale(SituationMap::RB::RubberbandBase * /*rubberband*/) { } // PlanEditor::onTransformScale( SituationMap::RB::RubberbandBase * rubberband ) void PlanEditor::onEditAttributes() { abortCurrentBboxRubberband(); if (!m_internal -> m_selectionList.isEmpty()) { SituationMap::UniqueMapId uid = m_internal -> m_selectionList.first(); const SituationMap::GeometryModel::Node &node = m_internal -> m_geoModelRenderer -> getAssociatedNode(m_internal->m_map->objectFactory().idToMapObject(uid)); onShowDialogTab(node); } else onShowDialogTab(SituationMap::GeometryModel::Node()); } // PlanEditor::onEditAttributes() void PlanEditor::onEditGraphicGeometry() { abortCurrentBboxRubberband(); if (1 != m_internal->m_selectionList.size()) return; SituationMap::UniqueMapId uid = m_internal -> m_selectionList.first(); SituationMap::AbstractMapObject *mapObj = m_internal -> m_map->objectFactory().idToMapObject(uid); const SituationMap::GeometryModel::Node &node = m_internal -> m_geoModelRenderer -> getAssociatedNode(mapObj); if (!node.isValid()) return; SituationMap::GeometryModel::PrimitiveType primitiveType = m_internal->m_geoModel->getType(node); SituationMap::RB::Rubberband *rubberband = nullptr; auto previewGeometrySinglePointMove = [this](SituationMap::RB::RubberbandBase * rb) -> bool { if (!rb) return true; SituationMap::AbstractMapObject *mapObj = (SituationMap::AbstractMapObject *)rb->userData(); SituationMap::GeometryModel::Node node = m_internal -> m_geoModelRenderer -> getAssociatedNode(mapObj); m_internal->m_geoModel->setPrimitivePoints(node, QList() << rb->singleCoordinate()); return true; }; switch (primitiveType) { case SituationMap::GeometryModel::PrimitiveType::ParticipantSymbol: case SituationMap::GeometryModel::PrimitiveType::DictionarySymbol : { const QString &sidc = m_internal->m_geoModel->getAttribute(node, SituationMap::GeometryModel::Attribute::SIDC).toString(); rubberband = onSymbolCreateRubberband(sidc, (SituationMap::GeometryModel::PrimitiveType::ParticipantSymbol== primitiveType) ); const QList &pointList = m_internal->m_geoModel->getPrimitivePoints(node); if (pointList.size() == 1) { m_internal->m_rbBackupNodeCoordinates = pointList; rubberband->installExternalUpdate(previewGeometrySinglePointMove); } } break; case SituationMap::GeometryModel::PrimitiveType::Ellipse : rubberband = onEllipseCreateRubberband(); break; case SituationMap::GeometryModel::PrimitiveType::Line : rubberband = onLineCreateRubberband(); break; case SituationMap::GeometryModel::PrimitiveType::Polyline : { bool closed = m_internal->m_geoModel->getAttribute(node, SituationMap::GeometryModel::Attribute::Close).toBool(); if (closed) rubberband = onPolygonCreateRubberband(); else rubberband = onPolylineCreateRubberband(); } break; case SituationMap::GeometryModel::PrimitiveType::Rectangle : rubberband = onRectangleCreateRubberband(); break; case SituationMap::GeometryModel::PrimitiveType::Spline : rubberband = onSmoothLineCreateRubberband(); break; case SituationMap::GeometryModel::PrimitiveType::Symbol : break; case SituationMap::GeometryModel::PrimitiveType::Text : { const QList &pointList = m_internal->m_geoModel->getPrimitivePoints(node); if (pointList.size() == 1) { m_internal->m_rbBackupNodeCoordinates = pointList; rubberband = onTextLineCreateRubberband(); rubberband->installExternalUpdate(previewGeometrySinglePointMove); } } break; default : break; } if (rubberband == nullptr) return; QList &&coordinateList = m_internal->m_geoModel->getPrimitivePoints(node); if (coordinateList.empty()) { rubberband -> deleteMySelf(); return; } SituationMap::RB::RubberbandRectangle *rRectRubber = dynamic_cast(rubberband); /* -------------------------------------------------------------------------------------------------------------------------------------------------------- * Now coordinates are always in geographic coordinate system and not longer in map coordinate system. The geometry model stores now the 4 verticies of * the rectangle, which are used now directly in rubberband. Keep in mind that the map coordinate sytem has to be the same as for the first input. * -------------------------------------------------------------------------------------------------------------------------------------------------------- if (rRectRubber != nullptr) { QPointF rectCenter = coordinateList.first(); qreal rectWidth2 = m_internal->m_geoModel->getAttribute(node, SituationMap::GeometryModel::Attribute::Width ).toDouble() * 0.5; qreal rectHeight2 = m_internal->m_geoModel->getAttribute(node, SituationMap::GeometryModel::Attribute::Height).toDouble() * 0.5; qreal rectPhi = m_internal->m_geoModel->getAttribute(node, SituationMap::GeometryModel::Attribute::Rotate).toDouble() * MathConst::pi / 180.0; qreal cosPhi = cos(rectPhi); qreal sinPhi = sin(rectPhi); SituationMap::CoordTranslationFunctor * ctf = m_internal->m_map->coordTranslationFunctor(); if (ctf) { rectCenter = ctf->geographicToMap( rectCenter ); } auto rotate = [cosPhi,sinPhi]( qreal x, qreal y ) -> QPointF { return QPointF( x*cosPhi - y*sinPhi, y*cosPhi + x*sinPhi ); }; // lamda rotate coordinateList.clear(); coordinateList += rotate( -rectWidth2, -rectHeight2 ) + rectCenter; coordinateList += rotate( +rectWidth2, -rectHeight2 ) + rectCenter; coordinateList += rotate( +rectWidth2, +rectHeight2 ) + rectCenter; coordinateList += rotate( -rectWidth2, +rectHeight2 ) + rectCenter; } * ------------------------------------------------------------------------------------------------------------------------------------------------------------ */ rubberband -> setCoordinateVector(coordinateList.toVector()); rubberband -> setUserData(mapObj); if ((rRectRubber != nullptr) && (!rRectRubber->rectangleIsValid())) { rubberband->deleteMySelf(); QString msg = tr("Map coordinate system differs from simulation coordinate system. Display of rectangles, ellipses ... will be wrong!"); LtsWidgets::LtsMessageDialog::showMessage(topLevelWidget(), msg, title()); ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << msg ; } SituationMap::RB::RubberbandEllipse *relli = dynamic_cast(rubberband); if (relli) { double start = m_internal->m_geoModel->getAttribute(node, SituationMap::GeometryModel::Attribute::Start).toDouble(); double extend = m_internal->m_geoModel->getAttribute(node, SituationMap::GeometryModel::Attribute::Extent).toDouble(); bool hasSector = (extend) < 360.0; relli->enableSector(); if (hasSector) { double startAngle = start * MathConst::pi / 180.0; double endAngle = (start + extend) * MathConst::pi / 180.0; relli->setSectorStart(sin(startAngle), cos(startAngle)); relli->setSectorEnd(sin(endAngle), cos(endAngle)); } else { relli->setSectorStart(0.0, 1.0); relli->setSectorEnd(0.0, 1.0); } } } // PlanEditor::onEditGraphicGeometry() void PlanEditor::onMoveGraphicGeometry() { abortCurrentBboxRubberband(); if (m_internal->m_selectionList.isEmpty()) return; createTransformRubberband(); } // PlanEditor::onMoveGraphicGeometry() void PlanEditor::onRotateGraphicGeometry() { abortCurrentBboxRubberband(); if (m_internal->m_selectionList.isEmpty()) return; createTransformRubberband(); } // PlanEditor::onRotateGraphicGeometry() void PlanEditor::onScaleGraphicGeometry() { abortCurrentBboxRubberband(); if (m_internal->m_selectionList.isEmpty()) return; createTransformRubberband(); } // PlanEditor::onScaleGraphicGeometry() void PlanEditor::onDelete() { abortCurrentBboxRubberband(); m_internal->m_geoModel->startUndoBlock(); { for (auto uid : qAsConst(m_internal->m_selectionList)) removeFeature(uid, false); } m_internal->m_geoModel->endUndoBlock(tr("delete feature(s)", TR_UNDO_TOKEN)); ModelTreeWidgetItem::updateTree(m_internal->m_modelTreeWidget, m_internal->m_modelTreeWidget->rootItem(), m_internal->m_map.data(), *(m_internal->m_geoModel)); } // onDelete() void PlanEditor::onGroup() { abortCurrentBboxRubberband(); SituationMap::GeometryModel::NodeList nodeList; SituationMap::GeometryModel::Node nodeToSelect; m_internal->m_geoModel->startUndoBlock(); { for (auto uid : qAsConst(m_internal->m_selectionList)) { SituationMap::GeometryModel::Node node = m_internal->m_geoModel->findPrimitive(uid); if (!nodeToSelect.isValid()) nodeToSelect = node; for (;;) { // go up to toplevel group node const SituationMap::GeometryModel::Node &group = m_internal->m_geoModel->isInGroup(node); if (!group.isValid()) break; node = group; } if (!nodeList.contains(node)) nodeList += node; } m_internal->m_geoModel->createGroup(nodeList); } m_internal->m_geoModel->endUndoBlock(tr("group", TR_UNDO_TOKEN)); ModelTreeWidgetItem::updateTree(m_internal->m_modelTreeWidget, m_internal->m_modelTreeWidget->rootItem(), m_internal->m_map.data(), *(m_internal->m_geoModel)); onClearSelection(); setSelected(nodeToSelect, true, true, true); } // PlanEditor::onGroup() void PlanEditor::getChildNodes(const SituationMap::GeometryModel::Node &node, SituationMap::GeometryModel::NodeList &childNodes) { const auto nodeList = m_internal->m_geoModel->getGroupPrimitives(node); for (const auto &subNode : nodeList) { if (SituationMap::GeometryModel::PrimitiveType::Node != m_internal->m_geoModel->getType(subNode)) childNodes << subNode; else getChildNodes(subNode, childNodes); } } // void PlanEditor::getChildNodes(const SituationMap::GeometryModel::Node & node, SituationMap::GeometryModel::NodeList & childNodes ) void PlanEditor::onUngroup() { abortCurrentBboxRubberband(); SituationMap::GeometryModel::NodeList nodeList; SituationMap::GeometryModel::NodeList selectedNodes; m_internal->m_geoModel->startUndoBlock(); { for (auto uid : qAsConst(m_internal->m_selectionList)) { SituationMap::GeometryModel::Node node = m_internal->m_geoModel->findPrimitive(uid); for (;;) { const SituationMap::GeometryModel::Node &group = m_internal->m_geoModel->isInGroup(node); if (!group.isValid()) break; node = group; } if (!nodeList.contains(node)) nodeList += node; } for (const auto &node : qAsConst(nodeList)) { getChildNodes(node, selectedNodes); m_internal->m_geoModel->releaseGroup(node); } } m_internal->m_geoModel->endUndoBlock(tr("ungroup", TR_UNDO_TOKEN)); ModelTreeWidgetItem::updateTree(m_internal->m_modelTreeWidget, m_internal->m_modelTreeWidget->rootItem(), m_internal->m_map.data(), *(m_internal->m_geoModel)); onUpdateView(); onClearSelection(); for (const auto &node : qAsConst(selectedNodes)) setSelected(node, true); } // PlanEditor::onUngroup() void PlanEditor::onRedo() { abortCurrentBboxRubberband(); // Maybe a pixelBasedTextbox is involved if (m_internal->m_textBoxTextEdit) delete m_internal->m_textEditorDialog; m_internal->m_geoModel->redo(); ModelTreeWidgetItem::updateTree(m_internal->m_modelTreeWidget, m_internal->m_modelTreeWidget->rootItem(), m_internal->m_map.data(), *(m_internal->m_geoModel)); } // PlanEditor::onRedo() void PlanEditor::onUndo() { abortCurrentBboxRubberband(); // Maybe a pixelBasedTextbox is involved if (m_internal->m_textBoxTextEdit) delete m_internal->m_textEditorDialog; m_internal->m_geoModel->undo(); ModelTreeWidgetItem::updateTree(m_internal->m_modelTreeWidget, m_internal->m_modelTreeWidget->rootItem(), m_internal->m_map.data(), *(m_internal->m_geoModel)); } // PlanEditor::onUndo() void PlanEditor::onUndoHistory() { abortCurrentBboxRubberband(); showHistory(m_internal -> m_dlgUndoHistory , m_internal -> m_lwUndoHistory , tr("Undo History") , m_internal->m_geoModel->undoHistory()); if (m_internal -> m_lwUndoHistory) { m_internal -> m_lwUndoHistory -> headerItem() ->setText(0, tr("Undo Step")); connect(m_internal -> m_lwUndoHistory, &QTreeWidget::itemDoubleClicked, this, [this](QTreeWidgetItem * undoItem) { if (undoItem) { bool ok = false; unsigned int undoCount = undoItem->data(0, Qt::UserRole).toUInt(&ok); if ((ok) && (undoCount > 0)) m_internal->m_geoModel->undo(undoCount); } m_internal -> m_dlgUndoHistory -> hide(); }); } else ERROR_LOG(m_internal->m_logger) << "Undefined pointer in Undo History Dialog, can't show dialog."; } // void PlanEditor::onUndoHistory() void PlanEditor::onRedoHistory() { abortCurrentBboxRubberband(); showHistory(m_internal -> m_dlgRedoHistory , m_internal -> m_lwRedoHistory , tr("Redo History") , m_internal->m_geoModel->redoHistory()); if (m_internal -> m_lwRedoHistory) { m_internal -> m_lwRedoHistory -> headerItem() ->setText(0, tr("Redo Step")); connect(m_internal -> m_lwRedoHistory, &QTreeWidget::itemDoubleClicked, this, [this](QTreeWidgetItem * redoItem) { if (redoItem) { bool ok = false; unsigned int redoCount = redoItem->data(0, Qt::UserRole).toUInt(&ok); if ((ok) && (redoCount > 0)) m_internal->m_geoModel->undo(redoCount); } m_internal -> m_lwRedoHistory -> hide(); }); } else ERROR_LOG(m_internal->m_logger) << "Undefined pointer in Redo History Dialog, can't show dialog."; } // void PlanEditor::onRedoHistory() void PlanEditor::showHistory(QPointer &dlgHistory , QTreeWidget *&lwHistory , const QString &title , const QStringList &history) { if (nullptr == dlgHistory) { dlgHistory = new LtsWidgets::LtsFramelessDialog(m_internal -> m_gbPlanEditorMain, LtsWidgets::Behaviour::Popup); QVBoxLayout *layout = new QVBoxLayout(dlgHistory); lwHistory = new QTreeWidget(dlgHistory); lwHistory -> setAlternatingRowColors(true); lwHistory -> setColumnCount(2); lwHistory -> headerItem() ->setText(1, QString()); lwHistory -> headerItem() ->setTextAlignment(0, Qt::AlignmentFlag::AlignLeft); lwHistory -> headerItem() ->setTextAlignment(1, Qt::AlignmentFlag::AlignRight); layout -> addWidget(new QLabel(title, dlgHistory), 0); layout -> addWidget(lwHistory, 2); connect(m_internal -> m_lwUndoHistory, &QObject::destroyed, this, [this](QObject *) { m_internal->m_lwUndoHistory = nullptr; }); } if (nullptr != lwHistory) { lwHistory -> clear(); unsigned int tokenCount = (unsigned int) history.size(); for (const QString &token : history) { QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() << token << QString::number(tokenCount)); item->setData(0, Qt::UserRole, QVariant(tokenCount)); lwHistory -> insertTopLevelItem(0, item); --tokenCount; } lwHistory->resizeColumnToContents(0); lwHistory->resizeColumnToContents(1); dlgHistory -> show(); } } // PlanEditor::showHistory( ... ) void PlanEditor::onCopy() { abortCurrentBboxRubberband(); m_internal->m_nodeClipboard.clear(); m_internal->m_modelClipboard->clearModel(); QVector nodesAddedClipboard; for (SituationMap::UniqueMapId uid : qAsConst(m_internal->m_selectionList)) { SituationMap::GeometryModel::Node node = m_internal->m_geoModel->findPrimitive(uid); while (!node.parentNode().isRootNode()) { if (!node.isValid()) break; node = node.parentNode(); } if (node.isValid()) { if (!nodesAddedClipboard.contains(node)) { nodesAddedClipboard.append(node); const SituationMap::GeometryModel::Node ©Clipboard = m_internal->m_modelClipboard->copy(node); m_internal->m_nodeClipboard.append(copyClipboard); } } } } // PlanEditor::onCopy() void PlanEditor::onCut() { abortCurrentBboxRubberband(); onCopy(); onDelete(); } // PlanEditor::onCut() void PlanEditor::onPaste() { abortCurrentBboxRubberband(); m_internal->m_geoModel->startUndoBlock(); { if (!m_internal->m_nodeClipboard.isEmpty()) { for (int i = 0; i < m_internal->m_nodeClipboard.size(); ++i) { // Insert copy of Node const SituationMap::GeometryModel::Node &clipboardNode = m_internal->m_nodeClipboard.at(i); const SituationMap::GeometryModel::Node ©Node = m_internal->m_geoModel->copy(clipboardNode); // Move Node some Pixels, to make insertion visible double pixelSize = m_internal->m_map->pixelSizeGeographic(); double dx = pixelSize * PasteDistance; double dy = -dx; m_internal -> m_geoModel -> translateNode( copyNode , dx, dy ); m_internal -> m_modelClipboard -> translateNode( clipboardNode, dx, dy ); } } } m_internal->m_geoModel->endUndoBlock(tr("paste", TR_UNDO_TOKEN)); ModelTreeWidgetItem::updateTree(m_internal->m_modelTreeWidget, m_internal->m_modelTreeWidget->rootItem(), m_internal->m_map.data(), *(m_internal->m_geoModel)); } // PlanEditor::onPaste() void PlanEditor::onSymbolModified( AdvancedSymbology::SymbologyType symbologyType , const QString &sidcPattern , const QString &symbolAttributes , const SituationMap::SimpleValue &participantState , const SituationMap::SimpleValue &participantPartyId , const SituationMap::SimpleValue &participantDamageId , const QMap> &participantTextModifier) { abortCurrentBboxRubberband(); m_internal->m_geoModel->startUndoBlock(); { for (SituationMap::UniqueMapId uid : qAsConst(m_internal->m_selectionList)) { const SituationMap::GeometryModel::Node &modelPrimitive = m_internal->m_geoModel->findPrimitive(uid); if (modelPrimitive.isValid()) { // ToDo: check which symbol norm is the sidc QString currentSidc = m_internal->m_geoModel->getAttribute(modelPrimitive, SituationMap::GeometryModel::Attribute::SIDC).toString(); if (AdvancedSymbology::SidcCodec::decodeSymbologyType(currentSidc) != symbologyType) { continue; } // Apply sidcPattern to sidc attribute of each selected feature for (int i = 0; i < sidcPattern.size(); ++i) { if (sidcPattern[i] != QChar(uint('*'))) currentSidc[i] = sidcPattern[i]; } #ifdef LTS_DEBUG DEBUG_LOG(m_internal->m_logger) << "SidcPattern " << sidcPattern; DEBUG_LOG(m_internal->m_logger) << "CurrentSidc " << currentSidc; DEBUG_LOG(m_internal->m_logger) << "NewSidc " << currentSidc; #endif if (SituationMap::GeometryModel::PrimitiveType::ParticipantSymbol == m_internal->m_geoModel->getType(modelPrimitive)) { // It's a participant symbol with background color as defined in symbolColor m_internal->m_geoModel->setPrimitiveAttribute(modelPrimitive, SituationMap::GeometryModel::Attribute::SIDC, currentSidc); m_internal->m_geoModel->setPrimitiveAttribute(modelPrimitive, SituationMap::GeometryModel::Attribute::SIDC_Attributes, symbolAttributes); } if (SituationMap::GeometryModel::PrimitiveType::DictionarySymbol == m_internal->m_geoModel->getType(modelPrimitive)) { // It's a symbol from ESRI DictionaryRenderer the symbol can have additinal attributes, the background color is fixed m_internal->m_geoModel->setPrimitiveAttribute(modelPrimitive, SituationMap::GeometryModel::Attribute::SIDC, currentSidc); m_internal->m_geoModel->setPrimitiveAttribute(modelPrimitive, SituationMap::GeometryModel::Attribute::SIDC_Attributes, symbolAttributes); } if (SituationMap::GeometryModel::PrimitiveType::ParticipantSymbol == m_internal->m_geoModel->getType(modelPrimitive)) { // It's a symbol from ESRI DictionaryRenderer the symbol can have additinal attributes, the background color is fixed m_internal->m_geoModel->setPrimitiveAttribute(modelPrimitive, SituationMap::GeometryModel::Attribute::SIDC, currentSidc); m_internal->m_geoModel->setPrimitiveAttribute(modelPrimitive, SituationMap::GeometryModel::Attribute::SIDC_Attributes, symbolAttributes); if (participantState.changed()) { m_internal->m_geoModel->setPrimitiveAttribute( modelPrimitive , SituationMap::GeometryModel::Attribute::Injury , static_cast(participantState.value()) ); } if (participantPartyId.changed()) { m_internal->m_geoModel->setPrimitiveAttribute(modelPrimitive, SituationMap::GeometryModel::Attribute::ForceId, participantPartyId.value()); } if (participantDamageId.changed()) { m_internal->m_geoModel->setPrimitiveAttribute(modelPrimitive, SituationMap::GeometryModel::Attribute::DamageForceId, participantDamageId.value()); } for (auto it = participantTextModifier.constBegin(); it != participantTextModifier.constEnd(); ++it) { const auto &mod = it.key(); const SituationMap::SimpleValue &value = it.value(); if (value.changed()) { SituationMap::GeometryModel::Attribute geoModelAttribute = SituationMap::GeometryModel::Attribute::None; switch (mod) { case SituationMap::Participant::TextModifier::EF_ReinforcedReduced : geoModelAttribute = SituationMap::GeometryModel::Attribute::Text_EF; break; case SituationMap::Participant::TextModifier::G_StaffComments : geoModelAttribute = SituationMap::GeometryModel::Attribute::Text_G; break; case SituationMap::Participant::TextModifier::H_AdditionalInformation : geoModelAttribute = SituationMap::GeometryModel::Attribute::Text_H; break; case SituationMap::Participant::TextModifier::JKLNP : geoModelAttribute = SituationMap::GeometryModel::Attribute::Text_KLNPJ; break; case SituationMap::Participant::TextModifier::M_HigherFormation : geoModelAttribute = SituationMap::GeometryModel::Attribute::Text_M; break; case SituationMap::Participant::TextModifier::T_UniqueDesignation : geoModelAttribute = SituationMap::GeometryModel::Attribute::Text_T; break; case SituationMap::Participant::TextModifier::V_Type : geoModelAttribute = SituationMap::GeometryModel::Attribute::Text_V; break; case SituationMap::Participant::TextModifier::W_DateTimeGroup : geoModelAttribute = SituationMap::GeometryModel::Attribute::Text_W; break; case SituationMap::Participant::TextModifier::XY_AltitudeLocation : geoModelAttribute = SituationMap::GeometryModel::Attribute::Text_XY; break; case SituationMap::Participant::TextModifier::Z_Speed : geoModelAttribute = SituationMap::GeometryModel::Attribute::Text_Z; break; case SituationMap::Participant::TextModifier::END : Q_ASSERT(false); break; } if (SituationMap::GeometryModel::Attribute::None != geoModelAttribute) { m_internal->m_geoModel->setPrimitiveAttribute(modelPrimitive, geoModelAttribute, value.value()); } } } } } // if isValid() } } m_internal->m_geoModel->endUndoBlock(tr("modify symbol sidc", TR_UNDO_TOKEN)); } // void PlanEditor::onSymbolModified( const QString & sidcPattern, const QString & symbolAttributes ) void PlanEditor::onSymbolXmlAttrModified(const QString & undoToken, SituationMap::GeometryModel::Attribute attribute, const QVariant &value) { m_internal->m_geoModel->startUndoBlock(); { for (SituationMap::UniqueMapId uid : qAsConst(m_internal->m_selectionList)) { const SituationMap::GeometryModel::Node &modelPrimitive = m_internal->m_geoModel->findPrimitive(uid); if (modelPrimitive.isValid()) { if (SituationMap::GeometryModel::PrimitiveType::Symbol == m_internal->m_geoModel->getType(modelPrimitive)) { m_internal->m_geoModel->setPrimitiveAttribute( modelPrimitive, attribute, value ); } } } } m_internal->m_geoModel->endUndoBlock(undoToken); } // void PlanEditor::onSymbolXmlAttrModified(const QString & undoToken, SituationMap::GeometryModel::Attribute attribute, const QVariant &value) //#define IMPORT_GUEZ_SYMBOLS_FROM_FILE 1 static void importGuezXMLSymbolsFromFile(const AdvancedSymbology::SymbolDictionary &symbolDict) { Q_UNUSED(symbolDict); #ifdef IMPORT_GUEZ_SYMBOLS_FROM_FILE QMap symbolIdtoPPType; const QString & dirPath = QStringLiteral("D:/Development/Projekte/SituationMap/Symbole-GUEZ"); QFile symbolDump(dirPath + QStringLiteral("/guez_symbole.unl")); QFile typeDump(dirPath + QStringLiteral("/guez_typen.unl")); if (typeDump.exists() && typeDump.open(QIODevice::ReadOnly | QIODevice::Text)) { while (true) { const QByteArray & lineData = typeDump.readLine(); if (lineData.isEmpty()) break; const QStringList &typeData = QString::fromLatin1(lineData).split(QLatin1Char('|')); if (typeData.size() > 4) { bool valid = false; int symbolId = typeData[3].toInt(&valid); if (valid) { symbolIdtoPPType[symbolId] = typeData[2]; qCritical() << typeData[2] << " --->> " << symbolId; } } } typeDump.close(); } if (symbolDump.exists() && symbolDump.open(QIODevice::ReadOnly|QIODevice::Text) ) { const QString & guezSymbolDirName = QStringLiteral("GuezSymbols"); QDir guezSymbolRoot(dirPath); QDir guezSymbolDir(dirPath + QStringLiteral("/") + guezSymbolDirName); guezSymbolDir.removeRecursively(); guezSymbolRoot.mkdir(guezSymbolDirName); QString xmlContent = QString::fromLatin1(symbolDump.readAll()); QString blockBegin = QStringLiteral(""); QString blockEnd = QStringLiteral(""); const QVector & xmlSymbolVector = xmlContent.splitRef(blockEnd,QString::SplitBehavior::SkipEmptyParts); for (auto xmlSymbolRef : qAsConst(xmlSymbolVector)) { int xmlBegin = xmlSymbolRef.indexOf(blockBegin); QString xmlSymbol = xmlSymbolRef.mid(xmlBegin).toString() + blockEnd + QStringLiteral("\n"); // replace some character to get a valid xml string: xmlSymbol.replace( QStringLiteral(" =") , QStringLiteral("=") ); xmlSymbol.replace( QStringLiteral("= ") , QStringLiteral("=") ); xmlSymbol.replace( QStringLiteral("/ >") , QStringLiteral("/>") ); xmlSymbol.replace( QStringLiteral("\\n") , QStringLiteral("\n") ); xmlSymbol.replace( QStringLiteral("\\") , QStringLiteral("") ); // skip over empty symbols int xmlEmptyCounter = xmlSymbol.indexOf(QStringLiteral("