Path: blob/main/src/netedit/dialogs/elements/lists/GNEElementTable.cpp
169688 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 GNEElementTable.cpp14/// @author Pablo Alvarez Lopez15/// @date Aug 202516///17// Table used in GNEElementList18/****************************************************************************/1920#include <netedit/dialogs/GNEVClassesDialog.h>21#include <netedit/GNEApplicationWindow.h>22#include <netedit/GNENet.h>23#include <netedit/GNETagProperties.h>24#include <netedit/GNEViewParent.h>25#include <utils/foxtools/MFXTextFieldIcon.h>26#include <utils/gui/div/GUIDesigns.h>2728#include "GNEElementTable.h"2930// ===========================================================================31// FOX callback mapping32// ===========================================================================3334FXDEFMAP(GNEElementTable::Row) RowMap[] = {35FXMAPFUNC(SEL_COMMAND, MID_GNE_ELEMENTTABLE_EDIT, GNEElementTable::Row::onCmdEditRow),36FXMAPFUNC(SEL_COMMAND, MID_GNE_ELEMENTTABLE_REMOVE, GNEElementTable::Row::onCmdRemoveRow),37FXMAPFUNC(SEL_COMMAND, MID_GNE_ELEMENTTABLE_DIALOG_VCLASS, GNEElementTable::Row::onCmdOpenVClassDialog),38FXMAPFUNC(SEL_COMMAND, MID_GNE_ELEMENTTABLE_DIALOG_ELEMENT, GNEElementTable::Row::onCmdOpenElementDialog)39};4041// Object implementation42FXIMPLEMENT(GNEElementTable::Row, FXHorizontalFrame, RowMap, ARRAYNUMBER(RowMap))4344// ===========================================================================45// method definitions46// ===========================================================================4748// ---------------------------------------------------------------------------49// GNEElementTable::ColumnHeader - methods50// ---------------------------------------------------------------------------5152GNEElementTable::ColumnHeader::ColumnHeader(GNEElementTable* elementTable, const GNETagProperties* tagProperties) :53FXHorizontalFrame(elementTable, GUIDesignAuxiliarHorizontalFrame) {54// create horizontal label with uniform width55auto horizontalFrameLabels = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrameUniform);56// create empty label57new FXLabel(horizontalFrameLabels, "", nullptr, GUIDesignLabelFixed(GUIDesignHeight));58// create a label for every attribute59for (const auto& attrProperty : tagProperties->getAttributeProperties()) {60// check if this attribute can be edited in dialog61if (attrProperty->isDialogEditor()) {62// create label63myLabels.push_back(std::make_pair(attrProperty->getAttr(), new FXLabel(horizontalFrameLabels, attrProperty->getAttrStr().c_str(),64nullptr, GUIDesignLabelThick(JUSTIFY_NORMAL))));65// check if this attribute is sortable66if (attrProperty->isNumerical()) {67mySortableAttrs.push_back(attrProperty->getAttr());68}69}70}71// calculate buttons label width72int buttonLabelWidth = 15 + GUIDesignHeight;73if (elementTable->myOptions & GNEElementList::Options::DIALOG_ELEMENT) {74buttonLabelWidth += GUIDesignHeight;75}76if (elementTable->myOptions & GNEElementList::Options::DIALOG_VCLASS) {77buttonLabelWidth += GUIDesignHeight;78}79// create empty label (icons and vertical scroller)80new FXLabel(horizontalFrameLabels, "", nullptr, GUIDesignLabelFixed(buttonLabelWidth));81}828384GNEElementTable::ColumnHeader::~ColumnHeader() {}858687void88GNEElementTable::ColumnHeader::enableRowHeader() {89// enable all labels90for (const auto& label : myLabels) {91label.second->enable();92}93}949596void97GNEElementTable::ColumnHeader::disableRowHeader() {98// disable all labels99for (const auto& label : myLabels) {100label.second->disable();101}102}103104105size_t106GNEElementTable::ColumnHeader::getNumColumns() const {107return myLabels.size();108}109110111const std::vector<SumoXMLAttr>&112GNEElementTable::ColumnHeader::getSortableAttributes() {113return mySortableAttrs;114}115116117int118GNEElementTable::ColumnHeader::getAttributeIndex(SumoXMLAttr attr) const {119for (int i = 0; i < (int)myLabels.size(); i++) {120if (myLabels.at(i).first == attr) {121return i;122}123}124return -1;125}126127// ---------------------------------------------------------------------------128// GNEElementTable::Row - methods129// ---------------------------------------------------------------------------130131GNEElementTable::Row::Row(GNEElementTable* elementTable, const size_t rowIndex,132GNEAttributeCarrier* AC) :133FXHorizontalFrame(elementTable->myRowsFrame, GUIDesignAuxiliarHorizontalFrame),134myElementTable(elementTable),135myRowIndex(rowIndex),136myAC(AC) {137// create and disable index label138myIndexLabel = new FXLabel(this, std::to_string(rowIndex + 1).c_str(), nullptr, GUIDesignLabelIconThick);139// create horizontal frame for text fields packed uniformly140FXHorizontalFrame* textFieldsFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrameUniform);141// create text fields142const auto toolTip = AC->getNet()->getViewNet()->getViewParent()->getGNEAppWindows()->getStaticTooltipMenu();143for (const auto& attrProperty : AC->getTagProperty()->getAttributeProperties()) {144// check if this attribute can be edited in dialog145if (attrProperty->isDialogEditor()) {146// create text field targeting the GNEElementTable147auto textField = new MFXTextFieldIcon(textFieldsFrame, toolTip, GUIIcon::EMPTY,148this, MID_GNE_ELEMENTTABLE_EDIT, GUIDesignTextField);149// set value from attribute carrier150textField->setText(AC->getAttribute(attrProperty->getAttr()).c_str());151// add in AttributeTextFields vector152myAttributeTextFields.push_back(std::make_pair(attrProperty->getAttr(), textField));153}154}155// create remove button targeting the GNEDialog156myRemoveButton = new FXButton(this, "", GUIIconSubSys::getIcon(GUIIcon::REMOVE), this,157MID_GNE_ELEMENTTABLE_REMOVE, GUIDesignButtonIcon);158// check if create vClass dialog button159if (elementTable->myOptions & GNEElementList::Options::DIALOG_VCLASS) {160// create open dialog button targeting the GNEDialog161myOpenVClassButton = new FXButton(this, "", GUIIconSubSys::getIcon(GUIIcon::VEHICLE), this,162MID_GNE_ELEMENTTABLE_DIALOG_VCLASS, GUIDesignButtonIcon);163}164// chekc if create element dialog button165if (elementTable->myOptions & GNEElementList::Options::DIALOG_ELEMENT) {166// create open dialog button targeting the GNEDialog167myOpenDialogButton = new FXButton(this, "", GUIIconSubSys::getIcon(GUIIcon::MODEINSPECT), this,168MID_GNE_ELEMENTTABLE_DIALOG_ELEMENT, GUIDesignButtonIcon);169}170// create row if table was previously created171if (elementTable->id() != 0) {172create();173elementTable->myRowsFrame->recalc();174}175}176177178GNEElementTable::Row::~Row() {}179180181void182GNEElementTable::Row::enableRow() {183// enable index label184myIndexLabel->enable();185// enable all text fields186for (const auto& attributeTextField : myAttributeTextFields) {187attributeTextField.second->enable();188}189// enable remove button190myRemoveButton->enable();191// enable open dialog button if it exists192if (myOpenDialogButton) {193myOpenDialogButton->enable();194}195}196197198void199GNEElementTable::Row::disableRow() {200// disable index label201myIndexLabel->disable();202// disable all text fields203for (const auto& attributeTextField : myAttributeTextFields) {204attributeTextField.second->disable();205}206// disable remove button207myRemoveButton->disable();208// disable open dialog button if it exists209if (myOpenDialogButton) {210myOpenDialogButton->disable();211}212}213214215void216GNEElementTable::Row::updateRow(GNEAttributeCarrier* AC) {217// set new attribute carrier218myAC = AC;219// update text fields220for (const auto& attributeTextField : myAttributeTextFields) {221// get value from attribute carrier222const std::string value = myAC->getAttribute(attributeTextField.first);223// set text in text field224attributeTextField.second->setText(value.c_str());225// set valid color226attributeTextField.second->setTextColor(GUIDesignTextColorBlack);227}228}229230231std::string232GNEElementTable::Row::getValue(const size_t column) const {233// check index234if (column < myAttributeTextFields.size()) {235// return text from text field236return myAttributeTextFields.at(column).second->getText().text();237} else {238throw ProcessError("Column ndex out of bounds in GNEElementTable::Row::getValue");239}240}241242243bool244GNEElementTable::Row::isValid() const {245// iterate over all text fields246for (const auto& attributeTextField : myAttributeTextFields) {247// check if text fields colors are valid248if ((attributeTextField.second->getTextColor() == GUIDesignTextColorRed) ||249(attributeTextField.second->getBackColor() == GUIDesignBackgroundColorRed)) {250return false;251}252}253return true;254}255256257long258GNEElementTable::Row::onCmdEditRow(FXObject* sender, FXSelector, void*) {259// iterate over all text fields260for (const auto& attributeTextField : myAttributeTextFields) {261// check if sender is the text field262if (attributeTextField.second == sender) {263// get value264const std::string value = attributeTextField.second->getText().text();265// check if the value is valid266if (!myAC->isValid(attributeTextField.first, value)) {267// set red color268attributeTextField.second->setTextColor(GUIDesignTextColorRed);269// set background red270if (value.empty()) {271attributeTextField.second->setBackColor(GUIDesignBackgroundColorRed);272}273} else {274// set value in GNEAttributeCarrier using undo-redo275myAC->setAttribute(attributeTextField.first, value, myAC->getNet()->getViewNet()->getUndoList());276// restore black color and kill focus277attributeTextField.second->setTextColor(GUIDesignTextColorBlack);278attributeTextField.second->setBackColor(GUIDesignBackgroundColorWhite);279attributeTextField.second->killFocus();280}281// stop after found text field282return 1;283}284}285return 0;286}287288289long290GNEElementTable::Row::onCmdRemoveRow(FXObject*, FXSelector, void*) {291return myElementTable->myElementList->removeElement(myRowIndex);292}293294295long296GNEElementTable::Row::onCmdOpenElementDialog(FXObject*, FXSelector, void*) {297return myElementTable->myElementList->openElementDialog(myRowIndex);298}299300301long302GNEElementTable::Row::onCmdOpenVClassDialog(FXObject*, FXSelector, void*) {303// get column with 'allow' attribute304const int allowColumnIndex = myElementTable->myColumnHeader->getAttributeIndex(SUMO_ATTR_ALLOW);305if (allowColumnIndex >= 0) {306// declare allowVClassesDialog307const auto allowVClassesDialog = new GNEVClassesDialog(myAC->getNet()->getViewNet()->getViewParent()->getGNEAppWindows(),308SUMO_ATTR_ALLOW, myAC->getAttribute(SUMO_ATTR_ALLOW));309// continue depending of result310if (allowVClassesDialog->getResult() == GNEDialog::Result::ACCEPT) {311myAttributeTextFields.at(allowColumnIndex).second->setText(allowVClassesDialog->getModifiedVClasses().c_str(), TRUE);312}313}314return 1;315}316317// ---------------------------------------------------------------------------318// GNEElementTable - methods319// ---------------------------------------------------------------------------320321GNEElementTable::GNEElementTable(GNEElementList* elementList, const GNETagProperties* tagProperties, GNEElementList::Options options) :322FXVerticalFrame(elementList, LAYOUT_FIX_WIDTH | ((options & GNEElementList::Options::FIXED_HEIGHT) ? LAYOUT_FIX_HEIGHT : LAYOUT_FILL_Y),3230, 0, 400, 300, 0, 0, 0, 0, 0, 0),324myElementList(elementList),325myOptions(options) {326// create column header327myColumnHeader = new ColumnHeader(this, tagProperties);328// create scroll windows for rows329myScrollWindow = new FXScrollWindow(this, GUIDesignScrollWindowFixedWidth(400));330// create vertical frame for rows and set back331myRowsFrame = new FXVerticalFrame(myScrollWindow, GUIDesignAuxiliarFrame);332myRowsFrame->setBackColor(GUIDesignBackgroundColorWhite);333}334335336GNEElementTable::~GNEElementTable() {337}338339340GNEElementTable::ColumnHeader*341GNEElementTable::getColumnHeader() const {342return myColumnHeader;343}344345346void347GNEElementTable::enableTable() {348// enable all rows349for (const auto& row : myRows) {350row->enableRow();351}352// enable horizontal frame353enable();354}355356357void358GNEElementTable::disableTable() {359// disable all rows360for (const auto& row : myRows) {361row->disableRow();362}363// disable horizontal frame364disable();365}366367368bool369GNEElementTable::isValid() const {370// check if we have any row invalid371for (const auto& row : myRows) {372if (!row->isValid()) {373return false;374}375}376return true;377}378379380void381GNEElementTable::resizeTable(const size_t numRows) {382// simply remove the rows if numRows is less than the current size383while (myRows.size() > numRows) {384delete myRows.back();385myRows.pop_back();386}387}388389390void391GNEElementTable::updateRow(const size_t index, GNEAttributeCarrier* AC) {392// continue depending of the index393if (index < myRows.size()) {394// simply update the row395myRows.at(index)->updateRow(AC);396} else if (index == myRows.size()) {397// create new row and add it to the list398myRows.push_back(new Row(this, index, AC));399} else {400throw ProcessError("Index out of bounds in GNEElementTable::updateRow");401}402}403404405std::string406GNEElementTable::getValue(const size_t rowIndex, const size_t columnIndex) const {407if (rowIndex < myRows.size()) {408return myRows.at(rowIndex)->getValue(columnIndex);409} else {410throw ProcessError("Row index out of bounds in GNEElementTable::getValue");411}412}413414/****************************************************************************/415416417