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() {