This adds support for dynamic, context-sensitive tooltips in Swing. This
patch is accompanied by a regression test in Mauve.
2006-10-14 Roman Kennke <[EMAIL PROTECTED]>
PR 27957
* javax/swing/JComponent.java
(toolTipText): Removed field.
(createToolTip): Don't set tooltip text here. This is done
in the ToolTipManager.
(setToolTipText): Set tooltip text as client property.
(getToolTipText): Get tooltip text from client property.
* javax/swing/ToolTipManager.java
(currentComponent): Made field non-static and of type JComponent.
(currentPoint): Made field non-static.
(currentTip): Made field non-static.
(popup): Made field non-static.
(toolTipText): New field. Stores the current tooltip text.
(checkTipUpdate): New helper method. Checks for updates of
the tooltip text and triggers the appropriate actions.
(getContentPaneDeepestComponent): Removed unneeded casts.
(mouseEntered): Removed unneeded cast. Initially fetch tooltip
text from component.
(mouseMoved): Check for tooltip text updates.
(showTip): Set tooltip text from current setting.
/Roman
Index: javax/swing/JComponent.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JComponent.java,v
retrieving revision 1.146
diff -u -1 -5 -r1.146 JComponent.java
--- javax/swing/JComponent.java 12 Oct 2006 10:20:52 -0000 1.146
+++ javax/swing/JComponent.java 14 Oct 2006 13:22:54 -0000
@@ -529,38 +529,30 @@
*
* @see #getAlignmentY
* @see #setAlignmentY
* @see javax.swing.OverlayLayout
* @see javax.swing.BoxLayout
*/
float alignmentY = -1.0F;
/**
* The border painted around this component.
*
* @see #paintBorder
*/
Border border;
- /**
- * The text to show in the tooltip associated with this component.
- *
- * @see #setToolTipText
- * @see #getToolTipText()
- */
- String toolTipText;
-
/**
* The popup menu for the component.
*
* @see #getComponentPopupMenu()
* @see #setComponentPopupMenu(JPopupMenu)
*/
JPopupMenu componentPopupMenu;
/**
* A flag that controls whether the [EMAIL PROTECTED] #getComponentPopupMenu()} method
* looks to the component's parent when the <code>componentPopupMenu</code>
* field is <code>null</code>.
*/
boolean inheritsPopupMenu;
@@ -1427,97 +1419,98 @@
}
}
/**
* Return the <code>toolTip</code> property of this component, creating it and
* setting it if it is currently <code>null</code>. This method can be
* overridden in subclasses which wish to control the exact form of
* tooltip created.
*
* @return The current toolTip
*/
public JToolTip createToolTip()
{
JToolTip toolTip = new JToolTip();
toolTip.setComponent(this);
- toolTip.setTipText(toolTipText);
-
return toolTip;
}
/**
- * Return the location at which the [EMAIL PROTECTED] #toolTipText} property should be
- * displayed, when triggered by a particular mouse event.
+ * Return the location at which the <code>toolTipText</code> property should
+ * be displayed, when triggered by a particular mouse event.
*
* @param event The event the tooltip is being presented in response to
*
* @return The point at which to display a tooltip, or <code>null</code>
* if swing is to choose a default location.
*/
public Point getToolTipLocation(MouseEvent event)
{
return null;
}
/**
- * Set the value of the [EMAIL PROTECTED] #toolTipText} property.
+ * Set the tooltip text for this component. If a non-<code>null</code>
+ * value is set, this component is registered in the
+ * <code>ToolTipManager</code> in order to turn on tooltips for this
+ * component. If a <code>null</code> value is set, tooltips are turne off
+ * for this component.
*
- * @param text The new property value
+ * @param text the tooltip text for this component
*
* @see #getToolTipText()
+ * @see #getToolTipText(MouseEvent)
*/
public void setToolTipText(String text)
{
+ String old = getToolTipText();
+ putClientProperty(TOOL_TIP_TEXT_KEY, text);
+ ToolTipManager ttm = ToolTipManager.sharedInstance();
if (text == null)
- {
- ToolTipManager.sharedInstance().unregisterComponent(this);
- toolTipText = null;
- return;
- }
-
- // XXX: The tip text doesn't get updated unless you set it to null
- // and then to something not-null. This is consistent with the behaviour
- // of Sun's ToolTipManager.
-
- String oldText = toolTipText;
- toolTipText = text;
-
- if (oldText == null)
- ToolTipManager.sharedInstance().registerComponent(this);
+ ttm.unregisterComponent(this);
+ else if (old == null)
+ ttm.registerComponent(this);
}
/**
- * Get the value of the [EMAIL PROTECTED] #toolTipText} property.
+ * Returns the current tooltip text for this component, or <code>null</code>
+ * if none has been set.
*
- * @return The current property value
+ * @return the current tooltip text for this component, or <code>null</code>
+ * if none has been set
*
* @see #setToolTipText
+ * @see #getToolTipText(MouseEvent)
*/
public String getToolTipText()
{
- return toolTipText;
+ return (String) getClientProperty(TOOL_TIP_TEXT_KEY);
}
/**
- * Get the value of the [EMAIL PROTECTED] #toolTipText} property, in response to a
- * particular mouse event.
+ * Returns the tooltip text for this component for a particular mouse
+ * event. This can be used to support context sensitive tooltips that can
+ * change with the mouse location. By default this returns the static
+ * tooltip text returned by [EMAIL PROTECTED] #getToolTipText()}.
*
- * @param event The mouse event which triggered the tooltip
+ * @param event the mouse event which triggered the tooltip
*
- * @return The current property value
+ * @return the tooltip text for this component for a particular mouse
+ * event
*
* @see #setToolTipText
+ * @see #getToolTipText()
*/
public String getToolTipText(MouseEvent event)
{
return getToolTipText();
}
/**
* Returns the flag that controls whether or not the component inherits its
* parent's popup menu when no popup menu is specified for this component.
*
* @return A boolean.
*
* @since 1.5
*
* @see #setInheritsPopupMenu(boolean)
Index: javax/swing/ToolTipManager.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/ToolTipManager.java,v
retrieving revision 1.33
diff -u -1 -5 -r1.33 ToolTipManager.java
--- javax/swing/ToolTipManager.java 9 Jul 2006 20:57:45 -0000 1.33
+++ javax/swing/ToolTipManager.java 14 Oct 2006 13:22:54 -0000
@@ -151,40 +151,45 @@
Timer insideTimer;
/** A global enabled setting for the ToolTipManager. */
private transient boolean enabled = true;
/** lightWeightPopupEnabled */
protected boolean lightWeightPopupEnabled = true;
/** heavyWeightPopupEnabled */
protected boolean heavyWeightPopupEnabled = false;
/** The shared instance of the ToolTipManager. */
private static ToolTipManager shared;
/** The current component the tooltip is being displayed for. */
- private static Component currentComponent;
+ private JComponent currentComponent;
/** The current tooltip. */
- private static JToolTip currentTip;
+ private JToolTip currentTip;
+
+ /**
+ * The tooltip text.
+ */
+ private String toolTipText;
/** The last known position of the mouse cursor. */
- private static Point currentPoint;
-
+ private Point currentPoint;
+
/** */
- private static Popup popup;
+ private Popup popup;
/**
* Creates a new ToolTipManager and sets up the timers.
*/
ToolTipManager()
{
enterTimer = new Timer(750, new insideTimerAction());
enterTimer.setRepeats(false);
insideTimer = new Timer(4000, new stillInsideTimerAction());
insideTimer.setRepeats(false);
exitTimer = new Timer(500, new outsideTimerAction());
exitTimer.setRepeats(false);
}
@@ -352,32 +357,32 @@
* This method is called whenever the mouse enters a JComponent registered
* with the ToolTipManager. When the mouse enters within the period of time
* specified by the reshow delay, the tooltip will be displayed
* immediately. Otherwise, it must wait for the initial delay before
* displaying the tooltip.
*
* @param event The MouseEvent.
*/
public void mouseEntered(MouseEvent event)
{
if (currentComponent != null
&& getContentPaneDeepestComponent(event) == currentComponent)
return;
currentPoint = event.getPoint();
- currentComponent = (Component) event.getSource();
-
+ currentComponent = (JComponent) event.getSource();
+ toolTipText = currentComponent.getToolTipText(event);
if (exitTimer.isRunning())
{
exitTimer.stop();
showTip();
return;
}
// This should always be stopped unless we have just fake-exited.
if (!enterTimer.isRunning())
enterTimer.start();
}
/**
* This method is called when the mouse exits a JComponent registered with the
* ToolTipManager. When the mouse exits, the tooltip should be hidden
* immediately.
@@ -431,51 +436,95 @@
{
currentPoint = event.getPoint();
if (enterTimer.isRunning())
enterTimer.restart();
}
/**
* This method is called when the mouse is moved in a JComponent registered
* with the ToolTipManager.
*
* @param event The MouseEvent.
*/
public void mouseMoved(MouseEvent event)
{
currentPoint = event.getPoint();
- if (enterTimer.isRunning())
- enterTimer.restart();
+ if (currentTip != null && currentTip.isShowing())
+ checkTipUpdate(event);
+ else
+ {
+ if (enterTimer.isRunning())
+ enterTimer.restart();
+ }
+ }
+
+ /**
+ * Checks if the tooltip's text or location changes when the mouse is moved
+ * over the component.
+ */
+ private void checkTipUpdate(MouseEvent ev)
+ {
+ JComponent comp = (JComponent) ev.getSource();
+ String newText = comp.getToolTipText(ev);
+ String oldText = toolTipText;
+ if (newText != null)
+ {
+ if (((newText != null && newText.equals(oldText)) || newText == null))
+ {
+ // No change at all. Restart timers.
+ if (popup == null)
+ enterTimer.restart();
+ else
+ insideTimer.restart();
+ }
+ else
+ {
+ // Update the tooltip.
+ toolTipText = newText;
+ hideTip();
+ showTip();
+ exitTimer.stop();
+ }
+ }
+ else
+ {
+ // Hide tooltip.
+ currentTip = null;
+ currentPoint = null;
+ hideTip();
+ enterTimer.stop();
+ exitTimer.stop();
+ }
}
/**
* This method displays the ToolTip. It can figure out the method needed to
* show it as well (whether to display it in heavyweight/lightweight panel
* or a window.) This is package-private to avoid an accessor method.
*/
void showTip()
{
if (!enabled || currentComponent == null || !currentComponent.isEnabled()
|| !currentComponent.isShowing())
{
popup = null;
return;
}
- if (currentTip == null || currentTip.getComponent() != currentComponent
- && currentComponent instanceof JComponent)
- currentTip = ((JComponent) currentComponent).createToolTip();
+ if (currentTip == null || currentTip.getComponent() != currentComponent)
+ currentTip = currentComponent.createToolTip();
+ currentTip.setTipText(toolTipText);
Point p = currentPoint;
Point cP = currentComponent.getLocationOnScreen();
Dimension dims = currentTip.getPreferredSize();
JLayeredPane pane = null;
JRootPane r = ((JRootPane) SwingUtilities.getAncestorOfClass(JRootPane.class,
currentComponent));
if (r != null)
pane = r.getLayeredPane();
if (pane == null)
return;
p.translate(cP.x, cP.y);
adjustLocation(p, pane, dims);
@@ -519,26 +568,26 @@
popup.hide();
}
/**
* This method returns the deepest component in the content pane for the
* first RootPaneContainer up from the currentComponent. This method is
* used in conjunction with one of the mouseXXX methods.
*
* @param e The MouseEvent.
*
* @return The deepest component in the content pane.
*/
private Component getContentPaneDeepestComponent(MouseEvent e)
{
Component source = (Component) e.getSource();
- Container parent = (Container) SwingUtilities.getAncestorOfClass(JRootPane.class,
- currentComponent);
+ Container parent = SwingUtilities.getAncestorOfClass(JRootPane.class,
+ currentComponent);
if (parent == null)
return null;
parent = ((JRootPane) parent).getContentPane();
Point p = e.getPoint();
p = SwingUtilities.convertPoint(source, p, parent);
Component target = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y);
return target;
}
}