/*
* Copyright 2003-2010 Tufts University Licensed under the
* Educational Community License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.osedu.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package tufts.vue;
import java.util.List;
import static tufts.Util.*;
/**
* This is for events issued from within an LWComponent object
* hierarchy. Usually they are property change events, although in
* some cases they are used for more general purpose signaling.
* <p>In addition to the functionality of a
* java.beans.PropertyChangeEvent, this event also tells you who the
* change happened to (not always the same as the source: e.g.,
* hierarchy events are usually sourced from the the parent), and in the
* case of multiple components changing, can give you an entire list
* of components who were part of a single change (especially useful
* for hierarchy events, where issuing individual events would be
* messy given that the event delivery hierarchy would be changing as
* each subsequent event is issued). LWCevent also differs from a
* PropertyChangeEvent in that it does not include the NEW value,
* as that can be obtained by asking the changed component to
* tell us the current value, and because more often than not,
* the listener already has code to pull that out anyway: it just
* wants to know it's time to go ahead and do it's thing because
* something important to it has changed. As it is, it only contains
* the oldValue field for the UndoManager. (And LWComponents are not
* supposed to fire an LWCEvent notification unless a value has
* actually changed.)
* <p>LWCevent is also used for special events placed into the
* hierachy, such as as "user action completed" event, which can be
* useful for signaling model listeners that if you've been waiting to
* update yourself (usually for performance reasons), now is the time
* to catch up. E.g., the MapPanner doesn't bother to attempt
* repainting until it sees this event. In VUE, the UndoManager
* singles these events for us, as it already has to have been
* explicitly told when a user action has been completed in order to
* know when to stop and collect a new aggregate undo action.
* <p>It is also used in special cases to essentially issue a "repaint"
* event to the GUI, although these are stop-gap cases that ultimately
* would be better handled as a recognized property change.
* @version $Revision: 1.42 $ / $Date: 2010-02-03 19:17:41 $ / $Author: mike $
*/
// Consider subclassing from PropertyChangeEvent.
// Consider LWCEvent as a keyed event with a source only,
// and adding subclasses for the property change events?
public class LWCEvent
{
/** This is either a proper LWComponent Key object, or a String */
public final Object key;
/** This is either an actual old value, or an Undoable that is capable of restoring the old value */
final Object oldValue;
/** What initiated this event -- usually the same as component */
public final Object source;
/** a LWCevent can either hold a single component or an array of components: one of them is always null */
private List<LWComponent> components = null;
public final LWComponent component;
// todo: we still using both src & component?
public LWCEvent(Object source, LWComponent c, Object key, Object oldValue)
{
this.source = source;
this.component = c;
this.key = key;
this.oldValue = oldValue;
}
public LWCEvent(Object source, LWComponent c, Object key) {
this (source, c, key, NO_OLD_VALUE);
}
public LWCEvent(Object source, List<LWComponent> components, Object key)
{
this.source = source;
this.components = components;
this.component = null;
this.key = key;
this.oldValue = NO_OLD_VALUE;
}
public Object getSource()
{
return this.source;
}
/** @return true if this event originated within the model itself (e.g., not something like a proxy repaint event) */
public boolean isModelSourced() {
// todo: this won't be accurate for group events where component is null and
// components is set. We'll return false, tho that's okay for now as this will
// only increase the handle priority of the event, so it's an okay fallback
// position. Eventually, we should set component to the parent LWComponent
// experiencing the add/remove of a collection of children, so we can accurately
// determine if this was a model-sourced event (as oppsed to, say, an Action
// sourced event).
return source == component;
}
public LWComponent getComponent()
{
if (component == null && components != null && components.size() > 0) {
if (DEBUG.Enabled && components.size() > 1) {
tufts.Util.printStackTrace(this + "RETURNING FIRST ONLY WHEN IT CONTAINS "
+ components.size() + "\n" + components + "\n");
}
return (LWComponent) components.get(0);
} else
return component;
}
public LWComponent onlyComponent() {
if (component == null && components != null)
return null;
else
return component;
}
public List<LWComponent> getComponents() {
return this.components;
}
/** @return the name of the key for this LWCEvent */
public String getName() {
if (this.key instanceof LWComponent.Key)
return ((LWComponent.Key)key).name;
else
return (String) this.key;
}
/** If the key is a proper Key, return it, otherwise, return null */
public LWComponent.Key getKey() {
if (this.key instanceof LWComponent.Key)
return (LWComponent.Key) key;
else
return null;
}
/** @return either the LWKey or the String key */
public Object getCode() {
return key;
}
/**
* Returns an old value if one was given to us. As null is a valid
* old value, it's distinguished from having no old value set
* by the the value LWCEvent.NO_OLD_VALUE. Or you can
* check for the presence of an old value by calling hasOldValue().
* If our stored oldValue is actually an Undoable, this will unpack
* the old value from the Undoable and return it (if one was provided).
*/
public Object getOldValue() {
if (oldValue instanceof Undoable)
return ((Undoable)oldValue).old;
else
return this.oldValue;
}
/** @return true if there is an old value to this event */
public boolean hasOldValue() {
return this.oldValue != NO_OLD_VALUE;
}
public boolean isUndoable() {
// this is really only used in LWMap to determine if an event should
// trigger a change in the edited status of a map. We need to
// check HierarchyChanging explicitly, as these are change events
// w/out old-values -- the "old" values are determined specially
// in the UndoManager itself. This fixes VUE-1654.
return hasOldValue() || key == LWKey.HierarchyChanging;
}
public String toString() {
//return "LWCEvent[" + paramString() + "]";
//return "[" + paramString() + "]";
return String.format(TERM_PURPLE + "%08X[%s%s]%s", hashCode(), paramString(), TERM_PURPLE, TERM_CLEAR);
}
public String paramString()
{
final StringBuffer buf = new StringBuffer(//TERM_PURPLE +
String.format("%-20s ", key)
+ source);
//+ TERM_CLEAR + " " + source);
if (component != null && component != source) {
buf.append(" c=" + component);
//basic information.. if more information wants to be stringfied, need to code this part
} else if (components != null) {
buf.append(" list(" + components.size() + ": ");
if (components.size() == 1) {
buf.append(components.get(0));
} else {
java.util.Iterator<LWComponent> iter = components.iterator();
while (iter.hasNext()) {
LWComponent c = iter.next();
buf.append(c.getUniqueComponentTypeLabel());
if (iter.hasNext())
buf.append(", ");
}
}
buf.append(')');
}
if (oldValue != null && oldValue != NO_OLD_VALUE)
buf.append(" (" + oldValue + ")");
return buf.toString();
}
/** for null masking */
public static final String NO_OLD_VALUE = "no_old_value";
}