1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2011-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    GNEConnectorFrame.cpp
11 /// @author  Jakob Erdmann
12 /// @date    May 2011
13 /// @version $Id$
14 ///
15 // The Widget for modifying lane-to-lane connections
16 /****************************************************************************/
17 
18 
19 // ===========================================================================
20 // included modules
21 // ===========================================================================
22 #include <config.h>
23 
24 #include <utils/foxtools/MFXUtils.h>
25 #include <utils/gui/windows/GUIAppEnum.h>
26 #include <utils/gui/div/GUIDesigns.h>
27 #include <utils/gui/images/GUIIconSubSys.h>
28 #include <netedit/changes/GNEChange_Connection.h>
29 #include <netedit/GNEViewParent.h>
30 #include <netedit/GNEUndoList.h>
31 #include <netedit/GNENet.h>
32 #include <netedit/GNEViewNet.h>
33 #include <netedit/netelements/GNELane.h>
34 #include <netedit/netelements/GNEConnection.h>
35 #include <netedit/netelements/GNEEdge.h>
36 #include <netedit/netelements/GNEJunction.h>
37 #include <netedit/demandelements/GNEDemandElement.h>
38 
39 #include "GNEConnectorFrame.h"
40 #include "GNESelectorFrame.h"
41 
42 
43 // ===========================================================================
44 // FOX callback mapping
45 // ===========================================================================
46 
47 FXDEFMAP(GNEConnectorFrame::ConnectionModifications) ConnectionModificationsMap[] = {
48     FXMAPFUNC(SEL_COMMAND,  MID_CANCEL,     GNEConnectorFrame::ConnectionModifications::onCmdCancelModifications),
49     FXMAPFUNC(SEL_COMMAND,  MID_OK,         GNEConnectorFrame::ConnectionModifications::onCmdSaveModifications),
50 };
51 
52 FXDEFMAP(GNEConnectorFrame::ConnectionOperations) ConnectionOperationsMap[] = {
53     FXMAPFUNC(SEL_COMMAND,  MID_CHOOSEN_CLEAR,                          GNEConnectorFrame::ConnectionOperations::onCmdClearSelectedConnections),
54     FXMAPFUNC(SEL_COMMAND,  MID_CHOOSEN_RESET,                          GNEConnectorFrame::ConnectionOperations::onCmdResetSelectedConnections),
55     FXMAPFUNC(SEL_COMMAND,  MID_GNE_CONNECTORFRAME_SELECTDEADENDS,      GNEConnectorFrame::ConnectionOperations::onCmdSelectDeadEnds),
56     FXMAPFUNC(SEL_COMMAND,  MID_GNE_CONNECTORFRAME_SELECTDEADSTARTS,    GNEConnectorFrame::ConnectionOperations::onCmdSelectDeadStarts),
57     FXMAPFUNC(SEL_COMMAND,  MID_GNE_CONNECTORFRAME_SELECTCONFLICTS,     GNEConnectorFrame::ConnectionOperations::onCmdSelectConflicts),
58     FXMAPFUNC(SEL_COMMAND,  MID_GNE_CONNECTORFRAME_SELECTPASS,          GNEConnectorFrame::ConnectionOperations::onCmdSelectPass),
59 };
60 
61 // Object implementation
FXIMPLEMENT(GNEConnectorFrame::ConnectionModifications,FXGroupBox,ConnectionModificationsMap,ARRAYNUMBER (ConnectionModificationsMap))62 FXIMPLEMENT(GNEConnectorFrame::ConnectionModifications, FXGroupBox, ConnectionModificationsMap, ARRAYNUMBER(ConnectionModificationsMap))
63 FXIMPLEMENT(GNEConnectorFrame::ConnectionOperations,    FXGroupBox, ConnectionOperationsMap,    ARRAYNUMBER(ConnectionOperationsMap))
64 
65 
66 // ===========================================================================
67 // method definitions
68 // ===========================================================================
69 
70 // ---------------------------------------------------------------------------
71 // GNEConnectorFrame::CurrentLane - methods
72 // ---------------------------------------------------------------------------
73 
74 GNEConnectorFrame::CurrentLane::CurrentLane(GNEConnectorFrame* connectorFrameParent) :
75     FXGroupBox(connectorFrameParent->myContentFrame, "Lane", GUIDesignGroupBoxFrame) {
76     // create lane label
77     myCurrentLaneLabel = new FXLabel(this, "No lane selected", 0, GUIDesignLabelLeft);
78 }
79 
80 
~CurrentLane()81 GNEConnectorFrame::CurrentLane::~CurrentLane() {}
82 
83 
84 void
updateCurrentLaneLabel(const std::string & laneID)85 GNEConnectorFrame::CurrentLane::updateCurrentLaneLabel(const std::string& laneID) {
86     if (laneID.empty()) {
87         myCurrentLaneLabel->setText("No lane selected");
88     } else {
89         myCurrentLaneLabel->setText((std::string("Current Lane: ") + laneID).c_str());
90     }
91 }
92 
93 // ---------------------------------------------------------------------------
94 // GNEConnectorFrame::ConnectionModifications - methods
95 // ---------------------------------------------------------------------------
96 
ConnectionModifications(GNEConnectorFrame * connectorFrameParent)97 GNEConnectorFrame::ConnectionModifications::ConnectionModifications(GNEConnectorFrame* connectorFrameParent) :
98     FXGroupBox(connectorFrameParent->myContentFrame, "Modifications", GUIDesignGroupBoxFrame),
99     myConnectorFrameParent(connectorFrameParent) {
100 
101     // Create "Cancel" button
102     myCancelButton = new FXButton(this, "Cancel\t\tDiscard connection modifications (Esc)",
103                                   GUIIconSubSys::getIcon(ICON_CANCEL), this, MID_CANCEL, GUIDesignButton);
104     // Create "OK" button
105     mySaveButton = new FXButton(this, "OK\t\tSave connection modifications (Enter)",
106                                 GUIIconSubSys::getIcon(ICON_ACCEPT), this, MID_OK, GUIDesignButton);
107 
108     // Create checkbox for protect routes
109     myProtectRoutesCheckBox = new FXCheckButton(this, "Protect routes", this, MID_GNE_SET_ATTRIBUTE, GUIDesignCheckButtonAttribute);
110 }
111 
112 
~ConnectionModifications()113 GNEConnectorFrame::ConnectionModifications::~ConnectionModifications() {}
114 
115 
116 long
onCmdCancelModifications(FXObject *,FXSelector,void *)117 GNEConnectorFrame::ConnectionModifications::onCmdCancelModifications(FXObject*, FXSelector, void*) {
118     if (myConnectorFrameParent->myCurrentEditedLane != 0) {
119         myConnectorFrameParent->getViewNet()->getUndoList()->p_abort();
120         if (myConnectorFrameParent->myNumChanges) {
121             myConnectorFrameParent->getViewNet()->setStatusBarText("Changes reverted");
122         }
123         myConnectorFrameParent->cleanup();
124         myConnectorFrameParent->getViewNet()->update();
125     }
126     return 1;
127 }
128 
129 
130 long
onCmdSaveModifications(FXObject *,FXSelector,void *)131 GNEConnectorFrame::ConnectionModifications::onCmdSaveModifications(FXObject*, FXSelector, void*) {
132     if (myConnectorFrameParent->myCurrentEditedLane != 0) {
133         // check if routes has to be protected
134         if (myProtectRoutesCheckBox->isEnabled() && (myProtectRoutesCheckBox->getCheck() == TRUE)) {
135             for (const auto& i : myConnectorFrameParent->myCurrentEditedLane->getParentEdge().getDemandElementChilds()) {
136                 if (!i->isDemandElementValid()) {
137                     FXMessageBox::warning(getApp(), MBOX_OK,
138                                           "Error saving connection operations", "%s",
139                                           ("Connection edition  cannot be saved because route '" + i->getID() + "' is broken.").c_str());
140                     return 1;
141                 }
142             }
143         }
144         // finish route editing
145         myConnectorFrameParent->getViewNet()->getUndoList()->p_end();
146         if (myConnectorFrameParent->myNumChanges) {
147             myConnectorFrameParent->getViewNet()->setStatusBarText("Changes accepted");
148         }
149         myConnectorFrameParent->cleanup();
150         myConnectorFrameParent->getViewNet()->update();
151     }
152     return 1;
153 }
154 
155 // ---------------------------------------------------------------------------
156 // GNEConnectorFrame::ConnectionOperations - methods
157 // ---------------------------------------------------------------------------
158 
ConnectionOperations(GNEConnectorFrame * connectorFrameParent)159 GNEConnectorFrame::ConnectionOperations::ConnectionOperations(GNEConnectorFrame* connectorFrameParent) :
160     FXGroupBox(connectorFrameParent->myContentFrame, "Operations", GUIDesignGroupBoxFrame),
161     myConnectorFrameParent(connectorFrameParent) {
162 
163     // Create "Select Dead Ends" button
164     mySelectDeadEndsButton = new FXButton(this, "Select Dead Ends\t\tSelects all lanes that have no outgoing connection (clears previous selection)",
165                                           0, this, MID_GNE_CONNECTORFRAME_SELECTDEADENDS, GUIDesignButton);
166     // Create "Select Dead Starts" button
167     mySelectDeadStartsButton = new FXButton(this, "Select Dead Starts\t\tSelects all lanes that have no incoming connection (clears previous selection)",
168                                             0, this, MID_GNE_CONNECTORFRAME_SELECTDEADSTARTS, GUIDesignButton);
169     // Create "Select Conflicts" button
170     mySelectConflictsButton = new FXButton(this, "Select Conflicts\t\tSelects all lanes with more than one incoming connection from the same edge (clears previous selection)",
171                                            0, this, MID_GNE_CONNECTORFRAME_SELECTCONFLICTS, GUIDesignButton);
172     // Create "Select Edges which may always pass" button
173     mySelectPassingButton = new FXButton(this, "Select Passing\t\tSelects all lanes with a connection that has has the 'pass' attribute set",
174                                          0, this, MID_GNE_CONNECTORFRAME_SELECTPASS, GUIDesignButton);
175     // Create "Clear Selected" button
176     myClearSelectedButton = new FXButton(this, "Clear Selected\t\tClears all connections of all selected objects",
177                                          0, this, MID_CHOOSEN_CLEAR, GUIDesignButton);
178     // Create "Reset Selected" button
179     myResetSelectedButton = new FXButton(this, "Reset Selected\t\tRecomputes connections at all selected junctions",
180                                          0, this, MID_CHOOSEN_RESET, GUIDesignButton);
181 }
182 
183 
~ConnectionOperations()184 GNEConnectorFrame::ConnectionOperations::~ConnectionOperations() {}
185 
186 
187 long
onCmdSelectDeadEnds(FXObject *,FXSelector,void *)188 GNEConnectorFrame::ConnectionOperations::onCmdSelectDeadEnds(FXObject*, FXSelector, void*) {
189     std::vector<GNEAttributeCarrier*> deadEnds;
190     // every edge knows its outgoing connections so we can look at each edge in isolation
191     const std::vector<GNEEdge*> edges = myConnectorFrameParent->getViewNet()->getNet()->retrieveEdges();
192     for (auto i : edges) {
193         for (auto j : i->getLanes()) {
194             if (i->getNBEdge()->getConnectionsFromLane(j->getIndex()).size() == 0) {
195                 deadEnds.push_back(j);
196             }
197         }
198     }
199     myConnectorFrameParent->getViewNet()->getViewParent()->getSelectorFrame()->handleIDs(deadEnds, GNESelectorFrame::ModificationMode::SET_REPLACE);
200     return 1;
201 }
202 
203 
204 long
onCmdSelectDeadStarts(FXObject *,FXSelector,void *)205 GNEConnectorFrame::ConnectionOperations::onCmdSelectDeadStarts(FXObject*, FXSelector, void*) {
206     std::vector<GNEAttributeCarrier*> deadStarts;
207     // every edge knows only its outgoing connections so we look at whole junctions
208     const std::vector<GNEJunction*> junctions = myConnectorFrameParent->getViewNet()->getNet()->retrieveJunctions();
209     for (auto i : junctions) {
210         // first collect all outgoing lanes
211         for (auto j : i->getNBNode()->getOutgoingEdges()) {
212             GNEEdge* edge = myConnectorFrameParent->getViewNet()->getNet()->retrieveEdge(j->getID());
213             for (auto k : edge->getLanes()) {
214                 deadStarts.push_back(k);
215             }
216         }
217         // then remove all approached lanes
218         for (auto j : i->getNBNode()->getIncomingEdges()) {
219             GNEEdge* edge = myConnectorFrameParent->getViewNet()->getNet()->retrieveEdge(j->getID());
220             for (auto k : edge->getNBEdge()->getConnections()) {
221                 deadStarts.push_back(myConnectorFrameParent->getViewNet()->getNet()->retrieveEdge(k.toEdge->getID())->getLanes()[k.toLane]);
222             }
223         }
224     }
225     myConnectorFrameParent->getViewNet()->getViewParent()->getSelectorFrame()->handleIDs(deadStarts, GNESelectorFrame::ModificationMode::SET_REPLACE);
226     return 1;
227 }
228 
229 
230 long
onCmdSelectConflicts(FXObject *,FXSelector,void *)231 GNEConnectorFrame::ConnectionOperations::onCmdSelectConflicts(FXObject*, FXSelector, void*) {
232     std::vector<GNEAttributeCarrier*> conflicts;
233     // conflicts happen per edge so we can look at each edge in isolation
234     const std::vector<GNEEdge*> edges = myConnectorFrameParent->getViewNet()->getNet()->retrieveEdges();
235     for (auto i : edges) {
236         const EdgeVector destinations = i->getNBEdge()->getConnectedEdges();
237         for (auto j : destinations) {
238             GNEEdge* dest = myConnectorFrameParent->getViewNet()->getNet()->retrieveEdge(j->getID());
239             for (auto k : dest->getLanes()) {
240                 const bool isConflicted = count_if(i->getNBEdge()->getConnections().begin(), i->getNBEdge()->getConnections().end(),
241                                                    NBEdge::connections_toedgelane_finder(j, (int)(k)->getIndex(), -1)) > 1;
242                 if (isConflicted) {
243                     conflicts.push_back(k);
244                 }
245             }
246         }
247 
248     }
249     myConnectorFrameParent->getViewNet()->getViewParent()->getSelectorFrame()->handleIDs(conflicts, GNESelectorFrame::ModificationMode::SET_REPLACE);
250     return 1;
251 }
252 
253 
254 long
onCmdSelectPass(FXObject *,FXSelector,void *)255 GNEConnectorFrame::ConnectionOperations::onCmdSelectPass(FXObject*, FXSelector, void*) {
256     std::vector<GNEAttributeCarrier*> pass;
257     const std::vector<GNEEdge*> edges = myConnectorFrameParent->getViewNet()->getNet()->retrieveEdges();
258     for (auto i : edges) {
259         for (auto j : i->getNBEdge()->getConnections()) {
260             if (j.mayDefinitelyPass) {
261                 pass.push_back(i->getLanes()[j.fromLane]);
262             }
263         }
264     }
265     myConnectorFrameParent->getViewNet()->getViewParent()->getSelectorFrame()->handleIDs(pass, GNESelectorFrame::ModificationMode::SET_REPLACE);
266     return 1;
267 }
268 
269 
270 long
onCmdClearSelectedConnections(FXObject *,FXSelector,void *)271 GNEConnectorFrame::ConnectionOperations::onCmdClearSelectedConnections(FXObject*, FXSelector, void*) {
272     myConnectorFrameParent->myConnectionModifications->onCmdCancelModifications(0, 0, 0);
273     myConnectorFrameParent->getViewNet()->getUndoList()->p_begin("clear connections from selected lanes, edges and " + toString(SUMO_TAG_JUNCTION) + "s");
274     // clear junction's connection
275     auto junctions = myConnectorFrameParent->getViewNet()->getNet()->retrieveJunctions(true);
276     for (auto i : junctions) {
277         i->setLogicValid(false, myConnectorFrameParent->getViewNet()->getUndoList()); // clear connections
278         i->setLogicValid(false, myConnectorFrameParent->getViewNet()->getUndoList(), GNEAttributeCarrier::FEATURE_MODIFIED); // prevent re-guessing
279     }
280     // clear edge's connection
281     auto edges = myConnectorFrameParent->getViewNet()->getNet()->retrieveEdges(true);
282     for (auto i : edges) {
283         for (auto j : i->getLanes()) {
284             myConnectorFrameParent->removeConnections(j);
285         }
286     }
287     // clear lane's connection
288     auto lanes = myConnectorFrameParent->getViewNet()->getNet()->retrieveLanes(true);
289     for (auto i : lanes) {
290         myConnectorFrameParent->removeConnections(dynamic_cast<GNELane*>(i));
291     }
292     myConnectorFrameParent->getViewNet()->getUndoList()->p_end();
293     return 1;
294 }
295 
296 
297 long
onCmdResetSelectedConnections(FXObject *,FXSelector,void *)298 GNEConnectorFrame::ConnectionOperations::onCmdResetSelectedConnections(FXObject*, FXSelector, void*) {
299     myConnectorFrameParent->myConnectionModifications->onCmdCancelModifications(0, 0, 0);
300     myConnectorFrameParent->getViewNet()->getUndoList()->p_begin("reset connections from selected lanes");
301     auto junctions = myConnectorFrameParent->getViewNet()->getNet()->retrieveJunctions(true);
302     for (auto i : junctions) {
303         i->setLogicValid(false, myConnectorFrameParent->getViewNet()->getUndoList());
304     }
305     myConnectorFrameParent->getViewNet()->getUndoList()->p_end();
306     return 1;
307 }
308 
309 // ---------------------------------------------------------------------------
310 // GNEConnectorFrame::ConnectionSelection - methods
311 // ---------------------------------------------------------------------------
312 
ConnectionSelection(GNEConnectorFrame * connectorFrameParent)313 GNEConnectorFrame::ConnectionSelection::ConnectionSelection(GNEConnectorFrame* connectorFrameParent) :
314     FXGroupBox(connectorFrameParent->myContentFrame, "Selection", GUIDesignGroupBoxFrame) {
315     // create Selection Hint
316     myHoldShiftLabel = new FXLabel(this, "Hold <SHIFT> while clicking\nto create unyielding\nconnections (pass=true).", 0, GUIDesignLabelFrameInformation);
317     myHoldControlLabel = new FXLabel(this, "Hold <CTRL> while clicking\nto create conflicting\nconnections (i.e. at zipper\nnodes or with incompatible\npermissions)", 0, GUIDesignLabelFrameInformation);
318 }
319 
320 
~ConnectionSelection()321 GNEConnectorFrame::ConnectionSelection::~ConnectionSelection() {}
322 
323 // ---------------------------------------------------------------------------
324 // GNEConnectorFrame::ConnectionLegend - methods
325 // ---------------------------------------------------------------------------
326 
ConnectionLegend(GNEConnectorFrame * connectorFrameParent)327 GNEConnectorFrame::ConnectionLegend::ConnectionLegend(GNEConnectorFrame* connectorFrameParent) :
328     FXGroupBox(connectorFrameParent->myContentFrame, "Legend", GUIDesignGroupBoxFrame),
329     mySourceColor(RGBColor::CYAN),
330     myTargetColor(RGBColor::GREEN),
331     myPotentialTargetColor(RGBColor(0, 64, 0, 255)),
332     myTargetPassColor(RGBColor::MAGENTA),
333     myConflictColor(RGBColor::YELLOW) {
334 
335     // create source label
336     mySourceLabel = new FXLabel(this, "Source lane", 0, GUIDesignLabelLeft);
337     mySourceLabel->setBackColor(MFXUtils::getFXColor(mySourceColor));
338 
339     // create target label
340     myTargetLabel = new FXLabel(this, "Target lane", 0, GUIDesignLabelLeft);
341     myTargetLabel->setBackColor(MFXUtils::getFXColor(myTargetColor));
342 
343     // create possible target label
344     myPossibleTargetLabel = new FXLabel(this, "Possible Target", 0, GUIDesignLabelLeft);
345     myPossibleTargetLabel->setBackColor(MFXUtils::getFXColor(myPotentialTargetColor));
346 
347     // create target (pass) label
348     myTargetPassLabel = new FXLabel(this, "Target (pass)", 0, GUIDesignLabelLeft);
349     myTargetPassLabel->setBackColor(MFXUtils::getFXColor(myTargetPassColor));
350 
351     // create conflict label
352     myConflictLabel = new FXLabel(this, "Conflict", 0, GUIDesignLabelLeft);
353     myConflictLabel->setBackColor(MFXUtils::getFXColor(myConflictColor));
354 }
355 
356 
~ConnectionLegend()357 GNEConnectorFrame::ConnectionLegend::~ConnectionLegend() {}
358 
359 
360 const RGBColor&
getSourceColor() const361 GNEConnectorFrame::ConnectionLegend::getSourceColor() const {
362     return mySourceColor;
363 }
364 
365 
366 const RGBColor&
getTargetColor() const367 GNEConnectorFrame::ConnectionLegend::getTargetColor() const {
368     return myTargetColor;
369 }
370 
371 
372 const RGBColor&
getPotentialTargetColor() const373 GNEConnectorFrame::ConnectionLegend::getPotentialTargetColor() const {
374     return myPotentialTargetColor;
375 }
376 
377 
378 const RGBColor&
getTargetPassColor() const379 GNEConnectorFrame::ConnectionLegend::getTargetPassColor() const {
380     return myTargetPassColor;
381 }
382 
383 
384 const RGBColor&
getConflictColor() const385 GNEConnectorFrame::ConnectionLegend::getConflictColor() const {
386     return myConflictColor;
387 }
388 
389 // ---------------------------------------------------------------------------
390 // GNEConnectorFrame - methods
391 // ---------------------------------------------------------------------------
392 
GNEConnectorFrame(FXHorizontalFrame * horizontalFrameParent,GNEViewNet * viewNet)393 GNEConnectorFrame::GNEConnectorFrame(FXHorizontalFrame* horizontalFrameParent, GNEViewNet* viewNet):
394     GNEFrame(horizontalFrameParent, viewNet, "Edit Connections"),
395     myCurrentEditedLane(0) {
396     // create current lane modul
397     myCurrentLane = new CurrentLane(this);
398 
399     // create connection modifications modul
400     myConnectionModifications = new ConnectionModifications(this);
401 
402     // create connection operations modul
403     myConnectionOperations = new ConnectionOperations(this);
404 
405     // create connection selection modul
406     myConnectionSelection = new ConnectionSelection(this);
407 
408     // create connection legend modul
409     myConnectionLegend = new ConnectionLegend(this);
410 }
411 
412 
~GNEConnectorFrame()413 GNEConnectorFrame::~GNEConnectorFrame() {}
414 
415 
416 void
handleLaneClick(const GNEViewNetHelper::ObjectsUnderCursor & objectsUnderCursor)417 GNEConnectorFrame::handleLaneClick(const GNEViewNetHelper::ObjectsUnderCursor& objectsUnderCursor) {
418     // build connection
419     buildConnection(objectsUnderCursor.getLaneFront(), myViewNet->getKeyPressed().shiftKeyPressed(), myViewNet->getKeyPressed().controlKeyPressed(), true);
420 }
421 
422 
423 GNEConnectorFrame::ConnectionModifications*
getConnectionModifications() const424 GNEConnectorFrame::getConnectionModifications() const {
425     return myConnectionModifications;
426 }
427 
428 
429 void
removeConnections(GNELane * lane)430 GNEConnectorFrame::removeConnections(GNELane* lane) {
431     // select lane as current lane
432     buildConnection(lane, false, false, true); // select as current lane
433     // iterate over all potential targets
434     for (auto i : myPotentialTargets) {
435         // remove connections using the apropiate parameters in function "buildConnection"
436         buildConnection(i, false, false, false);
437     }
438     // save modifications
439     myConnectionModifications->onCmdSaveModifications(0, 0, 0);
440 }
441 
442 
443 void
buildConnection(GNELane * lane,bool mayDefinitelyPass,bool allowConflict,bool toggle)444 GNEConnectorFrame::buildConnection(GNELane* lane, bool mayDefinitelyPass, bool allowConflict, bool toggle) {
445     if (myCurrentEditedLane == 0) {
446         myCurrentEditedLane = lane;
447         myCurrentEditedLane->setSpecialColor(&myConnectionLegend->getSourceColor());
448         initTargets();
449         myNumChanges = 0;
450         myViewNet->getUndoList()->p_begin("modify " + toString(SUMO_TAG_CONNECTION) + "s");
451     } else if (myPotentialTargets.count(lane)
452                || (allowConflict && lane->getParentEdge().getGNEJunctionSource() == myCurrentEditedLane->getParentEdge().getGNEJunctionDestiny())) {
453         const int fromIndex = myCurrentEditedLane->getIndex();
454         GNEEdge& srcEdge = myCurrentEditedLane->getParentEdge();
455         GNEEdge& destEdge = lane->getParentEdge();
456         std::vector<NBEdge::Connection> connections = srcEdge.getNBEdge()->getConnectionsFromLane(fromIndex);
457         bool changed = false;
458         LaneStatus status = getLaneStatus(connections, lane);
459         if (status == CONFLICTED && allowConflict) {
460             status = UNCONNECTED;
461         }
462         switch (status) {
463             case UNCONNECTED:
464                 if (toggle) {
465                     // create new connection
466                     NBEdge::Connection newCon(fromIndex, destEdge.getNBEdge(), lane->getIndex(), mayDefinitelyPass);
467                     // if the connection was previously deleted (by clicking the same lane twice), restore all values
468                     for (NBEdge::Connection& c : myDeletedConnections) {
469                         // fromLane must be the same, only check toLane
470                         if (c.toEdge == destEdge.getNBEdge() && c.toLane == lane->getIndex()) {
471                             newCon = c;
472                             newCon.mayDefinitelyPass = mayDefinitelyPass;
473                         }
474                     }
475                     NBConnection newNBCon(srcEdge.getNBEdge(), fromIndex, destEdge.getNBEdge(), lane->getIndex(), newCon.tlLinkIndex);
476                     myViewNet->getUndoList()->add(new GNEChange_Connection(&srcEdge, newCon, false, true), true);
477                     lane->setSpecialColor(mayDefinitelyPass ? &myConnectionLegend->getTargetPassColor() : &myConnectionLegend->getTargetColor());
478                     srcEdge.getGNEJunctionDestiny()->invalidateTLS(myViewNet->getUndoList(), NBConnection::InvalidConnection, newNBCon);
479                 }
480                 break;
481             case CONNECTED:
482             case CONNECTED_PASS: {
483                 // remove connection
484                 GNEConnection* con = srcEdge.retrieveGNEConnection(fromIndex, destEdge.getNBEdge(), lane->getIndex());
485                 myDeletedConnections.push_back(con->getNBEdgeConnection());
486                 myViewNet->getNet()->deleteConnection(con, myViewNet->getUndoList());
487                 lane->setSpecialColor(&myConnectionLegend->getPotentialTargetColor());
488                 changed = true;
489                 break;
490             }
491             case CONFLICTED:
492                 SVCPermissions fromPermissions = srcEdge.getNBEdge()->getPermissions(fromIndex);
493                 SVCPermissions toPermissions = destEdge.getNBEdge()->getPermissions(lane->getIndex());
494                 if ((fromPermissions & toPermissions) == SVC_PEDESTRIAN) {
495                     myViewNet->setStatusBarText("Pedestrian connections are generated automatically");
496                 } else if ((fromPermissions & toPermissions) == 0) {
497                     myViewNet->setStatusBarText("Incompatible vehicle class permissions");
498                 } else {
499                     myViewNet->setStatusBarText("Another lane from the same edge already connects to that lane");
500                 }
501                 break;
502         }
503         if (changed) {
504             myNumChanges += 1;
505         }
506     } else {
507         myViewNet->setStatusBarText("Invalid target for " + toString(SUMO_TAG_CONNECTION));
508     }
509     myCurrentLane->updateCurrentLaneLabel(myCurrentEditedLane->getID());
510 }
511 
512 
513 void
initTargets()514 GNEConnectorFrame::initTargets() {
515     // gather potential targets
516     NBNode* nbn = myCurrentEditedLane->getParentEdge().getGNEJunctionDestiny()->getNBNode();
517 
518     for (auto it : nbn->getOutgoingEdges()) {
519         GNEEdge* edge = myViewNet->getNet()->retrieveEdge(it->getID());
520         for (auto it_lane : edge->getLanes()) {
521             myPotentialTargets.insert(it_lane);
522         }
523     }
524     // set color for existing connections
525     std::vector<NBEdge::Connection> connections = myCurrentEditedLane->getParentEdge().getNBEdge()->getConnectionsFromLane(myCurrentEditedLane->getIndex());
526     for (auto it : myPotentialTargets) {
527         switch (getLaneStatus(connections, it)) {
528             case CONNECTED:
529                 it->setSpecialColor(&myConnectionLegend->getTargetColor());
530                 break;
531             case CONNECTED_PASS:
532                 it->setSpecialColor(&myConnectionLegend->getTargetPassColor());
533                 break;
534             case CONFLICTED:
535                 it->setSpecialColor(&myConnectionLegend->getConflictColor());
536                 break;
537             case UNCONNECTED:
538                 it->setSpecialColor(&myConnectionLegend->getPotentialTargetColor());
539                 break;
540         }
541     }
542 }
543 
544 
545 void
cleanup()546 GNEConnectorFrame::cleanup() {
547     // restore colors of potential targets
548     for (auto it : myPotentialTargets) {
549         it->setSpecialColor(0);
550     }
551     // clear attributes
552     myPotentialTargets.clear();
553     myNumChanges = 0;
554     myCurrentEditedLane->setSpecialColor(0);
555     myCurrentEditedLane = nullptr;
556     myDeletedConnections.clear();
557     myCurrentLane->updateCurrentLaneLabel("");
558 }
559 
560 
561 GNEConnectorFrame::LaneStatus
getLaneStatus(const std::vector<NBEdge::Connection> & connections,GNELane * targetLane)562 GNEConnectorFrame::getLaneStatus(const std::vector<NBEdge::Connection>& connections, GNELane* targetLane) {
563     NBEdge* srcEdge = myCurrentEditedLane->getParentEdge().getNBEdge();
564     const int fromIndex = myCurrentEditedLane->getIndex();
565     NBEdge* destEdge = targetLane->getParentEdge().getNBEdge();
566     const int toIndex = targetLane->getIndex();
567     std::vector<NBEdge::Connection>::const_iterator con_it = find_if(
568                 connections.begin(), connections.end(),
569                 NBEdge::connections_finder(fromIndex, destEdge, toIndex));
570     const bool isConnected = con_it != connections.end();
571     if (isConnected) {
572         if (con_it->mayDefinitelyPass) {
573             return CONNECTED_PASS;
574         } else {
575             return CONNECTED;
576         }
577     } else if (srcEdge->hasConnectionTo(destEdge, toIndex)
578                || (srcEdge->getPermissions(fromIndex) & destEdge->getPermissions(toIndex) & ~SVC_PEDESTRIAN) == 0) {
579         return CONFLICTED;
580     } else {
581         return UNCONNECTED;
582     }
583 }
584 
585 /****************************************************************************/
586