Path: blob/main/src/netedit/dialogs/elements/lists/GNEElementTable.cpp
193687 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2026 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->isSortable()) {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()->getGNEApplicationWindow()->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()->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()->getGNEApplicationWindow(),308myElementTable->myElementList->getDialogParent(),309SUMO_ATTR_ALLOW, myAC->getAttribute(SUMO_ATTR_ALLOW));310// continue depending of result311if (allowVClassesDialog->getResult() == GNEDialog::Result::ACCEPT) {312myAttributeTextFields.at(allowColumnIndex).second->setText(allowVClassesDialog->getModifiedVClasses().c_str(), TRUE);313}314}315return 1;316}317318// ---------------------------------------------------------------------------319// GNEElementTable - methods320// ---------------------------------------------------------------------------321322GNEElementTable::GNEElementTable(GNEElementList* elementList, const GNETagProperties* tagProperties, GNEElementList::Options options) :323FXVerticalFrame(elementList, LAYOUT_FIX_WIDTH | ((options & GNEElementList::Options::FIXED_HEIGHT) ? LAYOUT_FIX_HEIGHT : LAYOUT_FILL_Y),3240, 0, 400, 300, 0, 0, 0, 0, 0, 0),325myElementList(elementList),326myOptions(options) {327// create column header328myColumnHeader = new ColumnHeader(this, tagProperties);329// create scroll windows for rows330myScrollWindow = new FXScrollWindow(this, GUIDesignScrollWindowFixedWidth(400));331// create vertical frame for rows and set back332myRowsFrame = new FXVerticalFrame(myScrollWindow, GUIDesignAuxiliarFrame);333myRowsFrame->setBackColor(GUIDesignBackgroundColorWhite);334}335336337GNEElementTable::~GNEElementTable() {338}339340341GNEElementTable::ColumnHeader*342GNEElementTable::getColumnHeader() const {343return myColumnHeader;344}345346347void348GNEElementTable::enableTable() {349// enable all rows350for (const auto& row : myRows) {351row->enableRow();352}353// enable horizontal frame354enable();355}356357358void359GNEElementTable::disableTable() {360// disable all rows361for (const auto& row : myRows) {362row->disableRow();363}364// disable horizontal frame365disable();366}367368369bool370GNEElementTable::isValid() const {371// check if we have any row invalid372for (const auto& row : myRows) {373if (!row->isValid()) {374return false;375}376}377return true;378}379380381void382GNEElementTable::resizeTable(const size_t numRows) {383// simply remove the rows if numRows is less than the current size384while (myRows.size() > numRows) {385delete myRows.back();386myRows.pop_back();387}388}389390391void392GNEElementTable::updateRow(const size_t index, GNEAttributeCarrier* AC) {393// continue depending of the index394if (index < myRows.size()) {395// simply update the row396myRows.at(index)->updateRow(AC);397} else if (index == myRows.size()) {398// create new row and add it to the list399myRows.push_back(new Row(this, index, AC));400} else {401throw ProcessError("Index out of bounds in GNEElementTable::updateRow");402}403}404405406std::string407GNEElementTable::getValue(const size_t rowIndex, const size_t columnIndex) const {408if (rowIndex < myRows.size()) {409return myRows.at(rowIndex)->getValue(columnIndex);410} else {411throw ProcessError("Row index out of bounds in GNEElementTable::getValue");412}413}414415/****************************************************************************/416417418