Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netedit/dialogs/elements/lists/GNEElementTable.cpp
169688 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2025 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->isNumerical()) {
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()->getViewNet()->getViewParent()->getGNEAppWindows()->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()->getViewNet()->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()->getViewNet()->getViewParent()->getGNEAppWindows(),
309
SUMO_ATTR_ALLOW, myAC->getAttribute(SUMO_ATTR_ALLOW));
310
// continue depending of result
311
if (allowVClassesDialog->getResult() == GNEDialog::Result::ACCEPT) {
312
myAttributeTextFields.at(allowColumnIndex).second->setText(allowVClassesDialog->getModifiedVClasses().c_str(), TRUE);
313
}
314
}
315
return 1;
316
}
317
318
// ---------------------------------------------------------------------------
319
// GNEElementTable - methods
320
// ---------------------------------------------------------------------------
321
322
GNEElementTable::GNEElementTable(GNEElementList* elementList, const GNETagProperties* tagProperties, GNEElementList::Options options) :
323
FXVerticalFrame(elementList, LAYOUT_FIX_WIDTH | ((options & GNEElementList::Options::FIXED_HEIGHT) ? LAYOUT_FIX_HEIGHT : LAYOUT_FILL_Y),
324
0, 0, 400, 300, 0, 0, 0, 0, 0, 0),
325
myElementList(elementList),
326
myOptions(options) {
327
// create column header
328
myColumnHeader = new ColumnHeader(this, tagProperties);
329
// create scroll windows for rows
330
myScrollWindow = new FXScrollWindow(this, GUIDesignScrollWindowFixedWidth(400));
331
// create vertical frame for rows and set back
332
myRowsFrame = new FXVerticalFrame(myScrollWindow, GUIDesignAuxiliarFrame);
333
myRowsFrame->setBackColor(GUIDesignBackgroundColorWhite);
334
}
335
336
337
GNEElementTable::~GNEElementTable() {
338
}
339
340
341
GNEElementTable::ColumnHeader*
342
GNEElementTable::getColumnHeader() const {
343
return myColumnHeader;
344
}
345
346
347
void
348
GNEElementTable::enableTable() {
349
// enable all rows
350
for (const auto& row : myRows) {
351
row->enableRow();
352
}
353
// enable horizontal frame
354
enable();
355
}
356
357
358
void
359
GNEElementTable::disableTable() {
360
// disable all rows
361
for (const auto& row : myRows) {
362
row->disableRow();
363
}
364
// disable horizontal frame
365
disable();
366
}
367
368
369
bool
370
GNEElementTable::isValid() const {
371
// check if we have any row invalid
372
for (const auto& row : myRows) {
373
if (!row->isValid()) {
374
return false;
375
}
376
}
377
return true;
378
}
379
380
381
void
382
GNEElementTable::resizeTable(const size_t numRows) {
383
// simply remove the rows if numRows is less than the current size
384
while (myRows.size() > numRows) {
385
delete myRows.back();
386
myRows.pop_back();
387
}
388
}
389
390
391
void
392
GNEElementTable::updateRow(const size_t index, GNEAttributeCarrier* AC) {
393
// continue depending of the index
394
if (index < myRows.size()) {
395
// simply update the row
396
myRows.at(index)->updateRow(AC);
397
} else if (index == myRows.size()) {
398
// create new row and add it to the list
399
myRows.push_back(new Row(this, index, AC));
400
} else {
401
throw ProcessError("Index out of bounds in GNEElementTable::updateRow");
402
}
403
}
404
405
406
std::string
407
GNEElementTable::getValue(const size_t rowIndex, const size_t columnIndex) const {
408
if (rowIndex < myRows.size()) {
409
return myRows.at(rowIndex)->getValue(columnIndex);
410
} else {
411
throw ProcessError("Row index out of bounds in GNEElementTable::getValue");
412
}
413
}
414
415
/****************************************************************************/
416
417