Path: blob/main/src/netedit/frames/network/GNEConnectorFrame.cpp
169685 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2011-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file GNEConnectorFrame.cpp14/// @author Jakob Erdmann15/// @date May 201116///17// The Widget for modifying lane-to-lane connections18/****************************************************************************/1920#include <netedit/GNENet.h>21#include <netedit/GNEUndoList.h>22#include <netedit/GNEViewParent.h>23#include <netedit/changes/GNEChange_Connection.h>24#include <netedit/dialogs/basic/GNEWarningBasicDialog.h>25#include <netedit/elements/network/GNEConnection.h>26#include <netedit/frames/common/GNESelectorFrame.h>27#include <utils/foxtools/MFXDynamicLabel.h>28#include <utils/gui/div/GUIDesigns.h>29#include <utils/gui/windows/GUIAppEnum.h>3031#include "GNEConnectorFrame.h"3233// ===========================================================================34// FOX callback mapping35// ===========================================================================3637FXDEFMAP(GNEConnectorFrame::ConnectionModifications) ConnectionModificationsMap[] = {38FXMAPFUNC(SEL_COMMAND, MID_CANCEL, GNEConnectorFrame::ConnectionModifications::onCmdCancelModifications),39FXMAPFUNC(SEL_COMMAND, MID_OK, GNEConnectorFrame::ConnectionModifications::onCmdSaveModifications),40};4142FXDEFMAP(GNEConnectorFrame::ConnectionOperations) ConnectionOperationsMap[] = {43FXMAPFUNC(SEL_COMMAND, MID_CHOOSEN_CLEAR, GNEConnectorFrame::ConnectionOperations::onCmdClearSelectedConnections),44FXMAPFUNC(SEL_COMMAND, MID_CHOOSEN_RESET, GNEConnectorFrame::ConnectionOperations::onCmdResetSelectedConnections),45FXMAPFUNC(SEL_COMMAND, MID_GNE_CONNECTORFRAME_SELECTDEADENDS, GNEConnectorFrame::ConnectionOperations::onCmdSelectDeadEnds),46FXMAPFUNC(SEL_COMMAND, MID_GNE_CONNECTORFRAME_SELECTDEADSTARTS, GNEConnectorFrame::ConnectionOperations::onCmdSelectDeadStarts),47FXMAPFUNC(SEL_COMMAND, MID_GNE_CONNECTORFRAME_SELECTCONFLICTS, GNEConnectorFrame::ConnectionOperations::onCmdSelectConflicts),48FXMAPFUNC(SEL_COMMAND, MID_GNE_CONNECTORFRAME_SELECTPASS, GNEConnectorFrame::ConnectionOperations::onCmdSelectPass),49};5051// Object implementation52FXIMPLEMENT(GNEConnectorFrame::ConnectionModifications, MFXGroupBoxModule, ConnectionModificationsMap, ARRAYNUMBER(ConnectionModificationsMap))53FXIMPLEMENT(GNEConnectorFrame::ConnectionOperations, MFXGroupBoxModule, ConnectionOperationsMap, ARRAYNUMBER(ConnectionOperationsMap))545556// ===========================================================================57// method definitions58// ===========================================================================5960// ---------------------------------------------------------------------------61// GNEConnectorFrame::CurrentLane - methods62// ---------------------------------------------------------------------------6364GNEConnectorFrame::CurrentLane::CurrentLane(GNEConnectorFrame* connectorFrameParent) :65MFXGroupBoxModule(connectorFrameParent, TL("Lane")) {66// create lane label67myCurrentLaneLabel = new FXLabel(getCollapsableFrame(), TL("No lane selected"), 0, GUIDesignLabel(JUSTIFY_LEFT));68}697071GNEConnectorFrame::CurrentLane::~CurrentLane() {}727374void75GNEConnectorFrame::CurrentLane::updateCurrentLaneLabel(const std::string& laneID) {76if (laneID.empty()) {77myCurrentLaneLabel->setText(TL("No lane selected"));78} else {79myCurrentLaneLabel->setText((std::string(TL("Current Lane: ")) + laneID).c_str());80}81}8283// ---------------------------------------------------------------------------84// GNEConnectorFrame::ConnectionModifications - methods85// ---------------------------------------------------------------------------8687GNEConnectorFrame::ConnectionModifications::ConnectionModifications(GNEConnectorFrame* connectorFrameParent) :88MFXGroupBoxModule(connectorFrameParent, TL("Modifications")),89myConnectorFrameParent(connectorFrameParent) {9091// Create "Cancel" button92myCancelButton = GUIDesigns::buildFXButton(getCollapsableFrame(), TL("Cancel"), "", TL("Discard connection modifications (Esc)"),93GUIIconSubSys::getIcon(GUIIcon::CANCEL), this, MID_CANCEL, GUIDesignButton);94// Create "OK" button95mySaveButton = GUIDesigns::buildFXButton(getCollapsableFrame(), TL("OK"), "", TL("Save connection modifications (Enter)"),96GUIIconSubSys::getIcon(GUIIcon::ACCEPT), this, MID_OK, GUIDesignButton);9798// Create checkbox for protect routes99myProtectRoutesCheckBox = new FXCheckButton(getCollapsableFrame(), TL("Protect routes"), this, MID_GNE_SET_ATTRIBUTE, GUIDesignCheckButton);100}101102103GNEConnectorFrame::ConnectionModifications::~ConnectionModifications() {}104105106long107GNEConnectorFrame::ConnectionModifications::onCmdCancelModifications(FXObject*, FXSelector, void*) {108if (myConnectorFrameParent->myCurrentEditedLane != 0) {109myConnectorFrameParent->getViewNet()->getUndoList()->abortAllChangeGroups();110if (myConnectorFrameParent->myNumChanges) {111myConnectorFrameParent->getViewNet()->setStatusBarText(TL("Changes reverted"));112}113myConnectorFrameParent->cleanup();114myConnectorFrameParent->getViewNet()->updateViewNet();115}116return 1;117}118119120long121GNEConnectorFrame::ConnectionModifications::onCmdSaveModifications(FXObject*, FXSelector, void*) {122if (myConnectorFrameParent->myCurrentEditedLane != 0) {123// check if routes has to be protected124if (myProtectRoutesCheckBox->isEnabled() && (myProtectRoutesCheckBox->getCheck() == TRUE)) {125for (const auto& demandElement : myConnectorFrameParent->myCurrentEditedLane->getParentEdge()->getChildDemandElements()) {126if (demandElement->isDemandElementValid() != GNEDemandElement::Problem::OK) {127// open warning dialog128GNEWarningBasicDialog(myConnectorFrameParent->getViewNet()->getViewParent()->getGNEAppWindows(),129TL("Error saving connection operations"),130TLF("Connection edition cannot be saved because route '%s' is broken.", demandElement->getID()));131return 1;132}133}134}135// finish route editing136myConnectorFrameParent->getViewNet()->getUndoList()->end();137if (myConnectorFrameParent->myNumChanges) {138myConnectorFrameParent->getViewNet()->setStatusBarText(TL("Changes accepted"));139}140myConnectorFrameParent->cleanup();141// mark network for recomputing142myConnectorFrameParent->getViewNet()->getNet()->requireRecompute();143}144return 1;145}146147// ---------------------------------------------------------------------------148// GNEConnectorFrame::ConnectionOperations - methods149// ---------------------------------------------------------------------------150151GNEConnectorFrame::ConnectionOperations::ConnectionOperations(GNEConnectorFrame* connectorFrameParent) :152MFXGroupBoxModule(connectorFrameParent, TL("Operations")),153myConnectorFrameParent(connectorFrameParent) {154155// Create "Select Dead Ends" button156mySelectDeadEndsButton = GUIDesigns::buildFXButton(getCollapsableFrame(), TL("Select Dead Ends"), "", TL("Selects all lanes that have no outgoing connection (clears previous selection)"),1570, this, MID_GNE_CONNECTORFRAME_SELECTDEADENDS, GUIDesignButton);158// Create "Select Dead Starts" button159mySelectDeadStartsButton = GUIDesigns::buildFXButton(getCollapsableFrame(), TL("Select Dead Starts"), "", TL("Selects all lanes that have no incoming connection (clears previous selection)"),1600, this, MID_GNE_CONNECTORFRAME_SELECTDEADSTARTS, GUIDesignButton);161// Create "Select Conflicts" button162mySelectConflictsButton = GUIDesigns::buildFXButton(getCollapsableFrame(), TL("Select Conflicts"), "", TL("Selects all lanes with more than one incoming connection from the same edge (clears previous selection)"),1630, this, MID_GNE_CONNECTORFRAME_SELECTCONFLICTS, GUIDesignButton);164// Create "Select Edges which may always pass" button165mySelectPassingButton = GUIDesigns::buildFXButton(getCollapsableFrame(), TL("Select Passing"), "", TL("Selects all lanes with a connection that has the 'pass' attribute set"),1660, this, MID_GNE_CONNECTORFRAME_SELECTPASS, GUIDesignButton);167// Create "Clear Selected" button168myClearSelectedButton = GUIDesigns::buildFXButton(getCollapsableFrame(), TL("Clear Selected"), "", TL("Clears all connections of all selected objects"),1690, this, MID_CHOOSEN_CLEAR, GUIDesignButton);170// Create "Reset Selected" button171myResetSelectedButton = GUIDesigns::buildFXButton(getCollapsableFrame(), TL("Reset Selected"), "", TL("Recomputes connections at all selected junctions"),1720, this, MID_CHOOSEN_RESET, GUIDesignButton);173}174175176GNEConnectorFrame::ConnectionOperations::~ConnectionOperations() {}177178179long180GNEConnectorFrame::ConnectionOperations::onCmdSelectDeadEnds(FXObject*, FXSelector, void*) {181// select all lanes that have no successor lane182std::vector<GNEAttributeCarrier*> deadEnds;183// every edge knows its outgoing connections so we can look at each edge in isolation184for (const auto& edge : myConnectorFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getEdges()) {185for (const auto& lane : edge.second->getChildLanes()) {186if (edge.second->getNBEdge()->getConnectionsFromLane(lane->getIndex()).size() == 0) {187deadEnds.push_back(lane);188}189}190}191myConnectorFrameParent->getViewNet()->getViewParent()->getSelectorFrame()->handleIDs(deadEnds, GNESelectorFrame::ModificationMode::Operation::REPLACE);192myConnectorFrameParent->getViewNet()->updateViewNet();193return 1;194}195196197long198GNEConnectorFrame::ConnectionOperations::onCmdSelectDeadStarts(FXObject*, FXSelector, void*) {199// select all lanes that have no predecessor lane200std::set<GNEAttributeCarrier*> deadStarts;201GNENet* net = myConnectorFrameParent->getViewNet()->getNet();202// every edge knows only its outgoing connections so we look at whole junctions203for (const auto& junction : myConnectorFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getJunctions()) {204// first collect all outgoing lanes205for (const auto& outgoingEdge : junction.second->getGNEOutgoingEdges()) {206for (const auto& lane : outgoingEdge->getChildLanes()) {207deadStarts.insert(lane);208}209}210// then remove all approached lanes211for (const auto& incomingEdge : junction.second->getGNEIncomingEdges()) {212for (const auto& connection : incomingEdge->getNBEdge()->getConnections()) {213deadStarts.erase(net->getAttributeCarriers()->retrieveEdge(connection.toEdge->getID())->getChildLanes()[connection.toLane]);214}215}216}217std::vector<GNEAttributeCarrier*> selectObjects(deadStarts.begin(), deadStarts.end());218myConnectorFrameParent->getViewNet()->getViewParent()->getSelectorFrame()->handleIDs(selectObjects, GNESelectorFrame::ModificationMode::Operation::REPLACE);219myConnectorFrameParent->getViewNet()->updateViewNet();220return 1;221}222223224long225GNEConnectorFrame::ConnectionOperations::onCmdSelectConflicts(FXObject*, FXSelector, void*) {226std::vector<GNEAttributeCarrier*> conflicts;227// conflicts happen per edge so we can look at each edge in isolation228for (const auto& edge : myConnectorFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getEdges()) {229const EdgeVector destinations = edge.second->getNBEdge()->getConnectedEdges();230for (const auto& destination : destinations) {231GNEEdge* dest = myConnectorFrameParent->getViewNet()->getNet()->getAttributeCarriers()->retrieveEdge(destination->getID());232for (const auto& lane : dest->getChildLanes()) {233const bool isConflicted = count_if(edge.second->getNBEdge()->getConnections().begin(), edge.second->getNBEdge()->getConnections().end(),234NBEdge::connections_toedgelane_finder(destination, (int)lane->getIndex(), -1)) > 1;235if (isConflicted) {236conflicts.push_back(lane);237}238}239}240241}242myConnectorFrameParent->getViewNet()->getViewParent()->getSelectorFrame()->handleIDs(conflicts, GNESelectorFrame::ModificationMode::Operation::REPLACE);243myConnectorFrameParent->getViewNet()->updateViewNet();244return 1;245}246247248long249GNEConnectorFrame::ConnectionOperations::onCmdSelectPass(FXObject*, FXSelector, void*) {250std::vector<GNEAttributeCarrier*> pass;251for (const auto& edge : myConnectorFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getEdges()) {252for (const auto& connection : edge.second->getNBEdge()->getConnections()) {253if (connection.mayDefinitelyPass) {254pass.push_back(edge.second->getChildLanes()[connection.fromLane]);255}256}257}258myConnectorFrameParent->getViewNet()->getViewParent()->getSelectorFrame()->handleIDs(pass, GNESelectorFrame::ModificationMode::Operation::REPLACE);259myConnectorFrameParent->getViewNet()->updateViewNet();260return 1;261}262263264long265GNEConnectorFrame::ConnectionOperations::onCmdClearSelectedConnections(FXObject*, FXSelector, void*) {266myConnectorFrameParent->myConnectionModifications->onCmdCancelModifications(0, 0, 0);267myConnectorFrameParent->getViewNet()->getUndoList()->begin(GUIIcon::CONNECTION, TL("clear connections from selected lanes, edges and junctions"));268// clear junction's connection269const auto selectedJunctions = myConnectorFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getSelectedJunctions();270for (const auto& junction : selectedJunctions) {271junction->setLogicValid(false, myConnectorFrameParent->getViewNet()->getUndoList()); // clear connections272junction->setLogicValid(false, myConnectorFrameParent->getViewNet()->getUndoList(), GNEAttributeCarrier::FEATURE_MODIFIED); // prevent re-guessing273}274// clear edge's connection275const auto selectedEdges = myConnectorFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getSelectedEdges();276for (const auto& edge : selectedEdges) {277for (const auto& lane : edge->getChildLanes()) {278myConnectorFrameParent->removeConnections(lane);279}280}281// clear lane's connection282const auto selectedLanes = myConnectorFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getSelectedLanes();283for (const auto& lane : selectedLanes) {284myConnectorFrameParent->removeConnections(lane);285}286myConnectorFrameParent->getViewNet()->getUndoList()->end();287myConnectorFrameParent->getViewNet()->updateViewNet();288return 1;289}290291292long293GNEConnectorFrame::ConnectionOperations::onCmdResetSelectedConnections(FXObject*, FXSelector, void*) {294myConnectorFrameParent->myConnectionModifications->onCmdCancelModifications(0, 0, 0);295myConnectorFrameParent->getViewNet()->getUndoList()->begin(GUIIcon::CONNECTION, TL("reset connections from selected lanes"));296const auto selectedJunctions = myConnectorFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getSelectedJunctions();297for (const auto& junction : selectedJunctions) {298junction->setLogicValid(false, myConnectorFrameParent->getViewNet()->getUndoList());299}300myConnectorFrameParent->getViewNet()->getUndoList()->end();301if (selectedJunctions.size() > 0) {302auto viewNet = myConnectorFrameParent->getViewNet();303viewNet->getNet()->requireRecompute();304viewNet->getNet()->computeNetwork(viewNet->getViewParent()->getGNEAppWindows());305}306myConnectorFrameParent->getViewNet()->updateViewNet();307return 1;308}309310// ---------------------------------------------------------------------------311// GNEConnectorFrame::ConnectionSelection - methods312// ---------------------------------------------------------------------------313314GNEConnectorFrame::ConnectionSelection::ConnectionSelection(GNEConnectorFrame* connectorFrameParent) :315MFXGroupBoxModule(connectorFrameParent, TL("Selection")) {316// create label317new MFXDynamicLabel(getCollapsableFrame(), (std::string("- ") + TL("Hold <SHIFT> while clicking to create unyielding connections (pass=true).")).c_str(), 0, GUIDesignLabelFrameInformation);318new MFXDynamicLabel(getCollapsableFrame(), (std::string("- ") + TL("Hold <CTRL> while clicking to create conflicting connections (i.e. at zipper nodes or with incompatible permissions)")).c_str(), 0, GUIDesignLabelFrameInformation);319}320321322GNEConnectorFrame::ConnectionSelection::~ConnectionSelection() {}323324// ---------------------------------------------------------------------------325// GNEConnectorFrame::ConnectionLegend - methods326// ---------------------------------------------------------------------------327328GNEConnectorFrame::Legend::Legend(GNEConnectorFrame* connectorFrameParent) :329MFXGroupBoxModule(connectorFrameParent, TL("Information")) {330331// create possible target label332FXLabel* possibleTargetLabel = new FXLabel(getCollapsableFrame(), TL("Possible Target"), 0, GUIDesignLabel(JUSTIFY_LEFT));333possibleTargetLabel->setBackColor(MFXUtils::getFXColor(connectorFrameParent->getViewNet()->getVisualisationSettings().candidateColorSettings.possible));334possibleTargetLabel->setTextColor(MFXUtils::getFXColor(RGBColor::WHITE));335336// create source label337FXLabel* sourceLabel = new FXLabel(getCollapsableFrame(), TL("Source lane"), 0, GUIDesignLabel(JUSTIFY_LEFT));338sourceLabel->setBackColor(MFXUtils::getFXColor(connectorFrameParent->getViewNet()->getVisualisationSettings().candidateColorSettings.source));339340// create target label341FXLabel* targetLabel = new FXLabel(getCollapsableFrame(), TL("Target lane"), 0, GUIDesignLabel(JUSTIFY_LEFT));342targetLabel->setBackColor(MFXUtils::getFXColor(connectorFrameParent->getViewNet()->getVisualisationSettings().candidateColorSettings.target));343344// create target (pass) label345FXLabel* targetPassLabel = new FXLabel(getCollapsableFrame(), TL("Target (pass)"), 0, GUIDesignLabel(JUSTIFY_LEFT));346targetPassLabel->setBackColor(MFXUtils::getFXColor(connectorFrameParent->getViewNet()->getVisualisationSettings().candidateColorSettings.special));347348// create conflict label349FXLabel* conflictLabel = new FXLabel(getCollapsableFrame(), TL("Conflict"), 0, GUIDesignLabel(JUSTIFY_LEFT));350conflictLabel->setBackColor(MFXUtils::getFXColor(connectorFrameParent->getViewNet()->getVisualisationSettings().candidateColorSettings.conflict));351}352353354GNEConnectorFrame::Legend::~Legend() {}355356// ---------------------------------------------------------------------------357// GNEConnectorFrame - methods358// ---------------------------------------------------------------------------359360GNEConnectorFrame::GNEConnectorFrame(GNEViewParent* viewParent, GNEViewNet* viewNet):361GNEFrame(viewParent, viewNet, TL("Edit Connections")),362myCurrentEditedLane(0),363myNumChanges(0) {364// create current lane module365myCurrentLane = new CurrentLane(this);366367// create connection modifications module368myConnectionModifications = new ConnectionModifications(this);369370// create connection operations module371myConnectionOperations = new ConnectionOperations(this);372373// create connection selection module374myConnectionSelection = new ConnectionSelection(this);375376// create connection legend module377myLegend = new Legend(this);378}379380381GNEConnectorFrame::~GNEConnectorFrame() {}382383384void385GNEConnectorFrame::handleLaneClick(const GNEViewNetHelper::ViewObjectsSelector& viewObjects) {386// get lane front387GNELane* clickedLane = viewObjects.getLaneFrontNonLocked();388// iterate over lanes389for (const auto& lane : viewObjects.getLanes()) {390// if parent edge of lane is front element, update clickedLane391if (lane->getParentEdge()->isMarkedForDrawingFront()) {392clickedLane = lane;393}394}395// build connection396buildConnection(clickedLane, myViewNet->getMouseButtonKeyPressed().shiftKeyPressed(), myViewNet->getMouseButtonKeyPressed().controlKeyPressed(), true);397}398399400GNEConnectorFrame::ConnectionModifications*401GNEConnectorFrame::getConnectionModifications() const {402return myConnectionModifications;403}404405406void407GNEConnectorFrame::removeConnections(GNELane* lane) {408// select lane as current lane409buildConnection(lane, false, false, true); // select as current lane410// iterate over all potential targets411for (const auto& potentialTarget : myPotentialTargets) {412// remove connections using the appropiate parameters in function "buildConnection"413buildConnection(potentialTarget, false, false, false);414}415// save modifications416myConnectionModifications->onCmdSaveModifications(0, 0, 0);417}418419420void421GNEConnectorFrame::buildConnection(GNELane* lane, const bool mayDefinitelyPass, const bool allowConflict, const bool toggle) {422if (myCurrentEditedLane == 0) {423myCurrentEditedLane = lane;424myCurrentEditedLane->setSpecialColor(&myViewNet->getVisualisationSettings().candidateColorSettings.source);425initTargets();426myNumChanges = 0;427myViewNet->getUndoList()->begin(GUIIcon::CONNECTION, TL("modify connections"));428} else if (myPotentialTargets.count(lane)429|| (allowConflict && lane->getParentEdge()->getFromJunction() == myCurrentEditedLane->getParentEdge()->getToJunction())) {430const int fromIndex = myCurrentEditedLane->getIndex();431GNEEdge* srcEdge = myCurrentEditedLane->getParentEdge();432GNEEdge* destEdge = lane->getParentEdge();433std::vector<NBEdge::Connection> connections = srcEdge->getNBEdge()->getConnectionsFromLane(fromIndex);434bool changed = false;435// get lane status436LaneStatus status = getLaneStatus(connections, lane);437if (status == LaneStatus::CONFLICTED && allowConflict) {438status = LaneStatus::UNCONNECTED;439}440// create depending of status441switch (status) {442case LaneStatus::UNCONNECTED:443if (toggle) {444// create new connection445NBEdge::Connection newCon(fromIndex, destEdge->getNBEdge(), lane->getIndex(), mayDefinitelyPass);446// if the connection was previously deleted (by clicking the same lane twice), restore all values447for (NBEdge::Connection& c : myDeletedConnections) {448// fromLane must be the same, only check toLane449if (c.toEdge == destEdge->getNBEdge() && c.toLane == lane->getIndex()) {450newCon = c;451newCon.mayDefinitelyPass = mayDefinitelyPass;452}453}454NBConnection newNBCon(srcEdge->getNBEdge(), fromIndex, destEdge->getNBEdge(), lane->getIndex(), newCon.tlLinkIndex);455myViewNet->getUndoList()->add(new GNEChange_Connection(srcEdge, newCon, false, true), true);456if (mayDefinitelyPass) {457lane->setSpecialColor(&myViewNet->getVisualisationSettings().candidateColorSettings.special);458} else {459lane->setSpecialColor(&myViewNet->getVisualisationSettings().candidateColorSettings.target);460}461srcEdge->getToJunction()->invalidateTLS(myViewNet->getUndoList(), NBConnection::InvalidConnection, newNBCon);462}463break;464case LaneStatus::CONNECTED:465case LaneStatus::CONNECTED_PASS: {466// remove connection467GNEConnection* con = srcEdge->retrieveGNEConnection(fromIndex, destEdge->getNBEdge(), lane->getIndex());468myDeletedConnections.push_back(con->getNBEdgeConnection());469myViewNet->getNet()->deleteConnection(con, myViewNet->getUndoList());470lane->setSpecialColor(&myViewNet->getVisualisationSettings().candidateColorSettings.possible);471changed = true;472break;473}474case LaneStatus::CONFLICTED:475SVCPermissions fromPermissions = srcEdge->getNBEdge()->getPermissions(fromIndex);476SVCPermissions toPermissions = destEdge->getNBEdge()->getPermissions(lane->getIndex());477if ((fromPermissions & toPermissions) == SVC_PEDESTRIAN) {478myViewNet->setStatusBarText(TL("Pedestrian connections are generated automatically"));479} else if ((fromPermissions & toPermissions) == 0) {480myViewNet->setStatusBarText(TL("Incompatible vehicle class permissions"));481} else {482myViewNet->setStatusBarText(TL("Another lane from the same edge already connects to that lane"));483}484break;485}486if (changed) {487myNumChanges += 1;488}489} else {490myViewNet->setStatusBarText(TL("Invalid target for connection"));491}492myCurrentLane->updateCurrentLaneLabel(myCurrentEditedLane->getID());493}494495496void497GNEConnectorFrame::initTargets() {498// gather potential targets499NBNode* nbn = myCurrentEditedLane->getParentEdge()->getToJunction()->getNBNode();500// get potential targets501for (const auto& NBEEdge : nbn->getOutgoingEdges()) {502GNEEdge* edge = myViewNet->getNet()->getAttributeCarriers()->retrieveEdge(NBEEdge->getID());503for (const auto& lane : edge->getChildLanes()) {504myPotentialTargets.insert(lane);505}506}507// set color for existing connections508std::vector<NBEdge::Connection> connections = myCurrentEditedLane->getParentEdge()->getNBEdge()->getConnectionsFromLane(myCurrentEditedLane->getIndex());509for (const auto& lane : myPotentialTargets) {510switch (getLaneStatus(connections, lane)) {511case LaneStatus::CONNECTED:512lane->setSpecialColor(&myViewNet->getVisualisationSettings().candidateColorSettings.target);513break;514case LaneStatus::CONNECTED_PASS:515lane->setSpecialColor(&myViewNet->getVisualisationSettings().candidateColorSettings.special);516break;517case LaneStatus::CONFLICTED:518lane->setSpecialColor(&myViewNet->getVisualisationSettings().candidateColorSettings.conflict);519break;520case LaneStatus::UNCONNECTED:521lane->setSpecialColor(&myViewNet->getVisualisationSettings().candidateColorSettings.possible);522break;523}524}525}526527528void529GNEConnectorFrame::cleanup() {530// restore colors of potential targets531for (auto it : myPotentialTargets) {532it->setSpecialColor(0);533}534// clear attributes535myPotentialTargets.clear();536myNumChanges = 0;537myCurrentEditedLane->setSpecialColor(0);538myCurrentEditedLane = nullptr;539myDeletedConnections.clear();540myCurrentLane->updateCurrentLaneLabel("");541}542543544GNEConnectorFrame::LaneStatus545GNEConnectorFrame::getLaneStatus(const std::vector<NBEdge::Connection>& connections, const GNELane* targetLane) const {546NBEdge* srcEdge = myCurrentEditedLane->getParentEdge()->getNBEdge();547const int fromIndex = myCurrentEditedLane->getIndex();548NBEdge* destEdge = targetLane->getParentEdge()->getNBEdge();549const int toIndex = targetLane->getIndex();550std::vector<NBEdge::Connection>::const_iterator con_it = find_if(551connections.begin(), connections.end(),552NBEdge::connections_finder(fromIndex, destEdge, toIndex));553const bool isConnected = con_it != connections.end();554if (isConnected) {555if (con_it->mayDefinitelyPass) {556return LaneStatus::CONNECTED_PASS;557} else {558return LaneStatus::CONNECTED;559}560} else if (srcEdge->hasConnectionTo(destEdge, toIndex)561|| (srcEdge->getPermissions(fromIndex) & destEdge->getPermissions(toIndex) & ~SVC_PEDESTRIAN) == 0) {562return LaneStatus::CONFLICTED;563} else {564return LaneStatus::UNCONNECTED;565}566}567568569/****************************************************************************/570571572