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>
26
28#include "GNEUndoList.h"
29
30
31// ===========================================================================
32// FOX callback mapping
33// ===========================================================================
34FXDEFMAP(GNEUndoList) GNEUndoListMap[] = {
35 FXMAPFUNC(SEL_COMMAND, MID_HOTKEY_CTRL_Z_UNDO, GNEUndoList::onCmdUndo),
36 FXMAPFUNC(SEL_UPDATE, MID_HOTKEY_CTRL_Z_UNDO, GNEUndoList::onUpdUndo),
37 FXMAPFUNC(SEL_COMMAND, MID_HOTKEY_CTRL_Y_REDO, GNEUndoList::onCmdRedo),
38 FXMAPFUNC(SEL_UPDATE, MID_HOTKEY_CTRL_Y_REDO, GNEUndoList::onUpdRedo),
39};
40
41// ===========================================================================
42// FOX-declarations
43// ===========================================================================
44
45FXIMPLEMENT_ABSTRACT(GNEUndoList, GNEChangeGroup, GNEUndoListMap, ARRAYNUMBER(GNEUndoListMap))
46
47
48// ===========================================================================
49// member method definitions
50// ===========================================================================
51
52// ---------------------------------------------------------------------------
53// GNEUndoList::Iterator
54// ---------------------------------------------------------------------------
55
57
58
59bool
61 return myCurrentChange == nullptr;
62}
63
64
65int
67 return myIndex;
68}
69
70
71const std::string
73 std::string redoName = myCurrentChange->redoName();
74 return redoName;
75}
76
77
78const std::string
80 return dynamic_cast<GNEChangeGroup*>(myCurrentChange)->getTimeStamp();
81}
82
83
84FXIcon*
86 const GNEChangeGroup* changeGroup = dynamic_cast<GNEChangeGroup*>(myCurrentChange);
87 if (changeGroup) {
88 return GUIIconSubSys::getIcon(changeGroup->getGroupIcon());
89 } else {
90 return nullptr;
91 }
92}
93
94
97 // move current change to next element
98 myCurrentChange = myCurrentChange->next;
99 // update index
100 myIndex++;
101 return *this;
102}
103
104
106 myCurrentChange(change),
107 myIndex(0) {
108}
109
110
112 myCurrentChange(nullptr),
113 myIndex(0) {
114}
115
116
120
121
125
126// ---------------------------------------------------------------------------
127// GNEUndoList
128// ---------------------------------------------------------------------------
129
134
135
137
138
139void
141 WRITE_DEBUG("Calling GNEUndoList::undo()");
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 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 // update view net (is called only if gViewUpdater.allowUpdate() is enable)
183 }
184 // update specific controls
186}
187
188
189std::string
191 if (undoList) {
192 return undoList->undoName();
193 } else {
194 return "";
195 }
196}
197
198
199std::string
201 if (redoList) {
202 return redoList->redoName();
203 } else {
204 return "";
205 }
206}
207
208
209void
210GNEUndoList::begin(GUIIcon icon, const std::string& description) {
213 } else {
214 begin(Supermode::NETWORK, icon, description);
215 }
216}
217
218
219void
220GNEUndoList::begin(const GNEAttributeCarrier* AC, const std::string& description) {
221 begin(AC->getTagProperty().getGUIIcon(), description);
222}
223
224
225void
226GNEUndoList::begin(Supermode supermode, GUIIcon icon, const std::string& description) {
227 myChangeGroups.push(new GNEChangeGroup(supermode, icon, description));
228 // get this reference
229 GNEChangeGroup* changeGroup = this;
230 // Calling begin while in the middle of doing something!
231 if (myWorking) {
232 throw ProcessError("GNEChangeGroup::begin: already working on undo or redo");
233 }
234 // Cut redo list
235 cut();
236 // Hunt for end of group chain
237 while (changeGroup->group) {
238 changeGroup = changeGroup->group;
239 }
240 // Add to end
241 changeGroup->group = myChangeGroups.top();
242 // disable update
244}
245
246
247void
249 myChangeGroups.pop();
250 // enable update
252 // update view without ignoring viewUpdater (used to avoid slowdows during massive edits)
254 // check if net has to be updated (called only if this is the last end
256 // check if we have to update selector frame
257 const auto& editModes = myGNEApplicationWindowParent->getViewNet()->getEditModes();
258 if ((editModes.isCurrentSupermodeNetwork() && editModes.networkEditMode == NetworkEditMode::NETWORK_INSPECT) ||
259 (editModes.isCurrentSupermodeDemand() && editModes.demandEditMode == DemandEditMode::DEMAND_INSPECT) ||
260 (editModes.isCurrentSupermodeData() && editModes.dataEditMode == DataEditMode::DATA_INSPECT)) {
261 // refresh inspect frame
263 } else if ((editModes.isCurrentSupermodeNetwork() && editModes.networkEditMode == NetworkEditMode::NETWORK_SELECT) ||
264 (editModes.isCurrentSupermodeDemand() && editModes.demandEditMode == DemandEditMode::DEMAND_SELECT) ||
265 (editModes.isCurrentSupermodeData() && editModes.dataEditMode == DataEditMode::DATA_SELECT)) {
266 // update informacion label in selection frame
268 }
269 }
270 // continue with end
271 GNEChangeGroup* change = nullptr;
272 GNEChangeGroup* changeGroup = this;
273 // Must have called begin
274 if (!changeGroup->group) {
275 throw ProcessError("GNEChangeGroup::end: no matching call to begin");
276 }
277 // Calling end while in the middle of doing something!
278 if (myWorking) {
279 throw ProcessError("GNEChangeGroup::end: already working on undo or redo");
280 }
281 // Hunt for one above end of group chain
282 while (changeGroup->group->group) {
283 changeGroup = changeGroup->group;
284 }
285 // Unlink from group chain
286 change = changeGroup->group;
287 changeGroup->group = nullptr;
288 // Add to group if non-empty
289 if (!change->empty()) {
290 // Append new change to undo list
291 change->next = changeGroup->undoList;
292 changeGroup->undoList = change;
293 } else {
294 // Delete bottom group
295 delete change;
296 }
297}
298
299
300void
302 // abort all change groups
304 // clear
305 GNEChange* change = nullptr;
306 while (redoList) {
307 change = redoList;
309 delete change;
310 }
311 while (undoList) {
312 change = undoList;
314 delete change;
315 }
316 delete group;
317 redoList = nullptr;
318 undoList = nullptr;
319 group = nullptr;
320}
321
322
323void
325 while (hasCommandGroup()) {
326 myChangeGroups.top()->undo();
327 myChangeGroups.pop();
328 // abort current subgroup
330 }
331}
332
333
334void
336 if (myChangeGroups.size() > 0) {
337 myChangeGroups.top()->undo();
338 myChangeGroups.pop();
339 // abort current subgroup
341 }
342}
343
344
345void
346GNEUndoList::add(GNEChange* change, bool doit, bool merge) {
347 GNEChangeGroup* changeGroup = this;
348 // Must pass a change
349 if (!change) {
350 throw ProcessError("GNEChangeGroup::add: nullptr change argument");
351 }
352 // Adding undo while in the middle of doing something!
353 if (myWorking) {
354 throw ProcessError("GNEChangeGroup::add: already working on undo or redo");
355 }
356 myWorking = true;
357 // Cut redo list
358 cut();
359 // Execute change
360 if (doit) {
361 change->redo();
362 }
363 // Hunt for end of group chain
364 while (changeGroup->group) {
365 changeGroup = changeGroup->group;
366 }
367 // Try to merge commands when desired and possible
368 if (merge && changeGroup->undoList && (group != nullptr) && change->canMerge() && changeGroup->undoList->mergeWith(change)) {
369 // Delete incoming change that was merged
370 delete change;
371 } else {
372 // Append incoming change
373 change->next = changeGroup->undoList;
374 changeGroup->undoList = change;
375 }
376 myWorking = false;
377}
378
379
380int
382 if (myChangeGroups.size() > 0) {
383 return myChangeGroups.top()->size();
384 } else {
385 return 0;
386 }
387}
388
389
392 if (undoList) {
393 // try to obtain Change Group
394 const GNEChangeGroup* begin = dynamic_cast<GNEChangeGroup*>(undoList);
395 if (begin) {
396 return begin->getGroupSupermode();
397 } else {
398 return undoList->getSupermode();
399 }
400 } else {
401 return Supermode::NETWORK;
402 }
403}
404
405
408 if (redoList) {
409 // try to obtain Change Group
410 const GNEChangeGroup* begin = dynamic_cast<GNEChangeGroup*>(redoList);
411 if (begin) {
412 return begin->getGroupSupermode();
413 } else {
414 return redoList->getSupermode();
415 }
416 } else {
417 return Supermode::NETWORK;
418 }
419}
420
421
422bool
424 return myChangeGroups.size() != 0;
425}
426
427
428bool
430 return myWorking;
431}
432
433
434long
435GNEUndoList::onCmdUndo(FXObject*, FXSelector, void*) {
436 undo();
437 return 1;
438}
439
440
441long
442GNEUndoList::onUpdUndo(FXObject* sender, FXSelector, void*) {
443 // first check if Undo Menu command or button has to be disabled
444 const bool buttonEnabled = canUndo() && !hasCommandGroup() &&
447 // cast button (see flickering problem #6209)
448 const FXButton* button = dynamic_cast<FXButton*>(sender);
449 // enable or disable depending of "enable" flag
450 if (button) {
451 // avoid unnecessary enables/disables (due flickering)
452 if (buttonEnabled && !button->isEnabled()) {
453 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), nullptr);
454 button->update();
455 } else if (!buttonEnabled && button->isEnabled()) {
456 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
457 button->update();
458 }
459 } else {
460 sender->handle(this, buttonEnabled ? FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE) : FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
461 }
462 // cast menu command
463 FXMenuCommand* menuCommand = dynamic_cast<FXMenuCommand*>(sender);
464 // only set caption on menu command item
465 if (menuCommand) {
466 // change caption of FXMenuCommand
467 std::string caption = undoName();
468 // set caption of FXmenuCommand edit/undo
470 caption = TL("Disabled undo");
472 caption = TL("Cannot Undo in the middle of ") + myGNEApplicationWindowParent->isUndoRedoEnabledTemporally();
473 } else if (hasCommandGroup()) {
474 caption = TL("Cannot Undo in the middle of ") + myChangeGroups.top()->getDescription();
475 } else if (!canUndo()) {
476 caption = TL("Undo");
477 }
478 menuCommand->setText(caption.c_str());
479 menuCommand->update();
480 }
481 return 1;
482}
483
484
485long
486GNEUndoList::onCmdRedo(FXObject*, FXSelector, void*) {
487 redo();
488 return 1;
489}
490
491
492long
493GNEUndoList::onUpdRedo(FXObject* sender, FXSelector, void*) {
494 // first check if Redo Menu command or button has to be disabled
495 const bool enable = canRedo() && !hasCommandGroup() &&
498 // cast button (see #6209)
499 const FXButton* button = dynamic_cast<FXButton*>(sender);
500 // enable or disable depending of "enable" flag
501 if (button) {
502 // avoid unnecessary enables/disables (due flickering)
503 if (enable && !button->isEnabled()) {
504 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), nullptr);
505 button->update();
506 } else if (!enable && button->isEnabled()) {
507 sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
508 button->update();
509 }
510 } else {
511 sender->handle(this, enable ? FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE) : FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
512 }
513 // cast menu command
514 FXMenuCommand* menuCommand = dynamic_cast<FXMenuCommand*>(sender);
515 // only set caption on menu command item
516 if (menuCommand) {
517 // change caption of FXMenuCommand
518 std::string caption = redoName();
519 // set caption of FXmenuCommand edit/undo
521 caption = TL("Disabled redo");
523 caption = TL("Cannot Redo in the middle of ") + myGNEApplicationWindowParent->isUndoRedoEnabledTemporally();
524 } else if (hasCommandGroup()) {
525 caption = TL("Cannot Redo in the middle of ") + myChangeGroups.top()->getDescription();
526 } else if (!canRedo()) {
527 caption = TL("Redo");
528 }
529 menuCommand->setText(caption.c_str());
530 menuCommand->update();
531 }
532 return 1;
533}
534
535
536void
538 GNEChange* change = nullptr;
539 while (redoList) {
540 change = redoList;
542 delete change;
543 }
544 redoList = nullptr;
545}
546
547
548void
550 // get reference to change group
551 GNEChangeGroup* changeGroup = this;
552 // Must be called after begin
553 if (!changeGroup->group) {
554 throw ProcessError("GNEChangeGroup::abort: no matching call to begin");
555 }
556 // Calling abort while in the middle of doing something!
557 if (myWorking) {
558 throw ProcessError("GNEChangeGroup::abort: already working on undo or redo");
559 }
560 // Hunt for one above end of group chain
561 while (changeGroup->group->group) {
562 changeGroup = changeGroup->group;
563 }
564 // Delete bottom group
565 delete changeGroup->group;
566 // New end of chain
567 changeGroup->group = nullptr;
568}
569
570
571bool
573 return (undoList != nullptr);
574}
575
576
577bool
579 return (redoList != nullptr);
580}
581
582/******************************/
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 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 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
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