Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netedit/dialogs/options/GNEOptionsEditor.cpp
169684 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2025 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 <utils/foxtools/MFXGroupBoxModule.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
MFXGroupBoxModule* groupBoxTree = new MFXGroupBoxModule(elementsFrameTree, TL("Topics"));
96
MFXGroupBoxModule* groupBoxOptions = new MFXGroupBoxModule(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
// continue depending of type
125
if (type == "STR") {
126
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionString(this, myEntriesFrame, topic, entry, description, defaultValue));
127
} else if (type == "TIME") {
128
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionTime(this, myEntriesFrame, topic, entry, description, defaultValue));
129
} else if ((type == "FILE") || (type == "NETWORK") || (type == "ADDITIONAL") || (type == "ROUTE") || (type == "DATA")) {
130
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionFilename(this, myEntriesFrame, topic, entry, description, defaultValue));
131
} else if (type == "BOOL") {
132
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionBool(this, myEntriesFrame, topic, entry, description, defaultValue));
133
} else if (type == "INT") {
134
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionInt(this, myEntriesFrame, topic, entry, description, defaultValue));
135
} else if (type == "FLOAT") {
136
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionFloat(this, myEntriesFrame, topic, entry, description, defaultValue));
137
} else if (type == "INT[]") {
138
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionIntVector(this, myEntriesFrame, topic, entry, description, defaultValue));
139
} else if (type == "STR[]") {
140
myOptionRowEntries.push_back(new GNEOptionsEditorRow::OptionStringVector(this, myEntriesFrame, topic, entry, description, defaultValue));
141
}
142
}
143
}
144
}
145
}
146
// create search elements
147
FXHorizontalFrame* searchFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
148
new FXLabel(searchFrame, TL("Search"), nullptr, GUIDesignLabelThickedFixed(TREELISTWIDTH - GUIDesignHeight + 14));
149
myDescriptionSearchCheckButton = new MFXCheckButtonTooltip(searchFrame, staticTooltipMenu, "", this, MID_GNE_SEARCH_USEDESCRIPTION, GUIDesignCheckButtonThick);
150
myDescriptionSearchCheckButton->setToolTipText(TL("Include description in search"));
151
mySearchButton = new MFXTextFieldSearch(searchFrame, staticTooltipMenu, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextField);
152
// after creation, adjust entries name sizes
153
for (const auto& entry : myOptionRowEntries) {
154
entry->adjustNameSize();
155
}
156
// set myShowToolTipsMenu
157
myShowToolTipsMenu->setChecked(getApp()->reg().readIntEntry("gui", "menuToolTips", 0) != 1);
158
}
159
160
161
GNEOptionsEditor::~GNEOptionsEditor() {
162
delete myCopyOfOptionsContainer;
163
}
164
165
166
void
167
GNEOptionsEditor::runInternalTest(const InternalTestStep::DialogArgument* /*dialogArgument*/) {
168
// not finished yet
169
}
170
171
172
bool
173
GNEOptionsEditor::isOptionModified() const {
174
return myOptionsModified;
175
}
176
177
void
178
GNEOptionsEditor::resetAllOptions() {
179
for (const auto& entry : myOptionRowEntries) {
180
entry->onCmdResetOption(nullptr, 0, nullptr);
181
}
182
myOptionsModified = false;
183
}
184
185
186
long
187
GNEOptionsEditor::onCmdSelectTopic(FXObject*, FXSelector, void*) {
188
if (mySearchButton->getText().count() == 0) {
189
updateVisibleEntriesByTopic();
190
}
191
return 1;
192
}
193
194
195
long
196
GNEOptionsEditor::onCmdSearch(FXObject*, FXSelector, void*) {
197
if (mySearchButton->getText().count() > 0) {
198
updateVisibleEntriesBySearch(mySearchButton->getText().text());
199
} else {
200
updateVisibleEntriesByTopic();
201
}
202
return 1;
203
}
204
205
206
long
207
GNEOptionsEditor::onCmdShowToolTipsMenu(FXObject*, FXSelector, void*) {
208
// get staticTooltipMenu
209
auto viewNet = myDialog->getApplicationWindow()->getViewNet();
210
// toggle check
211
myShowToolTipsMenu->setChecked(!myShowToolTipsMenu->amChecked());
212
if (viewNet) {
213
viewNet->getViewParent()->getShowToolTipsMenu()->setChecked(myShowToolTipsMenu->amChecked());
214
viewNet->getViewParent()->getShowToolTipsMenu()->update();
215
}
216
// enable/disable static tooltip
217
myDialog->getApplicationWindow()->getStaticTooltipMenu()->enableStaticToolTip(myShowToolTipsMenu->amChecked());
218
// save in registry
219
getApp()->reg().writeIntEntry("gui", "menuToolTips", myShowToolTipsMenu->amChecked() ? 0 : 1);
220
update();
221
222
return 1;
223
}
224
225
226
long
227
GNEOptionsEditor::onCmdSaveOptions(FXObject*, FXSelector, void*) {
228
// open file dialog
229
const auto optionsFileDialog = GNEFileDialog(myDialog->getApplicationWindow(), TL("options file"),
230
SUMOXMLDefinitions::XMLFileExtensions.getStrings(),
231
GNEFileDialog::OpenMode::SAVE,
232
GNEFileDialog::ConfigType::NETEDIT);
233
// check file
234
if (optionsFileDialog.getResult() == GNEDialog::Result::ACCEPT) {
235
std::ofstream out(StringUtils::transcodeToLocal(optionsFileDialog.getFilename()));
236
myOptionsContainer.writeConfiguration(out, true, false, false, optionsFileDialog.getFilename(), true);
237
out.close();
238
}
239
return 1;
240
}
241
242
243
long
244
GNEOptionsEditor::onCmdLoadOptions(FXObject*, FXSelector, void*) {
245
// open file dialog
246
const auto optionsFileDialog = GNEFileDialog(myDialog->getApplicationWindow(), TL("options file"),
247
SUMOXMLDefinitions::XMLFileExtensions.getStrings(),
248
GNEFileDialog::OpenMode::LOAD_SINGLE,
249
GNEFileDialog::ConfigType::NETEDIT);
250
// check file
251
if ((optionsFileDialog.getResult() == GNEDialog::Result::ACCEPT) && loadConfiguration(optionsFileDialog.getFilename())) {
252
// update entries
253
for (const auto& entry : myOptionRowEntries) {
254
entry->updateOption();
255
}
256
}
257
return 1;
258
}
259
260
261
long
262
GNEOptionsEditor::onCmdResetDefault(FXObject*, FXSelector, void*) {
263
// restore entries
264
for (const auto& entry : myOptionRowEntries) {
265
entry->restoreOption();
266
}
267
return 1;
268
}
269
270
271
GNEOptionsEditor::GNEOptionsEditor() :
272
myOptionsContainer(OptionsCont::EMPTY_OPTIONS),
273
myOriginalOptionsContainer(OptionsCont::EMPTY_OPTIONS) {
274
}
275
276
277
bool
278
GNEOptionsEditor::updateVisibleEntriesByTopic() {
279
// iterate over tree elements and get the selected item
280
for (const auto& treeItemTopic : myTreeItemTopics) {
281
if (treeItemTopic.first->isSelected()) {
282
// iterate over entries
283
for (const auto& entry : myOptionRowEntries) {
284
if (entry->getTopic() == treeItemTopic.second) {
285
entry->show();
286
} else {
287
entry->hide();
288
}
289
}
290
myEntriesFrame->recalc();
291
myEntriesFrame->update();
292
return true;
293
}
294
}
295
// no topic selected, then show all
296
for (const auto& entry : myOptionRowEntries) {
297
entry->show();
298
}
299
myEntriesFrame->recalc();
300
myEntriesFrame->update();
301
return true;
302
}
303
304
305
void
306
GNEOptionsEditor::updateVisibleEntriesBySearch(std::string searchText) {
307
// first lower case search text
308
searchText = StringUtils::to_lower_case(searchText);
309
// iterate over entries
310
for (const auto& entry : myOptionRowEntries) {
311
if (searchText.empty()) {
312
// show all entries if searchText is empty
313
entry->show();
314
} else if (entry->getNameLower().find(searchText) != std::string::npos) {
315
entry->show();
316
} else if ((myDescriptionSearchCheckButton->getCheck() == TRUE) &&
317
(entry->getDescriptionLower().find(searchText) != std::string::npos)) {
318
entry->show();
319
} else {
320
entry->hide();
321
}
322
}
323
myEntriesFrame->recalc();
324
myEntriesFrame->update();
325
}
326
327
328
bool
329
GNEOptionsEditor::loadConfiguration(const std::string& file) {
330
// make all options writable
331
myOptionsContainer.resetWritable();
332
// build parser
333
XERCES_CPP_NAMESPACE::SAXParser parser;
334
parser.setValidationScheme(XERCES_CPP_NAMESPACE::SAXParser::Val_Never);
335
parser.setDisableDefaultEntityResolution(true);
336
// start the parsing
337
OptionsLoader handler(myOptionsContainer);
338
try {
339
parser.setDocumentHandler(&handler);
340
parser.setErrorHandler(&handler);
341
parser.parse(StringUtils::transcodeToLocal(file).c_str());
342
if (handler.errorOccurred()) {
343
WRITE_ERROR(TL("Could not load configuration '") + file + "'.");
344
return false;
345
}
346
} catch (const XERCES_CPP_NAMESPACE::XMLException& e) {
347
WRITE_ERROR(TL("Could not load tool configuration '") + file + "':\n " + StringUtils::transcode(e.getMessage()));
348
return false;
349
}
350
// write info
351
WRITE_MESSAGE(TL("Loaded configuration."));
352
return true;
353
}
354
355
/****************************************************************************/
356
357