Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netedit/dialogs/elements/lists/GNEElementTable.cpp
193687 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2026 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 GNEElementTable.cpp
15
/// @author Pablo Alvarez Lopez
16
/// @date Aug 2025
17
///
18
// Table used in GNEElementList
19
/****************************************************************************/
20
21
#include <netedit/dialogs/GNEVClassesDialog.h>
22
#include <netedit/GNEApplicationWindow.h>
23
#include <netedit/GNENet.h>
24
#include <netedit/GNETagProperties.h>
25
#include <netedit/GNEViewParent.h>
26
#include <utils/foxtools/MFXTextFieldIcon.h>
27
#include <utils/gui/div/GUIDesigns.h>
28
29
#include "GNEElementTable.h"
30
31
// ===========================================================================
32
// FOX callback mapping
33
// ===========================================================================
34
35
FXDEFMAP(GNEElementTable::Row) RowMap[] = {
36
FXMAPFUNC(SEL_COMMAND, MID_GNE_ELEMENTTABLE_EDIT, GNEElementTable::Row::onCmdEditRow),
37
FXMAPFUNC(SEL_COMMAND, MID_GNE_ELEMENTTABLE_REMOVE, GNEElementTable::Row::onCmdRemoveRow),
38
FXMAPFUNC(SEL_COMMAND, MID_GNE_ELEMENTTABLE_DIALOG_VCLASS, GNEElementTable::Row::onCmdOpenVClassDialog),
39
FXMAPFUNC(SEL_COMMAND, MID_GNE_ELEMENTTABLE_DIALOG_ELEMENT, GNEElementTable::Row::onCmdOpenElementDialog)
40
};
41
42
// Object implementation
43
FXIMPLEMENT(GNEElementTable::Row, FXHorizontalFrame, RowMap, ARRAYNUMBER(RowMap))
44
45
// ===========================================================================
46
// method definitions
47
// ===========================================================================
48
49
// ---------------------------------------------------------------------------
50
// GNEElementTable::ColumnHeader - methods
51
// ---------------------------------------------------------------------------
52
53
GNEElementTable::ColumnHeader::ColumnHeader(GNEElementTable* elementTable, const GNETagProperties* tagProperties) :
54
FXHorizontalFrame(elementTable, GUIDesignAuxiliarHorizontalFrame) {
55
// create horizontal label with uniform width
56
auto horizontalFrameLabels = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrameUniform);
57
// create empty label
58
new FXLabel(horizontalFrameLabels, "", nullptr, GUIDesignLabelFixed(GUIDesignHeight));
59
// create a label for every attribute
60
for (const auto& attrProperty : tagProperties->getAttributeProperties()) {
61
// check if this attribute can be edited in dialog
62
if (attrProperty->isDialogEditor()) {
63
// create label
64
myLabels.push_back(std::make_pair(attrProperty->getAttr(), new FXLabel(horizontalFrameLabels, attrProperty->getAttrStr().c_str(),
65
nullptr, GUIDesignLabelThick(JUSTIFY_NORMAL))));
66
// check if this attribute is sortable
67
if (attrProperty->isSortable()) {
68
mySortableAttrs.push_back(attrProperty->getAttr());
69
}
70
}
71
}
72
// calculate buttons label width
73
int buttonLabelWidth = 15 + GUIDesignHeight;
74
if (elementTable->myOptions & GNEElementList::Options::DIALOG_ELEMENT) {
75
buttonLabelWidth += GUIDesignHeight;
76
}
77
if (elementTable->myOptions & GNEElementList::Options::DIALOG_VCLASS) {
78
buttonLabelWidth += GUIDesignHeight;
79
}
80
// create empty label (icons and vertical scroller)
81
new FXLabel(horizontalFrameLabels, "", nullptr, GUIDesignLabelFixed(buttonLabelWidth));
82
}
83
84
85
GNEElementTable::ColumnHeader::~ColumnHeader() {}
86
87
88
void
89
GNEElementTable::ColumnHeader::enableRowHeader() {
90
// enable all labels
91
for (const auto& label : myLabels) {
92
label.second->enable();
93
}
94
}
95
96
97
void
98
GNEElementTable::ColumnHeader::disableRowHeader() {
99
// disable all labels
100
for (const auto& label : myLabels) {
101
label.second->disable();
102
}
103
}
104
105
106
size_t
107
GNEElementTable::ColumnHeader::getNumColumns() const {
108
return myLabels.size();
109
}
110
111
112
const std::vector<SumoXMLAttr>&
113
GNEElementTable::ColumnHeader::getSortableAttributes() {
114
return mySortableAttrs;
115
}
116
117
118
int
119
GNEElementTable::ColumnHeader::getAttributeIndex(SumoXMLAttr attr) const {
120
for (int i = 0; i < (int)myLabels.size(); i++) {
121
if (myLabels.at(i).first == attr) {
122
return i;
123
}
124
}
125
return -1;
126
}
127
128
// ---------------------------------------------------------------------------
129
// GNEElementTable::Row - methods
130
// ---------------------------------------------------------------------------
131
132
GNEElementTable::Row::Row(GNEElementTable* elementTable, const size_t rowIndex,
133
GNEAttributeCarrier* AC) :
134
FXHorizontalFrame(elementTable->myRowsFrame, GUIDesignAuxiliarHorizontalFrame),
135
myElementTable(elementTable),
136
myRowIndex(rowIndex),
137
myAC(AC) {
138
// create and disable index label
139
myIndexLabel = new FXLabel(this, std::to_string(rowIndex + 1).c_str(), nullptr, GUIDesignLabelIconThick);
140
// create horizontal frame for text fields packed uniformly
141
FXHorizontalFrame* textFieldsFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrameUniform);
142
// create text fields
143
const auto toolTip = AC->getNet()->getGNEApplicationWindow()->getStaticTooltipMenu();
144
for (const auto& attrProperty : AC->getTagProperty()->getAttributeProperties()) {
145
// check if this attribute can be edited in dialog
146
if (attrProperty->isDialogEditor()) {
147
// create text field targeting the GNEElementTable
148
auto textField = new MFXTextFieldIcon(textFieldsFrame, toolTip, GUIIcon::EMPTY,
149
this, MID_GNE_ELEMENTTABLE_EDIT, GUIDesignTextField);
150
// set value from attribute carrier
151
textField->setText(AC->getAttribute(attrProperty->getAttr()).c_str());
152
// add in AttributeTextFields vector
153
myAttributeTextFields.push_back(std::make_pair(attrProperty->getAttr(), textField));
154
}
155
}
156
// create remove button targeting the GNEDialog
157
myRemoveButton = new FXButton(this, "", GUIIconSubSys::getIcon(GUIIcon::REMOVE), this,
158
MID_GNE_ELEMENTTABLE_REMOVE, GUIDesignButtonIcon);
159
// check if create vClass dialog button
160
if (elementTable->myOptions & GNEElementList::Options::DIALOG_VCLASS) {
161
// create open dialog button targeting the GNEDialog
162
myOpenVClassButton = new FXButton(this, "", GUIIconSubSys::getIcon(GUIIcon::VEHICLE), this,
163
MID_GNE_ELEMENTTABLE_DIALOG_VCLASS, GUIDesignButtonIcon);
164
}
165
// chekc if create element dialog button
166
if (elementTable->myOptions & GNEElementList::Options::DIALOG_ELEMENT) {
167
// create open dialog button targeting the GNEDialog
168
myOpenDialogButton = new FXButton(this, "", GUIIconSubSys::getIcon(GUIIcon::MODEINSPECT), this,
169
MID_GNE_ELEMENTTABLE_DIALOG_ELEMENT, GUIDesignButtonIcon);
170
}
171
// create row if table was previously created
172
if (elementTable->id() != 0) {
173
create();
174
elementTable->myRowsFrame->recalc();
175
}
176
}
177
178
179
GNEElementTable::Row::~Row() {}
180
181
182
void
183
GNEElementTable::Row::enableRow() {
184
// enable index label
185
myIndexLabel->enable();
186
// enable all text fields
187
for (const auto& attributeTextField : myAttributeTextFields) {
188
attributeTextField.second->enable();
189
}
190
// enable remove button
191
myRemoveButton->enable();
192
// enable open dialog button if it exists
193
if (myOpenDialogButton) {
194
myOpenDialogButton->enable();
195
}
196
}
197
198
199
void
200
GNEElementTable::Row::disableRow() {
201
// disable index label
202
myIndexLabel->disable();
203
// disable all text fields
204
for (const auto& attributeTextField : myAttributeTextFields) {
205
attributeTextField.second->disable();
206
}
207
// disable remove button
208
myRemoveButton->disable();
209
// disable open dialog button if it exists
210
if (myOpenDialogButton) {
211
myOpenDialogButton->disable();
212
}
213
}
214
215
216
void
217
GNEElementTable::Row::updateRow(GNEAttributeCarrier* AC) {
218
// set new attribute carrier
219
myAC = AC;
220
// update text fields
221
for (const auto& attributeTextField : myAttributeTextFields) {
222
// get value from attribute carrier
223
const std::string value = myAC->getAttribute(attributeTextField.first);
224
// set text in text field
225
attributeTextField.second->setText(value.c_str());
226
// set valid color
227
attributeTextField.second->setTextColor(GUIDesignTextColorBlack);
228
}
229
}
230
231
232
std::string
233
GNEElementTable::Row::getValue(const size_t column) const {
234
// check index
235
if (column < myAttributeTextFields.size()) {
236
// return text from text field
237
return myAttributeTextFields.at(column).second->getText().text();
238
} else {
239
throw ProcessError("Column ndex out of bounds in GNEElementTable::Row::getValue");
240
}
241
}
242
243
244
bool
245
GNEElementTable::Row::isValid() const {
246
// iterate over all text fields
247
for (const auto& attributeTextField : myAttributeTextFields) {
248
// check if text fields colors are valid
249
if ((attributeTextField.second->getTextColor() == GUIDesignTextColorRed) ||
250
(attributeTextField.second->getBackColor() == GUIDesignBackgroundColorRed)) {
251
return false;
252
}
253
}
254
return true;
255
}
256
257
258
long
259
GNEElementTable::Row::onCmdEditRow(FXObject* sender, FXSelector, void*) {
260
// iterate over all text fields
261
for (const auto& attributeTextField : myAttributeTextFields) {
262
// check if sender is the text field
263
if (attributeTextField.second == sender) {
264
// get value
265
const std::string value = attributeTextField.second->getText().text();
266
// check if the value is valid
267
if (!myAC->isValid(attributeTextField.first, value)) {
268
// set red color
269
attributeTextField.second->setTextColor(GUIDesignTextColorRed);
270
// set background red
271
if (value.empty()) {
272
attributeTextField.second->setBackColor(GUIDesignBackgroundColorRed);
273
}
274
} else {
275
// set value in GNEAttributeCarrier using undo-redo
276
myAC->setAttribute(attributeTextField.first, value, myAC->getNet()->getUndoList());
277
// restore black color and kill focus
278
attributeTextField.second->setTextColor(GUIDesignTextColorBlack);
279
attributeTextField.second->setBackColor(GUIDesignBackgroundColorWhite);
280
attributeTextField.second->killFocus();
281
}
282
// stop after found text field
283
return 1;
284
}
285
}
286
return 0;
287
}
288
289
290
long
291
GNEElementTable::Row::onCmdRemoveRow(FXObject*, FXSelector, void*) {
292
return myElementTable->myElementList->removeElement(myRowIndex);
293
}
294
295
296
long
297
GNEElementTable::Row::onCmdOpenElementDialog(FXObject*, FXSelector, void*) {
298
return myElementTable->myElementList->openElementDialog(myRowIndex);
299
}
300
301
302
long
303
GNEElementTable::Row::onCmdOpenVClassDialog(FXObject*, FXSelector, void*) {
304
// get column with 'allow' attribute
305
const int allowColumnIndex = myElementTable->myColumnHeader->getAttributeIndex(SUMO_ATTR_ALLOW);
306
if (allowColumnIndex >= 0) {
307
// declare allowVClassesDialog
308
const auto allowVClassesDialog = new GNEVClassesDialog(myAC->getNet()->getGNEApplicationWindow(),
309
myElementTable->myElementList->getDialogParent(),
310
SUMO_ATTR_ALLOW, myAC->getAttribute(SUMO_ATTR_ALLOW));
311
// continue depending of result
312
if (allowVClassesDialog->getResult() == GNEDialog::Result::ACCEPT) {
313
myAttributeTextFields.at(allowColumnIndex).second->setText(allowVClassesDialog->getModifiedVClasses().c_str(), TRUE);
314
}
315
}
316
return 1;
317
}
318
319
// ---------------------------------------------------------------------------
320
// GNEElementTable - methods
321
// ---------------------------------------------------------------------------
322
323
GNEElementTable::GNEElementTable(GNEElementList* elementList, const GNETagProperties* tagProperties, GNEElementList::Options options) :
324
FXVerticalFrame(elementList, LAYOUT_FIX_WIDTH | ((options & GNEElementList::Options::FIXED_HEIGHT) ? LAYOUT_FIX_HEIGHT : LAYOUT_FILL_Y),
325
0, 0, 400, 300, 0, 0, 0, 0, 0, 0),
326
myElementList(elementList),
327
myOptions(options) {
328
// create column header
329
myColumnHeader = new ColumnHeader(this, tagProperties);
330
// create scroll windows for rows
331
myScrollWindow = new FXScrollWindow(this, GUIDesignScrollWindowFixedWidth(400));
332
// create vertical frame for rows and set back
333
myRowsFrame = new FXVerticalFrame(myScrollWindow, GUIDesignAuxiliarFrame);
334
myRowsFrame->setBackColor(GUIDesignBackgroundColorWhite);
335
}
336
337
338
GNEElementTable::~GNEElementTable() {
339
}
340
341
342
GNEElementTable::ColumnHeader*
343
GNEElementTable::getColumnHeader() const {
344
return myColumnHeader;
345
}
346
347
348
void
349
GNEElementTable::enableTable() {
350
// enable all rows
351
for (const auto& row : myRows) {
352
row->enableRow();
353
}
354
// enable horizontal frame
355
enable();
356
}
357
358
359
void
360
GNEElementTable::disableTable() {
361
// disable all rows
362
for (const auto& row : myRows) {
363
row->disableRow();
364
}
365
// disable horizontal frame
366
disable();
367
}
368
369
370
bool
371
GNEElementTable::isValid() const {
372
// check if we have any row invalid
373
for (const auto& row : myRows) {
374
if (!row->isValid()) {
375
return false;
376
}
377
}
378
return true;
379
}
380
381
382
void
383
GNEElementTable::resizeTable(const size_t numRows) {
384
// simply remove the rows if numRows is less than the current size
385
while (myRows.size() > numRows) {
386
delete myRows.back();
387
myRows.pop_back();
388
}
389
}
390
391
392
void
393
GNEElementTable::updateRow(const size_t index, GNEAttributeCarrier* AC) {
394
// continue depending of the index
395
if (index < myRows.size()) {
396
// simply update the row
397
myRows.at(index)->updateRow(AC);
398
} else if (index == myRows.size()) {
399
// create new row and add it to the list
400
myRows.push_back(new Row(this, index, AC));
401
} else {
402
throw ProcessError("Index out of bounds in GNEElementTable::updateRow");
403
}
404
}
405
406
407
std::string
408
GNEElementTable::getValue(const size_t rowIndex, const size_t columnIndex) const {
409
if (rowIndex < myRows.size()) {
410
return myRows.at(rowIndex)->getValue(columnIndex);
411
} else {
412
throw ProcessError("Row index out of bounds in GNEElementTable::getValue");
413
}
414
}
415
416
/****************************************************************************/
417
418