Author: jfuerth
Date: Tue Sep 2 14:50:11 2008
New Revision: 2675
Added:
trunk/src/ca/sqlpower/architect/olap/CompoundEditEvent.java
trunk/src/ca/sqlpower/architect/olap/CompoundEditListener.java
Modified:
trunk/src/ca/sqlpower/architect/olap/OLAPObject.java
trunk/src/ca/sqlpower/architect/olap/OLAPUtil.java
trunk/src/ca/sqlpower/architect/olap/undo/OLAPUndoManager.java
trunk/src/ca/sqlpower/architect/swingui/olap/action/CreateCubeAction.java
trunk/src/ca/sqlpower/architect/swingui/olap/action/CreateDimensionAction.java
Log:
Implemented compound edits on OLAPObjects, and the UndoManager support for
grouping them together on the undo list.
Also made the create actions for cubes and dimensions into atomic events
(without this, undoing the creation is a 2-step process: one for adding the
child, and one for modifying the properties)
Added: trunk/src/ca/sqlpower/architect/olap/CompoundEditEvent.java
==============================================================================
--- (empty file)
+++ trunk/src/ca/sqlpower/architect/olap/CompoundEditEvent.java Tue Sep 2
14:50:11 2008
@@ -0,0 +1,61 @@
+/*
+ * 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.olap;
+
+public class CompoundEditEvent {
+
+ private final OLAPObject source;
+
+ private final String presentationName;
+
+ /**
+ * Creates a new compound edit event from the given source with the
given
+ * presentation name.
+ *
+ * @param source
+ * The object that fired the event
+ * @param presentationName
+ * The name for this compound edit
+ */
+ public CompoundEditEvent(OLAPObject source, String presentationName) {
+ this.source = source;
+ this.presentationName = presentationName;
+ }
+
+ /**
+ * Creates a new compound edit event from the given source. The
presentation
+ * name will be null.
+ *
+ * @param source
+ * The object that fired the event
+ */
+ public CompoundEditEvent(OLAPObject source) {
+ this.source = source;
+ presentationName = null;
+ }
+
+ public OLAPObject getSource() {
+ return source;
+ }
+
+ public String getPresentationName() {
+ return presentationName;
+ }
+}
Added: trunk/src/ca/sqlpower/architect/olap/CompoundEditListener.java
==============================================================================
--- (empty file)
+++ trunk/src/ca/sqlpower/architect/olap/CompoundEditListener.java Tue Sep
2 14:50:11 2008
@@ -0,0 +1,53 @@
+/*
+ * 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.olap;
+
+/**
+ * Interface for listeners interested in being notified when a compound
+ * edit begins or ends.
+ */
+public interface CompoundEditListener {
+
+ /**
+ * Called when a compound edit has begun on an OLAPObject. All property
+ * change and child events from the source of this event and any of its
+ * descendants should be considered part of an atomic change that ends
when
+ * a [EMAIL PROTECTED] #compoundEditEnded(OLAPObject, String)} event is received
from
+ * the same source.
+ *
+ * @param source
+ * the object under which the following series of changes
should
+ * be considered atomic.
+ * @param presentationName
+ * The user-visible name for the sequence of edits.
+ */
+ void compoundEditStarted(CompoundEditEvent evt);
+
+ /**
+ * Called when a compound edit has ended on an OLAPObject. This method
will
+ * never be called more times than
+ * [EMAIL PROTECTED] #compoundEditStarted(OLAPObject, String)} has already been
called.
+ *
+ * @param source
+ * the object under which the compound edit state has ended.
+ */
+ void compoundEditEnded(CompoundEditEvent evt);
+
+}
Modified: trunk/src/ca/sqlpower/architect/olap/OLAPObject.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/olap/OLAPObject.java (original)
+++ trunk/src/ca/sqlpower/architect/olap/OLAPObject.java Tue Sep 2
14:50:11 2008
@@ -48,6 +48,11 @@
* parent's addChild method(s).
*/
private OLAPObject parent;
+
+ /**
+ * All compound edit listeners registered on this object.
+ */
+ private final List<CompoundEditListener> compoundEditListeners = new
ArrayList<CompoundEditListener>();
protected OLAPObject() {
pcs = new PropertyChangeSupport(this);
@@ -167,15 +172,40 @@
public void removePropertyChangeListener(PropertyChangeListener
listener) {
pcs.removePropertyChangeListener(listener);
}
-
- public void startCompoundEdit(String description) {
+
+ public void addCompoundEditListener(CompoundEditListener listener) {
+ compoundEditListeners.add(listener);
+ }
+
+ public void removeCompoundEditListener(CompoundEditListener listener) {
+ compoundEditListeners.remove(listener);
+ }
+
+ public void startCompoundEdit(String presentationName) {
compoundEditDepth++;
- // TODO fire compound edit event
+ logger.debug("Compound edit on " + getClass().getSimpleName() + "
starting. Depth=" + compoundEditDepth);
+ if (compoundEditDepth == 1) {
+ logger.debug("Firing compoundEditStarted to " +
compoundEditListeners.size() + " listeners...");
+ CompoundEditEvent evt = new CompoundEditEvent(this,
presentationName);
+ for (int i = compoundEditListeners.size() - 1; i >= 0; i--) {
+ compoundEditListeners.get(i).compoundEditStarted(evt);
+ }
+ }
}
public void endCompoundEdit() {
+ logger.debug("Compound edit on " + getClass().getSimpleName() + "
ending. Depth=" + compoundEditDepth);
+ if (compoundEditDepth <= 0) {
+ throw new IllegalStateException("Compound edit depth is
already " + compoundEditDepth);
+ }
compoundEditDepth--;
- // TODO fire compound edit event
+ if (compoundEditDepth == 0) {
+ logger.debug("Firing compoundEditEnded to " +
compoundEditListeners.size() + " listeners...");
+ CompoundEditEvent evt = new CompoundEditEvent(this);
+ for (int i = compoundEditListeners.size() - 1; i >= 0; i--) {
+ compoundEditListeners.get(i).compoundEditEnded(evt);
+ }
+ }
}
protected void fireChildAdded(Class<? extends OLAPObject> childClass,
int index, OLAPObject child) {
Modified: trunk/src/ca/sqlpower/architect/olap/OLAPUtil.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/olap/OLAPUtil.java (original)
+++ trunk/src/ca/sqlpower/architect/olap/OLAPUtil.java Tue Sep 2 14:50:11
2008
@@ -108,18 +108,49 @@
* OLAPObjects rooted at root. If you don't want to know
about
* property changes to the nodes themselves, you can leave
this
* parameter as null.
+ * @param cel
+ * The CompoundEditListener to add to the subtree of
OLAPObjects
+ * rooted at root. If you don't want to know about compound
edit
+ * groupings, you can leave this parameter as null.
*/
- public static void listenToHierarchy(OLAPObject root,
OLAPChildListener ocl, PropertyChangeListener pcl) {
+ public static void listenToHierarchy(
+ OLAPObject root,
+ OLAPChildListener ocl,
+ PropertyChangeListener pcl,
+ CompoundEditListener cel) {
root.addChildListener(ocl);
if (pcl != null) {
root.addPropertyChangeListener(pcl);
}
+ if (cel != null) {
+ root.addCompoundEditListener(cel);
+ }
for (OLAPObject child : root.getChildren()) {
- listenToHierarchy(child, ocl, pcl);
+ listenToHierarchy(child, ocl, pcl, cel);
}
}
/**
+ * Adds the given OLAPChildListener and optional
PropertyChangeListener to
+ * the given root object and all of its descendants.
+ *
+ * @param ocl
+ * The OLAPChildListener to add to the subtree of
OLAPObjects
+ * rooted at root.
+ * @param pcl
+ * The PropertyChangeListener to add to the subtree of
+ * OLAPObjects rooted at root. If you don't want to know
about
+ * property changes to the nodes themselves, you can leave
this
+ * parameter as null.
+ */
+ public static void listenToHierarchy(
+ OLAPObject root,
+ OLAPChildListener ocl,
+ PropertyChangeListener pcl) {
+ listenToHierarchy(root, ocl, pcl, null);
+ }
+
+ /**
* Removes the given OLAPChildListener and optional
PropertyChangeListener
* from the given root object and all of its descendants.
*
@@ -137,15 +168,55 @@
* if you want. If you weren't listening for property change
* events, you can leave this parameter as null. Note that
this
* parameter is pronounced "pockle," not "pickle."
+ * @param cel
+ * The CompoundEditListener to add to the subtree of
+ * OLAPObjects rooted at root. It is not an error if the
listener
+ * is not registered with any or all of the objects in the
+ * subtree, so it's safe to call this with the root of the
tree
+ * if you want. If you weren't listening for compound edit
+ * events, you can leave this parameter as null.
*/
- public static void unlistenToHierarchy(OLAPObject root,
OLAPChildListener ocl, PropertyChangeListener pcl) {
+ public static void unlistenToHierarchy(
+ OLAPObject root,
+ OLAPChildListener ocl,
+ PropertyChangeListener pcl,
+ CompoundEditListener cel) {
root.removeChildListener(ocl);
if (pcl != null) {
root.removePropertyChangeListener(pcl);
}
+ if (cel != null) {
+ root.removeCompoundEditListener(cel);
+ }
for (OLAPObject child : root.getChildren()) {
- unlistenToHierarchy(child, ocl, pcl);
+ unlistenToHierarchy(child, ocl, pcl, cel);
}
+ }
+
+ /**
+ * Removes the given OLAPChildListener and optional
PropertyChangeListener
+ * from the given root object and all of its descendants.
+ *
+ * @param ocl
+ * The OLAPChildListener to remove from the subtree of
+ * OLAPObjects rooted at root. It is not an error if the
listener
+ * is not registered with any or all of the objects in the
+ * subtree, so it's safe to call this with the root of the
tree
+ * if you want.
+ * @param pcl
+ * The PropertyChangeListener to add to the subtree of
+ * OLAPObjects rooted at root. It is not an error if the
listener
+ * is not registered with any or all of the objects in the
+ * subtree, so it's safe to call this with the root of the
tree
+ * if you want. If you weren't listening for property change
+ * events, you can leave this parameter as null. Note that
this
+ * parameter is pronounced "pockle," not "pickle."
+ */
+ public static void unlistenToHierarchy(
+ OLAPObject root,
+ OLAPChildListener ocl,
+ PropertyChangeListener pcl) {
+ unlistenToHierarchy(root, ocl, pcl, null);
}
/**
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 Tue Sep
2 14:50:11 2008
@@ -28,11 +28,14 @@
import javax.swing.event.ChangeListener;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
+import javax.swing.undo.CompoundEdit;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEdit;
import org.apache.log4j.Logger;
+import ca.sqlpower.architect.olap.CompoundEditEvent;
+import ca.sqlpower.architect.olap.CompoundEditListener;
import ca.sqlpower.architect.olap.OLAPChildEvent;
import ca.sqlpower.architect.olap.OLAPChildListener;
import ca.sqlpower.architect.olap.OLAPObject;
@@ -51,8 +54,23 @@
private boolean undoing = false;
+ /**
+ * Will be non-null when this undo manager is in the middle of
receiving a
+ * series of edits that are supposed to be undone and redone as one
step.
+ */
+ private CompoundEdit currentCompoundEdit;
+
+ /**
+ * The number of times we've started a compound edit minus the number
of
+ * times we've finished one. If greater than 0, we are in a compound
edit
+ * and [EMAIL PROTECTED] #currentCompoundEdit} will be non-null.
+ * <p>
+ * This value will never be less than 0.
+ */
+ private int compoundEditDepth = 0;
+
public OLAPUndoManager(OLAPObject root) {
- OLAPUtil.listenToHierarchy(root, eventHandler, eventHandler);
+ OLAPUtil.listenToHierarchy(root, eventHandler, eventHandler,
eventHandler);
// TODO need to track the playpen components as well
}
@@ -93,20 +111,53 @@
}
}
- private class UndoableEventHandler implements OLAPChildListener,
PropertyChangeListener {
+ private class UndoableEventHandler implements OLAPChildListener,
PropertyChangeListener, CompoundEditListener {
public void olapChildAdded(OLAPChildEvent e) {
addEdit(new OLAPChildEdit(e, false));
- OLAPUtil.listenToHierarchy(e.getChild(), this, this);
+ OLAPUtil.listenToHierarchy(e.getChild(), this, this, this);
}
public void olapChildRemoved(OLAPChildEvent e) {
addEdit(new OLAPChildEdit(e, true));
- OLAPUtil.unlistenToHierarchy(e.getChild(), this, this);
+ OLAPUtil.unlistenToHierarchy(e.getChild(), this, this, this);
}
public void propertyChange(PropertyChangeEvent e) {
addEdit(new PropertyChangeEdit(e));
+ }
+
+ 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);
+ }
+ }
+
+ 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();
+ }
}
}
Modified:
trunk/src/ca/sqlpower/architect/swingui/olap/action/CreateCubeAction.java
==============================================================================
---
trunk/src/ca/sqlpower/architect/swingui/olap/action/CreateCubeAction.java
(original)
+++
trunk/src/ca/sqlpower/architect/swingui/olap/action/CreateCubeAction.java
Tue Sep 2 14:50:11 2008
@@ -78,6 +78,7 @@
@Override
public DataEntryPanel place(Point p) throws ArchitectException {
+ schema.startCompoundEdit("Create Cube");
schema.addCube(cp.getModel());
playpen.selectNone();
playpen.addPlayPenComponent(cp, p);
@@ -87,6 +88,14 @@
@Override
public void discardChanges() {
schema.removeCube(cp.getModel());
+ schema.endCompoundEdit();
+ }
+
+ @Override
+ public boolean applyChanges() {
+ boolean applied = super.applyChanges();
+ schema.endCompoundEdit();
+ return applied;
}
};
return editPanel;
Modified:
trunk/src/ca/sqlpower/architect/swingui/olap/action/CreateDimensionAction.java
==============================================================================
---
trunk/src/ca/sqlpower/architect/swingui/olap/action/CreateDimensionAction.java
(original)
+++
trunk/src/ca/sqlpower/architect/swingui/olap/action/CreateDimensionAction.java
Tue Sep 2 14:50:11 2008
@@ -81,6 +81,7 @@
@Override
public DataEntryPanel place(Point p) throws ArchitectException {
+ schema.startCompoundEdit("Create a dimension");
schema.addDimension(dp.getModel());
playpen.selectNone();
playpen.addPlayPenComponent(dp, p);
@@ -90,6 +91,14 @@
@Override
public void discardChanges() {
schema.removeDimension(dp.getModel());
+ schema.endCompoundEdit();
+ }
+
+ @Override
+ public boolean applyChanges() {
+ boolean applied = super.applyChanges();
+ schema.endCompoundEdit();
+ return applied;
}
};
return editPanel;
@@ -97,3 +106,5 @@
}
}
+
+