diff --git a/src/java/com/threerings/stage/tools/editor/EditorFrame.java b/src/java/com/threerings/stage/tools/editor/EditorFrame.java index e1d27db4..1a20ce30 100644 --- a/src/java/com/threerings/stage/tools/editor/EditorFrame.java +++ b/src/java/com/threerings/stage/tools/editor/EditorFrame.java @@ -72,7 +72,7 @@ public class EditorFrame extends ManagedJFrame { _writer = writer; // treat a closing window as a request to quit - addWindowListener(new WindowAdapter () { + addWindowListener(new WindowAdapter() { @Override public void windowClosing (WindowEvent e) { handleQuit(null); @@ -96,7 +96,7 @@ public class EditorFrame extends ManagedJFrame System.getProperty("user.dir")); } _chooser = (target == null) ? new JFileChooser() : new JFileChooser(target); - _chooser.setFileFilter(new FileFilter () { + _chooser.setFileFilter(new FileFilter() { @Override public boolean accept (File f) { return (f.isDirectory() || f.getName().endsWith(".xml")); } @@ -272,6 +272,14 @@ public class EditorFrame extends ManagedJFrame accel = KeyStroke.getKeyStroke(KeyEvent.VK_M, ActionEvent.CTRL_MASK); MenuUtil.addMenuItem(menuActions, "Update mini view", KeyEvent.VK_M, accel, this, "updateMiniView"); + + accel = KeyStroke.getKeyStroke(KeyEvent.VK_Z, ActionEvent.CTRL_MASK); + MenuUtil.addMenuItem(menuActions, "Undo", + KeyEvent.VK_Z, accel, this, "undo"); + + accel = KeyStroke.getKeyStroke(KeyEvent.VK_Y, ActionEvent.CTRL_MASK); + MenuUtil.addMenuItem(menuActions, "Redo", + KeyEvent.VK_Y, accel, this, "redo"); } protected void setScene (StageScene scene) @@ -494,6 +502,16 @@ public class EditorFrame extends ManagedJFrame _scrollBox.updateView(); } + public void undo (ActionEvent evt) + { + _svpanel.undo(); + } + + public void redo (ActionEvent evt) + { + _svpanel.redo(); + } + /** * Handles a request to open the preferences dialog. */ diff --git a/src/java/com/threerings/stage/tools/editor/EditorScenePanel.java b/src/java/com/threerings/stage/tools/editor/EditorScenePanel.java index 01a2d98e..0b3559cb 100644 --- a/src/java/com/threerings/stage/tools/editor/EditorScenePanel.java +++ b/src/java/com/threerings/stage/tools/editor/EditorScenePanel.java @@ -23,6 +23,7 @@ package com.threerings.stage.tools.editor; import java.util.ArrayList; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.awt.AlphaComposite; @@ -220,6 +221,8 @@ public class EditorScenePanel extends StageScenePanel */ protected void placeTile (int x, int y) { + markCheckpoint(); + Rectangle drag = clearTileSelectRegion(x, y); // sanity check @@ -250,6 +253,8 @@ public class EditorScenePanel extends StageScenePanel */ protected void deleteTile (int x, int y) { + markCheckpoint(); + Rectangle drag = clearTileSelectRegion(x, y); log.info("Deleting " + drag); @@ -301,6 +306,8 @@ public class EditorScenePanel extends StageScenePanel */ protected void editTile (int x, int y) { + markCheckpoint(); + // bail if we're not hovering over a scene object if (_hobject == null || !(_hobject instanceof SceneObject)) { return; @@ -332,6 +339,8 @@ public class EditorScenePanel extends StageScenePanel */ protected void editPortal (EditablePortal portal) { + markCheckpoint(); + // create our portal dialog if we haven't yet if (_dialogPortal == null) { _dialogPortal = new PortalDialog(_ctx, this); @@ -397,6 +406,8 @@ public class EditorScenePanel extends StageScenePanel break; case EditorModel.ACTION_PLACE_PORTAL: + markCheckpoint(); + Point fcoords = getFullCoords(mx, my); // mouse button three is delete if (event.getButton() == MouseEvent.BUTTON3) { @@ -939,7 +950,7 @@ public class EditorScenePanel extends StageScenePanel /** * Paint the specified StageLocation */ - protected void paintLocation(Graphics2D gfx, StageLocation loc, Color color, boolean highlight) + protected void paintLocation (Graphics2D gfx, StageLocation loc, Color color, boolean highlight) { // get the portal's center coordinate Point spos = new Point(); @@ -1118,6 +1129,49 @@ public class EditorScenePanel extends StageScenePanel } } + protected void markCheckpoint () + { + _undo.preserve(); + _redo.clear(); + } + + public void undo () + { + if (_undo.size() > 0) { + _redo.preserve(); + _undo.rollback(); + } + } + + public void redo () + { + if (_redo.size() > 0) { + _undo.preserve(); + _redo.rollback(); + } + } + + protected class UndoStack extends LinkedList + { + public void preserve () { + addFirst(_model.clone()); + + if (size() > MAX_UNDO_SIZE) { + removeLast(); + } + } + + public void rollback () { + if (size() > 0) { + _model = removeFirst(); + _blocks.clear(); + refreshScene(); + } + } + } + + protected static final int MAX_UNDO_SIZE = 20; + /** Provides access to stuff. */ protected EditorContext _ctx; @@ -1177,6 +1231,9 @@ public class EditorScenePanel extends StageScenePanel /** The object currently being edited by the object editor dialog. */ protected SceneObject _eobject; + protected UndoStack _undo = new UndoStack(); + protected UndoStack _redo = new UndoStack(); + /** The triangle used to render a portal on-screen. */ protected static Polygon _locTri;