Eclipse SUMO - Simulation of Urban MObility
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>
21 #include <netedit/GNEViewParent.h>
24 
25 #include "GNEApplicationWindow.h"
26 #include "GNEUndoList.h"
27 
28 
29 // ===========================================================================
30 // FOX callback mapping
31 // ===========================================================================
32 FXDEFMAP(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 
43 FXIMPLEMENT_ABSTRACT(GNEUndoList, GNEChangeGroup, GNEUndoListMap, ARRAYNUMBER(GNEUndoListMap))
44 
45 
46 // ===========================================================================
47 // member method definitions
48 // ===========================================================================
49 
50 // ---------------------------------------------------------------------------
51 // GNEUndoList::Iterator
52 // ---------------------------------------------------------------------------
53 
55 
56 
57 bool
59  return myCurrentChange == nullptr;
60 }
61 
62 
63 int
65  return myIndex;
66 }
67 
68 
69 const std::string
71  std::string redoName = myCurrentChange->redoName();
72  return redoName;
73 }
74 
75 
76 const std::string
78  return dynamic_cast<GNEChangeGroup*>(myCurrentChange)->getTimeStamp();
79 }
80 
81 
82 FXIcon*
84  const GNEChangeGroup* changeGroup = dynamic_cast<GNEChangeGroup*>(myCurrentChange);
85  if (changeGroup) {
86  return GUIIconSubSys::getIcon(changeGroup->getGroupIcon());
87  } else {
88  return nullptr;
89  }
90 }
91 
92 
95  // move current change to next element
96  myCurrentChange = myCurrentChange->next;
97  // update index
98  myIndex++;
99  return *this;
100 }
101 
102 
104  myCurrentChange(change),
105  myIndex(0) {
106 }
107 
108 
110  myCurrentChange(nullptr),
111  myIndex(0) {
112 }
113 
114 
117 }
118 
119 
122 }
123 
124 // ---------------------------------------------------------------------------
125 // GNEUndoList
126 // ---------------------------------------------------------------------------
127 
129  myWorking(false),
131 }
132 
133 
135 
136 
137 void
139  WRITE_DEBUG("Calling GNEUndoList::undo()");
140  GNEChange* change = nullptr;
141  if (group) {
142  throw ProcessError("GNEUndoList::undo() cannot call undo inside begin-end block");
143  }
144  if (undoList) {
145  myWorking = true;
146  change = undoList;
147  // Remove from undoList BEFORE undo
149  change->undo();
150  // Hang into redoList AFTER undo
151  change->next = redoList;
152  redoList = change;
153  myWorking = false;
154  }
155  // update specific controls
157 }
158 
159 
160 void
162  WRITE_DEBUG("Calling GNEUndoList::redo()");
163  GNEChange* change = nullptr;
164  if (group) {
165  throw ProcessError("GNEUndoList::redo() cannot call undo inside begin-end block");
166  }
167  if (redoList) {
168  myWorking = true;
169  change = redoList;
170  // Remove from redoList BEFORE redo
172  change->redo();
173  // Hang into undoList AFTER redo
174  change->next = undoList;
175  undoList = change;
176  myWorking = false;
177  }
178  // update specific controls
180 }
181 
182 
183 std::string
185  if (undoList) {
186  return undoList->undoName();
187  } else {
188  return "";
189  }
190 }
191 
192 
193 std::string
195  if (redoList) {
196  return redoList->redoName();
197  } else {
198  return "";
199  }
200 }
201 
202 
203 void
204 GNEUndoList::begin(GUIIcon icon, const std::string& description) {
207  } else {
208  begin(Supermode::NETWORK, icon, description);
209  }
210 }
211 
212 
213 void
214 GNEUndoList::begin(const GNEAttributeCarrier* AC, const std::string& description) {
215  begin(AC->getTagProperty().getGUIIcon(), description);
216 }
217 
218 
219 void
220 GNEUndoList::begin(Supermode supermode, GUIIcon icon, const std::string& description) {
221  myChangeGroups.push(new GNEChangeGroup(supermode, icon, description));
222  // get this reference
223  GNEChangeGroup* changeGroup = this;
224  // Calling begin while in the middle of doing something!
225  if (myWorking) {
226  throw ProcessError("GNEChangeGroup::begin: already working on undo or redo");
227  }
228  // Cut redo list
229  cut();
230  // Hunt for end of group chain
231  while (changeGroup->group) {
232  changeGroup = changeGroup->group;
233  }
234  // Add to end
235  changeGroup->group = myChangeGroups.top();
236 }
237 
238 
239 void
241  myChangeGroups.pop();
242  // check if net has to be updated
244  // update view
246  // check if we have to update selector frame
247  const auto& editModes = myGNEApplicationWindowParent->getViewNet()->getEditModes();
248  if ((editModes.isCurrentSupermodeNetwork() && editModes.networkEditMode == NetworkEditMode::NETWORK_SELECT) ||
249  (editModes.isCurrentSupermodeDemand() && editModes.demandEditMode == DemandEditMode::DEMAND_SELECT) ||
250  (editModes.isCurrentSupermodeData() && editModes.dataEditMode == DataEditMode::DATA_SELECT)) {
252  }
253  }
254  // continue with end
255  GNEChangeGroup* change = nullptr;
256  GNEChangeGroup* changeGroup = this;
257  // Must have called begin
258  if (!changeGroup->group) {
259  throw ProcessError("GNEChangeGroup::end: no matching call to begin");
260  }
261  // Calling end while in the middle of doing something!
262  if (myWorking) {
263  throw ProcessError("GNEChangeGroup::end: already working on undo or redo");
264  }
265  // Hunt for one above end of group chain
266  while (changeGroup->group->group) {
267  changeGroup = changeGroup->group;
268  }
269  // Unlink from group chain
270  change = changeGroup->group;
271  changeGroup->group = nullptr;
272  // Add to group if non-empty
273  if (!change->empty()) {
274  // Append new change to undo list
275  change->next = changeGroup->undoList;
276  changeGroup->undoList = change;
277  } else {
278  // Delete bottom group
279  delete change;
280  }
281 }
282 
283 
284 void
286  // abort all change groups
288  // clear
289  GNEChange* change = nullptr;
290  while (redoList) {
291  change = redoList;
293  delete change;
294  }
295  while (undoList) {
296  change = undoList;
298  delete change;
299  }
300  delete group;
301  redoList = nullptr;
302  undoList = nullptr;
303  group = nullptr;
304 }
305 
306 
307 void
309  while (hasCommandGroup()) {
310  myChangeGroups.top()->undo();
311  myChangeGroups.pop();
312  // abort current subgroup
314  }
315 }
316 
317 
318 void
320  if (myChangeGroups.size() > 0) {
321  myChangeGroups.top()->undo();
322  myChangeGroups.pop();
323  // abort current subgroup
325  }
326 }
327 
328 
329 void
330 GNEUndoList::add(GNEChange* change, bool doit, bool merge) {
331  GNEChangeGroup* changeGroup = this;
332  // Must pass a change
333  if (!change) {
334  throw ProcessError("GNEChangeGroup::add: nullptr change argument");
335  }
336  // Adding undo while in the middle of doing something!
337  if (myWorking) {
338  throw ProcessError("GNEChangeGroup::add: already working on undo or redo");
339  }
340  myWorking = true;
341  // Cut redo list
342  cut();
343  // Execute change
344  if (doit) {
345  change->redo();
346  }
347  // Hunt for end of group chain
348  while (changeGroup->group) {
349  changeGroup = changeGroup->group;
350  }
351  // Try to merge commands when desired and possible
352  if (merge && changeGroup->undoList && (group != nullptr) && change->canMerge() && changeGroup->undoList->mergeWith(change)) {
353  // Delete incoming change that was merged
354  delete change;
355  } else {
356  // Append incoming change
357  change->next = changeGroup->undoList;
358  changeGroup->undoList = change;
359  }
360  myWorking = false;
361 }
362 
363 
364 int
366  if (myChangeGroups.size() > 0) {
367  return myChangeGroups.top()->size();
368  } else {
369  return 0;
370  }
371 }
372 
373 
374 Supermode
376  if (undoList) {
377  // try to obtain Change Group
378  const GNEChangeGroup* begin = dynamic_cast<GNEChangeGroup*>(undoList);
379  if (begin) {
380  return begin->getGroupSupermode();
381  } else {
382  return undoList->getSupermode();
383  }
384  } else {
385  return Supermode::NETWORK;
386  }
387 }
388 
389 
390 Supermode
392  if (redoList) {
393  // try to obtain Change Group
394  const GNEChangeGroup* begin = dynamic_cast<GNEChangeGroup*>(redoList);
395  if (begin) {
396  return begin->getGroupSupermode();
397  } else {
398  return redoList->getSupermode();
399  }
400  } else {
401  return Supermode::NETWORK;
402  }
403 }
404 
405 
406 bool
408  return myChangeGroups.size() != 0;
409 }
410 
411 
412 bool
414  return myWorking;
415 }
416 
417 
418 long
419 GNEUndoList::onCmdUndo(FXObject*, FXSelector, void*) {
420  undo();
421  return 1;
422 }
423 
424 
425 long
426 GNEUndoList::onUpdUndo(FXObject* sender, FXSelector, void*) {
427  // first check if Undo Menu command or button has to be disabled
428  const bool enable = canUndo() && !hasCommandGroup() && myGNEApplicationWindowParent->isUndoRedoEnabled().empty();
429  // cast button (see #6209)
430  const FXButton* button = dynamic_cast<FXButton*>(sender);
431  // enable or disable depending of "enable" flag
432  if (button) {
433  // avoid unnecessary enables/disables (due flickering)
434  if (enable && !button->isEnabled()) {
435  sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), nullptr);
436  button->update();
437  } else if (!enable && button->isEnabled()) {
438  sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
439  button->update();
440  }
441  } else {
442  sender->handle(this, enable ? FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE) : FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
443  }
444  // cast menu command
445  FXMenuCommand* menuCommand = dynamic_cast<FXMenuCommand*>(sender);
446  // only set caption on menu command item
447  if (menuCommand) {
448  // change caption of FXMenuCommand
449  std::string caption = undoName();
450  // set caption of FXmenuCommand edit/undo
452  caption = TL("Cannot Undo in the middle of ") + myGNEApplicationWindowParent->isUndoRedoEnabled();
453  } else if (hasCommandGroup()) {
454  caption = TL("Cannot Undo in the middle of ") + myChangeGroups.top()->getDescription();
455  } else if (!canUndo()) {
456  caption = TL("Undo");
457  }
458  menuCommand->setText(caption.c_str());
459  menuCommand->update();
460  }
461  return 1;
462 }
463 
464 
465 long
466 GNEUndoList::onCmdRedo(FXObject*, FXSelector, void*) {
467  redo();
468  return 1;
469 }
470 
471 
472 long
473 GNEUndoList::onUpdRedo(FXObject* sender, FXSelector, void*) {
474  // first check if Redo Menu command or button has to be disabled
475  const bool enable = canRedo() && !hasCommandGroup() && myGNEApplicationWindowParent->isUndoRedoEnabled().empty();
476  // cast button (see #6209)
477  const FXButton* button = dynamic_cast<FXButton*>(sender);
478  // enable or disable depending of "enable" flag
479  if (button) {
480  // avoid unnecessary enables/disables (due flickering)
481  if (enable && !button->isEnabled()) {
482  sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), nullptr);
483  button->update();
484  } else if (!enable && button->isEnabled()) {
485  sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
486  button->update();
487  }
488  } else {
489  sender->handle(this, enable ? FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE) : FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);
490  }
491  // cast menu command
492  FXMenuCommand* menuCommand = dynamic_cast<FXMenuCommand*>(sender);
493  // only set caption on menu command item
494  if (menuCommand) {
495  // change caption of FXMenuCommand
496  std::string caption = redoName();
497  // set caption of FXmenuCommand edit/undo
499  caption = TL("Cannot Redo in the middle of ") + myGNEApplicationWindowParent->isUndoRedoEnabled();
500  } else if (hasCommandGroup()) {
501  caption = TL("Cannot Redo in the middle of ") + myChangeGroups.top()->getDescription();
502  } else if (!canRedo()) {
503  caption = TL("Redo");
504  }
505  menuCommand->setText(caption.c_str());
506  menuCommand->update();
507  }
508  return 1;
509 }
510 
511 
512 void
514  GNEChange* change = nullptr;
515  while (redoList) {
516  change = redoList;
518  delete change;
519  }
520  redoList = nullptr;
521 }
522 
523 
524 void
526  // get reference to change group
527  GNEChangeGroup* changeGroup = this;
528  // Must be called after begin
529  if (!changeGroup->group) {
530  throw ProcessError("GNEChangeGroup::abort: no matching call to begin");
531  }
532  // Calling abort while in the middle of doing something!
533  if (myWorking) {
534  throw ProcessError("GNEChangeGroup::abort: already working on undo or redo");
535  }
536  // Hunt for one above end of group chain
537  while (changeGroup->group->group) {
538  changeGroup = changeGroup->group;
539  }
540  // Delete bottom group
541  delete changeGroup->group;
542  // New end of chain
543  changeGroup->group = nullptr;
544 }
545 
546 
547 bool
549  return (undoList != nullptr);
550 }
551 
552 
553 bool
555  return (redoList != nullptr);
556 }
557 
558 /******************************/
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
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.
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
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.
friend class GNEUndoList
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 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
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
Definition: GNEUndoList.cpp:77
const std::string getDescription() const
get description
Definition: GNEUndoList.cpp:70
bool end() const
check if iterator is at the end
Definition: GNEUndoList.cpp:58
FXIcon * getIcon() const
get icon
Definition: GNEUndoList.cpp:83
Iterator()
default constructor
Iterator & operator++(int)
increment operator
Definition: GNEUndoList.cpp:94
GNEChange * myCurrentChange
current change
Definition: GNEUndoList.h:81
int getIndex() const
get index
Definition: GNEUndoList.cpp:64
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
Definition: GNEUndoList.h:236
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.
Definition: GNEUndoList.h:230
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
Definition: GNEUndoList.h:233
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
Definition: GNEViewNet.cpp:703
GNEViewParent * getViewParent() const
get the net object
void updateViewNet() const
Mark the entire GNEViewNet to be repainted later.
Definition: GNEViewNet.cpp:410
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