Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
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-2023 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#include <netedit/GNEViewNet.h>
24
26#include "GNEUndoList.h"
27
28
29// ===========================================================================
30// FOX callback mapping
31// ===========================================================================
32FXDEFMAP(GNEUndoList) GNEUndoListMap[] = {
33 FXMAPFUNC(SEL_COMMAND, MID_HOTKEY_CTRL_Z_UNDO, GNEUndoList::onCmdUndo),
34 FXMAPFUNC(SEL_UPDATE, MID_HOTKEY_CTRL_Z_UNDO, GNEUndoList::onUpdUndo),
35 FXMAPFUNC(SEL_COMMAND, MID_HOTKEY_CTRL_Y_REDO, GNEUndoList::onCmdRedo),
36 FXMAPFUNC(SEL_UPDATE, MID_HOTKEY_CTRL_Y_REDO, GNEUndoList::onUpdRedo),
37};
38
39// ===========================================================================
40// FOX-declarations
41// ===========================================================================
42
43FXIMPLEMENT_ABSTRACT(GNEUndoList, GNEChangeGroup, GNEUndoListMap, ARRAYNUMBER(GNEUndoListMap))
44
45
46// ===========================================================================
47// member method definitions
48// ===========================================================================
49
50// ---------------------------------------------------------------------------
51// GNEUndoList::Iterator
52// ---------------------------------------------------------------------------
53
55
56
57bool
59 return myCurrentChange == nullptr;
60}
61
62
63int
65 return myIndex;
66}
67
68
69const std::string
71 std::string redoName = myCurrentChange->redoName();
72 // remove "redo "
73 if (redoName.size() >= 5) {
74 redoName.erase(0, 5);
75 }
76 return redoName;
77}
78
79
80const std::string
82 return dynamic_cast<GNEChangeGroup*>(myCurrentChange)->getTimeStamp();
83}
84
85
86FXIcon*
88 const GNEChangeGroup* changeGroup = dynamic_cast<GNEChangeGroup*>(myCurrentChange);
89 if (changeGroup) {
90 return GUIIconSubSys::getIcon(changeGroup->getGroupIcon());
91 } else {
92 return nullptr;
93 }
94}
95
96
99 // move current change to next element
100 myCurrentChange = myCurrentChange->next;
101 // update index
102 myIndex++;
103 return *this;
104}
105
106
108 myCurrentChange(change),
109 myIndex(0) {
110}
111
112
114 myCurrentChange(nullptr),
115 myIndex(0) {
116}
117
118
122
123
127
128// ---------------------------------------------------------------------------
129// GNEUndoList
130// ---------------------------------------------------------------------------
131
136
137
139
140
141void
143 WRITE_DEBUG("Calling GNEUndoList::undo()");
144 GNEChange* change = nullptr;
145 if (group) {
146 throw ProcessError("GNEUndoList::undo() cannot call undo inside begin-end block");
147 }
148 if (undoList) {
149 myWorking = true;
150 change = undoList;
151 // Remove from undoList BEFORE undo
153 change->undo();
154 // Hang into redoList AFTER undo
155 change->next = redoList;
156 redoList = change;
157 myWorking = false;
158 }
159 // update specific controls
161}
162
163
164void
166 WRITE_DEBUG("Calling GNEUndoList::redo()");
167 GNEChange* change = nullptr;
168 if (group) {
169 throw ProcessError("GNEUndoList::redo() cannot call undo inside begin-end block");
170 }
171 if (redoList) {
172 myWorking = true;
173 change = redoList;
174 // Remove from redoList BEFORE redo
176 change->redo();
177 // Hang into undoList AFTER redo
178 change->next = undoList;
179 undoList = change;
180 myWorking = false;
181 }
182 // update specific controls
184}
185
186
187std::string
189 if (undoList) {
190 return undoList->undoName();
191 } else {
192 return "";
193 }
194}
195
196
197std::string
199 if (redoList) {
200 return redoList->redoName();
201 } else {
202 return "";
203 }
204}
205
206
207void
208GNEUndoList::begin(GUIIcon icon, const std::string& description) {
211 } else {
212 begin(Supermode::NETWORK, icon, description);
213 }
214}
215
216
217void
218GNEUndoList::begin(Supermode supermode, GUIIcon icon, const std::string& description) {
219 myChangeGroups.push(new GNEChangeGroup(supermode, icon, description));
220 // get this reference
221 GNEChangeGroup* changeGroup = this;
222 // Calling begin while in the middle of doing something!
223 if (myWorking) {
224 throw ProcessError("GNEChangeGroup::begin: already working on undo or redo");
225 }
226 // Cut redo list
227 cut();
228 // Hunt for end of group chain
229 while (changeGroup->group) {
230 changeGroup = changeGroup->group;
231 }
232 // Add to end
233 changeGroup->group = myChangeGroups.top();
234}
235
236
237void
239 myChangeGroups.pop();
240 // check if net has to be updated
242 // update view
244 // check if we have to update selector frame
245 const auto& editModes = myGNEApplicationWindowParent->getViewNet()->getEditModes();
246 if ((editModes.isCurrentSupermodeNetwork() && editModes.networkEditMode == NetworkEditMode::NETWORK_SELECT) ||
247 (editModes.isCurrentSupermodeDemand() && editModes.demandEditMode == DemandEditMode::DEMAND_SELECT) ||
248 (editModes.isCurrentSupermodeData() && editModes.dataEditMode == DataEditMode::DATA_SELECT)) {
250 }
251 }
252 // continue with end
253 GNEChangeGroup* change = nullptr;
254 GNEChangeGroup* changeGroup = this;
255 // Must have called begin
256 if (!changeGroup->group) {
257 throw ProcessError("GNEChangeGroup::end: no matching call to begin");
258 }
259 // Calling end while in the middle of doing something!
260 if (myWorking) {
261 throw ProcessError("GNEChangeGroup::end: already working on undo or redo");
262 }
263 // Hunt for one above end of group chain
264 while (changeGroup->group->group) {
265 changeGroup = changeGroup->group;
266 }
267 // Unlink from group chain
268 change = changeGroup->group;
269 changeGroup->group = nullptr;
270 // Add to group if non-empty
271 if (!change->empty()) {
272 // Append new change to undo list
273 change->next = changeGroup->undoList;
274 changeGroup->undoList = change;
275 } else {
276 // Delete bottom group
277 delete change;
278 }
279}
280
281
282void
284 // abort all change groups
286 // clear
287 GNEChange* change = nullptr;
288 while (redoList) {
289 change = redoList;
291 delete change;
292 }
293 while (undoList) {
294 change = undoList;
296 delete change;
297 }
298 delete group;
299 redoList = nullptr;
300 undoList = nullptr;
301 group = nullptr;
302}
303
304
305void
307 while (hasCommandGroup()) {
308 myChangeGroups.top()->undo();
309 myChangeGroups.pop();
310 // abort current subgroup
312 }
313}
314
315
316void
318 if (myChangeGroups.size() > 0) {
319 myChangeGroups.top()->undo();
320 myChangeGroups.pop();
321 // abort current subgroup
323 }
324}
325
326
327void
328GNEUndoList::add(GNEChange* change, bool doit, bool merge) {
329 GNEChangeGroup* changeGroup = this;
330 // Must pass a change
331 if (!change) {
332 throw ProcessError("GNEChangeGroup::add: nullptr change argument");
333 }
334 // Adding undo while in the middle of doing something!
335 if (myWorking) {
336 throw ProcessError("GNEChangeGroup::add: already working on undo or redo");
337 }
338 myWorking = true;
339 // Cut redo list
340 cut();
341 // Execute change
342 if (doit) {
343 change->redo();
344 }
345 // Hunt for end of group chain
346 while (changeGroup->group) {
347 changeGroup = changeGroup->group;
348 }
349 // Try to merge commands when desired and possible
350 if (merge && changeGroup->undoList && (group != nullptr) && change->canMerge() && changeGroup->undoList->mergeWith(change)) {
351 // Delete incoming change that was merged
352 delete change;
353 } else {
354 // Append incoming change
355 change->next = changeGroup->undoList;
356 changeGroup->undoList = change;
357 }
358 myWorking = false;
359}
360
361
362void
364 if (change->trueChange()) {
365 add(change, true);
366 } else {
367 delete change;
368 }
369}
370
371
372int
374 if (myChangeGroups.size() > 0) {
375 return myChangeGroups.top()->size();
376 } else {
377 return 0;
378 }
379}
380
381
384 if (undoList) {
385 // try to obtain Change Group
386 const GNEChangeGroup* begin = dynamic_cast<GNEChangeGroup*>(undoList);
387 if (begin) {
388 return begin->getGroupSupermode();
389 } else {
390 return undoList->getSupermode();
391 }
392 } else {
393 return Supermode::NETWORK;
394 }
395}
396
397
400 if (redoList) {
401 // try to obtain Change Group
402 const GNEChangeGroup* begin = dynamic_cast<GNEChangeGroup*>(redoList);
403 if (begin) {
404 return begin->getGroupSupermode();
405 } else {
406 return redoList->getSupermode();
407 }
408 } else {
409 return Supermode::NETWORK;
410 }
411}
412
413
414bool
416 return myChangeGroups.size() != 0;
417}
418
419
420bool
422 return myWorking;
423}
424
425
426long
427GNEUndoList::onCmdUndo(FXObject*, FXSelector, void*) {
428 undo();
429 return 1;
430}
431
432
433long
434GNEUndoList::onUpdUndo(FXObject* sender, FXSelector, void*) {
435 // first check if Undo Menu command or button has to be disabled
436 const bool enable = canUndo() && !hasCommandGroup() && myGNEApplicationWindowParent->isUndoRedoEnabled().empty();
437 // cast button (see #6209)
438 const FXButton* button = dynamic_cast<FXButton*>(sender);
439 // enable or disable depending of "enable" flag
440 if (button) {
441 // avoid unnecessary enables/disables (due flickering)
442 if (enable && !button->isEnabled()) {
443 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), nullptr);
444 button->update();
445 } else if (!enable && button->isEnabled()) {
446 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
447 button->update();
448 }
449 } else {
450 sender->handle(this, enable ? FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE) : FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
451 }
452 // cast menu command
453 FXMenuCommand* menuCommand = dynamic_cast<FXMenuCommand*>(sender);
454 // only set caption on menu command item
455 if (menuCommand) {
456 // change caption of FXMenuCommand
457 std::string caption = undoName();
458 // set caption of FXmenuCommand edit/undo
460 caption = "Cannot Undo in the middle of " + myGNEApplicationWindowParent->isUndoRedoEnabled();
461 } else if (hasCommandGroup()) {
462 caption = "Cannot Undo in the middle of " + myChangeGroups.top()->getDescription();
463 } else if (!canUndo()) {
464 caption = "Undo";
465 }
466 menuCommand->setText(caption.c_str());
467 menuCommand->update();
468 }
469 return 1;
470}
471
472
473long
474GNEUndoList::onCmdRedo(FXObject*, FXSelector, void*) {
475 redo();
476 return 1;
477}
478
479
480long
481GNEUndoList::onUpdRedo(FXObject* sender, FXSelector, void*) {
482 // first check if Redo Menu command or button has to be disabled
483 const bool enable = canRedo() && !hasCommandGroup() && myGNEApplicationWindowParent->isUndoRedoEnabled().empty();
484 // cast button (see #6209)
485 const FXButton* button = dynamic_cast<FXButton*>(sender);
486 // enable or disable depending of "enable" flag
487 if (button) {
488 // avoid unnecessary enables/disables (due flickering)
489 if (enable && !button->isEnabled()) {
490 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), nullptr);
491 button->update();
492 } else if (!enable && button->isEnabled()) {
493 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
494 button->update();
495 }
496 } else {
497 sender->handle(this, enable ? FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE) : FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
498 }
499 // cast menu command
500 FXMenuCommand* menuCommand = dynamic_cast<FXMenuCommand*>(sender);
501 // only set caption on menu command item
502 if (menuCommand) {
503 // change caption of FXMenuCommand
504 std::string caption = redoName();
505 // set caption of FXmenuCommand edit/undo
507 caption = "Cannot Redo in the middle of " + myGNEApplicationWindowParent->isUndoRedoEnabled();
508 } else if (hasCommandGroup()) {
509 caption = "Cannot Redo in the middle of " + myChangeGroups.top()->getDescription();
510 } else if (!canRedo()) {
511 caption = "Redo";
512 }
513 menuCommand->setText(caption.c_str());
514 menuCommand->update();
515 }
516 return 1;
517}
518
519
520void
522 GNEChange* change = nullptr;
523 while (redoList) {
524 change = redoList;
526 delete change;
527 }
528 redoList = nullptr;
529}
530
531
532void
534 // get reference to change group
535 GNEChangeGroup* changeGroup = this;
536 // Must be called after begin
537 if (!changeGroup->group) {
538 throw ProcessError("GNEChangeGroup::abort: no matching call to begin");
539 }
540 // Calling abort while in the middle of doing something!
541 if (myWorking) {
542 throw ProcessError("GNEChangeGroup::abort: already working on undo or redo");
543 }
544 // Hunt for one above end of group chain
545 while (changeGroup->group->group) {
546 changeGroup = changeGroup->group;
547 }
548 // Delete bottom group
549 delete changeGroup->group;
550 // New end of chain
551 changeGroup->group = nullptr;
552}
553
554
555bool
557 return (undoList != nullptr);
558}
559
560
561bool
563 return (redoList != nullptr);
564}
565
566/******************************/
FXDEFMAP(GNEUndoList) GNEUndoListMap[]
@ DATA_SELECT
mode for selecting data elements
Supermode
@brie enum for supermodes
@ NETWORK
Network mode (Edges, junctions, etc..)
@ NETWORK_SELECT
mode for selecting network elements
@ DEMAND_SELECT
mode for selecting demand elements
@ MID_HOTKEY_CTRL_Y_REDO
Undo.
Definition GUIAppEnum.h:131
@ MID_HOTKEY_CTRL_Z_UNDO
Redo.
Definition GUIAppEnum.h:133
GUIIcon
An enumeration of icons used by the gui applications.
Definition GUIIcons.h:33
#define WRITE_DEBUG(msg)
Definition MsgHandler.h:281
The main window of Netedit.
void updateControls()
update control contents after undo/redo or recompute
GNEViewNet * getViewNet()
get pointer to viewNet
const std::string & isUndoRedoEnabled() const
check if undo-redo is enabled
the function-object for an editing operation (abstract base)
bool trueChange()
wether original and new value differ
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:61
virtual void undo()=0
undo action/operation
Supermode getSupermode() const
get supermode
Definition GNEChange.cpp:68
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:80
friend class GNEUndoList
Definition GNEChange.h:62
virtual std::string redoName() const =0
return rendoName
GNEChange * next
Definition GNEChange.h:225
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:74
void updateInformationLabel()
update information label
SelectionInformation * getSelectionInformation() const
get modul for selection information
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...
void changeAttribute(GNEChange_Attribute *change)
special method for change attributes, avoid empty changes, always execute
const GNEViewNetHelper::EditModes & getEditModes() const
get edit modes
GNEViewParent * getViewParent() const
get the net object
void updateViewNet() const
Mark the entire GNEViewNet to be repainted later.
GNESelectorFrame * getSelectorFrame() const
get frame for select elements
static FXIcon * getIcon(const GUIIcon which)
returns a icon previously defined in the enum GUIIcon
Supermode currentSupermode
the current supermode