package net.xoetrope.swing;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JTree;
import javax.swing.TransferHandler;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import net.xoetrope.swing.dnd.XTransferHandlerFactory;
import net.xoetrope.swing.tree.XTreeModelAdapter;
import net.xoetrope.xui.XAttributedComponent;
import net.xoetrope.xui.XProjectManager;
import net.xoetrope.xui.data.XModel;
import net.xoetrope.xui.events.XHandlerInvoker;
import net.xoetrope.xui.events.XListenerHelper;
import net.xoetrope.xui.style.XStyleComponent;
/**
* @todo add listeners
* @todo add data bindings
* @todo add simple path/node selection interfaces
*/
/**
* A tree control, wraps JTree
* <p>Copyright (c) Xoetrope Ltd., 1998-2004<br>
* License: see license.txt
* $Revision: 2.4 $
*/
public class XTree extends JTree implements XAttributedComponent, TreeSelectionListener, XListenerHelper, XStyleComponent, MouseListener
{
private XHandlerInvoker invoker;
private XModel selectedNode, lastSelectedNode;
private String styleName;
/**
* Create a new JTree component
*/
public XTree()
{
setScrollsOnExpand( true );
addTreeSelectionListener( this );
addMouseListener( this );
}
/**
* Set one or more attributes of the component.
* @param attribName the name of the attribute
* @param attribValue the value of the attribute
* @return 0 for success, non zero for failure or to require some further action
*/
public int setAttribute( String attribName, Object attribValue )
{
String attribNameLwr = attribName.toLowerCase();
String attribValueStr = (String)attribValue;
String attribValueLwr = null;
if ( attribValue != null )
attribValueLwr = attribValueStr.toLowerCase();
if ( attribNameLwr.equals( "tooltip" ))
setToolTipText( attribValueStr );
else if ( attribNameLwr.equals( "dragenabled" )) {
boolean state = "true".equals( attribValueLwr );
setDragEnabled( state );
}
else
return -1;
return 0;
}
/**
* Called whenever the value of the selection changes.
* @param e the event that characterizes the change.
*/
public void valueChanged(TreeSelectionEvent e)
{
invokeSelection();
}
private void invokeSelection()
{
getSelectedNode();
if (( selectedNode != null ) && ( invoker != null ))
invoker.invoke();
}
/**
* Get the selected model node.
* @return the selection
*/
public XModel getSelectedNode()
{
TreePath tp = getSelectionPath();
if ( tp != null ) {
Object path[] = tp.getPath();
if (( path != null ) && ( path.length > 0 )) {
Object obj = path[ path.length - 1 ];
if ( obj instanceof XTreeModelAdapter ) {
XTreeModelAdapter ta = (XTreeModelAdapter)path[ path.length - 1 ];
selectedNode = ta.getModel();
}
}
else
selectedNode = null;
return selectedNode;
}
return null;
}
/**
* Add an event handler response method to a component such that the page's
* response method is invoked when the event occurs
* @param page the page containing the method
* @param handlerType the type of event handler
* @param methodName the method to invoke
* @throws NoSuchMethodException Couldn't set the handler
*/
public void addHandler( Object page, String handlerType, String methodName ) throws NoSuchMethodException
{
invoker = new XHandlerInvoker( page, this, methodName );
}
/**
* Set the tree style name
* @param style the style name
*/
public void setStyle( String style )
{
styleName = style;
}
/**
* Get the style name
* @return the style name
*/
public String getStyleName()
{
return styleName;
}
/**
* A mouse event
* @param me the mouse event
*/
public void mouseEntered( MouseEvent me ){}
/**
* A mouse event
* @param me the mouse event
*/
public void mouseExited( MouseEvent me ){}
/**
* Allow a mouse event to trigger the event if it is the same as the last
* selection. In other cases the tree selection listener will handle the event,
* changes the selection
* @param me teh mouse event
*/
public void mouseClicked( MouseEvent me )
{
if ( lastSelectedNode == getSelectedNode())
invokeSelection();
lastSelectedNode = selectedNode;
}
/**
* A mouse event
* @param me the mouse event
*/
public void mouseReleased( MouseEvent me ){}
/**
* A mouse event
* @param me the mouse event
*/
public void mousePressed( MouseEvent me ){}
/**
* Setup drag and drop support
* @param state true to enable drag and drop support
*/
public void setDragEnabled( boolean state )
{
if ( state ) {
XTransferHandlerFactory thf = XTransferHandlerFactory.getInstance( XProjectManager.getCurrentProject());
if ( thf != null ) {
TransferHandler th = thf.getTransferHandler( this, null );
if ( th != null ) {
super.setDragEnabled( true );
setTransferHandler( th );
// addMouseListener( this );
// addMouseMotionListener( this );
return;
}
}
}
super.setDragEnabled( false );
}
/**
* Find the nearest tree path in the current tree model. The method is
* designed for use when a tree is refreshed with a new or updated model that
* references the same objects. The tree nodes and tree paths will be
* different in then revised model and therefore the tree methods that rely
* on tree paths saved prior to the update will fail to find the equivalent
* objects. This method descends the tree attempting to find the new paths
* that reference the same (equals) objects. The search will stop if when
* the first element in the path is not found.
* @param tp the old path
* @return the new path or tp if none was found, or if the path is unchanged.
*/
public TreePath findNearestPath( TreePath tp )
{
if ( tp == null )
return null;
Object[] elements = tp.getPath();
Object[] nearestPaths = new Object[ elements.length ];
TreeModel tm = getModel();
Object root = tm.getRoot();
if ( root == null )
return null;
int idx = 0;
nearestPaths[ idx++ ] = root;
Object p = root;
for ( int i = 1; i < elements.length; i++ ) {
if ( elements[ i ] instanceof XTreeModelAdapter ) {
XTreeModelAdapter tma = (XTreeModelAdapter)elements[ i ];
XModel model = tma.getModel();
int numChildren = tm.getChildCount( p );
for ( int j = 0; j < numChildren; j++ ) {
Object childObj = tm.getChild( p, j );
if ( childObj instanceof XTreeModelAdapter ) {
XTreeModelAdapter childTma = (XTreeModelAdapter)childObj;
XModel childModel = childTma.getModel();
if ( childModel.getId().equals( model.getId() )) {
nearestPaths[ idx++ ] = childObj;
p = childObj;
break;
}
}
}
}
}
if ( idx > 0 ) {
Object[] paths = new Object[ idx ];
System.arraycopy( nearestPaths, 0, paths, 0, idx );
TreePath path = new TreePath( paths );
setSelectionPath( path );
scrollPathToVisible( path );
return path;
}
return tp;
}
}