This patch contains several fixes, maing the tree editing to work better with the layout cache.

2006-04-20  Audrius Meskauskas  <[EMAIL PROTECTED]>

   * javax/swing/plaf/basic/BasicTreeUI.java
   (finish): Invalidate path bounds.
   (getMaxHeight): Set the row height to the layout cache.
   (startEditing): Do not request to recalculated
   row height and preferred size.
   * javax/swing/tree/DefaultTreeCellEditor.java
   (ICON_TEXT_GAP, ICON_TREE_GAP): Removed, replacing
   with 0.
   * javax/swing/tree/FixedHeightLayoutCache.java:
   Rewritten.
Index: javax/swing/plaf/basic/BasicTreeUI.java
===================================================================
RCS file: /sources/classpath/classpath/javax/swing/plaf/basic/BasicTreeUI.java,v
retrieving revision 1.123
diff -u -r1.123 BasicTreeUI.java
--- javax/swing/plaf/basic/BasicTreeUI.java	13 Apr 2006 17:57:48 -0000	1.123
+++ javax/swing/plaf/basic/BasicTreeUI.java	20 Apr 2006 11:42:48 -0000
@@ -100,6 +100,7 @@
 import javax.swing.tree.AbstractLayoutCache;
 import javax.swing.tree.DefaultTreeCellEditor;
 import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.FixedHeightLayoutCache;
 import javax.swing.tree.TreeCellEditor;
 import javax.swing.tree.TreeCellRenderer;
 import javax.swing.tree.TreeModel;
@@ -668,7 +669,8 @@
 
         maxHeight = Math.max(maxHeight, iconHeight + gap);
       }
-
+     
+    treeState.setRowHeight(maxHeight);
     return maxHeight;
   }
 
@@ -1508,8 +1510,9 @@
   {
     if (! validCachedPreferredSize)
       {
+        Rectangle size = tree.getBounds();
         // Add the scrollbar dimensions to the preferred size.
-        preferredSize = new Dimension(treeState.getPreferredWidth(null),
+        preferredSize = new Dimension(treeState.getPreferredWidth(size),
                                       treeState.getPreferredHeight());
         validCachedPreferredSize = true;
       }
@@ -1598,12 +1601,6 @@
    */
   protected boolean startEditing(TreePath path, MouseEvent event)
   {
-    // Force to recalculate the maximal row height.
-    maxHeight = 0;
-
-    // Force to recalculate the cached preferred size.
-    validCachedPreferredSize = false;
-
     updateCellEditor();
     TreeCellEditor ed = getCellEditor();
 
@@ -3298,15 +3295,17 @@
    */
   void finish()
   {
+    treeState.invalidatePathBounds(treeState.getPathForRow(editingRow));
     editingPath = null;
     editingRow = - 1;
     stopEditingInCompleteEditing = false;
     isEditing = false;
+    Rectangle bounds = editingComponent.getParent().getBounds();
     tree.removeAll();
     validCachedPreferredSize = false;
-
     // Repaint the region, where was the editing component.
-    tree.repaint(editingComponent.getParent().getBounds());
+    tree.repaint(bounds);
     editingComponent = null;
+    tree.requestFocus();
   }
 } // BasicTreeUI
Index: javax/swing/tree/DefaultTreeCellEditor.java
===================================================================
RCS file: /sources/classpath/classpath/javax/swing/tree/DefaultTreeCellEditor.java,v
retrieving revision 1.20
diff -u -r1.20 DefaultTreeCellEditor.java
--- javax/swing/tree/DefaultTreeCellEditor.java	26 Jan 2006 11:26:28 -0000	1.20
+++ javax/swing/tree/DefaultTreeCellEditor.java	20 Apr 2006 11:42:51 -0000
@@ -77,17 +77,6 @@
   implements ActionListener, TreeCellEditor, TreeSelectionListener
 {
   /**
-   * The gap between the icon and editing component during editing. 
-   */
-  static int ICON_TEXT_GAP = 3;
-  
-  /**
-   * The left margin of the editing container (the gap between the tree and
-   * the editing component of the editing icon.
-   */
-  static int TREE_ICON_GAP = ICON_TEXT_GAP;
-  
-  /**
    * The number of the fast mouse clicks, required to start the editing 
    * session.
    */
@@ -141,7 +130,7 @@
         {
           // From the previous version, the left margin is taken as half
           // of the icon width.
-          editingIcon.paintIcon(this, g, TREE_ICON_GAP, 0);
+          editingIcon.paintIcon(this, g, 0, 0);
         }
       super.paint(g);
     }
@@ -157,7 +146,7 @@
 
       // Move the component to the left, leaving room for the editing icon:
       if (editingIcon != null)
-        eOffset = TREE_ICON_GAP + editingIcon.getIconWidth() + ICON_TEXT_GAP;
+        eOffset = editingIcon.getIconWidth();
       else
         eOffset = 0;
 
@@ -166,7 +155,7 @@
       c.setLocation(eOffset, 0);
 
       // Span the editing component near over all window width.
-      c.setSize(bounds.width - eOffset - TREE_ICON_GAP, bounds.height);
+      c.setSize(bounds.width - eOffset, bounds.height);
       /*
        * @specnote the Sun sets some more narrow editing component width (it is
        * not documented how does it is calculated). However as our text field is
@@ -542,7 +531,8 @@
    * If the realEditor returns true to this message, prepareForEditing  
    * is messaged and true is returned.
    * 
-   * @param event - the event the editor should use to consider whether to begin editing or not
+   * @param event - the event the editor should use to consider whether to 
+   * begin editing or not
    * @return true if editing can be started
    */
   public boolean isCellEditable(EventObject event)
Index: javax/swing/tree/FixedHeightLayoutCache.java
===================================================================
RCS file: /sources/classpath/classpath/javax/swing/tree/FixedHeightLayoutCache.java,v
retrieving revision 1.10
diff -u -r1.10 FixedHeightLayoutCache.java
--- javax/swing/tree/FixedHeightLayoutCache.java	13 Apr 2006 16:49:16 -0000	1.10
+++ javax/swing/tree/FixedHeightLayoutCache.java	20 Apr 2006 11:42:52 -0000
@@ -37,6 +37,8 @@
 
 package javax.swing.tree;
 
+import gnu.javax.swing.tree.GnuPath;
+
 import java.awt.Rectangle;
 import java.util.Enumeration;
 import java.util.HashSet;
@@ -45,22 +47,21 @@
 import java.util.Set;
 import java.util.Vector;
 
-import javax.swing.UIManager;
 import javax.swing.event.TreeModelEvent;
 
+
 /**
  * The fixed height tree layout. This class assumes that all cells in the tree
  * have the same fixed height. This may be not the case, for instance, if leaves
  * and branches have different height, of if the tree rows may have arbitrary
  * variable height. This class will also work if the NodeDimensions are not
- * set. If they are set, the size calculations are just forwarded to the set
- * instance. 
+ * set.  
  * 
  * @author Audrius Meskauskas
  * @author Andrew Selkirk 
  */
 public class FixedHeightLayoutCache
-		extends AbstractLayoutCache
+		extends VariableHeightLayoutCache
 {
   /**
    * The cached node record.
@@ -114,8 +115,23 @@
      */
     private TreePath path;
     
+    /**
+     * Get the path for this node. The derived class is returned,
+     * making check for the last child of some parent easier.
+     */
     TreePath getPath()
     {
+      boolean lastChild = false;
+      if (parent!=null)
+        {
+          int nc = treeModel.getChildCount(parent);
+          if (nc > 0)
+            {
+              int n = treeModel.getIndexOfChild(parent, node);
+              if (n == nc-1)
+                lastChild = true;
+            }
+        }
       if (path == null)
         {
           LinkedList lpath = new LinkedList();
@@ -128,12 +144,26 @@
               else
                 rp = null;
             }
-          path = new TreePath(lpath.toArray());
+          path = new GnuPath(lpath.toArray(), lastChild);
         }
       return path;
     }
+    
+    /**
+     * Get the rectangle bounds (compute, if required).
+     */
+    Rectangle getBounds()
+    {
+      // This method may be called in the context when the tree rectangle is
+      // not known. To work around this, it is assumed near infinitely large.
+      if (bounds==null)
+        bounds = getNodeDimensions(node, row, depth, isExpanded, 
+                                   new Rectangle(Integer.MAX_VALUE/2,
+                                                 Integer.MAX_VALUE/2));
+      return bounds;      
+    }
   }
-
+  
   /**
    * The set of all expanded tree nodes.
    */
@@ -153,6 +183,16 @@
    * If true, the row map must be recomputed before using.
    */
   boolean dirty;
+  
+  /**
+   * The cumulative height of all rows.
+   */
+  int totalHeight;
+  
+  /**
+   * The maximal width.
+   */
+  int maximalWidth;
 
   /**
    * Creates the unitialised instance. Before using the class, the row height
@@ -184,6 +224,8 @@
   {
     nodes.clear();
     row2node.clear();
+    
+    totalHeight = maximalWidth = 0;
 
     Object root = treeModel.getRoot();
 
@@ -211,8 +253,10 @@
     Integer n = new Integer(row2node.size());
     row2node.put(n, node);
     
-    nodes.put(node, new NodeRecord(n.intValue(), depth, node, parent));
-
+    NodeRecord nr = new NodeRecord(n.intValue(), depth, node, parent);
+    nodes.put(node, nr);
+     
+    // For expanded nodes and for the root node.
     if (expanded.contains(node) || parent == null)
       {
         int sc = treeModel.getChildCount(node);
@@ -226,14 +270,15 @@
   }
 
   /**
-   * This should invalidate the width of the last path component, but 
-   * following the JDK 1.4 API it is not cached and the method should return
-   * without action.
-   * @param path the path being invalidated, ignored.
+   * Discard the bound information for the given path.
+   * 
+   * @param path the path, for that the bound information must be recomputed.
    */
   public void invalidatePathBounds(TreePath path)
   {
-    // Following JDK 1.4 API, should return without action.
+    NodeRecord r = (NodeRecord) nodes.get(path.getLastPathComponent());
+    if (r!=null)
+      r.bounds = null;
   } 
 
   /**
@@ -277,8 +322,7 @@
    * Get bounds for the given tree path.
    * 
    * @param path the tree path
-   * @param rect the rectangle, specifying the area where the path should be
-   *          displayed.
+   * @param rect the rectangle that will be reused to return the result.
    * @return Rectangle the bounds of the last line, defined by the given path.
    */
   public Rectangle getBounds(TreePath path, Rectangle rect)
@@ -288,18 +332,22 @@
     Object last = path.getLastPathComponent();
     NodeRecord r = (NodeRecord) nodes.get(last);
     if (r == null)
-      // This node is not visible.
-      return new Rectangle();
+    // This node is not visible.
+      {
+        rect.x = rect.y = rect.width = rect.height = 0;
+      }
     else
       {
         if (r.bounds == null)
           {
-            Rectangle dim = getNodeDimensions(last, r.row, r.depth, r.isExpanded,
-                                              rect);
+            Rectangle dim = getNodeDimensions(last, r.row, r.depth,
+                                              r.isExpanded, rect);
             r.bounds = dim;
           }
-        return r.bounds;
+
+        rect.setRect(r.bounds);
       }
+    return rect;
   } 
 
   /**
@@ -351,28 +399,59 @@
     if (dirty)
       update();
 
-    // We do not need to iterate because all rows have the same height.
-    int row = y / rowHeight;
-    if (row < 0)
-      row = 0;
-    if (row > getRowCount())
-      row = getRowCount() - 1;
+    // As the rows have arbitrary height, we need to iterate.
+    NodeRecord best = null;
+    NodeRecord r;
+    Enumeration en = nodes.elements();
     
-    if (row < 0)
-      return null; // Empty tree - nothing to return.
-    
-    Object node = row2node.get(new Integer(row));
-    NodeRecord nr = (NodeRecord) nodes.get(node);
-    return nr.getPath();
+    int dist = Integer.MAX_VALUE;
+
+    while (en.hasMoreElements() && dist > 0)
+      {
+        r = (NodeRecord) en.nextElement();
+        if (best == null)
+          {
+            best = r;
+            dist = distance(r.getBounds(), x, y);
+          }
+        else
+          {
+            int rr = distance(r.getBounds(), x, y);
+            if (rr < dist)
+              {
+                best = r;
+                dist = rr;
+              }
+          }
+      }
+
+    if (best == null)
+      return null;
+    else
+      return best.getPath();
   } 
+  
+  /**
+   * Get the closest distance from this point till the given rectangle. Only
+   * vertical distance is taken into consideration.
+   */
+  int distance(Rectangle r, int x, int y)
+  {
+    if (y < r.y)
+      return r.y - y;
+    else if (y > r.y + r.height)
+      return y - (r.y + r.height);
+    else
+      return 0;
+  }
 
   /**
-   * Get the number of the visible childs for the given tree path. If the
-   * node is not expanded, 0 is returned. Otherwise, the number of children
-   * is obtained from the model as the number of children for the last path
+   * Get the number of the visible childs for the given tree path. If the node
+   * is not expanded, 0 is returned. Otherwise, the number of children is
+   * obtained from the model as the number of children for the last path
    * component.
    * 
-   * @param path the tree path 
+   * @param path the tree path
    * @return int the number of the visible childs (for row).
    */
   public int getVisibleChildCount(TreePath path)  
@@ -466,7 +545,7 @@
    */
   public void setModel(TreeModel newModel)
   {
-    super.setModel(newModel);
+    treeModel = newModel;
     dirty = true;
   }
   
@@ -484,32 +563,40 @@
   }
 
   /**
-   * Get the node dimensions. If the NodeDimensions are not set, this method
-   * calculates dimensions assuming the fixed row height.
-   * 
-   * @param value the last node in the path
-   * @param row the node row
-   * @param depth the indentation depth
-   * @param expanded true if this node is expanded, false otherwise
-   * @param bounds the area where the tree is displayed
-   */
-  protected Rectangle getNodeDimensions(Object value, int row, int depth,
-                                        boolean expanded, Rectangle bounds)
-  {
-    if (nodeDimensions != null)
-      return nodeDimensions.getNodeDimensions(value, row, depth, expanded,
-                                              bounds);
-    else
+   * Get the sum of heights for all rows.
+   */
+  public int getPreferredHeight()
+  {
+    if (dirty)
+      update();
+    totalHeight = 0;
+    Enumeration en = nodes.elements();
+    while (en.hasMoreElements())
       {
-        Rectangle r = new Rectangle(bounds);
-
-        int indent = depth * UIManager.getInt("Tree.rightChildIndent");
+        NodeRecord nr = (NodeRecord) en.nextElement();
+        Rectangle r = nr.getBounds();
+        totalHeight += r.height;
+      }
+    return totalHeight;
+  }
 
-        r.x = indent;
-        r.y = row * getRowHeight();
-        r.width = bounds.width = r.x;
-        r.height = getRowHeight();
-        return r;
+  /**
+   * Get the maximal width.
+   */
+  public int getPreferredWidth(Rectangle value)
+  {
+    if (dirty)
+      update();
+    
+    maximalWidth = 0;
+    Enumeration en = nodes.elements();
+    while (en.hasMoreElements())
+      {
+        NodeRecord nr = (NodeRecord) en.nextElement();
+        Rectangle r = nr.getBounds();
+        if (r.x + r.width > maximalWidth)
+          maximalWidth = r.x + r.width;
       }
+    return maximalWidth;
   }
 }

Reply via email to