jdk/src/share/classes/javax/swing/ToolTipManager.java
changeset 2 90ce3da70b43
child 4199 151e13fd2df1
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 
       
    27 package javax.swing;
       
    28 
       
    29 import java.awt.event.*;
       
    30 import java.applet.*;
       
    31 import java.awt.*;
       
    32 import java.io.Serializable;
       
    33 import sun.swing.UIAction;
       
    34 
       
    35 /**
       
    36  * Manages all the <code>ToolTips</code> in the system.
       
    37  * <p>
       
    38  * ToolTipManager contains numerous properties for configuring how long it
       
    39  * will take for the tooltips to become visible, and how long till they
       
    40  * hide. Consider a component that has a different tooltip based on where
       
    41  * the mouse is, such as JTree. When the mouse moves into the JTree and
       
    42  * over a region that has a valid tooltip, the tooltip will become
       
    43  * visibile after <code>initialDelay</code> milliseconds. After
       
    44  * <code>dismissDelay</code> milliseconds the tooltip will be hidden. If
       
    45  * the mouse is over a region that has a valid tooltip, and the tooltip
       
    46  * is currently visible, when the mouse moves to a region that doesn't have
       
    47  * a valid tooltip the tooltip will be hidden. If the mouse then moves back
       
    48  * into a region that has a valid tooltip within <code>reshowDelay</code>
       
    49  * milliseconds, the tooltip will immediately be shown, otherwise the
       
    50  * tooltip will be shown again after <code>initialDelay</code> milliseconds.
       
    51  *
       
    52  * @see JComponent#createToolTip
       
    53  * @author Dave Moore
       
    54  * @author Rich Schiavi
       
    55  */
       
    56 public class ToolTipManager extends MouseAdapter implements MouseMotionListener  {
       
    57     Timer enterTimer, exitTimer, insideTimer;
       
    58     String toolTipText;
       
    59     Point  preferredLocation;
       
    60     JComponent insideComponent;
       
    61     MouseEvent mouseEvent;
       
    62     boolean showImmediately;
       
    63     final static ToolTipManager sharedInstance = new ToolTipManager();
       
    64     transient Popup tipWindow;
       
    65     /** The Window tip is being displayed in. This will be non-null if
       
    66      * the Window tip is in differs from that of insideComponent's Window.
       
    67      */
       
    68     private Window window;
       
    69     JToolTip tip;
       
    70 
       
    71     private Rectangle popupRect = null;
       
    72     private Rectangle popupFrameRect = null;
       
    73 
       
    74     boolean enabled = true;
       
    75     private boolean tipShowing = false;
       
    76 
       
    77     private FocusListener focusChangeListener = null;
       
    78     private MouseMotionListener moveBeforeEnterListener = null;
       
    79     private KeyListener accessibilityKeyListener = null;
       
    80 
       
    81     // PENDING(ges)
       
    82     protected boolean lightWeightPopupEnabled = true;
       
    83     protected boolean heavyWeightPopupEnabled = false;
       
    84 
       
    85     ToolTipManager() {
       
    86         enterTimer = new Timer(750, new insideTimerAction());
       
    87         enterTimer.setRepeats(false);
       
    88         exitTimer = new Timer(500, new outsideTimerAction());
       
    89         exitTimer.setRepeats(false);
       
    90         insideTimer = new Timer(4000, new stillInsideTimerAction());
       
    91         insideTimer.setRepeats(false);
       
    92 
       
    93         moveBeforeEnterListener = new MoveBeforeEnterListener();
       
    94         accessibilityKeyListener = new AccessibilityKeyListener();
       
    95     }
       
    96 
       
    97     /**
       
    98      * Enables or disables the tooltip.
       
    99      *
       
   100      * @param flag  true to enable the tip, false otherwise
       
   101      */
       
   102     public void setEnabled(boolean flag) {
       
   103         enabled = flag;
       
   104         if (!flag) {
       
   105             hideTipWindow();
       
   106         }
       
   107     }
       
   108 
       
   109     /**
       
   110      * Returns true if this object is enabled.
       
   111      *
       
   112      * @return true if this object is enabled, false otherwise
       
   113      */
       
   114     public boolean isEnabled() {
       
   115         return enabled;
       
   116     }
       
   117 
       
   118     /**
       
   119      * When displaying the <code>JToolTip</code>, the
       
   120      * <code>ToolTipManager</code> chooses to use a lightweight
       
   121      * <code>JPanel</code> if it fits. This method allows you to
       
   122      * disable this feature. You have to do disable it if your
       
   123      * application mixes light weight and heavy weights components.
       
   124      *
       
   125      * @param aFlag true if a lightweight panel is desired, false otherwise
       
   126      *
       
   127      */
       
   128     public void setLightWeightPopupEnabled(boolean aFlag){
       
   129         lightWeightPopupEnabled = aFlag;
       
   130     }
       
   131 
       
   132     /**
       
   133      * Returns true if lightweight (all-Java) <code>Tooltips</code>
       
   134      * are in use, or false if heavyweight (native peer)
       
   135      * <code>Tooltips</code> are being used.
       
   136      *
       
   137      * @return true if lightweight <code>ToolTips</code> are in use
       
   138      */
       
   139     public boolean isLightWeightPopupEnabled() {
       
   140         return lightWeightPopupEnabled;
       
   141     }
       
   142 
       
   143 
       
   144     /**
       
   145      * Specifies the initial delay value.
       
   146      *
       
   147      * @param milliseconds  the number of milliseconds to delay
       
   148      *        (after the cursor has paused) before displaying the
       
   149      *        tooltip
       
   150      * @see #getInitialDelay
       
   151      */
       
   152     public void setInitialDelay(int milliseconds) {
       
   153         enterTimer.setInitialDelay(milliseconds);
       
   154     }
       
   155 
       
   156     /**
       
   157      * Returns the initial delay value.
       
   158      *
       
   159      * @return an integer representing the initial delay value,
       
   160      *          in milliseconds
       
   161      * @see #setInitialDelay
       
   162      */
       
   163     public int getInitialDelay() {
       
   164         return enterTimer.getInitialDelay();
       
   165     }
       
   166 
       
   167     /**
       
   168      * Specifies the dismissal delay value.
       
   169      *
       
   170      * @param milliseconds  the number of milliseconds to delay
       
   171      *        before taking away the tooltip
       
   172      * @see #getDismissDelay
       
   173      */
       
   174     public void setDismissDelay(int milliseconds) {
       
   175         insideTimer.setInitialDelay(milliseconds);
       
   176     }
       
   177 
       
   178     /**
       
   179      * Returns the dismissal delay value.
       
   180      *
       
   181      * @return an integer representing the dismissal delay value,
       
   182      *          in milliseconds
       
   183      * @see #setDismissDelay
       
   184      */
       
   185     public int getDismissDelay() {
       
   186         return insideTimer.getInitialDelay();
       
   187     }
       
   188 
       
   189     /**
       
   190      * Used to specify the amount of time before the user has to wait
       
   191      * <code>initialDelay</code> milliseconds before a tooltip will be
       
   192      * shown. That is, if the tooltip is hidden, and the user moves into
       
   193      * a region of the same Component that has a valid tooltip within
       
   194      * <code>milliseconds</code> milliseconds the tooltip will immediately
       
   195      * be shown. Otherwise, if the user moves into a region with a valid
       
   196      * tooltip after <code>milliseconds</code> milliseconds, the user
       
   197      * will have to wait an additional <code>initialDelay</code>
       
   198      * milliseconds before the tooltip is shown again.
       
   199      *
       
   200      * @param milliseconds time in milliseconds
       
   201      * @see #getReshowDelay
       
   202      */
       
   203     public void setReshowDelay(int milliseconds) {
       
   204         exitTimer.setInitialDelay(milliseconds);
       
   205     }
       
   206 
       
   207     /**
       
   208      * Returns the reshow delay property.
       
   209      *
       
   210      * @return reshown delay property
       
   211      * @see #setReshowDelay
       
   212      */
       
   213     public int getReshowDelay() {
       
   214         return exitTimer.getInitialDelay();
       
   215     }
       
   216 
       
   217     void showTipWindow() {
       
   218         if(insideComponent == null || !insideComponent.isShowing())
       
   219             return;
       
   220         String mode = UIManager.getString("ToolTipManager.enableToolTipMode");
       
   221         if ("activeApplication".equals(mode)) {
       
   222             KeyboardFocusManager kfm =
       
   223                     KeyboardFocusManager.getCurrentKeyboardFocusManager();
       
   224             if (kfm.getFocusedWindow() == null) {
       
   225                 return;
       
   226             }
       
   227         }
       
   228         if (enabled) {
       
   229             Dimension size;
       
   230             Point screenLocation = insideComponent.getLocationOnScreen();
       
   231             Point location = new Point();
       
   232             GraphicsConfiguration gc;
       
   233             gc = insideComponent.getGraphicsConfiguration();
       
   234             Rectangle sBounds = gc.getBounds();
       
   235             Insets screenInsets = Toolkit.getDefaultToolkit()
       
   236                                              .getScreenInsets(gc);
       
   237             // Take into account screen insets, decrease viewport
       
   238             sBounds.x += screenInsets.left;
       
   239             sBounds.y += screenInsets.top;
       
   240             sBounds.width -= (screenInsets.left + screenInsets.right);
       
   241             sBounds.height -= (screenInsets.top + screenInsets.bottom);
       
   242         boolean leftToRight
       
   243                 = SwingUtilities.isLeftToRight(insideComponent);
       
   244 
       
   245             // Just to be paranoid
       
   246             hideTipWindow();
       
   247 
       
   248             tip = insideComponent.createToolTip();
       
   249             tip.setTipText(toolTipText);
       
   250             size = tip.getPreferredSize();
       
   251 
       
   252             if(preferredLocation != null) {
       
   253                 location.x = screenLocation.x + preferredLocation.x;
       
   254                 location.y = screenLocation.y + preferredLocation.y;
       
   255         if (!leftToRight) {
       
   256             location.x -= size.width;
       
   257         }
       
   258             } else {
       
   259                 location.x = screenLocation.x + mouseEvent.getX();
       
   260                 location.y = screenLocation.y + mouseEvent.getY() + 20;
       
   261         if (!leftToRight) {
       
   262             if(location.x - size.width>=0) {
       
   263                 location.x -= size.width;
       
   264             }
       
   265         }
       
   266 
       
   267             }
       
   268 
       
   269         // we do not adjust x/y when using awt.Window tips
       
   270         if (popupRect == null){
       
   271         popupRect = new Rectangle();
       
   272         }
       
   273         popupRect.setBounds(location.x,location.y,
       
   274                 size.width,size.height);
       
   275 
       
   276         // Fit as much of the tooltip on screen as possible
       
   277             if (location.x < sBounds.x) {
       
   278                 location.x = sBounds.x;
       
   279             }
       
   280             else if (location.x - sBounds.x + size.width > sBounds.width) {
       
   281                 location.x = sBounds.x + Math.max(0, sBounds.width - size.width)
       
   282 ;
       
   283             }
       
   284             if (location.y < sBounds.y) {
       
   285                 location.y = sBounds.y;
       
   286             }
       
   287             else if (location.y - sBounds.y + size.height > sBounds.height) {
       
   288                 location.y = sBounds.y + Math.max(0, sBounds.height - size.height);
       
   289             }
       
   290 
       
   291             PopupFactory popupFactory = PopupFactory.getSharedInstance();
       
   292 
       
   293             if (lightWeightPopupEnabled) {
       
   294         int y = getPopupFitHeight(popupRect, insideComponent);
       
   295         int x = getPopupFitWidth(popupRect,insideComponent);
       
   296         if (x>0 || y>0) {
       
   297             popupFactory.setPopupType(PopupFactory.MEDIUM_WEIGHT_POPUP);
       
   298         } else {
       
   299             popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
       
   300         }
       
   301             }
       
   302             else {
       
   303                 popupFactory.setPopupType(PopupFactory.MEDIUM_WEIGHT_POPUP);
       
   304             }
       
   305         tipWindow = popupFactory.getPopup(insideComponent, tip,
       
   306                           location.x,
       
   307                           location.y);
       
   308             popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
       
   309 
       
   310         tipWindow.show();
       
   311 
       
   312             Window componentWindow = SwingUtilities.windowForComponent(
       
   313                                                     insideComponent);
       
   314 
       
   315             window = SwingUtilities.windowForComponent(tip);
       
   316             if (window != null && window != componentWindow) {
       
   317                 window.addMouseListener(this);
       
   318             }
       
   319             else {
       
   320                 window = null;
       
   321             }
       
   322 
       
   323             insideTimer.start();
       
   324         tipShowing = true;
       
   325         }
       
   326     }
       
   327 
       
   328     void hideTipWindow() {
       
   329         if (tipWindow != null) {
       
   330             if (window != null) {
       
   331                 window.removeMouseListener(this);
       
   332                 window = null;
       
   333             }
       
   334             tipWindow.hide();
       
   335             tipWindow = null;
       
   336             tipShowing = false;
       
   337             tip = null;
       
   338             insideTimer.stop();
       
   339         }
       
   340     }
       
   341 
       
   342     /**
       
   343      * Returns a shared <code>ToolTipManager</code> instance.
       
   344      *
       
   345      * @return a shared <code>ToolTipManager</code> object
       
   346      */
       
   347     public static ToolTipManager sharedInstance() {
       
   348         return sharedInstance;
       
   349     }
       
   350 
       
   351     // add keylistener here to trigger tip for access
       
   352     /**
       
   353      * Registers a component for tooltip management.
       
   354      * <p>
       
   355      * This will register key bindings to show and hide the tooltip text
       
   356      * only if <code>component</code> has focus bindings. This is done
       
   357      * so that components that are not normally focus traversable, such
       
   358      * as <code>JLabel</code>, are not made focus traversable as a result
       
   359      * of invoking this method.
       
   360      *
       
   361      * @param component  a <code>JComponent</code> object to add
       
   362      * @see JComponent#isFocusTraversable
       
   363      */
       
   364     public void registerComponent(JComponent component) {
       
   365         component.removeMouseListener(this);
       
   366         component.addMouseListener(this);
       
   367         component.removeMouseMotionListener(moveBeforeEnterListener);
       
   368         component.addMouseMotionListener(moveBeforeEnterListener);
       
   369         component.removeKeyListener(accessibilityKeyListener);
       
   370         component.addKeyListener(accessibilityKeyListener);
       
   371     }
       
   372 
       
   373     /**
       
   374      * Removes a component from tooltip control.
       
   375      *
       
   376      * @param component  a <code>JComponent</code> object to remove
       
   377      */
       
   378     public void unregisterComponent(JComponent component) {
       
   379         component.removeMouseListener(this);
       
   380         component.removeMouseMotionListener(moveBeforeEnterListener);
       
   381         component.removeKeyListener(accessibilityKeyListener);
       
   382     }
       
   383 
       
   384     // implements java.awt.event.MouseListener
       
   385     /**
       
   386      *  Called when the mouse enters the region of a component.
       
   387      *  This determines whether the tool tip should be shown.
       
   388      *
       
   389      *  @param event  the event in question
       
   390      */
       
   391     public void mouseEntered(MouseEvent event) {
       
   392         initiateToolTip(event);
       
   393     }
       
   394 
       
   395     private void initiateToolTip(MouseEvent event) {
       
   396         if (event.getSource() == window) {
       
   397             return;
       
   398         }
       
   399         JComponent component = (JComponent)event.getSource();
       
   400         component.removeMouseMotionListener(moveBeforeEnterListener);
       
   401 
       
   402         exitTimer.stop();
       
   403 
       
   404         Point location = event.getPoint();
       
   405         // ensure tooltip shows only in proper place
       
   406         if (location.x < 0 ||
       
   407             location.x >=component.getWidth() ||
       
   408             location.y < 0 ||
       
   409             location.y >= component.getHeight()) {
       
   410             return;
       
   411         }
       
   412 
       
   413         if (insideComponent != null) {
       
   414             enterTimer.stop();
       
   415         }
       
   416         // A component in an unactive internal frame is sent two
       
   417         // mouseEntered events, make sure we don't end up adding
       
   418         // ourselves an extra time.
       
   419         component.removeMouseMotionListener(this);
       
   420         component.addMouseMotionListener(this);
       
   421 
       
   422         boolean sameComponent = (insideComponent == component);
       
   423 
       
   424         insideComponent = component;
       
   425     if (tipWindow != null){
       
   426             mouseEvent = event;
       
   427             if (showImmediately) {
       
   428                 String newToolTipText = component.getToolTipText(event);
       
   429                 Point newPreferredLocation = component.getToolTipLocation(
       
   430                                                          event);
       
   431                 boolean sameLoc = (preferredLocation != null) ?
       
   432                             preferredLocation.equals(newPreferredLocation) :
       
   433                             (newPreferredLocation == null);
       
   434 
       
   435                 if (!sameComponent || !toolTipText.equals(newToolTipText) ||
       
   436                          !sameLoc) {
       
   437                     toolTipText = newToolTipText;
       
   438                     preferredLocation = newPreferredLocation;
       
   439                     showTipWindow();
       
   440                 }
       
   441             } else {
       
   442                 enterTimer.start();
       
   443             }
       
   444         }
       
   445     }
       
   446 
       
   447     // implements java.awt.event.MouseListener
       
   448     /**
       
   449      *  Called when the mouse exits the region of a component.
       
   450      *  Any tool tip showing should be hidden.
       
   451      *
       
   452      *  @param event  the event in question
       
   453      */
       
   454     public void mouseExited(MouseEvent event) {
       
   455         boolean shouldHide = true;
       
   456         if (insideComponent == null) {
       
   457             // Drag exit
       
   458         }
       
   459         if (window != null && event.getSource() == window) {
       
   460           // if we get an exit and have a heavy window
       
   461           // we need to check if it if overlapping the inside component
       
   462             Container insideComponentWindow = insideComponent.getTopLevelAncestor();
       
   463             // insideComponent may be removed after tooltip is made visible
       
   464             if (insideComponentWindow != null) {
       
   465                 Point location = event.getPoint();
       
   466                 SwingUtilities.convertPointToScreen(location, window);
       
   467 
       
   468                 location.x -= insideComponentWindow.getX();
       
   469                 location.y -= insideComponentWindow.getY();
       
   470 
       
   471                 location = SwingUtilities.convertPoint(null, location, insideComponent);
       
   472                 if (location.x >= 0 && location.x < insideComponent.getWidth() &&
       
   473                         location.y >= 0 && location.y < insideComponent.getHeight()) {
       
   474                     shouldHide = false;
       
   475                 } else {
       
   476                     shouldHide = true;
       
   477                 }
       
   478             }
       
   479         } else if(event.getSource() == insideComponent && tipWindow != null) {
       
   480             Window win = SwingUtilities.getWindowAncestor(insideComponent);
       
   481             if (win != null) {  // insideComponent may have been hidden (e.g. in a menu)
       
   482                 Point location = SwingUtilities.convertPoint(insideComponent,
       
   483                                                              event.getPoint(),
       
   484                                                              win);
       
   485                 Rectangle bounds = insideComponent.getTopLevelAncestor().getBounds();
       
   486                 location.x += bounds.x;
       
   487                 location.y += bounds.y;
       
   488 
       
   489                 Point loc = new Point(0, 0);
       
   490                 SwingUtilities.convertPointToScreen(loc, tip);
       
   491                 bounds.x = loc.x;
       
   492                 bounds.y = loc.y;
       
   493                 bounds.width = tip.getWidth();
       
   494                 bounds.height = tip.getHeight();
       
   495 
       
   496                 if (location.x >= bounds.x && location.x < (bounds.x + bounds.width) &&
       
   497                     location.y >= bounds.y && location.y < (bounds.y + bounds.height)) {
       
   498                     shouldHide = false;
       
   499                 } else {
       
   500                     shouldHide = true;
       
   501                 }
       
   502             }
       
   503         }
       
   504 
       
   505         if (shouldHide) {
       
   506             enterTimer.stop();
       
   507         if (insideComponent != null) {
       
   508                 insideComponent.removeMouseMotionListener(this);
       
   509             }
       
   510             insideComponent = null;
       
   511             toolTipText = null;
       
   512             mouseEvent = null;
       
   513             hideTipWindow();
       
   514             exitTimer.restart();
       
   515         }
       
   516     }
       
   517 
       
   518     // implements java.awt.event.MouseListener
       
   519     /**
       
   520      *  Called when the mouse is pressed.
       
   521      *  Any tool tip showing should be hidden.
       
   522      *
       
   523      *  @param event  the event in question
       
   524      */
       
   525     public void mousePressed(MouseEvent event) {
       
   526         hideTipWindow();
       
   527         enterTimer.stop();
       
   528         showImmediately = false;
       
   529         insideComponent = null;
       
   530         mouseEvent = null;
       
   531     }
       
   532 
       
   533     // implements java.awt.event.MouseMotionListener
       
   534     /**
       
   535      *  Called when the mouse is pressed and dragged.
       
   536      *  Does nothing.
       
   537      *
       
   538      *  @param event  the event in question
       
   539      */
       
   540     public void mouseDragged(MouseEvent event) {
       
   541     }
       
   542 
       
   543     // implements java.awt.event.MouseMotionListener
       
   544     /**
       
   545      *  Called when the mouse is moved.
       
   546      *  Determines whether the tool tip should be displayed.
       
   547      *
       
   548      *  @param event  the event in question
       
   549      */
       
   550     public void mouseMoved(MouseEvent event) {
       
   551         if (tipShowing) {
       
   552             checkForTipChange(event);
       
   553         }
       
   554         else if (showImmediately) {
       
   555             JComponent component = (JComponent)event.getSource();
       
   556             toolTipText = component.getToolTipText(event);
       
   557             if (toolTipText != null) {
       
   558                 preferredLocation = component.getToolTipLocation(event);
       
   559                 mouseEvent = event;
       
   560                 insideComponent = component;
       
   561                 exitTimer.stop();
       
   562                 showTipWindow();
       
   563             }
       
   564         }
       
   565         else {
       
   566             // Lazily lookup the values from within insideTimerAction
       
   567             insideComponent = (JComponent)event.getSource();
       
   568             mouseEvent = event;
       
   569             toolTipText = null;
       
   570             enterTimer.restart();
       
   571         }
       
   572     }
       
   573 
       
   574     /**
       
   575      * Checks to see if the tooltip needs to be changed in response to
       
   576      * the MouseMoved event <code>event</code>.
       
   577      */
       
   578     private void checkForTipChange(MouseEvent event) {
       
   579         JComponent component = (JComponent)event.getSource();
       
   580         String newText = component.getToolTipText(event);
       
   581         Point  newPreferredLocation = component.getToolTipLocation(event);
       
   582 
       
   583         if (newText != null || newPreferredLocation != null) {
       
   584             mouseEvent = event;
       
   585             if (((newText != null && newText.equals(toolTipText)) || newText == null) &&
       
   586                 ((newPreferredLocation != null && newPreferredLocation.equals(preferredLocation))
       
   587                  || newPreferredLocation == null)) {
       
   588                 if (tipWindow != null) {
       
   589                     insideTimer.restart();
       
   590                 } else {
       
   591                     enterTimer.restart();
       
   592                 }
       
   593             } else {
       
   594                 toolTipText = newText;
       
   595                 preferredLocation = newPreferredLocation;
       
   596                 if (showImmediately) {
       
   597                     hideTipWindow();
       
   598                     showTipWindow();
       
   599                     exitTimer.stop();
       
   600                 } else {
       
   601                     enterTimer.restart();
       
   602                 }
       
   603             }
       
   604         } else {
       
   605             toolTipText = null;
       
   606             preferredLocation = null;
       
   607             mouseEvent = null;
       
   608             insideComponent = null;
       
   609             hideTipWindow();
       
   610             enterTimer.stop();
       
   611             exitTimer.restart();
       
   612         }
       
   613     }
       
   614 
       
   615     protected class insideTimerAction implements ActionListener {
       
   616         public void actionPerformed(ActionEvent e) {
       
   617             if(insideComponent != null && insideComponent.isShowing()) {
       
   618                 // Lazy lookup
       
   619                 if (toolTipText == null && mouseEvent != null) {
       
   620                     toolTipText = insideComponent.getToolTipText(mouseEvent);
       
   621                     preferredLocation = insideComponent.getToolTipLocation(
       
   622                                               mouseEvent);
       
   623                 }
       
   624                 if(toolTipText != null) {
       
   625                     showImmediately = true;
       
   626                     showTipWindow();
       
   627                 }
       
   628                 else {
       
   629                     insideComponent = null;
       
   630                     toolTipText = null;
       
   631                     preferredLocation = null;
       
   632                     mouseEvent = null;
       
   633                     hideTipWindow();
       
   634                 }
       
   635             }
       
   636         }
       
   637     }
       
   638 
       
   639     protected class outsideTimerAction implements ActionListener {
       
   640         public void actionPerformed(ActionEvent e) {
       
   641             showImmediately = false;
       
   642         }
       
   643     }
       
   644 
       
   645     protected class stillInsideTimerAction implements ActionListener {
       
   646         public void actionPerformed(ActionEvent e) {
       
   647             hideTipWindow();
       
   648             enterTimer.stop();
       
   649             showImmediately = false;
       
   650             insideComponent = null;
       
   651             mouseEvent = null;
       
   652         }
       
   653     }
       
   654 
       
   655   /* This listener is registered when the tooltip is first registered
       
   656    * on a component in order to catch the situation where the tooltip
       
   657    * was turned on while the mouse was already within the bounds of
       
   658    * the component.  This way, the tooltip will be initiated on a
       
   659    * mouse-entered or mouse-moved, whichever occurs first.  Once the
       
   660    * tooltip has been initiated, we can remove this listener and rely
       
   661    * solely on mouse-entered to initiate the tooltip.
       
   662    */
       
   663     private class MoveBeforeEnterListener extends MouseMotionAdapter {
       
   664         public void mouseMoved(MouseEvent e) {
       
   665             initiateToolTip(e);
       
   666         }
       
   667     }
       
   668 
       
   669     static Frame frameForComponent(Component component) {
       
   670         while (!(component instanceof Frame)) {
       
   671             component = component.getParent();
       
   672         }
       
   673         return (Frame)component;
       
   674     }
       
   675 
       
   676   private FocusListener createFocusChangeListener(){
       
   677     return new FocusAdapter(){
       
   678       public void focusLost(FocusEvent evt){
       
   679         hideTipWindow();
       
   680         insideComponent = null;
       
   681         JComponent c = (JComponent)evt.getSource();
       
   682         c.removeFocusListener(focusChangeListener);
       
   683       }
       
   684     };
       
   685   }
       
   686 
       
   687   // Returns: 0 no adjust
       
   688   //         -1 can't fit
       
   689   //         >0 adjust value by amount returned
       
   690   private int getPopupFitWidth(Rectangle popupRectInScreen, Component invoker){
       
   691     if (invoker != null){
       
   692       Container parent;
       
   693       for (parent = invoker.getParent(); parent != null; parent = parent.getParent()){
       
   694         // fix internal frame size bug: 4139087 - 4159012
       
   695         if(parent instanceof JFrame || parent instanceof JDialog ||
       
   696            parent instanceof JWindow) { // no check for awt.Frame since we use Heavy tips
       
   697           return getWidthAdjust(parent.getBounds(),popupRectInScreen);
       
   698         } else if (parent instanceof JApplet || parent instanceof JInternalFrame) {
       
   699           if (popupFrameRect == null){
       
   700             popupFrameRect = new Rectangle();
       
   701           }
       
   702           Point p = parent.getLocationOnScreen();
       
   703           popupFrameRect.setBounds(p.x,p.y,
       
   704                                    parent.getBounds().width,
       
   705                                    parent.getBounds().height);
       
   706           return getWidthAdjust(popupFrameRect,popupRectInScreen);
       
   707         }
       
   708       }
       
   709     }
       
   710     return 0;
       
   711   }
       
   712 
       
   713   // Returns:  0 no adjust
       
   714   //          >0 adjust by value return
       
   715   private int getPopupFitHeight(Rectangle popupRectInScreen, Component invoker){
       
   716     if (invoker != null){
       
   717       Container parent;
       
   718       for (parent = invoker.getParent(); parent != null; parent = parent.getParent()){
       
   719         if(parent instanceof JFrame || parent instanceof JDialog ||
       
   720            parent instanceof JWindow) {
       
   721           return getHeightAdjust(parent.getBounds(),popupRectInScreen);
       
   722         } else if (parent instanceof JApplet || parent instanceof JInternalFrame) {
       
   723           if (popupFrameRect == null){
       
   724             popupFrameRect = new Rectangle();
       
   725           }
       
   726           Point p = parent.getLocationOnScreen();
       
   727           popupFrameRect.setBounds(p.x,p.y,
       
   728                                    parent.getBounds().width,
       
   729                                    parent.getBounds().height);
       
   730           return getHeightAdjust(popupFrameRect,popupRectInScreen);
       
   731         }
       
   732       }
       
   733     }
       
   734     return 0;
       
   735   }
       
   736 
       
   737   private int getHeightAdjust(Rectangle a, Rectangle b){
       
   738     if (b.y >= a.y && (b.y + b.height) <= (a.y + a.height))
       
   739       return 0;
       
   740     else
       
   741       return (((b.y + b.height) - (a.y + a.height)) + 5);
       
   742   }
       
   743 
       
   744   // Return the number of pixels over the edge we are extending.
       
   745   // If we are over the edge the ToolTipManager can adjust.
       
   746   // REMIND: what if the Tooltip is just too big to fit at all - we currently will just clip
       
   747   private int getWidthAdjust(Rectangle a, Rectangle b){
       
   748     //    System.out.println("width b.x/b.width: " + b.x + "/" + b.width +
       
   749     //                 "a.x/a.width: " + a.x + "/" + a.width);
       
   750     if (b.x >= a.x && (b.x + b.width) <= (a.x + a.width)){
       
   751       return 0;
       
   752     }
       
   753     else {
       
   754       return (((b.x + b.width) - (a.x +a.width)) + 5);
       
   755     }
       
   756   }
       
   757 
       
   758 
       
   759     //
       
   760     // Actions
       
   761     //
       
   762     private void show(JComponent source) {
       
   763         if (tipWindow != null) { // showing we unshow
       
   764             hideTipWindow();
       
   765             insideComponent = null;
       
   766         }
       
   767         else {
       
   768             hideTipWindow(); // be safe
       
   769             enterTimer.stop();
       
   770             exitTimer.stop();
       
   771             insideTimer.stop();
       
   772             insideComponent = source;
       
   773             if (insideComponent != null){
       
   774                 toolTipText = insideComponent.getToolTipText();
       
   775                 preferredLocation = new Point(10,insideComponent.getHeight()+
       
   776                                               10);  // manual set
       
   777                 showTipWindow();
       
   778                 // put a focuschange listener on to bring the tip down
       
   779                 if (focusChangeListener == null){
       
   780                     focusChangeListener = createFocusChangeListener();
       
   781                 }
       
   782                 insideComponent.addFocusListener(focusChangeListener);
       
   783             }
       
   784         }
       
   785     }
       
   786 
       
   787     private void hide(JComponent source) {
       
   788         hideTipWindow();
       
   789         source.removeFocusListener(focusChangeListener);
       
   790         preferredLocation = null;
       
   791         insideComponent = null;
       
   792     }
       
   793 
       
   794     /* This listener is registered when the tooltip is first registered
       
   795      * on a component in order to process accessibility keybindings.
       
   796      * This will apply globally across L&F
       
   797      *
       
   798      * Post Tip: Ctrl+F1
       
   799      * Unpost Tip: Esc and Ctrl+F1
       
   800      */
       
   801     private class AccessibilityKeyListener extends KeyAdapter {
       
   802         public void keyPressed(KeyEvent e) {
       
   803             if (!e.isConsumed()) {
       
   804                 JComponent source = (JComponent) e.getComponent();
       
   805                 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
       
   806                     if (tipWindow != null) {
       
   807                         hide(source);
       
   808                         e.consume();
       
   809                     }
       
   810                 } else if (e.getKeyCode() == KeyEvent.VK_F1
       
   811                         && e.getModifiers() == Event.CTRL_MASK) {
       
   812                     // Shown tooltip will be hidden
       
   813                     ToolTipManager.this.show(source);
       
   814                     e.consume();
       
   815                 }
       
   816             }
       
   817         }
       
   818     }
       
   819 }