| 1 | //License: GPL. Copyright 2007 by Immanuel Scholz and others |
|---|
| 2 | package org.openstreetmap.josm.data; |
|---|
| 3 | |
|---|
| 4 | import java.util.Collection; |
|---|
| 5 | import java.util.Iterator; |
|---|
| 6 | import java.util.LinkedList; |
|---|
| 7 | |
|---|
| 8 | import org.openstreetmap.josm.Main; |
|---|
| 9 | import org.openstreetmap.josm.command.Command; |
|---|
| 10 | import org.openstreetmap.josm.data.osm.OsmPrimitive; |
|---|
| 11 | import org.openstreetmap.josm.gui.MapView; |
|---|
| 12 | import org.openstreetmap.josm.gui.layer.Layer; |
|---|
| 13 | import org.openstreetmap.josm.gui.layer.OsmDataLayer.CommandQueueListener; |
|---|
| 14 | |
|---|
| 15 | public class UndoRedoHandler implements MapView.LayerChangeListener { |
|---|
| 16 | |
|---|
| 17 | /** |
|---|
| 18 | * All commands that were made on the dataset. Don't write from outside! |
|---|
| 19 | */ |
|---|
| 20 | public final LinkedList<Command> commands = new LinkedList<Command>(); |
|---|
| 21 | /** |
|---|
| 22 | * The stack for redoing commands |
|---|
| 23 | */ |
|---|
| 24 | public final LinkedList<Command> redoCommands = new LinkedList<Command>(); |
|---|
| 25 | |
|---|
| 26 | private final LinkedList<CommandQueueListener> listenerCommands = new LinkedList<CommandQueueListener>(); |
|---|
| 27 | |
|---|
| 28 | public UndoRedoHandler() { |
|---|
| 29 | MapView.addLayerChangeListener(this); |
|---|
| 30 | } |
|---|
| 31 | |
|---|
| 32 | /** |
|---|
| 33 | * Execute the command and add it to the intern command queue. |
|---|
| 34 | */ |
|---|
| 35 | public void addNoRedraw(final Command c) { |
|---|
| 36 | c.executeCommand(); |
|---|
| 37 | commands.add(c); |
|---|
| 38 | // Limit the number of commands in the undo list. |
|---|
| 39 | // Currently you have to undo the commands one by one. If |
|---|
| 40 | // this changes, a higher default value may be reasonable. |
|---|
| 41 | if (commands.size() > Main.pref.getInteger("undo.max", 1000)) { |
|---|
| 42 | commands.removeFirst(); |
|---|
| 43 | } |
|---|
| 44 | redoCommands.clear(); |
|---|
| 45 | } |
|---|
| 46 | |
|---|
| 47 | public void afterAdd() { |
|---|
| 48 | fireCommandsChanged(); |
|---|
| 49 | |
|---|
| 50 | // the command may have changed the selection so tell the listeners about the current situation |
|---|
| 51 | Main.main.getCurrentDataSet().fireSelectionChanged(); |
|---|
| 52 | } |
|---|
| 53 | |
|---|
| 54 | /** |
|---|
| 55 | * Execute the command and add it to the intern command queue. |
|---|
| 56 | */ |
|---|
| 57 | synchronized public void add(final Command c) { |
|---|
| 58 | addNoRedraw(c); |
|---|
| 59 | afterAdd(); |
|---|
| 60 | } |
|---|
| 61 | |
|---|
| 62 | /** |
|---|
| 63 | * Undoes the last added command. |
|---|
| 64 | */ |
|---|
| 65 | public void undo() { |
|---|
| 66 | undo(1); |
|---|
| 67 | } |
|---|
| 68 | |
|---|
| 69 | /** |
|---|
| 70 | * Undoes multiple commands. |
|---|
| 71 | */ |
|---|
| 72 | synchronized public void undo(int num) { |
|---|
| 73 | if (commands.isEmpty()) |
|---|
| 74 | return; |
|---|
| 75 | Collection<? extends OsmPrimitive> oldSelection = Main.main.getCurrentDataSet().getSelected(); |
|---|
| 76 | Main.main.getCurrentDataSet().beginUpdate(); |
|---|
| 77 | try { |
|---|
| 78 | for (int i=1; i<=num; ++i) { |
|---|
| 79 | final Command c = commands.removeLast(); |
|---|
| 80 | c.undoCommand(); |
|---|
| 81 | redoCommands.addFirst(c); |
|---|
| 82 | if (commands.isEmpty()) { |
|---|
| 83 | break; |
|---|
| 84 | } |
|---|
| 85 | } |
|---|
| 86 | } |
|---|
| 87 | finally { |
|---|
| 88 | Main.main.getCurrentDataSet().endUpdate(); |
|---|
| 89 | } |
|---|
| 90 | fireCommandsChanged(); |
|---|
| 91 | Collection<? extends OsmPrimitive> newSelection = Main.main.getCurrentDataSet().getSelected(); |
|---|
| 92 | if (!oldSelection.equals(newSelection)) { |
|---|
| 93 | Main.main.getCurrentDataSet().fireSelectionChanged(); |
|---|
| 94 | } |
|---|
| 95 | } |
|---|
| 96 | |
|---|
| 97 | /** |
|---|
| 98 | * Redoes the last undoed command. |
|---|
| 99 | */ |
|---|
| 100 | public void redo() { |
|---|
| 101 | redo(1); |
|---|
| 102 | } |
|---|
| 103 | |
|---|
| 104 | /** |
|---|
| 105 | * Redoes multiple commands. |
|---|
| 106 | */ |
|---|
| 107 | public void redo(int num) { |
|---|
| 108 | if (redoCommands.isEmpty()) |
|---|
| 109 | return; |
|---|
| 110 | Collection<? extends OsmPrimitive> oldSelection = Main.main.getCurrentDataSet().getSelected(); |
|---|
| 111 | for (int i=0; i<num; ++i) { |
|---|
| 112 | final Command c = redoCommands.removeFirst(); |
|---|
| 113 | c.executeCommand(); |
|---|
| 114 | commands.add(c); |
|---|
| 115 | if (redoCommands.isEmpty()) { |
|---|
| 116 | break; |
|---|
| 117 | } |
|---|
| 118 | } |
|---|
| 119 | fireCommandsChanged(); |
|---|
| 120 | Collection<? extends OsmPrimitive> newSelection = Main.main.getCurrentDataSet().getSelected(); |
|---|
| 121 | if (!oldSelection.equals(newSelection)) { |
|---|
| 122 | Main.main.getCurrentDataSet().fireSelectionChanged(); |
|---|
| 123 | } |
|---|
| 124 | } |
|---|
| 125 | |
|---|
| 126 | public void fireCommandsChanged() { |
|---|
| 127 | for (final CommandQueueListener l : listenerCommands) { |
|---|
| 128 | l.commandChanged(commands.size(), redoCommands.size()); |
|---|
| 129 | } |
|---|
| 130 | } |
|---|
| 131 | |
|---|
| 132 | public void clean() { |
|---|
| 133 | redoCommands.clear(); |
|---|
| 134 | commands.clear(); |
|---|
| 135 | fireCommandsChanged(); |
|---|
| 136 | } |
|---|
| 137 | |
|---|
| 138 | public void clean(Layer layer) { |
|---|
| 139 | if (layer == null) |
|---|
| 140 | return; |
|---|
| 141 | boolean changed = false; |
|---|
| 142 | for (Iterator<Command> it = commands.iterator(); it.hasNext();) { |
|---|
| 143 | if (it.next().invalidBecauselayerRemoved(layer)) { |
|---|
| 144 | it.remove(); |
|---|
| 145 | changed = true; |
|---|
| 146 | } |
|---|
| 147 | } |
|---|
| 148 | for (Iterator<Command> it = redoCommands.iterator(); it.hasNext();) { |
|---|
| 149 | if (it.next().invalidBecauselayerRemoved(layer)) { |
|---|
| 150 | it.remove(); |
|---|
| 151 | changed = true; |
|---|
| 152 | } |
|---|
| 153 | } |
|---|
| 154 | if (changed) { |
|---|
| 155 | fireCommandsChanged(); |
|---|
| 156 | } |
|---|
| 157 | } |
|---|
| 158 | |
|---|
| 159 | public void layerRemoved(Layer oldLayer) { |
|---|
| 160 | clean(oldLayer); |
|---|
| 161 | } |
|---|
| 162 | |
|---|
| 163 | public void layerAdded(Layer newLayer) {} |
|---|
| 164 | public void activeLayerChange(Layer oldLayer, Layer newLayer) {} |
|---|
| 165 | |
|---|
| 166 | public void removeCommandQueueListener(CommandQueueListener l) { |
|---|
| 167 | listenerCommands.remove(l); |
|---|
| 168 | } |
|---|
| 169 | |
|---|
| 170 | public boolean addCommandQueueListener(CommandQueueListener l) { |
|---|
| 171 | return listenerCommands.add(l); |
|---|
| 172 | } |
|---|
| 173 | } |
|---|