home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / jfc.bin / VisibleTreeNode.java < prev    next >
Text File  |  1998-02-26  |  17KB  |  602 lines

  1. /*
  2.  * @(#)VisibleTreeNode.java    1.5 98/02/02
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20.  
  21. package com.sun.java.swing.plaf.basic;
  22.  
  23. import com.sun.java.swing.tree.TreeModel;
  24. import com.sun.java.swing.tree.DefaultMutableTreeNode;
  25. import com.sun.java.swing.tree.MutableTreeNode;
  26. import com.sun.java.swing.tree.TreePath;
  27. import com.sun.java.swing.tree.TreeSelectionModel;
  28. import java.awt.Dimension;
  29. import java.awt.Rectangle;
  30. import java.util.Enumeration;
  31. import java.util.Vector;
  32.  
  33. /**
  34.  * VisibleTreeNode is used by AbstractTreeUI to keep track of each of
  35.  * the nodes that have been expanded. This will also cache the preferred
  36.  * size of the value this represents.<p>
  37.  * <p>
  38.  * Warning: serialized objects of this class will not be compatible with
  39.  * future swing releases.  The current serialization support is appropriate
  40.  * for short term storage or RMI between Swing1.0 applications.  It will
  41.  * not be possible to load serialized Swing1.0 objects with future releases
  42.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  43.  * baseline for the serialized form of Swing objects.
  44.  *
  45.  * @version 1.5 02/02/98
  46.  * @author Scott Violet
  47.  */
  48. public class VisibleTreeNode extends DefaultMutableTreeNode {
  49.     /** AbstractTreeUI this was created for. */
  50.     protected AbstractTreeUI  treeUI;
  51.     /** Preferred size needed to draw the user object. */
  52.     protected Dimension       preferredSize;
  53.     /** Y location that the user object will be drawn at. */
  54.     protected int             yOrigin;
  55.     /** Is this node currently expanded? */
  56.     protected boolean         expanded;
  57.     /** Has this node been expanded at least once? */
  58.     protected boolean         hasBeenExpanded;
  59.     /** Is this node in a tree? */
  60.     protected boolean         isValid;
  61.  
  62.     public static final Dimension EMPTY_SIZE = new Dimension(0, 0);
  63.  
  64.     public VisibleTreeNode(AbstractTreeUI treeUI, Object value, int index) {
  65.     super(value);
  66.     this.treeUI = treeUI;
  67.     isValid = true;
  68.     updatePreferredSize(index);
  69.     }
  70.  
  71.     /**
  72.      * Passes this message on to super and if the newParent is
  73.      * null, meaning we're no longer in the tree. markInvalid
  74.      * is messaged.
  75.      */
  76.     public void setParent(MutableTreeNode newParent) {
  77.     super.setParent(newParent);
  78.     if(newParent == null)
  79.         markInvalid();
  80.     }
  81.  
  82.     /**
  83.      * Marks this node and all its children as invalid.
  84.      */
  85.     void markInvalid() {
  86.     isValid = false;
  87.     if(children != null) {
  88.         for(int counter = children.size() - 1; counter >= 0;
  89.         counter--)
  90.         ((VisibleTreeNode)children.elementAt(counter))
  91.             .markInvalid();
  92.     }
  93.     }
  94.  
  95.     /**
  96.      * Sets y origin the user object will be drawn at to
  97.      * <I>newYOrigin</I>.
  98.      */
  99.     protected void setYOrigin(int newYOrigin) {
  100.     yOrigin = newYOrigin;
  101.     }
  102.  
  103.     /**
  104.      * Returns the y origin the user object will be drawn at.
  105.      */
  106.     public int getYOrigin() {
  107.     if(treeUI.isFixedRowHeight()) {
  108.         int      aRow = getRow();
  109.  
  110.         if(aRow == -1)
  111.         return -1;
  112.         return treeUI.getRowHeight() * aRow;
  113.     }
  114.     return yOrigin;
  115.     }
  116.  
  117.     /**
  118.      * Shifts the y origin by <code>offset</code>.
  119.      */
  120.     protected void shiftYOriginBy(int offset) {
  121.     yOrigin += offset;
  122.     }
  123.  
  124.     public void updatePreferredSize() {
  125.     updatePreferredSize(-1);
  126.     }
  127.  
  128.     /**
  129.      * Updates the preferred size by asking the current renderer
  130.      * for the Dimension needed to draw the user object this
  131.      * instance represents.
  132.      */
  133.     protected void updatePreferredSize(int index) {
  134.     preferredSize = treeUI.getSizeOfNode(this, index);
  135.     if(preferredSize == null) {
  136.         preferredSize = EMPTY_SIZE;
  137.         treeUI.updateNodeSizes = true;
  138.     }
  139.     else if(preferredSize.height == 0) {
  140.         treeUI.updateNodeSizes = true;
  141.     }
  142.  
  143.     int            rh = treeUI.getRowHeight();
  144.  
  145.     if(rh > 0)
  146.         preferredSize.height = rh;
  147.     }
  148.  
  149.     /**
  150.      * Returns true if this node has a valid size.
  151.      */
  152.     public boolean hasValidSize() {
  153.     return (preferredSize == null || preferredSize.height == 0);
  154.     }
  155.  
  156.     /**
  157.      * Returns the preferred size needed to draw the value this
  158.      * node represents.  Will always return a valid dimenion, but
  159.      * dimension may be 0, 0
  160.      */
  161.     public Dimension getPreferredSize() {
  162.     return preferredSize;
  163.     }
  164.  
  165.     /**
  166.      * Returns the location and size of this node.
  167.      */
  168.     public Rectangle getNodeBounds() {
  169.     Dimension                    pSize = getPreferredSize();
  170.  
  171.     return new Rectangle(treeUI.getXOriginOfNode(this), getYOrigin(),
  172.                  pSize.width, pSize.height);
  173.     }
  174.  
  175.     /**
  176.      * The highest visible nodes have a depth of 0. 
  177.      */
  178.     public int getVisibleLevel()  { 
  179.     if (treeUI.isRootVisible()) {
  180.         return getLevel();
  181.     } else {
  182.         return getLevel()-1;
  183.     }
  184.     }
  185.  
  186.     /**
  187.      * Returns the row of the receiver.
  188.      */
  189.     public int getRow() {
  190.     return treeUI.visibleNodes.indexOf(this);
  191.     }
  192.  
  193.     /**
  194.      * Returns true if this node has been expanded at least once.
  195.      */
  196.     public boolean hasBeenExpanded() {
  197.     return hasBeenExpanded;
  198.     }
  199.  
  200.     /** 
  201.      * Returns true if the receiver has been expanded.
  202.      */
  203.     public boolean isExpanded() {
  204.     return expanded;
  205.     }
  206.  
  207.     /**
  208.      * Returns true if the receiver is selected.
  209.      */
  210.     public boolean isSelected() {
  211.     return treeUI.isSelectedIndex(getRow());
  212.     }
  213.  
  214.     /**
  215.      * Returns true if the receiver is a leaf.
  216.      */
  217.     public boolean isLeaf() {
  218.     return treeUI.getModel().isLeaf(this.getValue());
  219.     }
  220.  
  221.     /**
  222.      * Returns the last visible node that is a child of this
  223.      * instance.
  224.      */
  225.     public VisibleTreeNode getLastVisibleNode() {
  226.     VisibleTreeNode                node = this;
  227.  
  228.     while(node.isExpanded() && node.getChildCount() > 0)
  229.         node = (VisibleTreeNode)node.getLastChild();
  230.     return node;
  231.     }
  232.  
  233.     /**
  234.      * If createIfNeeded is true the children will be loaded from
  235.      * the model if they haven't already been loaded.  The children
  236.      * are then loaded, regardless of whether or not this
  237.      * node is currently expanded.
  238.      */
  239.     public Enumeration getLoadedChildren(boolean createIfNeeded) {
  240.     if(!createIfNeeded || hasBeenExpanded)
  241.         return super.children();
  242.  
  243.     VisibleTreeNode   newNode;
  244.     Object            realNode = getValue();
  245.     TreeModel         treeModel = treeUI.getModel();
  246.     int               count = treeModel.getChildCount(realNode);
  247.  
  248.     hasBeenExpanded = true;
  249.  
  250.     int    childRow = getRow();
  251.  
  252.     if(childRow == -1) {
  253.         for (int i = 0; i < count; i++) {
  254.         newNode = treeUI.createNodeForValue
  255.             (treeModel.getChild(realNode, i), -1);
  256.         this.add(newNode);
  257.         }
  258.     }
  259.     else {
  260.         childRow++;
  261.         for (int i = 0; i < count; i++) {
  262.         newNode = treeUI.createNodeForValue
  263.             (treeModel.getChild(realNode, i), childRow++);
  264.         this.add(newNode);
  265.         }
  266.     }
  267.     return super.children();
  268.     }
  269.  
  270.     /**
  271.      * Returns the children of the receiver.
  272.      * If the receiver is not currently expanded, this will return an
  273.      * empty enumeration.
  274.      */
  275.     public Enumeration children() {
  276.     if (!this.isExpanded()) {
  277.         return DefaultMutableTreeNode.EMPTY_ENUMERATION;
  278.     } else {
  279.         return super.children();
  280.     }
  281.     }
  282.  
  283.     /**
  284.      * Returns true if the receiver is currently visible.
  285.      */
  286.     public boolean isVisible() {
  287.     return treeUI.visibleNodes.contains(this);
  288.     }
  289.  
  290.     /**
  291.      * Messaged when the child count has changed and this node hasn't
  292.      * yet been expanded. This is meant to be used by subclassers.
  293.      */
  294.     public void modelChildCountChanged() {
  295.     }
  296.  
  297.     /**
  298.      * Returns the number of children this will have. If the children
  299.      * have not yet been loaded, this messages the model.
  300.      */
  301.     public int getModelChildCount() {
  302.     if(hasBeenExpanded)
  303.         return super.getChildCount();
  304.     return treeUI.getModel().getChildCount(getValue());
  305.     }
  306.  
  307.     /**
  308.      * Returns the number of visible children. This is a deep search.
  309.      */
  310.     public int visibleChildCount() {
  311.     /* This instance is included in the preorder enumeration, so
  312.        start with -1. */
  313.     int               childCount = -1;
  314.     Enumeration       cursor = preorderEnumeration();
  315.  
  316.     while(cursor.hasMoreElements()) {
  317.         childCount++;
  318.         cursor.nextElement();
  319.     }
  320.     return childCount;
  321.     }
  322.  
  323.     /**
  324.      * Toggles the receiver between expanded and collapsed.
  325.      */
  326.     public void toggleExpanded() {
  327.     if (isExpanded()) {
  328.         collapse();
  329.     } else {
  330.         expand();
  331.     }
  332.     }
  333.  
  334.     /**
  335.      * Messaged from expand and collapse. This is meant for subclassers
  336.      * that may wish to do something interesting with this.
  337.      */
  338.     protected void didAdjustTree() {
  339.     }
  340.  
  341.     /**
  342.      * Expands the receiver.
  343.      */
  344.     public void expand() {
  345.     expand(true);
  346.     }
  347.  
  348.     /**
  349.      * Expands this node in the tree.  This will load the children
  350.      * from the treeModel if this node has not previously been
  351.      * expanded.  If <I>adjustTree</I> is true the tree and selection
  352.      * are updated accordingly.
  353.      */
  354.     protected void expand(boolean adjustTree) {
  355.     if (!isExpanded() && !isLeaf()) {
  356.         Vector visibleNodes = getVisibleNodes();
  357.         boolean isFixed = treeUI.isFixedRowHeight();
  358.         expanded = true;
  359.  
  360.         int originalRow = getRow();
  361.  
  362.         if (!hasBeenExpanded) {
  363.         VisibleTreeNode   newNode;
  364.         Object realNode = getValue();
  365.         TreeModel     treeModel = treeUI.getModel();
  366.         int count = treeModel.getChildCount(realNode);
  367.         hasBeenExpanded = true;
  368.         if(originalRow == -1) {
  369.             for (int i = 0; i < count; i++) {
  370.             newNode = treeUI.createNodeForValue(treeModel.getChild
  371.                                 (realNode, i), -1);
  372.             this.add(newNode);
  373.             }
  374.         }
  375.         else {
  376.             int offset = originalRow + 1;
  377.             for (int i = 0; i < count; i++) {
  378.             newNode = treeUI.createNodeForValue(treeModel.getChild
  379.                          (realNode, i), offset);
  380.             this.add(newNode);
  381.             }
  382.         }
  383.         }
  384.         
  385.         int i = originalRow;
  386.         Enumeration cursor = preorderEnumeration();
  387.         cursor.nextElement(); // don't add me, I'm already in
  388.  
  389.         int newYOrigin;
  390.  
  391.         if(isFixed)
  392.         newYOrigin = 0;
  393.         else if(this == treeUI.treeCacheRoot && !treeUI.isRootVisible())
  394.         newYOrigin = 0;
  395.         else
  396.         newYOrigin = getYOrigin() + this.getPreferredSize().height;
  397.         VisibleTreeNode   aNode;
  398.         if(!isFixed)
  399.         {
  400.         boolean           updateNodeSizes = treeUI.updateNodeSizes;
  401.  
  402.         while (cursor.hasMoreElements()) {
  403.             aNode = (VisibleTreeNode)cursor.nextElement();
  404.             if(!updateNodeSizes && !aNode.hasValidSize())
  405.             aNode.updatePreferredSize(i + 1);
  406.             aNode.setYOrigin(newYOrigin);
  407.             newYOrigin += aNode.getPreferredSize().height;
  408.             visibleNodes.insertElementAt(aNode, ++i);
  409.         }
  410.         }
  411.         else
  412.         {
  413.         while (cursor.hasMoreElements()) {
  414.             aNode = (VisibleTreeNode)cursor.nextElement();
  415.             visibleNodes.insertElementAt(aNode, ++i);
  416.         }
  417.         }
  418.  
  419.         int   endRow = i;
  420.         
  421.         if(originalRow != endRow && adjustTree)
  422.         {
  423.         /* Adjust the Y origin of any nodes following this row. */
  424.         if(!isFixed && getChildCount() > 0 && ++i < treeUI.getRowCount())
  425.         {
  426.             int              counter;
  427.             int              heightDiff = newYOrigin - 
  428.             (getYOrigin() + this.getPreferredSize().height);
  429.  
  430.             for(counter = visibleNodes.size() - 1;counter >= i;
  431.             counter--)
  432.             ((VisibleTreeNode)visibleNodes.elementAt(counter)).
  433.                 shiftYOriginBy(heightDiff);
  434.         }
  435.         didAdjustTree();
  436.         treeUI.visibleNodesChanged();
  437.         }
  438.  
  439.         treeUI.pathWasExpanded(getTreePath());
  440.  
  441.         TreeSelectionModel treeSelectionModel = treeUI.getSelectionModel();
  442.         /* Update the selection, if the list selection model wants
  443.            to select all the items that were added, then we need
  444.            to update the list selection model. */
  445.         if(treeSelectionModel != null) {
  446.         if(originalRow != -1 && originalRow < endRow &&
  447.            treeSelectionModel.isRowSelected(originalRow) &&
  448.            treeSelectionModel.isRowSelected(originalRow + 1)) {
  449.             TreePath[]               toSelect;
  450.             
  451.             toSelect = new TreePath[endRow - originalRow];
  452.             for(i = endRow; i > originalRow; i--)
  453.             toSelect[endRow - i] = treeUI.getNode(i).getTreePath();
  454.             treeSelectionModel.addSelectionPaths(toSelect);
  455.         }
  456.         else
  457.             treeSelectionModel.resetRowSelection();
  458.         }
  459.     }
  460.     }
  461.  
  462.     /**
  463.      * Collapsed the receiver.
  464.      */
  465.     public void collapse() {
  466.     collapse(true);
  467.     }
  468.  
  469.     /**
  470.      * Collapses this node in the tree.  If <I>adjustTree</I> is
  471.      * true the tree and selection are updated accordingly.
  472.      */
  473.     protected void collapse(boolean adjustTree) {
  474.     if (isExpanded()) {
  475.         Vector      selectedPaths = null;
  476.         Enumeration cursor = preorderEnumeration();
  477.         cursor.nextElement(); // don't remove me, I'm still visible
  478.         int rowsDeleted = 0;
  479.         boolean isFixed = treeUI.isFixedRowHeight();
  480.         int lastYEnd;
  481.         if(isFixed)
  482.         lastYEnd = 0;
  483.         else
  484.         lastYEnd = this.getPreferredSize().height + getYOrigin();
  485.         int startYEnd = lastYEnd;
  486.         int myRow = getRow();
  487.         Vector         visibleNodes = getVisibleNodes();
  488.         TreeSelectionModel       treeSelectionModel;
  489.  
  490.         treeSelectionModel = treeUI.getSelectionModel();
  491.  
  492.         if(!isFixed)
  493.         {
  494.         while(cursor.hasMoreElements()) {
  495.             VisibleTreeNode node = (VisibleTreeNode)cursor.
  496.             nextElement();
  497.             if (visibleNodes.contains(node)) {
  498.             rowsDeleted++;
  499.             if(treeSelectionModel != null &&
  500.                treeSelectionModel.isRowSelected
  501.                (rowsDeleted + myRow)) {
  502.                 if(selectedPaths == null)
  503.                 selectedPaths = new Vector();
  504.                 selectedPaths.addElement(node.getTreePath());
  505.             }
  506.             visibleNodes.removeElement(node);
  507.             lastYEnd = node.getYOrigin() +
  508.                 node.getPreferredSize().height;
  509.             }
  510.         }
  511.         }
  512.         else
  513.         {
  514.         while(cursor.hasMoreElements()) {
  515.             VisibleTreeNode node = (VisibleTreeNode)cursor.
  516.             nextElement();
  517.             if (visibleNodes.contains(node)) {
  518.             rowsDeleted++;
  519.             if(treeSelectionModel != null &&
  520.                treeSelectionModel.isRowSelected
  521.                (rowsDeleted + myRow)) {
  522.                 if(selectedPaths == null)
  523.                 selectedPaths = new Vector();
  524.                 selectedPaths.addElement(node.getTreePath());
  525.             }
  526.             visibleNodes.removeElement(node);
  527.             }
  528.         }
  529.         }
  530.  
  531.         if(rowsDeleted > 0 && adjustTree && myRow != -1)
  532.         {
  533.         /* Adjust the Y origin of any rows following this one. */
  534.         if(!isFixed && (myRow + 1) < treeUI.getRowCount() &&
  535.            startYEnd != lastYEnd)
  536.         {
  537.             int                 counter, maxCounter, shiftAmount;
  538.  
  539.             shiftAmount = startYEnd - lastYEnd;
  540.             for(counter = myRow + 1, maxCounter =
  541.                 visibleNodes.size();
  542.             counter < maxCounter;counter++)
  543.             ((VisibleTreeNode)visibleNodes.elementAt(counter))
  544.                 .shiftYOriginBy(shiftAmount);
  545.         }
  546.         expanded = false;
  547.         didAdjustTree();
  548.         treeUI.visibleNodesChanged();
  549.         }
  550.         else
  551.         expanded = false;
  552.  
  553.         treeUI.pathWasCollapsed(getTreePath());
  554.  
  555.         /* Adjust the selections. */
  556.         if(treeSelectionModel != null && rowsDeleted > 0 &&
  557.            myRow != -1) {
  558.         if(selectedPaths != null) {
  559.             int              maxCounter = selectedPaths.size();
  560.             TreePath[]      treePaths = new TreePath[maxCounter];
  561.  
  562.             selectedPaths.copyInto(treePaths);
  563.             treeSelectionModel.removeSelectionPaths(treePaths);
  564.             treeSelectionModel.addSelectionPath(getTreePath());
  565.         }
  566.         else
  567.             treeSelectionModel.resetRowSelection();
  568.         }
  569.     }
  570.     }
  571.  
  572.     /**
  573.      * Returns the value the receiver is representing. This is a cover
  574.      * for getUserObject.
  575.      */
  576.     public Object getValue() {
  577.     return getUserObject();
  578.     }
  579.  
  580.     /**
  581.      * Returns a TreePath instance for this node.
  582.      */
  583.     protected TreePath getTreePath() {
  584.     return treeUI.createTreePathFor(this);
  585.     }
  586.  
  587.     /**
  588.      * Returns the visibleNodes instance variable of the tree the receiver
  589.      * is contained in.
  590.      */
  591.     protected Vector getVisibleNodes() {
  592.     return treeUI.visibleNodes;
  593.     }
  594.  
  595.     /**
  596.      * Messages the tree with updateYLocationsFrom(row).
  597.      */
  598.     protected void updateTreeYLocationsFrom(int row) {
  599.     treeUI.updateYLocationsFrom(row);
  600.     }
  601. }
  602.