source: josm/trunk/src/org/openstreetmap/josm/data/UndoRedoHandler.java@ 13507

Last change on this file since 13507 was 13010, checked in by bastiK, 7 years ago

see #14120 - fix use of deprecated API (selection handling)

  • Property svn:eol-style set to native
File size: 6.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data;
3
4import java.util.Iterator;
5import java.util.LinkedList;
6
7import org.openstreetmap.josm.Main;
8import org.openstreetmap.josm.command.Command;
9import org.openstreetmap.josm.data.osm.DataSet;
10import org.openstreetmap.josm.spi.preferences.Config;
11import org.openstreetmap.josm.tools.CheckParameterUtil;
12
13/**
14 * This is the global undo/redo handler for all {@link DataSet}s.
15 * <p>
16 * If you want to change a data set, you can use {@link #add(Command)} to execute a command on it and make that command undoable.
17 */
18public class UndoRedoHandler {
19
20 /**
21 * All commands that were made on the dataset. Don't write from outside!
22 *
23 * @see #getLastCommand()
24 */
25 public final LinkedList<Command> commands = new LinkedList<>();
26 /**
27 * The stack for redoing commands
28 */
29 public final LinkedList<Command> redoCommands = new LinkedList<>();
30
31 private final LinkedList<CommandQueueListener> listenerCommands = new LinkedList<>();
32
33 /**
34 * Constructs a new {@code UndoRedoHandler}.
35 */
36 public UndoRedoHandler() {
37 // Do nothing
38 }
39
40 /**
41 * A listener that gets notified of command queue (undo/redo) size changes.
42 * @since 12718 (moved from {@code OsmDataLayer}
43 */
44 @FunctionalInterface
45 public interface CommandQueueListener {
46 /**
47 * Notifies the listener about the new queue size
48 * @param queueSize Undo stack size
49 * @param redoSize Redo stack size
50 */
51 void commandChanged(int queueSize, int redoSize);
52 }
53
54 /**
55 * Gets the last command that was executed on the command stack.
56 * @return That command or <code>null</code> if there is no such command.
57 * @since #12316
58 */
59 public Command getLastCommand() {
60 return commands.peekLast();
61 }
62
63 /**
64 * Executes the command and add it to the intern command queue.
65 * @param c The command to execute. Must not be {@code null}.
66 */
67 public void addNoRedraw(final Command c) {
68 CheckParameterUtil.ensureParameterNotNull(c, "c");
69 c.executeCommand();
70 commands.add(c);
71 // Limit the number of commands in the undo list.
72 // Currently you have to undo the commands one by one. If
73 // this changes, a higher default value may be reasonable.
74 if (commands.size() > Config.getPref().getInt("undo.max", 1000)) {
75 commands.removeFirst();
76 }
77 redoCommands.clear();
78 }
79
80 /**
81 * Fires a commands change event after adding a command.
82 */
83 public void afterAdd() {
84 fireCommandsChanged();
85 }
86
87 /**
88 * Executes the command and add it to the intern command queue.
89 * @param c The command to execute. Must not be {@code null}.
90 */
91 public synchronized void add(final Command c) {
92 addNoRedraw(c);
93 afterAdd();
94 }
95
96 /**
97 * Undoes the last added command.
98 */
99 public void undo() {
100 undo(1);
101 }
102
103 /**
104 * Undoes multiple commands.
105 * @param num The number of commands to undo
106 */
107 public synchronized void undo(int num) {
108 if (commands.isEmpty())
109 return;
110 DataSet ds = Main.main.getEditDataSet();
111 if (ds != null) {
112 ds.beginUpdate();
113 }
114 try {
115 for (int i = 1; i <= num; ++i) {
116 final Command c = commands.removeLast();
117 c.undoCommand();
118 redoCommands.addFirst(c);
119 if (commands.isEmpty()) {
120 break;
121 }
122 }
123 } finally {
124 if (ds != null) {
125 ds.endUpdate();
126 }
127 }
128 fireCommandsChanged();
129 }
130
131 /**
132 * Redoes the last undoed command.
133 */
134 public void redo() {
135 redo(1);
136 }
137
138 /**
139 * Redoes multiple commands.
140 * @param num The number of commands to redo
141 */
142 public void redo(int num) {
143 if (redoCommands.isEmpty())
144 return;
145 for (int i = 0; i < num; ++i) {
146 final Command c = redoCommands.removeFirst();
147 c.executeCommand();
148 commands.add(c);
149 if (redoCommands.isEmpty()) {
150 break;
151 }
152 }
153 fireCommandsChanged();
154 }
155
156 /**
157 * Fires a command change to all listeners.
158 */
159 private void fireCommandsChanged() {
160 for (final CommandQueueListener l : listenerCommands) {
161 l.commandChanged(commands.size(), redoCommands.size());
162 }
163 }
164
165 /**
166 * Resets the undo/redo list.
167 */
168 public void clean() {
169 redoCommands.clear();
170 commands.clear();
171 fireCommandsChanged();
172 }
173
174 /**
175 * Resets all commands that affect the given dataset.
176 * @param dataSet The data set that was affected.
177 * @since 12718
178 */
179 public void clean(DataSet dataSet) {
180 if (dataSet == null)
181 return;
182 boolean changed = false;
183 for (Iterator<Command> it = commands.iterator(); it.hasNext();) {
184 if (it.next().getAffectedDataSet() == dataSet) {
185 it.remove();
186 changed = true;
187 }
188 }
189 for (Iterator<Command> it = redoCommands.iterator(); it.hasNext();) {
190 if (it.next().getAffectedDataSet() == dataSet) {
191 it.remove();
192 changed = true;
193 }
194 }
195 if (changed) {
196 fireCommandsChanged();
197 }
198 }
199
200 /**
201 * Removes a command queue listener.
202 * @param l The command queue listener to remove
203 */
204 public void removeCommandQueueListener(CommandQueueListener l) {
205 listenerCommands.remove(l);
206 }
207
208 /**
209 * Adds a command queue listener.
210 * @param l The commands queue listener to add
211 * @return {@code true} if the listener has been added, {@code false} otherwise
212 */
213 public boolean addCommandQueueListener(CommandQueueListener l) {
214 return listenerCommands.add(l);
215 }
216}
Note: See TracBrowser for help on using the repository browser.