FileDocCategorySizeDatePackage
TreeNode.javaAPI DocJMF 2.1.1e24051Mon May 12 12:20:32 BST 2003jmapps.ui

TreeNode.java

/*
 * @(#)TreeNode.java	1.4 02/08/21
 *
 * Copyright (c) 1996-2002 Sun Microsystems, Inc.  All rights reserved.
 */

package jmapps.ui;

import java.util.*;
import java.awt.*;
import java.awt.event.*;


public class TreeNode extends Vector {

    public static final int     MARGIN_HORZ = 6;
    public static final int     MARGIN_VERT = 2;
    public static final int     BOXSIZE = 8;

    public static final String  ACTION_NODE_ADDED = "Node Added";
    public static final String  ACTION_NODE_REMOVED = "Node Removed";
    public static final String  ACTION_NODE_EXPANDED = "Node Expanded";
    public static final String  ACTION_NODE_COLLAPSED = "Node Collapsed";
    public static final String  ACTION_NODE_SETCURRENT = "Node Set Current";
    public static final String  ACTION_NODE_RESETCURRENT = "Node Reset Current";

    public static final Color   colorBg = Color.lightGray;
    public static final Color   colorCurBg = new Color ( 0, 0, 128 );
    public static final Color   colorFg = Color.black;
    public static final Color   colorCurFg = Color.yellow;
    public static final Color   colorLines = Color.darkGray;

    private Component           componentOwner;
    private Vector              vectorActionListeners = new Vector ();

    private boolean     boolExpanded = false;
    private boolean     boolCurrent = false;
    private boolean     boolVisible = true;
    private Image       imageTreeElement = null;
    private Image       imageTreeElementCur = null;
    private int         nWidthImage = 0;
    private int         nHeightImage = 0;
    private Rectangle   rectElement = null;
    private Rectangle   rectElementFull = null;

    private String      strName;
    private Object      objUserData = null;
    private TreeNode    nodeOwner;


    public TreeNode ( String stringName, Component clWindowOwner ) {
        super ();

        this.strName = new String ( stringName );
        this.componentOwner = clWindowOwner;
        rectElement = new Rectangle ( 0, 0, 0, 0 );
        rectElementFull = new Rectangle ( 0, 0, 0, 0 );

        setImage ( ImageArea.loadImage ( "treeNode.gif", clWindowOwner, true ) );
        setImageCur ( ImageArea.loadImage ( "treeNodeCur.gif", clWindowOwner, true ) );
	}

    public void addActionListener ( ActionListener listener ) {
        if ( listener == null )
            return;
        vectorActionListeners.addElement ( listener );
    }

    public void rewmoveActionListener ( ActionListener listener ) {
        if ( listener == null )
            return;
        vectorActionListeners.removeElement ( listener );
    }

    public String getName () {
        return ( strName );
    }

    public String getFullPathName () {
        String  stringName;

        if ( nodeOwner != null )
            stringName = nodeOwner.getFullPathName ();
        else
            stringName = new String ();
        stringName += "/" + strName;
        return ( stringName );
    }

    public TreeNode getOwner () {
        return ( nodeOwner );
    }

    public TreeNode getRoot () {
        TreeNode    nodeRoot;

        nodeRoot = this;
        while ( nodeRoot.getOwner() != null )
            nodeRoot = nodeRoot.getOwner ();
        return ( nodeRoot );
    }

    public Object getUserData () {
        return ( objUserData );
    }

    public TreeNode getSubElement ( int nIndex ) {
        int         nSize;
        TreeNode    node;

        nSize = size ();
        if ( nIndex >= nSize  ||  nIndex < 0 )
            return ( null );
        node = (TreeNode) elementAt ( nIndex );
        return ( node );
    }

    public TreeNode getSubElement ( String stringName ) {
        int         i;
        int         nSize;
        TreeNode    node;
        String      stringElementName = null;


        nSize = size ();
        for ( i = 0;  i < nSize;  i++ ) {
            node = (TreeNode) elementAt ( i );
            stringElementName = node.getName ();
            if ( stringElementName.equals (stringName) )
                return ( node );
        }
        return ( null );
    }

    public boolean isSubElement ( String stringName ) {
        int         i;
        int         nSize;
        TreeNode    node;
        String      stringElementName = null;


        nSize = size ();
        for ( i = 0;  i < nSize;  i++ ) {
            node = (TreeNode) elementAt ( i );
            stringElementName = node.getName ();
            if ( stringElementName.equals(stringName) )
                return ( true );
        }
        return ( false );
    }

    public TreeNode findElement ( String stringFullPath ) {
        int         i;
        int         nSize;
        TreeNode    node = null;


        if ( stringFullPath.equals("/" + strName) )
            return ( this );
        if ( !(stringFullPath.startsWith("/"+strName)) )
            return ( null );

        stringFullPath = stringFullPath.substring ( strName.length() + 1 );
        nSize = size ();
        for ( i = 0;  i < nSize  &&  node == null;  i++ ) {
            node = (TreeNode) elementAt ( i );
            node = node.findElement ( stringFullPath );
        }
        return ( node );
    }

    public TreeNode addSubElement ( TreeNode node ) {
        ActionEvent     event;

        if ( node.nodeOwner != null )
            node.nodeOwner.removeSubElement ( node );
        addElement ( node );
        node.nodeOwner = this;

        event = new ActionEvent ( this, ActionEvent.ACTION_PERFORMED, ACTION_NODE_ADDED );
        fireActionEvent ( event );

        return ( node );
    }

    public TreeNode addSubElement ( String stringName ) {
        TreeNode    node;

        node = new TreeNode ( stringName, componentOwner );
        addSubElement ( node );
        return ( node );
    }

    public TreeNode insertSubElement ( int nIndex, TreeNode node ) {
        int     nSize;

        if ( node.nodeOwner != null )
            node.nodeOwner.removeSubElement ( node );

        nSize = size ();
        if ( nIndex < 0 )
            nIndex = 0;
        if ( nIndex > nSize )
            nIndex = nSize;
        insertElementAt ( node, nIndex );
        node.nodeOwner = this;
        return ( node );
    }

    public TreeNode insertSubElement ( int nIndex, String stringName ) {
        TreeNode    node;

        node = new TreeNode ( stringName, componentOwner );
        insertSubElement ( nIndex, node );
        return ( node );
    }

    public void removeSubElement ( TreeNode node ) {
        boolean         boolResult;
        ActionEvent     event;

        boolResult = removeElement ( node );
        if ( boolResult == true ) {
            node.nodeOwner = null;
            event = new ActionEvent ( this, ActionEvent.ACTION_PERFORMED, ACTION_NODE_REMOVED );
            fireActionEvent ( event );
        }
    }

    public void destroySubElement ( TreeNode node ) {
        node.destroyAllSubElements ();
        node.nodeOwner = null;
        removeSubElement ( node );
    }

    public void destroySubElement ( int nIndex ) {
        int         nSize;
        TreeNode    node;

        nSize = size ();
        if ( nIndex >= nSize  ||  nIndex < 0 )
            return;
        node = (TreeNode) elementAt ( nIndex );
        destroySubElement ( node );
    }

    public void destroyAllSubElements () {
        int         nSize;
        TreeNode    node;

        nSize = size ();
        while ( nSize > 0 ) {
            nSize--;
            destroySubElement ( nSize );
        }
    }

    public void sortSubElements () {
        int         i, j;
        int         nSize;
        TreeNode    node1;
        TreeNode    node2;
        String      stringName1;
        String      stringName2;
        int         nResult;


        nSize = size ();
        for ( i = 0;  i < nSize;  i++ ) {
            node1 = (TreeNode) elementAt ( i );
            stringName1 = node1.getName ();
            removeElementAt ( i );
            for ( j = 0;  j < i;  j++ ) {
                node2 = (TreeNode) elementAt ( j );
                stringName2 = node2.getName ();
                nResult = stringName1.compareTo ( stringName2 );
                if ( nResult < 0 )
                    break;
            }
            insertElementAt ( node1, j );
            node1.sortSubElements ();
        }
    }

    public int getImmediateSubElementsCount () {
        int	nSize;

        nSize = size ();
        return ( nSize );
    }

    public int getRecursiveSubElementsCount () {
        int     i;
        int     nCount;
        int     nSize;
        Object  object;

        nSize = size ();
        nCount = nSize;
        for ( i = 0;  i < nSize;  i++ ) {
            object = elementAt ( i );
            if ( object != null  &&  object instanceof TreeNode )
                nCount += ((TreeNode)object).getRecursiveSubElementsCount ();
        }
        return ( nCount );
    }

    public int getExpandedSubElementsCount () {
        int     i;
        int     nCount;
        int     nSize;
        Object  object;

        nSize = size ();
        nCount = nSize;
        for ( i = 0;  i < nSize;  i++ ) {
            object = elementAt ( i );
            if ( object != null  &&  object instanceof TreeNode  &&  ((TreeNode)object).isExpanded() )
                nCount += ((TreeNode)object).getExpandedSubElementsCount ();
        }
        return ( nCount );
    }

    public boolean isRecursiveSubElement ( Object object ) {
        boolean     boolResult = false;
        int         i;
        int         nSize;
        Object      objectSub;

        if ( object == this )
            return ( true );
        nSize = size ();
        for ( i = 0;  i < nSize  &&  boolResult == false;  i++ ) {
            objectSub = elementAt ( i );
            if ( object == objectSub )
                return ( true );
            if ( objectSub != null  &&  objectSub instanceof TreeNode )
                boolResult = ((TreeNode)objectSub).isRecursiveSubElement ( object );
        }
        return ( boolResult );
	}

    public void setUserData ( Object objectData ) {
        this.objUserData = objectData;
    }

    public boolean isVisible () {
        return ( boolVisible );
    }

    public void setVisible ( boolean boolVisible ) {
        if ( this.boolVisible == boolVisible )
			return;

        this.boolVisible = boolVisible;
        if ( boolVisible == false  &&  boolExpanded == false )
            switchExpanded ();
    }

    public boolean isExpanded () {
        return ( boolExpanded );
    }

    public void setExpanded ( boolean boolExpanded ) {
        if ( this.boolExpanded != boolExpanded )
            switchExpanded ();
    }

    public boolean switchExpanded () {
        ActionEvent     event;

        if ( boolExpanded == true ) {
            boolExpanded = false;
            event = new ActionEvent ( this, ActionEvent.ACTION_PERFORMED, ACTION_NODE_COLLAPSED );
        }
        else {
            boolExpanded = true;
            event = new ActionEvent ( this, ActionEvent.ACTION_PERFORMED, ACTION_NODE_EXPANDED );
        }
        fireActionEvent ( event );
        return ( boolExpanded );
    }

    public void setCurrent ( boolean boolCurrent ) {
        TreeNode	node;
        ActionEvent event;

        if ( this.boolCurrent == boolCurrent )
            return;

        if ( boolCurrent == true ) {
            node = (TreeNode) getRoot ();
            node = node.getCurrent ();
            if ( node != null )
                node.setCurrent ( false );

            node = (TreeNode) nodeOwner;
            while ( node != null  &&  node instanceof TreeNode ) {
                if ( node.isExpanded() == false )
                    node.setExpanded ( true );
                node = (TreeNode) node.nodeOwner;
            }
            event = new ActionEvent ( this, ActionEvent.ACTION_PERFORMED, ACTION_NODE_SETCURRENT );
        }
        else {
            event = new ActionEvent ( this, ActionEvent.ACTION_PERFORMED, ACTION_NODE_RESETCURRENT );
        }

        this.boolCurrent = boolCurrent;
        computeImageSize ();
        fireActionEvent ( event );
    }

    public TreeNode getCurrent () {
        int         i;
        int         nSize;
        TreeNode    node;
        TreeNode    nodeCurrent = null;

        if ( boolCurrent == true )
            return ( this );
        nSize = getImmediateSubElementsCount ();
        for ( i = 0;  i < nSize  &&  nodeCurrent == null;  i++ ) {
            node = (TreeNode) getSubElement ( i );
            nodeCurrent = node.getCurrent ();
        }
        return ( nodeCurrent );
    }

    public void setImage ( Image imageNew ) {
        imageTreeElement = imageNew;
        computeImageSize ();
    }

    public void setImageCur ( Image imageNew ) {
        imageTreeElementCur = imageNew;
        computeImageSize ();
    }

    public Rectangle getNodeBounds () {
        return ( new Rectangle(rectElement) );
    }

    public Rectangle getNodeBoundsFull () {
        return ( new Rectangle(rectElementFull) );
    }

    public void onMouseDoubleclick ( Point pointMouse ) {
        int         i;
        int         nSize;
        TreeNode    node;

        nSize = getImmediateSubElementsCount ();
        if ( !(rectElementFull.contains (pointMouse)) )
            return;
        if ( rectElement.contains (pointMouse)
                    &&  pointMouse.x >= rectElement.x + 2 * MARGIN_HORZ + BOXSIZE
                    &&  nSize > 0 ) {
            switchExpanded ();
            return;
        }
        if ( boolExpanded == false )
            return;

        for ( i = 0;  i < nSize;  i++ ) {
            node = (TreeNode) getSubElement ( i );
            node.onMouseDoubleclick ( pointMouse );
        }
    }

    public void onMousePressedLeft ( Point pointMouse, boolean boolShift, boolean boolCtrl, boolean boolAlt ) {
        int         i;
        int         nSize;
        TreeNode    node;

        nSize = getImmediateSubElementsCount ();
        if ( !(rectElementFull.contains (pointMouse)) )
            return;
        if ( rectElement.contains (pointMouse) ) {
            if ( pointMouse.x >= rectElement.x + 2 * MARGIN_HORZ + BOXSIZE )
                setCurrent ( true );
            else if ( nSize > 0 )
                switchExpanded ();
            return;
        }
        if ( boolExpanded == false )
            return;

        for ( i = 0;  i < nSize;  i++ ) {
            node = (TreeNode) getSubElement ( i );
            node.onMousePressedLeft ( pointMouse, boolShift, boolCtrl, boolAlt );
        }
    }

    public void onMousePressedRight ( Point pointMouse, boolean boolShift, boolean boolCtrl, boolean boolAlt ) {
    }

    public void drawElement ( Graphics graphics, int nOffsetX, int nOffsetY ) {
        int             nX;
        int             nY;
        int             nWidth;
        int             nHeight;
        int             nSize;
        Rectangle       rect;
        Rectangle       rectLastSubElement;
        FontMetrics     fontMetrics;
        TreeNode        node;
        Image           imageDraw;



        nSize = getImmediateSubElementsCount ();
        rect = new Rectangle ( rectElement );
        rect.x -= nOffsetX;
        rect.y -= nOffsetY;
        fontMetrics = graphics.getFontMetrics ();

        graphics.setColor ( colorLines );

        if ( nSize > 0 ) {
            nX = rect.x + MARGIN_HORZ;
            nY = rect.y + (rect.height - BOXSIZE) / 2;
            nWidth = BOXSIZE;
            nHeight = BOXSIZE;
            graphics.setColor ( colorBg );
            graphics.fillRect ( nX, nY, nWidth, nHeight );
            graphics.setColor ( colorLines );
            graphics.drawRect ( nX, nY, nWidth, nHeight );

            nX = rect.x + MARGIN_HORZ + BOXSIZE;
            nY = rect.y + rect.height / 2;
            drawDottedLine ( graphics, nX, nY, nX + MARGIN_HORZ, nY );
        }
        else {
            nX = rect.x + MARGIN_HORZ + BOXSIZE / 2;
            nY = rect.y + rect.height / 2;
            drawDottedLine ( graphics, nX, nY, nX + MARGIN_HORZ, nY );
        }

        if ( nSize > 0  &&  boolExpanded == true ) {
            node = (TreeNode) getSubElement ( nSize - 1 );
            rectLastSubElement = node.getNodeBounds ();

            nX = rect.x + 2 * MARGIN_HORZ + BOXSIZE + nWidthImage / 2;
            nY = rect.y + (rect.height - nHeightImage) / 2 + nHeightImage + 1;
            nHeight = rectLastSubElement.y - nOffsetY - nY + rectLastSubElement.height / 2;
            drawDottedLine ( graphics, nX, nY, nX, nY + nHeight );
        }

        graphics.setColor ( colorFg );

        if ( nSize > 0 ) {
            nX = rect.x + MARGIN_HORZ + 2;
            nY = rect.y + rect.height / 2;
            nWidth = BOXSIZE - 2 * 2;
            graphics.drawLine ( nX, nY, nX + nWidth, nY );
            if ( boolExpanded == false ) {
                nX = rect.x + MARGIN_HORZ + BOXSIZE / 2;
                nY = rect.y + (rect.height - BOXSIZE) / 2 + 2;
                nHeight = BOXSIZE - 2 * 2;
                graphics.drawLine ( nX, nY, nX, nY + nHeight );
            }
        }

        nX = rect.x + 2 * MARGIN_HORZ + BOXSIZE;
        nY = rect.y + (rect.height - nHeightImage) / 2;
		
        if ( boolCurrent == true )
            imageDraw = imageTreeElementCur;
        else
            imageDraw = imageTreeElement;

        if ( imageDraw != null )
            graphics.drawImage ( imageDraw, nX, nY, colorBg, componentOwner );

        if ( nWidthImage < 0  ||  nHeightImage < 0 ) {
            nWidthImage = imageDraw.getWidth ( componentOwner );
            nHeightImage = imageDraw.getHeight ( componentOwner );
        }

        nX = rect.x + 3 * MARGIN_HORZ + BOXSIZE + nWidthImage - MARGIN_HORZ / 2;
        nY = rect.y + 2;
        nWidth = fontMetrics.stringWidth ( strName ) + MARGIN_HORZ;
        nHeight = rect.height - 3;
        if ( boolCurrent == true ) {
            graphics.setColor ( colorCurBg );
            graphics.fillRect ( nX, nY, nWidth, nHeight );
            graphics.setColor ( colorCurFg );
        }
        else {
            graphics.setColor ( colorFg );
        }

        if ( boolCurrent == true  &&  hasOwnerFocus() )
            drawDottedRectangle ( graphics, nX, nY, nWidth, nHeight );

        nHeight = fontMetrics.getAscent ();
        nX = rect.x + 3 * MARGIN_HORZ + BOXSIZE + nWidthImage;
        nY = rect.y + (rect.height - nHeight) / 2 + nHeight;
        graphics.drawString ( strName, nX, nY );
    }

    public void drawDottedRectangle ( Graphics graphics, int nX, int nY, int nWidth, int nHeight ) {
        drawDottedLine ( graphics, nX, nY, nX + nWidth - 1, nY );
        drawDottedLine ( graphics, nX + nWidth - 1, nY, nX + nWidth - 1, nY + nHeight - 1 );
        drawDottedLine ( graphics, nX + nWidth - 1, nY + nHeight - 1, nX, nY + nHeight - 1 );
        drawDottedLine ( graphics, nX, nY + nHeight - 1, nX, nY );
    }

    public void drawDottedLine ( Graphics graphics, int nX1, int nY1, int nX2, int nY2 ) {
        int     nX, nY;
        double  dDiv;

        if ( nX1 == nX2  &&  nY1 == nY2 ) {
            drawDot ( graphics, nX1, nY1 );
            return;
        }

        if ( nX1 > nX2 ) {
            nX = nX1;
            nX1 = nX2;
            nX2 = nX;
        }
        if ( nY1 > nY2 ) {
            nY = nY1;
            nY1 = nY2;
            nY2 = nY;
        }

        if ( nX2 - nX1 > nY2 - nY1 ) {
            dDiv = (double)(nY2 - nY1) / (nX2 - nX1);
            for ( nX = nX1;   nX <= nX2;   nX++ ) {
                nY = (int)Math.rint ( nY1 + (nX - nX1) * dDiv );
                drawDot ( graphics, nX, nY );
            }
        }
        else {
            dDiv = (nX2 - nX1) / (nY2 - nY1);
            for ( nY = nY1;   nY <= nY2;   nY++ ) {
                nX = (int)Math.rint ( nX1 + (nY - nY1) * dDiv );
                drawDot ( graphics, nX, nY );
            }
        }
    }

    public void drawDot ( Graphics graphics, int nX, int nY ) {
        if ( (nX + nY) % 2 == 0 )
            graphics.drawLine ( nX, nY, nX, nY );
    }


    public void computeImageSize () {
        if ( boolCurrent == false  &&  imageTreeElement != null ) {
            nWidthImage = imageTreeElement.getWidth ( componentOwner );
            nHeightImage = imageTreeElement.getHeight ( componentOwner );
        }
        else if ( boolCurrent == true  &&  imageTreeElementCur != null ) {
            nWidthImage = imageTreeElementCur.getWidth ( componentOwner );
            nHeightImage = imageTreeElementCur.getHeight ( componentOwner );
        }
        else {
            nWidthImage = 0;
            nHeightImage = 0;
        }
    }

    public Rectangle recomputeLayout ( Rectangle rect, FontMetrics fontMetrics ) {
        int         i;
        int         nSize;
        int         nOffsetX;
        int         nOffsetY;
        int         nHeight;
        TreeNode    node;
        Rectangle   rectSubElement;


        computeImageSize ();

        rectElement.x = rect.x;
        rectElement.y = rect.y;

        if ( boolVisible == true ) {
            rectElement.height = Math.max ( fontMetrics.getHeight(), BOXSIZE + 2 * MARGIN_VERT );
            rectElement.height = Math.max ( rectElement.height, nHeightImage + 2 * MARGIN_VERT );
            rectElement.width = BOXSIZE + nWidthImage + fontMetrics.stringWidth(strName) + 4 * MARGIN_HORZ;
        }
        else {
            rectElement.height = 0;
            rectElement.width = 0;
        }
        rectElementFull.setBounds ( rectElement );

        if ( boolExpanded == true ) {
            if ( boolVisible == true )
                nOffsetX = BOXSIZE / 2 + MARGIN_HORZ + nWidthImage / 2;
            else
                nOffsetX = 0;
            nOffsetY = rectElement.height;

            rectSubElement = new Rectangle ( rect );
            rectSubElement.x += nOffsetX;
            rectSubElement.y += nOffsetY;

            nSize = getImmediateSubElementsCount ();
            for ( i = 0;  i < nSize;  i++ ) {
                node = (TreeNode) getSubElement ( i );
                rectSubElement = node.recomputeLayout ( rectSubElement, fontMetrics );
                rectElementFull.height += rectSubElement.height;
                rectSubElement.y += rectSubElement.height;
                if ( rectElementFull.width < nOffsetX + rectSubElement.width )
                    rectElementFull.width = nOffsetX + rectSubElement.width;
            }
        }
        return ( new Rectangle(rectElementFull) );
    }

    public void addToTreeElList ( Vector cw_vectorTreeElList ) {
        int         i;
        int         nSize;
        TreeNode    node;

        if ( boolVisible == true )
            cw_vectorTreeElList.addElement ( this );

        if ( boolExpanded == false )
            return;

        nSize = getImmediateSubElementsCount ();
        for ( i = 0;  i < nSize;  i++ ) {
            node = (TreeNode) getSubElement ( i );
            node.addToTreeElList ( cw_vectorTreeElList );
        }
    }

    private boolean hasOwnerFocus () {
        boolean     boolResult = false;
        Component   component;
        Window      window;

        if ( componentOwner == null )
            return ( false );

        component = componentOwner;
        while ( component != null ) {
            if ( component instanceof Window ) {
                boolResult = (((Window)component).getFocusOwner() == componentOwner);
                break;
            }
            component = component.getParent ();
        }
        return ( boolResult );
    }

    private void fireActionEvent ( ActionEvent event ) {
        int             i;
        int             nCount;
        ActionListener  listener;

        nCount = vectorActionListeners.size ();
        for ( i = 0;  i < nCount;  i++ ) {
            listener = (ActionListener) vectorActionListeners.elementAt ( i );
            listener.actionPerformed ( event );
        }
    }

}