Author: jfuerth
Date: Wed Sep  3 11:25:40 2008
New Revision: 2686

Added:
   trunk/src/ca/sqlpower/architect/swingui/PlayPenComponentLocationEdit.java
Modified:
   trunk/src/ca/sqlpower/architect/olap/undo/OLAPUndoManager.java
   trunk/src/ca/sqlpower/architect/swingui/olap/OLAPEditSession.java
   trunk/src/ca/sqlpower/architect/swingui/olap/OLAPPlayPenFactory.java
   trunk/src/ca/sqlpower/architect/undo/PropertyChangeEdit.java

Log:
Now OLAP playpen component repositioning is undoable. We took a different (better) approach to making this a compound operation. We intend to apply this same strategy to the relational playpen soon. See PlayPenComponentLocationEdit for details on how we've handled this.

Modified: trunk/src/ca/sqlpower/architect/olap/undo/OLAPUndoManager.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/olap/undo/OLAPUndoManager.java (original) +++ trunk/src/ca/sqlpower/architect/olap/undo/OLAPUndoManager.java Wed Sep 3 11:25:40 2008
@@ -111,6 +111,38 @@
         }
     }

+    public void startCompoundEdit(final String presentationName) {
+        compoundEditDepth++;
+ logger.debug("Starting compound edit. My depth: " + compoundEditDepth);
+        if (compoundEditDepth == 1) {
+ logger.debug("Beginning compound edit \"" + presentationName + "\"");
+            currentCompoundEdit = new CompoundEdit() {
+                @Override
+                public String getPresentationName() {
+                    return presentationName;
+                }
+            };
+            addEdit(currentCompoundEdit);
+        }
+    }
+
+    public void endCompoundEdit() {
+ logger.debug("Ending compound event. My depth: " + compoundEditDepth);
+        if (currentCompoundEdit == null || compoundEditDepth <= 0) {
+            throw new IllegalStateException(
+                    "Received a compoundEditEnded but was not " +
+ "in a compound edit state: depth=" + compoundEditDepth +
+                    "; currentEdit=" + currentCompoundEdit);
+        }
+        compoundEditDepth--;
+        if (compoundEditDepth == 0) {
+ logger.debug("Ending compound edit \"" + currentCompoundEdit.getPresentationName() + "\"");
+            currentCompoundEdit.end();
+            currentCompoundEdit = null;
+            fireChangeEvent();
+        }
+    }
+
private class UndoableEventHandler implements OLAPChildListener, PropertyChangeListener, CompoundEditListener {

         public void olapChildAdded(OLAPChildEvent e) {
@@ -128,36 +160,11 @@
         }

         public void compoundEditStarted(CompoundEditEvent evt) {
-            compoundEditDepth++;
- logger.debug("Got start compound event. My depth: " + compoundEditDepth);
-            if (compoundEditDepth == 1) {
-                final String presentationName = evt.getPresentationName();
- logger.debug("Beginning compound edit \"" + presentationName + "\"");
-                currentCompoundEdit = new CompoundEdit() {
-                    @Override
-                    public String getPresentationName() {
-                        return presentationName;
-                    }
-                };
-                addEdit(currentCompoundEdit);
-            }
+            startCompoundEdit(evt.getPresentationName());
         }
-
+
         public void compoundEditEnded(CompoundEditEvent evt) {
- logger.debug("Got end compound event. My depth: " + compoundEditDepth);
-            if (currentCompoundEdit == null || compoundEditDepth <= 0) {
-                throw new IllegalStateException(
-                        "Received a compoundEditEnded but was not " +
- "in a compound edit state: depth=" + compoundEditDepth +
-                        "; currentEdit=" + currentCompoundEdit);
-            }
-            compoundEditDepth--;
-            if (compoundEditDepth == 0) {
- logger.debug("Ending compound edit \"" + currentCompoundEdit.getPresentationName() + "\"");
-                currentCompoundEdit.end();
-                currentCompoundEdit = null;
-                fireChangeEvent();
-            }
+            endCompoundEdit();
         }

     }

Added: trunk/src/ca/sqlpower/architect/swingui/PlayPenComponentLocationEdit.java
==============================================================================
--- (empty file)
+++ trunk/src/ca/sqlpower/architect/swingui/PlayPenComponentLocationEdit.java Wed Sep 3 11:25:40 2008
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2008, SQL Power Group Inc.
+ *
+ * This file is part of Power*Architect.
+ *
+ * Power*Architect is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Power*Architect is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package ca.sqlpower.architect.swingui;
+
+import java.awt.Point;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.swing.undo.AbstractUndoableEdit;
+import javax.swing.undo.CannotRedoException;
+import javax.swing.undo.CannotUndoException;
+import javax.swing.undo.UndoableEdit;
+
+import ca.sqlpower.architect.undo.PropertyChangeEdit;
+import ca.sqlpower.architect.undo.UndoManager;
+
+/**
+ * An undoable edit that absorbs PropertyChangeEdits from PlayPenComponents. The
+ * way it is used is to add it to the undo manager using
+ * [EMAIL PROTECTED] UndoManager#addEdit(UndoableEdit)} before any components start moving.
+ * Then as each successive [EMAIL PROTECTED] PropertyChangeEdit} is added to 
the undo
+ * manager, this edit will absorb it if it represents a change in location of a
+ * PlayPenComponent. As soon as any other type of edit is added to the undo
+ * manager, this edit will refuse to absorb it, and new edits will be
+ * accumulated by the undo manager as usual.
+ */
+public class PlayPenComponentLocationEdit extends AbstractUndoableEdit {
+
+    private final Map<PlayPenComponent, Point> initialBounds =
+        new HashMap<PlayPenComponent, Point>();
+
+    private final Map<PlayPenComponent, Point> finalBounds =
+        new HashMap<PlayPenComponent, Point>();
+
+    @Override
+    public boolean addEdit(UndoableEdit anEdit) {
+        if (anEdit instanceof PropertyChangeEdit) {
+
+            PropertyChangeEdit pce = (PropertyChangeEdit) anEdit;
+
+            if (pce.getPropertyName().equals("location")
+                    && pce.getSource() instanceof PlayPenComponent) {
+                PlayPenComponent ppc = (PlayPenComponent) pce.getSource();
+                if (!initialBounds.containsKey(pce.getSource())) {
+ initialBounds.put(ppc, new Point((Point) pce.getOldValue()));
+                }
+                finalBounds.put(ppc, new Point((Point) pce.getNewValue()));
+                anEdit.die();
+                return true;
+            }
+
+        }
+        return false;
+    }
+
+    @Override
+    public void undo() throws CannotUndoException {
+        super.undo();
+ for (Map.Entry<PlayPenComponent, Point> entry : initialBounds.entrySet()) {
+            entry.getKey().setLocation(entry.getValue());
+        }
+    }
+
+    @Override
+    public void redo() throws CannotRedoException {
+        super.redo();
+ for (Map.Entry<PlayPenComponent, Point> entry : finalBounds.entrySet()) {
+            entry.getKey().setLocation(entry.getValue());
+        }
+    }
+
+    @Override
+    public String getUndoPresentationName() {
+        return "Undo moving " + initialBounds.size() + " objects";
+    }
+
+    @Override
+    public String getRedoPresentationName() {
+        return "Redo moving " + finalBounds.size() + " objects";
+    }
+
+    @Override
+    public boolean isSignificant() {
+        return finalBounds.size() > 0;
+    }
+}

Modified: trunk/src/ca/sqlpower/architect/swingui/olap/OLAPEditSession.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/swingui/olap/OLAPEditSession.java (original) +++ trunk/src/ca/sqlpower/architect/swingui/olap/OLAPEditSession.java Wed Sep 3 11:25:40 2008
@@ -128,9 +128,8 @@

         tree = new OLAPTree(swingSession, this, olapSession.getSchema());
         tree.setCellRenderer(new OLAPTreeCellRenderer());
-        pp = OLAPPlayPenFactory.createPlayPen(swingSession, this);
-
         undoManager = new OLAPUndoManager(olapSession);
+ pp = OLAPPlayPenFactory.createPlayPen(swingSession, this, undoManager);

         // Don't create actions here. PlayPen is currently null.
     }

Modified: trunk/src/ca/sqlpower/architect/swingui/olap/OLAPPlayPenFactory.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/swingui/olap/OLAPPlayPenFactory.java (original) +++ trunk/src/ca/sqlpower/architect/swingui/olap/OLAPPlayPenFactory.java Wed Sep 3 11:25:40 2008
@@ -20,6 +20,8 @@
 package ca.sqlpower.architect.swingui.olap;

 import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
@@ -45,9 +47,11 @@
 import ca.sqlpower.architect.olap.OLAPUtil;
 import ca.sqlpower.architect.olap.MondrianModel.Cube;
 import ca.sqlpower.architect.olap.MondrianModel.Hierarchy;
+import ca.sqlpower.architect.olap.undo.OLAPUndoManager;
 import ca.sqlpower.architect.swingui.ArchitectSwingSession;
 import ca.sqlpower.architect.swingui.PlayPen;
 import ca.sqlpower.architect.swingui.PlayPenComponent;
+import ca.sqlpower.architect.swingui.PlayPenComponentLocationEdit;
 import ca.sqlpower.architect.swingui.event.ItemSelectionEvent;
 import ca.sqlpower.architect.swingui.event.ItemSelectionListener;
 import ca.sqlpower.architect.swingui.event.PlayPenContentEvent;
@@ -57,12 +61,19 @@
 import ca.sqlpower.architect.swingui.event.SelectionEvent;
 import ca.sqlpower.architect.swingui.event.SelectionListener;
 import ca.sqlpower.architect.swingui.olap.DimensionPane.HierarchySection;
+import ca.sqlpower.architect.undo.PropertyChangeEdit;
+import ca.sqlpower.architect.undo.UndoCompoundEvent;
+import ca.sqlpower.architect.undo.UndoCompoundEventListener;

 public class OLAPPlayPenFactory {

private static final Logger logger = Logger.getLogger(OLAPPlayPenFactory.class);

- public static PlayPen createPlayPen(ArchitectSwingSession session, OLAPEditSession oSession) {
+    public static PlayPen createPlayPen(
+            ArchitectSwingSession session,
+            OLAPEditSession oSession,
+            OLAPUndoManager undoManager) {
+
         if (session == null) {
             throw new NullPointerException("Null session");
         }
@@ -81,6 +92,14 @@
         pp.addSelectionListener(synchronizer);
         oSession.getOlapTree().addTreeSelectionListener(synchronizer);
         pp.getContentPane().addPlayPenContentListener(synchronizer);
+
+ PlayPenUndoAdapter undoAdapter = new PlayPenUndoAdapter(undoManager);
+        for (PlayPenComponent ppc : pp.getPlayPenComponents()) {
+            ppc.addPropertyChangeListener(undoAdapter);
+        }
+        pp.getContentPane().addPlayPenContentListener(undoAdapter);
+        pp.addUndoEventListener(undoAdapter);
+
         return pp;
     }

@@ -263,6 +282,43 @@
         public void PlayPenLifeEnding(PlayPenLifecycleEvent e) {
OLAPUtil.unlistenToHierarchy(session.getOlapSession().getSchema(), this, null);
         }
+    }
+
+ static class PlayPenUndoAdapter implements PlayPenContentListener, PropertyChangeListener, UndoCompoundEventListener {
+
+        private final OLAPUndoManager undoManager;
+
+        PlayPenUndoAdapter(OLAPUndoManager undoManager) {
+            this.undoManager = undoManager;
+        }
+
+        public void PlayPenComponentAdded(PlayPenContentEvent e) {
+ logger.debug("Adding property change listener to new ppc: " + e.getPlayPenComponent());
+            e.getPlayPenComponent().addPropertyChangeListener(this);
+        }
+
+        public void PlayPenComponentRemoved(PlayPenContentEvent e) {
+ logger.debug("Removing property change listener from deleted ppc: " + e.getPlayPenComponent());
+            e.getPlayPenComponent().removePropertyChangeListener(this);
+        }
+
+        public void propertyChange(PropertyChangeEvent e) {
+            if (e.getPropertyName().equals("location")) {
+ // this edit will be absorbed by our PlayPenComponentLocationEdit
+                PropertyChangeEdit edit = new PropertyChangeEdit(e);
+                undoManager.addEdit(edit);
+            }
+        }
+
+        public void compoundEditStart(UndoCompoundEvent e) {
+            undoManager.addEdit(new PlayPenComponentLocationEdit());
+        }
+
+        public void compoundEditEnd(UndoCompoundEvent e) {
+            // the location edit will simply stop absorbing
+            // edits because new edits are of the wrong type
+        }
+
     }

     static class SelectionSynchronizer

Modified: trunk/src/ca/sqlpower/architect/undo/PropertyChangeEdit.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/undo/PropertyChangeEdit.java        
(original)
+++ trunk/src/ca/sqlpower/architect/undo/PropertyChangeEdit.java Wed Sep 3 11:25:40 2008
@@ -85,6 +85,35 @@
     public String getPresentationName() {
         return "property change edit";
     }
+
+    /**
+ * Returns the name of the property that this edit represents a change to.
+     */
+    public String getPropertyName() {
+        return sourceEvent.getPropertyName();
+    }
+
+    /**
+ * Returns the property's new value. This is the value that this edit will redo to.
+     */
+    public Object getNewValue() {
+        return sourceEvent.getNewValue();
+    }
+
+    /**
+ * Returns the property's old value. This is the value that this edit will undo to.
+     */
+    public Object getOldValue() {
+        return sourceEvent.getOldValue();
+    }
+
+    /**
+     * Returns the object whose property changed.
+     * @return
+     */
+    public Object getSource() {
+        return sourceEvent.getSource();
+    }

     @Override
     public String toString() {

Reply via email to