Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4671567

REGRESSION: Tooltips under 1.3.1_03 failing to display for JTree rows

    Details

    • Subcomponent:
    • Resolved In Build:
      04
    • CPU:
      x86, sparc
    • OS:
      linux, solaris_8, windows_nt, windows_2000, windows_xp

      Description

      SelectedNode();
                  DefaultMutableTreeNode parent;

                  /* Determine where to create the new node. */
                  if(lastItem != null) {
                      parent = (DefaultMutableTreeNode)lastItem.getParent();
                      if(parent == null) {
                          parent = (DefaultMutableTreeNode)treeModel.getRoot();
                          lastItem = null;
                      }
                  }
                  else
                      parent = (DefaultMutableTreeNode)treeModel.getRoot();
                  if(lastItem == null)
                      newIndex = treeModel.getChildCount(parent);
                  else
                      newIndex = parent.getIndex(lastItem);

                  /* Let the treemodel know. */
                  treeModel.insertNodeInto(createNewNode("Inserted " +
                                              Integer.toString(insertCount++)),
                                           parent, newIndex);
              }
          } // End of SampleTree.InsertAction


          /**
            * ReloadAction is used to reload from the selected node. If nothing
            * is selected, reload is not issued.
            */
          class ReloadAction extends Object implements ActionListener
          {
              /**
                * Messaged when the user clicks on the Reload menu item.
                * Determines the selection from the Tree and asks the treemodel
                * to reload from that node.
                */
              public void actionPerformed(ActionEvent e) {
                  DefaultMutableTreeNode lastItem = getSelectedNode();

                  if(lastItem != null)
                      treeModel.reload(lastItem);
              }
          } // End of SampleTree.ReloadAction

          /**
            * RemoveAction removes the selected node from the tree. If
            * The root or nothing is selected nothing is removed.
            */
          class RemoveAction extends Object implements ActionListener
          {
              /**
                * Removes the selected item as long as it isn't root.
                */
              public void actionPerformed(ActionEvent e) {
                  DefaultMutableTreeNode lastItem = getSelectedNode();

                  if(lastItem != null && lastItem != (DefaultMutableTreeNode)
      treeModel.getRoot()) {
                      treeModel.removeNodeFromParent(lastItem);
                  }
              }
          } // End of SampleTree.RemoveAction


          /**
            * ShowHandlesChangeListener implements the ChangeListener interface
            * to toggle the state of showing the handles in the tree.
            */
          class ShowHandlesChangeListener extends Object implements ChangeListener
          {
              public void stateChanged(ChangeEvent e) {
                  tree.setShowsRootHandles(((JCheckBox)e.getSource()).isSelected());
              }

          } // End of class SampleTree.ShowHandlesChangeListener


          /**
            * ShowRootChangeListener implements the ChangeListener interface
            * to toggle the state of showing the root node in the tree.
            */
          class ShowRootChangeListener extends Object implements ChangeListener
          {
              public void stateChanged(ChangeEvent e) {
                  tree.setRootVisible(((JCheckBox)e.getSource()).isSelected());
              }

          } // End of class SampleTree.ShowRootChangeListener


          /**
            * TreeEditableChangeListener implements the ChangeListener interface
            * to toggle between allowing editing and now allowing editing in
            * the tree.
            */
          class TreeEditableChangeListener extends Object implements ChangeListener
          {
              public void stateChanged(ChangeEvent e) {
                  tree.setEditable(((JCheckBox)e.getSource()).isSelected());
              }

          } // End of class SampleTree.TreeEditableChangeListener


      // *************************** !CANDLE ***********************************
      // * BEGIN DEFINITION OF JTREE EXTENSION TO DEMONSTRATE TOOLTIP PROBLEM *
      // * UNDER 1.3.1_03. *
      // *************************** !CANDLE ***********************************

          /**
            * ToolTipTree
            */
          class ToolTipTree extends JTree
          {
             private Point currentTipLocation;

             public ToolTipTree( TreeModel model )
             {
                super( model );
             }

             /**
              * Returns the location for the origin of the tooltip, or
              * null to hide the tooltip.
              */
             public Point getToolTipLocation( MouseEvent event )
             {
                // Determine if we are hovering over a row in the tree...
                int row = tree.getRowForLocation( event.getX(), event.getY() );
                if (row > -1)
                {
                   // set tool tip origin to be at current mouse position...
                   currentTipLocation = event.getPoint();
                }
                else
                {
                   // set tool tip location to null to hide tool tip...
                   currentTipLocation = null;
                }

                return currentTipLocation;
             }

             /**
              * Returns the string to be used as the tooltip text.
              */
             public String getToolTipText( MouseEvent event )
             {
                String tooltiptext = null;
                Point tipLocation = null;

                // See if hovering over a row in the tree...
                tipLocation = getToolTipLocation( event );
                if (tipLocation != null)
                {
                   // Use label associated with tree row as tip text...
                   TreePath path = getPathForLocation( tipLocation.x, tipLocation.y );
                   tooltiptext = path.getLastPathComponent().toString();
                }

                return tooltiptext;
             }

          } // End of class SampleTree.ToolTipTree

      // *************************** !CANDLE ***********************************
      // * END DEFINITION OF JTREE EXTENSION TO DEMONSTRATE TOOLTIP PROBLEM *
      // * UNDER 1.3.1_03. *
      // *************************** !CANDLE ***********************************

          static public void main(String args[]) {
              new SampleTree();
          }
      }

      ---------- END SOURCE ----------

      CUSTOMER WORKAROUND :
      No workaround found, but desparately needed. Falling back
      to 1.3.1_02 run-time does work, but is not desired.
      (Review ID: 145250)
      ======================================================================


      Name: jk109818 Date: 04/19/2002


      FULL PRODUCT VERSION :
      java version "1.3.1_03"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1_03-b03)
      Java HotSpot(TM) Client VM (build 1.3.1_03-b03, mixed mode)

      FULL OPERATING SYSTEM VERSION : Windows NT 4.0, SP6a


      A DESCRIPTION OF THE PROBLEM :
      We have an application where tooltips are displayed when a
      user positions the mouse over a row (node) in JTree. The
      information in the tooltip varies depending on the state of
      the underlying model associated with the row being
      targeted. When we determine that the mouse position is no
      longer hovering over a JTree row, the tooltip display is
      dismissed.

      Under 1.3.1_02 and prior 1.3.1 releases, this tooltip
      feature worked fine. However, under 1.3.1_03, this feature
      has been regressed. In short, the tooltip either never
      appears when hovering over a visible tree row, or it will
      appear only once over the first targeted tree row.

      After comparing the source changes made to the
      javax\swing\ToolTipManager class between the 1.3.1_02 and
      1.3.1_03, the regression appears to have been introduced by
      the addition and removal of the following statement
      relative to the 1.3.1_02 level release:

      insideComponent = null;

      If I change the 1.3.1_03 version of the ToolTipManager
      class to be identical to the 1.3.1_02 version with respect
      to the above statement only, the regression disappears and
      the tooltips work normally.

      I have included a modified version of the SampleTree demo
      application packaged with the 1.3.1_03 SDK. Specifically,
      I included the modified source code for the SampleTree.java
      module found in the SampleTree\src directory. The sample
      was modified to show a tooltip whenever the mouse is
      positioned over a tree row. The text associated with the
      tooltip varies based on the label associated with the
      visible tree row itself. All changed and/or added
        statements made to SampleTree.java are tagged with the
      literal "!CANDLE" for easy identification.

      If you compile and run the SampleTree demo with this change
      under the 1.3.1_03 run-time, you will observe that no
      tooltips are displayed. However, if you run the exact same
      test with the 1.3.1_02 run-time, you will observe that the
      tooltips are displayed as the mouse hovers over each
      visible tree row and, further, that the tooltip is
      dismissed (hidden) when the mouse is not positioned over a
      visible tree row.

      Again, the regression is related to the excessive
      nullification of the insideComponent field in the 1.3.1_03
      run-time relative to the more sparse use of this statement
      in the 1.3.1_02 run-time.

      Thank you in advance for your assistance.

      REGRESSION. Last worked in version 1.3.1_02

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Recompile the SampleTree demo with attached version of
      modified SampleTree.java module.

      2. Using the 1.3.1_03 run-time, run the SampleTree demo
      and position the mouse over any visible tree row. Observe
      that no visible tooltip will be displayed.

      3. Using the 1.3.1_02 run-time, run the same SampleTree
      demo and position the mouse over any visible tree row.
      Observe that a tooltip appears with the name of the tree
      row label text.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
        Tooltips should be displayed when mouse is positioned over
      visible tree row. Tooltips should be hidden when mouse is
      no longer over a visible tree row.

      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      /*
       * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
       * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
       */

      import javax.swing.*;
      import javax.swing.event.*;
      import java.awt.BorderLayout;
      import java.awt.Color;
      import java.awt.Dimension;
      import java.awt.FlowLayout;
      import java.awt.event.ActionEvent;
      import java.awt.event.ActionListener;
      import java.awt.event.WindowAdapter;
      import java.awt.event.WindowEvent;
      import javax.swing.tree.*;

      import java.awt.event.MouseEvent; // !CANDLE
      import java.awt.Point; // !CANDLE

      /**
        * A demo for illustrating how to do different things with JTree.
        * The data that this displays is rather boring, that is each node will
        * have 7 children that have random names based on the fonts. Each node
        * is then drawn with that font and in a different color.
        * While the data isn't interesting the example illustrates a number
        * of things:
        *
        * For an example of dynamicaly loading children refer to DynamicTreeNode.
        * For an example of adding/removing/inserting/reloading refer to the inner
        * classes of this class, AddAction, RemovAction, InsertAction and
        * ReloadAction.
        * For an example of creating your own cell renderer refer to
        * SampleTreeCellRenderer.
        * For an example of subclassing JTreeModel for editing refer to
        * SampleTreeModel.
        *
        * @version 1.18 02/06/02
        * @author Scott Violet
        */

      public class SampleTree
      {
          /** Window for showing Tree. */
          protected JFrame frame;
          /** Tree used for the example. */
          protected JTree tree;
          /** Tree model. */
          protected DefaultTreeModel treeModel;

          /**
            * Constructs a new instance of SampleTree.
            */
          public SampleTree() {
              // Force SampleTree to come up in the Cross Platform L&F
              try {
                  UIManager.setLookAndFeel
      (UIManager.getCrossPlatformLookAndFeelClassName());
                  // If you want the System L&F instead, comment out the above line
      and
                  // uncomment the following:
                  // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName
      ());
              } catch (Exception exc) {
                  System.err.println("Error loading L&F: " + exc);
              }


              JMenuBar menuBar = constructMenuBar();
              JPanel panel = new JPanel(true);

              frame = new JFrame("SampleTree");
              frame.getContentPane().add("Center", panel);
              frame.setJMenuBar(menuBar);
              frame.setBackground(Color.lightGray);

              /* Create the JTreeModel. */
              DefaultMutableTreeNode root = createNewNode("Root");
              treeModel = new SampleTreeModel(root);

              /* Create the tree. */
      // tree = new JTree(treeModel); !CANDLE
              tree = new SampleTree.ToolTipTree(treeModel); // !CANDLE

              /* Enable tool tips for the tree, without this tool tips will not
                 be picked up. */
              ToolTipManager.sharedInstance().registerComponent(tree);

              /* Make the tree use an instance of SampleTreeCellRenderer for
                 drawing. */
              tree.setCellRenderer(new SampleTreeCellRenderer());

              /* Make tree ask for the height of each row. */
              tree.setRowHeight(-1);

              /* Put the Tree in a scroller. */
              JScrollPane sp = new JScrollPane();
              sp.setPreferredSize(new Dimension(300, 300));
              sp.getViewport().add(tree);

              /* And show it. */
              panel.setLayout(new BorderLayout());
              panel.add("Center", sp);
              panel.add("South", constructOptionsPanel());

              frame.addWindowListener( new WindowAdapter() {
                  public void windowClosing(WindowEvent e) {System.exit(0);}});

              frame.pack();
              frame.show();
          }

          /** Constructs a JPanel containing check boxes for the different
            * options that tree supports. */
          private JPanel constructOptionsPanel() {
              JCheckBox aCheckbox;
              JPanel retPanel = new JPanel(false);
              JPanel borderPane = new JPanel(false);

              borderPane.setLayout(new BorderLayout());
              retPanel.setLayout(new FlowLayout());

              aCheckbox = new JCheckBox("show handles");
              aCheckbox.setSelected(tree.getShowsRootHandles());
              aCheckbox.addChangeListener(new ShowHandlesChangeListener());
              retPanel.add(aCheckbox);

              aCheckbox = new JCheckBox("show root");
              aCheckbox.setSelected(tree.isRootVisible());
              aCheckbox.addChangeListener(new ShowRootChangeListener());
              retPanel.add(aCheckbox);

              aCheckbox = new JCheckBox("editable");
              aCheckbox.setSelected(tree.isEditable());
              aCheckbox.addChangeListener(new TreeEditableChangeListener());
              aCheckbox.setToolTipText("Triple click to edit");
              retPanel.add(aCheckbox);

              borderPane.add(retPanel, BorderLayout.CENTER);

              /* Create a set of radio buttons that dictate what selection should
                 be allowed in the tree. */
              ButtonGroup group = new ButtonGroup();
              JPanel buttonPane = new JPanel(false);
              JRadioButton button;

              buttonPane.setLayout(new FlowLayout());
              button = new JRadioButton("Single");
              button.addActionListener(new AbstractAction() {
                  public boolean isEnabled() { return true; }
                  public void actionPerformed(ActionEvent e) {
                      tree.getSelectionModel().setSelectionMode
                          (TreeSelectionModel.SINGLE_TREE_SELECTION);
                  }
              });
              group.add(button);
              buttonPane.add(button);
              button = new JRadioButton("Contiguous");
              button.addActionListener(new AbstractAction() {
                  public boolean isEnabled() { return true; }
                  public void actionPerformed(ActionEvent e) {
                      tree.getSelectionModel().setSelectionMode
                          (TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
                  }
              });
              group.add(button);
              buttonPane.add(button);
              button = new JRadioButton("Discontiguous");
              button.addActionListener(new AbstractAction() {
                  public boolean isEnabled() { return true; }
                  public void actionPerformed(ActionEvent e) {
                      tree.getSelectionModel().setSelectionMode
                          (TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
                  }
              });
              button.setSelected(true);
              group.add(button);
              buttonPane.add(button);

              borderPane.add(buttonPane, BorderLayout.SOUTH);

              // NOTE: This will be enabled in a future release.
              // Create a label and combobox to determine how many clicks are
              // needed to expand.
      /*
              JPanel clickPanel = new JPanel();
              Object[] values = { "Never", new Integer(1),
                                              new Integer(2), new Integer(3) };
              final JComboBox clickCBox = new JComboBox(values);

              clickPanel.setLayout(new FlowLayout());
              clickPanel.add(new JLabel("Click count to expand:"));
              clickCBox.setSelectedIndex(2);
              clickCBox.addActionListener(new ActionListener() {
                  public void actionPerformed(ActionEvent ae) {
                      Object selItem = clickCBox.getSelectedItem();

                      if(selItem instanceof Integer)
                          tree.setToggleClickCount(((Integer)selItem).intValue());
                      else // Don't toggle
                          tree.setToggleClickCount(0);
                  }
              });
              clickPanel.add(clickCBox);
              borderPane.add(clickPanel, BorderLayout.NORTH);
      */
              return borderPane;
          }

          /** Construct a menu. */
          private JMenuBar constructMenuBar() {
              JMenu menu;
              JMenuBar menuBar = new JMenuBar();
              JMenuItem menuItem;

              /* Good ol exit. */
              menu = new JMenu("File");
              menuBar.add(menu);

              menuItem = menu.add(new JMenuItem("Exit"));
              menuItem.addActionListener(new ActionListener() {
                  public void actionPerformed(ActionEvent e) {
                      System.exit(0);
                  }});

              /* Tree related stuff. */
              menu = new JMenu("Tree");
              menuBar.add(menu);

              menuItem = menu.add(new JMenuItem("Add"));
              menuItem.addActionListener(new AddAction());

              menuItem = menu.add(new JMenuItem("Insert"));
              menuItem.addActionListener(new InsertAction());

              menuItem = menu.add(new JMenuItem("Reload"));
              menuItem.addActionListener(new ReloadAction());

              menuItem = menu.add(new JMenuItem("Remove"));
              menuItem.addActionListener(new RemoveAction());

              return menuBar;
          }

          /**
            * Returns the TreeNode instance that is selected in the tree.
            * If nothing is selected, null is returned.
            */
          protected DefaultMutableTreeNode getSelectedNode() {
              TreePath selPath = tree.getSelectionPath();

              if(selPath != null)
                  return (DefaultMutableTreeNode)selPath.getLastPathComponent();
              return null;
          }

          protected DefaultMutableTreeNode createNewNode(String name) {
              return new DynamicTreeNode(new SampleData(null, Color.black, name));
          }

          /**
            * AddAction is used to add a new item after the selected item.
            */
          class AddAction extends Object implements ActionListener
          {
              /** Number of nodes that have been added. */
              public int addCount;

              /**
                * Messaged when the user clicks on the Add menu item.
                * Determines the selection from the Tree and adds an item
                * after that. If nothing is selected, an item is added to
                * the root.
                */
              public void actionPerformed(ActionEvent e) {
                  int newIndex;
                  DefaultMutableTreeNode lastItem = getSelectedNode();
                  DefaultMutableTreeNode parent;

                  /* Determine where to create the new node. */
                  if(lastItem != null) {
                      parent = (DefaultMutableTreeNode)lastItem.getParent();
                      if(parent == null) {
                          parent = (DefaultMutableTreeNode)treeModel.getRoot();
                          lastItem = null;
                      }
                  }
                  else
                      parent = (DefaultMutableTreeNode)treeModel.getRoot();
                  if(lastItem == null)
                      newIndex = treeModel.getChildCount(parent);
                  else
                      newIndex = parent.getIndex(lastItem) + 1;

                  /* Let the treemodel know. */
                  treeModel.insertNodeInto(createNewNode("Added " +
                                              Integer.toString(addCount++)),
                                           parent, newIndex);
              }
          } // End of SampleTree.AddAction


          /**
            * InsertAction is used to insert a new item before the selected item.
            */
          class InsertAction extends Object implements ActionListener
          {
              /** Number of nodes that have been added. */
              public int insertCount;

              /**
                * Messaged when the user clicks on the Insert menu item.
                * Determines the selection from the Tree and inserts an item
                * after that. If nothing is selected, an item is added to
                * the root.
                */
              public void actionPerformed(ActionEvent e) {
                  int newIndex;
                  DefaultMutableTreeNode lastItem = get

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                duke J. Duke (Inactive)
                Reporter:
                jkimsunw Jeffrey Kim (Inactive)
              • Votes:
                0 Vote for this issue
                Watchers:
                0 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved:
                  Imported:
                  Indexed: