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-2024 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>
25
27#include "GNEUndoList.h"
28
29
30// ===========================================================================
31// FOX callback mapping
32// ===========================================================================
33FXDEFMAP(GNEUndoList) GNEUndoListMap[] = {
34 FXMAPFUNC(SEL_COMMAND, MID_HOTKEY_CTRL_Z_UNDO, GNEUndoList::onCmdUndo),
35 FXMAPFUNC(SEL_UPDATE, MID_HOTKEY_CTRL_Z_UNDO, GNEUndoList::onUpdUndo),
36 FXMAPFUNC(SEL_COMMAND, MID_HOTKEY_CTRL_Y_REDO, GNEUndoList::onCmdRedo),
37 FXMAPFUNC(SEL_UPDATE, MID_HOTKEY_CTRL_Y_REDO, GNEUndoList::onUpdRedo),
38};
39
40// ===========================================================================
41// FOX-declarations
42// ===========================================================================
43
44FXIMPLEMENT_ABSTRACT(GNEUndoList, GNEChangeGroup, GNEUndoListMap, ARRAYNUMBER(GNEUndoListMap))
45
46
47// ===========================================================================
48// member method definitions
49// ===========================================================================
50
51// ---------------------------------------------------------------------------
52// GNEUndoList::Iterator
53// ---------------------------------------------------------------------------
54
56
57
58bool
60 return myCurrentChange == nullptr;
61}
62
63
64int
66 return myIndex;
67}
68
69
70const std::string
72 std::string redoName = myCurrentChange->redoName();
73 return redoName;
74}
75
76
77const std::string
79 return dynamic_cast<GNEChangeGroup*>(myCurrentChange)->getTimeStamp();
80}
81
82
83FXIcon*
85 const GNEChangeGroup* changeGroup = dynamic_cast<GNEChangeGroup*>(myCurrentChange);
86 if (changeGroup) {
87 return GUIIconSubSys::getIcon(changeGroup->getGroupIcon());
88 } else {
89 return nullptr;
90 }
91}
92
93
96 // move current change to next element
97 myCurrentChange = myCurrentChange->next;
98 // update index
99 myIndex++;
100 return *this;
101}
102
103
105 myCurrentChange(change),
106 myIndex(0) {
107}
108
109
111 myCurrentChange(nullptr),
112 myIndex(0) {
113}
114
115
119
120
124
125// ---------------------------------------------------------------------------
126// GNEUndoList
127// ---------------------------------------------------------------------------
128
133
134
136
137
138void
140 WRITE_DEBUG("Calling GNEUndoList::undo()");
141 GNEChange* change = nullptr;
142 if (group) {
143 throw ProcessError("GNEUndoList::undo() cannot call undo inside begin-end block");
144 }
145 if (undoList) {
146 myWorking = true;
147 change = undoList;
148 // Remove from undoList BEFORE undo
150 change->undo();
151 // Hang into redoList AFTER undo
152 change->next = redoList;
153 redoList = change;
154 myWorking = false;
155 // update view net (is called only if gViewUpdater.allowUpdate() is enable)
157 }
158 // update specific controls
160}
161
162
163void
165 WRITE_DEBUG("Calling GNEUndoList::redo()");
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
255 // check if we have to update selector frame
256 const auto& editModes = myGNEApplicationWindowParent->getViewNet()->getEditModes();
257 if ((editModes.isCurrentSupermodeNetwork() && editModes.networkEditMode == NetworkEditMode::NETWORK_SELECT) ||
258 (editModes.isCurrentSupermodeDemand() && editModes.demandEditMode == DemandEditMode::DEMAND_SELECT) ||
259 (editModes.isCurrentSupermodeData() && editModes.dataEditMode == DataEditMode::DATA_SELECT)) {
261 }
262 }
263 // continue with end
264 GNEChangeGroup* change = nullptr;
265 GNEChangeGroup* changeGroup = this;
266 // Must have called begin
267 if (!changeGroup->group) {
268 throw ProcessError("GNEChangeGroup::end: no matching call to begin");
269 }
270 // Calling end while in the middle of doing something!
271 if (myWorking) {
272 throw ProcessError("GNEChangeGroup::end: already working on undo or redo");
273 }
274 // Hunt for one above end of group chain
275 while (changeGroup->group->group) {
276 changeGroup = changeGroup->group;
277 }
278 // Unlink from group chain
279 change = changeGroup->group;
280 changeGroup->group = nullptr;
281 // Add to group if non-empty
282 if (!change->empty()) {
283 // Append new change to undo list
284 change->next = changeGroup->undoList;
285 changeGroup->undoList = change;
286 } else {
287 // Delete bottom group
288 delete change;
289 }
290}
291
292
293void
295 // abort all change groups
297 // clear
298 GNEChange* change = nullptr;
299 while (redoList) {
300 change = redoList;
302 delete change;
303 }
304 while (undoList) {
305 change = undoList;
307 delete change;
308 }
309 delete group;
310 redoList = nullptr;
311 undoList = nullptr;
312 group = nullptr;
313}
314
315
316void
318 while (hasCommandGroup()) {
319 myChangeGroups.top()->undo();
320 myChangeGroups.pop();
321 // abort current subgroup
323 }
324}
325
326
327void
329 if (myChangeGroups.size() > 0) {
330 myChangeGroups.top()->undo();
331 myChangeGroups.pop();
332 // abort current subgroup
334 }
335}
336
337
338void
339GNEUndoList::add(GNEChange* change, bool doit, bool merge) {
340 GNEChangeGroup* changeGroup = this;
341 // Must pass a change
342 if (!change) {
343 throw ProcessError("GNEChangeGroup::add: nullptr change argument");
344 }
345 // Adding undo while in the middle of doing something!
346 if (myWorking) {
347 throw ProcessError("GNEChangeGroup::add: already working on undo or redo");
348 }
349 myWorking = true;
350 // Cut redo list
351 cut();
352 // Execute change
353 if (doit) {
354 change->redo();
355 }
356 // Hunt for end of group chain
357 while (changeGroup->group) {
358 changeGroup = changeGroup->group;
359 }
360 // Try to merge commands when desired and possible
361 if (merge && changeGroup->undoList && (group != nullptr) && change->canMerge() && changeGroup->undoList->mergeWith(change)) {
362 // Delete incoming change that was merged
363 delete change;
364 } else {
365 // Append incoming change
366 change->next = changeGroup->undoList;
367 changeGroup->undoList = change;
368 }
369 myWorking = false;
370}
371
372
373int
375 if (myChangeGroups.size() > 0) {
376 return myChangeGroups.top()->size();
377 } else {
378 return 0;
379 }
380}
381
382
385 if (undoList) {
386 // try to obtain Change Group
387 const GNEChangeGroup* begin = dynamic_cast<GNEChangeGroup*>(undoList);
388 if (begin) {
389 return begin->getGroupSupermode();
390 } else {
391 return undoList->getSupermode();
392 }
393 } else {
394 return Supermode::NETWORK;
395 }
396}
397
398
401 if (redoList) {
402 // try to obtain Change Group
403 const GNEChangeGroup* begin = dynamic_cast<GNEChangeGroup*>(redoList);
404 if (begin) {
405 return begin->getGroupSupermode();
406 } else {
407 return redoList->getSupermode();
408 }
409 } else {
410 return Supermode::NETWORK;
411 }
412}
413
414
415bool
417 return myChangeGroups.size() != 0;
418}
419
420
421bool
423 return myWorking;
424}
425
426
427long
428GNEUndoList::onCmdUndo(FXObject*, FXSelector, void*) {
429 undo();
430 return 1;
431}
432
433
434long
435GNEUndoList::onUpdUndo(FXObject* sender, FXSelector, void*) {
436 // first check if Undo Menu command or button has to be disabled
437 const bool buttonEnabled = canUndo() && !hasCommandGroup() &&
440 // cast button (see flickering problem #6209)
441 const FXButton* button = dynamic_cast<FXButton*>(sender);
442 // enable or disable depending of "enable" flag
443 if (button) {
444 // avoid unnecessary enables/disables (due flickering)
445 if (buttonEnabled && !button->isEnabled()) {
446 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), nullptr);
447 button->update();
448 } else if (!buttonEnabled && button->isEnabled()) {
449 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
450 button->update();
451 }
452 } else {
453 sender->handle(this, buttonEnabled ? FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE) : FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
454 }
455 // cast menu command
456 FXMenuCommand* menuCommand = dynamic_cast<FXMenuCommand*>(sender);
457 // only set caption on menu command item
458 if (menuCommand) {
459 // change caption of FXMenuCommand
460 std::string caption = undoName();
461 // set caption of FXmenuCommand edit/undo
463 caption = TL("Disabled undo");
465 caption = TL("Cannot Undo in the middle of ") + myGNEApplicationWindowParent->isUndoRedoEnabledTemporally();
466 } else if (hasCommandGroup()) {
467 caption = TL("Cannot Undo in the middle of ") + myChangeGroups.top()->getDescription();
468 } else if (!canUndo()) {
469 caption = TL("Undo");
470 }
471 menuCommand->setText(caption.c_str());
472 menuCommand->update();
473 }
474 return 1;
475}
476
477
478long
479GNEUndoList::onCmdRedo(FXObject*, FXSelector, void*) {
480 redo();
481 return 1;
482}
483
484
485long
486GNEUndoList::onUpdRedo(FXObject* sender, FXSelector, void*) {
487 // first check if Redo Menu command or button has to be disabled
488 const bool enable = canRedo() && !hasCommandGroup() &&
491 // cast button (see #6209)
492 const FXButton* button = dynamic_cast<FXButton*>(sender);
493 // enable or disable depending of "enable" flag
494 if (button) {
495 // avoid unnecessary enables/disables (due flickering)
496 if (enable && !button->isEnabled()) {
497 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), nullptr);
498 button->update();
499 } else if (!enable && button->isEnabled()) {
500 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
501 button->update();
502 }
503 } else {
504 sender->handle(this, enable ? FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE) : FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
505 }
506 // cast menu command
507 FXMenuCommand* menuCommand = dynamic_cast<FXMenuCommand*>(sender);
508 // only set caption on menu command item
509 if (menuCommand) {
510 // change caption of FXMenuCommand
511 std::string caption = redoName();
512 // set caption of FXmenuCommand edit/undo
514 caption = TL("Disabled redo");
516 caption = TL("Cannot Redo in the middle of ") + myGNEApplicationWindowParent->isUndoRedoEnabledTemporally();
517 } else if (hasCommandGroup()) {
518 caption = TL("Cannot Redo in the middle of ") + myChangeGroups.top()->getDescription();
519 } else if (!canRedo()) {
520 caption = TL("Redo");
521 }
522 menuCommand->setText(caption.c_str());
523 menuCommand->update();
524 }
525 return 1;
526}
527
528
529void
531 GNEChange* change = nullptr;
532 while (redoList) {
533 change = redoList;
535 delete change;
536 }
537 redoList = nullptr;
538}
539
540
541void
543 // get reference to change group
544 GNEChangeGroup* changeGroup = this;
545 // Must be called after begin
546 if (!changeGroup->group) {
547 throw ProcessError("GNEChangeGroup::abort: no matching call to begin");
548 }
549 // Calling abort while in the middle of doing something!
550 if (myWorking) {
551 throw ProcessError("GNEChangeGroup::abort: already working on undo or redo");
552 }
553 // Hunt for one above end of group chain
554 while (changeGroup->group->group) {
555 changeGroup = changeGroup->group;
556 }
557 // Delete bottom group
558 delete changeGroup->group;
559 // New end of chain
560 changeGroup->group = nullptr;
561}
562
563
564bool
566 return (undoList != nullptr);
567}
568
569
570bool
572 return (redoList != nullptr);
573}
574
575/******************************/
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: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 WRITE_DEBUG(msg)
Definition MsgHandler.h:306
#define TL(string)
Definition MsgHandler.h:315
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: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 redoName
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
GUIIcon getGUIIcon() const
get GUI icon associated to this Tag
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
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