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    GNESelectorFrame.cpp
11 /// @author  Jakob Erdmann
12 /// @date    Mar 2011
13 /// @version $Id$
14 ///
15 // The Widget for modifying selections of network-elements
16 // (some elements adapted from GUIDialog_GLChosenEditor)
17 /****************************************************************************/
18 
19 
20 // ===========================================================================
21 // included modules
22 // ===========================================================================
23 #include <config.h>
24 
25 #include <utils/foxtools/MFXUtils.h>
26 #include <utils/gui/windows/GUIAppEnum.h>
27 #include <utils/gui/div/GUIDesigns.h>
28 #include <utils/gui/images/GUIIconSubSys.h>
29 #include <netedit/GNENet.h>
30 #include <netedit/GNEViewNet.h>
31 #include <netedit/netelements/GNEJunction.h>
32 #include <netedit/netelements/GNEEdge.h>
33 #include <netedit/netelements/GNELane.h>
34 #include <netedit/netelements/GNEConnection.h>
35 #include <netedit/netelements/GNECrossing.h>
36 #include <netedit/additionals/GNEAdditional.h>
37 #include <netedit/additionals/GNEPoly.h>
38 #include <netedit/additionals/GNEPOI.h>
39 #include <netedit/GNEUndoList.h>
40 #include <utils/gui/globjects/GUIGlObjectStorage.h>
41 
42 #include "GNESelectorFrame.h"
43 
44 
45 // ===========================================================================
46 // FOX callback mapping
47 // ===========================================================================
48 FXDEFMAP(GNESelectorFrame::LockGLObjectTypes::ObjectTypeEntry) ObjectTypeEntryMap[] = {
49     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SET_ATTRIBUTE,  GNESelectorFrame::LockGLObjectTypes::ObjectTypeEntry::onCmdSetCheckBox)
50 };
51 
52 FXDEFMAP(GNESelectorFrame::ModificationMode) ModificationModeMap[] = {
53     FXMAPFUNC(SEL_COMMAND,  MID_CHOOSEN_OPERATION,  GNESelectorFrame::ModificationMode::onCmdSelectModificationMode)
54 };
55 
56 FXDEFMAP(GNESelectorFrame::ElementSet) ElementSetMap[] = {
57     FXMAPFUNC(SEL_COMMAND,  MID_CHOOSEN_ELEMENTS,   GNESelectorFrame::ElementSet::onCmdSelectElementSet)
58 };
59 
60 FXDEFMAP(GNESelectorFrame::MatchAttribute) MatchAttributeMap[] = {
61     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SELECTORFRAME_SELECTTAG,        GNESelectorFrame::MatchAttribute::onCmdSelMBTag),
62     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SELECTORFRAME_SELECTATTRIBUTE,  GNESelectorFrame::MatchAttribute::onCmdSelMBAttribute),
63     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SELECTORFRAME_PROCESSSTRING,    GNESelectorFrame::MatchAttribute::onCmdSelMBString),
64     FXMAPFUNC(SEL_COMMAND,  MID_HELP,                               GNESelectorFrame::MatchAttribute::onCmdHelp)
65 };
66 
67 FXDEFMAP(GNESelectorFrame::VisualScaling) VisualScalingMap[] = {
68     FXMAPFUNC(SEL_COMMAND,  MID_GNE_SELECTORFRAME_SELECTSCALE,      GNESelectorFrame::VisualScaling::onCmdScaleSelection)
69 };
70 
71 FXDEFMAP(GNESelectorFrame::SelectionOperation) SelectionOperationMap[] = {
72     FXMAPFUNC(SEL_COMMAND,  MID_CHOOSEN_LOAD,   GNESelectorFrame::SelectionOperation::onCmdLoad),
73     FXMAPFUNC(SEL_COMMAND,  MID_CHOOSEN_SAVE,   GNESelectorFrame::SelectionOperation::onCmdSave),
74     FXMAPFUNC(SEL_COMMAND,  MID_CHOOSEN_INVERT, GNESelectorFrame::SelectionOperation::onCmdInvert),
75     FXMAPFUNC(SEL_COMMAND,  MID_CHOOSEN_CLEAR,  GNESelectorFrame::SelectionOperation::onCmdClear)
76 };
77 
78 // Object implementation
FXIMPLEMENT(GNESelectorFrame::LockGLObjectTypes::ObjectTypeEntry,FXObject,ObjectTypeEntryMap,ARRAYNUMBER (ObjectTypeEntryMap))79 FXIMPLEMENT(GNESelectorFrame::LockGLObjectTypes::ObjectTypeEntry,   FXObject,       ObjectTypeEntryMap,     ARRAYNUMBER(ObjectTypeEntryMap))
80 FXIMPLEMENT(GNESelectorFrame::ModificationMode,                     FXGroupBox,     ModificationModeMap,    ARRAYNUMBER(ModificationModeMap))
81 FXIMPLEMENT(GNESelectorFrame::ElementSet,                           FXGroupBox,     ElementSetMap,          ARRAYNUMBER(ElementSetMap))
82 FXIMPLEMENT(GNESelectorFrame::MatchAttribute,                       FXGroupBox,     MatchAttributeMap,      ARRAYNUMBER(MatchAttributeMap))
83 FXIMPLEMENT(GNESelectorFrame::VisualScaling,                        FXGroupBox,     VisualScalingMap,       ARRAYNUMBER(VisualScalingMap))
84 FXIMPLEMENT(GNESelectorFrame::SelectionOperation,                   FXGroupBox,     SelectionOperationMap,  ARRAYNUMBER(SelectionOperationMap))
85 
86 // ===========================================================================
87 // method definitions
88 // ===========================================================================
89 
90 GNESelectorFrame::GNESelectorFrame(FXHorizontalFrame* horizontalFrameParent, GNEViewNet* viewNet):
91     GNEFrame(horizontalFrameParent, viewNet, "Selection") {
92     // create selectedItems modul
93     myLockGLObjectTypes = new LockGLObjectTypes(this);
94     // create Modification Mode modul
95     myModificationMode = new ModificationMode(this);
96     // create ElementSet modul
97     myElementSet = new ElementSet(this);
98     // create MatchAttribute modul
99     myMatchAttribute = new MatchAttribute(this);
100     // create VisualScaling modul
101     myVisualScaling = new VisualScaling(this);
102     // create SelectionOperation modul
103     mySelectionOperation = new SelectionOperation(this);
104     // Create groupbox for information about selections
105     FXGroupBox* selectionHintGroupBox = new FXGroupBox(myContentFrame, "Information", GUIDesignGroupBoxFrame);
106     // Create Selection Hint
107     new FXLabel(selectionHintGroupBox, " - Hold <SHIFT> for \n   rectangle selection.\n - Press <DEL> to\n   delete selected items.", nullptr, GUIDesignLabelFrameInformation);
108 
109 }
110 
111 
~GNESelectorFrame()112 GNESelectorFrame::~GNESelectorFrame() {}
113 
114 
115 void
show()116 GNESelectorFrame::show() {
117     // show Type Entries depending of current supermode
118     myLockGLObjectTypes->showTypeEntries();
119     // refresh element set
120     myElementSet->refreshElementSet();
121     // Show frame
122     GNEFrame::show();
123 }
124 
125 
126 void
hide()127 GNESelectorFrame::hide() {
128     // hide frame
129     GNEFrame::hide();
130 }
131 
132 
133 void
clearCurrentSelection() const134 GNESelectorFrame::clearCurrentSelection() const {
135     // for clear selection, simply change all GNE_ATTR_SELECTED attribute of current selected elements
136     myViewNet->getUndoList()->p_begin("clear selection");
137     // obtain selected ACs depending of current supermode
138     std::vector<GNEAttributeCarrier*> selectedAC = myViewNet->getNet()->getSelectedAttributeCarriers(false);
139     // change attribute GNE_ATTR_SELECTED of all selected items to false
140     for (auto i : selectedAC) {
141         i->setAttribute(GNE_ATTR_SELECTED, "false", myViewNet->getUndoList());
142     }
143     // finish clear selection
144     myViewNet->getUndoList()->p_end();
145     // update view
146     myViewNet->update();
147 }
148 
149 
150 void
handleIDs(const std::vector<GNEAttributeCarrier * > & ACs,ModificationMode::SetOperation setop)151 GNESelectorFrame::handleIDs(const std::vector<GNEAttributeCarrier*>& ACs, ModificationMode::SetOperation setop) {
152     const ModificationMode::SetOperation setOperation = ((setop == ModificationMode::SET_DEFAULT) ? myModificationMode->getModificationMode() : setop);
153     // declare two sets of attribute carriers, one for select and another for unselect
154     std::set<std::pair<std::string, GNEAttributeCarrier*> > ACToSelect;
155     std::set<std::pair<std::string, GNEAttributeCarrier*> > ACToUnselect;
156     // in restrict AND replace mode all current selected attribute carriers will be unselected
157     if ((setOperation == ModificationMode::SET_REPLACE) || (setOperation == ModificationMode::SET_RESTRICT)) {
158         // obtain selected ACs depending of current supermode
159         std::vector<GNEAttributeCarrier*> selectedAC = myViewNet->getNet()->getSelectedAttributeCarriers(false);
160         // add id into ACs to unselect
161         for (auto i : selectedAC) {
162             ACToUnselect.insert(std::pair<std::string, GNEAttributeCarrier*>(i->getID(), i));
163         }
164     }
165     // handle ids
166     for (auto i : ACs) {
167         // iterate over AtributeCarriers an place it in ACToSelect or ACToUnselect
168         switch (setOperation) {
169             case GNESelectorFrame::ModificationMode::SET_SUB:
170                 ACToUnselect.insert(std::pair<std::string, GNEAttributeCarrier*>(i->getID(), i));
171                 break;
172             case GNESelectorFrame::ModificationMode::SET_RESTRICT:
173                 if (ACToUnselect.find(std::pair<std::string, GNEAttributeCarrier*>(i->getID(), i)) != ACToUnselect.end()) {
174                     ACToSelect.insert(std::pair<std::string, GNEAttributeCarrier*>(i->getID(), i));
175                 }
176                 break;
177             default:
178                 ACToSelect.insert(std::pair<std::string, GNEAttributeCarrier*>(i->getID(), i));
179                 break;
180         }
181     }
182     // select junctions and their connections if Auto select junctions is enabled (note: only for "add mode")
183     if (myViewNet->autoSelectNodes() && GNESelectorFrame::ModificationMode::SET_ADD) {
184         std::vector<GNEEdge*> edgesToSelect;
185         // iterate over ACToSelect and extract edges
186         for (auto i : ACToSelect) {
187             if (i.second->getTagProperty().getTag() == SUMO_TAG_EDGE) {
188                 edgesToSelect.push_back(dynamic_cast<GNEEdge*>(i.second));
189             }
190         }
191         // iterate over extracted edges
192         for (auto i : edgesToSelect) {
193             // select junction source and all their connections and crossings
194             ACToSelect.insert(std::make_pair(i->getGNEJunctionSource()->getID(), i->getGNEJunctionSource()));
195             for (auto j : i->getGNEJunctionSource()->getGNEConnections()) {
196                 ACToSelect.insert(std::make_pair(j->getID(), j));
197             }
198             for (auto j : i->getGNEJunctionSource()->getGNECrossings()) {
199                 ACToSelect.insert(std::make_pair(j->getID(), j));
200             }
201             // select junction destiny and all their connections crossings
202             ACToSelect.insert(std::make_pair(i->getGNEJunctionDestiny()->getID(), i->getGNEJunctionDestiny()));
203             for (auto j : i->getGNEJunctionDestiny()->getGNEConnections()) {
204                 ACToSelect.insert(std::make_pair(j->getID(), j));
205             }
206             for (auto j : i->getGNEJunctionDestiny()->getGNECrossings()) {
207                 ACToSelect.insert(std::make_pair(j->getID(), j));
208             }
209         }
210     }
211     // only continue if there is ACs to select or unselect
212     if ((ACToSelect.size() + ACToUnselect.size()) > 0) {
213         // first unselect AC of ACToUnselect and then selects AC of ACToSelect
214         myViewNet->getUndoList()->p_begin("selection using rectangle");
215         for (auto i : ACToUnselect) {
216             if (i.second->getTagProperty().isSelectable()) {
217                 i.second->setAttribute(GNE_ATTR_SELECTED, "false", myViewNet->getUndoList());
218             }
219         }
220         for (auto i : ACToSelect) {
221             if (i.second->getTagProperty().isSelectable()) {
222                 i.second->setAttribute(GNE_ATTR_SELECTED, "true", myViewNet->getUndoList());
223             }
224         }
225         // finish operation
226         myViewNet->getUndoList()->p_end();
227     }
228     // update view
229     myViewNet->update();
230 }
231 
232 
233 GNESelectorFrame::ModificationMode*
getModificationModeModul() const234 GNESelectorFrame::getModificationModeModul() const {
235     return myModificationMode;
236 }
237 
238 
239 GNESelectorFrame::LockGLObjectTypes*
getLockGLObjectTypes() const240 GNESelectorFrame::getLockGLObjectTypes() const {
241     return myLockGLObjectTypes;
242 }
243 
244 
245 std::vector<GNEAttributeCarrier*>
getMatches(SumoXMLTag ACTag,SumoXMLAttr ACAttr,char compOp,double val,const std::string & expr)246 GNESelectorFrame::getMatches(SumoXMLTag ACTag, SumoXMLAttr ACAttr, char compOp, double val, const std::string& expr) {
247     std::vector<GNEAttributeCarrier*> result;
248     std::vector<GNEAttributeCarrier*> allACbyTag = myViewNet->getNet()->retrieveAttributeCarriers(ACTag);
249     const auto& tagValue = GNEAttributeCarrier::getTagProperties(ACTag);
250     for (auto it : allACbyTag) {
251         if (expr == "") {
252             result.push_back(it);
253         } else if (tagValue.hasAttribute(ACAttr) && tagValue.getAttributeProperties(ACAttr).isNumerical()) {
254             double acVal;
255             std::istringstream buf(it->getAttribute(ACAttr));
256             buf >> acVal;
257             switch (compOp) {
258                 case '<':
259                     if (acVal < val) {
260                         result.push_back(it);
261                     }
262                     break;
263                 case '>':
264                     if (acVal > val) {
265                         result.push_back(it);
266                     }
267                     break;
268                 case '=':
269                     if (acVal == val) {
270                         result.push_back(it);
271                     }
272                     break;
273             }
274         } else {
275             // string match
276             std::string acVal = it->getAttributeForSelection(ACAttr);
277             switch (compOp) {
278                 case '@':
279                     if (acVal.find(expr) != std::string::npos) {
280                         result.push_back(it);
281                     }
282                     break;
283                 case '!':
284                     if (acVal.find(expr) == std::string::npos) {
285                         result.push_back(it);
286                     }
287                     break;
288                 case '=':
289                     if (acVal == expr) {
290                         result.push_back(it);
291                     }
292                     break;
293                 case '^':
294                     if (acVal != expr) {
295                         result.push_back(it);
296                     }
297                     break;
298             }
299         }
300     }
301     return result;
302 }
303 
304 // ---------------------------------------------------------------------------
305 // ModificationMode::LockGLObjectTypes - methods
306 // ---------------------------------------------------------------------------
307 
LockGLObjectTypes(GNESelectorFrame * selectorFrameParent)308 GNESelectorFrame::LockGLObjectTypes::LockGLObjectTypes(GNESelectorFrame* selectorFrameParent) :
309     FXGroupBox(selectorFrameParent->myContentFrame, "Locked selected items", GUIDesignGroupBoxFrame),
310     mySelectorFrameParent(selectorFrameParent) {
311     // create a matrix for TypeEntries
312     FXMatrix* matrixLockGLObjectTypes = new FXMatrix(this, 3, GUIDesignMatrixLockGLTypes);
313     // create typeEntries for the different Network elements
314     myTypeEntries[GLO_JUNCTION] = std::make_pair(Supermode::GNE_SUPERMODE_NETWORK, new ObjectTypeEntry(matrixLockGLObjectTypes, "Junctions"));
315     myTypeEntries[GLO_EDGE] = std::make_pair(Supermode::GNE_SUPERMODE_NETWORK, new ObjectTypeEntry(matrixLockGLObjectTypes, "Edges"));
316     myTypeEntries[GLO_LANE] = std::make_pair(Supermode::GNE_SUPERMODE_NETWORK, new ObjectTypeEntry(matrixLockGLObjectTypes, "Lanes"));
317     myTypeEntries[GLO_CONNECTION] = std::make_pair(Supermode::GNE_SUPERMODE_NETWORK, new ObjectTypeEntry(matrixLockGLObjectTypes, "Connections"));
318     myTypeEntries[GLO_ADDITIONAL] = std::make_pair(Supermode::GNE_SUPERMODE_NETWORK, new ObjectTypeEntry(matrixLockGLObjectTypes, "Additionals"));
319     myTypeEntries[GLO_CROSSING] = std::make_pair(Supermode::GNE_SUPERMODE_NETWORK, new ObjectTypeEntry(matrixLockGLObjectTypes, "Crossings"));
320     myTypeEntries[GLO_POLYGON] = std::make_pair(Supermode::GNE_SUPERMODE_NETWORK, new ObjectTypeEntry(matrixLockGLObjectTypes, "Polygons"));
321     myTypeEntries[GLO_POI] = std::make_pair(Supermode::GNE_SUPERMODE_NETWORK, new ObjectTypeEntry(matrixLockGLObjectTypes, "POIs"));
322     // create typeEntries for the different Demand elements
323     myTypeEntries[GLO_ROUTE] = std::make_pair(Supermode::GNE_SUPERMODE_DEMAND, new ObjectTypeEntry(matrixLockGLObjectTypes, "Routes"));
324     myTypeEntries[GLO_VEHICLE] = std::make_pair(Supermode::GNE_SUPERMODE_DEMAND, new ObjectTypeEntry(matrixLockGLObjectTypes, "Vehicles"));
325     myTypeEntries[GLO_FLOW] = std::make_pair(Supermode::GNE_SUPERMODE_DEMAND, new ObjectTypeEntry(matrixLockGLObjectTypes, "Flows"));
326     myTypeEntries[GLO_TRIP] = std::make_pair(Supermode::GNE_SUPERMODE_DEMAND, new ObjectTypeEntry(matrixLockGLObjectTypes, "Trips"));
327 }
328 
329 
~LockGLObjectTypes()330 GNESelectorFrame::LockGLObjectTypes::~LockGLObjectTypes() {
331     // remove all type entries
332     for (const auto& i : myTypeEntries) {
333         delete i.second.second;
334     }
335 }
336 
337 
338 void
addedLockedObject(const GUIGlObjectType type)339 GNESelectorFrame::LockGLObjectTypes::addedLockedObject(const GUIGlObjectType type) {
340     myTypeEntries.at(type).second->counterUp();
341 }
342 
343 
344 void
removeLockedObject(const GUIGlObjectType type)345 GNESelectorFrame::LockGLObjectTypes::removeLockedObject(const GUIGlObjectType type) {
346     myTypeEntries.at(type).second->counterDown();
347 }
348 
349 
350 bool
IsObjectTypeLocked(const GUIGlObjectType type) const351 GNESelectorFrame::LockGLObjectTypes::IsObjectTypeLocked(const GUIGlObjectType type) const {
352     if ((type >= 100) && (type < 199)) {
353         return myTypeEntries.at(GLO_ADDITIONAL).second->isGLTypeLocked();
354     } else {
355         return myTypeEntries.at(type).second->isGLTypeLocked();
356     }
357 }
358 
359 
360 void
showTypeEntries()361 GNESelectorFrame::LockGLObjectTypes::showTypeEntries() {
362     for (const auto& i : myTypeEntries) {
363         // showr or hidde type entries depending of current supermode
364         if (i.second.first == mySelectorFrameParent->getViewNet()->getEditModes().currentSupermode) {
365             i.second.second->showObjectTypeEntry();
366         } else {
367             i.second.second->hideObjectTypeEntry();
368         }
369     }
370     // recalc frame parent
371     recalc();
372 }
373 
374 
ObjectTypeEntry(FXMatrix * matrixParent,const std::string & label)375 GNESelectorFrame::LockGLObjectTypes::ObjectTypeEntry::ObjectTypeEntry(FXMatrix* matrixParent, const std::string& label) :
376     FXObject(),
377     myCounter(0) {
378     // create elements
379     myLabelCounter = new FXLabel(matrixParent, "0", nullptr, GUIDesignLabelLeft);
380     myLabelTypeName = new FXLabel(matrixParent, (label + " ").c_str(), nullptr, GUIDesignLabelLeft);
381     myCheckBoxLocked = new FXCheckButton(matrixParent, "unlocked", this, MID_GNE_SET_ATTRIBUTE, GUIDesignCheckButtonLeft);
382 }
383 
384 
385 void
showObjectTypeEntry()386 GNESelectorFrame::LockGLObjectTypes::ObjectTypeEntry::showObjectTypeEntry() {
387     myLabelCounter->show();
388     myLabelTypeName->show();
389     myCheckBoxLocked->show();
390 }
391 
392 
393 void
hideObjectTypeEntry()394 GNESelectorFrame::LockGLObjectTypes::ObjectTypeEntry::hideObjectTypeEntry() {
395     myLabelCounter->hide();
396     myLabelTypeName->hide();
397     myCheckBoxLocked->hide();
398 }
399 
400 
401 void
counterUp()402 GNESelectorFrame::LockGLObjectTypes::ObjectTypeEntry::counterUp() {
403     myCounter++;
404     myLabelCounter->setText(toString(myCounter).c_str());
405 }
406 
407 
408 void
counterDown()409 GNESelectorFrame::LockGLObjectTypes::ObjectTypeEntry::counterDown() {
410     myCounter--;
411     myLabelCounter->setText(toString(myCounter).c_str());
412 }
413 
414 
415 bool
isGLTypeLocked() const416 GNESelectorFrame::LockGLObjectTypes::ObjectTypeEntry::isGLTypeLocked() const {
417     return (myCheckBoxLocked->getCheck() == TRUE);
418 }
419 
420 
421 long
onCmdSetCheckBox(FXObject *,FXSelector,void *)422 GNESelectorFrame::LockGLObjectTypes::ObjectTypeEntry::onCmdSetCheckBox(FXObject*, FXSelector, void*) {
423     if (myCheckBoxLocked->getCheck() == TRUE) {
424         myCheckBoxLocked->setText("locked");
425     } else {
426         myCheckBoxLocked->setText("unlocked");
427     }
428     return 1;
429 }
430 
431 // ---------------------------------------------------------------------------
432 // ModificationMode::ModificationMode - methods
433 // ---------------------------------------------------------------------------
434 
ModificationMode(GNESelectorFrame * selectorFrameParent)435 GNESelectorFrame::ModificationMode::ModificationMode(GNESelectorFrame* selectorFrameParent) :
436     FXGroupBox(selectorFrameParent->myContentFrame, "Modification Mode", GUIDesignGroupBoxFrame),
437     mySelectorFrameParent(selectorFrameParent),
438     myModificationModeType(SET_ADD) {
439     // Create all options buttons
440     myAddRadioButton = new FXRadioButton(this, "add\t\tSelected objects are added to the previous selection",
441                                          this, MID_CHOOSEN_OPERATION, GUIDesignRadioButton);
442     myRemoveRadioButton = new FXRadioButton(this, "remove\t\tSelected objects are removed from the previous selection",
443                                             this, MID_CHOOSEN_OPERATION, GUIDesignRadioButton);
444     myKeepRadioButton = new FXRadioButton(this, "keep\t\tRestrict previous selection by the current selection",
445                                           this, MID_CHOOSEN_OPERATION, GUIDesignRadioButton);
446     myReplaceRadioButton = new FXRadioButton(this, "replace\t\tReplace previous selection by the current selection",
447             this, MID_CHOOSEN_OPERATION, GUIDesignRadioButton);
448     myAddRadioButton->setCheck(true);
449 }
450 
451 
~ModificationMode()452 GNESelectorFrame::ModificationMode::~ModificationMode() {}
453 
454 
455 GNESelectorFrame::ModificationMode::SetOperation
getModificationMode() const456 GNESelectorFrame::ModificationMode::getModificationMode() const {
457     return myModificationModeType;
458 }
459 
460 
461 long
onCmdSelectModificationMode(FXObject * obj,FXSelector,void *)462 GNESelectorFrame::ModificationMode::onCmdSelectModificationMode(FXObject* obj, FXSelector, void*) {
463     if (obj == myAddRadioButton) {
464         myModificationModeType = SET_ADD;
465         myAddRadioButton->setCheck(true);
466         myRemoveRadioButton->setCheck(false);
467         myKeepRadioButton->setCheck(false);
468         myReplaceRadioButton->setCheck(false);
469         return 1;
470     } else if (obj == myRemoveRadioButton) {
471         myModificationModeType = SET_SUB;
472         myAddRadioButton->setCheck(false);
473         myRemoveRadioButton->setCheck(true);
474         myKeepRadioButton->setCheck(false);
475         myReplaceRadioButton->setCheck(false);
476         return 1;
477     } else if (obj == myKeepRadioButton) {
478         myModificationModeType = SET_RESTRICT;
479         myAddRadioButton->setCheck(false);
480         myRemoveRadioButton->setCheck(false);
481         myKeepRadioButton->setCheck(true);
482         myReplaceRadioButton->setCheck(false);
483         return 1;
484     } else if (obj == myReplaceRadioButton) {
485         myModificationModeType = SET_REPLACE;
486         myAddRadioButton->setCheck(false);
487         myRemoveRadioButton->setCheck(false);
488         myKeepRadioButton->setCheck(false);
489         myReplaceRadioButton->setCheck(true);
490         return 1;
491     } else {
492         return 0;
493     }
494 }
495 
496 // ---------------------------------------------------------------------------
497 // ModificationMode::ElementSet - methods
498 // ---------------------------------------------------------------------------
499 
ElementSet(GNESelectorFrame * selectorFrameParent)500 GNESelectorFrame::ElementSet::ElementSet(GNESelectorFrame* selectorFrameParent) :
501     FXGroupBox(selectorFrameParent->myContentFrame, "Element Set", GUIDesignGroupBoxFrame),
502     mySelectorFrameParent(selectorFrameParent),
503     myCurrentElementSet(ELEMENTSET_NETELEMENT) {
504     // Create MatchTagBox for tags and fill it
505     mySetComboBox = new FXComboBox(this, GUIDesignComboBoxNCol, this, MID_CHOOSEN_ELEMENTS, GUIDesignComboBox);
506 }
507 
508 
~ElementSet()509 GNESelectorFrame::ElementSet::~ElementSet() {}
510 
511 
512 GNESelectorFrame::ElementSet::ElementSetType
getElementSet() const513 GNESelectorFrame::ElementSet::getElementSet() const {
514     return myCurrentElementSet;
515 }
516 
517 
518 void
refreshElementSet()519 GNESelectorFrame::ElementSet::refreshElementSet() {
520     // first clear item
521     mySetComboBox->clearItems();
522     // now fill elements depending of supermode
523     if (mySelectorFrameParent->getViewNet()->getEditModes().currentSupermode == GNE_SUPERMODE_NETWORK) {
524         mySetComboBox->appendItem("Net Element");
525         mySetComboBox->appendItem("Additional");
526         mySetComboBox->appendItem("Shape");
527     } else {
528         mySetComboBox->appendItem("Demand Element");
529     }
530     mySetComboBox->setNumVisible(mySetComboBox->getNumItems());
531     // update rest of elements
532     onCmdSelectElementSet(0, 0, 0);
533 }
534 
535 
536 long
onCmdSelectElementSet(FXObject *,FXSelector,void *)537 GNESelectorFrame::ElementSet::onCmdSelectElementSet(FXObject*, FXSelector, void*) {
538     // check depending of current supermode
539     if (mySelectorFrameParent->getViewNet()->getEditModes().currentSupermode == GNE_SUPERMODE_NETWORK) {
540         if (mySetComboBox->getText() == "Net Element") {
541             myCurrentElementSet = ELEMENTSET_NETELEMENT;
542             mySetComboBox->setTextColor(FXRGB(0, 0, 0));
543             // enable match attribute
544             mySelectorFrameParent->myMatchAttribute->enableMatchAttribute();
545         } else if (mySetComboBox->getText() == "Additional") {
546             myCurrentElementSet = ELEMENTSET_ADDITIONAL;
547             mySetComboBox->setTextColor(FXRGB(0, 0, 0));
548             // enable match attribute
549             mySelectorFrameParent->myMatchAttribute->enableMatchAttribute();
550         } else if (mySetComboBox->getText() == "Shape") {
551             myCurrentElementSet = ELEMENTSET_SHAPE;
552             mySetComboBox->setTextColor(FXRGB(0, 0, 0));
553             // enable match attribute
554             mySelectorFrameParent->myMatchAttribute->enableMatchAttribute();
555         } else {
556             myCurrentElementSet = ELEMENTSET_INVALID;
557             mySetComboBox->setTextColor(FXRGB(255, 0, 0));
558             // disable match attribute
559             mySelectorFrameParent->myMatchAttribute->disableMatchAttribute();
560         }
561     } else {
562         if (mySetComboBox->getText() == "Demand Element") {
563             myCurrentElementSet = ELEMENTSET_DEMANDELEMENT;
564             mySetComboBox->setTextColor(FXRGB(0, 0, 0));
565             // enable match attribute
566             mySelectorFrameParent->myMatchAttribute->enableMatchAttribute();
567         } else {
568             myCurrentElementSet = ELEMENTSET_INVALID;
569             mySetComboBox->setTextColor(FXRGB(255, 0, 0));
570             // disable match attribute
571             mySelectorFrameParent->myMatchAttribute->disableMatchAttribute();
572         }
573     }
574     return 1;
575 }
576 
577 // ---------------------------------------------------------------------------
578 // ModificationMode::MatchAttribute - methods
579 // ---------------------------------------------------------------------------
580 
MatchAttribute(GNESelectorFrame * selectorFrameParent)581 GNESelectorFrame::MatchAttribute::MatchAttribute(GNESelectorFrame* selectorFrameParent) :
582     FXGroupBox(selectorFrameParent->myContentFrame, "Match Attribute", GUIDesignGroupBoxFrame),
583     mySelectorFrameParent(selectorFrameParent),
584     myCurrentTag(SUMO_TAG_EDGE),
585     myCurrentAttribute(SUMO_ATTR_ID) {
586     // Create MatchTagBox for tags
587     myMatchTagComboBox = new FXComboBox(this, GUIDesignComboBoxNCol, this, MID_GNE_SELECTORFRAME_SELECTTAG, GUIDesignComboBox);
588     // Create listBox for Attributes
589     myMatchAttrComboBox = new FXComboBox(this, GUIDesignComboBoxNCol, this, MID_GNE_SELECTORFRAME_SELECTATTRIBUTE, GUIDesignComboBox);
590     // Create TextField for Match string
591     myMatchString = new FXTextField(this, GUIDesignTextFieldNCol, this, MID_GNE_SELECTORFRAME_PROCESSSTRING, GUIDesignTextField);
592     // Create help button
593     new FXButton(this, "Help", nullptr, this, MID_HELP, GUIDesignButtonRectangular);
594     // Fill list of sub-items (first element will be "edge")
595     enableMatchAttribute();
596     // Set speed of edge as default attribute
597     myMatchAttrComboBox->setText("speed");
598     myCurrentAttribute = SUMO_ATTR_SPEED;
599     // Set default value for Match string
600     myMatchString->setText(">10.0");
601 }
602 
603 
~MatchAttribute()604 GNESelectorFrame::MatchAttribute::~MatchAttribute() {}
605 
606 
607 void
enableMatchAttribute()608 GNESelectorFrame::MatchAttribute::enableMatchAttribute() {
609     // enable comboboxes and text field
610     myMatchTagComboBox->enable();
611     myMatchAttrComboBox->enable();
612     myMatchString->enable();
613     // Clear items of myMatchTagComboBox
614     myMatchTagComboBox->clearItems();
615     // Set items depending of current item set
616     std::vector<SumoXMLTag> listOfTags;
617     if (mySelectorFrameParent->myElementSet->getElementSet() == ElementSet::ELEMENTSET_NETELEMENT) {
618         listOfTags = GNEAttributeCarrier::allowedTagsByCategory(GNEAttributeCarrier::TagType::TAGTYPE_NETELEMENT, true);
619     } else if (mySelectorFrameParent->myElementSet->getElementSet() == ElementSet::ELEMENTSET_ADDITIONAL) {
620         listOfTags = GNEAttributeCarrier::allowedTagsByCategory(GNEAttributeCarrier::TagType::TAGTYPE_ADDITIONAL | GNEAttributeCarrier::TagType::TAGTYPE_TAZ, true);
621     } else if (mySelectorFrameParent->myElementSet->getElementSet() == ElementSet::ELEMENTSET_SHAPE) {
622         listOfTags = GNEAttributeCarrier::allowedTagsByCategory(GNEAttributeCarrier::TagType::TAGTYPE_SHAPE, true);
623     } else if (mySelectorFrameParent->myElementSet->getElementSet() == ElementSet::ELEMENTSET_DEMANDELEMENT) {
624         listOfTags = GNEAttributeCarrier::allowedTagsByCategory(GNEAttributeCarrier::TagType::TAGTYPE_DEMANDELEMENT | GNEAttributeCarrier::TagType::TAGTYPE_STOP, true);
625     } else {
626         throw ProcessError("Invalid element set");
627     }
628     // fill combo box
629     for (auto i : listOfTags) {
630         myMatchTagComboBox->appendItem(toString(i).c_str());
631     }
632     // set first item as current item
633     myMatchTagComboBox->setCurrentItem(0);
634     myMatchTagComboBox->setNumVisible(myMatchTagComboBox->getNumItems());
635     // Fill attributes with the current element type
636     onCmdSelMBTag(nullptr, 0, nullptr);
637 }
638 
639 
640 void
disableMatchAttribute()641 GNESelectorFrame::MatchAttribute::disableMatchAttribute() {
642     // disable comboboxes and text field
643     myMatchTagComboBox->disable();
644     myMatchAttrComboBox->disable();
645     myMatchString->disable();
646     // change colors to black (even if there are invalid values)
647     myMatchTagComboBox->setTextColor(FXRGB(0, 0, 0));
648     myMatchAttrComboBox->setTextColor(FXRGB(0, 0, 0));
649     myMatchString->setTextColor(FXRGB(0, 0, 0));
650 }
651 
652 
653 long
onCmdSelMBTag(FXObject *,FXSelector,void *)654 GNESelectorFrame::MatchAttribute::onCmdSelMBTag(FXObject*, FXSelector, void*) {
655     // First check what type of elementes is being selected
656     myCurrentTag = SUMO_TAG_NOTHING;
657     // find current element tag
658     std::vector<SumoXMLTag> listOfTags;
659     if (mySelectorFrameParent->myElementSet->getElementSet() == ElementSet::ELEMENTSET_NETELEMENT) {
660         listOfTags = GNEAttributeCarrier::allowedTagsByCategory(GNEAttributeCarrier::TagType::TAGTYPE_NETELEMENT, true);
661     } else if (mySelectorFrameParent->myElementSet->getElementSet() == ElementSet::ELEMENTSET_ADDITIONAL) {
662         listOfTags = GNEAttributeCarrier::allowedTagsByCategory(GNEAttributeCarrier::TagType::TAGTYPE_ADDITIONAL | GNEAttributeCarrier::TagType::TAGTYPE_TAZ, true);
663     } else if (mySelectorFrameParent->myElementSet->getElementSet() == ElementSet::ELEMENTSET_SHAPE) {
664         listOfTags = GNEAttributeCarrier::allowedTagsByCategory(GNEAttributeCarrier::TagType::TAGTYPE_SHAPE, true);
665     } else if (mySelectorFrameParent->myElementSet->getElementSet() == ElementSet::ELEMENTSET_DEMANDELEMENT) {
666         listOfTags = GNEAttributeCarrier::allowedTagsByCategory(GNEAttributeCarrier::TagType::TAGTYPE_DEMANDELEMENT | GNEAttributeCarrier::TagType::TAGTYPE_STOP, true);
667     } else {
668         throw ProcessError("Unkown set");
669     }
670     // fill myMatchTagComboBox
671     for (auto i : listOfTags) {
672         if (toString(i) == myMatchTagComboBox->getText().text()) {
673             myCurrentTag = i;
674         }
675     }
676     // check that typed-by-user value is correct
677     if (myCurrentTag != SUMO_TAG_NOTHING) {
678         // obtain tag property (only for improve code legibility)
679         const auto& tagValue = GNEAttributeCarrier::getTagProperties(myCurrentTag);
680         // set color and enable items
681         myMatchTagComboBox->setTextColor(FXRGB(0, 0, 0));
682         myMatchAttrComboBox->enable();
683         myMatchString->enable();
684         myMatchAttrComboBox->clearItems();
685         // fill attribute combo box
686         for (auto it : tagValue) {
687             myMatchAttrComboBox->appendItem(toString(it.first).c_str());
688         }
689         // Add extra attribute "generic"
690         myMatchAttrComboBox->appendItem(toString(GNE_ATTR_GENERIC).c_str());
691         // check if item can block movement
692         if (tagValue.canBlockMovement()) {
693             myMatchAttrComboBox->appendItem(toString(GNE_ATTR_BLOCK_MOVEMENT).c_str());
694         }
695         // check if item can block shape
696         if (tagValue.canBlockShape()) {
697             myMatchAttrComboBox->appendItem(toString(GNE_ATTR_BLOCK_SHAPE).c_str());
698         }
699         // check if item can close shape
700         if (tagValue.canCloseShape()) {
701             myMatchAttrComboBox->appendItem(toString(GNE_ATTR_CLOSE_SHAPE).c_str());
702         }
703         // check if item can have parent
704         if (tagValue.hasParent()) {
705             myMatchAttrComboBox->appendItem(toString(GNE_ATTR_PARENT).c_str());
706         }
707         // @ToDo: Here can be placed a button to set the default value
708         myMatchAttrComboBox->setNumVisible(myMatchAttrComboBox->getNumItems());
709         onCmdSelMBAttribute(nullptr, 0, nullptr);
710     } else {
711         // change color to red and disable items
712         myMatchTagComboBox->setTextColor(FXRGB(255, 0, 0));
713         myMatchAttrComboBox->disable();
714         myMatchString->disable();
715     }
716     update();
717     return 1;
718 }
719 
720 
721 long
onCmdSelMBAttribute(FXObject *,FXSelector,void *)722 GNESelectorFrame::MatchAttribute::onCmdSelMBAttribute(FXObject*, FXSelector, void*) {
723     // first obtain a copy of item attributes vinculated with current tag
724     auto tagPropertiesCopy = GNEAttributeCarrier::getTagProperties(myCurrentTag);
725     // obtain tag property (only for improve code legibility)
726     const auto& tagValue = GNEAttributeCarrier::getTagProperties(myCurrentTag);
727     // add an extra AttributeValues to allow select ACs using as criterium "generic parameters"
728     GNEAttributeCarrier::AttributeProperties extraAttrProperty;
729     extraAttrProperty = GNEAttributeCarrier::AttributeProperties(GNE_ATTR_GENERIC,
730                         GNEAttributeCarrier::AttrProperty::ATTRPROPERTY_STRING,
731                         "Generic Parameters");
732     tagPropertiesCopy.addAttribute(extraAttrProperty);
733     // add extra attribute if item can block movement
734     if (tagValue.canBlockMovement()) {
735         // add an extra AttributeValues to allow select ACs using as criterium "block movement"
736         extraAttrProperty = GNEAttributeCarrier::AttributeProperties(GNE_ATTR_BLOCK_MOVEMENT,
737                             GNEAttributeCarrier::AttrProperty::ATTRPROPERTY_BOOL | GNEAttributeCarrier::AttrProperty::ATTRPROPERTY_DEFAULTVALUE,
738                             "Block movement",
739                             "false");
740         tagPropertiesCopy.addAttribute(extraAttrProperty);
741     }
742     // add extra attribute if item can block shape
743     if (tagValue.canBlockShape()) {
744         // add an extra AttributeValues to allow select ACs using as criterium "block shape"
745         extraAttrProperty = GNEAttributeCarrier::AttributeProperties(GNE_ATTR_BLOCK_SHAPE,
746                             GNEAttributeCarrier::AttrProperty::ATTRPROPERTY_BOOL | GNEAttributeCarrier::AttrProperty::ATTRPROPERTY_DEFAULTVALUE,
747                             "Block shape",
748                             "false");
749         tagPropertiesCopy.addAttribute(extraAttrProperty);
750     }
751     // add extra attribute if item can close shape
752     if (tagValue.canCloseShape()) {
753         // add an extra AttributeValues to allow select ACs using as criterium "close shape"
754         extraAttrProperty = GNEAttributeCarrier::AttributeProperties(GNE_ATTR_CLOSE_SHAPE,
755                             GNEAttributeCarrier::AttrProperty::ATTRPROPERTY_BOOL | GNEAttributeCarrier::AttrProperty::ATTRPROPERTY_DEFAULTVALUE,
756                             "Close shape",
757                             "true");
758         tagPropertiesCopy.addAttribute(extraAttrProperty);
759     }
760     // add extra attribute if item can have parent
761     if (tagValue.hasParent()) {
762         // add an extra AttributeValues to allow select ACs using as criterium "parent"
763         extraAttrProperty = GNEAttributeCarrier::AttributeProperties(GNE_ATTR_PARENT,
764                             GNEAttributeCarrier::AttrProperty::ATTRPROPERTY_STRING,
765                             "Parent element");
766         tagPropertiesCopy.addAttribute(extraAttrProperty);
767     }
768     // set current selected attribute
769     myCurrentAttribute = SUMO_ATTR_NOTHING;
770     for (auto i : tagPropertiesCopy) {
771         if (toString(i.first) == myMatchAttrComboBox->getText().text()) {
772             myCurrentAttribute = i.first;
773         }
774     }
775     // check if selected attribute is valid
776     if (myCurrentAttribute != SUMO_ATTR_NOTHING) {
777         myMatchAttrComboBox->setTextColor(FXRGB(0, 0, 0));
778         myMatchString->enable();
779     } else {
780         myMatchAttrComboBox->setTextColor(FXRGB(255, 0, 0));
781         myMatchString->disable();
782     }
783     return 1;
784 }
785 
786 
787 long
onCmdSelMBString(FXObject *,FXSelector,void *)788 GNESelectorFrame::MatchAttribute::onCmdSelMBString(FXObject*, FXSelector, void*) {
789     // obtain expresion
790     std::string expr(myMatchString->getText().text());
791     const auto& tagValue = GNEAttributeCarrier::getTagProperties(myCurrentTag);
792     bool valid = true;
793     if (expr == "") {
794         // the empty expression matches all objects
795         mySelectorFrameParent->handleIDs(mySelectorFrameParent->getMatches(myCurrentTag, myCurrentAttribute, '@', 0, expr));
796     } else if (tagValue.hasAttribute(myCurrentAttribute) && tagValue.getAttributeProperties(myCurrentAttribute).isNumerical()) {
797         // The expression must have the form
798         //  <val matches if attr < val
799         //  >val matches if attr > val
800         //  =val matches if attr = val
801         //  val matches if attr = val
802         char compOp = expr[0];
803         if (compOp == '<' || compOp == '>' || compOp == '=') {
804             expr = expr.substr(1);
805         } else {
806             compOp = '=';
807         }
808         // check if value can be parsed to double
809         if (GNEAttributeCarrier::canParse<double>(expr.c_str())) {
810             mySelectorFrameParent->handleIDs(mySelectorFrameParent->getMatches(myCurrentTag, myCurrentAttribute, compOp, GNEAttributeCarrier::parse<double>(expr.c_str()), expr));
811         } else {
812             valid = false;
813         }
814     } else {
815         // The expression must have the form
816         //   =str: matches if <str> is an exact match
817         //   !str: matches if <str> is not a substring
818         //   ^str: matches if <str> is not an exact match
819         //   str: matches if <str> is a substring (sends compOp '@')
820         // Alternatively, if the expression is empty it matches all objects
821         char compOp = expr[0];
822         if (compOp == '=' || compOp == '!' || compOp == '^') {
823             expr = expr.substr(1);
824         } else {
825             compOp = '@';
826         }
827         mySelectorFrameParent->handleIDs(mySelectorFrameParent->getMatches(myCurrentTag, myCurrentAttribute, compOp, 0, expr));
828     }
829     if (valid) {
830         myMatchString->setTextColor(FXRGB(0, 0, 0));
831         myMatchString->killFocus();
832     } else {
833         myMatchString->setTextColor(FXRGB(255, 0, 0));
834     }
835     return 1;
836 }
837 
838 
839 long
onCmdHelp(FXObject *,FXSelector,void *)840 GNESelectorFrame::MatchAttribute::onCmdHelp(FXObject*, FXSelector, void*) {
841     // Create dialog box
842     FXDialogBox* additionalNeteditAttributesHelpDialog = new FXDialogBox(this, "Netedit Parameters Help", GUIDesignDialogBox);
843     additionalNeteditAttributesHelpDialog->setIcon(GUIIconSubSys::getIcon(ICON_MODEADDITIONAL));
844     // set help text
845     std::ostringstream help;
846     help
847             << "- The 'Match Attribute' controls allow to specify a set of objects which are then applied to the current selection\n"
848             << "  according to the current 'Modification Mode'.\n"
849             << "     1. Select an object type from the first input box\n"
850             << "     2. Select an attribute from the second input box\n"
851             << "     3. Enter a 'match expression' in the third input box and press <return>\n"
852             << "\n"
853             << "- The empty expression matches all objects\n"
854             << "- For numerical attributes the match expression must consist of a comparison operator ('<', '>', '=') and a number.\n"
855             << "- An object matches if the comparison between its attribute and the given number by the given operator evaluates to 'true'\n"
856             << "\n"
857             << "- For string attributes the match expression must consist of a comparison operator ('', '=', '!', '^') and a string.\n"
858             << "     '' (no operator) matches if string is a substring of that object'ts attribute.\n"
859             << "     '=' matches if string is an exact match.\n"
860             << "     '!' matches if string is not a substring.\n"
861             << "     '^' matches if string is not an exact match.\n"
862             << "\n"
863             << "- Examples:\n"
864             << "     junction; id; 'foo' -> match all junctions that have 'foo' in their id\n"
865             << "     junction; type; '=priority' -> match all junctions of type 'priority', but not of type 'priority_stop'\n"
866             << "     edge; speed; '>10' -> match all edges with a speed above 10\n";
867     // Create label with the help text
868     new FXLabel(additionalNeteditAttributesHelpDialog, help.str().c_str(), nullptr, GUIDesignLabelFrameInformation);
869     // Create horizontal separator
870     new FXHorizontalSeparator(additionalNeteditAttributesHelpDialog, GUIDesignHorizontalSeparator);
871     // Create frame for OK Button
872     FXHorizontalFrame* myHorizontalFrameOKButton = new FXHorizontalFrame(additionalNeteditAttributesHelpDialog, GUIDesignAuxiliarHorizontalFrame);
873     // Create Button Close (And two more horizontal frames to center it)
874     new FXHorizontalFrame(myHorizontalFrameOKButton, GUIDesignAuxiliarHorizontalFrame);
875     new FXButton(myHorizontalFrameOKButton, "OK\t\tclose", GUIIconSubSys::getIcon(ICON_ACCEPT), additionalNeteditAttributesHelpDialog, FXDialogBox::ID_ACCEPT, GUIDesignButtonOK);
876     new FXHorizontalFrame(myHorizontalFrameOKButton, GUIDesignAuxiliarHorizontalFrame);
877     // Write Warning in console if we're in testing mode
878     WRITE_DEBUG("Opening help dialog of selector frame");
879     // create Dialog
880     additionalNeteditAttributesHelpDialog->create();
881     // show in the given position
882     additionalNeteditAttributesHelpDialog->show(PLACEMENT_CURSOR);
883     // refresh APP
884     getApp()->refresh();
885     // open as modal dialog (will block all windows until stop() or stopModal() is called)
886     getApp()->runModalFor(additionalNeteditAttributesHelpDialog);
887     // Write Warning in console if we're in testing mode
888     WRITE_DEBUG("Close help dialog of selector frame");
889     return 1;
890 }
891 
892 // ---------------------------------------------------------------------------
893 // ModificationMode::VisualScaling - methods
894 // ---------------------------------------------------------------------------
895 
VisualScaling(GNESelectorFrame * selectorFrameParent)896 GNESelectorFrame::VisualScaling::VisualScaling(GNESelectorFrame* selectorFrameParent) :
897     FXGroupBox(selectorFrameParent->myContentFrame, "Visual Scaling", GUIDesignGroupBoxFrame),
898     mySelectorFrameParent(selectorFrameParent) {
899     // Create spin button and configure it
900     mySelectionScaling = new FXRealSpinner(this, 7, this, MID_GNE_SELECTORFRAME_SELECTSCALE, GUIDesignSpinDial);
901     //mySelectionScaling->setNumberFormat(1);
902     //mySelectionScaling->setIncrements(0.1, .5, 1);
903     mySelectionScaling->setIncrement(0.5);
904     mySelectionScaling->setRange(1, 100000);
905     mySelectionScaling->setValue(1);
906     mySelectionScaling->setHelpText("Enlarge selected objects");
907 }
908 
909 
~VisualScaling()910 GNESelectorFrame::VisualScaling::~VisualScaling() {}
911 
912 
913 long
onCmdScaleSelection(FXObject *,FXSelector,void *)914 GNESelectorFrame::VisualScaling::onCmdScaleSelection(FXObject*, FXSelector, void*) {
915     // set scale in viewnet
916     mySelectorFrameParent->getViewNet()->setSelectionScaling(mySelectionScaling->getValue());
917     mySelectorFrameParent->getViewNet()->update();
918     return 1;
919 }
920 
921 // ---------------------------------------------------------------------------
922 // ModificationMode::SelectionOperation - methods
923 // ---------------------------------------------------------------------------
924 
SelectionOperation(GNESelectorFrame * selectorFrameParent)925 GNESelectorFrame::SelectionOperation::SelectionOperation(GNESelectorFrame* selectorFrameParent) :
926     FXGroupBox(selectorFrameParent->myContentFrame, "Operations for selections", GUIDesignGroupBoxFrame),
927     mySelectorFrameParent(selectorFrameParent) {
928     // Create "Clear List" Button
929     new FXButton(this, "Clear\t\t", nullptr, this, MID_CHOOSEN_CLEAR, GUIDesignButton);
930     // Create "Invert" Button
931     new FXButton(this, "Invert\t\t", nullptr, this, MID_CHOOSEN_INVERT, GUIDesignButton);
932     // Create "Save" Button
933     new FXButton(this, "Save\t\tSave ids of currently selected objects to a file.", nullptr, this, MID_CHOOSEN_SAVE, GUIDesignButton);
934     // Create "Load" Button
935     new FXButton(this, "Load\t\tLoad ids from a file according to the current modfication mode.", nullptr, this, MID_CHOOSEN_LOAD, GUIDesignButton);
936 }
937 
938 
~SelectionOperation()939 GNESelectorFrame::SelectionOperation::~SelectionOperation() {}
940 
941 
942 long
onCmdLoad(FXObject *,FXSelector,void *)943 GNESelectorFrame::SelectionOperation::onCmdLoad(FXObject*, FXSelector, void*) {
944     // get the new file name
945     FXFileDialog opendialog(this, "Open List of Selected Items");
946     opendialog.setIcon(GUIIconSubSys::getIcon(ICON_EMPTY));
947     opendialog.setSelectMode(SELECTFILE_EXISTING);
948     opendialog.setPatternList("Selection files (*.txt)\nAll files (*)");
949     if (gCurrentFolder.length() != 0) {
950         opendialog.setDirectory(gCurrentFolder);
951     }
952     if (opendialog.execute()) {
953         std::vector<GNEAttributeCarrier*> loadedACs;
954         gCurrentFolder = opendialog.getDirectory();
955         std::string file = opendialog.getFilename().text();
956         std::ostringstream msg;
957         std::ifstream strm(file.c_str());
958         // check if file can be opened
959         if (!strm.good()) {
960             WRITE_ERROR("Could not open '" + file + "'.");
961             return 0;
962         }
963         while (strm.good()) {
964             std::string line;
965             strm >> line;
966             // check if line isn't empty
967             if (line.length() != 0) {
968                 // obtain GLObject
969                 GUIGlObject* object = GUIGlObjectStorage::gIDStorage.getObjectBlocking(line);
970                 // check if GUIGlObject exist and their  their GL type isn't blocked
971                 if ((object != nullptr) && !mySelectorFrameParent->myLockGLObjectTypes->IsObjectTypeLocked(object->getType())) {
972                     // obtain GNEAttributeCarrier
973                     GNEAttributeCarrier* AC = mySelectorFrameParent->getViewNet()->getNet()->retrieveAttributeCarrier(object->getGlID(), false);
974                     // check if AC exist and if is selectable
975                     if (AC && AC->getTagProperty().isSelectable())
976                         // now check if we're in the correct supermode to load this element
977                         if (((mySelectorFrameParent->getViewNet()->getEditModes().currentSupermode == GNE_SUPERMODE_NETWORK) && !AC->getTagProperty().isDemandElement()) ||
978                                 ((mySelectorFrameParent->getViewNet()->getEditModes().currentSupermode == GNE_SUPERMODE_DEMAND) && AC->getTagProperty().isDemandElement())) {
979                             loadedACs.push_back(AC);
980                         }
981                 }
982             }
983         }
984         // change selected attribute in loaded ACs allowing undo/redo
985         if (loadedACs.size() > 0) {
986             mySelectorFrameParent->getViewNet()->getUndoList()->p_begin("load selection");
987             mySelectorFrameParent->handleIDs(loadedACs);
988             mySelectorFrameParent->getViewNet()->getUndoList()->p_end();
989         }
990     }
991     mySelectorFrameParent->getViewNet()->update();
992     return 1;
993 }
994 
995 
996 long
onCmdSave(FXObject *,FXSelector,void *)997 GNESelectorFrame::SelectionOperation::onCmdSave(FXObject*, FXSelector, void*) {
998     FXString file = MFXUtils::getFilename2Write(
999                         this, "Save List of selected Items", ".txt", GUIIconSubSys::getIcon(ICON_EMPTY), gCurrentFolder);
1000     if (file == "") {
1001         return 1;
1002     }
1003     try {
1004         OutputDevice& dev = OutputDevice::getDevice(file.text());
1005         for (auto i : mySelectorFrameParent->myViewNet->getNet()->getSelectedAttributeCarriers(false)) {
1006             GUIGlObject* object = dynamic_cast<GUIGlObject*>(i);
1007             if (object) {
1008                 dev << GUIGlObject::TypeNames.getString(object->getType()) << ":" << i->getID() << "\n";
1009             }
1010         }
1011         dev.close();
1012     } catch (IOError& e) {
1013         // write warning if netedit is running in testing mode
1014         WRITE_DEBUG("Opening FXMessageBox 'error storing selection'");
1015         // open message box error
1016         FXMessageBox::error(this, MBOX_OK, "Storing Selection failed", "%s", e.what());
1017         // write warning if netedit is running in testing mode
1018         WRITE_DEBUG("Closed FXMessageBox 'error storing selection' with 'OK'");
1019     }
1020     return 1;
1021 }
1022 
1023 
1024 long
onCmdClear(FXObject *,FXSelector,void *)1025 GNESelectorFrame::SelectionOperation::onCmdClear(FXObject*, FXSelector, void*) {
1026     // clear current selection
1027     mySelectorFrameParent->clearCurrentSelection();
1028     return 1;
1029 }
1030 
1031 
1032 long
onCmdInvert(FXObject *,FXSelector,void *)1033 GNESelectorFrame::SelectionOperation::onCmdInvert(FXObject*, FXSelector, void*) {
1034     // first make a copy of current selected elements
1035     std::vector<GNEAttributeCarrier*> copyOfSelectedAC = mySelectorFrameParent->getViewNet()->getNet()->getSelectedAttributeCarriers(false);
1036     // for invert selection, first clean current selection and next select elements of set "unselectedElements"
1037     mySelectorFrameParent->getViewNet()->getUndoList()->p_begin("invert selection");
1038     // select junctions, edges, lanes connections and crossings
1039     std::vector<GNEJunction*> junctions = mySelectorFrameParent->getViewNet()->getNet()->retrieveJunctions();
1040     for (auto i : junctions) {
1041         i->setAttribute(GNE_ATTR_SELECTED, "true", mySelectorFrameParent->getViewNet()->getUndoList());
1042         // due we iterate over all junctions, only it's neccesary iterate over incoming edges
1043         for (auto j : i->getGNEIncomingEdges()) {
1044             // only select edges if "select edges" flag is enabled. In other case, select only lanes
1045             if (mySelectorFrameParent->getViewNet()->getViewOptions().selectEdges()) {
1046                 j->setAttribute(GNE_ATTR_SELECTED, "true", mySelectorFrameParent->getViewNet()->getUndoList());
1047             } else {
1048                 for (auto k : j->getLanes()) {
1049                     k->setAttribute(GNE_ATTR_SELECTED, "true", mySelectorFrameParent->getViewNet()->getUndoList());
1050                 }
1051             }
1052             // select connections
1053             for (auto k : j->getGNEConnections()) {
1054                 k->setAttribute(GNE_ATTR_SELECTED, "true", mySelectorFrameParent->getViewNet()->getUndoList());
1055             }
1056         }
1057         // select crossings
1058         for (auto j : i->getGNECrossings()) {
1059             j->setAttribute(GNE_ATTR_SELECTED, "true", mySelectorFrameParent->getViewNet()->getUndoList());
1060         }
1061     }
1062     // select additionals
1063     std::vector<GNEAdditional*> additionals = mySelectorFrameParent->getViewNet()->getNet()->retrieveAdditionals();
1064     for (auto i : additionals) {
1065         if (i->getTagProperty().isSelectable()) {
1066             i->setAttribute(GNE_ATTR_SELECTED, "true", mySelectorFrameParent->getViewNet()->getUndoList());
1067         }
1068     }
1069     // select polygons
1070     for (auto i : mySelectorFrameParent->getViewNet()->getNet()->getPolygons()) {
1071         dynamic_cast<GNEPoly*>(i.second)->setAttribute(GNE_ATTR_SELECTED, "true", mySelectorFrameParent->getViewNet()->getUndoList());
1072     }
1073     // select POIs
1074     for (auto i : mySelectorFrameParent->getViewNet()->getNet()->getPOIs()) {
1075         dynamic_cast<GNEPOI*>(i.second)->setAttribute(GNE_ATTR_SELECTED, "true", mySelectorFrameParent->getViewNet()->getUndoList());
1076     }
1077     // now iterate over all elements of "copyOfSelectedAC" and undselect it
1078     for (auto i : copyOfSelectedAC) {
1079         i->setAttribute(GNE_ATTR_SELECTED, "false", mySelectorFrameParent->getViewNet()->getUndoList());
1080     }
1081     // finish selection operation
1082     mySelectorFrameParent->getViewNet()->getUndoList()->p_end();
1083     // update view
1084     mySelectorFrameParent->getViewNet()->update();
1085     return 1;
1086 }
1087 
1088 
1089 /****************************************************************************/
1090