Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netedit/dialogs/options/GNEOptionsEditor.cpp
193874 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4
// This program and the accompanying materials are made available under the
5
// terms of the Eclipse Public License 2.0 which is available at
6
// https://www.eclipse.org/legal/epl-2.0/
7
// This Source Code may also be made available under the following Secondary
8
// Licenses when the conditions for such availability set forth in the Eclipse
9
// Public License 2.0 are satisfied: GNU General Public License, version 2
10
// or later which is available at
11
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
/****************************************************************************/
14
/// @file GNEOptionsEditor.cpp
15
/// @author Pablo Alvarez Lopez
16
/// @date May 2023
17
///
18
// A Dialog for setting options (see OptionsCont)
19
/****************************************************************************/
20
#include <config.h>
21
22
#include <fstream>
23
24
#include <netedit/dialogs/GNEDialog.h>
25
#include <netedit/GNEApplicationWindow.h>
26
#include <netedit/GNEViewNet.h>
27
#include <netedit/GNEViewParent.h>
28
#include <utils/foxtools/MFXCheckButtonTooltip.h>
29
#include <netedit/frames/common/GNEGroupBoxModule.h>
30
#include <utils/foxtools/MFXStaticToolTip.h>
31
#include <utils/foxtools/MFXTextFieldSearch.h>
32
#include <utils/gui/div/GUIDesigns.h>
33
#include <utils/options/OptionsLoader.h>
34
#include <xercesc/parsers/SAXParser.hpp>
35
36
#include "GNEOptionsEditor.h"
37
38
// ===========================================================================
39
// Defines
40
// ===========================================================================
41
42
#define TREELISTWIDTH 200
43
44
// ===========================================================================
45
// FOX callback mapping
46
// ===========================================================================
47
48
FXDEFMAP(GNEOptionsEditor) GNEOptionsEditorMap[] = {
49
FXMAPFUNC(SEL_COMMAND, MID_GNE_SELECT, GNEOptionsEditor::onCmdSelectTopic),
50
FXMAPFUNC(SEL_COMMAND, MID_GNE_SEARCH_USEDESCRIPTION, GNEOptionsEditor::onCmdSearch),
51
FXMAPFUNC(SEL_COMMAND, MID_MTEXTFIELDSEARCH_UPDATED, GNEOptionsEditor::onCmdSearch),
52
FXMAPFUNC(SEL_COMMAND, MID_SHOWTOOLTIPS_MENU, GNEOptionsEditor::onCmdShowToolTipsMenu),
53
FXMAPFUNC(SEL_COMMAND, MID_CHOOSEN_SAVE, GNEOptionsEditor::onCmdSaveOptions),
54
FXMAPFUNC(SEL_COMMAND, MID_CHOOSEN_LOAD, GNEOptionsEditor::onCmdLoadOptions),
55
FXMAPFUNC(SEL_COMMAND, MID_GNE_BUTTON_DEFAULT, GNEOptionsEditor::onCmdResetDefault),
56
};
57
58
// Object implementation
59
FXIMPLEMENT(GNEOptionsEditor, FXVerticalFrame, GNEOptionsEditorMap, ARRAYNUMBER(GNEOptionsEditorMap))
60
61
// ===========================================================================
62
// method definitions
63
// ===========================================================================
64
65
GNEOptionsEditor::GNEOptionsEditor(GNEDialog* dialog, const std::string& titleName, OptionsCont& optionsContainer,
66
const OptionsCont& originalOptionsContainer) :
67
FXVerticalFrame(dialog->getContentFrame(), GUIDesignAuxiliarFrame),
68
myDialog(dialog),
69
myOptionsContainer(optionsContainer),
70
myCopyOfOptionsContainer(optionsContainer.clone()),
71
myOriginalOptionsContainer(originalOptionsContainer) {
72
// get staticTooltipMenu
73
auto staticTooltipMenu = dialog->getApplicationWindow()->getStaticTooltipMenu();
74
// add buttons frame
75
FXHorizontalFrame* buttonsFrame = new FXHorizontalFrame(this, GUIDesignHorizontalFrameNoPadding);
76
myShowToolTipsMenu = new MFXCheckableButton(false, buttonsFrame, staticTooltipMenu,
77
(std::string("\t") + TL("Toggle Menu Tooltips") + std::string("\t") + TL("Toggles whether tooltips in the menu shall be shown.")).c_str(),
78
GUIIconSubSys::getIcon(GUIIcon::SHOWTOOLTIPS_MENU), this, MID_SHOWTOOLTIPS_MENU, GUIDesignMFXCheckableButtonSquare);
79
auto saveFile = new MFXButtonTooltip(buttonsFrame, staticTooltipMenu, TL("Save options"),
80
GUIIconSubSys::getIcon(GUIIcon::SAVE), this, MID_CHOOSEN_SAVE, GUIDesignButtonConfiguration);
81
saveFile->setTipText(TL("Save configuration file"));
82
auto loadFile = new MFXButtonTooltip(buttonsFrame, staticTooltipMenu, TL("Load options"),
83
GUIIconSubSys::getIcon(GUIIcon::OPEN), this, MID_CHOOSEN_LOAD, GUIDesignButtonConfiguration);
84
loadFile->setTipText(TL("Load configuration file"));
85
auto resetDefault = new MFXButtonTooltip(buttonsFrame, staticTooltipMenu, TL("Default options"),
86
GUIIconSubSys::getIcon(GUIIcon::RESET), this, MID_GNE_BUTTON_DEFAULT, GUIDesignButtonConfiguration);
87
resetDefault->setTipText(TL("Reset all options to default"));
88
// add separator
89
new FXSeparator(this);
90
// create elements frame
91
FXHorizontalFrame* elementsFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarFrame);
92
FXVerticalFrame* elementsFrameTree = new FXVerticalFrame(elementsFrame, GUIDesignAuxiliarVerticalFrame);
93
FXVerticalFrame* elementsFrameValues = new FXVerticalFrame(elementsFrame, GUIDesignAuxiliarFrame);
94
// Create GroupBox modules
95
GNEGroupBoxModule* groupBoxTree = new GNEGroupBoxModule(elementsFrameTree, TL("Topics"));
96
GNEGroupBoxModule* groupBoxOptions = new GNEGroupBoxModule(elementsFrameValues, TL("Options"));
97
// create FXTreeList
98
myTopicsTreeList = new FXTreeList(groupBoxTree->getCollapsableFrame(), this, MID_GNE_SELECT, GUIDesignTreeListFixedWidth);
99
myTopicsTreeList->setWidth(TREELISTWIDTH);
100
// add root item
101
myRootItem = myTopicsTreeList->appendItem(nullptr, titleName.c_str());
102
myRootItem->setExpanded(TRUE);
103
// create scroll
104
FXScrollWindow* scrollTabEntries = new FXScrollWindow(groupBoxOptions->getCollapsableFrame(), LAYOUT_FILL_X | LAYOUT_FILL_Y);
105
// create vertical frame for entries
106
myEntriesFrame = new FXVerticalFrame(scrollTabEntries, GUIDesignAuxiliarFrame);
107
// iterate over all topics
108
for (const auto& topic : myOptionsContainer.getSubTopics()) {
109
// check if we have to ignore this topic
110
if (myIgnoredTopics.count(topic) == 0) {
111
// add topic into myTreeItemTopics and tree
112
myTreeItemTopics[myTopicsTreeList->appendItem(myRootItem, topic.c_str())] = topic;
113
// iterate over entries
114
const std::vector<std::string> entries = myOptionsContainer.getSubTopicsEntries(topic);
115
for (const auto& entry : entries) {
116
// check if we have to ignore this entry
117
if (myIgnoredEntries.count(entry) == 0) {
118
// get type
119
const std::string type = myOptionsContainer.getTypeName(entry);
120
// get description
121
const std::string description = myOptionsContainer.getDescription(entry);
122
// get default value
123
const std::string defaultValue = myOptionsContainer.getValueString(entry);
124
// check if is editable
125
const bool editable = myOptionsContainer.isEditable(entry);
126
// continue depending of type
127
if (type == "STR") {
128
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionString(this, myEntriesFrame, topic, entry, description, defaultValue, editable));
129
} else if (type == "TIME") {
130
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionTime(this, myEntriesFrame, topic, entry, description, defaultValue, editable));
131
} else if ((type == "FILE") || (type == "NETWORK") || (type == "ADDITIONAL") || (type == "ROUTE") || (type == "DATA")) {
132
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionFilename(this, myEntriesFrame, topic, entry, description, defaultValue, editable));
133
} else if (type == "BOOL") {
134
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionBool(this, myEntriesFrame, topic, entry, description, defaultValue, editable));
135
} else if (type == "INT") {
136
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionInt(this, myEntriesFrame, topic, entry, description, defaultValue, editable));
137
} else if (type == "FLOAT") {
138
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionFloat(this, myEntriesFrame, topic, entry, description, defaultValue, editable));
139
} else if (type == "INT[]") {
140
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionIntVector(this, myEntriesFrame, topic, entry, description, defaultValue, editable));
141
} else if (type == "STR[]") {
142
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionStringVector(this, myEntriesFrame, topic, entry, description, defaultValue, editable));
143
}
144
}
145
}
146
}
147
}
148
// create search elements
149
FXHorizontalFrame* searchFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
150
new FXLabel(searchFrame, TL("Search"), nullptr, GUIDesignLabelThickedFixed(TREELISTWIDTH - GUIDesignHeight + 14));
151
myDescriptionSearchCheckButton = new MFXCheckButtonTooltip(searchFrame, staticTooltipMenu, "", this, MID_GNE_SEARCH_USEDESCRIPTION, GUIDesignCheckButtonThick);
152
myDescriptionSearchCheckButton->setToolTipText(TL("Include description in search"));
153
mySearchButton = new MFXTextFieldSearch(searchFrame, staticTooltipMenu, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextField);
154
// after creation, adjust entries name sizes
155
for (const auto& entry : myOptionRowEntries) {
156
entry->adjustNameSize();
157
}
158
// set myShowToolTipsMenu
159
myShowToolTipsMenu->setChecked(getApp()->reg().readIntEntry("gui", "menuToolTips", 0) != 1);
160
}
161
162
163
GNEOptionsEditor::~GNEOptionsEditor() {
164
delete myCopyOfOptionsContainer;
165
}
166
167
168
void
169
GNEOptionsEditor::runInternalTest(const InternalTestStep::DialogArgument* /*dialogArgument*/) {
170
// not finished yet
171
}
172
173
174
bool
175
GNEOptionsEditor::isOptionModified() const {
176
return myOptionsModified;
177
}
178
179
void
180
GNEOptionsEditor::resetAllOptions() {
181
for (const auto& entry : myOptionRowEntries) {
182
entry->onCmdResetOption(nullptr, 0, nullptr);
183
}
184
myOptionsModified = false;
185
}
186
187
188
long
189
GNEOptionsEditor::onCmdSelectTopic(FXObject*, FXSelector, void*) {
190
if (mySearchButton->getText().count() == 0) {
191
updateVisibleEntriesByTopic();
192
}
193
return 1;
194
}
195
196
197
long
198
GNEOptionsEditor::onCmdSearch(FXObject*, FXSelector, void*) {
199
if (mySearchButton->getText().count() > 0) {
200
updateVisibleEntriesBySearch(mySearchButton->getText().text());
201
} else {
202
updateVisibleEntriesByTopic();
203
}
204
return 1;
205
}
206
207
208
long
209
GNEOptionsEditor::onCmdShowToolTipsMenu(FXObject*, FXSelector, void*) {
210
// get staticTooltipMenu
211
auto viewNet = myDialog->getApplicationWindow()->getViewNet();
212
// toggle check
213
myShowToolTipsMenu->setChecked(!myShowToolTipsMenu->amChecked());
214
if (viewNet) {
215
viewNet->getViewParent()->getShowToolTipsMenu()->setChecked(myShowToolTipsMenu->amChecked());
216
viewNet->getViewParent()->getShowToolTipsMenu()->update();
217
}
218
// enable/disable static tooltip
219
myDialog->getApplicationWindow()->getStaticTooltipMenu()->enableStaticToolTip(myShowToolTipsMenu->amChecked());
220
// save in registry
221
getApp()->reg().writeIntEntry("gui", "menuToolTips", myShowToolTipsMenu->amChecked() ? 0 : 1);
222
update();
223
224
return 1;
225
}
226
227
228
long
229
GNEOptionsEditor::onCmdSaveOptions(FXObject*, FXSelector, void*) {
230
// open file dialog
231
const GNEFileDialog optionsFileDialog(myDialog->getApplicationWindow(), myDialog,
232
TL("options file"),
233
SUMOXMLDefinitions::XMLFileExtensions.getStrings(),
234
GNEFileDialog::OpenMode::SAVE,
235
GNEFileDialog::ConfigType::NETEDIT);
236
// check file
237
if (optionsFileDialog.getResult() == GNEDialog::Result::ACCEPT) {
238
std::ofstream out(StringUtils::transcodeToLocal(optionsFileDialog.getFilename()));
239
myOptionsContainer.writeConfiguration(out, true, false, false, optionsFileDialog.getFilename(), true);
240
out.close();
241
}
242
return 1;
243
}
244
245
246
long
247
GNEOptionsEditor::onCmdLoadOptions(FXObject*, FXSelector, void*) {
248
// open file dialog
249
const GNEFileDialog optionsFileDialog(myDialog->getApplicationWindow(), myDialog,
250
TL("options file"),
251
SUMOXMLDefinitions::XMLFileExtensions.getStrings(),
252
GNEFileDialog::OpenMode::LOAD_SINGLE,
253
GNEFileDialog::ConfigType::NETEDIT);
254
// check file
255
if ((optionsFileDialog.getResult() == GNEDialog::Result::ACCEPT) && loadConfiguration(optionsFileDialog.getFilename())) {
256
// update entries
257
for (const auto& entry : myOptionRowEntries) {
258
entry->updateOption();
259
}
260
}
261
return 1;
262
}
263
264
265
long
266
GNEOptionsEditor::onCmdResetDefault(FXObject*, FXSelector, void*) {
267
// restore entries
268
for (const auto& entry : myOptionRowEntries) {
269
entry->restoreOption();
270
}
271
return 1;
272
}
273
274
275
GNEOptionsEditor::GNEOptionsEditor() :
276
myOptionsContainer(OptionsCont::EMPTY_OPTIONS),
277
myOriginalOptionsContainer(OptionsCont::EMPTY_OPTIONS) {
278
}
279
280
281
bool
282
GNEOptionsEditor::updateVisibleEntriesByTopic() {
283
// iterate over tree elements and get the selected item
284
for (const auto& treeItemTopic : myTreeItemTopics) {
285
if (treeItemTopic.first->isSelected()) {
286
// iterate over entries
287
for (const auto& entry : myOptionRowEntries) {
288
if (entry->getTopic() == treeItemTopic.second) {
289
entry->show();
290
} else {
291
entry->hide();
292
}
293
}
294
myEntriesFrame->recalc();
295
myEntriesFrame->update();
296
return true;
297
}
298
}
299
// no topic selected, then show all
300
for (const auto& entry : myOptionRowEntries) {
301
entry->show();
302
}
303
myEntriesFrame->recalc();
304
myEntriesFrame->update();
305
return true;
306
}
307
308
309
void
310
GNEOptionsEditor::updateVisibleEntriesBySearch(std::string searchText) {
311
// first lower case search text
312
searchText = StringUtils::to_lower_case(searchText);
313
// iterate over entries
314
for (const auto& entry : myOptionRowEntries) {
315
if (searchText.empty()) {
316
// show all entries if searchText is empty
317
entry->show();
318
} else if (entry->getNameLower().find(searchText) != std::string::npos) {
319
entry->show();
320
} else if ((myDescriptionSearchCheckButton->getCheck() == TRUE) &&
321
(entry->getDescriptionLower().find(searchText) != std::string::npos)) {
322
entry->show();
323
} else {
324
entry->hide();
325
}
326
}
327
myEntriesFrame->recalc();
328
myEntriesFrame->update();
329
}
330
331
332
bool
333
GNEOptionsEditor::loadConfiguration(const std::string& file) {
334
// make all options writable
335
myOptionsContainer.resetWritable();
336
// build parser
337
XERCES_CPP_NAMESPACE::SAXParser parser;
338
parser.setValidationScheme(XERCES_CPP_NAMESPACE::SAXParser::Val_Never);
339
parser.setDisableDefaultEntityResolution(true);
340
// start the parsing
341
OptionsLoader handler(myOptionsContainer);
342
try {
343
parser.setDocumentHandler(&handler);
344
parser.setErrorHandler(&handler);
345
parser.parse(StringUtils::transcodeToLocal(file).c_str());
346
if (handler.errorOccurred()) {
347
WRITE_ERROR(TL("Could not load configuration '") + file + "'.");
348
return false;
349
}
350
} catch (const XERCES_CPP_NAMESPACE::XMLException& e) {
351
WRITE_ERROR(TL("Could not load tool configuration '") + file + "':\n " + StringUtils::transcode(e.getMessage()));
352
return false;
353
}
354
// write info
355
WRITE_MESSAGE(TL("Loaded configuration."));
356
return true;
357
}
358
359
/****************************************************************************/
360
361