Path: blob/main/src/netedit/dialogs/options/GNEOptionsEditorRow.cpp
169684 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file GNEOptionsEditorRow.cpp14/// @author Pablo Alvarez Lopez15/// @date May 202316///17// Row used in GNEOptionsEditor to edit options18/****************************************************************************/19#include <config.h>2021#include <netedit/dialogs/GNEDialog.h>22#include <netedit/GNEApplicationWindow.h>23#include <utils/common/MsgHandler.h>24#include <utils/common/StringTokenizer.h>25#include <utils/foxtools/MFXLabelTooltip.h>26#include <utils/gui/div/GUIDesigns.h>2728#include "GNEOptionsEditorRow.h"29#include "GNEOptionsEditor.h"3031// ===========================================================================32// Defines33// ===========================================================================3435#define MARGIN 436#define MINNAMEWIDTH 2003738// ===========================================================================39// FOX callback mapping40// ===========================================================================4142FXDEFMAP(GNEOptionsEditorRow::OptionRow) OptionRowMap[] = {43FXMAPFUNC(SEL_COMMAND, MID_GNE_SET_ATTRIBUTE, GNEOptionsEditorRow::OptionRow::onCmdSetOption),44FXMAPFUNC(SEL_COMMAND, MID_GNE_RESET, GNEOptionsEditorRow::OptionRow::onCmdResetOption),45};4647FXDEFMAP(GNEOptionsEditorRow::OptionFilename) OptionFilenameMap[] = {48FXMAPFUNC(SEL_COMMAND, MID_GNE_SET_ATTRIBUTE_DIALOG, GNEOptionsEditorRow::OptionFilename::onCmdOpenDialog),49};5051// Object implementation52FXIMPLEMENT_ABSTRACT(GNEOptionsEditorRow::OptionRow, FXHorizontalFrame, OptionRowMap, ARRAYNUMBER(OptionRowMap))53FXIMPLEMENT_ABSTRACT(GNEOptionsEditorRow::OptionFilename, GNEOptionsEditorRow::OptionRow, OptionFilenameMap, ARRAYNUMBER(OptionFilenameMap))5455// ===========================================================================56// method definitions57// ===========================================================================5859// ---------------------------------------------------------------------------60// GNEOptionsEditorRow::OptionRow - methods61// ---------------------------------------------------------------------------6263GNEOptionsEditorRow::OptionRow::OptionRow(GNEOptionsEditor* optionsEditor, FXComposite* parent, const std::string& topic,64const std::string& name, const std::string& description, const std::string& defaultValue) :65FXHorizontalFrame(parent, GUIDesignAuxiliarHorizontalFrame),66myOptionsEditor(optionsEditor),67myTopic(topic),68myName(name),69myDescription(description),70myDefaultValue(defaultValue) {71// build label with name (default width 150)72myNameLabel = new MFXLabelTooltip(this, myOptionsEditor->myDialog->getApplicationWindow()->getStaticTooltipMenu(),73name.c_str(), nullptr, GUIDesignLabelThickedFixed(MINNAMEWIDTH));74// set description as tooltip75myNameLabel->setTipText(description.c_str());76// create content frame77myContentFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);78// Create reset button79myResetButton = GUIDesigns::buildFXButton(this, "", "", TL("Reset value"), GUIIconSubSys::getIcon(GUIIcon::RESET), this, MID_GNE_RESET, GUIDesignButtonIcon);80}818283void84GNEOptionsEditorRow::OptionRow::adjustNameSize() {85const int nameWidth = myNameLabel->getFont()->getTextWidth(myNameLabel->getText().text(), myNameLabel->getText().length() + MARGIN);86if (nameWidth > MINNAMEWIDTH) {87myNameLabel->setWidth(nameWidth);88}89}909192const std::string&93GNEOptionsEditorRow::OptionRow::getTopic() const {94return myTopic;95}969798const std::string99GNEOptionsEditorRow::OptionRow::getNameLower() const {100return StringUtils::to_lower_case(myName);101}102103104const std::string105GNEOptionsEditorRow::OptionRow::getDescriptionLower() const {106return StringUtils::to_lower_case(myDescription);107}108109110void111GNEOptionsEditorRow::OptionRow::updateResetButton() {112if (getValue() != myDefaultValue) {113myResetButton->enable();114} else {115myResetButton->disable();116}117}118119// ---------------------------------------------------------------------------120// GNEOptionsEditorRow::OptionString - methods121// ---------------------------------------------------------------------------122123GNEOptionsEditorRow::OptionString::OptionString(GNEOptionsEditor* optionsEditor, FXComposite* parent,124const std::string& topic, const std::string& name, const std::string& description, const std::string& defaultValue) :125OptionRow(optionsEditor, parent, topic, name, description, defaultValue) {126myStringTextField = new FXTextField(myContentFrame, GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextField);127myStringTextField->setText(myOptionsEditor->myOptionsContainer.getString(name).c_str());128updateOption();129}130131132void133GNEOptionsEditorRow::OptionString::updateOption() {134myStringTextField->setText(myOptionsEditor->myOptionsContainer.getString(myName).c_str());135updateResetButton();136}137138139void140GNEOptionsEditorRow::OptionString::restoreOption() {141myStringTextField->setText(myOptionsEditor->myOriginalOptionsContainer.getString(myName).c_str());142onCmdSetOption(nullptr, 0, nullptr);143}144145146long147GNEOptionsEditorRow::OptionString::onCmdSetOption(FXObject*, FXSelector, void*) {148myOptionsEditor->myOptionsContainer.resetWritable();149myOptionsEditor->myOptionsContainer.set(myName, myStringTextField->getText().text());150myOptionsEditor->myOptionsModified = true;151updateResetButton();152return 1;153}154155156long157GNEOptionsEditorRow::OptionString::onCmdResetOption(FXObject*, FXSelector, void*) {158myStringTextField->setText(myDefaultValue.c_str());159updateResetButton();160return 1;161}162163164std::string165GNEOptionsEditorRow::OptionString::getValue() const {166return myStringTextField->getText().text();167}168169170GNEOptionsEditorRow::OptionStringVector::OptionStringVector(GNEOptionsEditor* optionsEditor, FXComposite* parent,171const std::string& topic, const std::string& name, const std::string& description, const std::string& defaultValue) :172OptionRow(optionsEditor, parent, topic, name, description, defaultValue) {173myStringVectorTextField = new FXTextField(myContentFrame, GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextField);174updateOption();175}176177178void179GNEOptionsEditorRow::OptionStringVector::updateOption() {180myStringVectorTextField->setText(toString(myOptionsEditor->myOptionsContainer.getStringVector(myName)).c_str());181updateResetButton();182}183184185void186GNEOptionsEditorRow::OptionStringVector::restoreOption() {187myStringVectorTextField->setText(toString(myOptionsEditor->myOriginalOptionsContainer.getStringVector(myName)).c_str());188onCmdSetOption(nullptr, 0, nullptr);189}190191192long193GNEOptionsEditorRow::OptionStringVector::onCmdSetOption(FXObject*, FXSelector, void*) {194myOptionsEditor->myOptionsContainer.resetWritable();195myOptionsEditor->myOptionsContainer.set(myName, myStringVectorTextField->getText().text());196myOptionsEditor->myOptionsModified = true;197updateResetButton();198return 1;199}200201202long203GNEOptionsEditorRow::OptionStringVector::onCmdResetOption(FXObject*, FXSelector, void*) {204myStringVectorTextField->setText(myDefaultValue.c_str());205updateResetButton();206return 1;207}208209210std::string211GNEOptionsEditorRow::OptionStringVector::getValue() const {212return myStringVectorTextField->getText().text();213}214215// ---------------------------------------------------------------------------216// GNEOptionsEditorRow::OptionBool - methods217// ---------------------------------------------------------------------------218219GNEOptionsEditorRow::OptionBool::OptionBool(GNEOptionsEditor* optionsEditor, FXComposite* parent,220const std::string& topic, const std::string& name, const std::string& description, const std::string& defaultValue) :221OptionRow(optionsEditor, parent, topic, name, description, defaultValue) {222myCheckButton = new FXCheckButton(myContentFrame, "", this, MID_GNE_SET_ATTRIBUTE, GUIDesignCheckButton);223updateOption();224}225226227void228GNEOptionsEditorRow::OptionBool::updateOption() {229if (myOptionsEditor->myOptionsContainer.getBool(myName)) {230myCheckButton->setCheck(TRUE);231myCheckButton->setText(TL("true"));232} else {233myCheckButton->setCheck(FALSE);234myCheckButton->setText(TL("false"));235}236updateResetButton();237}238239240void241GNEOptionsEditorRow::OptionBool::restoreOption() {242if (myOptionsEditor->myOriginalOptionsContainer.getBool(myName)) {243myCheckButton->setCheck(TRUE);244myCheckButton->setText(TL("true"));245} else {246myCheckButton->setCheck(FALSE);247myCheckButton->setText(TL("false"));248}249onCmdSetOption(nullptr, 0, nullptr);250}251252253long254GNEOptionsEditorRow::OptionBool::onCmdSetOption(FXObject*, FXSelector, void*) {255myOptionsEditor->myOptionsContainer.resetWritable();256if (myCheckButton->getCheck()) {257myOptionsEditor->myOptionsContainer.set(myName, "true");258myCheckButton->setText(TL("true"));259} else {260myOptionsEditor->myOptionsContainer.set(myName, "false");261myCheckButton->setText(TL("false"));262}263myOptionsEditor->myOptionsModified = true;264// special checks for Debug flags265if ((myName == "gui-testing-debug") && myOptionsEditor->myOptionsContainer.isSet("gui-testing-debug")) {266MsgHandler::enableDebugMessages(myOptionsEditor->myOptionsContainer.getBool("gui-testing-debug"));267}268if ((myName == "gui-testing-debug-gl") && myOptionsEditor->myOptionsContainer.isSet("gui-testing-debug-gl")) {269MsgHandler::enableDebugGLMessages(myOptionsEditor->myOptionsContainer.getBool("gui-testing-debug-gl"));270}271updateResetButton();272return 1;273}274275276long277GNEOptionsEditorRow::OptionBool::onCmdResetOption(FXObject*, FXSelector, void*) {278if (myDefaultValue.empty()) {279myCheckButton->setCheck(FALSE);280myCheckButton->setText(TL("false"));281} else if (StringUtils::toBool(myDefaultValue)) {282myCheckButton->setCheck(TRUE);283myCheckButton->setText(TL("true"));284} else {285myCheckButton->setCheck(FALSE);286myCheckButton->setText(TL("false"));287}288updateResetButton();289return 1;290}291292293std::string294GNEOptionsEditorRow::OptionBool::getValue() const {295return myCheckButton->getCheck() ? "true" : "false";296}297298// ---------------------------------------------------------------------------299// GNEOptionsEditorRow::OptionInt - methods300// ---------------------------------------------------------------------------301302GNEOptionsEditorRow::OptionInt::OptionInt(GNEOptionsEditor* optionsEditor, FXComposite* parent,303const std::string& topic, const std::string& name, const std::string& description, const std::string& defaultValue) :304OptionRow(optionsEditor, parent, topic, name, description, defaultValue) {305myIntTextField = new FXTextField(myContentFrame, GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextFieldRestricted(TEXTFIELD_INTEGER));306updateOption();307}308309310void311GNEOptionsEditorRow::OptionInt::updateOption() {312myIntTextField->setText(toString(myOptionsEditor->myOptionsContainer.getInt(myName)).c_str());313updateResetButton();314}315316317void318GNEOptionsEditorRow::OptionInt::restoreOption() {319myIntTextField->setText(toString(myOptionsEditor->myOriginalOptionsContainer.getInt(myName)).c_str());320onCmdSetOption(nullptr, 0, nullptr);321}322323324long325GNEOptionsEditorRow::OptionInt::onCmdSetOption(FXObject*, FXSelector, void*) {326if (myIntTextField->getText().empty()) {327myIntTextField->setText(myDefaultValue.c_str());328} else {329myOptionsEditor->myOptionsContainer.resetWritable();330myOptionsEditor->myOptionsContainer.set(myName, myIntTextField->getText().text());331myOptionsEditor->myOptionsModified = true;332}333updateResetButton();334return 1;335}336337338long339GNEOptionsEditorRow::OptionInt::onCmdResetOption(FXObject*, FXSelector, void*) {340myIntTextField->setText(myDefaultValue.c_str());341updateResetButton();342return 1;343}344345346std::string347GNEOptionsEditorRow::OptionInt::getValue() const {348return myIntTextField->getText().text();349}350351// ---------------------------------------------------------------------------352// GNEOptionsEditorRow::OptionIntVector - methods353// ---------------------------------------------------------------------------354355GNEOptionsEditorRow::OptionIntVector::OptionIntVector(GNEOptionsEditor* optionsEditor, FXComposite* parent,356const std::string& topic, const std::string& name, const std::string& description, const std::string& defaultValue) :357OptionRow(optionsEditor, parent, topic, name, description, defaultValue) {358myIntVectorTextField = new FXTextField(myContentFrame, GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextField);359myIntVectorTextField->setText(toString(myOptionsEditor->myOptionsContainer.getIntVector(name)).c_str());360updateOption();361}362363364void365GNEOptionsEditorRow::OptionIntVector::updateOption() {366myIntVectorTextField->setText(toString(myOptionsEditor->myOptionsContainer.getIntVector(myName)).c_str());367updateResetButton();368}369370371void372GNEOptionsEditorRow::OptionIntVector::restoreOption() {373myIntVectorTextField->setText(toString(myOptionsEditor->myOriginalOptionsContainer.getIntVector(myName)).c_str());374onCmdSetOption(nullptr, 0, nullptr);375}376377378long379GNEOptionsEditorRow::OptionIntVector::onCmdSetOption(FXObject*, FXSelector, void*) {380try {381// check that int vector can be parsed382const auto intVector = StringTokenizer(myIntVectorTextField->getText().text()).getVector();383for (const auto& intValue : intVector) {384StringUtils::toInt(intValue);385}386myOptionsEditor->myOptionsContainer.resetWritable();387myOptionsEditor->myOptionsContainer.set(myName, myIntVectorTextField->getText().text());388myIntVectorTextField->setTextColor(GUIDesignTextColorBlack);389myOptionsEditor->myOptionsModified = true;390} catch (...) {391myIntVectorTextField->setTextColor(GUIDesignTextColorRed);392}393updateResetButton();394return 1;395}396397398long399GNEOptionsEditorRow::OptionIntVector::onCmdResetOption(FXObject*, FXSelector, void*) {400myIntVectorTextField->setText(myDefaultValue.c_str());401updateResetButton();402return 1;403}404405406std::string407GNEOptionsEditorRow::OptionIntVector::getValue() const {408return myIntVectorTextField->getText().text();409}410411// ---------------------------------------------------------------------------412// GNEOptionsEditorRow::OptionFloat - methods413// ---------------------------------------------------------------------------414415GNEOptionsEditorRow::OptionFloat::OptionFloat(GNEOptionsEditor* optionsEditor, FXComposite* parent, const std::string& topic,416const std::string& name, const std::string& description, const std::string& defaultValue) :417OptionRow(optionsEditor, parent, topic, name, description, parseFloat(defaultValue)) {418myFloatTextField = new FXTextField(myContentFrame, GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextFieldRestricted(TEXTFIELD_REAL));419myFloatTextField->setText(toString(myOptionsEditor->myOptionsContainer.getFloat(name)).c_str());420updateOption();421}422423424void425GNEOptionsEditorRow::OptionFloat::updateOption() {426myFloatTextField->setText(toString(myOptionsEditor->myOptionsContainer.getFloat(myName)).c_str());427updateResetButton();428}429430431void432GNEOptionsEditorRow::OptionFloat::restoreOption() {433myFloatTextField->setText(toString(myOptionsEditor->myOriginalOptionsContainer.getFloat(myName)).c_str());434onCmdSetOption(nullptr, 0, nullptr);435}436437438long439GNEOptionsEditorRow::OptionFloat::onCmdSetOption(FXObject*, FXSelector, void*) {440// avoid empty values441if (myFloatTextField->getText().empty()) {442myFloatTextField->setText(myDefaultValue.c_str());443} else {444myOptionsEditor->myOptionsContainer.resetWritable();445myOptionsEditor->myOptionsContainer.set(myName, myFloatTextField->getText().text());446myOptionsEditor->myOptionsModified = true;447}448updateResetButton();449return 1;450}451452453long454GNEOptionsEditorRow::OptionFloat::onCmdResetOption(FXObject*, FXSelector, void*) {455myFloatTextField->setText(myDefaultValue.c_str());456updateResetButton();457return 1;458}459460461std::string462GNEOptionsEditorRow::OptionFloat::getValue() const {463return myFloatTextField->getText().text();464}465466467std::string468GNEOptionsEditorRow::OptionFloat::parseFloat(const std::string& value) const {469try {470return toString(StringUtils::toDouble(value));471} catch (...) {472return value;473}474}475476// ---------------------------------------------------------------------------477// GNEOptionsEditorRow::OptionTime - methods478// ---------------------------------------------------------------------------479480GNEOptionsEditorRow::OptionTime::OptionTime(GNEOptionsEditor* optionsEditor, FXComposite* parent, const std::string& topic,481const std::string& name, const std::string& description, const std::string& defaultValue) :482OptionRow(optionsEditor, parent, topic, name, description, parseTime(defaultValue)) {483myTimeTextField = new FXTextField(myContentFrame, GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextField);484myTimeTextField->setText(toString(myOptionsEditor->myOptionsContainer.getString(name)).c_str());485updateOption();486}487488489void490GNEOptionsEditorRow::OptionTime::updateOption() {491myTimeTextField->setText(toString(myOptionsEditor->myOptionsContainer.getString(myName)).c_str());492updateResetButton();493}494495496void497GNEOptionsEditorRow::OptionTime::restoreOption() {498myTimeTextField->setText(toString(myOptionsEditor->myOriginalOptionsContainer.getString(myName)).c_str());499onCmdSetOption(nullptr, 0, nullptr);500}501502503long504GNEOptionsEditorRow::OptionTime::onCmdSetOption(FXObject*, FXSelector, void*) {505// avoid empty values506if (myTimeTextField->getText().empty()) {507myTimeTextField->setText(myDefaultValue.c_str());508} else {509myOptionsEditor->myOptionsContainer.resetWritable();510myOptionsEditor->myOptionsContainer.set(myName, myTimeTextField->getText().text());511myOptionsEditor->myOptionsModified = true;512}513updateResetButton();514return 1;515}516517518long519GNEOptionsEditorRow::OptionTime::onCmdResetOption(FXObject*, FXSelector, void*) {520myTimeTextField->setText(myDefaultValue.c_str());521updateResetButton();522return 1;523}524525526std::string527GNEOptionsEditorRow::OptionTime::getValue() const {528return myTimeTextField->getText().text();529}530531532std::string533GNEOptionsEditorRow::OptionTime::parseTime(const std::string& value) const {534try {535return time2string(string2time(value));536} catch (...) {537return value;538}539}540541// ---------------------------------------------------------------------------542// GNEOptionsEditorRow::OptionFilename - methods543// ---------------------------------------------------------------------------544545GNEOptionsEditorRow::OptionFilename::OptionFilename(GNEOptionsEditor* optionsEditor, FXComposite* parent, const std::string& topic,546const std::string& name, const std::string& description, const std::string& defaultValue) :547OptionRow(optionsEditor, parent, topic, name, description, defaultValue) {548myOpenFilenameButton = GUIDesigns::buildFXButton(myContentFrame, "", "", TL("Select filename"),549GUIIconSubSys::getIcon(GUIIcon::OPEN), this, MID_GNE_SET_ATTRIBUTE_DIALOG, GUIDesignButtonIcon);550myFilenameTextField = new FXTextField(myContentFrame, GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextField);551updateOption();552}553554555void556GNEOptionsEditorRow::OptionFilename::updateOption() {557myFilenameTextField->setText(myOptionsEditor->myOptionsContainer.getString(myName).c_str());558updateResetButton();559}560561562void563GNEOptionsEditorRow::OptionFilename::restoreOption() {564myFilenameTextField->setText(myOptionsEditor->myOriginalOptionsContainer.getString(myName).c_str());565onCmdSetOption(nullptr, 0, nullptr);566}567568569long570GNEOptionsEditorRow::OptionFilename::onCmdOpenDialog(FXObject*, FXSelector, void*) {571// get open mode572GNEFileDialog::OpenMode openMode = (myName.find("output") != std::string::npos) ? GNEFileDialog::OpenMode::SAVE : GNEFileDialog::OpenMode::LOAD_SINGLE;573// open dialog574const auto XMLFileDialog = GNEFileDialog(myOptionsEditor->myDialog->getApplicationWindow(), TL("XML file"),575SUMOXMLDefinitions::XMLFileExtensions.getStrings(), openMode,576GNEFileDialog::ConfigType::NETEDIT);577// check that file is valid578if (XMLFileDialog.getResult() == GNEDialog::Result::ACCEPT) {579myFilenameTextField->setText(XMLFileDialog.getFilename().c_str(), TRUE);580}581updateResetButton();582return 1;583}584585586long587GNEOptionsEditorRow::OptionFilename::onCmdSetOption(FXObject*, FXSelector, void*) {588if (SUMOXMLDefinitions::isValidFilename(myFilenameTextField->getText().text())) {589myOptionsEditor->myOptionsContainer.resetWritable();590myOptionsEditor->myOptionsContainer.set(myName, myFilenameTextField->getText().text());591myFilenameTextField->setTextColor(GUIDesignTextColorBlack);592myOptionsEditor->myOptionsModified = true;593} else {594myFilenameTextField->setTextColor(GUIDesignTextColorRed);595}596updateResetButton();597return 1;598}599600601long602GNEOptionsEditorRow::OptionFilename::onCmdResetOption(FXObject*, FXSelector, void*) {603myFilenameTextField->setText(myDefaultValue.c_str());604updateResetButton();605return 1;606}607608609GNEOptionsEditorRow::OptionFilename::OptionFilename() {}610611612std::string613GNEOptionsEditorRow::OptionFilename::getValue() const {614return myFilenameTextField->getText().text();615}616617/****************************************************************************/618619620