Eclipse SUMO - Simulation of Urban MObility
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
GNEUndoList.cpp
Go to the documentation of this file.
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/****************************************************************************/
19/****************************************************************************/
20
22#include <netedit/GNEViewNet.h>
28
30#include "GNEUndoList.h"
31
32// ===========================================================================
33// FOX callback mapping
34// ===========================================================================
35FXDEFMAP(GNEUndoList) GNEUndoListMap[] = {
36 FXMAPFUNC(SEL_COMMAND, MID_HOTKEY_CTRL_Z_UNDO, GNEUndoList::onCmdUndo),
37 FXMAPFUNC(SEL_UPDATE, MID_HOTKEY_CTRL_Z_UNDO, GNEUndoList::onUpdUndo),
38 FXMAPFUNC(SEL_COMMAND, MID_HOTKEY_CTRL_Y_REDO, GNEUndoList::onCmdRedo),
39 FXMAPFUNC(SEL_UPDATE, MID_HOTKEY_CTRL_Y_REDO, GNEUndoList::onUpdRedo),
40};
41
42// ===========================================================================
43// FOX-declarations
44// ===========================================================================
45
46FXIMPLEMENT_ABSTRACT(GNEUndoList, GNEChangeGroup, GNEUndoListMap, ARRAYNUMBER(GNEUndoListMap))
47
48
49// ===========================================================================
50// member method definitions
51// ===========================================================================
52
53// ---------------------------------------------------------------------------
54// GNEUndoList::Iterator
55// ---------------------------------------------------------------------------
56
58
59
60bool
62 return myCurrentChange == nullptr;
63}
64
65
66int
68 return myIndex;
69}
70
71
72const std::string
74 std::string redoName = myCurrentChange->redoName();
75 return redoName;
76}
77
78
79const std::string
81 return dynamic_cast<GNEChangeGroup*>(myCurrentChange)->getTimeStamp();
82}
83
84
85FXIcon*
87 const GNEChangeGroup* changeGroup = dynamic_cast<GNEChangeGroup*>(myCurrentChange);
88 if (changeGroup) {
89 return GUIIconSubSys::getIcon(changeGroup->getGroupIcon());
90 } else {
91 return nullptr;
92 }
93}
94
95
98 // move current change to next element
99 myCurrentChange = myCurrentChange->next;
100 // update index
101 myIndex++;
102 return *this;
103}
104
105
107 myCurrentChange(change),
108 myIndex(0) {
109}
110
111
113 myCurrentChange(nullptr),
114 myIndex(0) {
115}
116
117
121
122
126
127// ---------------------------------------------------------------------------
128// GNEUndoList
129// ---------------------------------------------------------------------------
130
135
136
138
139
140void
142 GNEChange* change = nullptr;
143 if (group) {
144 throw ProcessError("GNEUndoList::undo() cannot call undo inside begin-end block");
145 }
146 if (undoList) {
147 myWorking = true;
148 change = undoList;
149 // Remove from undoList BEFORE undo
151 change->undo();
152 // Hang into redoList AFTER undo
153 change->next = redoList;
154 redoList = change;
155 myWorking = false;
156 // update view net (is called only if gViewUpdater.allowUpdate() is enable)
158 }
159 // update specific controls
161}
162
163
164void
166 GNEChange* change = nullptr;
167 if (group) {
168 throw ProcessError("GNEUndoList::redo() cannot call undo inside begin-end block");
169 }
170 if (redoList) {
171 myWorking = true;
172 change = redoList;
173 // Remove from redoList BEFORE redo
175 change->redo();
176 // Hang into undoList AFTER redo
177 change->next = undoList;
178 undoList = change;
179 myWorking = false;
180 // update view net (is called only if gViewUpdater.allowUpdate() is enable)
182 }
183 // update specific controls
185}
186
187
188std::string
190 if (undoList) {
191 return undoList->undoName();
192 } else {
193 return "";
194 }
195}
196
197
198std::string
200 if (redoList) {
201 return redoList->redoName();
202 } else {
203 return "";
204 }
205}
206
207
208void
209GNEUndoList::begin(GUIIcon icon, const std::string& description) {
212 } else {
213 begin(Supermode::NETWORK, icon, description);
214 }
215}
216
217
218void
219GNEUndoList::begin(const GNEAttributeCarrier* AC, const std::string& description) {
220 begin(AC->getTagProperty()->getGUIIcon(), description);
221}
222
223
224void
225GNEUndoList::begin(Supermode supermode, GUIIcon icon, const std::string& description) {
226 myChangeGroups.push(new GNEChangeGroup(supermode, icon, description));
227 // get this reference
228 GNEChangeGroup* changeGroup = this;
229 // Calling begin while in the middle of doing something!
230 if (myWorking) {
231 throw ProcessError("GNEChangeGroup::begin: already working on undo or redo");
232 }
233 // Cut redo list
234 cut();
235 // Hunt for end of group chain
236 while (changeGroup->group) {
237 changeGroup = changeGroup->group;
238 }
239 // Add to end
240 changeGroup->group = myChangeGroups.top();
241 // disable update
243}
244
245
246void
248 myChangeGroups.pop();
249 // enable update
251 // update view without ignoring viewUpdater (used to avoid slowdows during massive edits)
253 // check if net has to be updated (called only if this is the last end
255 // check if we have to update selector frame
256 const auto& editModes = myGNEApplicationWindowParent->getViewNet()->getEditModes();
257 if ((editModes.isCurrentSupermodeNetwork() && editModes.networkEditMode == NetworkEditMode::NETWORK_INSPECT) ||
258 (editModes.isCurrentSupermodeDemand() && editModes.demandEditMode == DemandEditMode::DEMAND_INSPECT) ||
259 (editModes.isCurrentSupermodeData() && editModes.dataEditMode == DataEditMode::DATA_INSPECT)) {
260 // refresh inspect frame
262 } else if ((editModes.isCurrentSupermodeNetwork() && editModes.networkEditMode == NetworkEditMode::NETWORK_SELECT) ||
263 (editModes.isCurrentSupermodeDemand() && editModes.demandEditMode == DemandEditMode::DEMAND_SELECT) ||
264 (editModes.isCurrentSupermodeData() && editModes.dataEditMode == DataEditMode::DATA_SELECT)) {
265 // update informacion label in selection frame
267 }
268 }
269 // continue with end
270 GNEChangeGroup* change = nullptr;
271 GNEChangeGroup* changeGroup = this;
272 // Must have called begin
273 if (!changeGroup->group) {
274 throw ProcessError("GNEChangeGroup::end: no matching call to begin");
275 }
276 // Calling end while in the middle of doing something!
277 if (myWorking) {
278 throw ProcessError("GNEChangeGroup::end: already working on undo or redo");
279 }
280 // Hunt for one above end of group chain
281 while (changeGroup->group->group) {
282 changeGroup = changeGroup->group;
283 }
284 // Unlink from group chain
285 change = changeGroup->group;
286 changeGroup->group = nullptr;
287 // Add to group if non-empty
288 if (!change->empty()) {
289 // Append new change to undo list
290 change->next = changeGroup->undoList;
291 changeGroup->undoList = change;
292 } else {
293 // Delete bottom group
294 delete change;
295 }
296}
297
298
299void
301 // abort all change groups
303 // clear
304 GNEChange* change = nullptr;
305 while (redoList) {
306 change = redoList;
308 delete change;
309 }
310 while (undoList) {
311 change = undoList;
313 delete change;
314 }
315 delete group;
316 redoList = nullptr;
317 undoList = nullptr;
318 group = nullptr;
319}
320
321
322void
324 while (hasCommandGroup()) {
325 myChangeGroups.top()->undo();
326 myChangeGroups.pop();
327 // abort current subgroup
329 }
330}
331
332
333void
335 if (myChangeGroups.size() > 0) {
336 myChangeGroups.top()->undo();
337 myChangeGroups.pop();
338 // abort current subgroup
340 }
341}
342
343
344void
345GNEUndoList::add(GNEChange* change, bool doit, bool merge) {
346 GNEChangeGroup* changeGroup = this;
347 // Must pass a change
348 if (!change) {
349 throw ProcessError("GNEChangeGroup::add: nullptr change argument");
350 }
351 // Adding undo while in the middle of doing something!
352 if (myWorking) {
353 throw ProcessError("GNEChangeGroup::add: already working on undo or redo");
354 }
355 myWorking = true;
356 // Cut redo list
357 cut();
358 // Execute change
359 if (doit) {
360 change->redo();
361 }
362 // Hunt for end of group chain
363 while (changeGroup->group) {
364 changeGroup = changeGroup->group;
365 }
366 // Try to merge commands when desired and possible
367 if (merge && changeGroup->undoList && (group != nullptr) && change->canMerge() && changeGroup->undoList->mergeWith(change)) {
368 // Delete incoming change that was merged
369 delete change;
370 } else {
371 // Append incoming change
372 change->next = changeGroup->undoList;
373 changeGroup->undoList = change;
374 }
375 myWorking = false;
376}
377
378
379int
381 if (myChangeGroups.size() > 0) {
382 return myChangeGroups.top()->size();
383 } else {
384 return 0;
385 }
386}
387
388
391 if (undoList) {
392 // try to obtain Change Group
393 const GNEChangeGroup* begin = dynamic_cast<GNEChangeGroup*>(undoList);
394 if (begin) {
395 return begin->getGroupSupermode();
396 } else {
397 return undoList->getSupermode();
398 }
399 } else {
400 return Supermode::NETWORK;
401 }
402}
403
404
407 if (redoList) {
408 // try to obtain Change Group
409 const GNEChangeGroup* begin = dynamic_cast<GNEChangeGroup*>(redoList);
410 if (begin) {
411 return begin->getGroupSupermode();
412 } else {
413 return redoList->getSupermode();
414 }
415 } else {
416 return Supermode::NETWORK;
417 }
418}
419
420
421bool
423 return myChangeGroups.size() != 0;
424}
425
426
427bool
429 return myWorking;
430}
431
432
433long
434GNEUndoList::onCmdUndo(FXObject*, FXSelector, void*) {
435 undo();
436 return 1;
437}
438
439
440long
441GNEUndoList::onUpdUndo(FXObject* sender, FXSelector, void*) {
442 // first check if Undo Menu command or button has to be disabled
443 const bool buttonEnabled = canUndo() && !hasCommandGroup() &&
446 // cast button (see flickering problem #6209)
447 const FXButton* button = dynamic_cast<FXButton*>(sender);
448 // enable or disable depending of "enable" flag
449 if (button) {
450 // avoid unnecessary enables/disables (due flickering)
451 if (buttonEnabled && !button->isEnabled()) {
452 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), nullptr);
453 button->update();
454 } else if (!buttonEnabled && button->isEnabled()) {
455 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
456 button->update();
457 }
458 } else {
459 sender->handle(this, buttonEnabled ? FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE) : FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
460 }
461 // cast menu command
462 FXMenuCommand* menuCommand = dynamic_cast<FXMenuCommand*>(sender);
463 // only set caption on menu command item
464 if (menuCommand) {
465 // change caption of FXMenuCommand
466 std::string caption = undoName();
467 // set caption of FXmenuCommand edit/undo
469 caption = TL("Disabled undo");
471 caption = TL("Cannot Undo in the middle of ") + myGNEApplicationWindowParent->isUndoRedoEnabledTemporally();
472 } else if (hasCommandGroup()) {
473 caption = TL("Cannot Undo in the middle of ") + myChangeGroups.top()->getDescription();
474 } else if (!canUndo()) {
475 caption = TL("Undo");
476 }
477 menuCommand->setText(caption.c_str());
478 menuCommand->update();
479 }
480 return 1;
481}
482
483
484long
485GNEUndoList::onCmdRedo(FXObject*, FXSelector, void*) {
486 redo();
487 return 1;
488}
489
490
491long
492GNEUndoList::onUpdRedo(FXObject* sender, FXSelector, void*) {
493 // first check if Redo Menu command or button has to be disabled
494 const bool enable = canRedo() && !hasCommandGroup() &&
497 // cast button (see #6209)
498 const FXButton* button = dynamic_cast<FXButton*>(sender);
499 // enable or disable depending of "enable" flag
500 if (button) {
501 // avoid unnecessary enables/disables (due flickering)
502 if (enable && !button->isEnabled()) {
503 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), nullptr);
504 button->update();
505 } else if (!enable && button->isEnabled()) {
506 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
507 button->update();
508 }
509 } else {
510 sender->handle(this, enable ? FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE) : FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
511 }
512 // cast menu command
513 FXMenuCommand* menuCommand = dynamic_cast<FXMenuCommand*>(sender);
514 // only set caption on menu command item
515 if (menuCommand) {
516 // change caption of FXMenuCommand
517 std::string caption = redoName();
518 // set caption of FXmenuCommand edit/undo
520 caption = TL("Disabled redo");
522 caption = TL("Cannot Redo in the middle of ") + myGNEApplicationWindowParent->isUndoRedoEnabledTemporally();
523 } else if (hasCommandGroup()) {
524 caption = TL("Cannot Redo in the middle of ") + myChangeGroups.top()->getDescription();
525 } else if (!canRedo()) {
526 caption = TL("Redo");
527 }
528 menuCommand->setText(caption.c_str());
529 menuCommand->update();
530 }
531 return 1;
532}
533
534
535void
537 GNEChange* change = nullptr;
538 while (redoList) {
539 change = redoList;
541 delete change;
542 }
543 redoList = nullptr;
544}
545
546
547void
549 // get reference to change group
550 GNEChangeGroup* changeGroup = this;
551 // Must be called after begin
552 if (!changeGroup->group) {
553 throw ProcessError("GNEChangeGroup::abort: no matching call to begin");
554 }
555 // Calling abort while in the middle of doing something!
556 if (myWorking) {
557 throw ProcessError("GNEChangeGroup::abort: already working on undo or redo");
558 }
559 // Hunt for one above end of group chain
560 while (changeGroup->group->group) {
561 changeGroup = changeGroup->group;
562 }
563 // Delete bottom group
564 delete changeGroup->group;
565 // New end of chain
566 changeGroup->group = nullptr;
567}
568
569
570bool
572 return (undoList != nullptr);
573}
574
575
576bool
578 return (redoList != nullptr);
579}
580
581/******************************/
FXDEFMAP(GNEUndoList) GNEUndoListMap[]
@ DATA_SELECT
mode for selecting data elements
@ DATA_INSPECT
mode for inspecting data elements
Supermode
@brie enum for supermodes
@ NETWORK
Network mode (Edges, junctions, etc..)
@ NETWORK_SELECT
mode for selecting network elements
@ NETWORK_INSPECT
mode for inspecting network elements
@ DEMAND_INSPECT
mode for inspecting demand elements
@ DEMAND_SELECT
mode for selecting demand elements
@ MID_HOTKEY_CTRL_Y_REDO
Undo.
Definition GUIAppEnum.h:133
@ MID_HOTKEY_CTRL_Z_UNDO
Redo.
Definition GUIAppEnum.h:135
GUIViewUpdater gViewUpdater
GUIIcon
An enumeration of icons used by the gui applications.
Definition GUIIcons.h:33
#define TL(string)
Definition MsgHandler.h:305
The main window of Netedit.
const std::string & isUndoRedoEnabledTemporally() const
check if undo-redo is enabled temporally
void updateControls()
update control contents after undo/redo or recompute
GNEViewNet * getViewNet()
get pointer to viewNet
const GNETagProperties * getTagProperty() const
get tagProperty associated with this Attribute Carrier
GNEChange * undoList
undo list command (can be access by GNEUndoList)
const std::string & getTimeStamp()
get timeStamp
GUIIcon getGroupIcon() const
get icon associated with this ChangeGroup
GNEChange * redoList
redo list command (can be access by GNEUndoList)
GNEChangeGroup * group
group (can be access by GNEUndoList)
bool empty() const
Return TRUE if empty.
Supermode getGroupSupermode() const
get supermode associated with this ChangeGroup
void undo()
Undo whole command group.
GNEChangeGroup()
FOX need this.
the function-object for an editing operation (abstract base)
Definition GNEChange.h:56
virtual void redo()=0
redo action/operation
virtual int size() const
Return the size of the command group.
Definition GNEChange.cpp:59
virtual void undo()=0
undo action/operation
Supermode getSupermode() const
get supermode
Definition GNEChange.cpp:66
bool mergeWith(GNEChange *command)
Called by the undo system to try and merge the new incoming command with this command; should return ...
Definition GNEChange.cpp:78
friend class GNEUndoList
Definition GNEChange.h:62
virtual std::string redoName() const =0
return redoName
GNEChange * next
Definition GNEChange.h:187
virtual std::string undoName() const =0
return undoName
bool canMerge() const
Return TRUE if this command can be merged with previous undo commands. This is useful to combine e....
Definition GNEChange.cpp:72
void refreshInspection()
refresh current inspection
void updateInformationLabel()
update information label
SelectionInformation * getSelectionInformation() const
get modul for selection information
GUIIcon getGUIIcon() const
get GUI icon associated to this tag property
FOX declaration.
Definition GNEUndoList.h:48
const std::string getTimeStamp() const
get timeStamp
const std::string getDescription() const
get description
bool end() const
check if iterator is at the end
FXIcon * getIcon() const
get icon
Iterator()
default constructor
Iterator & operator++(int)
increment operator
GNEChange * myCurrentChange
current change
Definition GNEUndoList.h:81
int getIndex() const
get index
RedoIterator(const GNEUndoList *undoList)
constructor for GNEUndoList
UndoIterator(const GNEUndoList *undoList)
constructor for GNEUndoList
void abortCurrentSubGroup()
Abort the current command sub-group being compiled. All commands already added to the sub-groups undo...
void end()
End undo command sub-group. If the sub-group is still empty, it will be deleted; otherwise,...
bool hasCommandGroup() const
Check if undoList has command group.
void undo()
undo the last command group
long onUpdUndo(FXObject *, FXSelector, void *)
event after Undo
void begin(GUIIcon icon, const std::string &description)
Begin undo command sub-group with current supermode. This begins a new group of commands that are tre...
std::string undoName() const
Return name of the first undo command available; if no undo command available this will return the em...
~GNEUndoList()
destructor
GNEApplicationWindow *const myGNEApplicationWindowParent
void abortAllChangeGroups()
reverts and discards ALL active chained change groups
Supermode getRedoSupermode() const
get redo supermode
long onUpdRedo(FXObject *, FXSelector, void *)
event after Redo
bool myWorking
Currently busy with undo or redo.
void cut()
Cut the redo list. This is automatically invoked when a new undo command is added.
bool canRedo() const
Can we redo more commands.
Supermode getUndoSupermode() const
get undo supermode
bool canUndo() const
Can we undo more commands.
std::stack< GNEChangeGroup * > myChangeGroups
void redo()
redo the last command group
long onCmdUndo(FXObject *, FXSelector, void *)
int currentCommandGroupSize() const
get size of current CommandGroup
void abortLastChangeGroup()
reverts last active chained change group
long onCmdRedo(FXObject *, FXSelector, void *)
redo change
bool busy() const
Return TRUE if currently inside undo or redo operation; this is useful to avoid generating another un...
std::string redoName() const
Return name of the first redo command available; if no Redo command available this will return the em...
void add(GNEChange *command, bool doit=false, bool merge=true)
Add new command, executing it if desired. The new command will be merged with the previous command if...
const GNEViewNetHelper::EditModes & getEditModes() const
get edit modes
GNEViewParent * getViewParent() const
get the net object
void updateViewNet(const bool ignoreViewUpdater=true) const
Mark the entire GNEViewNet to be repainted later.
GNESelectorFrame * getSelectorFrame() const
get frame for select elements
GNEInspectorFrame * getInspectorFrame() const
get frame for inspect elements
static FXIcon * getIcon(const GUIIcon which)
returns a icon previously defined in the enum GUIIcon
void enableUpdate()
enable update
void disableUpdate()
disable update
Supermode currentSupermode
the current supermode