1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-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    GNETLSEditorFrame.cpp
11 /// @author  Jakob Erdmann
12 /// @date    May 2011
13 /// @version $Id$
14 ///
15 // The Widget for modifying traffic lights
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 <netbuild/NBLoadedSUMOTLDef.h>
28 #include <utils/gui/images/GUIIconSubSys.h>
29 #include <utils/xml/XMLSubSys.h>
30 #include <netimport/NIXMLTrafficLightsHandler.h>
31 #include <netedit/changes/GNEChange_TLS.h>
32 #include <netedit/GNEViewNet.h>
33 #include <netedit/GNENet.h>
34 #include <netedit/netelements/GNEJunction.h>
35 #include <netedit/netelements/GNEEdge.h>
36 #include <netedit/netelements/GNELane.h>
37 #include <netedit/GNEUndoList.h>
38 #include <netedit/netelements/GNEInternalLane.h>
39 #include <utils/options/OptionsCont.h>
40 
41 #include "GNETLSEditorFrame.h"
42 
43 // ===========================================================================
44 // FOX callback mapping
45 // ===========================================================================
46 
47 FXDEFMAP(GNETLSEditorFrame) GNETLSEditorFrameMap[] = {
48     FXMAPFUNC(SEL_COMMAND,    MID_CANCEL,                       GNETLSEditorFrame::onCmdCancel),
49     FXMAPFUNC(SEL_UPDATE,     MID_CANCEL,                       GNETLSEditorFrame::onUpdModified),
50     FXMAPFUNC(SEL_COMMAND,    MID_OK,                           GNETLSEditorFrame::onCmdOK),
51     FXMAPFUNC(SEL_UPDATE,     MID_OK,                           GNETLSEditorFrame::onUpdModified),
52     FXMAPFUNC(SEL_COMMAND,    MID_GNE_TLSFRAME_CREATE,          GNETLSEditorFrame::onCmdDefCreate),
53     FXMAPFUNC(SEL_UPDATE,     MID_GNE_TLSFRAME_CREATE,          GNETLSEditorFrame::onUpdDefCreate),
54     FXMAPFUNC(SEL_COMMAND,    MID_GNE_TLSFRAME_DELETE,          GNETLSEditorFrame::onCmdDefDelete),
55     FXMAPFUNC(SEL_UPDATE,     MID_GNE_TLSFRAME_DELETE,          GNETLSEditorFrame::onUpdDefSwitch),
56     FXMAPFUNC(SEL_COMMAND,    MID_GNE_TLSFRAME_SWITCH,          GNETLSEditorFrame::onCmdDefSwitch),
57     FXMAPFUNC(SEL_UPDATE,     MID_GNE_TLSFRAME_SWITCH,          GNETLSEditorFrame::onUpdDefSwitch),
58     FXMAPFUNC(SEL_COMMAND,    MID_GNE_TLSFRAME_OFFSET,          GNETLSEditorFrame::onCmdDefOffset),
59     FXMAPFUNC(SEL_UPDATE,     MID_GNE_TLSFRAME_OFFSET,          GNETLSEditorFrame::onUpdNeedsDef),
60     FXMAPFUNC(SEL_COMMAND,    MID_GNE_TLSFRAME_RENAME,          GNETLSEditorFrame::onCmdDefRename),
61     FXMAPFUNC(SEL_COMMAND,    MID_GNE_TLSFRAME_SUBRENAME,       GNETLSEditorFrame::onCmdDefSubRename),
62     FXMAPFUNC(SEL_COMMAND,    MID_GNE_TLSFRAME_ADDOFF,          GNETLSEditorFrame::onCmdDefAddOff),
63     FXMAPFUNC(SEL_COMMAND,    MID_GNE_TLSFRAME_GUESSPROGRAM,    GNETLSEditorFrame::onCmdGuess),
64     FXMAPFUNC(SEL_COMMAND,    MID_GNE_TLSFRAME_PHASE_CREATE,    GNETLSEditorFrame::onCmdPhaseCreate),
65     FXMAPFUNC(SEL_UPDATE,     MID_GNE_TLSFRAME_PHASE_CREATE,    GNETLSEditorFrame::onUpdNeedsDef),
66     FXMAPFUNC(SEL_COMMAND,    MID_GNE_TLSFRAME_PHASE_DELETE,    GNETLSEditorFrame::onCmdPhaseDelete),
67     FXMAPFUNC(SEL_UPDATE,     MID_GNE_TLSFRAME_PHASE_DELETE,    GNETLSEditorFrame::onUpdNeedsDefAndPhase),
68     FXMAPFUNC(SEL_COMMAND,    MID_GNE_TLSFRAME_CLEANUP,         GNETLSEditorFrame::onCmdCleanup),
69     FXMAPFUNC(SEL_UPDATE,     MID_GNE_TLSFRAME_CLEANUP,         GNETLSEditorFrame::onUpdNeedsDef),
70     FXMAPFUNC(SEL_COMMAND,    MID_GNE_TLSFRAME_ADDUNUSED,       GNETLSEditorFrame::onCmdAddUnused),
71     FXMAPFUNC(SEL_UPDATE,     MID_GNE_TLSFRAME_ADDUNUSED,       GNETLSEditorFrame::onUpdNeedsDef),
72     FXMAPFUNC(SEL_SELECTED,   MID_GNE_TLSFRAME_PHASE_TABLE,     GNETLSEditorFrame::onCmdPhaseSwitch),
73     FXMAPFUNC(SEL_DESELECTED, MID_GNE_TLSFRAME_PHASE_TABLE,     GNETLSEditorFrame::onCmdPhaseSwitch),
74     FXMAPFUNC(SEL_CHANGED,    MID_GNE_TLSFRAME_PHASE_TABLE,     GNETLSEditorFrame::onCmdPhaseSwitch),
75     FXMAPFUNC(SEL_REPLACED,   MID_GNE_TLSFRAME_PHASE_TABLE,     GNETLSEditorFrame::onCmdPhaseEdit),
76 };
77 
78 FXDEFMAP(GNETLSEditorFrame::TLSFile) TLSFileMap[] = {
79     FXMAPFUNC(SEL_COMMAND,    MID_GNE_TLSFRAME_LOAD_PROGRAM,    GNETLSEditorFrame::TLSFile::onCmdLoadTLSProgram),
80     FXMAPFUNC(SEL_UPDATE,     MID_GNE_TLSFRAME_LOAD_PROGRAM,    GNETLSEditorFrame::TLSFile::onUpdNeedsDef),
81     FXMAPFUNC(SEL_COMMAND,    MID_GNE_TLSFRAME_SAVE_PROGRAM,    GNETLSEditorFrame::TLSFile::onCmdSaveTLSProgram),
82     FXMAPFUNC(SEL_UPDATE,     MID_GNE_TLSFRAME_SAVE_PROGRAM,    GNETLSEditorFrame::TLSFile::onUpdNeedsDef),
83 };
84 
85 // Object implementation
FXIMPLEMENT(GNETLSEditorFrame,FXVerticalFrame,GNETLSEditorFrameMap,ARRAYNUMBER (GNETLSEditorFrameMap))86 FXIMPLEMENT(GNETLSEditorFrame,          FXVerticalFrame,    GNETLSEditorFrameMap,   ARRAYNUMBER(GNETLSEditorFrameMap))
87 FXIMPLEMENT(GNETLSEditorFrame::TLSFile, FXGroupBox,         TLSFileMap,             ARRAYNUMBER(TLSFileMap))
88 
89 
90 // ===========================================================================
91 // method definitions
92 // ===========================================================================
93 
94 GNETLSEditorFrame::GNETLSEditorFrame(FXHorizontalFrame* horizontalFrameParent, GNEViewNet* viewNet):
95     GNEFrame(horizontalFrameParent, viewNet, "Edit Traffic Light"),
96     myEditedDef(nullptr) {
97 
98     // create TLSJunction modul
99     myTLSJunction = new GNETLSEditorFrame::TLSJunction(this);
100 
101     // create TLSDefinition modul
102     myTLSDefinition = new GNETLSEditorFrame::TLSDefinition(this);
103 
104     // create TLSAttributes modul
105     myTLSAttributes = new GNETLSEditorFrame::TLSAttributes(this);
106 
107     // create TLSModifications modul
108     myTLSModifications = new GNETLSEditorFrame::TLSModifications(this);
109 
110     // create TLSPhases modul
111     myTLSPhases = new GNETLSEditorFrame::TLSPhases(this);
112 
113     // create TLSFile modul
114     myTLSFile = new GNETLSEditorFrame::TLSFile(this);
115 
116     // "Add 'off' program"
117     /*
118     new FXButton(myContentFrame, "Add \"Off\"-Program\t\tAdds a program for switching off this traffic light",
119             0, this, MID_GNE_TLSFRAME_ADDOFF, GUIDesignButton);
120     */
121 }
122 
123 
~GNETLSEditorFrame()124 GNETLSEditorFrame::~GNETLSEditorFrame() {
125     cleanup();
126 }
127 
128 
129 void
editJunction(GNEJunction * junction)130 GNETLSEditorFrame::editJunction(GNEJunction* junction) {
131     if (myTLSJunction->getCurrentJunction() == nullptr || (!myTLSModifications->checkHaveModifications() && (junction != myTLSJunction->getCurrentJunction()))) {
132         onCmdCancel(nullptr, 0, nullptr);
133         myViewNet->getUndoList()->p_begin("modifying traffic light definition");
134         myTLSJunction->setCurrentJunction(junction);
135         myTLSAttributes->initTLSAttributes(myTLSJunction->getCurrentJunction());
136         myTLSJunction->updateJunctionDescription();
137         myTLSJunction->getCurrentJunction()->selectTLS(true);
138         if (myTLSAttributes->getNumberOfTLSDefinitions() > 0) {
139             for (NBNode* node : myTLSAttributes->getCurrentTLSDefinition()->getNodes()) {
140                 myViewNet->getNet()->retrieveJunction(node->getID())->selectTLS(true);
141             }
142         }
143     } else {
144         myViewNet->setStatusBarText("Unsaved modifications. Abort or Save");
145     }
146 }
147 
148 
149 bool
isTLSSaved()150 GNETLSEditorFrame::isTLSSaved() {
151     if (myTLSModifications->checkHaveModifications()) {
152         // write warning if netedit is running in testing mode
153         WRITE_DEBUG("Opening question FXMessageBox 'save TLS'");
154         // open question box
155         FXuint answer = FXMessageBox::question(this, MBOX_YES_NO_CANCEL,
156                                                "Save TLS Changes", "%s",
157                                                "There is unsaved changes in current edited traffic light.\nDo you want to save it before changing mode?");
158         if (answer == MBOX_CLICKED_YES) { //1:yes, 2:no, 4:esc/cancel
159             // write warning if netedit is running in testing mode
160             WRITE_DEBUG("Closed FXMessageBox 'save TLS' with 'YES'");
161             // save modifications
162             onCmdOK(nullptr, 0, nullptr);
163             return true;
164         } else if (answer == MBOX_CLICKED_NO) {
165             // write warning if netedit is running in testing mode
166             WRITE_DEBUG("Closed FXMessageBox 'save TLS' with 'No'");
167             // cancel modifications
168             onCmdCancel(nullptr, 0, nullptr);
169             return true;
170         } else {
171             // write warning if netedit is running in testing mode
172             WRITE_DEBUG("Closed FXMessageBox 'save TLS' with 'Cancel'");
173             // abort changing mode
174             return false;
175         }
176     } else {
177         return true;
178     }
179 }
180 
181 
182 bool
parseTLSPrograms(const std::string & file)183 GNETLSEditorFrame::parseTLSPrograms(const std::string& file) {
184     NBTrafficLightLogicCont& tllCont = myViewNet->getNet()->getTLLogicCont();
185     NBTrafficLightLogicCont tmpTLLCont;;
186     NIXMLTrafficLightsHandler tllHandler(tmpTLLCont, myViewNet->getNet()->getEdgeCont());
187     // existing definitions must be available to update their programs
188     std::set<NBTrafficLightDefinition*> origDefs;
189     for (NBTrafficLightDefinition* def : tllCont.getDefinitions()) {
190         // make a copy of every program
191         NBTrafficLightLogic* logic = tllCont.getLogic(def->getID(), def->getProgramID());
192         if (logic != nullptr) {
193             NBTrafficLightDefinition* copy = new NBLoadedSUMOTLDef(def, logic);
194             std::vector<NBNode*> nodes = def->getNodes();
195             for (auto it_node : nodes) {
196                 GNEJunction* junction = myViewNet->getNet()->retrieveJunction(it_node->getID());
197                 myViewNet->getUndoList()->add(new GNEChange_TLS(junction, def, false, false), true);
198                 myViewNet->getUndoList()->add(new GNEChange_TLS(junction, copy, true), true);
199             }
200             tmpTLLCont.insert(copy);
201             origDefs.insert(copy);
202         } else {
203             WRITE_WARNING("tlLogic '" + def->getID() + "', program '" + def->getProgramID() + "' could not be built");
204         }
205     }
206     //std::cout << " initialized tmpCont with " << origDefs.size() << " defs\n";
207     XMLSubSys::runParser(tllHandler, file);
208 
209     std::vector<NBLoadedSUMOTLDef*> loadedTLS;
210     for (NBTrafficLightDefinition* def : tmpTLLCont.getDefinitions()) {
211         NBLoadedSUMOTLDef* sdef = dynamic_cast<NBLoadedSUMOTLDef*>(def);
212         if (sdef != nullptr) {
213             loadedTLS.push_back(sdef);
214         }
215     }
216     myViewNet->setStatusBarText("Loaded " + toString(loadedTLS.size()) + " programs");
217     for (auto def : loadedTLS) {
218         if (origDefs.count(def) != 0) {
219             // already add to undolist before
220             //std::cout << " skip " << def->getDescription() << "\n";
221             continue;
222         }
223         std::vector<NBNode*> nodes = def->getNodes();
224         //std::cout << " add " << def->getDescription() << " for nodes=" << toString(nodes) << "\n";
225         for (auto it_node : nodes) {
226             GNEJunction* junction = myViewNet->getNet()->retrieveJunction(it_node->getID());
227             //myViewNet->getUndoList()->add(new GNEChange_TLS(junction, myTLSEditorParent->myEditedDef, false), true);
228             myViewNet->getUndoList()->add(new GNEChange_TLS(junction, def, true), true);
229         }
230     }
231     // clean up temporary container to avoid deletion of defs when it's destruct is called
232     for (NBTrafficLightDefinition* def : tmpTLLCont.getDefinitions()) {
233         tmpTLLCont.removeProgram(def->getID(), def->getProgramID(), false);
234     }
235     return true;
236 }
237 
238 
239 long
onCmdCancel(FXObject *,FXSelector,void *)240 GNETLSEditorFrame::onCmdCancel(FXObject*, FXSelector, void*) {
241     if (myTLSJunction->getCurrentJunction() != nullptr) {
242         myViewNet->getUndoList()->p_abort();
243         cleanup();
244         myViewNet->update();
245     }
246     return 1;
247 }
248 
249 
250 long
onCmdOK(FXObject *,FXSelector,void *)251 GNETLSEditorFrame::onCmdOK(FXObject*, FXSelector, void*) {
252     if (myTLSJunction->getCurrentJunction() != nullptr) {
253         if (myTLSModifications->checkHaveModifications()) {
254             NBTrafficLightDefinition* oldDefinition = myTLSAttributes->getCurrentTLSDefinition();
255             std::vector<NBNode*> nodes = oldDefinition->getNodes();
256             for (auto it : nodes) {
257                 GNEJunction* junction = myViewNet->getNet()->retrieveJunction(it->getID());
258                 myViewNet->getUndoList()->add(new GNEChange_TLS(junction, oldDefinition, false), true);
259                 myViewNet->getUndoList()->add(new GNEChange_TLS(junction, myEditedDef, true), true);
260             }
261             myEditedDef = nullptr;
262             myViewNet->getUndoList()->p_end();
263             cleanup();
264             myViewNet->update();
265         } else {
266             onCmdCancel(nullptr, 0, nullptr);
267         }
268     }
269     return 1;
270 }
271 
272 
273 long
onCmdDefCreate(FXObject *,FXSelector,void *)274 GNETLSEditorFrame::onCmdDefCreate(FXObject*, FXSelector, void*) {
275     GNEJunction* junction = myTLSJunction->getCurrentJunction();
276     // abort because we onCmdOk assumes we wish to save an edited definition
277     onCmdCancel(nullptr, 0, nullptr);
278     // check that current junction has two or more edges
279     if ((junction->getGNEIncomingEdges().size() > 0) && (junction->getGNEOutgoingEdges().size() > 0)) {
280         if (junction->getAttribute(SUMO_ATTR_TYPE) != toString(NODETYPE_TRAFFIC_LIGHT)) {
281             junction->setAttribute(SUMO_ATTR_TYPE, toString(NODETYPE_TRAFFIC_LIGHT), myViewNet->getUndoList());
282         } else {
283             myViewNet->getUndoList()->add(new GNEChange_TLS(junction, nullptr, true, true), true);
284         }
285         editJunction(junction);
286     } else {
287         // write warning if netedit is running in testing mode
288         WRITE_DEBUG("Opening warning FXMessageBox 'invalid TLS'");
289         // open question box
290         FXMessageBox::warning(this, MBOX_OK,
291                               "TLS cannot be created", "%s",
292                               "Traffic Light cannot be created because junction must have\n at least one incoming edge and one outgoing edge.");
293         // write warning if netedit is running in testing mode
294         WRITE_DEBUG("Closed FXMessageBox 'invalid TLS'");
295     }
296     return 1;
297 }
298 
299 
300 long
onCmdDefDelete(FXObject *,FXSelector,void *)301 GNETLSEditorFrame::onCmdDefDelete(FXObject*, FXSelector, void*) {
302     GNEJunction* junction = myTLSJunction->getCurrentJunction();
303     const bool changeType = myTLSAttributes->getNumberOfTLSDefinitions() == 1;
304     NBTrafficLightDefinition* tlDef = myTLSAttributes->getCurrentTLSDefinition();
305     onCmdCancel(nullptr, 0, nullptr); // abort because onCmdOk assumes we wish to save an edited definition
306     if (changeType) {
307         junction->setAttribute(SUMO_ATTR_TYPE, toString(NODETYPE_PRIORITY), myViewNet->getUndoList());
308     } else {
309         myViewNet->getUndoList()->add(new GNEChange_TLS(junction, tlDef, false), true);
310     }
311     return 1;
312 }
313 
314 
315 long
onCmdDefSwitch(FXObject *,FXSelector,void *)316 GNETLSEditorFrame::onCmdDefSwitch(FXObject*, FXSelector, void*) {
317     assert(myTLSJunction->getCurrentJunction() != 0);
318     assert(myTLSAttributes->getNumberOfTLSDefinitions() == myTLSAttributes->getNumberOfPrograms());
319     NBTrafficLightDefinition* tlDef = myTLSAttributes->getCurrentTLSDefinition();
320     // logic may not have been recomputed yet. recompute to be sure
321     NBTrafficLightLogicCont& tllCont = myViewNet->getNet()->getTLLogicCont();
322     myViewNet->getNet()->computeJunction(myTLSJunction->getCurrentJunction());
323     NBTrafficLightLogic* tllogic = tllCont.getLogic(tlDef->getID(), tlDef->getProgramID());
324     if (tllogic != nullptr) {
325         // now we can be sure that the tlDef is up to date (i.e. re-guessed)
326         buildIinternalLanes(tlDef);
327         // create working copy from original def
328         delete myEditedDef;
329         myEditedDef = new NBLoadedSUMOTLDef(tlDef, tllogic);
330         myTLSAttributes->setOffset(myEditedDef->getLogic()->getOffset());
331         myTLSPhases->initPhaseTable();
332         myTLSPhases->updateCycleDuration();
333         myTLSPhases->showCycleDuration();
334     } else {
335         // tlDef has no valid logic (probably because id does not control any links
336         onCmdCancel(nullptr, 0, nullptr);
337         myViewNet->setStatusBarText("Traffic light does not control any links");
338     }
339     return 1;
340 }
341 
342 
343 long
onUpdDefSwitch(FXObject * o,FXSelector,void *)344 GNETLSEditorFrame::onUpdDefSwitch(FXObject* o, FXSelector, void*) {
345     const bool enable = myTLSAttributes->getNumberOfTLSDefinitions() > 0 && !myTLSModifications->checkHaveModifications();
346     o->handle(this, FXSEL(SEL_COMMAND, enable ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE), nullptr);
347     return 1;
348 }
349 
350 
351 long
onUpdNeedsDef(FXObject * o,FXSelector,void *)352 GNETLSEditorFrame::onUpdNeedsDef(FXObject* o, FXSelector, void*) {
353     const bool enable = myTLSAttributes->getNumberOfTLSDefinitions() > 0;
354     o->handle(this, FXSEL(SEL_COMMAND, enable ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE), nullptr);
355     return 1;
356 }
357 
358 
359 long
onUpdNeedsDefAndPhase(FXObject * o,FXSelector,void *)360 GNETLSEditorFrame::onUpdNeedsDefAndPhase(FXObject* o, FXSelector, void*) {
361     // do not delete the last phase
362     const bool enable = myTLSAttributes->getNumberOfTLSDefinitions() > 0 && myTLSPhases->getPhaseTable()->getNumRows() > 1;
363     o->handle(this, FXSEL(SEL_COMMAND, enable ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE), nullptr);
364     return 1;
365 }
366 
367 
368 long
onUpdDefCreate(FXObject * o,FXSelector,void *)369 GNETLSEditorFrame::onUpdDefCreate(FXObject* o, FXSelector, void*) {
370     const bool enable = myTLSJunction->getCurrentJunction() != nullptr && !myTLSModifications->checkHaveModifications();
371     o->handle(this, FXSEL(SEL_COMMAND, enable ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE), nullptr);
372     return 1;
373 }
374 
375 
376 long
onUpdModified(FXObject * o,FXSelector,void *)377 GNETLSEditorFrame::onUpdModified(FXObject* o, FXSelector, void*) {
378     bool enable = myTLSModifications->checkHaveModifications();
379     o->handle(this, FXSEL(SEL_COMMAND, enable ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE), nullptr);
380     return 1;
381 }
382 
383 
384 
385 long
onCmdDefOffset(FXObject *,FXSelector,void *)386 GNETLSEditorFrame::onCmdDefOffset(FXObject*, FXSelector, void*) {
387     myTLSModifications->setHaveModifications(true);
388     myEditedDef->setOffset(myTLSAttributes->getOffset());
389     return 1;
390 }
391 
392 
393 long
onCmdDefRename(FXObject *,FXSelector,void *)394 GNETLSEditorFrame::onCmdDefRename(FXObject*, FXSelector, void*) {
395     return 1;
396 }
397 
398 
399 long
onCmdDefSubRename(FXObject *,FXSelector,void *)400 GNETLSEditorFrame::onCmdDefSubRename(FXObject*, FXSelector, void*) {
401     return 1;
402 }
403 
404 
405 long
onCmdDefAddOff(FXObject *,FXSelector,void *)406 GNETLSEditorFrame::onCmdDefAddOff(FXObject*, FXSelector, void*) {
407     return 1;
408 }
409 
410 
411 long
onCmdGuess(FXObject *,FXSelector,void *)412 GNETLSEditorFrame::onCmdGuess(FXObject*, FXSelector, void*) {
413     return 1;
414 }
415 
416 
417 long
onCmdPhaseSwitch(FXObject *,FXSelector,void *)418 GNETLSEditorFrame::onCmdPhaseSwitch(FXObject*, FXSelector, void*) {
419     const int index = myTLSPhases->getPhaseTable()->getCurrentRow();
420     const NBTrafficLightLogic::PhaseDefinition& phase = getPhases()[index];
421     myTLSPhases->getPhaseTable()->selectRow(index);
422     // need not hold since links could have been deleted somewhere else and indices may be reused
423     // assert(phase.state.size() == myInternalLanes.size());
424     for (auto it : myInternalLanes) {
425         int tlIndex = it.first;
426         std::vector<GNEInternalLane*> lanes = it.second;
427         LinkState state = LINKSTATE_DEADEND;
428         if (tlIndex >= 0 && tlIndex < (int)phase.state.size()) {
429             state = (LinkState)phase.state[tlIndex];
430         }
431         for (auto it_lane : lanes) {
432             it_lane->setLinkState(state);
433         }
434     }
435     myViewNet->update();
436     return 1;
437 }
438 
439 bool
fixedDuration() const440 GNETLSEditorFrame::fixedDuration() const {
441     assert(myEditedDef != nullptr);
442     return myEditedDef->getType() == TLTYPE_STATIC;
443 }
444 
445 long
onCmdPhaseCreate(FXObject *,FXSelector,void *)446 GNETLSEditorFrame::onCmdPhaseCreate(FXObject*, FXSelector, void*) {
447     myTLSModifications->setHaveModifications(true);
448     // allows insertion at first position by deselecting via arrow keys
449     int newIndex = myTLSPhases->getPhaseTable()->getSelStartRow() + 1;
450     int oldIndex = MAX2(0, myTLSPhases->getPhaseTable()->getSelStartRow());
451     // copy current row
452     SUMOTime duration = getSUMOTime(myTLSPhases->getPhaseTable()->getItemText(oldIndex, 0));
453     std::string state = myTLSPhases->getPhaseTable()->getItemText(oldIndex, fixedDuration() ? 1 : 3).text();
454 
455     std::set<int> crossingIndices;
456     for (NBNode* n : myEditedDef->getNodes()) {
457         for (NBNode::Crossing* c : n->getCrossings()) {
458             crossingIndices.insert(c->tlLinkIndex);
459             crossingIndices.insert(c->tlLinkIndex2);
460         }
461     }
462 
463     // smart adapations for new state
464     bool haveGreen = false;
465     bool haveYellow = false;
466     for (char c : state) {
467         if (c == LINKSTATE_TL_GREEN_MAJOR || c == LINKSTATE_TL_GREEN_MINOR) {
468             haveGreen = true;
469         } else if (c == LINKSTATE_TL_YELLOW_MAJOR || c == LINKSTATE_TL_YELLOW_MINOR) {
470             haveYellow = true;
471         }
472     }
473     const OptionsCont& oc = OptionsCont::getOptions();
474     if (haveGreen && haveYellow) {
475         // guess left-mover state
476         duration = TIME2STEPS(oc.getInt("tls.left-green.time"));
477         for (int i = 0; i < (int)state.size(); i++) {
478             if (state[i] == LINKSTATE_TL_YELLOW_MAJOR || state[i] == LINKSTATE_TL_YELLOW_MINOR) {
479                 state[i] = LINKSTATE_TL_RED;
480             } else if (state[i] == LINKSTATE_TL_GREEN_MINOR) {
481                 state[i] = LINKSTATE_TL_GREEN_MAJOR;
482             }
483         }
484     } else if (haveGreen) {
485         // guess yellow state
486         myEditedDef->setParticipantsInformation();
487         duration = TIME2STEPS(myEditedDef->computeBrakingTime(oc.getFloat("tls.yellow.min-decel")));
488         for (int i = 0; i < (int)state.size(); i++) {
489             if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
490                 if (crossingIndices.count(i) == 0) {
491                     state[i] = LINKSTATE_TL_YELLOW_MINOR;
492                 } else {
493                     state[i] = LINKSTATE_TL_RED;
494                 }
495             }
496         }
497     } else if (haveYellow) {
498         duration = TIME2STEPS(oc.isDefault("tls.allred.time") ? 2 :  oc.getInt("tls.allred.time"));
499         // guess all-red state
500         for (int i = 0; i < (int)state.size(); i++) {
501             if (state[i] == LINKSTATE_TL_YELLOW_MAJOR || state[i] == LINKSTATE_TL_YELLOW_MINOR) {
502                 state[i] = LINKSTATE_TL_RED;
503             }
504         }
505     }
506 
507     myEditedDef->getLogic()->addStep(duration, state, std::vector<int>(), "", newIndex);
508     myTLSPhases->getPhaseTable()->setCurrentItem(newIndex, 0);
509     myTLSPhases->initPhaseTable(newIndex);
510     myTLSPhases->getPhaseTable()->setFocus();
511     return 1;
512 }
513 
514 
515 long
onCmdPhaseDelete(FXObject *,FXSelector,void *)516 GNETLSEditorFrame::onCmdPhaseDelete(FXObject*, FXSelector, void*) {
517     myTLSModifications->setHaveModifications(true);
518     const int newRow = MAX2((int)0, (int)myTLSPhases->getPhaseTable()->getCurrentRow() - 1);
519     myEditedDef->getLogic()->deletePhase(myTLSPhases->getPhaseTable()->getCurrentRow());
520     myTLSPhases->initPhaseTable(newRow);
521     myTLSPhases->getPhaseTable()->setFocus();
522     return 1;
523 }
524 
525 
526 long
onCmdCleanup(FXObject *,FXSelector,void *)527 GNETLSEditorFrame::onCmdCleanup(FXObject*, FXSelector, void*) {
528     myTLSModifications->setHaveModifications(myEditedDef->cleanupStates());
529     myTLSPhases->initPhaseTable(0);
530     myTLSPhases->getPhaseTable()->setFocus();
531     return 1;
532 }
533 
534 
535 long
onCmdAddUnused(FXObject *,FXSelector,void *)536 GNETLSEditorFrame::onCmdAddUnused(FXObject*, FXSelector, void*) {
537     myEditedDef->getLogic()->setStateLength(
538         myEditedDef->getLogic()->getNumLinks() + 1);
539     myTLSModifications->setHaveModifications(true);
540     myTLSPhases->initPhaseTable(0);
541     myTLSPhases->getPhaseTable()->setFocus();
542     return 1;
543 }
544 
545 
546 long
onCmdPhaseEdit(FXObject *,FXSelector,void * ptr)547 GNETLSEditorFrame::onCmdPhaseEdit(FXObject*, FXSelector, void* ptr) {
548     /* @note: there is a bug when copying/pasting rows: when this handler is
549      * called the value of the cell is not yet updated. This means you have to
550      * click inside the cell and hit enter to actually update the value */
551     FXTablePos* tp = (FXTablePos*)ptr;
552     FXString value = myTLSPhases->getPhaseTable()->getItemText(tp->row, tp->col);
553     const int colDuration = 0;
554     const int colMinDur = fixedDuration() ? -1 : 1;
555     const int colMaxDur = fixedDuration() ? -1 : 2;
556     const int colState = fixedDuration() ? 1 : 3;
557     const int colNext = fixedDuration() ? 2 : 4;
558     const int colName = fixedDuration() ? 3 : 5;
559 
560     if (tp->col == colDuration) {
561         // duration edited
562         if (GNEAttributeCarrier::canParse<double>(value.text())) {
563             SUMOTime duration = getSUMOTime(value);
564             if (duration > 0) {
565                 myEditedDef->getLogic()->setPhaseDuration(tp->row, duration);
566                 myTLSModifications->setHaveModifications(true);
567                 myTLSPhases->updateCycleDuration();
568                 return 1;
569             }
570         }
571         // input error, reset value
572         myTLSPhases->getPhaseTable()->setItemText(tp->row, colDuration, toString(STEPS2TIME(getPhases()[tp->row].duration)).c_str());
573     } else if (tp->col == colMinDur) {
574         // minDur edited
575         if (GNEAttributeCarrier::canParse<double>(value.text())) {
576             SUMOTime minDur = getSUMOTime(value);
577             if (minDur > 0) {
578                 myEditedDef->getLogic()->setPhaseMinDuration(tp->row, minDur);
579                 myTLSModifications->setHaveModifications(true);
580                 return 1;
581             }
582         } else if (StringUtils::prune(value.text()).empty()) {
583             myEditedDef->getLogic()->setPhaseMinDuration(tp->row, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
584             myTLSModifications->setHaveModifications(true);
585             return 1;
586         }
587         // input error, reset value
588         myTLSPhases->getPhaseTable()->setItemText(tp->row, colMinDur, varDurString(getPhases()[tp->row].minDur).c_str());
589     } else if (tp->col == colMaxDur) {
590         // maxDur edited
591         if (GNEAttributeCarrier::canParse<double>(value.text())) {
592             SUMOTime maxDur = getSUMOTime(value);
593             if (maxDur > 0) {
594                 myEditedDef->getLogic()->setPhaseMaxDuration(tp->row, maxDur);
595                 myTLSModifications->setHaveModifications(true);
596                 return 1;
597             }
598         } else if (StringUtils::prune(value.text()).empty()) {
599             myEditedDef->getLogic()->setPhaseMaxDuration(tp->row, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
600             myTLSModifications->setHaveModifications(true);
601             return 1;
602         }
603         // input error, reset value
604         myTLSPhases->getPhaseTable()->setItemText(tp->row, colMaxDur, varDurString(getPhases()[tp->row].maxDur).c_str());
605     } else if (tp->col == colState) {
606         // state edited
607         try {
608             // insert phase with new step and delete the old phase
609             const NBTrafficLightLogic::PhaseDefinition& phase = getPhases()[tp->row];
610             myEditedDef->getLogic()->addStep(phase.duration, value.text(), phase.next, phase.name, tp->row);
611             myEditedDef->getLogic()->deletePhase(tp->row + 1);
612             myTLSModifications->setHaveModifications(true);
613             onCmdPhaseSwitch(nullptr, 0, nullptr);
614         } catch (ProcessError&) {
615             // input error, reset value
616             myTLSPhases->getPhaseTable()->setItemText(tp->row, colState, getPhases()[tp->row].state.c_str());
617         }
618     } else if (tp->col == colNext) {
619         // next edited
620         bool ok = true;
621         if (GNEAttributeCarrier::canParse<std::vector<int> >(value.text())) {
622             std::vector<int> next = GNEAttributeCarrier::parse<std::vector<int> >(value.text());
623             for (int n : next) {
624                 if (n < 0 || n >= myTLSPhases->getPhaseTable()->getNumRows()) {
625                     ok = false;
626                     break;
627                 }
628             }
629             if (ok) {
630                 myEditedDef->getLogic()->setPhaseNext(tp->row, next);
631                 myTLSModifications->setHaveModifications(true);
632                 return 1;
633             }
634         }
635         // input error, reset value
636         myTLSPhases->getPhaseTable()->setItemText(tp->row, colNext, "");
637     } else if (tp->col == colName) {
638         // name edited
639         myEditedDef->getLogic()->setPhaseName(tp->row, value.text());
640         myTLSModifications->setHaveModifications(true);
641         return 1;
642     }
643     return 1;
644 }
645 
646 
647 void
cleanup()648 GNETLSEditorFrame::cleanup() {
649     if (myTLSJunction->getCurrentJunction()) {
650         myTLSJunction->getCurrentJunction()->selectTLS(false);
651         if (myTLSAttributes->getNumberOfTLSDefinitions() > 0) {
652             for (NBNode* node : myTLSAttributes->getCurrentTLSDefinition()->getNodes()) {
653                 myViewNet->getNet()->retrieveJunction(node->getID())->selectTLS(false);
654             }
655         }
656     }
657     // clean data structures
658     myTLSJunction->setCurrentJunction(nullptr);
659     myTLSModifications->setHaveModifications(false);
660     delete myEditedDef;
661     myEditedDef = nullptr;
662     buildIinternalLanes(nullptr); // only clears
663     // clean up controls
664     myTLSAttributes->clearTLSAttributes();
665     myTLSPhases->initPhaseTable(); // only clears when there are no definitions
666     myTLSPhases->hideCycleDuration();
667     myTLSJunction->updateJunctionDescription();
668 }
669 
670 
671 void
buildIinternalLanes(NBTrafficLightDefinition * tlDef)672 GNETLSEditorFrame::buildIinternalLanes(NBTrafficLightDefinition* tlDef) {
673     SUMORTree& rtree = myViewNet->getNet()->getVisualisationSpeedUp();
674     // clean up previous objects
675     for (auto it : myInternalLanes) {
676         for (auto it_intLanes : it.second) {
677             rtree.removeAdditionalGLObject(it_intLanes);
678             delete it_intLanes;
679         }
680     }
681     myInternalLanes.clear();
682     // create new internal lanes
683     if (tlDef != nullptr) {
684         const int NUM_POINTS = 10;
685         assert(myTLSJunction->getCurrentJunction());
686         NBNode* nbn = myTLSJunction->getCurrentJunction()->getNBNode();
687         std::string innerID = ":" + nbn->getID(); // see NWWriter_SUMO::writeInternalEdges
688         const NBConnectionVector& links = tlDef->getControlledLinks();
689         for (auto it : links) {
690             int tlIndex = it.getTLIndex();
691             PositionVector shape = it.getFrom()->getToNode()->computeInternalLaneShape(it.getFrom(), NBEdge::Connection(it.getFromLane(),
692                                    it.getTo(), it.getToLane()), NUM_POINTS);
693             if (shape.length() < 2) {
694                 // enlarge shape to ensure visibility
695                 shape.clear();
696                 PositionVector laneShapeFrom = it.getFrom()->getLaneShape(it.getFromLane());
697                 PositionVector laneShapeTo = it.getTo()->getLaneShape(it.getToLane());
698                 shape.push_back(laneShapeFrom.positionAtOffset(MAX2(0.0, laneShapeFrom.length() - 1)));
699                 shape.push_back(laneShapeTo.positionAtOffset(MIN2(1.0, laneShapeFrom.length())));
700             }
701             GNEInternalLane* ilane = new GNEInternalLane(this, innerID + '_' + toString(tlIndex),  shape, tlIndex);
702             rtree.addAdditionalGLObject(ilane);
703             myInternalLanes[tlIndex].push_back(ilane);
704         }
705         for (NBNode* nbn : tlDef->getNodes()) {
706             for (auto c : nbn->getCrossings()) {
707                 if (c->tlLinkIndex2 > 0 && c->tlLinkIndex2 != c->tlLinkIndex) {
708                     // draw both directions
709                     PositionVector forward = c->shape;
710                     forward.move2side(c->width / 4);
711                     GNEInternalLane* ilane = new GNEInternalLane(this, c->id, forward, c->tlLinkIndex);
712                     rtree.addAdditionalGLObject(ilane);
713                     myInternalLanes[c->tlLinkIndex].push_back(ilane);
714 
715                     PositionVector backward = c->shape.reverse();
716                     backward.move2side(c->width / 4);
717                     GNEInternalLane* ilane2 = new GNEInternalLane(this, c->id + "_r", backward, c->tlLinkIndex2);
718                     rtree.addAdditionalGLObject(ilane2);
719                     myInternalLanes[c->tlLinkIndex2].push_back(ilane2);
720                 } else {
721                     // draw only one lane for both directions
722                     GNEInternalLane* ilane = new GNEInternalLane(this, c->id, c->shape, c->tlLinkIndex);
723                     rtree.addAdditionalGLObject(ilane);
724                     myInternalLanes[c->tlLinkIndex].push_back(ilane);
725                 }
726             }
727         }
728     }
729 }
730 
731 
732 std::string
varDurString(SUMOTime dur)733 GNETLSEditorFrame::varDurString(SUMOTime dur) {
734     return dur == NBTrafficLightDefinition::UNSPECIFIED_DURATION ? "   " : toString(STEPS2TIME(dur));
735 }
736 
737 
738 const std::vector<NBTrafficLightLogic::PhaseDefinition>&
getPhases()739 GNETLSEditorFrame::getPhases() {
740     return myEditedDef->getLogic()->getPhases();
741 }
742 
743 
744 void
handleChange(GNEInternalLane * lane)745 GNETLSEditorFrame::handleChange(GNEInternalLane* lane) {
746     myTLSModifications->setHaveModifications(true);
747     if (myViewNet->changeAllPhases()) {
748         const std::vector<NBTrafficLightLogic::PhaseDefinition>& phases = getPhases();
749         for (int row = 0; row < (int)phases.size(); row++) {
750             myEditedDef->getLogic()->setPhaseState(row, lane->getTLIndex(), lane->getLinkState());
751         }
752     } else {
753         myEditedDef->getLogic()->setPhaseState(myTLSPhases->getPhaseTable()->getCurrentRow(), lane->getTLIndex(), lane->getLinkState());
754     }
755     myTLSPhases->initPhaseTable(myTLSPhases->getPhaseTable()->getCurrentRow());
756     myTLSPhases->getPhaseTable()->setFocus();
757 }
758 
759 
760 void
handleMultiChange(GNELane * lane,FXObject * obj,FXSelector sel,void * eventData)761 GNETLSEditorFrame::handleMultiChange(GNELane* lane, FXObject* obj, FXSelector sel, void* eventData) {
762     if (myEditedDef != nullptr) {
763         myTLSModifications->setHaveModifications(true);
764         const NBConnectionVector& links = myEditedDef->getControlledLinks();
765         std::set<std::string> fromIDs;
766         fromIDs.insert(lane->getMicrosimID());
767         GNEEdge& edge = lane->getParentEdge();
768         // if neither the lane nor its edge are selected, apply changes to the whole edge
769         if (!edge.isAttributeCarrierSelected() && !lane->isAttributeCarrierSelected()) {
770             for (auto it_lane : edge.getLanes()) {
771                 fromIDs.insert(it_lane->getMicrosimID());
772             }
773         } else {
774             // if the edge is selected, apply changes to all lanes of all selected edges
775             if (edge.isAttributeCarrierSelected()) {
776                 std::vector<GNEEdge*> edges = myViewNet->getNet()->retrieveEdges(true);
777                 for (auto it : edges) {
778                     for (auto it_lane : it->getLanes()) {
779                         fromIDs.insert(it_lane->getMicrosimID());
780                     }
781                 }
782             }
783             // if the lane is selected, apply changes to all selected lanes
784             if (lane->isAttributeCarrierSelected()) {
785                 std::vector<GNELane*> lanes = myViewNet->getNet()->retrieveLanes(true);
786                 for (auto it_lane : lanes) {
787                     fromIDs.insert(it_lane->getMicrosimID());
788                 }
789             }
790 
791         }
792         // set new state for all connections from the chosen lane IDs
793         for (auto it : links) {
794             if (fromIDs.count(it.getFrom()->getLaneID(it.getFromLane())) > 0) {
795                 std::vector<GNEInternalLane*> lanes = myInternalLanes[it.getTLIndex()];
796                 for (auto it_lane : lanes) {
797                     it_lane->onDefault(obj, sel, eventData);
798                 }
799             }
800         }
801     }
802 }
803 
804 
805 bool
controlsEdge(GNEEdge & edge) const806 GNETLSEditorFrame::controlsEdge(GNEEdge& edge) const {
807     if (myEditedDef != nullptr) {
808         const NBConnectionVector& links = myEditedDef->getControlledLinks();
809         for (auto it : links) {
810             if (it.getFrom()->getID() == edge.getMicrosimID()) {
811                 return true;
812             }
813         }
814     }
815     return false;
816 }
817 
818 
819 SUMOTime
getSUMOTime(const FXString & string)820 GNETLSEditorFrame::getSUMOTime(const FXString& string) {
821     assert(GNEAttributeCarrier::canParse<double>(string.text()));
822     return TIME2STEPS(GNEAttributeCarrier::parse<double>(string.text()));
823 }
824 
825 // ---------------------------------------------------------------------------
826 // GNETLSEditorFrame::TLSAttributes - methods
827 // ---------------------------------------------------------------------------
828 
TLSAttributes(GNETLSEditorFrame * TLSEditorParent)829 GNETLSEditorFrame::TLSAttributes::TLSAttributes(GNETLSEditorFrame* TLSEditorParent) :
830     FXGroupBox(TLSEditorParent->myContentFrame, "Traffic light Attributes", GUIDesignGroupBoxFrame),
831     myTLSEditorParent(TLSEditorParent) {
832 
833     // create frame, label and textfield for name (By default disabled)
834     FXHorizontalFrame* nameFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
835     myNameLabel = new FXLabel(nameFrame, "ID", nullptr, GUIDesignLabelAttribute);
836     myNameTextField = new FXTextField(nameFrame, GUIDesignTextFieldNCol, myTLSEditorParent, MID_GNE_TLSFRAME_SWITCH, GUIDesignTextField);
837     myNameTextField->disable();
838 
839     // create frame, label and comboBox for Program (By default hidden)
840     FXHorizontalFrame* programFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
841     myProgramLabel = new FXLabel(programFrame, "Program", nullptr, GUIDesignLabelAttribute);
842     myProgramComboBox = new FXComboBox(programFrame, GUIDesignComboBoxNCol, myTLSEditorParent, MID_GNE_TLSFRAME_SWITCH, GUIDesignComboBoxAttribute);
843     myProgramComboBox->disable();
844 
845     // create frame, label and TextField for Offset (By default disabled)
846     FXHorizontalFrame* offsetFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
847     myOffsetLabel = new FXLabel(offsetFrame, "Offset", nullptr, GUIDesignLabelAttribute);
848     myOffsetTextField = new FXTextField(offsetFrame, GUIDesignTextFieldNCol, myTLSEditorParent, MID_GNE_TLSFRAME_OFFSET, GUIDesignTextFieldReal);
849     myOffsetTextField->disable();
850 }
851 
852 
~TLSAttributes()853 GNETLSEditorFrame::TLSAttributes::~TLSAttributes() {}
854 
855 
856 void
initTLSAttributes(GNEJunction * junction)857 GNETLSEditorFrame::TLSAttributes::initTLSAttributes(GNEJunction* junction) {
858     assert(junction);
859     myTLSDefinitions.clear();
860     // enable name TextField
861     myNameTextField->enable();
862     // enable Offset
863     myOffsetTextField->enable();
864     // obtain TLSs
865     for (auto it : junction->getNBNode()->getControllingTLS()) {
866         myTLSDefinitions.push_back(it);
867         myNameTextField->setText(it->getID().c_str());
868         myNameTextField->enable();
869         myProgramComboBox->appendItem(it->getProgramID().c_str());
870     }
871     if (myTLSDefinitions.size() > 0) {
872         myProgramComboBox->enable();
873         myProgramComboBox->setCurrentItem(0);
874         myProgramComboBox->setNumVisible(myProgramComboBox->getNumItems());
875         myTLSEditorParent->onCmdDefSwitch(nullptr, 0, nullptr);
876     }
877 }
878 
879 
880 void
clearTLSAttributes()881 GNETLSEditorFrame::TLSAttributes::clearTLSAttributes() {
882     // clear definitions
883     myTLSDefinitions.clear();
884     // clear and disable name TextField
885     myNameTextField->setText("");
886     myNameTextField->disable();
887     // clear and disable myProgramComboBox
888     myProgramComboBox->clearItems();
889     myProgramComboBox->disable();
890     // clear and disable Offset TextField
891     myOffsetTextField->setText("");
892     myOffsetTextField->disable();
893 }
894 
895 
896 NBTrafficLightDefinition*
getCurrentTLSDefinition() const897 GNETLSEditorFrame::TLSAttributes::getCurrentTLSDefinition() const {
898     return myTLSDefinitions.at(myProgramComboBox->getCurrentItem());
899 }
900 
901 
902 int
getNumberOfTLSDefinitions() const903 GNETLSEditorFrame::TLSAttributes::getNumberOfTLSDefinitions() const {
904     return (int)myTLSDefinitions.size();
905 }
906 
907 
908 int
getNumberOfPrograms() const909 GNETLSEditorFrame::TLSAttributes::getNumberOfPrograms() const {
910     return myProgramComboBox->getNumItems();
911 }
912 
913 
914 SUMOTime
getOffset() const915 GNETLSEditorFrame::TLSAttributes::getOffset() const {
916     return getSUMOTime(myOffsetTextField->getText());
917 }
918 
919 
920 void
setOffset(SUMOTime offset)921 GNETLSEditorFrame::TLSAttributes::setOffset(SUMOTime offset) {
922     myOffsetTextField->setText(toString(STEPS2TIME(offset)).c_str());
923 }
924 
925 // ---------------------------------------------------------------------------
926 // GNETLSEditorFrame::TLSJunction - methods
927 // ---------------------------------------------------------------------------
928 
TLSJunction(GNETLSEditorFrame * TLSEditorParent)929 GNETLSEditorFrame::TLSJunction::TLSJunction(GNETLSEditorFrame* TLSEditorParent) :
930     FXGroupBox(TLSEditorParent->myContentFrame, "Junction", GUIDesignGroupBoxFrame),
931     myTLSEditorParent(TLSEditorParent),
932     myCurrentJunction(nullptr) {
933     // Create frame for junction ID
934     FXHorizontalFrame* junctionIDFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
935     myLabelJunctionID = new FXLabel(junctionIDFrame, "Junction ID", nullptr, GUIDesignLabelAttribute);
936     myTextFieldJunctionID = new FXTextField(junctionIDFrame, GUIDesignTextFieldNCol, this, MID_GNE_TLSFRAME_SELECT_JUNCTION, GUIDesignTextField);
937     myTextFieldJunctionID->setEditable(false);
938     // create frame for junction status
939     FXHorizontalFrame* junctionIDStatus = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
940     myLabelJunctionStatus = new FXLabel(junctionIDStatus, "Status", nullptr, GUIDesignLabelAttribute);
941     myTextFieldJunctionStatus = new FXTextField(junctionIDStatus, GUIDesignTextFieldNCol, this, MID_GNE_TLSFRAME_UPDATE_STATUS, GUIDesignTextField);
942     myTextFieldJunctionStatus->setEditable(false);
943     // update junction description after creation
944     updateJunctionDescription();
945     // show TLS Junction
946     show();
947 }
948 
949 
~TLSJunction()950 GNETLSEditorFrame::TLSJunction::~TLSJunction() {}
951 
952 
953 GNEJunction*
getCurrentJunction() const954 GNETLSEditorFrame::TLSJunction::getCurrentJunction() const {
955     return myCurrentJunction;
956 }
957 
958 
959 void
setCurrentJunction(GNEJunction * junction)960 GNETLSEditorFrame::TLSJunction::setCurrentJunction(GNEJunction* junction) {
961     myCurrentJunction = junction;
962 }
963 
964 
965 void
updateJunctionDescription() const966 GNETLSEditorFrame::TLSJunction::updateJunctionDescription() const {
967     if (myCurrentJunction == nullptr) {
968         myTextFieldJunctionID->setText("");
969         myTextFieldJunctionStatus->setText("");
970     } else {
971         NBNode* nbn = myCurrentJunction->getNBNode();
972         myTextFieldJunctionID->setText(nbn->getID().c_str());
973         if (!nbn->isTLControlled()) {
974             myTextFieldJunctionStatus->setText("uncontrolled");
975         } else {
976             myTextFieldJunctionStatus->setText(myTLSEditorParent->myTLSModifications->checkHaveModifications() ? "modified" : "unmodified");
977         }
978     }
979 }
980 
981 // ---------------------------------------------------------------------------
982 // GNETLSEditorFrame::TLSDefinition - methods
983 // ---------------------------------------------------------------------------
984 
TLSDefinition(GNETLSEditorFrame * TLSEditorParent)985 GNETLSEditorFrame::TLSDefinition::TLSDefinition(GNETLSEditorFrame* TLSEditorParent) :
986     FXGroupBox(TLSEditorParent->myContentFrame, "Traffic lights definition", GUIDesignGroupBoxFrame),
987     myTLSEditorParent(TLSEditorParent) {
988     // create create tlDef button
989     myNewTLProgram = new FXButton(this, "Create TLS\t\tCreate a new traffic light program",
990                                   GUIIconSubSys::getIcon(ICON_MODETLS), TLSEditorParent, MID_GNE_TLSFRAME_CREATE, GUIDesignButton);
991     // create delete tlDef button
992     myDeleteTLProgram = new FXButton(this, "Delete TLS\t\tDelete a traffic light program. If all programs are deleted the junction turns into a priority junction.",
993                                      GUIIconSubSys::getIcon(ICON_REMOVE), TLSEditorParent, MID_GNE_TLSFRAME_DELETE, GUIDesignButton);
994     // show TLS TLSDefinition
995     show();
996 }
997 
998 
~TLSDefinition()999 GNETLSEditorFrame::TLSDefinition::~TLSDefinition() {}
1000 
1001 // ---------------------------------------------------------------------------
1002 // GNETLSEditorFrame::TLSPhases - methods
1003 // ---------------------------------------------------------------------------
1004 
TLSPhases(GNETLSEditorFrame * TLSEditorParent)1005 GNETLSEditorFrame::TLSPhases::TLSPhases(GNETLSEditorFrame* TLSEditorParent) :
1006     FXGroupBox(TLSEditorParent->myContentFrame, "Phases", GUIDesignGroupBoxFrame),
1007     myTLSEditorParent(TLSEditorParent),
1008     myTableFont(new FXFont(getApp(), "Courier New", 9)) {
1009 
1010     // create and configure phase table
1011     myTableScroll = new FXScrollWindow(this, LAYOUT_FILL_X | LAYOUT_FIX_HEIGHT);
1012     myPhaseTable = new FXTable(myTableScroll, myTLSEditorParent, MID_GNE_TLSFRAME_PHASE_TABLE, GUIDesignTableLimitedHeight);
1013     myPhaseTable->setColumnHeaderMode(LAYOUT_FIX_HEIGHT);
1014     myPhaseTable->setColumnHeaderHeight(0);
1015     myPhaseTable->setRowHeaderMode(LAYOUT_FIX_WIDTH);
1016     myPhaseTable->setRowHeaderWidth(0);
1017     myPhaseTable->hide();
1018     myPhaseTable->setFont(myTableFont);
1019     myPhaseTable->setHelpText("phase duration in seconds | phase state");
1020 
1021     // create total duration info label
1022     myCycleDuration = new FXLabel(this, "", nullptr, GUIDesignLabelLeft);
1023 
1024     // create new phase button
1025     myInsertDuplicateButton = new FXButton(this, "Insert Phase\t\tInsert new phase after the selected phase. The new state is deduced from the selected phase.", nullptr, myTLSEditorParent, MID_GNE_TLSFRAME_PHASE_CREATE, GUIDesignButton);
1026 
1027     // create delete phase button
1028     myDeleteSelectedPhaseButton = new FXButton(this, "Delete Phase\t\tDelete selected phase", nullptr, myTLSEditorParent, MID_GNE_TLSFRAME_PHASE_DELETE, GUIDesignButton);
1029 
1030     // create cleanup states button
1031     new FXButton(this, "Cleanup States\t\tClean unused states from all phase.", nullptr, myTLSEditorParent, MID_GNE_TLSFRAME_CLEANUP, GUIDesignButton);
1032 
1033     // add unused states button
1034     new FXButton(this, "Add Unused States\t\tExtend the state vector for all phases by one entry.", nullptr, myTLSEditorParent, MID_GNE_TLSFRAME_ADDUNUSED, GUIDesignButton);
1035 
1036     // show TLSFile
1037     show();
1038 }
1039 
1040 
~TLSPhases()1041 GNETLSEditorFrame::TLSPhases::~TLSPhases() {
1042     delete myTableFont;
1043 }
1044 
1045 
1046 FXTable*
getPhaseTable() const1047 GNETLSEditorFrame::TLSPhases::getPhaseTable() const {
1048     return myPhaseTable;
1049 }
1050 
1051 
1052 void
initPhaseTable(int index)1053 GNETLSEditorFrame::TLSPhases::initPhaseTable(int index) {
1054     myPhaseTable->setVisibleRows(1);
1055     myPhaseTable->setVisibleColumns(2);
1056     myPhaseTable->hide();
1057     if (myTLSEditorParent->myTLSAttributes->getNumberOfTLSDefinitions() > 0) {
1058         const bool fixed = myTLSEditorParent->fixedDuration();
1059         const int cols = fixed ? 4 : 6;
1060         const int colDuration = 0;
1061         const int colMinDur = fixed ? -1 : 1;
1062         const int colMaxDur = fixed ? -1 : 2;
1063         const int colState = fixed ? 1 : 3;
1064         const int colNext = fixed ? 2 : 4;
1065         const int colName = fixed ? 3 : 5;
1066 
1067         const std::vector<NBTrafficLightLogic::PhaseDefinition>& phases = myTLSEditorParent->getPhases();
1068         myPhaseTable->setTableSize((int)phases.size(), cols);
1069         myPhaseTable->setVisibleRows((int)phases.size());
1070         myPhaseTable->setVisibleColumns(cols);
1071         for (int row = 0; row < (int)phases.size(); row++) {
1072             myPhaseTable->setItemText(row, colDuration, toString(STEPS2TIME(phases[row].duration)).c_str());
1073             if (!fixed) {
1074                 myPhaseTable->setItemText(row, colMinDur, varDurString(phases[row].minDur).c_str());
1075                 myPhaseTable->setItemText(row, colMaxDur, varDurString(phases[row].maxDur).c_str());
1076             }
1077             myPhaseTable->setItemText(row, colState, phases[row].state.c_str());
1078             myPhaseTable->setItemText(row, colNext, phases[row].next.size() > 0 ? toString(phases[row].next).c_str() : " ");
1079             myPhaseTable->setItemText(row, colName, phases[row].name.c_str());
1080             myPhaseTable->getItem(row, 1)->setJustify(FXTableItem::LEFT);
1081         }
1082         myPhaseTable->fitColumnsToContents(0, cols);
1083         myPhaseTable->setHeight((int)phases.size() * 21); // experimental
1084         myPhaseTable->setCurrentItem(index, 0);
1085         myPhaseTable->selectRow(index, true);
1086         myPhaseTable->show();
1087         myPhaseTable->setFocus();
1088         myTableScroll->setHeight(myPhaseTable->getHeight() + 10);
1089     }
1090     update();
1091 }
1092 
1093 
1094 void
showCycleDuration()1095 GNETLSEditorFrame::TLSPhases::showCycleDuration() {
1096     myCycleDuration->show();
1097 }
1098 
1099 
1100 void
hideCycleDuration()1101 GNETLSEditorFrame::TLSPhases::hideCycleDuration() {
1102     myCycleDuration->hide();
1103 }
1104 
1105 void
updateCycleDuration()1106 GNETLSEditorFrame::TLSPhases::updateCycleDuration() {
1107     SUMOTime cycleDuration = 0;
1108     for (auto it : myTLSEditorParent->getPhases()) {
1109         cycleDuration += it.duration;
1110     }
1111     std::string text = "Cycle time: " + toString(STEPS2TIME(cycleDuration));
1112     myCycleDuration->setText(text.c_str());
1113 }
1114 
1115 // ---------------------------------------------------------------------------
1116 // GNETLSEditorFrame::TLSModifications - methods
1117 // ---------------------------------------------------------------------------
1118 
TLSModifications(GNETLSEditorFrame * TLSEditorParent)1119 GNETLSEditorFrame::TLSModifications::TLSModifications(GNETLSEditorFrame* TLSEditorParent) :
1120     FXGroupBox(TLSEditorParent->myContentFrame, "Modifications", GUIDesignGroupBoxFrame),
1121     myTLSEditorParent(TLSEditorParent),
1122     myHaveModifications(false) {
1123     // create save modifications button
1124     mySaveModificationsButtons = new FXButton(this, "Save\t\tSave program modifications (Enter)",
1125             GUIIconSubSys::getIcon(ICON_OK), myTLSEditorParent, MID_OK, GUIDesignButton);
1126     // create discard modifications buttons
1127     myDiscardModificationsButtons = new FXButton(this, "Cancel\t\tDiscard program modifications (Esc)",
1128             GUIIconSubSys::getIcon(ICON_CANCEL), myTLSEditorParent, MID_CANCEL, GUIDesignButton);
1129     // show TLSModifications
1130     show();
1131 }
1132 
1133 
~TLSModifications()1134 GNETLSEditorFrame::TLSModifications::~TLSModifications() {}
1135 
1136 
1137 bool
checkHaveModifications() const1138 GNETLSEditorFrame::TLSModifications::checkHaveModifications() const {
1139     return myHaveModifications;
1140 }
1141 
1142 
1143 void
setHaveModifications(bool value)1144 GNETLSEditorFrame::TLSModifications::setHaveModifications(bool value) {
1145     myHaveModifications = value;
1146 }
1147 
1148 // ---------------------------------------------------------------------------
1149 // GNETLSEditorFrame::TLSFile - methods
1150 // ---------------------------------------------------------------------------
1151 
TLSFile(GNETLSEditorFrame * TLSEditorParent)1152 GNETLSEditorFrame::TLSFile::TLSFile(GNETLSEditorFrame* TLSEditorParent) :
1153     FXGroupBox(TLSEditorParent->myContentFrame, "TLS Program", GUIDesignGroupBoxFrame),
1154     myTLSEditorParent(TLSEditorParent) {
1155     // create create tlDef button
1156     myLoadTLSProgramButton = new FXButton(this, "Load TLS Program", nullptr, this, MID_GNE_TLSFRAME_LOAD_PROGRAM, GUIDesignButton);
1157     // create create tlDef button
1158     mySaveTLSProgramButton = new FXButton(this, "Save TLS Program", nullptr, this, MID_GNE_TLSFRAME_SAVE_PROGRAM, GUIDesignButton);
1159     // show TLSFile
1160     show();
1161 }
1162 
1163 
~TLSFile()1164 GNETLSEditorFrame::TLSFile::~TLSFile() {}
1165 
1166 
1167 long
onCmdLoadTLSProgram(FXObject *,FXSelector,void *)1168 GNETLSEditorFrame::TLSFile::onCmdLoadTLSProgram(FXObject*, FXSelector, void*) {
1169     FXFileDialog opendialog(this, "Load TLS Program");
1170     opendialog.setIcon(GUIIconSubSys::getIcon(ICON_MODETLS));
1171     opendialog.setSelectMode(SELECTFILE_EXISTING);
1172     opendialog.setPatternList("*.xml");
1173     if (gCurrentFolder.length() != 0) {
1174         opendialog.setDirectory(gCurrentFolder);
1175     }
1176     if (opendialog.execute()) {
1177         // run parser
1178         NBTrafficLightLogicCont tmpTLLCont;;
1179         NIXMLTrafficLightsHandler tllHandler(tmpTLLCont, myTLSEditorParent->myViewNet->getNet()->getEdgeCont(), true);
1180         tmpTLLCont.insert(myTLSEditorParent->myEditedDef);
1181         XMLSubSys::runParser(tllHandler, opendialog.getFilename().text());
1182 
1183         NBLoadedSUMOTLDef* newDefSameProgram = nullptr;
1184         std::set<NBLoadedSUMOTLDef*> newDefsOtherProgram;
1185         for (auto item : tmpTLLCont.getPrograms(myTLSEditorParent->myEditedDef->getID())) {
1186             if (item.second != myTLSEditorParent->myEditedDef) {
1187                 NBLoadedSUMOTLDef* sdef = dynamic_cast<NBLoadedSUMOTLDef*>(item.second);
1188                 if (item.first == myTLSEditorParent->myEditedDef->getProgramID()) {
1189                     newDefSameProgram = sdef;
1190                 } else {
1191                     newDefsOtherProgram.insert(sdef);
1192                 }
1193             }
1194         }
1195         const int newPrograms = (int)newDefsOtherProgram.size();
1196         if (newPrograms > 0 || newDefSameProgram != nullptr) {
1197             std::vector<NBNode*> nodes = myTLSEditorParent->myEditedDef->getNodes();
1198             for (auto newProg : newDefsOtherProgram) {
1199                 for (auto it_node : nodes) {
1200                     GNEJunction* junction = myTLSEditorParent->getViewNet()->getNet()->retrieveJunction(it_node->getID());
1201                     myTLSEditorParent->getViewNet()->getUndoList()->add(new GNEChange_TLS(junction, newProg, true), true);
1202                 }
1203             }
1204             if (newPrograms > 0) {
1205                 WRITE_MESSAGE("Loaded " + toString(newPrograms) + " new programs for tlLogic '" + myTLSEditorParent->myEditedDef->getID() + "'");
1206             }
1207             if (newDefSameProgram != nullptr) {
1208                 // replace old program when loading the same program ID
1209                 myTLSEditorParent->myEditedDef = newDefSameProgram;
1210                 WRITE_MESSAGE("Updated program '" + newDefSameProgram->getProgramID() +  "' for tlLogic '" + myTLSEditorParent->myEditedDef->getID() + "'");
1211             }
1212         } else {
1213             myTLSEditorParent->getViewNet()->setStatusBarText("No programs found for traffic light '" + myTLSEditorParent->myEditedDef->getID() + "'");
1214         }
1215 
1216         // clean up temporary container to avoid deletion of defs when it's destruct is called
1217         for (NBTrafficLightDefinition* def : tmpTLLCont.getDefinitions()) {
1218             tmpTLLCont.removeProgram(def->getID(), def->getProgramID(), false);
1219         }
1220 
1221         myTLSEditorParent->myTLSPhases->initPhaseTable();
1222         myTLSEditorParent->myTLSModifications->setHaveModifications(true);
1223     }
1224     return 0;
1225 }
1226 
1227 
1228 long
onCmdSaveTLSProgram(FXObject *,FXSelector,void *)1229 GNETLSEditorFrame::TLSFile::onCmdSaveTLSProgram(FXObject*, FXSelector, void*) {
1230     FXString file = MFXUtils::getFilename2Write(this,
1231                     "Save TLS Program as", ".xml",
1232                     GUIIconSubSys::getIcon(ICON_MODETLS),
1233                     gCurrentFolder);
1234     if (file == "") {
1235         return 1;
1236     }
1237     OutputDevice& device = OutputDevice::getDevice(file.text());
1238 
1239     // save program
1240     device.writeXMLHeader("additional", "additional_file.xsd");
1241     device.openTag(SUMO_TAG_TLLOGIC);
1242     device.writeAttr(SUMO_ATTR_ID, myTLSEditorParent->myEditedDef->getLogic()->getID());
1243     device.writeAttr(SUMO_ATTR_TYPE, myTLSEditorParent->myEditedDef->getLogic()->getType());
1244     device.writeAttr(SUMO_ATTR_PROGRAMID, myTLSEditorParent->myEditedDef->getLogic()->getProgramID());
1245     device.writeAttr(SUMO_ATTR_OFFSET, writeSUMOTime(myTLSEditorParent->myEditedDef->getLogic()->getOffset()));
1246     // write the phases
1247     const bool varPhaseLength = myTLSEditorParent->myEditedDef->getLogic()->getType() != TLTYPE_STATIC;
1248     const std::vector<NBTrafficLightLogic::PhaseDefinition>& phases = myTLSEditorParent->myEditedDef->getLogic()->getPhases();
1249     for (auto j : phases) {
1250         device.openTag(SUMO_TAG_PHASE);
1251         device.writeAttr(SUMO_ATTR_DURATION, writeSUMOTime(j.duration));
1252         device.writeAttr(SUMO_ATTR_STATE, j.state);
1253         if (varPhaseLength) {
1254             if (j.minDur != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1255                 device.writeAttr(SUMO_ATTR_MINDURATION, writeSUMOTime(j.minDur));
1256             }
1257             if (j.maxDur != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1258                 device.writeAttr(SUMO_ATTR_MAXDURATION, writeSUMOTime(j.maxDur));
1259             }
1260         }
1261         device.closeTag();
1262     }
1263     device.close();
1264     return 1;
1265 }
1266 
1267 
1268 std::string
writeSUMOTime(SUMOTime steps)1269 GNETLSEditorFrame::TLSFile::writeSUMOTime(SUMOTime steps) {
1270     double time = STEPS2TIME(steps);
1271     if (time == std::floor(time)) {
1272         return toString(int(time));
1273     } else {
1274         return toString(time);
1275     }
1276 }
1277 
1278 long
onUpdNeedsDef(FXObject * o,FXSelector,void *)1279 GNETLSEditorFrame::TLSFile::onUpdNeedsDef(FXObject* o, FXSelector, void*) {
1280     const bool enable = myTLSEditorParent->myTLSAttributes->getNumberOfTLSDefinitions() > 0;
1281     o->handle(this, FXSEL(SEL_COMMAND, enable ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE), nullptr);
1282     return 1;
1283 }
1284 /****************************************************************************/
1285