[304] | 1 | //License: GPL. Copyright 2007 by Immanuel Scholz and others
|
---|
[301] | 2 | package org.openstreetmap.josm.data;
|
---|
| 3 |
|
---|
[2539] | 4 | import java.util.Collection;
|
---|
[304] | 5 | import java.util.Iterator;
|
---|
[301] | 6 | import java.util.LinkedList;
|
---|
| 7 |
|
---|
| 8 | import org.openstreetmap.josm.Main;
|
---|
| 9 | import org.openstreetmap.josm.command.Command;
|
---|
[2539] | 10 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
[2621] | 11 | import org.openstreetmap.josm.gui.MapView;
|
---|
[304] | 12 | import org.openstreetmap.josm.gui.layer.Layer;
|
---|
[301] | 13 | import org.openstreetmap.josm.gui.layer.OsmDataLayer.CommandQueueListener;
|
---|
| 14 |
|
---|
[2621] | 15 | public class UndoRedoHandler implements MapView.LayerChangeListener {
|
---|
[301] | 16 |
|
---|
[1169] | 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 | */
|
---|
[3262] | 24 | public final LinkedList<Command> redoCommands = new LinkedList<Command>();
|
---|
[301] | 25 |
|
---|
[4908] | 26 | private final LinkedList<CommandQueueListener> listenerCommands = new LinkedList<CommandQueueListener>();
|
---|
[301] | 27 |
|
---|
[1169] | 28 | public UndoRedoHandler() {
|
---|
[2621] | 29 | MapView.addLayerChangeListener(this);
|
---|
[1169] | 30 | }
|
---|
[304] | 31 |
|
---|
[1169] | 32 | /**
|
---|
| 33 | * Execute the command and add it to the intern command queue.
|
---|
| 34 | */
|
---|
[2098] | 35 | public void addNoRedraw(final Command c) {
|
---|
[1169] | 36 | c.executeCommand();
|
---|
| 37 | commands.add(c);
|
---|
[3143] | 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 | }
|
---|
[1169] | 44 | redoCommands.clear();
|
---|
[2098] | 45 | }
|
---|
| 46 |
|
---|
| 47 | public void afterAdd() {
|
---|
[1169] | 48 | fireCommandsChanged();
|
---|
[1877] | 49 |
|
---|
| 50 | // the command may have changed the selection so tell the listeners about the current situation
|
---|
[2348] | 51 | Main.main.getCurrentDataSet().fireSelectionChanged();
|
---|
[1169] | 52 | }
|
---|
[304] | 53 |
|
---|
[1169] | 54 | /**
|
---|
[2098] | 55 | * Execute the command and add it to the intern command queue.
|
---|
| 56 | */
|
---|
[2975] | 57 | synchronized public void add(final Command c) {
|
---|
[2098] | 58 | addNoRedraw(c);
|
---|
| 59 | afterAdd();
|
---|
| 60 | }
|
---|
| 61 |
|
---|
| 62 | /**
|
---|
[1169] | 63 | * Undoes the last added command.
|
---|
| 64 | */
|
---|
[3262] | 65 | public void undo() {
|
---|
| 66 | undo(1);
|
---|
| 67 | }
|
---|
| 68 |
|
---|
| 69 | /**
|
---|
| 70 | * Undoes multiple commands.
|
---|
| 71 | */
|
---|
| 72 | synchronized public void undo(int num) {
|
---|
[1169] | 73 | if (commands.isEmpty())
|
---|
| 74 | return;
|
---|
[2552] | 75 | Collection<? extends OsmPrimitive> oldSelection = Main.main.getCurrentDataSet().getSelected();
|
---|
[3910] | 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 | }
|
---|
[3479] | 85 | }
|
---|
[3262] | 86 | }
|
---|
[3910] | 87 | finally {
|
---|
| 88 | Main.main.getCurrentDataSet().endUpdate();
|
---|
| 89 | }
|
---|
[1169] | 90 | fireCommandsChanged();
|
---|
[2552] | 91 | Collection<? extends OsmPrimitive> newSelection = Main.main.getCurrentDataSet().getSelected();
|
---|
| 92 | if (!oldSelection.equals(newSelection)) {
|
---|
| 93 | Main.main.getCurrentDataSet().fireSelectionChanged();
|
---|
[2539] | 94 | }
|
---|
[1169] | 95 | }
|
---|
[301] | 96 |
|
---|
[1169] | 97 | /**
|
---|
| 98 | * Redoes the last undoed command.
|
---|
| 99 | */
|
---|
| 100 | public void redo() {
|
---|
[3262] | 101 | redo(1);
|
---|
| 102 | }
|
---|
| 103 |
|
---|
| 104 | /**
|
---|
| 105 | * Redoes multiple commands.
|
---|
| 106 | */
|
---|
| 107 | public void redo(int num) {
|
---|
[1169] | 108 | if (redoCommands.isEmpty())
|
---|
| 109 | return;
|
---|
[2552] | 110 | Collection<? extends OsmPrimitive> oldSelection = Main.main.getCurrentDataSet().getSelected();
|
---|
[3262] | 111 | for (int i=0; i<num; ++i) {
|
---|
[3275] | 112 | final Command c = redoCommands.removeFirst();
|
---|
[3262] | 113 | c.executeCommand();
|
---|
| 114 | commands.add(c);
|
---|
[3479] | 115 | if (redoCommands.isEmpty()) {
|
---|
[3262] | 116 | break;
|
---|
[3479] | 117 | }
|
---|
[3262] | 118 | }
|
---|
[1169] | 119 | fireCommandsChanged();
|
---|
[2552] | 120 | Collection<? extends OsmPrimitive> newSelection = Main.main.getCurrentDataSet().getSelected();
|
---|
| 121 | if (!oldSelection.equals(newSelection)) {
|
---|
| 122 | Main.main.getCurrentDataSet().fireSelectionChanged();
|
---|
| 123 | }
|
---|
[1169] | 124 | }
|
---|
[301] | 125 |
|
---|
[1169] | 126 | public void fireCommandsChanged() {
|
---|
[1814] | 127 | for (final CommandQueueListener l : listenerCommands) {
|
---|
[1169] | 128 | l.commandChanged(commands.size(), redoCommands.size());
|
---|
[1814] | 129 | }
|
---|
[1169] | 130 | }
|
---|
[301] | 131 |
|
---|
[1169] | 132 | public void clean() {
|
---|
| 133 | redoCommands.clear();
|
---|
| 134 | commands.clear();
|
---|
| 135 | fireCommandsChanged();
|
---|
| 136 | }
|
---|
[304] | 137 |
|
---|
[1894] | 138 | public void clean(Layer layer) {
|
---|
| 139 | if (layer == null)
|
---|
| 140 | return;
|
---|
[1169] | 141 | boolean changed = false;
|
---|
| 142 | for (Iterator<Command> it = commands.iterator(); it.hasNext();) {
|
---|
[1894] | 143 | if (it.next().invalidBecauselayerRemoved(layer)) {
|
---|
[1169] | 144 | it.remove();
|
---|
| 145 | changed = true;
|
---|
| 146 | }
|
---|
| 147 | }
|
---|
| 148 | for (Iterator<Command> it = redoCommands.iterator(); it.hasNext();) {
|
---|
[1894] | 149 | if (it.next().invalidBecauselayerRemoved(layer)) {
|
---|
[1169] | 150 | it.remove();
|
---|
| 151 | changed = true;
|
---|
| 152 | }
|
---|
| 153 | }
|
---|
[1814] | 154 | if (changed) {
|
---|
[1169] | 155 | fireCommandsChanged();
|
---|
[1814] | 156 | }
|
---|
[1169] | 157 | }
|
---|
[304] | 158 |
|
---|
[6084] | 159 | @Override
|
---|
[1894] | 160 | public void layerRemoved(Layer oldLayer) {
|
---|
| 161 | clean(oldLayer);
|
---|
| 162 | }
|
---|
| 163 |
|
---|
[6084] | 164 | @Override
|
---|
[1169] | 165 | public void layerAdded(Layer newLayer) {}
|
---|
[6084] | 166 | @Override
|
---|
[1169] | 167 | public void activeLayerChange(Layer oldLayer, Layer newLayer) {}
|
---|
[4908] | 168 |
|
---|
| 169 | public void removeCommandQueueListener(CommandQueueListener l) {
|
---|
| 170 | listenerCommands.remove(l);
|
---|
| 171 | }
|
---|
| 172 |
|
---|
| 173 | public boolean addCommandQueueListener(CommandQueueListener l) {
|
---|
| 174 | return listenerCommands.add(l);
|
---|
| 175 | }
|
---|
[301] | 176 | }
|
---|