Frames | No Frames |
1: /* ToolTipManager.java -- 2: Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package javax.swing; 39: 40: import java.awt.Component; 41: import java.awt.Container; 42: import java.awt.Dimension; 43: import java.awt.Point; 44: import java.awt.event.ActionEvent; 45: import java.awt.event.ActionListener; 46: import java.awt.event.MouseAdapter; 47: import java.awt.event.MouseEvent; 48: import java.awt.event.MouseMotionListener; 49: 50: /** 51: * This class is responsible for the registration of JToolTips to Components 52: * and for displaying them when appropriate. 53: */ 54: public class ToolTipManager extends MouseAdapter implements MouseMotionListener 55: { 56: /** 57: * This ActionListener is associated with the Timer that listens to whether 58: * the JToolTip can be hidden after four seconds. 59: */ 60: protected class stillInsideTimerAction implements ActionListener 61: { 62: /** 63: * This method creates a new stillInsideTimerAction object. 64: */ 65: protected stillInsideTimerAction() 66: { 67: // Nothing to do here. 68: } 69: 70: /** 71: * This method hides the JToolTip when the Timer has finished. 72: * 73: * @param event The ActionEvent. 74: */ 75: public void actionPerformed(ActionEvent event) 76: { 77: hideTip(); 78: } 79: } 80: 81: /** 82: * This Actionlistener is associated with the Timer that listens to whether 83: * the mouse cursor has re-entered the JComponent in time for an immediate 84: * redisplay of the JToolTip. 85: */ 86: protected class outsideTimerAction implements ActionListener 87: { 88: /** 89: * This method creates a new outsideTimerAction object. 90: */ 91: protected outsideTimerAction() 92: { 93: // Nothing to do here. 94: } 95: 96: /** 97: * This method is called when the Timer that listens to whether the mouse 98: * cursor has re-entered the JComponent has run out. 99: * 100: * @param event The ActionEvent. 101: */ 102: public void actionPerformed(ActionEvent event) 103: { 104: // TODO: What should be done here, if anything? 105: } 106: } 107: 108: /** 109: * This ActionListener is associated with the Timer that listens to whether 110: * it is time for the JToolTip to be displayed after the mouse has entered 111: * the JComponent. 112: */ 113: protected class insideTimerAction implements ActionListener 114: { 115: /** 116: * This method creates a new insideTimerAction object. 117: */ 118: protected insideTimerAction() 119: { 120: // Nothing to do here. 121: } 122: 123: /** 124: * This method displays the JToolTip when the Mouse has been still for the 125: * delay. 126: * 127: * @param event The ActionEvent. 128: */ 129: public void actionPerformed(ActionEvent event) 130: { 131: showTip(); 132: } 133: } 134: 135: /** 136: * The Timer that determines whether the Mouse has been still long enough 137: * for the JToolTip to be displayed. 138: */ 139: Timer enterTimer; 140: 141: /** 142: * The Timer that determines whether the Mouse has re-entered the JComponent 143: * quickly enough for the JToolTip to be displayed immediately. 144: */ 145: Timer exitTimer; 146: 147: /** 148: * The Timer that determines whether the JToolTip has been displayed long 149: * enough for it to be hidden. 150: */ 151: Timer insideTimer; 152: 153: /** A global enabled setting for the ToolTipManager. */ 154: private transient boolean enabled = true; 155: 156: /** lightWeightPopupEnabled */ 157: protected boolean lightWeightPopupEnabled = true; 158: 159: /** heavyWeightPopupEnabled */ 160: protected boolean heavyWeightPopupEnabled = false; 161: 162: /** The shared instance of the ToolTipManager. */ 163: private static ToolTipManager shared; 164: 165: /** The current component the tooltip is being displayed for. */ 166: private static Component currentComponent; 167: 168: /** The current tooltip. */ 169: private static JToolTip currentTip; 170: 171: /** The last known position of the mouse cursor. */ 172: private static Point currentPoint; 173: 174: /** */ 175: private static Popup popup; 176: 177: /** 178: * Creates a new ToolTipManager and sets up the timers. 179: */ 180: ToolTipManager() 181: { 182: enterTimer = new Timer(750, new insideTimerAction()); 183: enterTimer.setRepeats(false); 184: 185: insideTimer = new Timer(4000, new stillInsideTimerAction()); 186: insideTimer.setRepeats(false); 187: 188: exitTimer = new Timer(500, new outsideTimerAction()); 189: exitTimer.setRepeats(false); 190: } 191: 192: /** 193: * This method returns the shared instance of ToolTipManager used by all 194: * JComponents. 195: * 196: * @return The shared instance of ToolTipManager. 197: */ 198: public static ToolTipManager sharedInstance() 199: { 200: if (shared == null) 201: shared = new ToolTipManager(); 202: 203: return shared; 204: } 205: 206: /** 207: * This method sets whether ToolTips are enabled or disabled for all 208: * JComponents. 209: * 210: * @param enabled Whether ToolTips are enabled or disabled for all 211: * JComponents. 212: */ 213: public void setEnabled(boolean enabled) 214: { 215: if (! enabled) 216: { 217: enterTimer.stop(); 218: exitTimer.stop(); 219: insideTimer.stop(); 220: } 221: 222: this.enabled = enabled; 223: } 224: 225: /** 226: * This method returns whether ToolTips are enabled. 227: * 228: * @return Whether ToolTips are enabled. 229: */ 230: public boolean isEnabled() 231: { 232: return enabled; 233: } 234: 235: /** 236: * This method returns whether LightweightToolTips are enabled. 237: * 238: * @return Whether LighweightToolTips are enabled. 239: */ 240: public boolean isLightWeightPopupEnabled() 241: { 242: return lightWeightPopupEnabled; 243: } 244: 245: /** 246: * This method sets whether LightweightToolTips are enabled. If you mix 247: * Lightweight and Heavyweight components, you must set this to false to 248: * ensure that the ToolTips popup above all other components. 249: * 250: * @param enabled Whether LightweightToolTips will be enabled. 251: */ 252: public void setLightWeightPopupEnabled(boolean enabled) 253: { 254: lightWeightPopupEnabled = enabled; 255: heavyWeightPopupEnabled = ! enabled; 256: } 257: 258: /** 259: * This method returns the initial delay before the ToolTip is shown when 260: * the mouse enters a Component. 261: * 262: * @return The initial delay before the ToolTip is shown. 263: */ 264: public int getInitialDelay() 265: { 266: return enterTimer.getDelay(); 267: } 268: 269: /** 270: * Sets the initial delay before the ToolTip is shown when the 271: * mouse enters a Component. 272: * 273: * @param delay The initial delay before the ToolTip is shown. 274: * 275: * @throws IllegalArgumentException if <code>delay</code> is less than zero. 276: */ 277: public void setInitialDelay(int delay) 278: { 279: enterTimer.setDelay(delay); 280: } 281: 282: /** 283: * This method returns the time the ToolTip will be shown before being 284: * hidden. 285: * 286: * @return The time the ToolTip will be shown before being hidden. 287: */ 288: public int getDismissDelay() 289: { 290: return insideTimer.getDelay(); 291: } 292: 293: /** 294: * Sets the time the ToolTip will be shown before being hidden. 295: * 296: * @param delay the delay (in milliseconds) before tool tips are hidden. 297: * 298: * @throws IllegalArgumentException if <code>delay</code> is less than zero. 299: */ 300: public void setDismissDelay(int delay) 301: { 302: insideTimer.setDelay(delay); 303: } 304: 305: /** 306: * This method returns the amount of delay where if the mouse re-enters a 307: * Component, the tooltip will be shown immediately. 308: * 309: * @return The reshow delay. 310: */ 311: public int getReshowDelay() 312: { 313: return exitTimer.getDelay(); 314: } 315: 316: /** 317: * Sets the amount of delay where if the mouse re-enters a 318: * Component, the tooltip will be shown immediately. 319: * 320: * @param delay The reshow delay (in milliseconds). 321: * 322: * @throws IllegalArgumentException if <code>delay</code> is less than zero. 323: */ 324: public void setReshowDelay(int delay) 325: { 326: exitTimer.setDelay(delay); 327: } 328: 329: /** 330: * This method registers a JComponent with the ToolTipManager. 331: * 332: * @param component The JComponent to register with the ToolTipManager. 333: */ 334: public void registerComponent(JComponent component) 335: { 336: component.addMouseListener(this); 337: component.addMouseMotionListener(this); 338: } 339: 340: /** 341: * This method unregisters a JComponent with the ToolTipManager. 342: * 343: * @param component The JComponent to unregister with the ToolTipManager. 344: */ 345: public void unregisterComponent(JComponent component) 346: { 347: component.removeMouseMotionListener(this); 348: component.removeMouseListener(this); 349: } 350: 351: /** 352: * This method is called whenever the mouse enters a JComponent registered 353: * with the ToolTipManager. When the mouse enters within the period of time 354: * specified by the reshow delay, the tooltip will be displayed 355: * immediately. Otherwise, it must wait for the initial delay before 356: * displaying the tooltip. 357: * 358: * @param event The MouseEvent. 359: */ 360: public void mouseEntered(MouseEvent event) 361: { 362: if (currentComponent != null 363: && getContentPaneDeepestComponent(event) == currentComponent) 364: return; 365: currentPoint = event.getPoint(); 366: 367: currentComponent = (Component) event.getSource(); 368: 369: if (exitTimer.isRunning()) 370: { 371: exitTimer.stop(); 372: showTip(); 373: return; 374: } 375: // This should always be stopped unless we have just fake-exited. 376: if (!enterTimer.isRunning()) 377: enterTimer.start(); 378: } 379: 380: /** 381: * This method is called when the mouse exits a JComponent registered with the 382: * ToolTipManager. When the mouse exits, the tooltip should be hidden 383: * immediately. 384: * 385: * @param event 386: * The MouseEvent. 387: */ 388: public void mouseExited(MouseEvent event) 389: { 390: if (getContentPaneDeepestComponent(event) == currentComponent) 391: return; 392: 393: currentPoint = event.getPoint(); 394: currentComponent = null; 395: hideTip(); 396: 397: if (! enterTimer.isRunning()) 398: exitTimer.start(); 399: if (enterTimer.isRunning()) 400: enterTimer.stop(); 401: if (insideTimer.isRunning()) 402: insideTimer.stop(); 403: } 404: 405: /** 406: * This method is called when the mouse is pressed on a JComponent 407: * registered with the ToolTipManager. When the mouse is pressed, the 408: * tooltip (if it is shown) must be hidden immediately. 409: * 410: * @param event The MouseEvent. 411: */ 412: public void mousePressed(MouseEvent event) 413: { 414: currentPoint = event.getPoint(); 415: if (enterTimer.isRunning()) 416: enterTimer.restart(); 417: else if (insideTimer.isRunning()) 418: { 419: insideTimer.stop(); 420: hideTip(); 421: } 422: } 423: 424: /** 425: * This method is called when the mouse is dragged in a JComponent 426: * registered with the ToolTipManager. 427: * 428: * @param event The MouseEvent. 429: */ 430: public void mouseDragged(MouseEvent event) 431: { 432: currentPoint = event.getPoint(); 433: if (enterTimer.isRunning()) 434: enterTimer.restart(); 435: } 436: 437: /** 438: * This method is called when the mouse is moved in a JComponent registered 439: * with the ToolTipManager. 440: * 441: * @param event The MouseEvent. 442: */ 443: public void mouseMoved(MouseEvent event) 444: { 445: currentPoint = event.getPoint(); 446: if (enterTimer.isRunning()) 447: enterTimer.restart(); 448: } 449: 450: /** 451: * This method displays the ToolTip. It can figure out the method needed to 452: * show it as well (whether to display it in heavyweight/lightweight panel 453: * or a window.) This is package-private to avoid an accessor method. 454: */ 455: void showTip() 456: { 457: if (!enabled || currentComponent == null || !currentComponent.isEnabled() 458: || !currentComponent.isShowing()) 459: { 460: popup = null; 461: return; 462: } 463: 464: if (currentTip == null || currentTip.getComponent() != currentComponent 465: && currentComponent instanceof JComponent) 466: currentTip = ((JComponent) currentComponent).createToolTip(); 467: 468: Point p = currentPoint; 469: Point cP = currentComponent.getLocationOnScreen(); 470: Dimension dims = currentTip.getPreferredSize(); 471: 472: JLayeredPane pane = null; 473: JRootPane r = ((JRootPane) SwingUtilities.getAncestorOfClass(JRootPane.class, 474: currentComponent)); 475: if (r != null) 476: pane = r.getLayeredPane(); 477: if (pane == null) 478: return; 479: 480: p.translate(cP.x, cP.y); 481: adjustLocation(p, pane, dims); 482: 483: currentTip.setBounds(0, 0, dims.width, dims.height); 484: 485: PopupFactory factory = PopupFactory.getSharedInstance(); 486: popup = factory.getPopup(currentComponent, currentTip, p.x, p.y); 487: popup.show(); 488: } 489: 490: /** 491: * Adjusts the point to a new location on the component, 492: * using the currentTip's dimensions. 493: * 494: * @param p - the point to convert. 495: * @param c - the component the point is on. 496: * @param d - the dimensions of the currentTip. 497: */ 498: private Point adjustLocation(Point p, Component c, Dimension d) 499: { 500: if (p.x + d.width > c.getWidth()) 501: p.x -= d.width; 502: if (p.x < 0) 503: p.x = 0; 504: if (p.y + d.height < c.getHeight()) 505: p.y += d.height; 506: if (p.y + d.height > c.getHeight()) 507: p.y -= d.height; 508: 509: return p; 510: } 511: 512: /** 513: * This method hides the ToolTip. 514: * This is package-private to avoid an accessor method. 515: */ 516: void hideTip() 517: { 518: if (popup != null) 519: popup.hide(); 520: } 521: 522: /** 523: * This method returns the deepest component in the content pane for the 524: * first RootPaneContainer up from the currentComponent. This method is 525: * used in conjunction with one of the mouseXXX methods. 526: * 527: * @param e The MouseEvent. 528: * 529: * @return The deepest component in the content pane. 530: */ 531: private Component getContentPaneDeepestComponent(MouseEvent e) 532: { 533: Component source = (Component) e.getSource(); 534: Container parent = (Container) SwingUtilities.getAncestorOfClass(JRootPane.class, 535: currentComponent); 536: if (parent == null) 537: return null; 538: parent = ((JRootPane) parent).getContentPane(); 539: Point p = e.getPoint(); 540: p = SwingUtilities.convertPoint(source, p, parent); 541: Component target = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y); 542: return target; 543: } 544: }