Path: blob/main/src/netedit/dialogs/GNEParametersDialog.cpp
169678 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 GNEParametersDialog.cpp14/// @author Pablo Alvarez Lopez15/// @date Jul 201816///17// Dialog for edit parameters18/****************************************************************************/1920#include <netbuild/NBLoadedSUMOTLDef.h>21#include <netedit/GNEApplicationWindow.h>22#include <netedit/GNENet.h>23#include <netedit/GNEUndoList.h>24#include <netedit/GNEViewParent.h>25#include <netedit/dialogs/basic/GNEHelpBasicDialog.h>26#include <netedit/dialogs/basic/GNEWarningBasicDialog.h>27#include <netedit/frames/common/GNEInspectorFrame.h>28#include <utils/gui/div/GUIDesigns.h>29#include <utils/xml/XMLSubSys.h>3031#include "GNEParametersDialog.h"3233// ===========================================================================34// FOX callback mapping35// ===========================================================================3637FXDEFMAP(GNEParametersDialog) GNEParametersDialogMap[] = {38FXMAPFUNC(SEL_COMMAND, MID_GNE_BUTTON_ACCEPT, GNEParametersDialog::onCmdAccept),39FXMAPFUNC(SEL_COMMAND, MID_GNE_BUTTON_CANCEL, GNEParametersDialog::onCmdCancel),40FXMAPFUNC(SEL_COMMAND, MID_GNE_BUTTON_RESET, GNEParametersDialog::onCmdReset),41FXMAPFUNC(SEL_CHORE, FXDialogBox::ID_CANCEL, GNEParametersDialog::onCmdCancel),42FXMAPFUNC(SEL_TIMEOUT, FXDialogBox::ID_CANCEL, GNEParametersDialog::onCmdCancel),43FXMAPFUNC(SEL_COMMAND, FXDialogBox::ID_CANCEL, GNEParametersDialog::onCmdCancel),44FXMAPFUNC(SEL_CLOSE, 0, GNEParametersDialog::onCmdCancel),45};4647FXDEFMAP(GNEParametersDialog::ParametersValues) ParametersValuesMap[] = {48FXMAPFUNC(SEL_COMMAND, MID_GNE_SET_ATTRIBUTE, GNEParametersDialog::ParametersValues::onCmdSetAttribute),49FXMAPFUNC(SEL_COMMAND, MID_GNE_REMOVE_ATTRIBUTE, GNEParametersDialog::ParametersValues::onCmdButtonPress),50FXMAPFUNC(SEL_PAINT, 0, GNEParametersDialog::ParametersValues::onPaint),51};5253FXDEFMAP(GNEParametersDialog::ParametersOperations) ParametersOperationsMap[] = {54FXMAPFUNC(SEL_COMMAND, MID_GNE_BUTTON_LOAD, GNEParametersDialog::ParametersOperations::onCmdLoadParameters),55FXMAPFUNC(SEL_COMMAND, MID_GNE_BUTTON_SAVE, GNEParametersDialog::ParametersOperations::onCmdSaveParameters),56FXMAPFUNC(SEL_COMMAND, MID_GNE_BUTTON_CLEAR, GNEParametersDialog::ParametersOperations::onCmdClearParameters),57FXMAPFUNC(SEL_COMMAND, MID_GNE_BUTTON_SORT, GNEParametersDialog::ParametersOperations::onCmdSortParameters),58FXMAPFUNC(SEL_COMMAND, MID_HELP, GNEParametersDialog::ParametersOperations::onCmdHelpParameter),59};6061// Object implementation62FXIMPLEMENT(GNEParametersDialog, GNEDialog, GNEParametersDialogMap, ARRAYNUMBER(GNEParametersDialogMap))63FXIMPLEMENT(GNEParametersDialog::ParametersValues, FXGroupBox, ParametersValuesMap, ARRAYNUMBER(ParametersValuesMap))64FXIMPLEMENT(GNEParametersDialog::ParametersOperations, FXGroupBox, ParametersOperationsMap, ARRAYNUMBER(ParametersOperationsMap))6566// ===========================================================================67// member method definitions68// ===========================================================================6970// ---------------------------------------------------------------------------71// GNEParametersDialog::ParametersValues - methods72// ---------------------------------------------------------------------------7374GNEParametersDialog::ParametersValues::ParametersValues(FXHorizontalFrame* frame, const std::string& name) :75FXGroupBox(frame, name.c_str(), GUIDesignGroupBoxFrameFill) {76// create labels for keys and values77FXHorizontalFrame* horizontalFrameLabels = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);78myKeyLabel = new FXLabel(horizontalFrameLabels, "key", nullptr, GUIDesignLabelThickedFixed(100));79new FXLabel(horizontalFrameLabels, "value", nullptr, GUIDesignLabelThick(JUSTIFY_NORMAL));80// create scroll windows81FXScrollWindow* scrollWindow = new FXScrollWindow(this, LAYOUT_FILL);82// create vertical frame for rows83myVerticalFrameRow = new FXVerticalFrame(scrollWindow, GUIDesignAuxiliarFrame);84}858687GNEParametersDialog::ParametersValues::~ParametersValues() {}888990void91GNEParametersDialog::ParametersValues::setParameters(const Parameterised::Map& newParameters) {92// clear rows93clearParameters();94// iterate over parameters95for (const auto& newParameter : newParameters) {96addParameter(newParameter);97}98}99100101void102GNEParametersDialog::ParametersValues::setParameters(const std::vector<std::pair<std::string, std::string> >& newParameters) {103// clear rows104clearParameters();105// iterate over parameters106for (const auto& newParameter : newParameters) {107addParameter(newParameter);108}109}110111112void113GNEParametersDialog::ParametersValues::addParameter(std::pair<std::string, std::string> newParameter) {114// enable last row115myParameterRows.back()->enableRow(newParameter.first, newParameter.second);116// add row117myParameterRows.push_back(new ParameterRow(this, myVerticalFrameRow));118// enable add button in the last row119myParameterRows.back()->toggleAddButton();120}121122123void124GNEParametersDialog::ParametersValues::clearParameters() {125// iterate over all rows126for (const auto& parameterRow : myParameterRows) {127delete parameterRow;128}129//clear myParameterRows;130myParameterRows.clear();131// add row132myParameterRows.push_back(new ParameterRow(this, myVerticalFrameRow));133// enable add button in the last row134myParameterRows.back()->toggleAddButton();135}136137138const std::vector<GNEParametersDialog::ParametersValues::ParameterRow*>139GNEParametersDialog::ParametersValues::getParameterRows() const {140return myParameterRows;141}142143144bool145GNEParametersDialog::ParametersValues::keyExist(const std::string& key) const {146// just iterate over myParameterRows and compare key147for (const auto& row : myParameterRows) {148if (row->keyField->getText().text() == key) {149return true;150}151}152return false;153}154155156long157GNEParametersDialog::ParametersValues::onPaint(FXObject* o, FXSelector f, void* p) {158// size of key label has to be updated in every interation159if (myParameterRows.size() > 0) {160myKeyLabel->setWidth(myParameterRows.front()->keyField->getWidth());161}162return FXGroupBox::onPaint(o, f, p);163}164165166long167GNEParametersDialog::ParametersValues::onCmdSetAttribute(FXObject* obj, FXSelector, void*) {168// find what value was changed169for (int i = 0; i < (int)myParameterRows.size(); i++) {170if (myParameterRows.at(i)->keyField == obj) {171// change color of text field depending if key is valid or empty172if (myParameterRows.at(i)->keyField->getText().empty() || SUMOXMLDefinitions::isValidParameterKey(myParameterRows.at(i)->keyField->getText().text())) {173myParameterRows.at(i)->keyField->setTextColor(GUIDesignTextColorBlack);174} else {175myParameterRows.at(i)->keyField->setTextColor(GUIDesignTextColorRed);176myParameterRows.at(i)->keyField->killFocus();177}178}179}180return 1;181}182183184long185GNEParametersDialog::ParametersValues::onCmdButtonPress(FXObject* obj, FXSelector, void*) {186// first check if add button was pressed187if (myParameterRows.back()->button == obj) {188// create new parameter189addParameter(std::make_pair("", ""));190return 1;191} else {192// in other case, button press was a "remove button". Find id and remove the Parameter193for (int i = 0; i < (int)myParameterRows.size(); i++) {194if (myParameterRows.at(i)->button == obj) {195// delete row196delete myParameterRows.at(i);197// just remove row198myParameterRows.erase(myParameterRows.begin() + i);199return 1;200}201}202}203// Nothing to do204return 1;205}206207208GNEParametersDialog::ParametersValues::ParameterRow::ParameterRow(ParametersValues* ParametersValues, FXVerticalFrame* verticalFrameParent) {209horizontalFrame = new FXHorizontalFrame(verticalFrameParent, GUIDesignAuxiliarHorizontalFrame);210keyField = new FXTextField(horizontalFrame, GUIDesignTextFieldNCol, ParametersValues, MID_GNE_SET_ATTRIBUTE, GUIDesignTextField);211valueField = new FXTextField(horizontalFrame, GUIDesignTextFieldNCol, ParametersValues, MID_GNE_SET_ATTRIBUTE, GUIDesignTextField);212button = GUIDesigns::buildFXButton(horizontalFrame, "", "", "", GUIIconSubSys::getIcon(GUIIcon::REMOVE), ParametersValues, MID_GNE_REMOVE_ATTRIBUTE, GUIDesignButtonIcon);213// only create elements if vertical frame was previously created214if (verticalFrameParent->id()) {215horizontalFrame->create();216}217// by defaults rows are disabled218disableRow();219}220221222GNEParametersDialog::ParametersValues::ParameterRow::~ParameterRow() {223// simply delete horizontalFrame (rest of elements will be automatic deleted due they are children of horizontal frame)224delete horizontalFrame;225}226227228void229GNEParametersDialog::ParametersValues::ParameterRow::disableRow() {230// hide all231keyField->setText("");232keyField->disable();233valueField->setText("");234valueField->disable();235button->disable();236button->setIcon(GUIIconSubSys::getIcon(GUIIcon::REMOVE));237}238239240void241GNEParametersDialog::ParametersValues::ParameterRow::enableRow(const std::string& parameter, const std::string& value) const {242// restore color and enable key field243keyField->setText(parameter.c_str());244if (parameter.empty() || SUMOXMLDefinitions::isValidParameterKey(parameter)) {245keyField->setTextColor(GUIDesignTextColorBlack);246} else {247keyField->setTextColor(GUIDesignTextColorRed);248}249keyField->enable();250// restore color and enable value field251valueField->setText(value.c_str());252valueField->enable();253// enable button and set icon remove254button->enable();255button->setIcon(GUIIconSubSys::getIcon(GUIIcon::REMOVE));256}257258259void260GNEParametersDialog::ParametersValues::ParameterRow::toggleAddButton() {261// clear and disable parameter and value fields262keyField->setText("");263keyField->disable();264valueField->setText("");265valueField->disable();266// enable remove button and set "add" icon and focus267button->enable();268button->setIcon(GUIIconSubSys::getIcon(GUIIcon::ADD));269button->setFocus();270}271272273bool274GNEParametersDialog::ParametersValues::ParameterRow::isButtonInAddMode() const {275return (button->getIcon() == GUIIconSubSys::getIcon(GUIIcon::ADD));276}277278279void280GNEParametersDialog::ParametersValues::ParameterRow::copyValues(const ParameterRow& other) {281keyField->setText(other.keyField->getText());282valueField->setText(other.valueField->getText());283}284285// ---------------------------------------------------------------------------286// GNEParametersDialog::ParametersOperations - methods287// ---------------------------------------------------------------------------288289GNEParametersDialog::ParametersOperations::ParametersOperations(FXHorizontalFrame* frame, GNEParametersDialog* ParameterDialogParent) :290FXGroupBox(frame, TL("Operations"), GUIDesignGroupBoxFrame100),291myParameterDialogParent(ParameterDialogParent) {292// create buttons293mySortButton = GUIDesigns::buildFXButton(this, TL("Sort"), "", "", GUIIconSubSys::getIcon(GUIIcon::RELOAD), this, MID_GNE_BUTTON_SORT, GUIDesignButtonFixed(100));294myClearButton = GUIDesigns::buildFXButton(this, TL("Clear"), "", "", GUIIconSubSys::getIcon(GUIIcon::CLEANJUNCTIONS), this, MID_GNE_BUTTON_CLEAR, GUIDesignButtonFixed(100));295myLoadButton = GUIDesigns::buildFXButton(this, TL("Load"), "", "", GUIIconSubSys::getIcon(GUIIcon::OPEN), this, MID_GNE_BUTTON_LOAD, GUIDesignButtonFixed(100));296mySaveButton = GUIDesigns::buildFXButton(this, TL("Save"), "", "", GUIIconSubSys::getIcon(GUIIcon::SAVE), this, MID_GNE_BUTTON_SAVE, GUIDesignButtonFixed(100));297myHelpButton = GUIDesigns::buildFXButton(this, TL("Help"), "", "", GUIIconSubSys::getIcon(GUIIcon::HELP), this, MID_HELP, GUIDesignButtonFixed(100));298}299300301GNEParametersDialog::ParametersOperations::~ParametersOperations() {}302303304long305GNEParametersDialog::ParametersOperations::onCmdLoadParameters(FXObject*, FXSelector, void*) {306// get the Additional file name307FXFileDialog opendialog(this, TL("Open Parameter Template"));308opendialog.setIcon(GUIIconSubSys::getIcon(GUIIcon::GREENVEHICLE));309opendialog.setSelectMode(SELECTFILE_EXISTING);310opendialog.setPatternList(SUMOXMLDefinitions::XMLFileExtensions.getMultilineString().c_str());311if (gCurrentFolder.length() != 0) {312opendialog.setDirectory(gCurrentFolder);313}314if (opendialog.execute()) {315gCurrentFolder = opendialog.getDirectory();316std::string file = opendialog.getFilename().text();317// save current number of parameters318const int numberOfParametersbeforeLoad = (int)myParameterDialogParent->myParametersValues->getParameterRows().size();319// Create additional handler and run parser320GNEParameterHandler handler(this, file);321if (!XMLSubSys::runParser(handler, file, false)) {322WRITE_MESSAGEF(TL("Loading of Parameters From % failed."), file);323}324// show loaded attributes325WRITE_MESSAGEF(TL("Loaded % Parameters."), toString((int)myParameterDialogParent->myParametersValues->getParameterRows().size() - numberOfParametersbeforeLoad));326}327return 1;328}329330331long332GNEParametersDialog::ParametersOperations::onCmdSaveParameters(FXObject*, FXSelector, void*) {333// obtain file to save parameters334FXString file = MFXUtils::getFilename2Write(this,335TL("Save Parameter Template file"),336SUMOXMLDefinitions::XMLFileExtensions.getMultilineString().c_str(),337GUIIconSubSys::getIcon(GUIIcon::GREENVEHICLE),338gCurrentFolder);339if (file == "") {340// None parameter file was selected, then stop function341return 1;342} else {343// open device344OutputDevice& device = OutputDevice::getDevice(file.text());345// write header346device.writeXMLHeader("Parameter", "parameter_file.xsd");347// iterate over all parameters and save it in the filename348for (const auto& row : myParameterDialogParent->myParametersValues->getParameterRows()) {349// write all except last350if (row != myParameterDialogParent->myParametersValues->getParameterRows().back()) {351// open tag352device.openTag(SUMO_TAG_PARAM);353// write key354device.writeAttr(SUMO_ATTR_KEY, row->keyField->getText().text());355// write value356device.writeAttr(SUMO_ATTR_VALUE, row->valueField->getText().text());357// close tag358device.closeTag();359}360}361// close device362device.close();363}364return 1;365}366367368long369GNEParametersDialog::ParametersOperations::onCmdClearParameters(FXObject*, FXSelector, void*) {370// simply clear parameters from ParametersValues371myParameterDialogParent->myParametersValues->clearParameters();372return 1;373}374375376long377GNEParametersDialog::ParametersOperations::onCmdSortParameters(FXObject*, FXSelector, void*) {378// declare two containers for parameters379std::vector<std::pair<std::string, std::string> > nonEmptyKeyValues;380std::vector<std::string> emptyKeyValues;381// first extract empty values382for (const auto& parameterRow : myParameterDialogParent->myParametersValues->getParameterRows()) {383// check if key is empty384if (!parameterRow->keyField->getText().empty()) {385nonEmptyKeyValues.push_back(std::make_pair(parameterRow->keyField->getText().text(), parameterRow->valueField->getText().text()));386} else if (!parameterRow->valueField->getText().empty()) {387emptyKeyValues.push_back(parameterRow->valueField->getText().text());388}389}390// sort non-empty parameters391std::sort(nonEmptyKeyValues.begin(), nonEmptyKeyValues.end());392// sort non-empty parameters393std::sort(emptyKeyValues.begin(), emptyKeyValues.end());394// add values without key395for (const auto& emptyKeyValue : emptyKeyValues) {396nonEmptyKeyValues.push_back(std::make_pair("", emptyKeyValue));397}398// finally setparameters in myParametersValues399myParameterDialogParent->myParametersValues->setParameters(nonEmptyKeyValues);400return 1;401}402403404long405GNEParametersDialog::ParametersOperations::onCmdHelpParameter(FXObject*, FXSelector, void*) {406// set help text407std::ostringstream help;408help409<< TL("- Parameters are defined by a Key and a Value.") << "\n"410<< TL("- In Netedit can be defined using format key1=parameter1|key2=parameter2|...") << "\n"411<< TL("- Duplicated and empty Keys aren't valid.") << "\n"412<< TL("- Whitespace and certain characters aren't allowed (@$%^&/|\\....)");413// create and open dialog414GNEHelpBasicDialog(myParameterDialogParent->getApplicationWindow(),415TL("Parameters Help"), help);416return 1;417}418419420GNEParametersDialog::ParametersOperations::GNEParameterHandler::GNEParameterHandler(ParametersOperations* ParametersOperationsParent, const std::string& file) :421SUMOSAXHandler(file),422myParametersOperationsParent(ParametersOperationsParent) {423}424425426GNEParametersDialog::ParametersOperations::GNEParameterHandler::~GNEParameterHandler() {}427428429void430GNEParametersDialog::ParametersOperations::GNEParameterHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {431// only continue if tag is valid432if (element != SUMO_TAG_NOTHING) {433// Call parse and build depending of tag434switch (element) {435case SUMO_TAG_PARAM:436// Check that format of Parameter is correct437if (!attrs.hasAttribute(SUMO_ATTR_KEY)) {438WRITE_WARNING(TL("Key of Parameter not defined"));439} else if (!attrs.hasAttribute(SUMO_ATTR_VALUE)) {440WRITE_WARNING(TL("Value of Parameter not defined"));441} else {442// obtain Key and value443std::string key = attrs.getString(SUMO_ATTR_KEY);444std::string value = attrs.getString(SUMO_ATTR_VALUE);445// check that parsed values are correct446if (!SUMOXMLDefinitions::isValidParameterKey(key)) {447if (key.size() == 0) {448WRITE_WARNING(TL("Key of Parameter cannot be empty"));449} else {450WRITE_WARNINGF(TL("Key '%' of Parameter contains invalid characters"), key);451}452} else if (myParametersOperationsParent->myParameterDialogParent->myParametersValues->keyExist(key)) {453WRITE_WARNINGF(TL("Key '%' already exist"), key);454} else {455// add parameter to vector of myParameterDialogParent456myParametersOperationsParent->myParameterDialogParent->myParametersValues->addParameter(std::make_pair(key, value));457}458}459break;460default:461break;462}463}464}465466// ---------------------------------------------------------------------------467// GNEParametersDialog - methods468// ---------------------------------------------------------------------------469470GNEParametersDialog::GNEParametersDialog(GNEApplicationWindow* applicationWindow, const Parameterised::Map& parameters) :471GNEDialog(applicationWindow, TL("Edit parameters"), GUIIcon::APP_TABLE, DialogType::PARAMETERS,472GNEDialog::Buttons::ACCEPT_CANCEL_RESET, OpenType::MODAL, GNEDialog::ResizeMode::RESIZABLE, 400, 300),473myOriginalParameters(parameters) {474// create frame for Parameters and operations475FXHorizontalFrame* horizontalFrame = new FXHorizontalFrame(myContentFrame, GUIDesignAuxiliarFrame);476// create parameters values477myParametersValues = new ParametersValues(horizontalFrame, TL("Parameters"));478// create parameters operations479myParametersOperations = new ParametersOperations(horizontalFrame, this);480// fill myParametersValues481myParametersValues->setParameters(parameters);482// open modal dialog483openDialog();484}485486GNEParametersDialog::~GNEParametersDialog() {}487488489void490GNEParametersDialog::runInternalTest(const InternalTestStep::DialogArgument* /*dialogArgument*/) {491// nothing to do (yet)492}493494495std::vector<std::pair<std::string, std::string> >496GNEParametersDialog::getEditedParameters() const {497std::vector<std::pair<std::string, std::string> > parameters;498for (const auto& parameterRow : myParametersValues->getParameterRows()) {499// ignore last row (the row with + button)500if (parameterRow != myParametersValues->getParameterRows().back()) {501// insert in parameters502parameters.push_back(std::make_pair(parameterRow->keyField->getText().text(), parameterRow->valueField->getText().text()));503}504}505return parameters;506}507508509long510GNEParametersDialog::onCmdAccept(FXObject*, FXSelector, void*) {511// declare vector for parameters in stringvector format512std::vector<std::pair<std::string, std::string> > editedParameters = getEditedParameters();513// check keys514for (const auto& editedParameter : editedParameters) {515if (editedParameter.first.empty()) {516// open warning Box517GNEWarningBasicDialog(myApplicationWindow, TL("Empty Parameter key"), TL("Parameters with empty keys aren't allowed"));518return 1;519} else if (!SUMOXMLDefinitions::isValidParameterKey(editedParameter.first)) {520// open warning Box521GNEWarningBasicDialog(myApplicationWindow, TL("Invalid Parameter key"), TL("There are keys with invalid characters"));522return 1;523}524}525// sort sortedParameters526std::sort(editedParameters.begin(), editedParameters.end());527// check if there is duplicated keys528for (auto i = editedParameters.begin(); (i + 1) != editedParameters.end(); i++) {529if ((i->first) == (i + 1)->first) {530// open warning Box531GNEWarningBasicDialog(myApplicationWindow, TL("Duplicated Parameters"), TL("Parameters with the same key aren't allowed"));532return 1;533}534}535// close dialog accepting536return closeDialogAccepting();537}538539540long541GNEParametersDialog::onCmdReset(FXObject*, FXSelector, void*) {542// restore original parameters543myParametersValues->setParameters(myOriginalParameters);544return 1;545}546547/****************************************************************************/548549550