Gridarta Editor
UndoControl.java
Go to the documentation of this file.
1 /*
2  * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games.
3  * Copyright (C) 2000-2023 The Gridarta Developers.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 package net.sf.gridarta.gui.undo;
21 
22 import java.util.IdentityHashMap;
23 import java.util.Map;
24 import javax.swing.Action;
41 import net.sf.japi.swing.action.ActionBuilder;
42 import net.sf.japi.swing.action.ActionBuilderFactory;
43 import net.sf.japi.swing.action.ActionMethod;
44 import org.jetbrains.annotations.NotNull;
45 import org.jetbrains.annotations.Nullable;
46 
51 public class UndoControl<G extends GameObject<G, A, R>, A extends MapArchObject<A>, R extends Archetype<G, A, R>> implements EditorAction {
52 
56  @NotNull
57  private static final ActionBuilder ACTION_BUILDER = ActionBuilderFactory.getInstance().getActionBuilder("net.sf.gridarta");
58 
62  @NotNull
63  private final Map<MapModel<G, A, R>, UndoModel<G, A, R>> undoModels = new IdentityHashMap<>();
64 
68  @Nullable
70 
74  private int maxUndoStates = 100;
75 
79  @Nullable
80  private Action aUndo;
81 
85  @Nullable
86  private Action aRedo;
87 
94  public UndoControl(@NotNull final MapManager<G, A, R> mapManager, @NotNull final GameObjectFactory<G, A, R> gameObjectFactory, @NotNull final GameObjectMatchers gameObjectMatchers) {
95  final MapManagerListener<G, A, R> mapManagerListener = new MapManagerListener<G, A, R>() {
96 
97  @Override
98  public void currentMapChanged(@Nullable final MapControl<G, A, R> mapControl) {
99  mapModel = mapControl == null ? null : mapControl.getMapModel();
100  refreshMenus();
101  }
102 
103  @Override
104  public void mapCreated(@NotNull final MapControl<G, A, R> mapControl, final boolean interactive) {
105  final MapModel<G, A, R> mapModel = mapControl.getMapModel();
106  final MapTransactionListener<G, A, R> mapTransactionListener = new MapTransactionListener<G, A, R>() {
107 
113  @Nullable
114  private UndoState<G, A, R> undoState;
115 
120  @Nullable
121  private UndoModel<G, A, R> undoModel;
122 
123  @Override
124  public void preBeginTransaction() {
125  // ignore
126  }
127 
128  @Override
129  public void beginTransaction(@NotNull final String name) {
130  undoModel = undoModels.get(mapModel);
131  undoState = new UndoState<>(name, mapModel.getMapArchObject().createClone());
132  }
133 
134  @Override
135  public void endTransaction(@NotNull final SavedSquares<G, A, R> savedSquares) {
136  final UndoState<G, A, R> savedUndoState = undoState;
137  assert savedUndoState != null;
138  final UndoModel<G, A, R> savedUndoModel = undoModel;
139  assert savedUndoModel != null;
140  if (savedSquares.isEmpty() && savedUndoState.getMapArchObject().equals(mapModel.getMapArchObject())) {
141  return;
142  }
143 
144  final SavedSquares<G, A, R> clonedSavedSquares = savedSquares.cloneAndClear();
145  clonedSavedSquares.removeEmptySquares(savedUndoState.getMapArchObject().getMapSize());
146  savedUndoState.setSavedSquares(clonedSavedSquares);
147  savedUndoModel.finish(savedUndoState);
148  if (maxUndoStates > 0) {
149  savedUndoModel.trimToSize(maxUndoStates);
150  }
151 
152  refreshMenus();
153  }
154 
155  @Override
156  public void postEndTransaction() {
157  // ignore
158  }
159 
160  };
161 
162  mapModel.addMapTransactionListener(mapTransactionListener);
163  undoModels.put(mapModel, new UndoModel<>(gameObjectFactory, gameObjectMatchers, mapTransactionListener));
164  }
165 
166  @Override
167  public void mapClosing(@NotNull final MapControl<G, A, R> mapControl) {
168  // ignore
169  }
170 
171  @Override
172  public void mapClosed(@NotNull final MapControl<G, A, R> mapControl) {
173  final UndoModel<G, A, R> undoModel = undoModels.remove(mapControl.getMapModel());
174  assert undoModel != null;
175  mapControl.getMapModel().removeMapTransactionListener(undoModel.getMapTransactionListener());
176  }
177 
178  };
179  mapManager.addMapManagerListener(mapManagerListener);
180  }
181 
185  private void refreshMenus() {
186  if (aUndo != null) {
187  final boolean undoEnabled = doUndo(false);
188  aUndo.setEnabled(undoEnabled);
189  if (undoEnabled) {
190  final UndoModel<G, A, R> undoModel = undoModels.get(mapModel);
191  assert undoModel != null;
192  aUndo.putValue(Action.NAME, ACTION_BUILDER.format("undo.name", undoModel.undoName()));
193  } else {
194  aUndo.putValue(Action.NAME, ActionBuilderUtils.getString(ACTION_BUILDER, "undo.text"));
195  }
196  }
197 
198  if (aRedo != null) {
199  final boolean redoEnabled = doRedo(false);
200  aRedo.setEnabled(redoEnabled);
201  if (redoEnabled) {
202  final UndoModel<G, A, R> redoModel = undoModels.get(mapModel);
203  assert redoModel != null;
204  aRedo.putValue(Action.NAME, ACTION_BUILDER.format("redo.name", redoModel.redoName()));
205  } else {
206  aRedo.putValue(Action.NAME, ActionBuilderUtils.getString(ACTION_BUILDER, "redo.text"));
207  }
208  }
209  }
210 
214  @ActionMethod
215  public void undo() {
216  doUndo(true);
217  }
218 
222  @ActionMethod
223  public void redo() {
224  doRedo(true);
225  }
226 
232  public int getMaxUndoStates() {
233  return maxUndoStates;
234  }
235 
241  public void setMaxUndoStates(final int maxUndoStates) {
242  this.maxUndoStates = maxUndoStates;
243 
244  if (maxUndoStates > 0) {
245  for (final UndoModel<G, A, R> undoModel : undoModels.values()) {
246  undoModel.trimToSize(maxUndoStates);
247  }
248  }
249  }
250 
256  private boolean doUndo(final boolean performAction) {
257  final MapModel<G, A, R> tmpMapModel = mapModel;
258  if (tmpMapModel == null) {
259  return false;
260  }
261 
262  final UndoModel<G, A, R> undoModel = undoModels.get(tmpMapModel);
263  if (undoModel == null) {
264  // XXX: should be "assert undoModel != null"; this does not work
265  // because MapViewManager.activateMapView() calls
266  // mainControl.setCurrentLevel() which then generates a
267  // currentMapChanged() event before a mapCreated() event
268  return false;
269  }
270 
271  if (!undoModel.canUndo()) {
272  return false;
273  }
274 
275  if (performAction) {
276  UndoActions.undo(undoModel, tmpMapModel);
277  refreshMenus();
278  }
279 
280  return true;
281  }
282 
288  private boolean doRedo(final boolean performAction) {
289  final MapModel<G, A, R> tmpMapModel = mapModel;
290  if (tmpMapModel == null) {
291  return false;
292  }
293 
294  final UndoModel<G, A, R> undoModel = undoModels.get(tmpMapModel);
295  if (undoModel == null) {
296  // XXX: should be "assert undoModel != null"; this does not work
297  // because MapViewManager.activateMapView() calls
298  // mainControl.setCurrentLevel() which then generates a
299  // currentMapChanged() event before a mapCreated() event
300  return false;
301  }
302 
303  if (!undoModel.canRedo()) {
304  return false;
305  }
306 
307  if (performAction) {
308  UndoActions.redo(undoModel, tmpMapModel);
309  refreshMenus();
310  }
311 
312  return true;
313  }
314 
315  @Override
316  public void setAction(@NotNull final Action action, @NotNull final String name) {
317  if (name.equals("undo")) {
318  aUndo = action;
319  } else if (name.equals("redo")) {
320  aRedo = action;
321  } else {
322  throw new IllegalArgumentException("unsupported action name: " + name);
323  }
324  refreshMenus();
325  }
326 
327 }
name
name
Definition: ArchetypeTypeSetParserTest-ignoreDefaultAttribute1-result.txt:2
net.sf.gridarta.model.mapmanager
Definition: AbstractMapManager.java:20
net.sf.gridarta.gui.undo.UndoControl.mapModel
MapModel< G, A, R > mapModel
Map model for the current map.
Definition: UndoControl.java:69
net.sf.gridarta.model.mapmodel.MapModel
A MapModel reflects the data of a map.
Definition: MapModel.java:75
net.sf.gridarta.model.gameobject.GameObjectFactory
Abstract factory for creating GameObject instances.
Definition: GameObjectFactory.java:31
net.sf.gridarta.model.mapmodel.MapModel.getMapArchObject
A getMapArchObject()
Returns the Map Arch Object with the meta information about the map.
net.sf.gridarta.model.mapmanager.MapManager
A MapManager manages all opened maps.
Definition: MapManager.java:37
net.sf.gridarta.gui.undo.UndoControl.undo
void undo()
"Undo" was selected.
Definition: UndoControl.java:215
net.sf.gridarta.gui.undo.UndoControl.doRedo
boolean doRedo(final boolean performAction)
Performs the "redo" action.
Definition: UndoControl.java:288
net.sf.gridarta.model.undo.UndoState.getMapArchObject
A getMapArchObject()
Returns the map arch object before the operation started.
Definition: UndoState.java:107
net.sf.gridarta
Base package of all Gridarta classes.
net.sf.gridarta.model.undo.UndoState.setSavedSquares
void setSavedSquares(@NotNull final SavedSquares< G, A, R > savedSquares)
Records the affected map squares.
Definition: UndoState.java:81
net.sf.gridarta.model.mapmodel.MapTransactionListener
Interface for listeners listening on map transactions of MapModels.
Definition: MapTransactionListener.java:33
net.sf
net.sf.gridarta.model.mapmanager.MapManagerListener
Interface for listeners listening to MapManager changes.
Definition: MapManagerListener.java:42
net.sf.gridarta.model.mapmodel
Definition: AboveFloorInsertionMode.java:20
net.sf.gridarta.gui.undo.UndoControl
Implements the controller for undo/redo actions.
Definition: UndoControl.java:51
net.sf.gridarta.gui.undo.UndoControl.refreshMenus
void refreshMenus()
Enables/disables menu entries based on the current state.
Definition: UndoControl.java:185
net.sf.gridarta.gui.undo.UndoControl.aRedo
Action aRedo
Action for "redo" function.
Definition: UndoControl.java:86
net.sf.gridarta.gui.undo.UndoControl.UndoControl
UndoControl(@NotNull final MapManager< G, A, R > mapManager, @NotNull final GameObjectFactory< G, A, R > gameObjectFactory, @NotNull final GameObjectMatchers gameObjectMatchers)
Creates a new instance.
Definition: UndoControl.java:94
net.sf.gridarta.model.archetype
Definition: AbstractArchetype.java:20
net.sf.gridarta.actions.UndoActions.undo
static< G extends GameObject< G, A, R > A extends R extends Archetype< G, A, R > void undo(@NotNull final UndoModel< G, A, R > undoModel, @NotNull final MapModel< G, A, R > mapModel)
Definition: UndoActions.java:47
net.sf.gridarta.model.gameobject.GameObject
Reflects a game object (object on a map).
Definition: GameObject.java:36
net.sf.gridarta.model.mapcontrol
Definition: DefaultMapControl.java:20
net.sf.gridarta.gui.undo.UndoControl.doUndo
boolean doUndo(final boolean performAction)
Performs the "undo" action.
Definition: UndoControl.java:256
net.sf.gridarta.gui.undo.UndoControl.ACTION_BUILDER
static final ActionBuilder ACTION_BUILDER
Action Builder to create Actions.
Definition: UndoControl.java:57
net.sf.gridarta.gui.undo.UndoControl.setMaxUndoStates
void setMaxUndoStates(final int maxUndoStates)
Sets the maximum number of undo states saved for each map.
Definition: UndoControl.java:241
net.sf.gridarta.model.undo
Implementation of Undo / Redo for Gridarta.
Definition: package-info.java:24
net.sf.gridarta.model.mapmodel.SavedSquares
Records a set of changed map squares.
Definition: SavedSquares.java:41
net.sf.gridarta.model.gameobject
GameObjects are the objects based on Archetypes found on maps.
Definition: AbstractGameObject.java:20
net
net.sf.gridarta.actions.UndoActions
Utility class implementing undo and redo actions.
Definition: UndoActions.java:34
net.sf.gridarta.gui.undo.UndoControl.undoModels
final Map< MapModel< G, A, R >, UndoModel< G, A, R > > undoModels
Contains an UndoModel for each known MapModel.
Definition: UndoControl.java:63
net.sf.gridarta.model.match
Classes related to matching {GameObjects}, so called { net.sf.gridarta.model.match....
Definition: AndGameObjectMatcher.java:20
net.sf.gridarta.actions.UndoActions.redo
static< G extends GameObject< G, A, R > A extends R extends Archetype< G, A, R > void redo(@NotNull final UndoModel< G, A, R > undoModel, @NotNull final MapModel< G, A, R > mapModel)
Definition: UndoActions.java:56
net.sf.gridarta.model.maparchobject.MapArchObject
Interface for MapArchObjects.
Definition: MapArchObject.java:40
net.sf.gridarta.model.match.GameObjectMatchers
Maintains GameObjectMatcher instances.
Definition: GameObjectMatchers.java:40
net.sf.gridarta.model.undo.UndoModel.canUndo
boolean canUndo()
Return whether an "undo" operation is possible.
Definition: UndoModel.java:133
net.sf.gridarta.model.mapmodel.SavedSquares.removeEmptySquares
void removeEmptySquares(@NotNull final Size2D size)
Removes empty squares outside a given area.
Definition: SavedSquares.java:233
net.sf.gridarta.model.undo.UndoModel.undoName
String undoName()
Return the "undo" operation name.
Definition: UndoModel.java:150
net.sf.gridarta.model.mapmodel.SavedSquares.cloneAndClear
SavedSquares< G, A, R > cloneAndClear()
Creates a new instance having the same contents as this instance, then forgets all saves squares in t...
Definition: SavedSquares.java:165
net.sf.gridarta.utils.ActionBuilderUtils.getString
static String getString(@NotNull final ActionBuilder actionBuilder, @NotNull final String key, @NotNull final String defaultValue)
Returns the value of a key.
Definition: ActionBuilderUtils.java:71
net.sf.gridarta.model.undo.UndoState
Holds information to undo/redo one edit operation.
Definition: UndoState.java:34
net.sf.gridarta.model.mapmodel.MapModel.addMapTransactionListener
void addMapTransactionListener(@NotNull MapTransactionListener< G, A, R > listener)
Registers a map transaction listener.
net.sf.gridarta.gui.undo.UndoControl.maxUndoStates
int maxUndoStates
The maximum number of undo states saved for each map.
Definition: UndoControl.java:74
net.sf.gridarta.gui.undo.UndoControl.redo
void redo()
"Redo" was selected.
Definition: UndoControl.java:223
net.sf.gridarta.model
net.sf.gridarta.model.archetype.Archetype
Reflects an Archetype.
Definition: Archetype.java:41
net.sf.gridarta.model.undo.UndoModel.redoName
String redoName()
Return the "redo" operation name.
Definition: UndoModel.java:162
net.sf.gridarta.actions
Definition: AbstractServerActions.java:20
net.sf.gridarta.model.mapcontrol.MapControl
Currently nothing more than a marker interface for unification.
Definition: MapControl.java:35
net.sf.gridarta.utils.ActionBuilderUtils
Utility class for ActionBuilder related functions.
Definition: ActionBuilderUtils.java:31
net.sf.gridarta.model.undo.UndoModel.getMapTransactionListener
MapTransactionListener< G, A, R > getMapTransactionListener()
Returns the MapTransactionListener that was passed to the constructor.
Definition: UndoModel.java:97
net.sf.gridarta.model.undo.UndoModel.trimToSize
void trimToSize(final int maxUndoStates)
Discard old undo information.
Definition: UndoModel.java:118
net.sf.gridarta.model.maparchobject
Definition: AbstractMapArchObject.java:20
net.sf.gridarta.model.undo.UndoModel
Maintains the undo state for one map control.
Definition: UndoModel.java:40
net.sf.gridarta.gui.undo.UndoControl.getMaxUndoStates
int getMaxUndoStates()
Returns the maximum number of undo states saved for each map.
Definition: UndoControl.java:232
net.sf.gridarta.gui.undo.UndoControl.aUndo
Action aUndo
Action for "undo" function.
Definition: UndoControl.java:80
net.sf.gridarta.utils.EditorAction
A global editor action.
Definition: EditorAction.java:29
net.sf.gridarta.model.undo.UndoModel.finish
void finish(@NotNull final UndoState< G, A, R > undoState)
Finishes an undo or redo operation.
Definition: UndoModel.java:208
net.sf.gridarta.model.undo.UndoModel.canRedo
boolean canRedo()
Return whether a "redo" operation is possible.
Definition: UndoModel.java:142
net.sf.gridarta.utils
Definition: ActionBuilderUtils.java:20
net.sf.gridarta.gui.undo.UndoControl.setAction
void setAction(@NotNull final Action action, @NotNull final String name)
Sets the Action instance for this editor action.
Definition: UndoControl.java:316