/*
Copyright (c) 2003-2008 ITerative Consulting Pty Ltd. All Rights Reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:
o Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
o Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
o This jcTOOL Helper Class software, whether in binary or source form may not be used within,
or to derive, any other product without the specific prior written permission of the copyright holder
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package DisplayProject;
import java.awt.Container;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.NoSuchElementException;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import DisplayProject.actions.ActionMgr;
import DisplayProject.actions.PendingAction;
import DisplayProject.controls.ListView;
import DisplayProject.controls.OutlineField;
import Framework.CloneHelper;
import Framework.ErrorMgr;
import Framework.GenericNode;
import Framework.ImageData;
import Framework.TextData;
import Framework.ValueChangeListener;
/**
* The DisplayNode class defines a single data node for display in an {@link DisplayProkect.controls.OutlineField} object, a {@link DisplayProkect.controls.ListView object, or a {@link javax.swing.JTree} object. Each item or "row" in an outline field, list view, or tree view corresponds one to one with a DisplayNode object.
*/
@SuppressWarnings("deprecation")
public class DisplayNode extends DefaultMutableTreeNode implements PropertyChangeListener, ValueChangeListener/*, Cloneable, DeepCloneable*/, GenericNode, Comparable<DisplayNode> {
private static Logger logger = Logger.getLogger(DisplayNode.class);
private static final long serialVersionUID = 2282530876648303910L;
protected TextData DVNodeText = null;
protected ImageData DVLargeIcon = null;
protected ImageData DVSmallIcon = null;
protected ImageData DVSelectedIcon = null;
protected Object Related = null;
protected boolean opened;
// TF:21/9/07:Added the filled and folder attributes to get behaviour to mirror Forte's
protected boolean filled;
protected boolean folder;
//PM:12/10/07 list of properties to exclude from comparison
private static List<String> ignoreList = Arrays.asList(new String[] {
"listeners",
"level",
"leafCount",
"allowsChildren",
"changePrefix",
"childAt",
"childCount",
"class",
"depth",
"firstChild",
"firstLeaf",
"folder",
"indent",
"isFilled",
"isFolder",
"isOpened",
"lastChild",
"lastLeaf",
"leaf",
"nextLeaf",
"nextLine",
"nextNode",
"nextSibling",
"nodeParent",
"opened",
"opened_PRIVATE",
"parent",
"path",
"prevSibling",
"previousLeaf",
"previousNode",
"previousSibling",
"propertyListeners",
"related",
"root",
"rootNode",
"siblingCount",
"userObject",
"userObjectPath"} );
public static class qq_Resolver {
public static final int cDVNODETEXT = 1;
public static final int cISFILLED = 4;
public static final int cISFOLDER = 2;
public static final int cISOPENED = 3;
public static final int cPARENT = 5;
public static final int cDVNODETEXT_ISFILLED = 6;
public static final int cDVNODETEXT_ISFOLDER = 7;
public static final int cDVNODETEXT_ISOPENED = 8;
public static final int cDVNODETEXT_PARENT = 9;
public static final int cDVNODETEXT_DVSMALLICON = 10;
public static final int cISFILLED_ISFOLDER = 11;
public static final int cISFILLED_ISOPENED = 12;
public static final int cISFILLED_PARENT = 13;
public static final int cISFOLDER_ISOPENED = 14;
public static final int cISFOLDER_PARENT = 15;
public static final int cISOPENED_PARENT = 16;
public static final int cDVNODETEXT_ISFILLED_ISFOLDER = 17;
public static final int cDVNODETEXT_ISFILLED_ISOPENED = 18;
public static final int cDVNODETEXT_ISFILLED_PARENT = 19;
public static final int cDVNODETEXT_ISFOLDER_ISOPENED = 20;
public static final int cDVNODETEXT_ISFOLDER_PARENT= 21;
public static final int cDVNODETEXT_ISOPENED_PARENT = 22;
public static final int cDVNODETEXT_DVSMALLICON_RELATED = 23;
public static final int cISFILLED_ISFOLDER_ISOPENED = 24;
public static final int cISFILLED_ISFOLDER_PARENT = 25;
public static final int cISFOLDER_ISOPENED_PARENT = 26;
public static final int cDVNODETEXT_ISFILLED_ISFOLDER_ISOPENED = 27;
public static final int cDVNODETEXT_ISFILLED_ISFOLDER_PARENT = 28;
public static final int cDVNODETEXT_ISFILLED_ISOPENED_PARENT = 29;
public static final int cDVNODETEXT_ISFOLDER_ISOPENED_PARENT = 30;
public static final int cDVNODETEXT_ISFILLED_ISFOLDER_ISOPENED_PARENT = 31;
public static final int cDVNODETEXT_ISFILLED_ISFOLDER_ISOPENED_PARENT_RELATED = 32;
public static final int cDVLARGEICON_DVNODETEXT_ISFILLED_ISFOLDER_ISOPENED_PARENT_RELATED = 33;
public static final int cDVSELECTEDICON_DVSMALLICON_ISFILLED_ISFOLDER_ISOPENED_PARENT_RELATED = 34;
public static final int cDVNODETEXT_DVSELECTEDICON_DVSMALLICON_ISFILLED_ISFOLDER_ISOPENED_PARENT_RELATED = 35;
public static final int cDVNODETEXT_RELATED = 36;
}
protected transient String changePrefix = "";
/**
* @return Returns the changePrefix.
* @uml.property name="changePrefix"
*/
public String getChangePrefix() {
return changePrefix;
}
/**
* @param changePrefix The changePrefix to set.
* @uml.property name="changePrefix"
*/
public void setChangePrefix(String changePrefix) {
this.changePrefix = changePrefix + "_";
}
public DisplayNode(TextData pNodeText, int pResolver) {
this();
this.setDVNodeText(pNodeText);
}
public DisplayNode(String pNodeText, int pResolver) {
this();
this.setDVNodeText(pNodeText);
}
public DisplayNode(boolean pValue, int pResolver ) {
this();
switch (pResolver) {
case qq_Resolver.cISFOLDER:
setIsFolder(pValue);
break;
case qq_Resolver.cISOPENED:
setIsOpened(pValue);
break;
case qq_Resolver.cISFILLED:
setIsFilled(pValue);
break;
}
}
public DisplayNode(DisplayNode pParent, int pResolver) {
this();
this.setParent(pParent);
}
public DisplayNode(TextData pNodeText, DisplayNode pParent, int pResolver) {
this();
this.setDVNodeText(pNodeText);
this.setParent(pParent);
}
public DisplayNode(String pNodeText, DisplayNode pParent, int pResolver) {
this();
this.setDVNodeText(pNodeText);
this.setParent(pParent);
}
public DisplayNode(TextData pNodeText, Object pRelated, int pResolver) {
this();
this.setDVNodeText(pNodeText);
this.setRelated(pRelated);
}
public DisplayNode(TextData pNodeText, boolean pVal, int pResolver) {
this();
switch (pResolver) {
case qq_Resolver.cDVNODETEXT_ISFILLED:
this.setDVNodeText(pNodeText);
this.setIsFilled(pVal);
break;
case qq_Resolver.cDVNODETEXT_ISFOLDER:
this.setDVNodeText(pNodeText);
this.setIsFolder(pVal);
break;
case qq_Resolver.cDVNODETEXT_ISOPENED:
this.setDVNodeText(pNodeText);
this.setIsOpened(pVal);
break;
}
}
public DisplayNode(String pNodeText, boolean pVal, int pResolver) {
this();
switch (pResolver) {
case qq_Resolver.cDVNODETEXT_ISFILLED:
this.setDVNodeText(pNodeText);
this.setIsFilled(pVal);
break;
case qq_Resolver.cDVNODETEXT_ISFOLDER:
this.setDVNodeText(pNodeText);
this.setIsFolder(pVal);
break;
case qq_Resolver.cDVNODETEXT_ISOPENED:
this.setDVNodeText(pNodeText);
this.setIsOpened(pVal);
break;
}
}
public DisplayNode(boolean pVal1, boolean pVal2, int pResolver ) {
this();
switch (pResolver) {
case qq_Resolver.cISFOLDER_ISOPENED:
setIsFolder(pVal1);
setIsOpened(pVal2);
break;
case qq_Resolver.cISFILLED_ISFOLDER:
setIsFilled(pVal1);
setIsFolder(pVal2);
break;
case qq_Resolver.cISFILLED_ISOPENED:
setIsFilled(pVal1);
setIsOpened(pVal2);
break;
}
}
public DisplayNode(boolean pVal, DisplayNode pParent, int pResolver) {
this();
switch (pResolver) {
case qq_Resolver.cISFILLED_PARENT:
this.setParent(pParent);
this.setIsFilled(pVal);
break;
case qq_Resolver.cISFOLDER_PARENT:
this.setParent(pParent);
this.setIsFolder(pVal);
break;
case qq_Resolver.cISOPENED_PARENT:
this.setParent(pParent);
this.setIsOpened(pVal);
break;
}
}
public DisplayNode(TextData pNodeText, boolean pVal1, boolean pVal2, int pResolver) {
this();
switch (pResolver) {
case qq_Resolver.cDVNODETEXT_ISFILLED_ISFOLDER:
this.setDVNodeText(pNodeText);
this.setIsFilled(pVal1);
this.setIsFolder(pVal2);
break;
case qq_Resolver.cDVNODETEXT_ISFILLED_ISOPENED:
this.setDVNodeText(pNodeText);
this.setIsFilled(pVal1);
this.setIsOpened(pVal2);
break;
case qq_Resolver.cDVNODETEXT_ISFOLDER_ISOPENED:
this.setDVNodeText(pNodeText);
this.setIsFolder(pVal1);
this.setIsOpened(pVal2);
break;
}
}
public DisplayNode(TextData pNodeText, boolean pVal, DisplayNode pParent, int pResolver) {
this();
switch (pResolver) {
case qq_Resolver.cDVNODETEXT_ISFILLED_PARENT:
this.setDVNodeText(pNodeText);
this.setIsFilled(pVal);
this.setParent(pParent);
break;
case qq_Resolver.cDVNODETEXT_ISFOLDER_PARENT:
this.setDVNodeText(pNodeText);
this.setIsFolder(pVal);
this.setParent(pParent);
break;
case qq_Resolver.cDVNODETEXT_ISOPENED_PARENT:
this.setDVNodeText(pNodeText);
this.setIsOpened(pVal);
this.setParent(pParent);
break;
}
}
public DisplayNode(String pNodeText, boolean pVal, DisplayNode pParent, int pResolver) {
this();
switch (pResolver) {
case qq_Resolver.cDVNODETEXT_ISFILLED_PARENT:
this.setDVNodeText(pNodeText);
this.setIsFilled(pVal);
this.setParent(pParent);
break;
case qq_Resolver.cDVNODETEXT_ISFOLDER_PARENT:
this.setDVNodeText(pNodeText);
this.setIsFolder(pVal);
this.setParent(pParent);
break;
case qq_Resolver.cDVNODETEXT_ISOPENED_PARENT:
this.setDVNodeText(pNodeText);
this.setIsOpened(pVal);
this.setParent(pParent);
break;
}
}
public DisplayNode(boolean pVal1, boolean pVal2, boolean pVal3, int pResolver ) {
this();
switch (pResolver) {
case qq_Resolver.cISFILLED_ISFOLDER_ISOPENED:
setIsFolder(pVal2);
setIsOpened(pVal3);
setIsFilled(pVal1);
break;
}
}
public DisplayNode(boolean pVal1, boolean pVal2, DisplayNode pParent, int pResolver) {
this();
switch(pResolver) {
case qq_Resolver.cISFILLED_ISFOLDER_PARENT:
this.setIsFilled(pVal1);
this.setIsFolder(pVal2);
this.setParent(pParent);
break;
case qq_Resolver.cISFOLDER_ISOPENED_PARENT:
this.setIsFolder(pVal1);
this.setIsOpened(pVal2);
this.setParent(pParent);
break;
}
}
public DisplayNode(TextData pNodeText, boolean pVal1, boolean pVal2, boolean pVal3, int pResolver) {
this();
this.setDVNodeText(pNodeText);
this.setIsFilled(pVal1);
this.setIsFolder(pVal2);
this.setIsOpened(pVal3);
}
public DisplayNode(TextData pNodeText, boolean pVal1, boolean pVal2, DisplayNode pParent, int pResolver) {
this();
switch (pResolver) {
case qq_Resolver.cDVNODETEXT_ISFILLED_ISFOLDER_PARENT:
this.setDVNodeText(pNodeText);
this.setIsFilled(pVal1);
this.setIsFolder(pVal2);
this.setParent(pParent);
break;
case qq_Resolver.cDVNODETEXT_ISFILLED_ISOPENED_PARENT:
this.setDVNodeText(pNodeText);
this.setIsFilled(pVal1);
this.setIsOpened(pVal2);
this.setParent(pParent);
break;
case qq_Resolver.cDVNODETEXT_ISFOLDER_ISOPENED_PARENT:
this.setDVNodeText(pNodeText);
this.setIsFolder(pVal1);
this.setIsOpened(pVal2);
this.setParent(pParent);
break;
}
}
public DisplayNode(TextData pValue, boolean pIsFilled, boolean pIsFolder, boolean pIsOpened, DisplayNode pParent, int pResolver) {
this();
this.setDVNodeText(pValue);
this.setIsFilled(pIsFilled);
this.setIsFolder(pIsFolder);
this.setIsOpened(pIsOpened);
this.setParent(pParent);
}
public DisplayNode() {
super();
//TF:13/8/07:Forte initialised the display node properties to false,
// and the IsFolder property will be set to true by default, so override it.
// TF:21/9/07:Divorced the folder attribute from whether a node allows children or not.
// this.allowsChildren = false;
setDVNodeText(new TextData());
}
public DisplayNode(TextData nodeText, Object related) {
this();
setDVNodeText(nodeText);
setRelated(related);
}
public DisplayNode(String nodeText, Object related) {
this();
setDVNodeText(nodeText);
setRelated(related);
}
public DisplayNode(String nodeText) {
this();
setDVNodeText(nodeText);
}
public DisplayNode(TextData pNodeText, boolean pIsFilled, boolean pIsFolder, boolean pIsOpened, DisplayNode pParent, Object pRelated, int pResolver) {
this();
if (pResolver == qq_Resolver.cDVNODETEXT_ISFILLED_ISFOLDER_ISOPENED_PARENT_RELATED) {
this.setDVNodeText(pNodeText);
this.setIsFilled(pIsFilled);
this.setIsFolder(pIsFolder);
this.setIsOpened(pIsOpened);
this.setParent(pParent);
this.setRelated(pRelated);
}
}
public DisplayNode(String pNodeText, boolean pIsFilled, boolean pIsFolder, boolean pIsOpened, DisplayNode pParent, Object pRelated, int pResolver) {
this();
if (pResolver == qq_Resolver.cDVNODETEXT_ISFILLED_ISFOLDER_ISOPENED_PARENT_RELATED) {
this.setDVNodeText(pNodeText);
this.setIsFilled(pIsFilled);
this.setIsFolder(pIsFolder);
this.setIsOpened(pIsOpened);
this.setParent(pParent);
this.setRelated(pRelated);
}
}
public DisplayNode(ImageData pDVSelectedIcon, ImageData pDVSmallIcon, boolean pIsFilled, boolean pIsFolder, boolean pIsOpened, DisplayNode pParent, Object pRelated, int pResolver) {
this();
if (pResolver == qq_Resolver.cDVSELECTEDICON_DVSMALLICON_ISFILLED_ISFOLDER_ISOPENED_PARENT_RELATED) {
this.setDVSmallIcon(pDVSmallIcon);
this.setDVSelectedIcon(pDVSelectedIcon);
this.setIsFilled(pIsFilled);
this.setIsFolder(pIsFolder);
this.setIsOpened(pIsOpened);
this.setParent(pParent);
this.setRelated(pRelated);
}
}
public DisplayNode(ImageData pLargeIcon, TextData pNodeText, boolean pIsFilled, boolean pIsFolder, boolean pIsOpened, DisplayNode pParent, Object pRelated, int pResolver) {
this();
if (pResolver == qq_Resolver.cDVLARGEICON_DVNODETEXT_ISFILLED_ISFOLDER_ISOPENED_PARENT_RELATED) {
this.setDVLargeIcon(pLargeIcon);
this.setDVNodeText(pNodeText);
this.setIsFilled(pIsFilled);
this.setIsFolder(pIsFolder);
this.setIsOpened(pIsOpened);
this.setParent(pParent);
this.setRelated(pRelated);
}
}
public DisplayNode(ImageData pLargeIcon, String pNodeText, boolean pIsFilled, boolean pIsFolder, boolean pIsOpened, DisplayNode pParent, Object pRelated, int pResolver) {
this();
if (pResolver == qq_Resolver.cDVLARGEICON_DVNODETEXT_ISFILLED_ISFOLDER_ISOPENED_PARENT_RELATED) {
this.setDVLargeIcon(pLargeIcon);
this.setDVNodeText(pNodeText);
this.setIsFilled(pIsFilled);
this.setIsFolder(pIsFolder);
this.setIsOpened(pIsOpened);
this.setParent(pParent);
this.setRelated(pRelated);
}
}
public DisplayNode(TextData pNodeText, ImageData pDVSelectedIcon, ImageData pDVSmallIcon, boolean pIsFilled, boolean pIsFolder, boolean pIsOpened, DisplayNode pParent, Object pRelated, int pResolver) {
this();
if (pResolver == qq_Resolver.cDVNODETEXT_DVSELECTEDICON_DVSMALLICON_ISFILLED_ISFOLDER_ISOPENED_PARENT_RELATED) {
this.setDVNodeText(pNodeText);
this.setDVSmallIcon(pDVSmallIcon);
this.setDVSelectedIcon(pDVSelectedIcon);
this.setIsFilled(pIsFilled);
this.setIsFolder(pIsFolder);
this.setIsOpened(pIsOpened);
this.setParent(pParent);
this.setRelated(pRelated);
}
}
public DisplayNode(String pNodeText, ImageData pDVSelectedIcon, ImageData pDVSmallIcon, boolean pIsFilled, boolean pIsFolder, boolean pIsOpened, DisplayNode pParent, Object pRelated, int pResolver) {
this();
if (pResolver == qq_Resolver.cDVNODETEXT_DVSELECTEDICON_DVSMALLICON_ISFILLED_ISFOLDER_ISOPENED_PARENT_RELATED) {
this.setDVNodeText(pNodeText);
this.setDVSmallIcon(pDVSmallIcon);
this.setDVSelectedIcon(pDVSelectedIcon);
this.setIsFilled(pIsFilled);
this.setIsFolder(pIsFolder);
this.setIsOpened(pIsOpened);
this.setParent(pParent);
this.setRelated(pRelated);
}
}
public DisplayNode(TextData pNodeText, ImageData pDVSmallIcon, int pResolver) {
this();
if (pResolver == qq_Resolver.cDVNODETEXT_DVSMALLICON) {
this.setDVNodeText(pNodeText);
this.setDVSmallIcon(pDVSmallIcon);
}
}
public DisplayNode(String pNodeText, ImageData pDVSmallIcon, int pResolver) {
this();
if (pResolver == qq_Resolver.cDVNODETEXT_DVSMALLICON) {
this.setDVNodeText(pNodeText);
this.setDVSmallIcon(pDVSmallIcon);
}
}
public DisplayNode(TextData pNodeText, ImageData pDVSmallIcon, Object pRelated, int pResolver) {
this();
if (pResolver == qq_Resolver.cDVNODETEXT_DVSMALLICON_RELATED) {
this.setDVNodeText(pNodeText);
this.setDVSmallIcon(pDVSmallIcon);
this.setRelated(pRelated);
}
}
public DisplayNode(String pNodeText, ImageData pDVSmallIcon, Object pRelated, int pResolver) {
this();
if (pResolver == qq_Resolver.cDVNODETEXT_DVSMALLICON_RELATED) {
this.setDVNodeText(pNodeText);
this.setDVSmallIcon(pDVSmallIcon);
this.setRelated(pRelated);
}
}
// ------------------------------------------------------------------------
// Constructor to generate the selected icon - CraigM: 27/07/2005
// ------------------------------------------------------------------------
public DisplayNode(String pText, ImageIcon pIcon, Object pRelated, Container pFrame) {
this.setDVNodeText(pText);
this.setRelated(pRelated);
if (pFrame instanceof JFrame) {
if (pIcon != null && pIcon.getIconWidth() > 0 && pIcon.getIconHeight() > 0) {
this.setDVLargeIcon(pIcon);
// Generate the selected icon
BufferedImage aIconModifier = (BufferedImage)pFrame.createImage(pIcon.getIconWidth(), pIcon.getIconHeight());
Graphics2D g = aIconModifier.createGraphics(); // Get a Graphics2D object
g.drawImage(pIcon.getImage(), 0, 0, pFrame); // Draw the Image data into the BufferedImage
g.dispose();
// Blue dots
int aColour = new JList().getSelectionBackground().getRGB();
for (int x=0; x<aIconModifier.getWidth(); x++) {
for (int y=0; y<aIconModifier.getHeight(); y++) {
if (y%2 == 0 || x%2 == 0) {
if (y%2 != 0 || x%2 != 0) {
aIconModifier.setRGB(x, y, aColour);
}
}
}
}
ImageIcon aIconSelected = new ImageIcon();
aIconSelected.setImage(aIconModifier);
this.setDVSelectedIcon(aIconSelected);
}
}
else {
this.setDVLargeIcon(pIcon);
this.setDVSelectedIcon(pIcon);
}
}
/**
* Clone this object and return a shallow clone of it. The <code>related</code> reference will be copied to this object,
* the DVNodeText TextData will be cloned a new instance and attached to this object, and the parent and kids references
* will be cleared, as per Forte.
*/
public Object clone() {
Object result = super.clone();
((DisplayNode)result).setDVNodeText(this.getDVNodeText() == null ? null : CloneHelper.clone(this.getDVNodeText(), true));
return result;
}
// TF:14/04/2010:JCT-640:Changed these definitions
private transient PropertyChangeSupport qq_Listeners = null;
public void firePropertyChange(PropertyChangeEvent evt) {
if (qq_Listeners != null) {
qq_Listeners.firePropertyChange(evt);
}
}
public void firePropertyChange(String pName, boolean oldValue, boolean newValue) {
if (qq_Listeners != null) {
qq_Listeners.firePropertyChange(pName, oldValue, newValue);
}
}
public void firePropertyChange(String pName, int oldValue, int newValue) {
if (qq_Listeners != null) {
qq_Listeners.firePropertyChange(pName, oldValue, newValue);
}
}
public void firePropertyChange(String pName, Object oldValue, Object newValue) {
if (qq_Listeners != null) {
qq_Listeners.firePropertyChange(pName, oldValue, newValue);
}
}
public boolean hasListeners(String propertyName) {
if (qq_Listeners != null) {
return qq_Listeners.hasListeners(propertyName);
}
return false;
}
public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
if (qq_Listeners == null) {
qq_Listeners = new PropertyChangeSupport(this);
}
qq_Listeners.addPropertyChangeListener(listener);
}
public synchronized void addPropertyChangeListener(String property, PropertyChangeListener listener) {
if (qq_Listeners == null) {
qq_Listeners = new PropertyChangeSupport(this);
}
qq_Listeners.addPropertyChangeListener(property, listener);
}
public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
if (qq_Listeners != null) {
qq_Listeners.removePropertyChangeListener(listener);
}
}
public synchronized void removePropertyChangeListener(String property, PropertyChangeListener listener) {
if (qq_Listeners != null) {
qq_Listeners.removePropertyChangeListener(property, listener);
}
}
public synchronized PropertyChangeSupport getPropertyListeners() {
if (qq_Listeners == null)
qq_Listeners = new PropertyChangeSupport(this);
return this.qq_Listeners;
}
public synchronized void setPropertyListeners(PropertyChangeSupport pcs) {
qq_Listeners = pcs;
}
/**
* @return Returns the dVLargeIcon.
* @uml.property name="dVLargeIcon"
*/
public ImageData getDVLargeIcon() {
return DVLargeIcon;
}
/**
* @param dVLargeIcon The dVLargeIcon to set.
* @uml.property name="dVLargeIcon"
*/
public void setDVLargeIcon(ImageData largeIcon) {
ImageData oldValue = DVLargeIcon;
DVLargeIcon = largeIcon;
getPropertyListeners().firePropertyChange("DVLargeIcon", oldValue, largeIcon);
}
public void setDVLargeIcon(ImageIcon smallIcon) {
setDVLargeIcon(new ImageData(smallIcon));
}
/**
* @return Returns the dVSelectedIcon.
* @uml.property name="dVSelectedIcon"
*/
public ImageData getDVSelectedIcon() {
return DVSelectedIcon;
}
/**
* @param dVSelectedIcon The dVSelectedIcon to set.
* @uml.property name="dVSelectedIcon"
*/
public void setDVSelectedIcon(final ImageData selectedIcon) {
// UIutils.invokeOnGuiThread(new Runnable() {
// public void run() {
ImageData oldValue = DVSelectedIcon;
DVSelectedIcon = selectedIcon;
getPropertyListeners().firePropertyChange("DVSelectedIcon", oldValue, selectedIcon);
// }
// });
}
public void setDVSelectedIcon(ImageIcon smallIcon) {
setDVSelectedIcon(new ImageData(smallIcon));
}
/**
* @return Returns the dVSmallIcon.
* @uml.property name="dVSmallIcon"
*/
public ImageData getDVSmallIcon() {
return DVSmallIcon;
}
/**
* @param dVSmallIcon The dVSmallIcon to set.
* @uml.property name="dVSmallIcon"
*/
public void setDVSmallIcon(final ImageData smallIcon) {
ImageData oldValue = DVSmallIcon;
DVSmallIcon = smallIcon;
getPropertyListeners().firePropertyChange("DVSmallIcon", oldValue, smallIcon);
}
public void setDVSmallIcon(ImageIcon smallIcon) {
setDVSmallIcon(new ImageData(smallIcon));
}
/**
* @param dVNodeText The dVNodeText to set.
* @uml.property name="dVNodeText"
*/
public void setDVNodeText (TextData value) {
TextData oldValue = DVNodeText;
this.DVNodeText = value;
//setUserObject(this.DVNodeText);
getPropertyListeners().firePropertyChange("DVNodeText", oldValue, DVNodeText);
}
public void setDVNodeText (String value) {
this.setDVNodeText(new TextData(value));
}
/**
* @return Returns the dVNodeText.
* @uml.property name="dVNodeText"
*/
public TextData getDVNodeText () {
return this.DVNodeText;
}
public String toString() {
return (this.DVNodeText != null) ? this.DVNodeText.toString() : null;
}
/**
* The Indent attribute (integer) is a read-only attribute
* that shows a display node�s indentation level in its hierarchy.
* A display node traces its indent level from left to right,
* starting at one from the root node.
* @return the indentation of the node
*/
public int getIndent() {
// TF:24/04/2009:Added a call to processGUIActions to ensure all parents
// and children have been set properly prior to determining the depth
UIutils.processGUIActions();
return this.getLevel();
}
/**
* @param related The related to set.
* @uml.property name="related"
*/
public void setRelated (Object value) {
Object oldValue = this.Related;
this.Related = value;
getPropertyListeners().firePropertyChange("Related", oldValue, value);
}
/**
* @return Returns the related.
* @uml.property name="related"
*/
public Object getRelated () {
return this.Related;
}
public boolean getIsFilled () {
return filled;
}
public void setIsFilled (boolean value) {
this.filled = value;
}
public boolean isOpened () {
return getIsOpened();
}
public boolean getIsOpened () {
return this.opened;
}
/**
* Set the Opened property of this display node. This will <b>not</b> reflect the changes back to the
* underlying structure -- it is designed to be called in response to a treeExpansion event which indicates
* the user has changed the tree by manually expanding or collapsing it. It is public only as an implementation
* consequence and should not be called.
* @param value
*/
public void setOpened_PRIVATE(boolean value) {
this.opened = value;
}
public void setOpened(boolean value) {
this.setIsOpened(value);
}
public void setIsOpened (boolean value) {
if (value != this.opened) {
this.setOpened_PRIVATE(value);
reflectOpenChange(value);
}
}
private void reflectOpenChange(final boolean value) {
ActionMgr.addAction(new PendingAction(null) {
@Override
public String toString() {
return "DisplayNode.reflectChange(" + value + ")";
}
@Override
public void performAction() {
DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)DisplayNode.this.getRoot();
if (rootNode != null) {
Object root = rootNode.getUserObject();
if (root != null) {
JTree tree = null;
if (root instanceof JTree) {
tree = (JTree)root;
}
else if (root instanceof OutlineField) {
tree = ((OutlineField)root).getTree();
}
if (tree != null) {
TreePath path = new TreePath(DisplayNode.this.getPath());
if (value) {
tree.expandPath(path);
tree.scrollPathToVisible(path);
}
else {
tree.collapsePath(path);
}
}
}
}
}
});
}
public boolean isFolder() {
// TF:21/9/07: The Forte "Folder" attribue has nothing to do with whether children are allowed or
// not, just whether they're displayed or not.
// return super.getAllowsChildren();
return folder;
}
public boolean getIsFolder () {
return isFolder();
}
public void setFolder(boolean value) {
// TF:21/9/07: The Forte "Folder" attribue has nothing to do with whether children are allowed or
// not, just whether they're displayed or not.
// return super.getAllowsChildren();
this.folder = value;
}
public void setIsFolder (boolean value) {
setFolder(value);
}
@SuppressWarnings("unchecked")
public DisplayNode findRelated(Object obj) {
for (Enumeration e = super.depthFirstEnumeration(); e.hasMoreElements() ;) {
Object o = e.nextElement();
if (o instanceof DisplayNode && ((DisplayNode)o).getRelated() == obj) {
return (DisplayNode)o;
}
}
return null;
}
@SuppressWarnings("unchecked")
public Array_Of_DisplayNode<DisplayNode> findAllRelated(Object obj) {
Array_Of_DisplayNode<DisplayNode> result = null;
for (Enumeration e = super.depthFirstEnumeration() ; e.hasMoreElements() ;) {
Object o = e.nextElement();
if (o instanceof DisplayNode && ((DisplayNode)o).getRelated() == obj) {
if (result == null) {
result = new Array_Of_DisplayNode<DisplayNode>();
}
result.add((DisplayNode)o);
}
}
return result;
}
public void removeFromParent() {
if (getParent() != null)
super.removeFromParent();
}
public void fireChange() {
}
public void propertyChange(PropertyChangeEvent arg0) {
getPropertyListeners().firePropertyChange(arg0.getPropertyName(), arg0.getOldValue(), arg0.getNewValue());
}
/**
* The UpdateFieldFromData method updates the outline field in which the display node is being displayed.
* Note that because this method can often be called repeatedly in quick succession we don't necessarily
* want to process the GUI actions which will involve a thread context switch. If there really is a need
* to update the screen immediately, explicitly invoke {@link UIutils.processGUIActions()}
*/
public void updateFieldFromData(){
ActionMgr.addAction(new PendingAction(null) {
@Override
public String toString() {
return "DisplayNode.updateFieldFromData()";
}
@Override
public void performAction() {
// Update the related object if needed.
DisplayNode aNode = DisplayNode.this;
while (aNode.parent != null)
aNode = (DisplayNode)aNode.getParent();
Object anObject = aNode.getUserObject();
if (anObject != null && anObject instanceof JComponent) {
((JComponent)anObject).updateUI();
}
DisplayNode.this.fireChange();
}
});
// TF:5/2/08:Removed this code as there's no need to process all the actions immediately
// (doing so can really slow processing down)
// ActionMgr.processGUIActions();
}
// TF:29/7/07:There is a complex interrelationship between many of the display node
// methods. For example, if I have Node A with child B, and I call B.setNextSibling(C)
// then I'm implicitly setting the parent of C as well. For this reason, we do all the
// sets as pending actions, but all the gets force the processing of the GUI actions,
// and then the gets are perfomed on the underlying model.
/**
* The isContainedIn method returns true if the current node is descended from the specified
* node or any of its descendant nodes.
*/
public boolean isContainedIn(GenericNode container) {
ActionMgr.processGUIActions();
DisplayNode parent = (DisplayNode)this.getParent();
while (parent != null) {
if (parent == container) {
return true;
}
parent = (DisplayNode)parent.getParent();
}
return false;
}
/**
* This method checks the specified node and its parent nodes, to see if the current node is
* an ancestor of the specified node. If it is, it returns <b>true</b>; if not, it returns <b>false</b>.
* @param node
* @return true if the current node is an ancestor of the specified node.
*/
public boolean isContainerFor(GenericNode node) {
if (node != null) {
return ((DisplayNode)node).isContainedIn(this);
}
return false;
}
/**
* Removes a node from it's parent and updates the underlying model appropriately. This should only be called on the EDT
* @param pNode
*/
private void removeParent() {
if (this.parent != null) {
boolean handled = false;
DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)getRoot();
if (rootNode != null) {
Object root = rootNode.getUserObject();
if (root != null) {
if (root instanceof JTree) {
DefaultTreeModel model = (DefaultTreeModel)(((JTree)root).getModel());
model.removeNodeFromParent(this);
handled = true;
}
else if (root instanceof ListView) {
ListView lv = (ListView)root;
lv.removeNode(this);
this.removeFromParent();
handled = true;
}
else if (root instanceof OutlineField) {
DefaultTreeModel model = (DefaultTreeModel)(((OutlineField)root).getModel());
model.removeNodeFromParent(this);
handled = true;
}
}
}
if (!handled) {
this.removeFromParent();
}
}
}
/**
* This method updates the underlying data model when the display node hierarchy is changed. It removes the
* node from any existing hierarchy and inserts it appropriately into the new hierarchy
*/
private void insertNode(DisplayNode pChild, DisplayNode pParent, int index) {
if (pChild.parent == pParent) {
// We're already a child and we're going to be re-ordered. That's fine, but we need to
// compensate for the index.
int oldIndex = pParent.getIndex(pChild);
if (oldIndex <= index) {
index--;
}
}
pChild.removeParent();
boolean handled = false;
DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)pParent.getRoot();
if (rootNode != null) {
Object root = rootNode.getUserObject();
if (root != null) {
if (root instanceof JTree) {
JTree tree = (JTree)root;
DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
model.insertNodeInto(pChild, pParent, index);
// TF:21/9/07:Re-ordered this so new nodes inserted into an existing tree would be opened/closed properly
if (!tree.isRootVisible() || !tree.getShowsRootHandles()) {
TreePath path = new TreePath(pChild);
if (pChild.isOpened()) {
tree.expandPath(path);
}
else {
tree.collapsePath(path);
}
}
if (pParent.isOpened()) {
// CraigM:12/06/2008 - Don't scroll (as Forte didn't), just make visible.
// tree.scrollPathToVisible(new TreePath(pChild.getPath()));
tree.makeVisible(new TreePath(pChild.getPath()));
}
handled = true;
}
else if (root instanceof OutlineField) {
OutlineField of = (OutlineField)root;
DefaultTreeModel model = (DefaultTreeModel)of.getModel();
model.insertNodeInto(pChild, pParent, index);
if (!of.isRootDisplayed()) {
TreePath path = new TreePath(rootNode);
if (pChild.isOpened()) {
of.getTree().expandPath(path);
}
}
of.nodeAdded(pChild);
handled = true;
}
else if (root instanceof ListView) {
((ListView)root).insertNode(index, pChild);
// TF:1/2/08:Added setting of the parent explicitly
pParent.insert(pChild, index);
handled = true;
}
}
}
if (!handled) {
pParent.insert(pChild, index);
}
}
/**
* Removes value from its present parent (if it has a parent), sets the child's parent to this node,
* and then adds the child to this node's child array at index 0. value must not be null and must not be an ancestor of this node.
* @param value
*/
public void setFirstChild(final DisplayNode value) {
ActionMgr.addAction(new PendingAction(null) {
@Override
public String toString() {
return "DisplayNode.setFirstChild(" + value + ")";
}
@Override
public void performAction() {
if (allowsChildren) {
// TF:22/9/07:Added a check for the null child in. It is not fully understood
// what effect setting this to null will have, but it seems like it removes all the children
if(value == null) {
while(getChildCount() > 0 && getChildAt(0) != null) {
((DisplayNode)getChildAt(0)).removeParent();
}
}
else {
insertNode(value, DisplayNode.this, 0);
}
}
}
});
}
/**
* Return the node�s immediate child node, or null if there is no immediate child.
* @param value
*/
public TreeNode getFirstChild() {
ActionMgr.processGUIActions();
try {
return super.getFirstChild();
}
catch (NoSuchElementException e) {
return null;
}
}
/**
* Set the node's last child node
* @param value
*/
public void setLastChild(final DisplayNode value) {
if (value != null) {
ActionMgr.addAction(new PendingAction(null) {
@Override
public String toString() {
return "DisplayNode.setLastChild(" + value + ")";
}
public void performAction() {
if (allowsChildren) {
insertNode(value, DisplayNode.this, getChildCount());
}
}
});
}
}
/**
* Return the last child node of this node, or null if there are no children
* @param value
* @return
*/
public TreeNode getLastChild() {
ActionMgr.processGUIActions();
try {
return super.getLastChild();
}
catch (NoSuchElementException e) {
return null;
}
}
/**
* set the next sibling of the current node to the passed node. If the node
* is currently parented to another display node, it will be removed from
* this node prior to be added to this node's parent.
* @param value
*/
public void setNextSibling(final DisplayNode value) {
if (value != null) {
ActionMgr.addAction(new PendingAction(null) {
@Override
public void performAction() {
if (getParent() != null) {
int index = getParent().getIndex(DisplayNode.this);
if (index >= 0) {
insertNode(value, (DisplayNode)getParent(), index+1);
}
}
else if (value.getParent() != null) {
value.setPrevSibling(DisplayNode.this);
}
}
});
}
}
/**
* Return the sibling directly after this child.
*/
public DisplayNode getNextSibling() {
ActionMgr.processGUIActions();
if (getParent() == null) {
return null;
}
else {
return (DisplayNode)((DisplayNode)getParent()).getChildAfter(this);
}
}
/**
* Set the previous sibling of this node. Note that this will try to parent the passed node
* onto this node's parent, but if this node has no parent, this node will be parented to
* the passed node's parent, to mimic the forte behaviour.
* @param value
*/
public void setPrevSibling(final DisplayNode value) {
if (value != null) {
ActionMgr.addAction(new PendingAction(null) {
@Override
public void performAction() {
if (getParent() != null) {
int index = getParent().getIndex(DisplayNode.this);
if (index >= 0) {
insertNode(value, (DisplayNode)getParent(), index);
}
}
else if (value.getParent() != null) {
value.setNextSibling(DisplayNode.this);
}
}
});
}
}
/**
* Return the sibling directly prior to this child.
*/
public DisplayNode getPrevSibling() {
ActionMgr.processGUIActions();
if (getParent() == null) {
return null;
}
else {
return (DisplayNode)((DisplayNode)getParent()).getChildBefore(this);
}
}
/**
* Get the parent of the display node. This method processes the pending actions prior
* to retrieving the parent to ensure any pending action changes are applied first.
*/
@Override
public TreeNode getParent() {
ActionMgr.processGUIActions();
return super.getParent();
}
/**
* Set the parent of this display node. Uses a pending action for delayed write
* @param pParent
*/
public void setParent(final DisplayNode pParent) {
ActionMgr.addAction(new PendingAction(null) {
@Override
public void performAction() {
DisplayNode currentParent = (DisplayNode)getParent();
if (pParent != currentParent) {
//Remove this from its current Parent
DisplayNode.this.removeParent();
if (pParent != null){
if (logger.isDebugEnabled()) {
logger.debug("DisplayNode[" + this.toString() + "].setParent(" + pParent.toString() + ")" );
}
// TF:21/9/07:Fixed this up, as Forte does not set this value to be true when adding children
// if (!pParent.isFolder()) {
// pParent.setFolder(true);
// }
insertNode(DisplayNode.this, pParent, pParent.getChildCount());
}
}
}
});
}
public void setNodeParent(DisplayNode value) {
super.setParent((DisplayNode)value);
this.updateFieldFromData();
}
/**
* undocumented forte method
* @return
*/
public DisplayNode getNextLine(){
DisplayNode result;
if (this.getChildCount() > 0) {
return (DisplayNode)this.getFirstChild();
}
else if ((result = (DisplayNode)this.getNextSibling()) != null){
return result;
}
else {
DisplayNode aNode = (DisplayNode)this.getParent();
while (aNode != null) {
if ((result = (DisplayNode)aNode.getNextSibling()) != null){
return result;
}
aNode = (DisplayNode)aNode.getParent();
}
}
return null;
}
public DisplayNode getRootNode(){
DisplayNode aNode = this;
while(aNode.getParent() != null){
aNode = (DisplayNode)aNode.getParent();
}
return aNode;
}
public void setParent(GenericNode value) {
setParent((DisplayNode)value);
}
/**
* TF:22/9/07: Fix for allowing the node to display like a folder when the
* attribute says it's a folder. We make all folders appear as such
*/
@Override
public boolean isLeaf() {
return !getIsFolder();
}
/**
* this method compares two display nodes based on their property values
*/
public int compareTo(DisplayNode source) {
PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(this.getClass());
int plus = 0;
int minus = 0;
for (int i = 0; i < targetPds.length; i++) {
PropertyDescriptor targetPd = targetPds[i];
if (targetPd != null &&
(ignoreList == null || (!ignoreList.contains(targetPd.getName())))) {
PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd == null) {
plus++;
minus++;
continue;
}
if (targetPd.getPropertyType() == null)
continue;
if (!targetPd.getPropertyType().equals(sourcePd.getPropertyType())){
plus++;
minus++;
continue;
}
try {
// CraigM: 19/03/2008 - Cater for methods that can't be found
Method sourceMethod = sourcePd.getReadMethod();
Method targetMethod = targetPd.getReadMethod();
Object sourceValue = null;
Object targetValue = null;
if (sourceMethod != null) {
sourceValue = sourceMethod.invoke(source, new Object[0]);
}
if (targetMethod != null) {
targetValue = targetMethod.invoke(this, new Object[0]);
}
if (targetValue == null && sourceValue == null) {
continue;
}
if (targetValue == null && sourceValue != null) {
minus++;
continue;
}
if (targetValue != null && sourceValue == null) {
plus++;
continue;
}
if (!targetValue.equals(sourceValue)) {
plus++;
minus++;
}
}
catch (Throwable ex) {
FatalBeanException errorVar = new FatalBeanException("DisplayNode: Could not compare properties from source", ex);
ErrorMgr.addError(errorVar);
throw errorVar;
}
}
}
if ((minus == 0) && (plus == 0)){
return 0;
} else if (Math.abs(minus) > Math.abs(plus)){
return -1;
} else
return 1;
}
}