The end of the JTree editing session is now broken: the new value after editing does not replace the previous value.

Roman Kennke wrote:
This implements the last missing methods in BasicTreeUI and fixes a couple of inconsistencies in the handling of JTree editing. I hope I didn't introduce any glitches again, please let me know if that's the case and I'll fix it.

2006-08-15  Roman Kennke  <[EMAIL PROTECTED]>

    * javax/swing/plaf/basic/BasicTreeUI.java
    (MouseHandler.selectedOnPress): New field.
    (MouseHandler.handleEvent): New helper method for handling
    selection and start/stop editing for mouse events.
    (MouseHandler.mouseDragged): Commented as no-op method.
    (MouseHandler.mouseMoved): Commented as no-op method.
    (MouseHandler.mousePressed): Use handleEvent() to handle
    selection and editing handling.
    (MouseHandler.mouseReleased): Use handleEvent() to handle
    selection and editing handling.
    (MouseInputHandler.MouseInputHandler): Register itself
    as mouse listener on source. Redispatch event to
    destination.
    (MouseInputHandler.dispatch): New helper method.
    (MouseInputHandler.mouseClicked): Dispatch event.
    (MouseInputHandler.mouseDragged): Dispatch event.
    (MouseInputHandler.mouseEntered): Stop dispatching
    if dragging stopped.
    (MouseInputHandler.mouseExited): Stop dispatching
    if dragging stopped.
    (MouseInputHandler.mouseMoved): Stop dispatching.
    (MouseInputHandler.mousePressed): Marked as no-op.
    (MouseInputHandler.mouseReleased): Dispatch and stop
    dispatching afterwards.
    (MouseInputHandler.removeFromSource): Implemented.
    (PropertyChangeHandler.propertyChange): Also handle
    editable property changes by calling setEditable().
    (SelectionModelPropertyChangeHandler.propertyChange):
    Reset row selection.
    (startEditTimer): Removed.
    (setCellEditor): Call updateEditor().
    (setEditable): Call updateEditor().
    (startEditingAtPath): Make path fully visible before starting
    editing.
    (startEditing): Maybe cancel previous edit session. Add
    editing component itself, not its parent container.
    Register MouseInputHandler for correctly redispatching
    initial events.
    (stopEditing): Message cellEditor and only completeEditing()
    when approved by cell editor.
    (updateCellEditor): Complete editing before updating
    the cell editor. Get cell editor from JTree if possible,
    otherwise create default editor. Update the listeners
    on the editor.
    * javax/swing/tree/DefaultTreeCellEditor.java
    (CLICK_COUNT_TO_START): Removed.
    (DefaultTreeCellEditor): Install correct border. Let setTree()
    update the listeners. Don't initialize lastPath and font yet.
    (actionPerformed): Implemented to start editing.
    (createTreeCellEditor): Set click count to start to 1, rather than
    3.
    (isCellEditable): Prepare editor here. Determine if we can
    start immediately, or if we trigger a timer to do so.
    (prepareForEditing): Don't removeAll() (not necessary),
    check editingComponent to be non-null.
    (setTree): Update listeners.
    (shouldStartEditingTimer): Check for left mouse button.
    (startEditingTimer): Lazily create timer.

/Roman
------------------------------------------------------------------------

Index: javax/swing/plaf/basic/BasicTreeUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicTreeUI.java,v
retrieving revision 1.152
diff -u -1 -2 -r1.152 BasicTreeUI.java
--- javax/swing/plaf/basic/BasicTreeUI.java     8 Aug 2006 12:08:58 -0000       
1.152
+++ javax/swing/plaf/basic/BasicTreeUI.java     15 Aug 2006 23:34:10 -0000
@@ -29,35 +29,35 @@
  modules, and to copy and distribute the resulting executable under
  terms of your choice, provided that you also meet, for each linked
  independent module, the terms and conditions of the license of that
  module.  An independent module is a module which is not derived from
  or based on this library.  If you modify this library, you may extend
  this exception to your version of the library, but you are not
  obligated to do so.  If you do not wish to do so, delete this
  exception statement from your version. */
package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException;
 import gnu.javax.swing.tree.GnuPath;
import java.awt.Color;
 import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.FontMetrics;
 import java.awt.Graphics;
 import java.awt.Insets;
 import java.awt.Label;
+import java.awt.Point;
 import java.awt.Rectangle;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.ComponentAdapter;
 import java.awt.event.ComponentEvent;
 import java.awt.event.ComponentListener;
 import java.awt.event.FocusEvent;
 import java.awt.event.FocusListener;
 import java.awt.event.InputEvent;
 import java.awt.event.KeyAdapter;
 import java.awt.event.KeyEvent;
 import java.awt.event.KeyListener;
@@ -266,31 +266,24 @@
PropertyChangeListener selectionModelPropertyChangeListener; ComponentListener componentListener; CellEditorListener cellEditorListener; TreeExpansionListener treeExpansionListener; TreeModelListener treeModelListener; /**
-   * This timer fires the editing action after about 1200 ms if not reset 
during
-   * that time. It handles the editing start with the single mouse click (and
-   * not the double mouse click) on the selected tree node.
-   */
-  Timer startEditTimer;
- - /**
    * The zero size icon, used for expand controls, if they are not visible.
    */
   static Icon nullIcon;
/**
    * The special value of the mouse event is sent indicating that this is not
    * just the mouse click, but the mouse click on the selected node. Sending
    * such event forces to start the cell editing session.
    */
   static final MouseEvent EDIT = new MouseEvent(new Label(), 7, 7, 7, 7, 7, 7,
                                                 false);
@@ -581,46 +574,45 @@
   protected boolean getShowsRootHandles()
   {
     return tree.getShowsRootHandles();
   }
/**
    * Sets the cell editor.
* * @param editor to set the cellEditor to.
    */
   protected void setCellEditor(TreeCellEditor editor)
   {
-    cellEditor = editor;
-    createdCellEditor = true;
+    updateCellEditor();
   }
/**
    * Returns the <code>TreeCellEditor</code> for this tree.
* * @return the cellEditor for this tree.
    */
   protected TreeCellEditor getCellEditor()
   {
     return cellEditor;
   }
/**
    * Configures the receiver to allow, or not allow, editing.
* * @param newValue sets the receiver to allow editing if true.
    */
   protected void setEditable(boolean newValue)
   {
-    tree.setEditable(newValue);
+    updateCellEditor();
   }
/**
    * Returns true if the receiver allows editing.
* * @return true if the receiver allows editing.
    */
   protected boolean isEditable()
   {
     return tree.isEditable();
   }
@@ -778,30 +770,31 @@
   }
/**
    * Stops the current editing session. This has no effect if the tree is not
    * being edited. Returns true if the editor allows the editing session to
    * stop.
* * @param tree is the tree to stop the editing on
    * @return true if the editor allows the editing session to stop.
    */
   public boolean stopEditing(JTree tree)
   {
-    if (isEditing(tree))
+    boolean ret = false;
+    if (editingComponent != null && cellEditor.stopCellEditing())
       {
         completeEditing(false, false, true);
-        finish();
+        ret = true;
       }
-    return ! isEditing(tree);
+    return ret;
   }
/**
    * Cancels the current editing session.
* * @param tree is the tree to cancel the editing session on.
    */
   public void cancelEditing(JTree tree)
   {
     // There is no need to send the cancel message to the editor,
     // as the cancellation event itself arrives from it. This would
     // only be necessary when cancelling the editing programatically.
@@ -809,25 +802,27 @@
     finish();
   }
/**
    * Selects the last item in path and tries to edit it. Editing will fail if
    * the CellEditor won't allow it for the selected item.
* * @param tree is the tree to edit on.
    * @param path is the path in tree to edit on.
    */
   public void startEditingAtPath(JTree tree, TreePath path)
   {
-    startEditing(path, null);
+    tree.scrollPathToVisible(path);
+    if (path != null && tree.isVisible(path))
+      startEditing(path, null);
   }
/**
    * Returns the path to the element that is being editted.
* * @param tree is the tree to get the editing path from.
    * @return the path that is being edited.
    */
   public TreePath getEditingPath(JTree tree)
   {
     return editingPath;
   }
@@ -1158,27 +1153,51 @@
   protected void updateDepthOffset()
   {
     depthOffset += getVerticalLegBuffer();
   }
/**
    * Updates the cellEditor based on editability of the JTree that we're
    * contained in. If the tree is editable but doesn't have a cellEditor, a
    * basic one will be used.
    */
   protected void updateCellEditor()
   {
-    if (tree.isEditable() && cellEditor == null)
-      setCellEditor(createDefaultCellEditor());
-    createdCellEditor = true;
+    completeEditing();
+    TreeCellEditor newEd = null;
+    if (tree != null && tree.isEditable())
+      {
+        newEd = tree.getCellEditor();
+        if (newEd == null)
+          {
+            newEd = createDefaultCellEditor();
+            if (newEd != null)
+              {
+                tree.setCellEditor(newEd);
+                createdCellEditor = true;
+              }
+          }
+      }
+    // Update listeners.
+    if (newEd != cellEditor)
+      {
+        if (cellEditor != null && cellEditorListener != null)
+          cellEditor.removeCellEditorListener(cellEditorListener);
+        cellEditor = newEd;
+        if (cellEditorListener == null)
+          cellEditorListener = createCellEditorListener();
+        if (cellEditor != null && cellEditorListener != null)
+          cellEditor.addCellEditorListener(cellEditorListener);
+        createdCellEditor = false;
+      }
   }
/**
    * Messaged from the tree we're in when the renderer has changed.
    */
   protected void updateRenderer()
   {
     if (tree != null)
       {
        TreeCellRenderer rend = tree.getCellRenderer();
        if (rend != null)
          {
@@ -1763,29 +1782,33 @@
   }
/**
    * Will start editing for node if there is a cellEditor and shouldSelectCall
    * returns true. This assumes that path is valid and visible.
* * @param path is the path to start editing
    * @param event is the MouseEvent performed on the path
    * @return true if successful
    */
   protected boolean startEditing(TreePath path, MouseEvent event)
   {
-    updateCellEditor();
-    TreeCellEditor ed = getCellEditor();
+    // Maybe cancel editing.
+    if (isEditing(tree) && tree.getInvokesStopCellEditing()
+        && ! stopEditing(tree))
+      return false;
+
+    completeEditing();
- if (ed != null && (event == EDIT || ed.shouldSelectCell(event))
-        && ed.isCellEditable(event))
+    TreeCellEditor ed = cellEditor;
+    if (ed != null && ed.isCellEditable(event))
       {
         Rectangle bounds = getPathBounds(tree, path);
// Extend the right boundary till the tree width.
         bounds.width = tree.getWidth() - bounds.x;
editingPath = path;
         editingRow = tree.getRowForPath(editingPath);
Object value = editingPath.getLastPathComponent(); stopEditingInCompleteEditing = false;
@@ -1793,29 +1816,42 @@
         isEditing = true;
         editingComponent = ed.getTreeCellEditorComponent(tree, value, true,
                                                          expanded,
                                                          isLeaf(editingRow),
                                                          editingRow);
// Remove all previous components (if still present). Only one
         // container with the editing component inside is allowed in the tree.
         tree.removeAll();
// The editing component must be added to its container. We add the
         // container, not the editing component itself.
-        Component container = editingComponent.getParent();
-        container.setBounds(bounds);
-        tree.add(container);
+        editingComponent.setBounds(bounds);
+        tree.add(editingComponent);
         editingComponent.requestFocus();
+ // Register MouseInputHandler to redispatch initial mouse events
+        // correctly.
+        if (event != null)
+          {
+            Point p = SwingUtilities.convertPoint(tree, event.getX(), 
event.getY(),
+                                                  editingComponent);
+            Component active =
+              SwingUtilities.getDeepestComponentAt(editingComponent, p.x, p.y);
+            if (active != null)
+              {
+                MouseInputHandler ih = new MouseInputHandler(tree, active, 
event);
+ + }
+          }
         return true;
       }
     return false;
   }
/**
    * If the <code>mouseX</code> and <code>mouseY</code> are in the expand or
    * collapse region of the row, this will toggle the row.
* * @param path the path we are concerned with
    * @param mouseX is the cursor's x position
    * @param mouseY is the cursor's y position
@@ -2338,149 +2374,129 @@
      */
     public void keyReleased(KeyEvent e)
     {
       // Nothing to do here.
     }
   }
/**
    * MouseListener is responsible for updating the selection based on mouse
    * events.
    */
   public class MouseHandler
-      extends MouseAdapter
-      implements MouseMotionListener
+    extends MouseAdapter
+    implements MouseMotionListener
   {
+ + /**
+     * If the cell has been selected on mouse press.
+     */
+    private boolean selectedOnPress;
+
     /**
      * Constructor
      */
     public MouseHandler()
     {
       // Nothing to do here.
     }
/**
      * Invoked when a mouse button has been pressed on a component.
* * @param e is the mouse event that occured
      */
     public void mousePressed(MouseEvent e)
     {
-      // Any mouse click cancels the previous waiting edit action, initiated
-      // by the single click on the selected node.
-      if (startEditTimer != null)
+      if (! e.isConsumed())
         {
-          startEditTimer.stop();
-          startEditTimer = null;
+          handleEvent(e);
+          selectedOnPress = true;
         }
-
-      if (tree != null && tree.isEnabled())
+      else
         {
-          // Always end the current editing session if clicked on the
-          // tree and outside the bounds of the editing component.
-          if (isEditing(tree))
-            if (!stopEditing(tree))
-            // Return if we have failed to cancel the editing session.
-              return;
- - int x = e.getX();
-          int y = e.getY();
-          TreePath path = getClosestPathForLocation(tree, x, y);
-
-          if (path != null)
-            {
-              Rectangle bounds = getPathBounds(tree, path);
-              if (SwingUtilities.isLeftMouseButton(e))
-                checkForClickInExpandControl(path, x, y);
-
-              if (x > bounds.x && x <= (bounds.x + bounds.width))
-                {
-                  TreePath currentLead = tree.getLeadSelectionPath();
-                  if (currentLead != null && currentLead.equals(path)
-                      && e.getClickCount() == 1 && tree.isEditable())
-                    {
-                      // Schedule the editing session.
-                      final TreePath editPath = path;
- - // The code below handles the required click-pause-click - // functionality which must be present in the tree UI. - // If the next click comes after the
-                      // time longer than the double click interval AND
-                      // the same node stays focused for the WAIT_TILL_EDITING
-                      // duration, the timer starts the editing session.
-                      if (startEditTimer != null)
-                        startEditTimer.stop();
-
-                      startEditTimer = new Timer(WAIT_TILL_EDITING,
-                         new ActionListener()
-                           {
-                              public void actionPerformed(ActionEvent e)
-                                {
-                                   startEditing(editPath, EDIT);
-                                }
-                            });
- - startEditTimer.setRepeats(false);
-                      startEditTimer.start();
-                    }
-                  else
-                    {
-                      if (e.getClickCount() == 2)
-                        toggleExpandState(path);
-                      else
-                        selectPathForEvent(path, e);
-                    }
-                }
-            }
+          selectedOnPress = false;
         }
-
-      // We need to request the focus.
-      tree.requestFocusInWindow();
     }
/**
      * Invoked when a mouse button is pressed on a component and then dragged.
      * MOUSE_DRAGGED events will continue to be delivered to the component 
where
      * the drag originated until the mouse button is released (regardless of
      * whether the mouse position is within the bounds of the component).
* * @param e is the mouse event that occured
      */
     public void mouseDragged(MouseEvent e)
-    throws NotImplementedException
     {
-      // TODO: What should be done here, if anything?
+      // Nothing to do here.
     }
/**
      * Invoked when the mouse button has been moved on a component (with no
      * buttons no down).
* * @param e the mouse event that occured
      */
     public void mouseMoved(MouseEvent e)
-    throws NotImplementedException
     {
-      // TODO: What should be done here, if anything?
+      // Nothing to do here.
     }
/**
      * Invoked when a mouse button has been released on a component.
* * @param e is the mouse event that occured
      */
     public void mouseReleased(MouseEvent e)
-    throws NotImplementedException
     {
-      // TODO: What should be done here, if anything?
+      if (! e.isConsumed() && ! selectedOnPress)
+        handleEvent(e);
+    }
+
+    /**
+     * Handles press and release events.
+     *
+     * @param e the mouse event
+     */
+    private void handleEvent(MouseEvent e)
+    {
+      if (tree != null && tree.isEnabled())
+        {
+          // Maybe stop editing.
+          if (isEditing(tree) && tree.getInvokesStopCellEditing()
+              && ! stopEditing(tree))
+            return;
+
+          // Explicitly request focus.
+          tree.requestFocusInWindow();
+
+          int x = e.getX();
+          int y = e.getY();
+          TreePath path = getClosestPathForLocation(tree, x, y);
+          if (path != null)
+            {
+              Rectangle b = getPathBounds(tree, path);
+              if (y <= b.y + b.height)
+                {
+                  if (SwingUtilities.isLeftMouseButton(e))
+                    checkForClickInExpandControl(path, x, y);
+                  if (x > b.x && x <= b.x + b.width)
+                    {
+                      if (! startEditing(path, e))
+                        selectPathForEvent(path, e);
+                    }
+                }
+            }
+        }
     }
   }
/**
    * MouseInputHandler handles passing all mouse events, including mouse motion
    * events, until the mouse is released to the destination it is constructed
    * with.
    */
   public class MouseInputHandler
       implements MouseInputListener
   {
     /** Source that events are coming from */
@@ -2492,115 +2508,134 @@
     /**
      * Constructor
* * @param source that events are coming from
      * @param destination that receives all events
      * @param e is the event received
      */
     public MouseInputHandler(Component source, Component destination,
                              MouseEvent e)
     {
       this.source = source;
       this.destination = destination;
+      source.addMouseListener(this);
+      source.addMouseMotionListener(this);
+      dispatch(e);
     }
/**
      * Invoked when the mouse button has been clicked (pressed and released) on
      * a component.
* * @param e mouse event that occured
      */
     public void mouseClicked(MouseEvent e)
-    throws NotImplementedException
     {
-      // TODO: What should be done here, if anything?
+      dispatch(e);
     }
/**
      * Invoked when a mouse button has been pressed on a component.
* * @param e mouse event that occured
      */
     public void mousePressed(MouseEvent e)
-    throws NotImplementedException
     {
-      // TODO: What should be done here, if anything?
+      // Nothing to do here.
     }
/**
      * Invoked when a mouse button has been released on a component.
* * @param e mouse event that occured
      */
     public void mouseReleased(MouseEvent e)
-    throws NotImplementedException
     {
-      // TODO: What should be done here, if anything?
+      dispatch(e);
+      removeFromSource();
     }
/**
      * Invoked when the mouse enters a component.
* * @param e mouse event that occured
      */
     public void mouseEntered(MouseEvent e)
-    throws NotImplementedException
     {
-      // TODO: What should be done here, if anything?
+      if (! SwingUtilities.isLeftMouseButton(e))
+        removeFromSource();
     }
/**
      * Invoked when the mouse exits a component.
* * @param e mouse event that occured
      */
     public void mouseExited(MouseEvent e)
-    throws NotImplementedException
     {
-      // TODO: What should be done here, if anything?
+      if (! SwingUtilities.isLeftMouseButton(e))
+        removeFromSource();
     }
/**
      * Invoked when a mouse button is pressed on a component and then dragged.
      * MOUSE_DRAGGED events will continue to be delivered to the component 
where
      * the drag originated until the mouse button is released (regardless of
      * whether the mouse position is within the bounds of the component).
* * @param e mouse event that occured
      */
     public void mouseDragged(MouseEvent e)
-    throws NotImplementedException
     {
-      // TODO: What should be done here, if anything?
+      dispatch(e);
     }
/**
      * Invoked when the mouse cursor has been moved onto a component but no
      * buttons have been pushed.
* * @param e mouse event that occured
      */
     public void mouseMoved(MouseEvent e)
-    throws NotImplementedException
     {
-      // TODO: What should be done here, if anything?
+      removeFromSource();
     }
/**
      * Removes event from the source
      */
     protected void removeFromSource()
-    throws NotImplementedException
     {
-      // TODO: Implement this properly.
+      if (source != null)
+        {
+          source.removeMouseListener(this);
+          source.removeMouseMotionListener(this);
+        }
+      source = null;
+      destination = null;
+    }
+
+    /**
+     * Redispatches mouse events to the destination.
+     *
+     * @param e the mouse event to redispatch
+     */
+    private void dispatch(MouseEvent e)
+    {
+      if (destination != null)
+        {
+          MouseEvent e2 = SwingUtilities.convertMouseEvent(source, e,
+                                                           destination);
+          destination.dispatchEvent(e2);
+        }
     }
   }
/**
    * Class responsible for getting size of node, method is forwarded to
    * BasicTreeUI method. X location does not include insets, that is handled in
    * getPathBounds.
    */
   public class NodeDimensionsHandler
       extends AbstractLayoutCache.NodeDimensions
   {
     /**
@@ -2697,53 +2732,55 @@
         }
       else if (property.equals(JTree.TREE_MODEL_PROPERTY))
         {
           setModel(tree.getModel());
         }
       else if (property.equals(JTree.CELL_RENDERER_PROPERTY))
         {
           setCellRenderer(tree.getCellRenderer());
           // Update layout.
           if (treeState != null)
             treeState.invalidateSizes();
         }
+      else if (property.equals(JTree.EDITABLE_PROPERTY))
+        setEditable(((Boolean) event.getNewValue()).booleanValue());
+ }
   }
/**
    * Listener on the TreeSelectionModel, resets the row selection if any of the
    * properties of the model change.
    */
   public class SelectionModelPropertyChangeHandler
-      implements PropertyChangeListener
+    implements PropertyChangeListener
   {
/**
      * Constructor
      */
     public SelectionModelPropertyChangeHandler()
     {
       // Nothing to do here.
     }
/**
      * This method gets called when a bound property is changed.
* * @param event A PropertyChangeEvent object describing the event source and
      *          the property that has changed.
      */
     public void propertyChange(PropertyChangeEvent event)
-    throws NotImplementedException
     {
-      // TODO: What should be done here, if anything?
+      treeSelectionModel.resetRowSelection();
     }
   }
/**
    * The action to cancel editing on this tree.
    */
   public class TreeCancelEditingAction
       extends AbstractAction
   {
     /**
      * Creates the new tree cancel editing action.
* Index: javax/swing/tree/DefaultTreeCellEditor.java
===================================================================
RCS file: 
/cvsroot/classpath/classpath/javax/swing/tree/DefaultTreeCellEditor.java,v
retrieving revision 1.22
diff -u -1 -2 -r1.22 DefaultTreeCellEditor.java
--- javax/swing/tree/DefaultTreeCellEditor.java 7 Jun 2006 14:36:03 -0000       
1.22
+++ javax/swing/tree/DefaultTreeCellEditor.java 15 Aug 2006 23:34:10 -0000
@@ -50,48 +50,43 @@
 import java.awt.event.ActionListener;
 import java.awt.event.MouseEvent;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.util.EventObject;
import javax.swing.DefaultCellEditor;
 import javax.swing.Icon;
 import javax.swing.JTextField;
 import javax.swing.JTree;
 import javax.swing.SwingUtilities;
+import javax.swing.Timer;
 import javax.swing.UIManager;
 import javax.swing.border.Border;
 import javax.swing.event.CellEditorListener;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.EventListenerList;
 import javax.swing.event.TreeSelectionEvent;
 import javax.swing.event.TreeSelectionListener;
/**
  * Participates in the tree cell editing.
* * @author Andrew Selkirk
  * @author Audrius Meskauskas
  */
 public class DefaultTreeCellEditor
   implements ActionListener, TreeCellEditor, TreeSelectionListener
 {
   /**
- * The number of the fast mouse clicks, required to start the editing - * session.
-   */
-  static int CLICK_COUNT_TO_START = 3;
- - /**
    * This container that appears on the tree during editing session.
* It contains the editing component displays various other editor - * specific parts like editing icon. */
   public class EditorContainer extends Container
   {
    /**
     * Use v 1.5 serial version UID for interoperability.
     */
     static final long serialVersionUID = 6470339600449699810L;
/**
@@ -358,38 +353,30 @@
   /**
* Constructs a DefaultTreeCellEditor object for a JTree using the specified * renderer and the specified editor. (Use this constructor * for specialized editing.) * * @param tree - a JTree object
    * @param renderer - a DefaultTreeCellRenderer object
    * @param editor - a TreeCellEditor object
    */
   public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer,
                                TreeCellEditor editor)
   {
-    setTree(tree);
     this.renderer = renderer;
- - if (editor == null)
-      editor = createTreeCellEditor();
-    else
-      editor.addCellEditorListener(new RealEditorListener());
- realEditor = editor; - - lastPath = tree.getLeadSelectionPath();
-    tree.addTreeSelectionListener(this);
+    if (realEditor == null)
+      realEditor = createTreeCellEditor();
     editingContainer = createContainer();
-    setFont(UIManager.getFont("Tree.font"));
+    setTree(tree);
     setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor"));
   }
/**
    * Configures the editing component whenever it is null.
* * @param tree the tree to configure to component for.
    * @param renderer the renderer used to set up the nodes
* @param editor the editor used */
   private void configureEditingComponent(JTree tree,
                                          DefaultTreeCellRenderer renderer,
@@ -508,52 +495,84 @@
                                               boolean isSelected, boolean 
expanded,
                                               boolean leaf, int row)
   {
     if (realEditor == null)
       realEditor = createTreeCellEditor();
return realEditor.getTreeCellEditorComponent(tree, value, isSelected,
                                                         expanded, leaf, row);
   }
/**
    * Returns the value currently being edited (requests it from the
-   * [EMAIL PROTECTED] realEditor}.
+   * [EMAIL PROTECTED] #realEditor}.
* * @return the value currently being edited
    */
   public Object getCellEditorValue()
   {
     return realEditor.getCellEditorValue();
   }
/** * 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
    * @return true if editing can be started
    */
   public boolean isCellEditable(EventObject event)
- { - if (editingComponent == null)
-        configureEditingComponent(tree, renderer, realEditor);
- - if (editingComponent != null && realEditor.isCellEditable(event))
+  {
+    boolean ret = false;
+    boolean ed = false;
+    if (event != null)
       {
-        prepareForEditing();
-        return true;
+        if (event.getSource() instanceof JTree)
+          {
+            setTree((JTree) event.getSource());
+            if (event instanceof MouseEvent)
+              {
+                MouseEvent me = (MouseEvent) event;
+                TreePath path = tree.getPathForLocation(me.getX(), me.getY());
+                ed = lastPath != null && path != null && lastPath.equals(path);
+                if (path != null)
+                  {
+                    lastRow = tree.getRowForPath(path);
+                    Object val = path.getLastPathComponent();
+                    boolean isSelected = tree.isRowSelected(lastRow);
+                    boolean isExpanded = tree.isExpanded(path);
+                    TreeModel m = tree.getModel();
+                    boolean isLeaf = m.isLeaf(val);
+                    determineOffset(tree, val, isSelected, isExpanded, isLeaf,
+                                    lastRow);
+                  }
+              }
+          }
       }
-    return false;
+    if (! realEditor.isCellEditable(event))
+      ret = false;
+ else + {
+        if (canEditImmediately(event))
+          ret = true;
+        else if (ed && shouldStartEditingTimer(event))
+          startEditingTimer();
+        else if (timer != null && timer.isRunning())
+          timer.stop();
+      }
+    if (ret)
+      prepareForEditing();
+    return ret;
+ } /**
    * Messages the realEditor for the return value.
* * @param event -
    *          the event the editor should use to start editing
    * @return true if the editor would like the editing cell to be selected;
    *         otherwise returns false
    */
   public boolean shouldSelectCell(EventObject event)
   {
@@ -644,59 +663,79 @@
     lastPath = e.getNewLeadSelectionPath();
     lastRow = tree.getRowForPath(lastPath);
     stopCellEditing();
   }
/**
    * Messaged when the timer fires.
* * @param e the event that characterizes the action.
    */
   public void actionPerformed(ActionEvent e)
   {
+    if (tree != null && lastPath != null)
+      tree.startEditingAtPath(lastPath);
   }
/**
    * Sets the tree currently editing for. This is needed to add a selection
    * listener.
* * @param newTree -
    *          the new tree to be edited
    */
   protected void setTree(JTree newTree)
   {
-    tree = newTree;
+    if (tree != newTree)
+      {
+        if (tree != null)
+          tree.removeTreeSelectionListener(this);
+        tree = newTree;
+        if (tree != null)
+          tree.addTreeSelectionListener(this);
+
+        if (timer != null)
+          timer.stop();
+      }
   }
/**
    * Returns true if event is a MouseEvent and the click count is 1.
* * @param event - the event being studied
    * @return true if editing should start
    */
   protected boolean shouldStartEditingTimer(EventObject event)
   {
- if ((event instanceof MouseEvent) && - ((MouseEvent) event).getClickCount() == 1)
-      return true;
-    return false;
+    boolean ret = false;
+    if (event instanceof MouseEvent)
+      {
+        MouseEvent me = (MouseEvent) event;
+        ret = SwingUtilities.isLeftMouseButton(me) && me.getClickCount() == 1
+              && inHitRegion(me.getX(), me.getY());
+      }
+    return ret;
   }
/** * Starts the editing timer (if one installed). */
   protected void startEditingTimer()
   {
-    if (timer != null)
-      timer.start();
+    if (timer == null)
+      {
+        timer = new Timer(1200, this);
+        timer.setRepeats(false);
+      }
+    timer.start();
   }
/** * Returns true if event is null, or it is a MouseEvent with * a click count > 2 and inHitRegion returns true. * * @param event - the event being studied * @return true if event is null, or it is a MouseEvent with * a click count > 2 and inHitRegion returns true */
   protected boolean canEditImmediately(EventObject event)
   {
@@ -714,25 +753,24 @@
* gap displayed by the renderer. In other words this returns true if * the user clicks over the text part displayed by the renderer, and * false otherwise. * * @param x - the x-coordinate of the point
    * @param y - the y-coordinate of the point
* * @return true if the passed in location is a valid mouse location
    */
   protected boolean inHitRegion(int x, int y)
   {
     Rectangle bounds = tree.getPathBounds(lastPath);
- return bounds.contains(x, y);
   }
/**
    * determineOffset
    * @param tree -
* @param value - * @param isSelected - * @param expanded - * @param leaf - * @param row - */
@@ -745,42 +783,43 @@
     if (c != null)
         offset = renderer.getIconTextGap() + c.getIconWidth();
     else
       offset = 0;
   }
/** * Invoked just before editing is to start. Will add the * editingComponent to the editingContainer.
    */
   protected void prepareForEditing()
   {
-    editingContainer.removeAll();
-    editingContainer.add(editingComponent);
+    if (editingComponent != null)
+      editingContainer.add(editingComponent);
   }
/**
    * Creates the container to manage placement of editingComponent.
* * @return the container to manage the placement of the editingComponent.
    */
   protected Container createContainer()
   {
     return new DefaultTreeCellEditor.EditorContainer();
   }
/** * This is invoked if a TreeCellEditor is not supplied in the constructor. * It returns a TextField editor. * * @return a new TextField editor
    */
   protected TreeCellEditor createTreeCellEditor()
   {
-    DefaultCellEditor editor = new DefaultCellEditor(new 
DefaultTreeCellEditor.DefaultTextField(
-                                  
UIManager.getBorder("Tree.selectionBorder")));
+    Border border = UIManager.getBorder("Tree.editorBorder");
+    JTextField tf = new DefaultTreeCellEditor.DefaultTextField(border);
+    DefaultCellEditor editor = new DefaultCellEditor(tf);
     editor.addCellEditorListener(new RealEditorListener());
-    editor.setClickCountToStart(CLICK_COUNT_TO_START);
+    editor.setClickCountToStart(1);
     realEditor = editor;
     return editor;
   }
 }


Reply via email to