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    GNEFrame.cpp
11 /// @author  Pablo Alvarez Lopez
12 /// @date    Jun 2016
13 /// @version $Id$
14 ///
15 // The Widget for add additional elements
16 /****************************************************************************/
17 
18 // ===========================================================================
19 // included modules
20 // ===========================================================================
21 
22 #include <config.h>
23 
24 #include <netedit/GNENet.h>
25 #include <netedit/GNEUndoList.h>
26 #include <netedit/GNEViewNet.h>
27 #include <netedit/GNEViewParent.h>
28 #include <netedit/additionals/GNEAdditional.h>
29 #include <netedit/additionals/GNEPOI.h>
30 #include <netedit/demandelements/GNEDemandElement.h>
31 #include <netedit/dialogs/GNEDialog_AllowDisallow.h>
32 #include <netedit/dialogs/GNEGenericParameterDialog.h>
33 #include <netedit/netelements/GNEConnection.h>
34 #include <netedit/netelements/GNECrossing.h>
35 #include <netedit/netelements/GNEEdge.h>
36 #include <netedit/netelements/GNEJunction.h>
37 #include <netedit/netelements/GNELane.h>
38 #include <utils/common/StringTokenizer.h>
39 #include <utils/foxtools/MFXMenuHeader.h>
40 #include <utils/foxtools/MFXUtils.h>
41 #include <utils/gui/div/GUIDesigns.h>
42 #include <utils/gui/images/GUIIconSubSys.h>
43 #include <utils/gui/images/GUITexturesHelper.h>
44 #include <utils/gui/windows/GUIAppEnum.h>
45 #include <utils/gui/windows/GUIMainWindow.h>
46 
47 #include "GNEFrame.h"
48 #include "GNEInspectorFrame.h"
49 #include "GNEDeleteFrame.h"
50 
51 
52 // ===========================================================================
53 // FOX callback mapping
54 // ===========================================================================
55 
56 FXDEFMAP(GNEFrame::ItemSelector) ItemSelectorMap[] = {
57     FXMAPFUNC(SEL_COMMAND, MID_GNE_SET_TYPE,    GNEFrame::ItemSelector::onCmdSelectItem)
58 };
59 
60 FXDEFMAP(GNEFrame::AttributesCreator) AttributesCreatorMap[] = {
61     FXMAPFUNC(SEL_COMMAND,  MID_HELP,   GNEFrame::AttributesCreator::onCmdHelp)
62 };
63 
64 FXDEFMAP(GNEFrame::AttributesCreator::RowCreator) RowCreatorMap[] = {
65     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SET_ATTRIBUTE_TEXT,         GNEFrame::AttributesCreator::RowCreator::onCmdSetAttribute),
66     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SET_ATTRIBUTE_BOOL,         GNEFrame::AttributesCreator::RowCreator::onCmdSetBooleanAttribute),
67     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SET_ATTRIBUTE_DIALOG,       GNEFrame::AttributesCreator::RowCreator::onCmdSetColorAttribute),
68     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SET_ATTRIBUTE_RADIOBUTTON,  GNEFrame::AttributesCreator::RowCreator::onCmdSelectRadioButton)
69 };
70 
71 FXDEFMAP(GNEFrame::AttributesEditor) AttributesEditorMap[] = {
72     FXMAPFUNC(SEL_COMMAND,  MID_HELP,   GNEFrame::AttributesEditor::onCmdAttributesEditorHelp)
73 };
74 
75 FXDEFMAP(GNEFrame::AttributesEditor::RowEditor) RowEditorMap[] = {
76     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SET_ATTRIBUTE,              GNEFrame::AttributesEditor::RowEditor::onCmdSetAttribute),
77     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SET_ATTRIBUTE_DIALOG,       GNEFrame::AttributesEditor::RowEditor::onCmdOpenAttributeDialog),
78     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SET_ATTRIBUTE_RADIOBUTTON,  GNEFrame::AttributesEditor::RowEditor::onCmdSetDisjointAttribute)
79 };
80 
81 FXDEFMAP(GNEFrame::AttributesEditorExtended) AttributesEditorExtendedMap[] = {
82     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SET_ATTRIBUTE_DIALOG,   GNEFrame::AttributesEditorExtended::onCmdOpenDialog)
83 };
84 
85 FXDEFMAP(GNEFrame::ACHierarchy) ACHierarchyMap[] = {
86     FXMAPFUNC(SEL_COMMAND,              MID_GNE_INSPECTORFRAME_CENTER,      GNEFrame::ACHierarchy::onCmdCenterItem),
87     FXMAPFUNC(SEL_COMMAND,              MID_GNE_INSPECTORFRAME_INSPECT,     GNEFrame::ACHierarchy::onCmdInspectItem),
88     FXMAPFUNC(SEL_COMMAND,              MID_GNE_INSPECTORFRAME_DELETE,      GNEFrame::ACHierarchy::onCmdDeleteItem),
89     FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,   MID_GNE_DELETEFRAME_CHILDS,         GNEFrame::ACHierarchy::onCmdShowChildMenu)
90 };
91 
92 FXDEFMAP(GNEFrame::GenericParametersEditor) GenericParametersEditorMap[] = {
93     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SET_ATTRIBUTE_DIALOG,   GNEFrame::GenericParametersEditor::onCmdEditGenericParameter),
94     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SET_ATTRIBUTE,          GNEFrame::GenericParametersEditor::onCmdSetGenericParameter)
95 };
96 
97 FXDEFMAP(GNEFrame::DrawingShape) DrawingShapeMap[] = {
98     FXMAPFUNC(SEL_COMMAND,  MID_GNE_STARTDRAWING,   GNEFrame::DrawingShape::onCmdStartDrawing),
99     FXMAPFUNC(SEL_COMMAND,  MID_GNE_STOPDRAWING,    GNEFrame::DrawingShape::onCmdStopDrawing),
100     FXMAPFUNC(SEL_COMMAND,  MID_GNE_ABORTDRAWING,   GNEFrame::DrawingShape::onCmdAbortDrawing)
101 };
102 
103 FXDEFMAP(GNEFrame::NeteditAttributes) NeteditAttributesMap[] = {
104     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SET_ATTRIBUTE,  GNEFrame::NeteditAttributes::onCmdSetNeteditAttribute),
105     FXMAPFUNC(SEL_COMMAND,  MID_HELP,               GNEFrame::NeteditAttributes::onCmdHelp)
106 };
107 
108 // Object implementation
109 FXIMPLEMENT(GNEFrame::ItemSelector,                     FXGroupBox,         ItemSelectorMap,                ARRAYNUMBER(ItemSelectorMap))
110 FXIMPLEMENT(GNEFrame::AttributesCreator,                FXGroupBox,         AttributesCreatorMap,           ARRAYNUMBER(AttributesCreatorMap))
111 FXIMPLEMENT(GNEFrame::AttributesCreator::RowCreator,    FXHorizontalFrame,  RowCreatorMap,                  ARRAYNUMBER(RowCreatorMap))
112 FXIMPLEMENT(GNEFrame::AttributesEditor,                 FXGroupBox,         AttributesEditorMap,            ARRAYNUMBER(AttributesEditorMap))
113 FXIMPLEMENT(GNEFrame::AttributesEditor::RowEditor,      FXHorizontalFrame,  RowEditorMap,                   ARRAYNUMBER(RowEditorMap))
114 FXIMPLEMENT(GNEFrame::AttributesEditorExtended,         FXGroupBox,         AttributesEditorExtendedMap,    ARRAYNUMBER(AttributesEditorExtendedMap))
115 FXIMPLEMENT(GNEFrame::ACHierarchy,                      FXGroupBox,         ACHierarchyMap,                 ARRAYNUMBER(ACHierarchyMap))
116 FXIMPLEMENT(GNEFrame::GenericParametersEditor,          FXGroupBox,         GenericParametersEditorMap,     ARRAYNUMBER(GenericParametersEditorMap))
117 FXIMPLEMENT(GNEFrame::DrawingShape,                     FXGroupBox,         DrawingShapeMap,                ARRAYNUMBER(DrawingShapeMap))
118 FXIMPLEMENT(GNEFrame::NeteditAttributes,                FXGroupBox,         NeteditAttributesMap,           ARRAYNUMBER(NeteditAttributesMap))
119 
120 // ===========================================================================
121 // static members
122 // ===========================================================================
123 
124 FXFont* GNEFrame::myFrameHeaderFont = nullptr;
125 
126 // ===========================================================================
127 // method definitions
128 // ===========================================================================
129 
130 // ---------------------------------------------------------------------------
131 // GNEFrame::ItemSelector - methods
132 // ---------------------------------------------------------------------------
133 
ItemSelector(GNEFrame * frameParent,GNEAttributeCarrier::TagType type,bool onlyDrawables)134 GNEFrame::ItemSelector::ItemSelector(GNEFrame* frameParent, GNEAttributeCarrier::TagType type, bool onlyDrawables) :
135     FXGroupBox(frameParent->myContentFrame, "Element", GUIDesignGroupBoxFrame),
136     myFrameParent(frameParent) {
137     // first check that property is valid
138     switch (type)     {
139         case GNEAttributeCarrier::TagType::TAGTYPE_NETELEMENT:
140             setText("Net element");
141             break;
142         case GNEAttributeCarrier::TagType::TAGTYPE_ADDITIONAL:
143             setText("Additional element");
144             break;
145         case GNEAttributeCarrier::TagType::TAGTYPE_SHAPE:
146             setText("Shape element");
147             break;
148         case GNEAttributeCarrier::TagType::TAGTYPE_TAZ:
149             setText("TAZ element");
150             break;
151         case GNEAttributeCarrier::TagType::TAGTYPE_VEHICLE:
152             setText("Vehicle");
153             break;
154         case GNEAttributeCarrier::TagType::TAGTYPE_STOP:
155             setText("Stop");
156             break;
157         default:
158             throw ProcessError("invalid tag property");
159     }
160     // fill myListOfTags
161     myListOfTags = GNEAttributeCarrier::allowedTagsByCategory(type, onlyDrawables);
162     // Create FXComboBox
163     myTypeMatchBox = new FXComboBox(this, GUIDesignComboBoxNCol, this, MID_GNE_SET_TYPE, GUIDesignComboBox);
164     // fill myTypeMatchBox with list of tags
165     for (const auto& i : myListOfTags) {
166         myTypeMatchBox->appendItem(toString(i).c_str());
167     }
168     // Set visible items
169     myTypeMatchBox->setNumVisible((int)myTypeMatchBox->getNumItems());
170     // ItemSelector is always shown
171     show();
172 }
173 
174 
~ItemSelector()175 GNEFrame::ItemSelector::~ItemSelector() {}
176 
177 
178 void
showItemSelector(bool enableModuls)179 GNEFrame::ItemSelector::showItemSelector(bool enableModuls) {
180     show();
181     // check if parent moduls has to be enabled
182     if (enableModuls && (myCurrentTagProperties.getTag() != SUMO_TAG_NOTHING)) {
183         myFrameParent->enableModuls(myCurrentTagProperties);
184     }
185 }
186 
187 
188 void
hideItemSelector()189 GNEFrame::ItemSelector::hideItemSelector() {
190     hide();
191     myFrameParent->disableModuls();
192 }
193 
194 
195 const GNEAttributeCarrier::TagProperties&
getCurrentTagProperties() const196 GNEFrame::ItemSelector::getCurrentTagProperties() const {
197     return myCurrentTagProperties;
198 }
199 
200 
201 void
setCurrentTypeTag(SumoXMLTag typeTag)202 GNEFrame::ItemSelector::setCurrentTypeTag(SumoXMLTag typeTag) {
203     // set empty tag properties
204     myCurrentTagProperties = GNEAttributeCarrier::TagProperties();
205     // make sure that tag is in myTypeMatchBox
206     for (int i = 0; i < (int)myTypeMatchBox->getNumItems(); i++) {
207         if (myTypeMatchBox->getItem(i).text() == toString(typeTag)) {
208             myTypeMatchBox->setCurrentItem(i);
209             // Set new current type
210             myCurrentTagProperties = GNEAttributeCarrier::getTagProperties(typeTag);
211         }
212     }
213     // Check that typeTag type is valid
214     if (myCurrentTagProperties.getTag() != SUMO_TAG_NOTHING) {
215         // show moduls if selected item is valid
216         myFrameParent->enableModuls(myCurrentTagProperties);
217     } else {
218         // hide all moduls if selected item isn't valid
219         myFrameParent->disableModuls();
220     }
221 }
222 
223 
224 void
refreshTagProperties()225 GNEFrame::ItemSelector::refreshTagProperties() {
226     // simply call onCmdSelectItem (to avoid duplicated code)
227     onCmdSelectItem(0, 0, 0);
228 }
229 
230 
231 long
onCmdSelectItem(FXObject *,FXSelector,void *)232 GNEFrame::ItemSelector::onCmdSelectItem(FXObject*, FXSelector, void*) {
233     // Check if value of myTypeMatchBox correspond of an allowed additional tags
234     for (const auto& i : myListOfTags) {
235         if (toString(i) == myTypeMatchBox->getText().text()) {
236             // set color of myTypeMatchBox to black (valid)
237             myTypeMatchBox->setTextColor(FXRGB(0, 0, 0));
238             // Set new current type
239             myCurrentTagProperties = GNEAttributeCarrier::getTagProperties(i);
240             // show moduls if selected item is valid
241             myFrameParent->enableModuls(myCurrentTagProperties);
242             // Write Warning in console if we're in testing mode
243             WRITE_DEBUG(("Selected item '" + myTypeMatchBox->getText() + "' in ItemSelector").text());
244             return 1;
245         }
246     }
247     // if additional name isn't correct, set SUMO_TAG_NOTHING as current type
248     myCurrentTagProperties = myInvalidTagProperty;
249     // hide all moduls if selected item isn't valid
250     myFrameParent->disableModuls();
251     // set color of myTypeMatchBox to red (invalid)
252     myTypeMatchBox->setTextColor(FXRGB(255, 0, 0));
253     // Write Warning in console if we're in testing mode
254     WRITE_DEBUG("Selected invalid item in ItemSelector");
255     return 1;
256 }
257 
258 // ---------------------------------------------------------------------------
259 // GNEFrame::NeteditAttributes- methods
260 // ---------------------------------------------------------------------------
261 
AttributesCreator(GNEFrame * frameParent)262 GNEFrame::AttributesCreator::AttributesCreator(GNEFrame* frameParent) :
263     FXGroupBox(frameParent->myContentFrame, "Internal attributes", GUIDesignGroupBoxFrame),
264     myFrameParent(frameParent) {
265     // Create single parameters
266     for (int i = 0; i < GNEAttributeCarrier::getHigherNumberOfAttributes(); i++) {
267         myRows.push_back(new RowCreator(this));
268     }
269     // Create help button
270     new FXButton(this, "Help", nullptr, this, MID_HELP, GUIDesignButtonRectangular);
271 }
272 
273 
~AttributesCreator()274 GNEFrame::AttributesCreator::~AttributesCreator() {
275 }
276 
277 
278 void
showAttributesCreatorModul(const GNEAttributeCarrier::TagProperties & tagProperties)279 GNEFrame::AttributesCreator::showAttributesCreatorModul(const GNEAttributeCarrier::TagProperties& tagProperties) {
280     // get current tag Properties
281     myTagProperties = tagProperties;
282     // Hide all fields
283     for (int i = 0; i < (int)myRows.size(); i++) {
284         myRows.at(i)->hideParameter();
285     }
286     // iterate over tag attributes and show it
287     for (auto i : myTagProperties) {
288         //  make sure that only non-unique attributes are shown (And depending of includeExtendedAttributes)
289         if (!i.second.isUnique()) {
290             myRows.at(i.second.getPositionListed())->showParameter(i.second);
291         }
292     }
293     // update disjoint attributes
294     updateDisjointAttributes(nullptr);
295     // recalc frame and show again
296     recalc();
297     show();
298 }
299 
300 
301 void
hideAttributesCreatorModul()302 GNEFrame::AttributesCreator::hideAttributesCreatorModul() {
303     hide();
304 }
305 
306 
307 std::map<SumoXMLAttr, std::string>
getAttributesAndValues(bool includeAll) const308 GNEFrame::AttributesCreator::getAttributesAndValues(bool includeAll) const {
309     std::map<SumoXMLAttr, std::string> values;
310     // get standard parameters
311     for (int i = 0; i < (int)myRows.size(); i++) {
312         if (myRows.at(i)->getAttrProperties().getAttr() != SUMO_ATTR_NOTHING) {
313             // ignore default values (except for disjont attributes, that has to be always writted)
314             if (myRows.at(i)->isRowEnabled() &&
315                     (includeAll || myTagProperties.isDisjointAttributes(myRows.at(i)->getAttrProperties().getAttr()) || !myRows.at(i)->getAttrProperties().hasDefaultValue() || (myRows.at(i)->getAttrProperties().getDefaultValue() != myRows.at(i)->getValue()))) {
316                 values[myRows.at(i)->getAttrProperties().getAttr()] = myRows.at(i)->getValue();
317             }
318         }
319     }
320     return values;
321 }
322 
323 
324 void
showWarningMessage(std::string extra) const325 GNEFrame::AttributesCreator::showWarningMessage(std::string extra) const {
326     std::string errorMessage;
327     // iterate over standar parameters
328     for (auto i : myTagProperties) {
329         if (errorMessage.empty()) {
330             // Return string with the error if at least one of the parameter isn't valid
331             std::string attributeValue = myRows.at(i.second.getPositionListed())->isAttributeValid();
332             if (attributeValue.size() != 0) {
333                 errorMessage = attributeValue;
334             }
335         }
336     }
337     // show warning box if input parameters aren't invalid
338     if (extra.size() == 0) {
339         errorMessage = "Invalid input parameter of " + myTagProperties.getTagStr() + ": " + errorMessage;
340     } else {
341         errorMessage = "Invalid input parameter of " + myTagProperties.getTagStr() + ": " + extra;
342     }
343 
344     // set message in status bar
345     myFrameParent->getViewNet()->setStatusBarText(errorMessage);
346     // Write Warning in console if we're in testing mode
347     WRITE_DEBUG(errorMessage);
348 }
349 
350 
351 bool
areValuesValid() const352 GNEFrame::AttributesCreator::areValuesValid() const {
353     // iterate over standar parameters
354     for (auto i : myTagProperties) {
355         // Return false if error message of attriuve isn't empty
356         if (myRows.at(i.second.getPositionListed())->isAttributeValid().size() != 0) {
357             return false;
358         }
359     }
360     return true;
361 }
362 
363 
364 void
updateDisjointAttributes(AttributesCreator::RowCreator * row)365 GNEFrame::AttributesCreator::updateDisjointAttributes(AttributesCreator::RowCreator* row) {
366     // currently only Flows supports disjoint attributes
367     if (myTagProperties.getTag() == SUMO_TAG_FLOW) {
368         // obtain all rows (to improve code legibility)
369         RowCreator* endRow = myRows[myTagProperties.getAttributeProperties(SUMO_ATTR_END).getPositionListed()];
370         RowCreator* numberRow = myRows[myTagProperties.getAttributeProperties(SUMO_ATTR_NUMBER).getPositionListed()];
371         RowCreator* vehsperhourRow = myRows[myTagProperties.getAttributeProperties(SUMO_ATTR_VEHSPERHOUR).getPositionListed()];
372         RowCreator* periodRow = myRows[myTagProperties.getAttributeProperties(SUMO_ATTR_PERIOD).getPositionListed()];
373         RowCreator* probabilityRow = myRows[myTagProperties.getAttributeProperties(SUMO_ATTR_PROB).getPositionListed()];
374         if (row == nullptr) {
375             // by default flows uses end and number
376             endRow->setRadioButtonCheck(true);
377             numberRow->setRadioButtonCheck(true);
378             vehsperhourRow->setRadioButtonCheck(false);
379             periodRow->setRadioButtonCheck(false);
380             probabilityRow->setRadioButtonCheck(false);
381         } else {
382             // check what row was clicked
383             switch (row->getAttrProperties().getAttr()) {
384                 // end has more priority as number
385                 case SUMO_ATTR_END:
386                     endRow->setRadioButtonCheck(true);
387                     // disable other combinations
388                     vehsperhourRow->setRadioButtonCheck(false);
389                     periodRow->setRadioButtonCheck(false);
390                     probabilityRow->setRadioButtonCheck(false);
391                     break;
392                 case SUMO_ATTR_NUMBER:
393                     numberRow->setRadioButtonCheck(true);
394                     // disable number if begin and end are enabled because end has more priority as number
395                     if (endRow->getRadioButtonCheck()) {
396                         endRow->setRadioButtonCheck(false);
397                     } else {
398                         // disable other combinations
399                         vehsperhourRow->setRadioButtonCheck(false);
400                         periodRow->setRadioButtonCheck(false);
401                         probabilityRow->setRadioButtonCheck(false);
402                     }
403                     break;
404                 case SUMO_ATTR_VEHSPERHOUR:
405                     // disable number if begin and end are enabled because end has more priority as number
406                     if (endRow->getRadioButtonCheck() && numberRow->getRadioButtonCheck()) {
407                         numberRow->setRadioButtonCheck(false);
408                     }
409                     // disable other combinations
410                     vehsperhourRow->setRadioButtonCheck(true);
411                     periodRow->setRadioButtonCheck(false);
412                     probabilityRow->setRadioButtonCheck(false);
413                     break;
414                 case SUMO_ATTR_PERIOD:
415                     // disable number if begin and end are enabled because end has more priority as number
416                     if (endRow->getRadioButtonCheck() && numberRow->getRadioButtonCheck()) {
417                         numberRow->setRadioButtonCheck(false);
418                     }
419                     // disable other combinations
420                     vehsperhourRow->setRadioButtonCheck(false);
421                     periodRow->setRadioButtonCheck(true);
422                     probabilityRow->setRadioButtonCheck(false);
423                     break;
424                 case SUMO_ATTR_PROB:
425                     // disable number if begin and end are enabled because end has more priority as number
426                     if (endRow->getRadioButtonCheck() && numberRow->getRadioButtonCheck()) {
427                         numberRow->setRadioButtonCheck(false);
428                     }
429                     // disable other combinations
430                     vehsperhourRow->setRadioButtonCheck(false);
431                     periodRow->setRadioButtonCheck(false);
432                     probabilityRow->setRadioButtonCheck(true);
433                     break;
434                 default:
435                     break;
436             }
437         }
438     } else if (myTagProperties.isStop()) {
439         // check if expected has to be enabled or disabled
440         if (myRows[myTagProperties.getAttributeProperties(SUMO_ATTR_TRIGGERED).getPositionListed()]->getValue() == "1") {
441             myRows[myTagProperties.getAttributeProperties(SUMO_ATTR_EXPECTED).getPositionListed()]->enableRow();
442         } else {
443             myRows[myTagProperties.getAttributeProperties(SUMO_ATTR_EXPECTED).getPositionListed()]->disableRow();
444         }
445         // check if expected contaienrs has to be enabled or disabled
446         if (myRows[myTagProperties.getAttributeProperties(SUMO_ATTR_CONTAINER_TRIGGERED).getPositionListed()]->getValue() == "1") {
447             myRows[myTagProperties.getAttributeProperties(SUMO_ATTR_EXPECTED_CONTAINERS).getPositionListed()]->enableRow();
448         } else {
449             myRows[myTagProperties.getAttributeProperties(SUMO_ATTR_EXPECTED_CONTAINERS).getPositionListed()]->disableRow();
450         }
451     }
452 }
453 
454 
455 long
onCmdHelp(FXObject *,FXSelector,void *)456 GNEFrame::AttributesCreator::onCmdHelp(FXObject*, FXSelector, void*) {
457     // open Help attributes dialog
458     myFrameParent->openHelpAttributesDialog(myTagProperties);
459     return 1;
460 }
461 
462 // ---------------------------------------------------------------------------
463 // GNEFrame::AttributesCreator::RowCreator - methods
464 // ---------------------------------------------------------------------------
465 
RowCreator(AttributesCreator * AttributesCreatorParent)466 GNEFrame::AttributesCreator::RowCreator::RowCreator(AttributesCreator* AttributesCreatorParent) :
467     FXHorizontalFrame(AttributesCreatorParent, GUIDesignAuxiliarHorizontalFrame),
468     myAttributesCreatorParent(AttributesCreatorParent) {
469     // Create left visual elements
470     myLabel = new FXLabel(this, "name", nullptr, GUIDesignLabelAttribute);
471     myColorEditor = new FXButton(this, "ColorButton", nullptr, this, MID_GNE_SET_ATTRIBUTE_DIALOG, GUIDesignButtonAttribute);
472     myRadioButton = new FXRadioButton(this, "name", this, MID_GNE_SET_ATTRIBUTE_RADIOBUTTON, GUIDesignRadioButtonAttribute);
473     // Create right visual elements
474     myTextFieldInt = new FXTextField(this, GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE_TEXT, GUIDesignTextFieldInt);
475     myTextFieldReal = new FXTextField(this, GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE_TEXT, GUIDesignTextFieldReal);
476     myTextFieldStrings = new FXTextField(this, GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE_TEXT, GUIDesignTextField);
477     myBoolCheckButton = new FXCheckButton(this, "Disabled", this, MID_GNE_SET_ATTRIBUTE_BOOL, GUIDesignCheckButtonAttribute);
478     // Hide elements
479     hideParameter();
480 }
481 
482 
483 void
showParameter(const GNEAttributeCarrier::AttributeProperties & attrProperties)484 GNEFrame::AttributesCreator::RowCreator::showParameter(const GNEAttributeCarrier::AttributeProperties& attrProperties) {
485     myAttrProperties = attrProperties;
486     myInvalidValue = "";
487     // show label, button for edit colors or radio button
488     if (myAttrProperties.isColor()) {
489         myColorEditor->setTextColor(FXRGB(0, 0, 0));
490         myColorEditor->setText(myAttrProperties.getAttrStr().c_str());
491         myColorEditor->show();
492     } else if (myAttributesCreatorParent->myTagProperties.isDisjointAttributes(myAttrProperties.getAttr())) {
493         myRadioButton->setText(myAttrProperties.getAttrStr().c_str());
494         myRadioButton->show();
495     } else {
496         myLabel->setText(myAttrProperties.getAttrStr().c_str());
497         myLabel->show();
498     }
499     if (myAttrProperties.isInt()) {
500         myTextFieldInt->setTextColor(FXRGB(0, 0, 0));
501         myTextFieldInt->setText(attrProperties.getDefaultValue().c_str());
502         myTextFieldInt->show();
503         // if it's associated to a radio button and is disabled, then disabled myTextFieldInt
504         if (myRadioButton->shown() && (myRadioButton->getCheck() == FALSE)) {
505             myTextFieldInt->disable();
506         }
507     } else if (myAttrProperties.isFloat()) {
508         myTextFieldReal->setTextColor(FXRGB(0, 0, 0));
509         myTextFieldReal->setText(attrProperties.getDefaultValue().c_str());
510         myTextFieldReal->show();
511         // if it's associated to a radio button and is disabled, then disable myTextFieldReal
512         if (myRadioButton->shown() && (myRadioButton->getCheck() == FALSE)) {
513             myTextFieldReal->disable();
514         }
515     } else if (myAttrProperties.isBool()) {
516         if (GNEAttributeCarrier::parse<bool>(attrProperties.getDefaultValue())) {
517             myBoolCheckButton->setCheck(true);
518             myBoolCheckButton->setText("true");
519         } else {
520             myBoolCheckButton->setCheck(false);
521             myBoolCheckButton->setText("false");
522         }
523         myBoolCheckButton->show();
524         // if it's associated to a radio button and is disabled, then disable myBoolCheckButton
525         if (myRadioButton->shown() && (myRadioButton->getCheck() == FALSE)) {
526             myBoolCheckButton->disable();
527         }
528     } else {
529         myTextFieldStrings->setTextColor(FXRGB(0, 0, 0));
530         myTextFieldStrings->setText(attrProperties.getDefaultValue().c_str());
531         myTextFieldStrings->show();
532         // if it's associated to a radio button and is disabled, then disable myTextFieldStrings
533         if (myRadioButton->shown() && (myRadioButton->getCheck() == FALSE)) {
534             myTextFieldStrings->disable();
535         }
536     }
537     show();
538 }
539 
540 
541 void
hideParameter()542 GNEFrame::AttributesCreator::RowCreator::hideParameter() {
543     myAttrProperties = GNEAttributeCarrier::AttributeProperties();
544     myLabel->hide();
545     myTextFieldInt->hide();
546     myTextFieldReal->hide();
547     myTextFieldStrings->hide();
548     myBoolCheckButton->hide();
549     myColorEditor->hide();
550     myRadioButton->hide();
551     hide();
552 }
553 
554 
555 const GNEAttributeCarrier::AttributeProperties&
getAttrProperties() const556 GNEFrame::AttributesCreator::RowCreator::getAttrProperties() const {
557     return myAttrProperties;
558 }
559 
560 
561 std::string
getValue() const562 GNEFrame::AttributesCreator::RowCreator::getValue() const {
563     if (myAttrProperties.isBool()) {
564         return (myBoolCheckButton->getCheck() == 1) ? "1" : "0";
565     } else if (myAttrProperties.isInt()) {
566         return myTextFieldInt->getText().text();
567     } else if (myAttrProperties.isFloat() || myAttrProperties.isTime()) {
568         return myTextFieldReal->getText().text();
569     } else {
570         return myTextFieldStrings->getText().text();
571     }
572 }
573 
574 
575 bool
getRadioButtonCheck() const576 GNEFrame::AttributesCreator::RowCreator::getRadioButtonCheck() const {
577     if (shown()) {
578         return myRadioButton->getCheck() == TRUE;
579     } else {
580         return false;
581     }
582 }
583 
584 
585 void
setRadioButtonCheck(bool value)586 GNEFrame::AttributesCreator::RowCreator::setRadioButtonCheck(bool value) {
587     if (shown()) {
588         // set radio button
589         myRadioButton->setCheck(value);
590         // enable or disable input fields
591         if (value) {
592             if (myAttrProperties.isBool()) {
593                 myBoolCheckButton->enable();
594             } else if (myAttrProperties.isInt()) {
595                 myTextFieldInt->enable();
596             } else if (myAttrProperties.isFloat() || myAttrProperties.isTime()) {
597                 myTextFieldReal->enable();
598             } else {
599                 myTextFieldStrings->enable();
600             }
601         } else {
602             if (myAttrProperties.isBool()) {
603                 myBoolCheckButton->disable();
604             } else if (myAttrProperties.isInt()) {
605                 myTextFieldInt->disable();
606             } else if (myAttrProperties.isFloat() || myAttrProperties.isTime()) {
607                 myTextFieldReal->disable();
608             } else {
609                 myTextFieldStrings->disable();
610             }
611         }
612     }
613 }
614 
615 
616 void
enableRow()617 GNEFrame::AttributesCreator::RowCreator::enableRow() {
618     if (myAttrProperties.isBool()) {
619         return myBoolCheckButton->enable();
620     } else if (myAttrProperties.isInt()) {
621         return myTextFieldInt->enable();
622     } else if (myAttrProperties.isFloat() || myAttrProperties.isTime()) {
623         return myTextFieldReal->enable();
624     } else {
625         return myTextFieldStrings->enable();
626     }
627 }
628 
629 
630 void
disableRow()631 GNEFrame::AttributesCreator::RowCreator::disableRow() {
632     if (myAttrProperties.isBool()) {
633         return myBoolCheckButton->disable();
634     } else if (myAttrProperties.isInt()) {
635         return myTextFieldInt->disable();
636     } else if (myAttrProperties.isFloat() || myAttrProperties.isTime()) {
637         return myTextFieldReal->disable();
638     } else {
639         return myTextFieldStrings->disable();
640     }
641 }
642 
643 
644 bool
isRowEnabled() const645 GNEFrame::AttributesCreator::RowCreator::isRowEnabled() const {
646     if (!shown()) {
647         return false;
648     } else if (myAttrProperties.isBool()) {
649         return myBoolCheckButton->isEnabled();
650     } else if (myAttrProperties.isInt()) {
651         return myTextFieldInt->isEnabled();
652     } else if (myAttrProperties.isFloat() || myAttrProperties.isTime()) {
653         return myTextFieldReal->isEnabled();
654     } else {
655         return myTextFieldStrings->isEnabled();
656     }
657 }
658 
659 
660 const std::string&
isAttributeValid() const661 GNEFrame::AttributesCreator::RowCreator::isAttributeValid() const {
662     return myInvalidValue;
663 }
664 
665 
666 GNEFrame::AttributesCreator*
getAttributesCreatorParent() const667 GNEFrame::AttributesCreator::RowCreator::getAttributesCreatorParent() const {
668     return myAttributesCreatorParent;
669 }
670 
671 
672 long
onCmdSetAttribute(FXObject *,FXSelector,void *)673 GNEFrame::AttributesCreator::RowCreator::onCmdSetAttribute(FXObject*, FXSelector, void*) {
674     // We assume that current value is valid
675     myInvalidValue = "";
676     // Check if format of current value of myTextField is correct
677     if (myAttrProperties.isInt()) {
678         if (GNEAttributeCarrier::canParse<int>(myTextFieldInt->getText().text())) {
679             // convert string to int
680             int intValue = GNEAttributeCarrier::parse<int>(myTextFieldInt->getText().text());
681             // Check if int value must be positive
682             if (myAttrProperties.isPositive() && (intValue < 0)) {
683                 myInvalidValue = "'" + myAttrProperties.getAttrStr() + "' cannot be negative";
684             }
685         } else {
686             myInvalidValue = "'" + myAttrProperties.getAttrStr() + "' doesn't have a valid 'int' format";
687         }
688     } else if (myAttrProperties.isTime()) {
689         // time attributes work as positive doubles
690         if (GNEAttributeCarrier::canParse<double>(myTextFieldReal->getText().text())) {
691             // convert string to double
692             double doubleValue = GNEAttributeCarrier::parse<double>(myTextFieldReal->getText().text());
693             // Check if parsed value is negative
694             if (doubleValue < 0) {
695                 myInvalidValue = "'" + myAttrProperties.getAttrStr() + "' cannot be negative";
696             }
697         } else {
698             myInvalidValue = "'" + myAttrProperties.getAttrStr() + "' doesn't have a valid 'time' format";
699         }
700     } else if (myAttrProperties.isFloat()) {
701         if (GNEAttributeCarrier::canParse<double>(myTextFieldReal->getText().text())) {
702             // convert string to double
703             double doubleValue = GNEAttributeCarrier::parse<double>(myTextFieldReal->getText().text());
704             // Check if double value must be positive
705             if (myAttrProperties.isPositive() && (doubleValue < 0)) {
706                 myInvalidValue = "'" + myAttrProperties.getAttrStr() + "' cannot be negative";
707                 // check if double value is a probability
708             } else if (myAttrProperties.isProbability() && ((doubleValue < 0) || doubleValue > 1)) {
709                 myInvalidValue = "'" + myAttrProperties.getAttrStr() + "' takes only values between 0 and 1";
710             } else if (myAttrProperties.hasAttrRange() && ((doubleValue < myAttrProperties.getMinimumRange()) || doubleValue > myAttrProperties.getMaximumRange())) {
711                 myInvalidValue = "'" + myAttrProperties.getAttrStr() + "' takes only values between " + toString(myAttrProperties.getMinimumRange()) + " and " + toString(myAttrProperties.getMaximumRange());
712             } else if ((myAttributesCreatorParent->myTagProperties.getTag() == SUMO_TAG_E2DETECTOR) && (myAttrProperties.getAttr() == SUMO_ATTR_LENGTH) && (doubleValue == 0)) {
713                 myInvalidValue = "E2 length cannot be 0";
714             }
715         } else {
716             myInvalidValue = "'" + myAttrProperties.getAttrStr() + "' doesn't have a valid 'float' format";
717         }
718     } else if (myAttrProperties.isColor()) {
719         // check if filename format is valid
720         if (GNEAttributeCarrier::canParse<RGBColor>(myTextFieldStrings->getText().text()) == false) {
721             myInvalidValue = "'" + myAttrProperties.getAttrStr() + "' doesn't have a valid 'RBGColor' format";
722         }
723     } else if (myAttrProperties.isFilename()) {
724         std::string file = myTextFieldStrings->getText().text();
725         // check if filename format is valid
726         if (SUMOXMLDefinitions::isValidFilename(file) == false) {
727             myInvalidValue = "input contains invalid characters for a filename";
728         } else if (myAttrProperties.getAttr() == SUMO_ATTR_IMGFILE) {
729             if (!file.empty()) {
730                 // only load value if file exist and can be loaded
731                 if (GUITexturesHelper::getTextureID(file) == -1) {
732                     myInvalidValue = "doesn't exist image '" + file + "'";
733                 }
734             }
735         }
736     } else if (myAttrProperties.getAttr() == SUMO_ATTR_NAME) {
737         std::string name = myTextFieldStrings->getText().text();
738         // check if name format is valid
739         if (SUMOXMLDefinitions::isValidAttribute(name) == false) {
740             myInvalidValue = "input contains invalid characters";
741         }
742     } else if (myAttrProperties.getAttr() == SUMO_ATTR_VTYPES) {
743         std::string name = myTextFieldStrings->getText().text();
744         // if list of VTypes isn't empty, check that all characters are valid
745         if (!name.empty() && !SUMOXMLDefinitions::isValidListOfTypeID(name)) {
746             myInvalidValue = "list of IDs contains invalid characters";
747         }
748     } else if (myAttrProperties.getAttr() == SUMO_ATTR_INDEX) {
749         // special case for stop indx
750         std::string index = myTextFieldStrings->getText().text();
751         if ((index != "fit") && (index != "end") && !GNEAttributeCarrier::canParse<int>(index)) {
752             myInvalidValue = "index isn't either 'fit' or 'end' or a valid positive int";
753         } else if (GNEAttributeCarrier::parse<int>(index) < 0) {
754             myInvalidValue = "index cannot be negative";
755         }
756     }
757     // change color of text field depending of myCurrentValueValid
758     if (myInvalidValue.size() == 0) {
759         myTextFieldInt->setTextColor(FXRGB(0, 0, 0));
760         myTextFieldInt->killFocus();
761         myTextFieldReal->setTextColor(FXRGB(0, 0, 0));
762         myTextFieldReal->killFocus();
763         myTextFieldStrings->setTextColor(FXRGB(0, 0, 0));
764         myTextFieldStrings->killFocus();
765     } else {
766         // IF value of TextField isn't valid, change their color to Red
767         myTextFieldInt->setTextColor(FXRGB(255, 0, 0));
768         myTextFieldReal->setTextColor(FXRGB(255, 0, 0));
769         myTextFieldStrings->setTextColor(FXRGB(255, 0, 0));
770     }
771     // Update aditional frame
772     update();
773     return 1;
774 }
775 
776 
777 long
onCmdSetBooleanAttribute(FXObject *,FXSelector,void *)778 GNEFrame::AttributesCreator::RowCreator::onCmdSetBooleanAttribute(FXObject*, FXSelector, void*) {
779     if (myBoolCheckButton->getCheck()) {
780         myBoolCheckButton->setText("true");
781     } else {
782         myBoolCheckButton->setText("false");
783     }
784     // update disjoint attribute
785     myAttributesCreatorParent->updateDisjointAttributes(nullptr);
786     return 0;
787 }
788 
789 
790 long
onCmdSetColorAttribute(FXObject *,FXSelector,void *)791 GNEFrame::AttributesCreator::RowCreator::onCmdSetColorAttribute(FXObject*, FXSelector, void*) {
792     // create FXColorDialog
793     FXColorDialog colordialog(this, tr("Color Dialog"));
794     colordialog.setTarget(this);
795     // If previous attribute wasn't correct, set black as default color
796     if (GNEAttributeCarrier::canParse<RGBColor>(myTextFieldStrings->getText().text())) {
797         colordialog.setRGBA(MFXUtils::getFXColor(RGBColor::parseColor(myTextFieldStrings->getText().text())));
798     } else {
799         colordialog.setRGBA(MFXUtils::getFXColor(RGBColor::parseColor(myAttrProperties.getDefaultValue())));
800     }
801     // execute dialog to get a new color
802     if (colordialog.execute()) {
803         myTextFieldStrings->setText(toString(MFXUtils::getRGBColor(colordialog.getRGBA())).c_str());
804         onCmdSetAttribute(nullptr, 0, nullptr);
805     }
806     return 0;
807 }
808 
809 long
onCmdSelectRadioButton(FXObject *,FXSelector,void *)810 GNEFrame::AttributesCreator::RowCreator::onCmdSelectRadioButton(FXObject*, FXSelector, void*) {
811     // write debug (for Netedit tests)
812     WRITE_DEBUG("Selected radio button for attribute '" + myAttrProperties.getAttrStr() + "'");
813     // update disjoint attributes in AC Attributes parent
814     myAttributesCreatorParent->updateDisjointAttributes(this);
815     return 0;
816 }
817 
818 // ---------------------------------------------------------------------------
819 // GNEFrame::AttributesEditor::RowEditor - methods
820 // ---------------------------------------------------------------------------
821 
RowEditor(GNEFrame::AttributesEditor * attributeEditorParent)822 GNEFrame::AttributesEditor::RowEditor::RowEditor(GNEFrame::AttributesEditor* attributeEditorParent) :
823     FXHorizontalFrame(attributeEditorParent, GUIDesignAuxiliarHorizontalFrame),
824     myAttributesEditorParent(attributeEditorParent),
825     myMultiple(false) {
826     // Create and hide label
827     myLabel = new FXLabel(this, "attributeLabel", nullptr, GUIDesignLabelAttribute);
828     myLabel->hide();
829     // Create and hide radio button
830     myRadioButton = new FXRadioButton(this, "name", this, MID_GNE_SET_ATTRIBUTE_RADIOBUTTON, GUIDesignRadioButtonAttribute);
831     myRadioButton->hide();
832     // Create and hide ButtonCombinableChoices
833     myButtonCombinableChoices = new FXButton(this, "AttributeButton", nullptr, this, MID_GNE_SET_ATTRIBUTE_DIALOG, GUIDesignButtonAttribute);
834     myButtonCombinableChoices->hide();
835     // create and hidde color editor
836     myColorEditor = new FXButton(this, "ColorButton", nullptr, this, MID_GNE_SET_ATTRIBUTE_DIALOG, GUIDesignButtonAttribute);
837     myColorEditor->hide();
838     // Create and hide textField for int attributes
839     myTextFieldInt = new FXTextField(this, GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextFieldInt);
840     myTextFieldInt->hide();
841     // Create and hide textField for real/time attributes
842     myTextFieldReal = new FXTextField(this, GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextFieldReal);
843     myTextFieldReal->hide();
844     // Create and hide textField for string attributes
845     myTextFieldStrings = new FXTextField(this, GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextField);
846     myTextFieldStrings->hide();
847     // Create and hide ComboBox
848     myChoicesCombo = new FXComboBox(this, GUIDesignComboBoxNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignComboBoxAttribute);
849     myChoicesCombo->hide();
850     // Create and hide checkButton
851     myBoolCheckButton = new FXCheckButton(this, "", this, MID_GNE_SET_ATTRIBUTE, GUIDesignCheckButtonAttribute);
852     myBoolCheckButton->hide();
853 }
854 
855 
856 void
showRow(const GNEAttributeCarrier::AttributeProperties & ACAttr,const std::string & value,bool disjointAttributeEnabled)857 GNEFrame::AttributesEditor::RowEditor::showRow(const GNEAttributeCarrier::AttributeProperties& ACAttr, const std::string& value, bool disjointAttributeEnabled) {
858     // start enabling all elements
859     myTextFieldInt->enable();
860     myTextFieldReal->enable();
861     myTextFieldStrings->enable();
862     myChoicesCombo->enable();
863     myBoolCheckButton->enable();
864     myButtonCombinableChoices->enable();
865     myColorEditor->enable();
866     myRadioButton->enable();
867     // Set current Attribute Property
868     myACAttr = ACAttr;
869     // set multiple
870     myMultiple = GNEAttributeCarrier::parse<std::vector<std::string>>(value).size() > 1;
871     if (myACAttr.isColor()) {
872         myColorEditor->setTextColor(FXRGB(0, 0, 0));
873         myColorEditor->setText(myACAttr.getAttrStr().c_str());
874         myColorEditor->show();
875     } else if (myACAttr.getTagPropertyParent().isDisjointAttributes(myACAttr.getAttr())) {
876         myRadioButton->setTextColor(FXRGB(0, 0, 0));
877         myRadioButton->setText(myACAttr.getAttrStr().c_str());
878         myRadioButton->setCheck(disjointAttributeEnabled);
879         myRadioButton->show();
880     } else {
881         // Show attribute Label
882         myLabel->setText(myACAttr.getAttrStr().c_str());
883         myLabel->show();
884     }
885     // Set field depending of the type of value
886     if (myACAttr.isBool()) {
887         // first we need to check if all boolean values are equal
888         bool allBooleanValuesEqual = true;
889         // declare  boolean vector
890         std::vector<bool> booleanVector;
891         // check if value can be parsed to a boolean vector
892         if (GNEAttributeCarrier::canParse<std::vector<bool> >(value)) {
893             booleanVector = GNEAttributeCarrier::parse<std::vector<bool> >(value);
894         }
895         // iterate over pased booleans comparing all element with the first
896         for (const auto& i : booleanVector) {
897             if (i != booleanVector.front()) {
898                 allBooleanValuesEqual = false;
899             }
900         }
901         // use checkbox or textfield depending if all booleans are equal
902         if (allBooleanValuesEqual) {
903             // set check button
904             if ((booleanVector.size() > 0) && booleanVector.front()) {
905                 myBoolCheckButton->setCheck(true);
906                 myBoolCheckButton->setText("true");
907             } else {
908                 myBoolCheckButton->setCheck(false);
909                 myBoolCheckButton->setText("false");
910             }
911             // show check button
912             myBoolCheckButton->show();
913             // enable or disable depending if attribute is editable and is enabled (used by disjoint attributes)
914             if (myACAttr.isNonEditable() || !disjointAttributeEnabled) {
915                 myBoolCheckButton->disable();
916             }
917         } else {
918             // show list of bools (0 1)
919             myTextFieldStrings->setText(value.c_str());
920             myTextFieldStrings->setTextColor(FXRGB(0, 0, 0));
921             myTextFieldStrings->show();
922             // enable or disable depending if attribute is editable and is enabled (used by disjoint attributes)
923             if (myACAttr.isNonEditable() || !disjointAttributeEnabled) {
924                 myTextFieldStrings->disable();
925             }
926         }
927     } else if (myACAttr.isDiscrete()) {
928         // Check if are combinable choices
929         if ((myACAttr.getDiscreteValues().size() > 0) && myACAttr.isCombinable()) {
930             // hide label
931             myLabel->hide();
932             // Show button combinable choices
933             myButtonCombinableChoices->setText(myACAttr.getAttrStr().c_str());
934             myButtonCombinableChoices->show();
935             // Show string with the values
936             myTextFieldStrings->setText(value.c_str());
937             myTextFieldStrings->setTextColor(FXRGB(0, 0, 0));
938             myTextFieldStrings->show();
939         } else if (!myMultiple) {
940             // fill comboBox
941             myChoicesCombo->clearItems();
942             for (const auto& it : myACAttr.getDiscreteValues()) {
943                 myChoicesCombo->appendItem(it.c_str());
944             }
945             // show combo box with values
946             myChoicesCombo->setNumVisible((int)myACAttr.getDiscreteValues().size());
947             myChoicesCombo->setCurrentItem(myChoicesCombo->findItem(value.c_str()));
948             myChoicesCombo->setTextColor(FXRGB(0, 0, 0));
949             myChoicesCombo->show();
950             // enable or disable depending if attribute is editable and is enabled (used by disjoint attributes)
951             if (myACAttr.isNonEditable() || !disjointAttributeEnabled) {
952                 myChoicesCombo->disable();
953             }
954         } else {
955             // represent combinable choices in multiple selections always with a textfield instead with a comboBox
956             myTextFieldStrings->setText(value.c_str());
957             myTextFieldStrings->setTextColor(FXRGB(0, 0, 0));
958             myTextFieldStrings->show();
959             // enable or disable depending if attribute is editable and is enabled (used by disjoint attributes)
960             if (myACAttr.isNonEditable() || !disjointAttributeEnabled) {
961                 myTextFieldStrings->disable();
962             }
963         }
964     } else if (myACAttr.isFloat() || myACAttr.isTime()) {
965         // show TextField for real/time values
966         myTextFieldReal->setText(value.c_str());
967         myTextFieldReal->setTextColor(FXRGB(0, 0, 0));
968         myTextFieldReal->show();
969         // enable or disable depending if attribute is editable and is enabled (used by disjoint attributes)
970         if (myACAttr.isNonEditable() || !disjointAttributeEnabled) {
971             myTextFieldReal->disable();
972         }
973     } else if (myACAttr.isInt()) {
974         // Show textField for int attributes
975         myTextFieldInt->setText(value.c_str());
976         myTextFieldInt->setTextColor(FXRGB(0, 0, 0));
977         myTextFieldInt->show();
978         // enable or disable depending if attribute is editable and is enabled (used by disjoint attributes)
979         if (myACAttr.isNonEditable() || !disjointAttributeEnabled) {
980             myTextFieldInt->disable();
981         }
982         // we need an extra check for connection attribute "TLIndex", because it cannot be edited if junction's connection doesn' have a TLS
983         if ((myACAttr.getTagPropertyParent().getTag() == SUMO_TAG_CONNECTION) && (myACAttr.getAttr() == SUMO_ATTR_TLLINKINDEX) && (value == "No TLS")) {
984             myTextFieldInt->disable();
985         }
986     } else {
987         // In any other case (String, list, etc.), show value as String
988         myTextFieldStrings->setText(value.c_str());
989         myTextFieldStrings->setTextColor(FXRGB(0, 0, 0));
990         myTextFieldStrings->show();
991         // enable or disable depending if attribute is editable and is enabled (used by disjoint attributes)
992         if (myACAttr.isNonEditable() || !disjointAttributeEnabled) {
993             myTextFieldStrings->disable();
994         }
995     }
996     // if Tag correspond to an network element but we're in demand mode (or vice versa), disable all elements
997     if (((myAttributesEditorParent->myFrameParent->getViewNet()->getEditModes().currentSupermode == GNE_SUPERMODE_NETWORK) && myACAttr.getTagPropertyParent().isDemandElement()) ||
998             ((myAttributesEditorParent->myFrameParent->getViewNet()->getEditModes().currentSupermode == GNE_SUPERMODE_DEMAND) && !myACAttr.getTagPropertyParent().isDemandElement())) {
999         myColorEditor->disable();
1000         myRadioButton->disable();
1001         myTextFieldInt->disable();
1002         myTextFieldReal->disable();
1003         myTextFieldStrings->disable();
1004         myChoicesCombo->disable();
1005         myBoolCheckButton->disable();
1006         myButtonCombinableChoices->disable();
1007     }
1008     // special case for Default vehicle types (ID cannot be edited)
1009     if ((ACAttr.getTagPropertyParent().getTag() == SUMO_TAG_VTYPE) && (ACAttr.getAttr() == SUMO_ATTR_ID) &&
1010             ((value == DEFAULT_VTYPE_ID) || (value == DEFAULT_PEDTYPE_ID) || (value == DEFAULT_BIKETYPE_ID))) {
1011         myTextFieldStrings->disable();
1012     }
1013     // Show Row
1014     show();
1015 }
1016 
1017 
1018 void
hideRow()1019 GNEFrame::AttributesEditor::RowEditor::hideRow() {
1020     // Hide all elements
1021     myLabel->hide();
1022     myColorEditor->hide();
1023     myRadioButton->hide();
1024     myTextFieldInt->hide();
1025     myTextFieldReal->hide();
1026     myTextFieldStrings->hide();
1027     myChoicesCombo->hide();
1028     myBoolCheckButton->hide();
1029     myButtonCombinableChoices->hide();
1030     // hide Row
1031     hide();
1032     // recalc after hide all elements
1033     recalc();
1034 }
1035 
1036 
1037 void
refreshRow(const std::string & value,bool forceRefresh,bool disjointAttributeEnabled)1038 GNEFrame::AttributesEditor::RowEditor::refreshRow(const std::string& value, bool forceRefresh, bool disjointAttributeEnabled) {
1039     // start enabling all elements
1040     myTextFieldInt->enable();
1041     myTextFieldReal->enable();
1042     myTextFieldStrings->enable();
1043     myChoicesCombo->enable();
1044     myBoolCheckButton->enable();
1045     myButtonCombinableChoices->enable();
1046     myColorEditor->enable();
1047     myRadioButton->enable();
1048     // set radio buton
1049     if (myRadioButton->shown()) {
1050         myRadioButton->setCheck(disjointAttributeEnabled);
1051     }
1052     if (myTextFieldInt->shown()) {
1053         // set last valid value and restore color if onlyValid is disabled
1054         if (myTextFieldInt->getTextColor() == FXRGB(0, 0, 0) || forceRefresh) {
1055             myTextFieldInt->setText(value.c_str());
1056             myTextFieldInt->setTextColor(FXRGB(0, 0, 0));
1057         }
1058         // disable depending of disjointAttributeEnabled
1059         if (myACAttr.isNonEditable() || !disjointAttributeEnabled) {
1060             myTextFieldInt->disable();
1061         }
1062     } else if (myTextFieldReal->shown()) {
1063         // set last valid value and restore color if onlyValid is disabled
1064         if (myTextFieldReal->getTextColor() == FXRGB(0, 0, 0) || forceRefresh) {
1065             myTextFieldReal->setText(value.c_str());
1066             myTextFieldReal->setTextColor(FXRGB(0, 0, 0));
1067         }
1068         // disable depending of disjointAttributeEnabled
1069         if (myACAttr.isNonEditable() || !disjointAttributeEnabled) {
1070             myTextFieldReal->disable();
1071         }
1072     } else if (myTextFieldStrings->shown()) {
1073         // set last valid value and restore color if onlyValid is disabled
1074         if (myTextFieldStrings->getTextColor() == FXRGB(0, 0, 0) || forceRefresh) {
1075             myTextFieldStrings->setText(value.c_str());
1076             myTextFieldStrings->setTextColor(FXRGB(0, 0, 0));
1077         }
1078         // disable depending of disjointAttributeEnabled
1079         if (myACAttr.isNonEditable() || !disjointAttributeEnabled) {
1080             myTextFieldStrings->disable();
1081         }
1082     } else if (myChoicesCombo->shown()) {
1083         // fill comboBox again
1084         myChoicesCombo->clearItems();
1085         for (const auto& it : myACAttr.getDiscreteValues()) {
1086             myChoicesCombo->appendItem(it.c_str());
1087         }
1088         // show combo box with values
1089         myChoicesCombo->setNumVisible((int)myACAttr.getDiscreteValues().size());
1090         myChoicesCombo->setCurrentItem(myChoicesCombo->findItem(value.c_str()));
1091         myChoicesCombo->setTextColor(FXRGB(0, 0, 0));
1092         myChoicesCombo->show();
1093         // disable depending of disjointAttributeEnabled
1094         if (myACAttr.isNonEditable() || !disjointAttributeEnabled) {
1095             myChoicesCombo->disable();
1096         }
1097     } else if (myBoolCheckButton->shown()) {
1098         if (GNEAttributeCarrier::canParse<bool>(value)) {
1099             myBoolCheckButton->setCheck(GNEAttributeCarrier::parse<bool>(value));
1100         } else {
1101             myBoolCheckButton->setCheck(false);
1102         }
1103         // disable depending of disjointAttributeEnabled
1104         if (myACAttr.isNonEditable() || !disjointAttributeEnabled) {
1105             myBoolCheckButton->disable();
1106         }
1107     }
1108     // if Tag correspond to an network element but we're in demand mode (or vice versa), disable all elements
1109     if (myACAttr.getAttr() != SUMO_ATTR_NOTHING) {
1110         if (((myAttributesEditorParent->myFrameParent->getViewNet()->getEditModes().currentSupermode == GNE_SUPERMODE_NETWORK) && myACAttr.getTagPropertyParent().isDemandElement()) ||
1111                 ((myAttributesEditorParent->myFrameParent->getViewNet()->getEditModes().currentSupermode == GNE_SUPERMODE_DEMAND) && !myACAttr.getTagPropertyParent().isDemandElement())) {
1112             myColorEditor->disable();
1113             myRadioButton->disable();
1114             myTextFieldInt->disable();
1115             myTextFieldReal->disable();
1116             myTextFieldStrings->disable();
1117             myChoicesCombo->disable();
1118             myBoolCheckButton->disable();
1119             myButtonCombinableChoices->disable();
1120         }
1121         // special case for Default vehicle types (ID cannot be edited)
1122         if ((myACAttr.getTagPropertyParent().getTag() == SUMO_TAG_VTYPE) && (myACAttr.getAttr() == SUMO_ATTR_ID) &&
1123                 ((value == DEFAULT_VTYPE_ID) || (value == DEFAULT_PEDTYPE_ID) || (value == DEFAULT_BIKETYPE_ID))) {
1124             myTextFieldStrings->disable();
1125         }
1126     }
1127 }
1128 
1129 
1130 bool
isRowValid() const1131 GNEFrame::AttributesEditor::RowEditor::isRowValid() const {
1132     return ((myTextFieldInt->getTextColor() == FXRGB(0, 0, 0)) && (myTextFieldReal->getTextColor() == FXRGB(0, 0, 0)) &&
1133             (myTextFieldStrings->getTextColor() == FXRGB(0, 0, 0)) && (myChoicesCombo->getTextColor() == FXRGB(0, 0, 0)));
1134 }
1135 
1136 
1137 long
onCmdOpenAttributeDialog(FXObject * obj,FXSelector,void *)1138 GNEFrame::AttributesEditor::RowEditor::onCmdOpenAttributeDialog(FXObject* obj, FXSelector, void*) {
1139     if (obj == myColorEditor) {
1140         // create FXColorDialog
1141         FXColorDialog colordialog(this, tr("Color Dialog"));
1142         colordialog.setTarget(this);
1143         // If previous attribute wasn't correct, set black as default color
1144         if (GNEAttributeCarrier::canParse<RGBColor>(myTextFieldStrings->getText().text())) {
1145             colordialog.setRGBA(MFXUtils::getFXColor(RGBColor::parseColor(myTextFieldStrings->getText().text())));
1146         } else if (!myACAttr.getDefaultValue().empty()) {
1147             colordialog.setRGBA(MFXUtils::getFXColor(RGBColor::parseColor(myACAttr.getDefaultValue())));
1148         } else {
1149             colordialog.setRGBA(MFXUtils::getFXColor(RGBColor::BLACK));
1150         }
1151         // execute dialog to get a new color
1152         if (colordialog.execute()) {
1153             std::string newValue = toString(MFXUtils::getRGBColor(colordialog.getRGBA()));
1154             myTextFieldStrings->setText(newValue.c_str());
1155             if (myAttributesEditorParent->myEditedACs.front()->isValid(myACAttr.getAttr(), newValue)) {
1156                 // if its valid for the first AC than its valid for all (of the same type)
1157                 if (myAttributesEditorParent->myEditedACs.size() > 1) {
1158                     myAttributesEditorParent->myFrameParent->getViewNet()->getUndoList()->p_begin("Change multiple attributes");
1159                 }
1160                 // Set new value of attribute in all selected ACs
1161                 for (const auto& it_ac : myAttributesEditorParent->myEditedACs) {
1162                     it_ac->setAttribute(myACAttr.getAttr(), newValue, myAttributesEditorParent->myFrameParent->getViewNet()->getUndoList());
1163                 }
1164                 // If previously value was incorrect, change font color to black
1165                 myTextFieldStrings->setTextColor(FXRGB(0, 0, 0));
1166                 myTextFieldStrings->killFocus();
1167             }
1168         }
1169         return 0;
1170     } else if (obj == myButtonCombinableChoices) {
1171         // if its valid for the first AC than its valid for all (of the same type)
1172         if (myAttributesEditorParent->myEditedACs.size() > 1) {
1173             myAttributesEditorParent->myFrameParent->getViewNet()->getUndoList()->p_begin("Change multiple attributes");
1174         }
1175         // open GNEDialog_AllowDisallow
1176         GNEDialog_AllowDisallow(myAttributesEditorParent->myFrameParent->getViewNet(), myAttributesEditorParent->myEditedACs.front()).execute();
1177         std::string allowed = myAttributesEditorParent->myEditedACs.front()->getAttribute(SUMO_ATTR_ALLOW);
1178         // Set new value of attribute in all selected ACs
1179         for (const auto& it_ac : myAttributesEditorParent->myEditedACs) {
1180             it_ac->setAttribute(SUMO_ATTR_ALLOW, allowed, myAttributesEditorParent->myFrameParent->getViewNet()->getUndoList());
1181         }
1182         // finish change multiple attributes
1183         if (myAttributesEditorParent->myEditedACs.size() > 1) {
1184             myAttributesEditorParent->myFrameParent->getViewNet()->getUndoList()->p_end();
1185         }
1186         // update frame parent after attribute sucesfully set
1187         myAttributesEditorParent->myFrameParent->updateFrameAfterChangeAttribute();
1188         return 1;
1189     } else {
1190         throw ProcessError("Invalid call to onCmdOpenAttributeDialog");
1191     }
1192 }
1193 
1194 
1195 long
onCmdSetAttribute(FXObject *,FXSelector,void *)1196 GNEFrame::AttributesEditor::RowEditor::onCmdSetAttribute(FXObject*, FXSelector, void*) {
1197     // Declare changed value
1198     std::string newVal;
1199     // First, obtain the string value of the new attribute depending of their type
1200     if (myACAttr.isBool()) {
1201         // first check if we're editing boolean as a list of string or as a checkbox
1202         if (myBoolCheckButton->shown()) {
1203             // Set true o false depending of the checkBox
1204             if (myBoolCheckButton->getCheck()) {
1205                 myBoolCheckButton->setText("true");
1206                 newVal = "true";
1207             } else {
1208                 myBoolCheckButton->setText("false");
1209                 newVal = "false";
1210             }
1211         } else {
1212             // obtain boolean value of myTextFieldStrings (because we're inspecting multiple attribute carriers with different values)
1213             newVal = myTextFieldStrings->getText().text();
1214         }
1215     } else if (myACAttr.isDiscrete()) {
1216         // Check if are combinable choices (for example, Vehicle Types)
1217         if ((myACAttr.getDiscreteValues().size() > 0) &&
1218                 myACAttr.isCombinable()) {
1219             // Get value obtained using AttributesEditor
1220             newVal = myTextFieldStrings->getText().text();
1221         } else if (!myMultiple) {
1222             // Get value of ComboBox
1223             newVal = myChoicesCombo->getText().text();
1224         } else {
1225             // due this is a multiple selection, obtain value of myTextFieldStrings instead of comboBox
1226             newVal = myTextFieldStrings->getText().text();
1227         }
1228     } else if (myACAttr.isFloat() || myACAttr.isTime()) {
1229         // Check if default value of attribute must be set
1230         if (myTextFieldReal->getText().empty() && myACAttr.hasDefaultValue()) {
1231             newVal = myACAttr.getDefaultValue();
1232             myTextFieldReal->setText(newVal.c_str());
1233         } else {
1234             // obtain value of myTextFieldReal
1235             newVal = myTextFieldReal->getText().text();
1236         }
1237     } else if (myACAttr.isInt()) {
1238         // Check if default value of attribute must be set
1239         if (myTextFieldInt->getText().empty() && myACAttr.hasDefaultValue()) {
1240             newVal = myACAttr.getDefaultValue();
1241             myTextFieldInt->setText(newVal.c_str());
1242         } else {
1243             // obtain value of myTextFieldInt
1244             newVal = myTextFieldInt->getText().text();
1245         }
1246     } else if (myACAttr.isString()) {
1247         // Check if default value of attribute must be set
1248         if (myTextFieldStrings->getText().empty() && myACAttr.hasDefaultValue()) {
1249             newVal = myACAttr.getDefaultValue();
1250             myTextFieldStrings->setText(newVal.c_str());
1251         } else {
1252             // obtain value of myTextFieldStrings
1253             newVal = myTextFieldStrings->getText().text();
1254         }
1255     }
1256 
1257     // we need a extra check for Position and Shape Values, due #2658
1258     if ((myACAttr.getAttr() == SUMO_ATTR_POSITION) || (myACAttr.getAttr() == SUMO_ATTR_SHAPE)) {
1259         newVal = stripWhitespaceAfterComma(newVal);
1260     }
1261 
1262     // Check if attribute must be changed
1263     if (myAttributesEditorParent->myEditedACs.front()->isValid(myACAttr.getAttr(), newVal)) {
1264         // if its valid for the first AC than its valid for all (of the same type)
1265         if (myAttributesEditorParent->myEditedACs.size() > 1) {
1266             myAttributesEditorParent->myFrameParent->getViewNet()->getUndoList()->p_begin("Change multiple attributes");
1267         } else if (myACAttr.getAttr() == SUMO_ATTR_ID) {
1268             // IDs attribute has to be encapsulated
1269             myAttributesEditorParent->myFrameParent->getViewNet()->getUndoList()->p_begin("change " + myACAttr.getTagPropertyParent().getTagStr() + " attribute");
1270         }
1271         // Set new value of attribute in all selected ACs
1272         for (const auto& it_ac : myAttributesEditorParent->myEditedACs) {
1273             it_ac->setAttribute(myACAttr.getAttr(), newVal, myAttributesEditorParent->myFrameParent->getViewNet()->getUndoList());
1274         }
1275         // finish change multiple attributes or ID Attributes
1276         if (myAttributesEditorParent->myEditedACs.size() > 1) {
1277             myAttributesEditorParent->myFrameParent->getViewNet()->getUndoList()->p_end();
1278         } else if (myACAttr.getAttr() == SUMO_ATTR_ID) {
1279             myAttributesEditorParent->myFrameParent->getViewNet()->getUndoList()->p_end();
1280         }
1281         // If previously value was incorrect, change font color to black
1282         if (myACAttr.isCombinable()) {
1283             myTextFieldStrings->setTextColor(FXRGB(0, 0, 0));
1284             myTextFieldStrings->killFocus();
1285             // in this case, we need to refresh the other values (For example, allow/Disallow objects)
1286             myAttributesEditorParent->refreshAttributeEditor(false, false);
1287         } else if (myACAttr.isDiscrete()) {
1288             myChoicesCombo->setTextColor(FXRGB(0, 0, 0));
1289             myChoicesCombo->killFocus();
1290         } else if (myACAttr.isFloat() || myACAttr.isTime()) {
1291             myTextFieldReal->setTextColor(FXRGB(0, 0, 0));
1292             myTextFieldReal->killFocus();
1293         } else if (myACAttr.isInt() && myTextFieldStrings != nullptr) {
1294             myTextFieldInt->setTextColor(FXRGB(0, 0, 0));
1295             myTextFieldInt->killFocus();
1296         } else if (myTextFieldStrings != nullptr) {
1297             myTextFieldStrings->setTextColor(FXRGB(0, 0, 0));
1298             myTextFieldStrings->killFocus();
1299         }
1300         // update frame parent after attribute sucesfully set
1301         myAttributesEditorParent->myFrameParent->updateFrameAfterChangeAttribute();
1302     } else {
1303         // If value of TextField isn't valid, change color to Red depending of type
1304         if (myACAttr.isCombinable()) {
1305             myTextFieldStrings->setTextColor(FXRGB(255, 0, 0));
1306             myTextFieldStrings->killFocus();
1307         } else if (myACAttr.isDiscrete()) {
1308             myChoicesCombo->setTextColor(FXRGB(255, 0, 0));
1309             myChoicesCombo->killFocus();
1310         } else if (myACAttr.isFloat() || myACAttr.isTime()) {
1311             myTextFieldReal->setTextColor(FXRGB(255, 0, 0));
1312         } else if (myACAttr.isInt() && myTextFieldStrings != nullptr) {
1313             myTextFieldInt->setTextColor(FXRGB(255, 0, 0));
1314         } else if (myTextFieldStrings != nullptr) {
1315             myTextFieldStrings->setTextColor(FXRGB(255, 0, 0));
1316         }
1317         // Write Warning in console if we're in testing mode
1318         WRITE_DEBUG("Value '" + newVal + "' for attribute " + myACAttr.getAttrStr() + " of " + myACAttr.getTagPropertyParent().getTagStr() + " isn't valid");
1319     }
1320     return 1;
1321 }
1322 
1323 
1324 long
onCmdSetDisjointAttribute(FXObject *,FXSelector,void *)1325 GNEFrame::AttributesEditor::RowEditor::onCmdSetDisjointAttribute(FXObject*, FXSelector, void*) {
1326     // write debug (for Netedit tests)
1327     WRITE_DEBUG("Selected radio button for attribute '" + myACAttr.getAttrStr() + "'");
1328     // change disjoint attribute with undo/redo
1329     myAttributesEditorParent->myEditedACs.front()->setDisjointAttribute(myACAttr.getAttr(),
1330             myAttributesEditorParent->myFrameParent->myViewNet->getUndoList());
1331     // refresh Attributes edito parent
1332     myAttributesEditorParent->refreshAttributeEditor(false, false);
1333     return 0;
1334 }
1335 
1336 
1337 std::string
stripWhitespaceAfterComma(const std::string & stringValue)1338 GNEFrame::AttributesEditor::RowEditor::stripWhitespaceAfterComma(const std::string& stringValue) {
1339     std::string result(stringValue);
1340     while (result.find(", ") != std::string::npos) {
1341         result = StringUtils::replace(result, ", ", ",");
1342     }
1343     return result;
1344 }
1345 
1346 // ---------------------------------------------------------------------------
1347 // GNEFrame::AttributesEditor - methods
1348 // ---------------------------------------------------------------------------
1349 
AttributesEditor(GNEFrame * FrameParent)1350 GNEFrame::AttributesEditor::AttributesEditor(GNEFrame* FrameParent) :
1351     FXGroupBox(FrameParent->myContentFrame, "Internal attributes", GUIDesignGroupBoxFrame),
1352     myFrameParent(FrameParent),
1353     myIncludeExtended(true) {
1354     // Create sufficient Row for all types of AttributeCarriers
1355     for (int i = 0; i < (int)GNEAttributeCarrier::getHigherNumberOfAttributes(); i++) {
1356         myVectorOfRows.push_back(new RowEditor(this));
1357     }
1358     // Create help button
1359     myHelpButton = new FXButton(this, "Help", nullptr, this, MID_HELP, GUIDesignButtonRectangular);
1360 }
1361 
1362 
1363 void
showAttributeEditorModul(const std::vector<GNEAttributeCarrier * > & ACs,bool includeExtended)1364 GNEFrame::AttributesEditor::showAttributeEditorModul(const std::vector<GNEAttributeCarrier*>& ACs, bool includeExtended) {
1365     myEditedACs = ACs;
1366     myIncludeExtended = includeExtended;
1367     // first hide all rows
1368     for (const auto& i : myVectorOfRows) {
1369         i->hideRow();
1370     }
1371     if (myEditedACs.size() > 0) {
1372         //  check if current AC is a Junction without TLSs (needed to hidde TLS options)
1373         bool disableTLSinJunctions = (dynamic_cast<GNEJunction*>(myEditedACs.front()) && (dynamic_cast<GNEJunction*>(myEditedACs.front())->getNBNode()->getControllingTLS().empty()));
1374         // Iterate over attributes
1375         for (const auto& i : myEditedACs.front()->getTagProperty()) {
1376             // disable editing for unique attributes in case of multi-selection
1377             if ((myEditedACs.size() > 1) && i.second.isUnique()) {
1378                 continue;
1379             }
1380             // disable editing of extended attributes if includeExtended isn't enabled
1381             if (i.second.isExtended() && !includeExtended) {
1382                 continue;
1383             }
1384             // Declare a set of occuring values and insert attribute's values of item (note: We use a set to avoid repeated values)
1385             std::set<std::string> occuringValues;
1386             for (const auto& it_ac : myEditedACs) {
1387                 occuringValues.insert(it_ac->getAttribute(i.first));
1388             }
1389             // get current value
1390             std::ostringstream oss;
1391             for (auto it_val = occuringValues.begin(); it_val != occuringValues.end(); it_val++) {
1392                 if (it_val != occuringValues.begin()) {
1393                     oss << " ";
1394                 }
1395                 oss << *it_val;
1396             }
1397             std::string value = oss.str();
1398             if ((myEditedACs.front()->getTagProperty().getTag() == SUMO_TAG_CONNECTION) &&
1399                     (i.first == SUMO_ATTR_TLLINKINDEX)
1400                     && value == toString(NBConnection::InvalidTlIndex)) {
1401                 // possibly the connections are newly created (allow assigning
1402                 // tlIndex if the junction(s) have a traffic light
1403                 for (const auto& it_ac : myEditedACs) {
1404                     if (!it_ac->isValid(SUMO_ATTR_TLLINKINDEX, "0")) {
1405                         value =  "No TLS";
1406                         break;
1407                     }
1408                 }
1409             }
1410             // Show attribute
1411             if ((disableTLSinJunctions && (myEditedACs.front()->getTagProperty().getTag() == SUMO_TAG_JUNCTION) &&
1412                     ((i.first == SUMO_ATTR_TLTYPE) || (i.first == SUMO_ATTR_TLID))) == false) {
1413                 // first show AttributesEditor
1414                 show();
1415                 // show attribute
1416                 myVectorOfRows[i.second.getPositionListed()]->showRow(i.second, value, myEditedACs.front()->isDisjointAttributeSet(i.first));
1417             }
1418         }
1419     }
1420 }
1421 
1422 
1423 void
hideAttributesEditorModul()1424 GNEFrame::AttributesEditor::hideAttributesEditorModul() {
1425     // hide al attributes
1426     for (const auto& i : myVectorOfRows) {
1427         i->hideRow();
1428     }
1429     // clear myEditedACs
1430     myEditedACs.clear();
1431     // hide also AttributesEditor
1432     hide();
1433 }
1434 
1435 
1436 void
refreshAttributeEditor(bool forceRefreshShape,bool forceRefreshPosition)1437 GNEFrame::AttributesEditor::refreshAttributeEditor(bool forceRefreshShape, bool forceRefreshPosition) {
1438     if (myEditedACs.size() > 0) {
1439         //  check if current AC is a Junction without TLSs (needed to hidde TLS options)
1440         bool disableTLSinJunctions = (dynamic_cast<GNEJunction*>(myEditedACs.front()) && (dynamic_cast<GNEJunction*>(myEditedACs.front())->getNBNode()->getControllingTLS().empty()));
1441         // Iterate over attributes
1442         for (const auto& i : myEditedACs.front()->getTagProperty()) {
1443             // disable editing for unique attributes in case of multi-selection
1444             if ((myEditedACs.size() > 1) && i.second.isUnique()) {
1445                 continue;
1446             }
1447             // Declare a set of occuring values and insert attribute's values of item
1448             std::set<std::string> occuringValues;
1449             for (const auto& it_ac : myEditedACs) {
1450                 occuringValues.insert(it_ac->getAttribute(i.first));
1451             }
1452             // get current value
1453             std::ostringstream oss;
1454             for (auto it_val = occuringValues.begin(); it_val != occuringValues.end(); it_val++) {
1455                 if (it_val != occuringValues.begin()) {
1456                     oss << " ";
1457                 }
1458                 oss << *it_val;
1459             }
1460             // Show attribute
1461             if ((disableTLSinJunctions && (myEditedACs.front()->getTagProperty().getTag() == SUMO_TAG_JUNCTION) &&
1462                     ((i.first == SUMO_ATTR_TLTYPE) || (i.first == SUMO_ATTR_TLID))) == false) {
1463                 // check if is a disjoint attribute
1464                 bool disjointAttributeSet = myEditedACs.front()->isDisjointAttributeSet(i.first);
1465                 // Check if refresh of Position or Shape has to be forced
1466                 if ((i.first  == SUMO_ATTR_SHAPE) && forceRefreshShape) {
1467                     myVectorOfRows[i.second.getPositionListed()]->refreshRow(oss.str(), true, disjointAttributeSet);
1468                 } else if ((i.first  == SUMO_ATTR_POSITION) && forceRefreshPosition) {
1469                     // Refresh attributes maintain invalid values
1470                     myVectorOfRows[i.second.getPositionListed()]->refreshRow(oss.str(), true, disjointAttributeSet);
1471                 } else {
1472                     // Refresh attributes maintain invalid values
1473                     myVectorOfRows[i.second.getPositionListed()]->refreshRow(oss.str(), false, disjointAttributeSet);
1474                 }
1475             }
1476         }
1477     }
1478 }
1479 
1480 
1481 const std::vector<GNEAttributeCarrier*>&
getEditedACs() const1482 GNEFrame::AttributesEditor::getEditedACs() const {
1483     return myEditedACs;
1484 }
1485 
1486 
1487 void
removeEditedAC(GNEAttributeCarrier * AC)1488 GNEFrame::AttributesEditor::removeEditedAC(GNEAttributeCarrier* AC) {
1489     // Only remove if there is inspected ACs
1490     if (myEditedACs.size() > 0) {
1491         // Try to find AC in myACs
1492         auto i = std::find(myEditedACs.begin(), myEditedACs.end(), AC);
1493         // if was found
1494         if (i != myEditedACs.end()) {
1495             // erase AC from inspected ACs
1496             myEditedACs.erase(i);
1497             // Write Warning in console if we're in testing mode
1498             WRITE_DEBUG("Removed inspected element from Inspected ACs. " + toString(myEditedACs.size()) + " ACs remains.");
1499             // Inspect multi selection again (To refresh Modul)
1500             showAttributeEditorModul(myEditedACs, myIncludeExtended);
1501         }
1502     }
1503 }
1504 
1505 
1506 long
onCmdAttributesEditorHelp(FXObject *,FXSelector,void *)1507 GNEFrame::AttributesEditor::onCmdAttributesEditorHelp(FXObject*, FXSelector, void*) {
1508     // open Help attributes dialog if there is inspected ACs
1509     if (myEditedACs.size() > 0) {
1510         // open Help attributes dialog
1511         myFrameParent->openHelpAttributesDialog(myEditedACs.front()->getTagProperty());
1512     }
1513     return 1;
1514 }
1515 
1516 // ---------------------------------------------------------------------------
1517 // GNEFrame::AttributesEditorExtended- methods
1518 // ---------------------------------------------------------------------------
1519 
AttributesEditorExtended(GNEFrame * frameParent)1520 GNEFrame::AttributesEditorExtended::AttributesEditorExtended(GNEFrame* frameParent) :
1521     FXGroupBox(frameParent->myContentFrame, "Extended attributes", GUIDesignGroupBoxFrame),
1522     myFrameParent(frameParent) {
1523     // Create open dialog button
1524     new FXButton(this, "Open attributes editor", nullptr, this, MID_GNE_SET_ATTRIBUTE_DIALOG, GUIDesignButton);
1525 }
1526 
1527 
~AttributesEditorExtended()1528 GNEFrame::AttributesEditorExtended::~AttributesEditorExtended() {}
1529 
1530 
1531 void
showAttributesEditorExtendedModul()1532 GNEFrame::AttributesEditorExtended::showAttributesEditorExtendedModul() {
1533     show();
1534 }
1535 
1536 
1537 void
hideAttributesEditorExtendedModul()1538 GNEFrame::AttributesEditorExtended::hideAttributesEditorExtendedModul() {
1539     hide();
1540 }
1541 
1542 
1543 long
onCmdOpenDialog(FXObject *,FXSelector,void *)1544 GNEFrame::AttributesEditorExtended::onCmdOpenDialog(FXObject*, FXSelector, void*) {
1545     // open AttributesCreator extended dialog
1546     myFrameParent->openAttributesEditorExtendedDialog();
1547     return 1;
1548 }
1549 
1550 // ---------------------------------------------------------------------------
1551 // GNEFrame::ACHierarchy - methods
1552 // ---------------------------------------------------------------------------
1553 
ACHierarchy(GNEFrame * frameParent)1554 GNEFrame::ACHierarchy::ACHierarchy(GNEFrame* frameParent) :
1555     FXGroupBox(frameParent->myContentFrame, "Hierarchy", GUIDesignGroupBoxFrame),
1556     myFrameParent(frameParent),
1557     myAC(nullptr) {
1558     // Create three list
1559     myTreelist = new FXTreeList(this, this, MID_GNE_DELETEFRAME_CHILDS, GUIDesignTreeListFrame);
1560     hide();
1561 }
1562 
1563 
~ACHierarchy()1564 GNEFrame::ACHierarchy::~ACHierarchy() {}
1565 
1566 
1567 void
showACHierarchy(GNEAttributeCarrier * AC)1568 GNEFrame::ACHierarchy::showACHierarchy(GNEAttributeCarrier* AC) {
1569     myAC = AC;
1570     // show ACHierarchy and refresh ACHierarchy
1571     if (myAC) {
1572         show();
1573         refreshACHierarchy();
1574     }
1575 }
1576 
1577 
1578 void
hideACHierarchy()1579 GNEFrame::ACHierarchy::hideACHierarchy() {
1580     myAC = nullptr;
1581     hide();
1582 }
1583 
1584 
1585 void
refreshACHierarchy()1586 GNEFrame::ACHierarchy::refreshACHierarchy() {
1587     // clear items
1588     myTreelist->clearItems();
1589     myTreeItemToACMap.clear();
1590     myTreeItemsConnections.clear();
1591     // show ACChilds of myAC
1592     if (myAC) {
1593         showAttributeCarrierChilds(myAC, showAttributeCarrierParents());
1594     }
1595 }
1596 
1597 
1598 long
onCmdShowChildMenu(FXObject *,FXSelector,void * eventData)1599 GNEFrame::ACHierarchy::onCmdShowChildMenu(FXObject*, FXSelector, void* eventData) {
1600     // Obtain event
1601     FXEvent* e = (FXEvent*)eventData;
1602     // obtain FXTreeItem in the given position
1603     FXTreeItem* item = myTreelist->getItemAt(e->win_x, e->win_y);
1604     // open Pop-up if FXTreeItem has a Attribute Carrier vinculated
1605     if (item && (myTreeItemsConnections.find(item) == myTreeItemsConnections.end())) {
1606         createPopUpMenu(e->root_x, e->root_y, myTreeItemToACMap[item]);
1607     }
1608     return 1;
1609 }
1610 
1611 
1612 long
onCmdCenterItem(FXObject *,FXSelector,void *)1613 GNEFrame::ACHierarchy::onCmdCenterItem(FXObject*, FXSelector, void*) {
1614     GUIGlObject* glObject = dynamic_cast<GUIGlObject*>(myRightClickedAC);
1615     if (glObject) {
1616         myFrameParent->getViewNet()->centerTo(glObject->getGlID(), false);
1617         myFrameParent->getViewNet()->update();
1618     }
1619     return 1;
1620 }
1621 
1622 
1623 long
onCmdInspectItem(FXObject *,FXSelector,void *)1624 GNEFrame::ACHierarchy::onCmdInspectItem(FXObject*, FXSelector, void*) {
1625     if ((myAC != nullptr) && (myRightClickedAC != nullptr)) {
1626         myFrameParent->getViewNet()->getViewParent()->getInspectorFrame()->inspectChild(myRightClickedAC, myAC);
1627     }
1628     return 1;
1629 }
1630 
1631 
1632 long
onCmdDeleteItem(FXObject *,FXSelector,void *)1633 GNEFrame::ACHierarchy::onCmdDeleteItem(FXObject*, FXSelector, void*) {
1634     // check if Inspector frame was opened before removing
1635     const std::vector<GNEAttributeCarrier*>& currentInspectedACs = myFrameParent->getViewNet()->getViewParent()->getInspectorFrame()->getAttributesEditor()->getEditedACs();
1636     // Remove Attribute Carrier
1637     /*
1638     myFrameParent->getViewNet()->getViewParent()->getDeleteFrame()->removeAttributeCarrier(myRightClickedAC);
1639     myFrameParent->getViewNet()->getViewParent()->getDeleteFrame()->hide();
1640     */
1641     // check if inspector frame has to be shown again
1642     if (currentInspectedACs.size() == 1) {
1643         if (currentInspectedACs.front() != myRightClickedAC) {
1644             myFrameParent->getViewNet()->getViewParent()->getInspectorFrame()->inspectSingleElement(currentInspectedACs.front());
1645         } else {
1646             // inspect a nullprt element to reset inspector frame
1647             myFrameParent->getViewNet()->getViewParent()->getInspectorFrame()->inspectSingleElement(nullptr);
1648         }
1649     }
1650     return 1;
1651 }
1652 
1653 
1654 void
createPopUpMenu(int X,int Y,GNEAttributeCarrier * ac)1655 GNEFrame::ACHierarchy::createPopUpMenu(int X, int Y, GNEAttributeCarrier* ac) {
1656     // create FXMenuPane
1657     FXMenuPane* pane = new FXMenuPane(myTreelist);
1658     // set current clicked AC
1659     myRightClickedAC = ac;
1660     // set name
1661     new MFXMenuHeader(pane, myFrameParent->getViewNet()->getViewParent()->getGUIMainWindow()->getBoldFont(), myRightClickedAC->getPopUpID().c_str(), myRightClickedAC->getIcon());
1662     new FXMenuSeparator(pane);
1663     // Fill FXMenuCommand
1664     new FXMenuCommand(pane, "Center", GUIIconSubSys::getIcon(ICON_RECENTERVIEW), this, MID_GNE_INSPECTORFRAME_CENTER);
1665     new FXMenuCommand(pane, "Inspect", GUIIconSubSys::getIcon(ICON_MODEINSPECT), this, MID_GNE_INSPECTORFRAME_INSPECT);
1666     new FXMenuCommand(pane, "Delete", GUIIconSubSys::getIcon(ICON_MODEDELETE), this, MID_GNE_INSPECTORFRAME_DELETE);
1667     // Center in the mouse position and create pane
1668     pane->setX(X);
1669     pane->setY(Y);
1670     pane->create();
1671     pane->show();
1672 }
1673 
1674 
1675 FXTreeItem*
showAttributeCarrierParents()1676 GNEFrame::ACHierarchy::showAttributeCarrierParents() {
1677     if (myAC->getTagProperty().isNetElement()) {
1678         // check demand element type
1679         switch (myAC->getTagProperty().getTag()) {
1680             case SUMO_TAG_EDGE: {
1681                 // obtain Edge
1682                 GNEEdge* edge = myFrameParent->getViewNet()->getNet()->retrieveEdge(myAC->getID(), false);
1683                 if (edge) {
1684                     // insert Junctions of edge in tree (Pararell because a edge has always two Junctions)
1685                     FXTreeItem* junctionSourceItem = myTreelist->insertItem(nullptr, nullptr, (edge->getGNEJunctionSource()->getHierarchyName() + " origin").c_str(), edge->getGNEJunctionSource()->getIcon(), edge->getGNEJunctionSource()->getIcon());
1686                     FXTreeItem* junctionDestinyItem = myTreelist->insertItem(nullptr, nullptr, (edge->getGNEJunctionSource()->getHierarchyName() + " destiny").c_str(), edge->getGNEJunctionSource()->getIcon(), edge->getGNEJunctionSource()->getIcon());
1687                     junctionDestinyItem->setExpanded(true);
1688                     // Save items in myTreeItemToACMap
1689                     myTreeItemToACMap[junctionSourceItem] = edge->getGNEJunctionSource();
1690                     myTreeItemToACMap[junctionDestinyItem] = edge->getGNEJunctionDestiny();
1691                     // return junction destiny Item
1692                     return junctionDestinyItem;
1693                 } else {
1694                     return nullptr;
1695                 }
1696             }
1697             case SUMO_TAG_LANE: {
1698                 // obtain lane
1699                 GNELane* lane = myFrameParent->getViewNet()->getNet()->retrieveLane(myAC->getID(), false);
1700                 if (lane) {
1701                     // obtain edge parent
1702                     GNEEdge* edge = myFrameParent->getViewNet()->getNet()->retrieveEdge(lane->getParentEdge().getID());
1703                     //inser Junctions of lane of edge in tree (Pararell because a edge has always two Junctions)
1704                     FXTreeItem* junctionSourceItem = myTreelist->insertItem(nullptr, nullptr, (edge->getGNEJunctionSource()->getHierarchyName() + " origin").c_str(), edge->getGNEJunctionSource()->getIcon(), edge->getGNEJunctionSource()->getIcon());
1705                     FXTreeItem* junctionDestinyItem = myTreelist->insertItem(nullptr, nullptr, (edge->getGNEJunctionSource()->getHierarchyName() + " destiny").c_str(), edge->getGNEJunctionSource()->getIcon(), edge->getGNEJunctionSource()->getIcon());
1706                     junctionDestinyItem->setExpanded(true);
1707                     // Create edge item
1708                     FXTreeItem* edgeItem = myTreelist->insertItem(nullptr, junctionDestinyItem, edge->getHierarchyName().c_str(), edge->getIcon(), edge->getIcon());
1709                     edgeItem->setExpanded(true);
1710                     // Save items in myTreeItemToACMap
1711                     myTreeItemToACMap[junctionSourceItem] = edge->getGNEJunctionSource();
1712                     myTreeItemToACMap[junctionDestinyItem] = edge->getGNEJunctionDestiny();
1713                     myTreeItemToACMap[edgeItem] = edge;
1714                     // return edge item
1715                     return edgeItem;
1716                 } else {
1717                     return nullptr;
1718                 }
1719             }
1720             case SUMO_TAG_CROSSING: {
1721                 // obtain Crossing
1722                 GNECrossing* crossing = myFrameParent->getViewNet()->getNet()->retrieveCrossing(myAC->getID(), false);
1723                 if (crossing) {
1724                     // obtain junction
1725                     GNEJunction* junction = crossing->getParentJunction();
1726                     // create junction item
1727                     FXTreeItem* junctionItem = myTreelist->insertItem(nullptr, nullptr, junction->getHierarchyName().c_str(), junction->getIcon(), junction->getIcon());
1728                     junctionItem->setExpanded(true);
1729                     // Save items in myTreeItemToACMap
1730                     myTreeItemToACMap[junctionItem] = junction;
1731                     // return junction Item
1732                     return junctionItem;
1733                 } else {
1734                     return nullptr;
1735                 }
1736             }
1737             case SUMO_TAG_CONNECTION: {
1738                 // obtain Connection
1739                 GNEConnection* connection = myFrameParent->getViewNet()->getNet()->retrieveConnection(myAC->getID(), false);
1740                 if (connection) {
1741                     // create edge from item
1742                     FXTreeItem* edgeFromItem = myTreelist->insertItem(nullptr, nullptr, connection->getEdgeFrom()->getHierarchyName().c_str(), connection->getEdgeFrom()->getIcon(), connection->getEdgeFrom()->getIcon());
1743                     edgeFromItem->setExpanded(true);
1744                     // create edge to item
1745                     FXTreeItem* edgeToItem = myTreelist->insertItem(nullptr, nullptr, connection->getEdgeTo()->getHierarchyName().c_str(), connection->getEdgeTo()->getIcon(), connection->getEdgeTo()->getIcon());
1746                     edgeToItem->setExpanded(true);
1747                     // create connection item
1748                     FXTreeItem* connectionItem = myTreelist->insertItem(nullptr, edgeToItem, connection->getHierarchyName().c_str(), connection->getIcon(), connection->getIcon());
1749                     connectionItem->setExpanded(true);
1750                     // Save items in myTreeItemToACMap
1751                     myTreeItemToACMap[edgeFromItem] = connection->getEdgeFrom();
1752                     myTreeItemToACMap[edgeToItem] = connection->getEdgeTo();
1753                     myTreeItemToACMap[connectionItem] = connection;
1754                     // return connection item
1755                     return connectionItem;
1756                 } else {
1757                     return nullptr;
1758                 }
1759             }
1760             default:
1761                 break;
1762         }
1763     } else if (myAC->getTagProperty().getTag() == SUMO_TAG_POILANE) {
1764         // Obtain POILane
1765         GNEPOI* POILane = myFrameParent->getViewNet()->getNet()->retrievePOI(myAC->getID(), false);
1766         if (POILane) {
1767             // obtain lane parent
1768             GNELane* lane = myFrameParent->getViewNet()->getNet()->retrieveLane(POILane->getLaneParents().at(0)->getID());
1769             // obtain edge parent
1770             GNEEdge* edge = myFrameParent->getViewNet()->getNet()->retrieveEdge(lane->getParentEdge().getID());
1771             //inser Junctions of lane of edge in tree (Pararell because a edge has always two Junctions)
1772             FXTreeItem* junctionSourceItem = myTreelist->insertItem(nullptr, nullptr, (edge->getGNEJunctionSource()->getHierarchyName() + " origin").c_str(), edge->getGNEJunctionSource()->getIcon(), edge->getGNEJunctionSource()->getIcon());
1773             FXTreeItem* junctionDestinyItem = myTreelist->insertItem(nullptr, nullptr, (edge->getGNEJunctionSource()->getHierarchyName() + " destiny").c_str(), edge->getGNEJunctionSource()->getIcon(), edge->getGNEJunctionSource()->getIcon());
1774             junctionDestinyItem->setExpanded(true);
1775             // Create edge item
1776             FXTreeItem* edgeItem = myTreelist->insertItem(nullptr, junctionDestinyItem, edge->getHierarchyName().c_str(), edge->getIcon(), edge->getIcon());
1777             edgeItem->setExpanded(true);
1778             // Create lane item
1779             FXTreeItem* laneItem = myTreelist->insertItem(nullptr, edgeItem, lane->getHierarchyName().c_str(), lane->getIcon(), lane->getIcon());
1780             laneItem->setExpanded(true);
1781             // Save items in myTreeItemToACMap
1782             myTreeItemToACMap[junctionSourceItem] = edge->getGNEJunctionSource();
1783             myTreeItemToACMap[junctionDestinyItem] = edge->getGNEJunctionDestiny();
1784             myTreeItemToACMap[edgeItem] = edge;
1785             myTreeItemToACMap[laneItem] = lane;
1786             // return Lane item
1787             return laneItem;
1788         } else {
1789             return nullptr;
1790         }
1791     } else if (myAC->getTagProperty().isAdditional() || myAC->getTagProperty().isTAZ()) {
1792         // Obtain Additional
1793         GNEAdditional* additional = myFrameParent->getViewNet()->getNet()->retrieveAdditional(myAC->getTagProperty().getTag(), myAC->getID(), false);
1794         if (additional) {
1795             // declare auxiliar FXTreeItem, due a demand element can have multiple "roots"
1796             FXTreeItem* root = nullptr;
1797             // check if there is demand elements parents
1798             if (additional->getAdditionalParents().size() > 0) {
1799                 // check if we have more than one edge
1800                 if (additional->getAdditionalParents().size() > 1) {
1801                     // insert first item
1802                     addListItem(additional->getAdditionalParents().front());
1803                     // insert "spacer"
1804                     if (additional->getAdditionalParents().size() > 2) {
1805                         addListItem(nullptr, ("..." + toString((int)additional->getAdditionalParents().size() - 2) + " additionals...").c_str(), 0, false);
1806                     }
1807                 }
1808                 // return last inserted item
1809                 root = addListItem(additional->getAdditionalParents().back());
1810             }
1811             // check if there is demand element parents
1812             if (additional->getDemandElementParents().size() > 0) {
1813                 // check if we have more than one demand element
1814                 if (additional->getDemandElementParents().size() > 1) {
1815                     // insert first item
1816                     addListItem(additional->getDemandElementParents().front());
1817                     // insert "spacer"
1818                     if (additional->getDemandElementParents().size() > 2) {
1819                         addListItem(nullptr, ("..." + toString((int)additional->getDemandElementParents().size() - 2) + " demand elements...").c_str(), 0, false);
1820                     }
1821                 }
1822                 // return last inserted item
1823                 root = addListItem(additional->getDemandElementParents().back());
1824             }
1825             // check if there is edge parents
1826             if (additional->getEdgeParents().size() > 0) {
1827                 // check if we have more than one edge
1828                 if (additional->getEdgeParents().size() > 1) {
1829                     // insert first item
1830                     addListItem(additional->getEdgeParents().front());
1831                     // insert "spacer"
1832                     if (additional->getEdgeParents().size() > 2) {
1833                         addListItem(nullptr, ("..." + toString((int)additional->getEdgeParents().size() - 2) + " edges...").c_str(), 0, false);
1834                     }
1835                 }
1836                 // return last inserted item
1837                 root = addListItem(additional->getEdgeParents().back());
1838             }
1839             // check if there is lane parents
1840             if (additional->getLaneParents().size() > 0) {
1841                 // check if we have more than one lane parent
1842                 if (additional->getLaneParents().size() > 1) {
1843                     // insert first item
1844                     addListItem(additional->getLaneParents().front());
1845                     // insert "spacer"
1846                     if (additional->getLaneParents().size() > 2) {
1847                         addListItem(nullptr, ("..." + toString((int)additional->getLaneParents().size() - 2) + " lanes...").c_str(), 0, false);
1848                     }
1849                 }
1850                 // return last inserted item
1851                 root = addListItem(additional->getLaneParents().back());
1852             }
1853             // return last inserted list item
1854             return root;
1855         }
1856     } else if (myAC->getTagProperty().isDemandElement()) {
1857         // Obtain DemandElement
1858         GNEDemandElement* demandElement = myFrameParent->getViewNet()->getNet()->retrieveDemandElement(myAC->getTagProperty().getTag(), myAC->getID(), false);
1859         if (demandElement) {
1860             // declare auxiliar FXTreeItem, due a demand element can have multiple "roots"
1861             FXTreeItem* root = nullptr;
1862             // check if there is demand elements parents
1863             if (demandElement->getAdditionalParents().size() > 0) {
1864                 // check if we have more than one edge
1865                 if (demandElement->getAdditionalParents().size() > 1) {
1866                     // insert first item
1867                     addListItem(demandElement->getAdditionalParents().front());
1868                     // insert "spacer"
1869                     if (demandElement->getAdditionalParents().size() > 2) {
1870                         addListItem(nullptr, ("..." + toString((int)demandElement->getAdditionalParents().size() - 2) + " additionals...").c_str(), 0, false);
1871                     }
1872                 }
1873                 // return last inserted item
1874                 root = addListItem(demandElement->getAdditionalParents().back());
1875             }
1876             // check if there is demand element parents
1877             if (demandElement->getDemandElementParents().size() > 0) {
1878                 // check if we have more than one demand element
1879                 if (demandElement->getDemandElementParents().size() > 1) {
1880                     // insert first item
1881                     addListItem(demandElement->getDemandElementParents().front());
1882                     // insert "spacer"
1883                     if (demandElement->getDemandElementParents().size() > 2) {
1884                         addListItem(nullptr, ("..." + toString((int)demandElement->getDemandElementParents().size() - 2) + " demand elements...").c_str(), 0, false);
1885                     }
1886                 }
1887                 // return last inserted item
1888                 root = addListItem(demandElement->getDemandElementParents().back());
1889             }
1890             // check if there is edge parents
1891             if (demandElement->getEdgeParents().size() > 0) {
1892                 // check if we have more than one edge
1893                 if (demandElement->getEdgeParents().size() > 1) {
1894                     // insert first item
1895                     addListItem(demandElement->getEdgeParents().front());
1896                     // insert "spacer"
1897                     if (demandElement->getEdgeParents().size() > 2) {
1898                         addListItem(nullptr, ("..." + toString((int)demandElement->getEdgeParents().size() - 2) + " edges...").c_str(), 0, false);
1899                     }
1900                 }
1901                 // return last inserted item
1902                 root = addListItem(demandElement->getEdgeParents().back());
1903             }
1904             // check if there is lane parents
1905             if (demandElement->getLaneParents().size() > 0) {
1906                 // check if we have more than one lane parent
1907                 if (demandElement->getLaneParents().size() > 1) {
1908                     // insert first item
1909                     addListItem(demandElement->getLaneParents().front());
1910                     // insert "spacer"
1911                     if (demandElement->getLaneParents().size() > 2) {
1912                         addListItem(nullptr, ("..." + toString((int)demandElement->getLaneParents().size() - 2) + " lanes...").c_str(), 0, false);
1913                     }
1914                 }
1915                 // return last inserted item
1916                 root = addListItem(demandElement->getLaneParents().back());
1917             }
1918             // return last inserted list item
1919             return root;
1920         }
1921     }
1922     // there isn't parents
1923     return nullptr;
1924 }
1925 
1926 
1927 void
showAttributeCarrierChilds(GNEAttributeCarrier * AC,FXTreeItem * itemParent)1928 GNEFrame::ACHierarchy::showAttributeCarrierChilds(GNEAttributeCarrier* AC, FXTreeItem* itemParent) {
1929     if (AC->getTagProperty().isNetElement()) {
1930         // Switch gl type of ac
1931         switch (AC->getTagProperty().getTag()) {
1932             case SUMO_TAG_JUNCTION: {
1933                 // retrieve junction
1934                 GNEJunction* junction = myFrameParent->getViewNet()->getNet()->retrieveJunction(AC->getID(), false);
1935                 if (junction) {
1936                     // insert junction item
1937                     FXTreeItem* junctionItem = addListItem(AC, itemParent);
1938                     // insert edges
1939                     for (auto i : junction->getGNEEdges()) {
1940                         showAttributeCarrierChilds(i, junctionItem);
1941                     }
1942                     // insert crossings
1943                     for (auto i : junction->getGNECrossings()) {
1944                         showAttributeCarrierChilds(i, junctionItem);
1945                     }
1946                 }
1947                 break;
1948             }
1949             case SUMO_TAG_EDGE: {
1950                 // retrieve edge
1951                 GNEEdge* edge = myFrameParent->getViewNet()->getNet()->retrieveEdge(AC->getID(), false);
1952                 if (edge) {
1953                     // insert edge item
1954                     FXTreeItem* edgeItem = addListItem(AC, itemParent);
1955                     // insert lanes
1956                     for (const auto& i : edge->getLanes()) {
1957                         showAttributeCarrierChilds(i, edgeItem);
1958                     }
1959                     // insert shape childs
1960                     for (const auto& i : edge->getShapeChilds()) {
1961                         showAttributeCarrierChilds(i, edgeItem);
1962                     }
1963                     // insert additional childs
1964                     for (const auto& i : edge->getAdditionalChilds()) {
1965                         showAttributeCarrierChilds(i, edgeItem);
1966                     }
1967                     // insert demand elements childs
1968                     for (const auto& i : edge->getDemandElementChilds()) {
1969                         showAttributeCarrierChilds(i, edgeItem);
1970                     }
1971                 }
1972                 break;
1973             }
1974             case SUMO_TAG_LANE: {
1975                 // retrieve lane
1976                 GNELane* lane = myFrameParent->getViewNet()->getNet()->retrieveLane(AC->getID(), false);
1977                 if (lane) {
1978                     // insert lane item
1979                     FXTreeItem* laneItem = addListItem(AC, itemParent);
1980                     // insert shape childs
1981                     for (const auto& i : lane->getShapeChilds()) {
1982                         showAttributeCarrierChilds(i, laneItem);
1983                     }
1984                     // insert additional childs
1985                     for (const auto& i : lane->getAdditionalChilds()) {
1986                         showAttributeCarrierChilds(i, laneItem);
1987                     }
1988                     // insert demand elements childs
1989                     for (const auto& i : lane->getDemandElementChilds()) {
1990                         showAttributeCarrierChilds(i, laneItem);
1991                     }
1992                     // insert incoming connections of lanes (by default isn't expanded)
1993                     if (lane->getGNEIncomingConnections().size() > 0) {
1994                         std::vector<GNEConnection*> incomingLaneConnections = lane->getGNEIncomingConnections();
1995                         // insert intermediate list item
1996                         FXTreeItem* incomingConnections = addListItem(laneItem, "Incomings", incomingLaneConnections.front()->getIcon(), false);
1997                         // insert incoming connections
1998                         for (auto i : incomingLaneConnections) {
1999                             showAttributeCarrierChilds(i, incomingConnections);
2000                         }
2001                     }
2002                     // insert outcoming connections of lanes (by default isn't expanded)
2003                     if (lane->getGNEOutcomingConnections().size() > 0) {
2004                         std::vector<GNEConnection*> outcomingLaneConnections = lane->getGNEOutcomingConnections();
2005                         // insert intermediate list item
2006                         FXTreeItem* outgoingConnections = addListItem(laneItem, "Outgoing", outcomingLaneConnections.front()->getIcon(), false);
2007                         // insert outcoming connections
2008                         for (auto i : outcomingLaneConnections) {
2009                             showAttributeCarrierChilds(i, outgoingConnections);
2010                         }
2011                     }
2012                 }
2013                 break;
2014             }
2015             case SUMO_TAG_CROSSING:
2016             case SUMO_TAG_CONNECTION: {
2017                 // insert connection item
2018                 addListItem(AC, itemParent);
2019                 break;
2020             }
2021             default:
2022                 break;
2023         }
2024     } else if (AC->getTagProperty().isShape()) {
2025         // insert shape item
2026         addListItem(AC, itemParent);
2027     } else if (AC->getTagProperty().isAdditional() || AC->getTagProperty().isTAZ()) {
2028         // retrieve additional
2029         GNEAdditional* additional = myFrameParent->getViewNet()->getNet()->retrieveAdditional(AC->getTagProperty().getTag(), AC->getID(), false);
2030         if (additional) {
2031             // insert additional item
2032             FXTreeItem* additionalItem = addListItem(AC, itemParent);
2033             // insert edge childs
2034             for (const auto& i : additional->getEdgeChilds()) {
2035                 showAttributeCarrierChilds(i, additionalItem);
2036             }
2037             // insert lane childs
2038             for (const auto& i : additional->getLaneChilds()) {
2039                 showAttributeCarrierChilds(i, additionalItem);
2040             }
2041             // insert shape childs
2042             for (const auto& i : additional->getShapeChilds()) {
2043                 showAttributeCarrierChilds(i, additionalItem);
2044             }
2045             // insert additionals childs
2046             for (const auto& i : additional->getAdditionalChilds()) {
2047                 showAttributeCarrierChilds(i, additionalItem);
2048             }
2049             // insert demand element childs
2050             for (const auto& i : additional->getDemandElementChilds()) {
2051                 showAttributeCarrierChilds(i, additionalItem);
2052             }
2053         }
2054     } else if (AC->getTagProperty().isDemandElement()) {
2055         // retrieve demandElement
2056         GNEDemandElement* demandElement = myFrameParent->getViewNet()->getNet()->retrieveDemandElement(AC->getTagProperty().getTag(), AC->getID(), false);
2057         if (demandElement) {
2058             // insert demandElement item
2059             FXTreeItem* demandElementItem = addListItem(AC, itemParent);
2060             // insert edge childs
2061             for (const auto& i : demandElement->getEdgeChilds()) {
2062                 showAttributeCarrierChilds(i, demandElementItem);
2063             }
2064             // insert lane childs
2065             for (const auto& i : demandElement->getLaneChilds()) {
2066                 showAttributeCarrierChilds(i, demandElementItem);
2067             }
2068             // insert shape childs
2069             for (const auto& i : demandElement->getShapeChilds()) {
2070                 showAttributeCarrierChilds(i, demandElementItem);
2071             }
2072             // insert additionals childs
2073             for (const auto& i : demandElement->getAdditionalChilds()) {
2074                 showAttributeCarrierChilds(i, demandElementItem);
2075             }
2076             // insert demand element childs
2077             for (const auto& i : demandElement->getDemandElementChilds()) {
2078                 showAttributeCarrierChilds(i, demandElementItem);
2079             }
2080         }
2081     }
2082 }
2083 
2084 
2085 FXTreeItem*
addListItem(GNEAttributeCarrier * AC,FXTreeItem * itemParent,std::string prefix,std::string sufix)2086 GNEFrame::ACHierarchy::addListItem(GNEAttributeCarrier* AC, FXTreeItem* itemParent, std::string prefix, std::string sufix) {
2087     // insert item in Tree list
2088     FXTreeItem* item = myTreelist->insertItem(nullptr, itemParent, (prefix + AC->getHierarchyName() + sufix).c_str(), AC->getIcon(), AC->getIcon());
2089     // insert item in map
2090     myTreeItemToACMap[item] = AC;
2091     // by default item is expanded
2092     item->setExpanded(true);
2093     return item;
2094 }
2095 
2096 
2097 FXTreeItem*
addListItem(FXTreeItem * itemParent,const std::string & text,FXIcon * icon,bool expanded)2098 GNEFrame::ACHierarchy::addListItem(FXTreeItem* itemParent, const std::string& text, FXIcon* icon, bool expanded) {
2099     // insert item in Tree list
2100     FXTreeItem* item = myTreelist->insertItem(nullptr, itemParent, text.c_str(), icon, icon);
2101     // set exapnded
2102     item->setExpanded(expanded);
2103     return item;
2104 }
2105 
2106 // ---------------------------------------------------------------------------
2107 // GNEFrame::GenericParametersEditor - methods
2108 // ---------------------------------------------------------------------------
2109 
GenericParametersEditor(GNEFrame * inspectorFrameParent)2110 GNEFrame::GenericParametersEditor::GenericParametersEditor(GNEFrame* inspectorFrameParent) :
2111     FXGroupBox(inspectorFrameParent->myContentFrame, "Generic parameters", GUIDesignGroupBoxFrame),
2112     myFrameParent(inspectorFrameParent),
2113     myAC(nullptr),
2114     myGenericParameters(nullptr) {
2115     // create empty vector with generic parameters
2116     myGenericParameters = new std::vector<std::pair<std::string, std::string> >;
2117     // create textfield and buttons
2118     myTextFieldGenericParameter = new FXTextField(this, GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextField);
2119     myEditGenericParameterButton = new FXButton(this, "Edit generic parameter", nullptr, this, MID_GNE_SET_ATTRIBUTE_DIALOG, GUIDesignButton);
2120 }
2121 
2122 
~GenericParametersEditor()2123 GNEFrame::GenericParametersEditor::~GenericParametersEditor() {
2124     delete myGenericParameters;
2125 }
2126 
2127 
2128 void
showGenericParametersEditor(GNEAttributeCarrier * AC)2129 GNEFrame::GenericParametersEditor::showGenericParametersEditor(GNEAttributeCarrier* AC) {
2130     if (AC != nullptr) {
2131         myAC = AC;
2132         myACs.clear();
2133         // obtain a copy of generic parameters of AC
2134         if (myAC) {
2135             *myGenericParameters = myAC->getGenericParameters();
2136         }
2137         // refresh GenericParametersEditor
2138         refreshGenericParametersEditor();
2139         // show groupbox
2140         show();
2141     }
2142 }
2143 
2144 
2145 void
showGenericParametersEditor(std::vector<GNEAttributeCarrier * > ACs)2146 GNEFrame::GenericParametersEditor::showGenericParametersEditor(std::vector<GNEAttributeCarrier*> ACs) {
2147     if (ACs.size() > 0) {
2148         myAC = nullptr;
2149         myACs = ACs;
2150         // check if generic parameters are different
2151         bool differentsGenericParameters = false;
2152         std::string genericParameter = myACs.front()->getAttribute(GNE_ATTR_GENERIC);
2153         for (auto i : myACs) {
2154             if (genericParameter != i->getAttribute(GNE_ATTR_GENERIC)) {
2155                 differentsGenericParameters = true;
2156             }
2157         }
2158         // set generic Parameters editor
2159         if (differentsGenericParameters) {
2160             myGenericParameters->clear();
2161         } else {
2162             *myGenericParameters = myACs.front()->getGenericParameters();
2163         }
2164         // refresh GenericParametersEditor
2165         refreshGenericParametersEditor();
2166         // show groupbox
2167         show();
2168     }
2169 }
2170 
2171 
2172 void
hideGenericParametersEditor()2173 GNEFrame::GenericParametersEditor::hideGenericParametersEditor() {
2174     myAC = nullptr;
2175     // hide groupbox
2176     hide();
2177 }
2178 
2179 
2180 void
refreshGenericParametersEditor()2181 GNEFrame::GenericParametersEditor::refreshGenericParametersEditor() {
2182     // update text field depending of AC
2183     if (myAC) {
2184         myTextFieldGenericParameter->setText(myAC->getAttribute(GNE_ATTR_GENERIC).c_str());
2185         myTextFieldGenericParameter->setTextColor(FXRGB(0, 0, 0));
2186         // disable myTextFieldGenericParameter if Tag correspond to an network element but we're in demand mode (or vice versa), disable all elements
2187         if (((myFrameParent->getViewNet()->getEditModes().currentSupermode == GNE_SUPERMODE_NETWORK) && myAC->getTagProperty().isDemandElement()) ||
2188                 ((myFrameParent->getViewNet()->getEditModes().currentSupermode == GNE_SUPERMODE_DEMAND) && !myAC->getTagProperty().isDemandElement())) {
2189             myTextFieldGenericParameter->disable();
2190             myEditGenericParameterButton->disable();
2191         } else {
2192             myTextFieldGenericParameter->enable();
2193             myEditGenericParameterButton->enable();
2194         }
2195     } else if (myACs.size() > 0) {
2196         // check if generic parameters of all inspected ACs are different
2197         std::string genericParameter = myACs.front()->getAttribute(GNE_ATTR_GENERIC);
2198 
2199         for (auto i : myACs) {
2200             if (genericParameter != i->getAttribute(GNE_ATTR_GENERIC)) {
2201                 genericParameter = "different generic attributes";
2202             }
2203         }
2204         myTextFieldGenericParameter->setText(genericParameter.c_str());
2205         myTextFieldGenericParameter->setTextColor(FXRGB(0, 0, 0));
2206         // disable myTextFieldGenericParameter if we're in demand mode and inspected AC isn't a demand element (or viceversa)
2207         if (((myFrameParent->getViewNet()->getEditModes().currentSupermode == GNE_SUPERMODE_NETWORK) && myACs.front()->getTagProperty().isDemandElement()) ||
2208                 ((myFrameParent->getViewNet()->getEditModes().currentSupermode == GNE_SUPERMODE_DEMAND) && !myACs.front()->getTagProperty().isDemandElement())) {
2209             myTextFieldGenericParameter->disable();
2210             myEditGenericParameterButton->disable();
2211         } else {
2212             myTextFieldGenericParameter->enable();
2213             myEditGenericParameterButton->enable();
2214         }
2215     }
2216 }
2217 
2218 
2219 std::string
getGenericParametersStr() const2220 GNEFrame::GenericParametersEditor::getGenericParametersStr() const {
2221     std::string result;
2222     // Generate an string using the following structure: "key1=value1|key2=value2|...
2223     for (auto i = myGenericParameters->begin(); i != myGenericParameters->end(); i++) {
2224         result += i->first + "=" + i->second + "|";
2225     }
2226     // remove the last "|"
2227     if (!result.empty()) {
2228         result.pop_back();
2229     }
2230     return result;
2231 }
2232 
2233 
2234 long
onCmdEditGenericParameter(FXObject *,FXSelector,void *)2235 GNEFrame::GenericParametersEditor::onCmdEditGenericParameter(FXObject*, FXSelector, void*) {
2236     // edit generic parameters using dialog
2237     if (GNEGenericParameterDialog(myFrameParent->getViewNet(), myGenericParameters).execute()) {
2238         // set values edited in Parameter dialog in Edited AC
2239         if (myAC) {
2240             myAC->setAttribute(GNE_ATTR_GENERIC, getGenericParametersStr(), myFrameParent->getViewNet()->getUndoList());
2241         } else if (myACs.size() > 0) {
2242             myFrameParent->getViewNet()->getUndoList()->p_begin("Change multiple generic attributes");
2243             for (auto i : myACs) {
2244                 i->setAttribute(GNE_ATTR_GENERIC, getGenericParametersStr(), myFrameParent->getViewNet()->getUndoList());
2245             }
2246             myFrameParent->getViewNet()->getUndoList()->p_end();
2247             // update frame parent after attribute sucesfully set
2248             myFrameParent->updateFrameAfterChangeAttribute();
2249         }
2250         // Refresh parameter editor
2251         refreshGenericParametersEditor();
2252     }
2253     return 1;
2254 }
2255 
2256 
2257 long
onCmdSetGenericParameter(FXObject *,FXSelector,void *)2258 GNEFrame::GenericParametersEditor::onCmdSetGenericParameter(FXObject*, FXSelector, void*) {
2259     // separate value in a vector of string using | as separator
2260     std::vector<std::string> parsedValues;
2261     StringTokenizer st(myTextFieldGenericParameter->getText().text(), "|", true);
2262     while (st.hasNext()) {
2263         parsedValues.push_back(st.next());
2264     }
2265     // first check if parsed generic parameters are valid
2266     for (auto i : parsedValues) {
2267         if (!GNEAttributeCarrier::isGenericParametersValid(i)) {
2268             WRITE_WARNING("Invalid format of Generic Parameter (" + i + ")");
2269             myTextFieldGenericParameter->setTextColor(FXRGB(255, 0, 0));
2270             return 1;
2271         }
2272     }
2273     // now check if there is duplicated parameters
2274     std::sort(parsedValues.begin(), parsedValues.end());
2275     for (auto i = parsedValues.begin(); i != parsedValues.end(); i++) {
2276         if (((i + 1) != parsedValues.end())) {
2277             std::vector<std::string> firstKey, secondKey;
2278             StringTokenizer stKey1(*i, "=", true);
2279             StringTokenizer stKey2(*(i + 1), "=", true);
2280             //parse both keys
2281             while (stKey1.hasNext()) {
2282                 firstKey.push_back(stKey1.next());
2283             }
2284             while (stKey2.hasNext()) {
2285                 secondKey.push_back(stKey2.next());
2286             }
2287             // compare both keys and stop if are equal
2288             if ((firstKey.size() != 2) || (secondKey.size() != 2) || (firstKey.front() == secondKey.front())) {
2289                 WRITE_WARNING("Generic Parameters wit the same key aren't allowed (" + (*i) + "," + * (i + 1) + ")");
2290                 myTextFieldGenericParameter->setTextColor(FXRGB(255, 0, 0));
2291                 return 1;
2292             }
2293         }
2294     }
2295     // parsed generic parameters ok, then set text field black and continue
2296     myTextFieldGenericParameter->setTextColor(FXRGB(0, 0, 0));
2297     myTextFieldGenericParameter->killFocus();
2298     // clear current existent generic parameters and set parsed generic parameters
2299     myGenericParameters->clear();
2300     for (auto i : parsedValues) {
2301         std::vector<std::string> parsedParameters;
2302         StringTokenizer stParam(i, "=", true);
2303         while (stParam.hasNext()) {
2304             parsedParameters.push_back(stParam.next());
2305         }
2306         // Check that parsed parameters are exactly two and contains valid chracters
2307         if (parsedParameters.size() == 2 && SUMOXMLDefinitions::isValidGenericParameterKey(parsedParameters.front()) && SUMOXMLDefinitions::isValidGenericParameterValue(parsedParameters.back())) {
2308             myGenericParameters->push_back(std::make_pair(parsedParameters.front(), parsedParameters.back()));
2309         }
2310     }
2311     // if we're editing generic attributes of an AttributeCarrier, set it
2312     if (myAC) {
2313         myAC->setAttribute(GNE_ATTR_GENERIC, getGenericParametersStr(), myFrameParent->getViewNet()->getUndoList());
2314     } else if (myACs.size() > 0) {
2315         myFrameParent->getViewNet()->getUndoList()->p_begin("Change multiple generic attributes");
2316         for (auto i : myACs) {
2317             i->setAttribute(GNE_ATTR_GENERIC, getGenericParametersStr(), myFrameParent->getViewNet()->getUndoList());
2318         }
2319         myFrameParent->getViewNet()->getUndoList()->p_end();
2320         // update frame parent after attribute sucesfully set
2321         myFrameParent->updateFrameAfterChangeAttribute();
2322     }
2323     return 1;
2324 }
2325 
2326 // ---------------------------------------------------------------------------
2327 // GNEFrame::DrawingShape - methods
2328 // ---------------------------------------------------------------------------
2329 
DrawingShape(GNEFrame * frameParent)2330 GNEFrame::DrawingShape::DrawingShape(GNEFrame* frameParent) :
2331     FXGroupBox(frameParent->myContentFrame, "Drawing", GUIDesignGroupBoxFrame),
2332     myFrameParent(frameParent),
2333     myDeleteLastCreatedPoint(false) {
2334     // create start and stop buttons
2335     myStartDrawingButton = new FXButton(this, "Start drawing", 0, this, MID_GNE_STARTDRAWING, GUIDesignButton);
2336     myStopDrawingButton = new FXButton(this, "Stop drawing", 0, this, MID_GNE_STOPDRAWING, GUIDesignButton);
2337     myAbortDrawingButton = new FXButton(this, "Abort drawing", 0, this, MID_GNE_ABORTDRAWING, GUIDesignButton);
2338 
2339     // create information label
2340     std::ostringstream information;
2341     information
2342             << "- 'Start drawing' or ENTER\n"
2343             << "  draws shape boundary.\n"
2344             << "- 'Stop drawing' or ENTER\n"
2345             << "  creates shape.\n"
2346             << "- 'Shift + Click'\n"
2347             << "  removes last created point.\n"
2348             << "- 'Abort drawing' or ESC\n"
2349             << "  removes drawed shape.";
2350     myInformationLabel = new FXLabel(this, information.str().c_str(), 0, GUIDesignLabelFrameInformation);
2351     // disable stop and abort functions as init
2352     myStopDrawingButton->disable();
2353     myAbortDrawingButton->disable();
2354 }
2355 
2356 
~DrawingShape()2357 GNEFrame::DrawingShape::~DrawingShape() {}
2358 
2359 
showDrawingShape()2360 void GNEFrame::DrawingShape::showDrawingShape() {
2361     // abort current drawing before show
2362     abortDrawing();
2363     // show FXGroupBox
2364     FXGroupBox::show();
2365 }
2366 
2367 
hideDrawingShape()2368 void GNEFrame::DrawingShape::hideDrawingShape() {
2369     // abort current drawing before hide
2370     abortDrawing();
2371     // show FXGroupBox
2372     FXGroupBox::hide();
2373 }
2374 
2375 
2376 void
startDrawing()2377 GNEFrame::DrawingShape::startDrawing() {
2378     // Only start drawing if DrawingShape modul is shown
2379     if (shown()) {
2380         // change buttons
2381         myStartDrawingButton->disable();
2382         myStopDrawingButton->enable();
2383         myAbortDrawingButton->enable();
2384     }
2385 }
2386 
2387 
2388 void
stopDrawing()2389 GNEFrame::DrawingShape::stopDrawing() {
2390     // try to build shape
2391     if (myFrameParent->buildShape()) {
2392         // clear created points
2393         myTemporalShapeShape.clear();
2394         myFrameParent->getViewNet()->update();
2395         // change buttons
2396         myStartDrawingButton->enable();
2397         myStopDrawingButton->disable();
2398         myAbortDrawingButton->disable();
2399     } else {
2400         // abort drawing if shape cannot be created
2401         abortDrawing();
2402     }
2403 }
2404 
2405 
2406 void
abortDrawing()2407 GNEFrame::DrawingShape::abortDrawing() {
2408     // clear created points
2409     myTemporalShapeShape.clear();
2410     myFrameParent->getViewNet()->update();
2411     // change buttons
2412     myStartDrawingButton->enable();
2413     myStopDrawingButton->disable();
2414     myAbortDrawingButton->disable();
2415 }
2416 
2417 
2418 void
addNewPoint(const Position & P)2419 GNEFrame::DrawingShape::addNewPoint(const Position& P) {
2420     if (myStopDrawingButton->isEnabled()) {
2421         myTemporalShapeShape.push_back(P);
2422     } else {
2423         throw ProcessError("A new point cannot be added if drawing wasn't started");
2424     }
2425 }
2426 
2427 
2428 void
removeLastPoint()2429 GNEFrame::DrawingShape::removeLastPoint() {
2430 
2431 }
2432 
2433 
2434 const PositionVector&
getTemporalShape() const2435 GNEFrame::DrawingShape::getTemporalShape() const {
2436     return myTemporalShapeShape;
2437 }
2438 
2439 
2440 bool
isDrawing() const2441 GNEFrame::DrawingShape::isDrawing() const {
2442     return myStopDrawingButton->isEnabled();
2443 }
2444 
2445 
2446 void
setDeleteLastCreatedPoint(bool value)2447 GNEFrame::DrawingShape::setDeleteLastCreatedPoint(bool value) {
2448     myDeleteLastCreatedPoint = value;
2449 }
2450 
2451 
2452 bool
getDeleteLastCreatedPoint()2453 GNEFrame::DrawingShape::getDeleteLastCreatedPoint() {
2454     return myDeleteLastCreatedPoint;
2455 }
2456 
2457 
2458 long
onCmdStartDrawing(FXObject *,FXSelector,void *)2459 GNEFrame::DrawingShape::onCmdStartDrawing(FXObject*, FXSelector, void*) {
2460     startDrawing();
2461     return 0;
2462 }
2463 
2464 
2465 long
onCmdStopDrawing(FXObject *,FXSelector,void *)2466 GNEFrame::DrawingShape::onCmdStopDrawing(FXObject*, FXSelector, void*) {
2467     stopDrawing();
2468     return 0;
2469 }
2470 
2471 
2472 long
onCmdAbortDrawing(FXObject *,FXSelector,void *)2473 GNEFrame::DrawingShape::onCmdAbortDrawing(FXObject*, FXSelector, void*) {
2474     abortDrawing();
2475     return 0;
2476 }
2477 
2478 // ---------------------------------------------------------------------------
2479 // GNEFrame::NeteditAttributes- methods
2480 // ---------------------------------------------------------------------------
2481 
NeteditAttributes(GNEFrame * frameParent)2482 GNEFrame::NeteditAttributes::NeteditAttributes(GNEFrame* frameParent) :
2483     FXGroupBox(frameParent->myContentFrame, "Netedit attributes", GUIDesignGroupBoxFrame),
2484     myFrameParent(frameParent),
2485     myCurrentLengthValid(true),
2486     myActualAdditionalReferencePoint(GNE_ADDITIONALREFERENCEPOINT_LEFT) {
2487     // Create FXListBox for the reference points and fill it
2488     myReferencePointMatchBox = new FXComboBox(this, GUIDesignComboBoxNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignComboBox);
2489     myReferencePointMatchBox->appendItem("reference left");
2490     myReferencePointMatchBox->appendItem("reference right");
2491     myReferencePointMatchBox->appendItem("reference center");
2492     // Create Frame for Length Label and textField
2493     FXHorizontalFrame* lengthFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
2494     myLengthLabel = new FXLabel(lengthFrame, toString(SUMO_ATTR_LENGTH).c_str(), 0, GUIDesignLabelAttribute);
2495     myLengthTextField = new FXTextField(lengthFrame, GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextField);
2496     myLengthTextField->setText("10");
2497     // Create Frame for block movement label and checkBox (By default disabled)
2498     FXHorizontalFrame* blockMovement = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
2499     myBlockMovementLabel = new FXLabel(blockMovement, "block move", 0, GUIDesignLabelAttribute);
2500     myBlockMovementCheckButton = new FXCheckButton(blockMovement, "false", this, MID_GNE_SET_ATTRIBUTE, GUIDesignCheckButtonAttribute);
2501     myBlockMovementCheckButton->setCheck(false);
2502     // Create Frame for block shape label and checkBox (By default disabled)
2503     FXHorizontalFrame* blockShapeFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
2504     myBlockShapeLabel = new FXLabel(blockShapeFrame, "block shape", 0, GUIDesignLabelAttribute);
2505     myBlockShapeCheckButton = new FXCheckButton(blockShapeFrame, "false", this, MID_GNE_SET_ATTRIBUTE, GUIDesignCheckButtonAttribute);
2506     // Create Frame for block close polygon and checkBox (By default disabled)
2507     FXHorizontalFrame* closePolygonFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
2508     myClosePolygonLabel = new FXLabel(closePolygonFrame, "Close shape", 0, GUIDesignLabelAttribute);
2509     myCloseShapeCheckButton = new FXCheckButton(closePolygonFrame, "false", this, MID_GNE_SET_ATTRIBUTE, GUIDesignCheckButtonAttribute);
2510     myBlockShapeCheckButton->setCheck(false);
2511     // Create help button
2512     helpReferencePoint = new FXButton(this, "Help", 0, this, MID_HELP, GUIDesignButtonRectangular);
2513     // Set visible items
2514     myReferencePointMatchBox->setNumVisible((int)myReferencePointMatchBox->getNumItems());
2515 }
2516 
2517 
~NeteditAttributes()2518 GNEFrame::NeteditAttributes::~NeteditAttributes() {}
2519 
2520 
2521 void
showNeteditAttributesModul(const GNEAttributeCarrier::TagProperties & tagProperty)2522 GNEFrame::NeteditAttributes::showNeteditAttributesModul(const GNEAttributeCarrier::TagProperties& tagProperty) {
2523     // we assume that frame will not be show
2524     bool showFrame = false;
2525     // check if lenght text field has to be showed
2526     if (tagProperty.canMaskStartEndPos()) {
2527         myLengthLabel->show();
2528         myLengthTextField->show();
2529         myReferencePointMatchBox->show();
2530         showFrame = true;
2531     } else {
2532         myLengthLabel->hide();
2533         myLengthTextField->hide();
2534         myReferencePointMatchBox->hide();
2535     }
2536     // check if block movement check button has to be show
2537     if (tagProperty.canBlockMovement()) {
2538         myBlockMovementLabel->show();
2539         myBlockMovementCheckButton->show();
2540         showFrame = true;
2541     } else {
2542         myBlockMovementLabel->hide();
2543         myBlockMovementCheckButton->hide();
2544     }
2545     // check if block shape check button has to be show
2546     if (tagProperty.canBlockShape()) {
2547         myBlockShapeLabel->show();
2548         myBlockShapeCheckButton->show();
2549         showFrame = true;
2550     } else {
2551         myBlockShapeLabel->hide();
2552         myBlockShapeCheckButton->hide();
2553     }
2554     // check if close shape check button has to be show
2555     if (tagProperty.canCloseShape()) {
2556         myClosePolygonLabel->show();
2557         myCloseShapeCheckButton->show();
2558         showFrame = true;
2559     } else {
2560         myClosePolygonLabel->hide();
2561         myCloseShapeCheckButton->hide();
2562     }
2563     // if at least one element is show, show modul
2564     if (showFrame) {
2565         show();
2566     } else {
2567         hide();
2568     }
2569 }
2570 
2571 
2572 void
hideNeteditAttributesModul()2573 GNEFrame::NeteditAttributes::hideNeteditAttributesModul() {
2574     hide();
2575 }
2576 
2577 
2578 bool
getNeteditAttributesAndValues(std::map<SumoXMLAttr,std::string> & valuesMap,GNELane * lane) const2579 GNEFrame::NeteditAttributes::getNeteditAttributesAndValues(std::map<SumoXMLAttr, std::string>& valuesMap, GNELane* lane) const {
2580     // check if we need to obtain a start and end position over an edge
2581     if (myReferencePointMatchBox->shown()) {
2582         // we need a valid lane to calculate position over lane
2583         if (lane == nullptr) {
2584             return false;
2585         } else if (myCurrentLengthValid) {
2586             // Obtain position of the mouse over lane (limited over grid)
2587             double mousePositionOverLane = lane->getShape().nearest_offset_to_point2D(myFrameParent->myViewNet->snapToActiveGrid(myFrameParent->myViewNet->getPositionInformation())) / lane->getLengthGeometryFactor();
2588             // check if current reference point is valid
2589             if (myActualAdditionalReferencePoint == GNE_ADDITIONALREFERENCEPOINT_INVALID) {
2590                 std::string errorMessage = "Current selected reference point isn't valid";
2591                 myFrameParent->myViewNet->setStatusBarText(errorMessage);
2592                 // Write Warning in console if we're in testing mode
2593                 WRITE_DEBUG(errorMessage);
2594                 return false;
2595             } else {
2596                 // obtain lenght
2597                 double lenght = GNEAttributeCarrier::parse<double>(myLengthTextField->getText().text());
2598                 // set start and end position
2599                 valuesMap[SUMO_ATTR_STARTPOS] = toString(setStartPosition(mousePositionOverLane, lenght));
2600                 valuesMap[SUMO_ATTR_ENDPOS] = toString(setEndPosition(mousePositionOverLane, lenght));
2601             }
2602         } else {
2603             return false;
2604         }
2605     }
2606     // Save block value if element can be blocked
2607     if (myBlockMovementCheckButton->shown()) {
2608         if (myBlockMovementCheckButton->getCheck() == 1) {
2609             valuesMap[GNE_ATTR_BLOCK_MOVEMENT] = "1";
2610         } else {
2611             valuesMap[GNE_ATTR_BLOCK_MOVEMENT] = "0";
2612         }
2613     }
2614     // Save block shape value if shape's element can be blocked
2615     if (myBlockShapeCheckButton->shown()) {
2616         if (myBlockShapeCheckButton->getCheck() == 1) {
2617             valuesMap[GNE_ATTR_BLOCK_SHAPE] = "1";
2618         } else {
2619             valuesMap[GNE_ATTR_BLOCK_SHAPE] = "0";
2620         }
2621     }
2622     // Save close shape value if shape's element can be closed
2623     if (myCloseShapeCheckButton->shown()) {
2624         if (myCloseShapeCheckButton->getCheck() == 1) {
2625             valuesMap[GNE_ATTR_CLOSE_SHAPE] = "1";
2626         } else {
2627             valuesMap[GNE_ATTR_CLOSE_SHAPE] = "0";
2628         }
2629     }
2630     // all ok, then return true to continue creating element
2631     return true;
2632 }
2633 
2634 
2635 long
onCmdSetNeteditAttribute(FXObject * obj,FXSelector,void *)2636 GNEFrame::NeteditAttributes::onCmdSetNeteditAttribute(FXObject* obj, FXSelector, void*) {
2637     if (obj == myBlockMovementCheckButton) {
2638         if (myBlockMovementCheckButton->getCheck()) {
2639             myBlockMovementCheckButton->setText("true");
2640         } else {
2641             myBlockMovementCheckButton->setText("false");
2642         }
2643     } else if (obj == myBlockShapeCheckButton) {
2644         if (myBlockShapeCheckButton->getCheck()) {
2645             myBlockShapeCheckButton->setText("true");
2646         } else {
2647             myBlockShapeCheckButton->setText("false");
2648         }
2649     } else if (obj == myCloseShapeCheckButton) {
2650         if (myCloseShapeCheckButton->getCheck()) {
2651             myCloseShapeCheckButton->setText("true");
2652         } else {
2653             myCloseShapeCheckButton->setText("false");
2654         }
2655     } else if (obj == myLengthTextField) {
2656         // change color of text field depending of the input length
2657         if (GNEAttributeCarrier::canParse<double>(myLengthTextField->getText().text()) &&
2658                 GNEAttributeCarrier::parse<double>(myLengthTextField->getText().text()) > 0) {
2659             myLengthTextField->setTextColor(FXRGB(0, 0, 0));
2660             myLengthTextField->killFocus();
2661             myCurrentLengthValid = true;
2662         } else {
2663             myLengthTextField->setTextColor(FXRGB(255, 0, 0));
2664             myCurrentLengthValid = false;
2665         }
2666         // Update aditional frame
2667         update();
2668     } else if (obj == myReferencePointMatchBox) {
2669         // Cast actual reference point type
2670         if (myReferencePointMatchBox->getText() == "reference left") {
2671             myReferencePointMatchBox->setTextColor(FXRGB(0, 0, 0));
2672             myActualAdditionalReferencePoint = GNE_ADDITIONALREFERENCEPOINT_LEFT;
2673             myLengthTextField->enable();
2674         } else if (myReferencePointMatchBox->getText() == "reference right") {
2675             myReferencePointMatchBox->setTextColor(FXRGB(0, 0, 0));
2676             myActualAdditionalReferencePoint = GNE_ADDITIONALREFERENCEPOINT_RIGHT;
2677             myLengthTextField->enable();
2678         } else if (myReferencePointMatchBox->getText() == "reference center") {
2679             myLengthTextField->enable();
2680             myReferencePointMatchBox->setTextColor(FXRGB(0, 0, 0));
2681             myActualAdditionalReferencePoint = GNE_ADDITIONALREFERENCEPOINT_CENTER;
2682             myLengthTextField->enable();
2683         } else {
2684             myReferencePointMatchBox->setTextColor(FXRGB(255, 0, 0));
2685             myActualAdditionalReferencePoint = GNE_ADDITIONALREFERENCEPOINT_INVALID;
2686             myLengthTextField->disable();
2687         }
2688     }
2689 
2690     return 1;
2691 }
2692 
2693 
2694 long
onCmdHelp(FXObject *,FXSelector,void *)2695 GNEFrame::NeteditAttributes::onCmdHelp(FXObject*, FXSelector, void*) {
2696     // Create dialog box
2697     FXDialogBox* additionalNeteditAttributesHelpDialog = new FXDialogBox(this, "Netedit Parameters Help", GUIDesignDialogBox);
2698     additionalNeteditAttributesHelpDialog->setIcon(GUIIconSubSys::getIcon(ICON_MODEADDITIONAL));
2699     // set help text
2700     std::ostringstream help;
2701     help
2702             << "- Referece point: Mark the initial position of the additional element.\n"
2703             << "  Example: If you want to create a busStop with a length of 30 in the point 100 of the lane:\n"
2704             << "  - Reference Left will create it with startPos = 70 and endPos = 100.\n"
2705             << "  - Reference Right will create it with startPos = 100 and endPos = 130.\n"
2706             << "  - Reference Center will create it with startPos = 85 and endPos = 115.\n"
2707             << "\n"
2708             << "- Block movement: if is enabled, the created additional element will be blocked. i.e. cannot be moved with\n"
2709             << "  the mouse. This option can be modified inspecting element.";
2710     // Create label with the help text
2711     new FXLabel(additionalNeteditAttributesHelpDialog, help.str().c_str(), 0, GUIDesignLabelFrameInformation);
2712     // Create horizontal separator
2713     new FXHorizontalSeparator(additionalNeteditAttributesHelpDialog, GUIDesignHorizontalSeparator);
2714     // Create frame for OK Button
2715     FXHorizontalFrame* myHorizontalFrameOKButton = new FXHorizontalFrame(additionalNeteditAttributesHelpDialog, GUIDesignAuxiliarHorizontalFrame);
2716     // Create Button Close (And two more horizontal frames to center it)
2717     new FXHorizontalFrame(myHorizontalFrameOKButton, GUIDesignAuxiliarHorizontalFrame);
2718     new FXButton(myHorizontalFrameOKButton, "OK\t\tclose", GUIIconSubSys::getIcon(ICON_ACCEPT), additionalNeteditAttributesHelpDialog, FXDialogBox::ID_ACCEPT, GUIDesignButtonOK);
2719     new FXHorizontalFrame(myHorizontalFrameOKButton, GUIDesignAuxiliarHorizontalFrame);
2720     // Write Warning in console if we're in testing mode
2721     WRITE_DEBUG("Opening NeteditAttributes help dialog");
2722     // create Dialog
2723     additionalNeteditAttributesHelpDialog->create();
2724     // show in the given position
2725     additionalNeteditAttributesHelpDialog->show(PLACEMENT_CURSOR);
2726     // refresh APP
2727     getApp()->refresh();
2728     // open as modal dialog (will block all windows until stop() or stopModal() is called)
2729     getApp()->runModalFor(additionalNeteditAttributesHelpDialog);
2730     // Write Warning in console if we're in testing mode
2731     WRITE_DEBUG("Closing NeteditAttributes help dialog");
2732     return 1;
2733     /**********
2734     help from PolygonFrame
2735             << "- Block movement: If enabled, the created polygon element will be blocked. i.e. cannot be moved with\n"
2736             << "  the mouse. This option can be modified inspecting element.\n"
2737             << "\n"
2738             << "- Block shape: If enabled, the shape of created polygon element will be blocked. i.e. their geometry points\n"
2739             << "  cannot be edited be moved with the mouse. This option can be modified inspecting element.\n"
2740             << "\n"
2741             << "- Close shape: If enabled, the created polygon element will be closed. i.e. the last created geometry point\n"
2742             << "  will be connected with the first geometry point automatically. This option can be modified inspecting element.";
2743 
2744     ****************/
2745 }
2746 
2747 
2748 double
setStartPosition(double positionOfTheMouseOverLane,double lengthOfAdditional) const2749 GNEFrame::NeteditAttributes::setStartPosition(double positionOfTheMouseOverLane, double lengthOfAdditional) const {
2750     switch (myActualAdditionalReferencePoint) {
2751         case GNE_ADDITIONALREFERENCEPOINT_LEFT:
2752             return positionOfTheMouseOverLane;
2753         case GNE_ADDITIONALREFERENCEPOINT_RIGHT:
2754             return positionOfTheMouseOverLane - lengthOfAdditional;
2755         case GNE_ADDITIONALREFERENCEPOINT_CENTER:
2756             return positionOfTheMouseOverLane - lengthOfAdditional / 2;
2757         default:
2758             throw InvalidArgument("Reference Point invalid");
2759     }
2760 }
2761 
2762 
2763 double
setEndPosition(double positionOfTheMouseOverLane,double lengthOfAdditional) const2764 GNEFrame::NeteditAttributes::setEndPosition(double positionOfTheMouseOverLane, double lengthOfAdditional)  const {
2765     switch (myActualAdditionalReferencePoint) {
2766         case GNE_ADDITIONALREFERENCEPOINT_LEFT:
2767             return positionOfTheMouseOverLane + lengthOfAdditional;
2768         case GNE_ADDITIONALREFERENCEPOINT_RIGHT:
2769             return positionOfTheMouseOverLane;
2770         case GNE_ADDITIONALREFERENCEPOINT_CENTER:
2771             return positionOfTheMouseOverLane + lengthOfAdditional / 2;
2772         default:
2773             throw InvalidArgument("Reference Point invalid");
2774     }
2775 }
2776 
2777 // ---------------------------------------------------------------------------
2778 // GNEFrame - methods
2779 // ---------------------------------------------------------------------------
2780 
GNEFrame(FXHorizontalFrame * horizontalFrameParent,GNEViewNet * viewNet,const std::string & frameLabel)2781 GNEFrame::GNEFrame(FXHorizontalFrame* horizontalFrameParent, GNEViewNet* viewNet, const std::string& frameLabel) :
2782     FXVerticalFrame(horizontalFrameParent, GUIDesignAuxiliarFrame),
2783     myViewNet(viewNet),
2784     myEdgeCandidateColor(RGBColor(0, 64, 0, 255)),
2785     myEdgeCandidateSelectedColor(RGBColor::GREEN) {
2786 
2787     // fill myPredefinedTagsMML (to avoid repeating this fill during every element creation)
2788     int i = 0;
2789     while (SUMOXMLDefinitions::attrs[i].key != SUMO_ATTR_NOTHING) {
2790         myPredefinedTagsMML[SUMOXMLDefinitions::attrs[i].key] = toString(SUMOXMLDefinitions::attrs[i].str);
2791         myPredefinedTagsMML[SUMOXMLDefinitions::attrs[i].key] = SUMOXMLDefinitions::attrs[i].str;
2792         i++;
2793     }
2794 
2795     // Create font only one time
2796     if (myFrameHeaderFont == nullptr) {
2797         myFrameHeaderFont = new FXFont(getApp(), "Arial", 14, FXFont::Bold);
2798     }
2799 
2800     // Create frame for header
2801     myHeaderFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
2802 
2803     // Create frame for left elements of header (By default unused)
2804     myHeaderLeftFrame = new FXHorizontalFrame(myHeaderFrame, GUIDesignAuxiliarHorizontalFrame);
2805     myHeaderLeftFrame->hide();
2806 
2807     // Create titel frame
2808     myFrameHeaderLabel = new FXLabel(myHeaderFrame, frameLabel.c_str(), nullptr, GUIDesignLabelFrameInformation);
2809 
2810     // Create frame for right elements of header (By default unused)
2811     myHeaderRightFrame = new FXHorizontalFrame(myHeaderFrame, GUIDesignAuxiliarHorizontalFrame);
2812     myHeaderRightFrame->hide();
2813 
2814     // Add separator
2815     new FXHorizontalSeparator(this, GUIDesignHorizontalSeparator);
2816 
2817     // Create frame for contents
2818     myScrollWindowsContents = new FXScrollWindow(this, GUIDesignContentsScrollWindow);
2819 
2820     // Create frame for contents
2821     myContentFrame = new FXVerticalFrame(myScrollWindowsContents, GUIDesignContentsFrame);
2822 
2823     // Set font of header
2824     myFrameHeaderLabel->setFont(myFrameHeaderFont);
2825 
2826     // Hide Frame
2827     FXVerticalFrame::hide();
2828 }
2829 
2830 
~GNEFrame()2831 GNEFrame::~GNEFrame() {
2832     // delete frame header only one time
2833     if (myFrameHeaderFont) {
2834         delete myFrameHeaderFont;
2835         myFrameHeaderFont = nullptr;
2836     }
2837 }
2838 
2839 
2840 void
focusUpperElement()2841 GNEFrame::focusUpperElement() {
2842     myFrameHeaderLabel->setFocus();
2843 }
2844 
2845 
2846 void
show()2847 GNEFrame::show() {
2848     // show scroll window
2849     FXVerticalFrame::show();
2850     // Show and update Frame Area in which this GNEFrame is placed
2851     myViewNet->getViewParent()->showFramesArea();
2852 }
2853 
2854 
2855 void
hide()2856 GNEFrame::hide() {
2857     // hide scroll window
2858     FXVerticalFrame::hide();
2859     // Hide Frame Area in which this GNEFrame is placed
2860     myViewNet->getViewParent()->hideFramesArea();
2861 }
2862 
2863 
2864 void
setFrameWidth(int newWidth)2865 GNEFrame::setFrameWidth(int newWidth) {
2866     setWidth(newWidth);
2867     myScrollWindowsContents->setWidth(newWidth);
2868 }
2869 
2870 
2871 GNEViewNet*
getViewNet() const2872 GNEFrame::getViewNet() const {
2873     return myViewNet;
2874 }
2875 
2876 
2877 FXLabel*
getFrameHeaderLabel() const2878 GNEFrame::getFrameHeaderLabel() const {
2879     return myFrameHeaderLabel;
2880 }
2881 
2882 
2883 FXFont*
getFrameHeaderFont() const2884 GNEFrame::getFrameHeaderFont() const {
2885     return myFrameHeaderFont;
2886 }
2887 
2888 
2889 void
updateFrameAfterUndoRedo()2890 GNEFrame::updateFrameAfterUndoRedo() {
2891     // this function has to be reimplemente in all child frames that needs to draw a polygon (for example, GNEFrame or GNETAZFrame)
2892 }
2893 
2894 // ---------------------------------------------------------------------------
2895 // GNEFrame - protected methods
2896 // ---------------------------------------------------------------------------
2897 
2898 bool
buildShape()2899 GNEFrame::buildShape() {
2900     // this function has to be reimplemente in all child frames that needs to draw a polygon (for example, GNEFrame or GNETAZFrame)
2901     return false;
2902 }
2903 
2904 
2905 void
enableModuls(const GNEAttributeCarrier::TagProperties &)2906 GNEFrame::enableModuls(const GNEAttributeCarrier::TagProperties&) {
2907     // this function has to be reimplemente in all child frames that uses a ItemSelector modul
2908 }
2909 
2910 
2911 void
disableModuls()2912 GNEFrame::disableModuls() {
2913     // this function has to be reimplemente in all child frames that uses a ItemSelector modul
2914 }
2915 
2916 
2917 void
updateFrameAfterChangeAttribute()2918 GNEFrame::updateFrameAfterChangeAttribute() {
2919     // this function has to be reimplemente in all child frames that uses a ItemSelector modul
2920 }
2921 
2922 void
openAttributesEditorExtendedDialog()2923 GNEFrame::openAttributesEditorExtendedDialog()  {
2924     // this function has to be reimplemente in all child frames that uses a AttributesCreator editor with extended attributes
2925 }
2926 
2927 
2928 void
openHelpAttributesDialog(const GNEAttributeCarrier::TagProperties & tagProperties) const2929 GNEFrame::openHelpAttributesDialog(const GNEAttributeCarrier::TagProperties& tagProperties) const {
2930     FXDialogBox* attributesHelpDialog = new FXDialogBox(myScrollWindowsContents, ("Parameters of " + tagProperties.getTagStr()).c_str(), GUIDesignDialogBoxResizable, 0, 0, 0, 0, 10, 10, 10, 38, 4, 4);
2931     // Create FXTable
2932     FXTable* myTable = new FXTable(attributesHelpDialog, attributesHelpDialog, MID_TABLE, GUIDesignTableNotEditable);
2933     attributesHelpDialog->setIcon(GUIIconSubSys::getIcon(ICON_MODEINSPECT));
2934     int sizeColumnDescription = 0;
2935     int sizeColumnDefinitions = 0;
2936     myTable->setVisibleRows((FXint)(tagProperties.getNumberOfAttributes()));
2937     myTable->setVisibleColumns(3);
2938     myTable->setTableSize((FXint)(tagProperties.getNumberOfAttributes()), 3);
2939     myTable->setBackColor(FXRGB(255, 255, 255));
2940     myTable->setColumnText(0, "Attribute");
2941     myTable->setColumnText(1, "Description");
2942     myTable->setColumnText(2, "Definition");
2943     myTable->getRowHeader()->setWidth(0);
2944     // Iterate over vector of additional parameters
2945     int itemIndex = 0;
2946     for (auto i : tagProperties) {
2947         // Set attribute
2948         FXTableItem* attribute = new FXTableItem(toString(i.first).c_str());
2949         attribute->setJustify(FXTableItem::CENTER_X);
2950         myTable->setItem(itemIndex, 0, attribute);
2951         // Set description of element
2952         FXTableItem* type = new FXTableItem("");
2953         type->setText(i.second.getDescription().c_str());
2954         sizeColumnDescription = MAX2(sizeColumnDescription, (int)i.second.getDescription().size());
2955         type->setJustify(FXTableItem::CENTER_X);
2956         myTable->setItem(itemIndex, 1, type);
2957         // Set definition
2958         FXTableItem* definition = new FXTableItem(i.second.getDefinition().c_str());
2959         definition->setJustify(FXTableItem::LEFT);
2960         myTable->setItem(itemIndex, 2, definition);
2961         sizeColumnDefinitions = MAX2(sizeColumnDefinitions, (int)i.second.getDefinition().size());
2962         itemIndex++;
2963     }
2964     // set header
2965     FXHeader* header = myTable->getColumnHeader();
2966     header->setItemJustify(0, JUSTIFY_CENTER_X);
2967     header->setItemSize(0, 120);
2968     header->setItemJustify(1, JUSTIFY_CENTER_X);
2969     header->setItemSize(1, sizeColumnDescription * 7);
2970     header->setItemJustify(2, JUSTIFY_CENTER_X);
2971     header->setItemSize(2, sizeColumnDefinitions * 6);
2972     // Create horizontal separator
2973     new FXHorizontalSeparator(attributesHelpDialog, GUIDesignHorizontalSeparator);
2974     // Create frame for OK Button
2975     FXHorizontalFrame* myHorizontalFrameOKButton = new FXHorizontalFrame(attributesHelpDialog, GUIDesignAuxiliarHorizontalFrame);
2976     // Create Button Close (And two more horizontal frames to center it)
2977     new FXHorizontalFrame(myHorizontalFrameOKButton, GUIDesignAuxiliarHorizontalFrame);
2978     new FXButton(myHorizontalFrameOKButton, "OK\t\tclose", GUIIconSubSys::getIcon(ICON_ACCEPT), attributesHelpDialog, FXDialogBox::ID_ACCEPT, GUIDesignButtonOK);
2979     new FXHorizontalFrame(myHorizontalFrameOKButton, GUIDesignAuxiliarHorizontalFrame);
2980     // Write Warning in console if we're in testing mode
2981     WRITE_DEBUG("Opening HelpAttributes dialog for tag '" + tagProperties.getTagStr() + "' showing " + toString(tagProperties.getNumberOfAttributes()) + " attributes");
2982     // create Dialog
2983     attributesHelpDialog->create();
2984     // show in the given position
2985     attributesHelpDialog->show(PLACEMENT_CURSOR);
2986     // refresh APP
2987     getApp()->refresh();
2988     // open as modal dialog (will block all windows until stop() or stopModal() is called)
2989     getApp()->runModalFor(attributesHelpDialog);
2990     // Write Warning in console if we're in testing mode
2991     WRITE_DEBUG("Closing HelpAttributes dialog for tag '" + tagProperties.getTagStr() + "'");
2992 }
2993 
2994 
2995 const RGBColor&
getEdgeCandidateColor() const2996 GNEFrame::getEdgeCandidateColor() const {
2997     return myEdgeCandidateColor;
2998 }
2999 
3000 
3001 const RGBColor&
getEdgeCandidateSelectedColor() const3002 GNEFrame::getEdgeCandidateSelectedColor() const {
3003     return myEdgeCandidateSelectedColor;
3004 }
3005 
3006 
3007 const std::map<int, std::string>&
getPredefinedTagsMML() const3008 GNEFrame::getPredefinedTagsMML() const {
3009     return myPredefinedTagsMML;
3010 }
3011 
3012 /****************************************************************************/
3013