GNU Classpath (0.19) | ||
Frames | No Frames |
1: /* ToolTipManager.java -- 2: Copyright (C) 2002, 2004 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.FlowLayout; 44: import java.awt.LayoutManager; 45: import java.awt.Panel; 46: import java.awt.Point; 47: import java.awt.event.ActionEvent; 48: import java.awt.event.ActionListener; 49: import java.awt.event.MouseAdapter; 50: import java.awt.event.MouseEvent; 51: import java.awt.event.MouseMotionListener; 52: 53: /** 54: * This class is responsible for the registration of JToolTips to Components 55: * and for displaying them when appropriate. 56: */ 57: public class ToolTipManager extends MouseAdapter implements MouseMotionListener 58: { 59: /** 60: * This ActionListener is associated with the Timer that listens to whether 61: * the JToolTip can be hidden after four seconds. 62: */ 63: protected class stillInsideTimerAction implements ActionListener 64: { 65: /** 66: * This method creates a new stillInsideTimerAction object. 67: */ 68: protected stillInsideTimerAction() 69: { 70: // Nothing to do here. 71: } 72: 73: /** 74: * This method hides the JToolTip when the Timer has finished. 75: * 76: * @param event The ActionEvent. 77: */ 78: public void actionPerformed(ActionEvent event) 79: { 80: hideTip(); 81: } 82: } 83: 84: /** 85: * This Actionlistener is associated with the Timer that listens to whether 86: * the mouse cursor has re-entered the JComponent in time for an immediate 87: * redisplay of the JToolTip. 88: */ 89: protected class outsideTimerAction implements ActionListener 90: { 91: /** 92: * This method creates a new outsideTimerAction object. 93: */ 94: protected outsideTimerAction() 95: { 96: // Nothing to do here. 97: } 98: 99: /** 100: * This method is called when the Timer that listens to whether the mouse 101: * cursor has re-entered the JComponent has run out. 102: * 103: * @param event The ActionEvent. 104: */ 105: public void actionPerformed(ActionEvent event) 106: { 107: // TODO: What should be done here, if anything? 108: } 109: } 110: 111: /** 112: * This ActionListener is associated with the Timer that listens to whether 113: * it is time for the JToolTip to be displayed after the mouse has entered 114: * the JComponent. 115: */ 116: protected class insideTimerAction implements ActionListener 117: { 118: /** 119: * This method creates a new insideTimerAction object. 120: */ 121: protected insideTimerAction() 122: { 123: // Nothing to do here. 124: } 125: 126: /** 127: * This method displays the JToolTip when the Mouse has been still for the 128: * delay. 129: * 130: * @param event The ActionEvent. 131: */ 132: public void actionPerformed(ActionEvent event) 133: { 134: showTip(); 135: } 136: } 137: 138: /** 139: * The Timer that determines whether the Mouse has been still long enough 140: * for the JToolTip to be displayed. 141: */ 142: Timer enterTimer; 143: 144: /** 145: * The Timer that determines whether the Mouse has re-entered the JComponent 146: * quickly enough for the JToolTip to be displayed immediately. 147: */ 148: Timer exitTimer; 149: 150: /** 151: * The Timer that determines whether the JToolTip has been displayed long 152: * enough for it to be hidden. 153: */ 154: Timer insideTimer; 155: 156: /** A global enabled setting for the ToolTipManager. */ 157: private transient boolean enabled = true; 158: 159: /** lightWeightPopupEnabled */ 160: protected boolean lightWeightPopupEnabled = true; 161: 162: /** heavyWeightPopupEnabled */ 163: protected boolean heavyWeightPopupEnabled = false; 164: 165: /** The shared instance of the ToolTipManager. */ 166: private static ToolTipManager shared; 167: 168: /** The current component the tooltip is being displayed for. */ 169: private static Component currentComponent; 170: 171: /** The current tooltip. */ 172: private static JToolTip currentTip; 173: 174: /** The last known position of the mouse cursor. */ 175: private static Point currentPoint; 176: 177: /** 178: * The panel that holds the tooltip when the tooltip is displayed fully 179: * inside the current container. 180: */ 181: private static Container containerPanel; 182: 183: /** 184: * The window used when the tooltip doesn't fit inside the current 185: * container. 186: */ 187: private static JDialog tooltipWindow; 188: 189: /** 190: * Creates a new ToolTipManager and sets up the timers. 191: */ 192: ToolTipManager() 193: { 194: enterTimer = new Timer(750, new insideTimerAction()); 195: enterTimer.setRepeats(false); 196: 197: insideTimer = new Timer(4000, new stillInsideTimerAction()); 198: insideTimer.setRepeats(false); 199: 200: exitTimer = new Timer(500, new outsideTimerAction()); 201: exitTimer.setRepeats(false); 202: } 203: 204: /** 205: * This method returns the shared instance of ToolTipManager used by all 206: * JComponents. 207: * 208: * @return The shared instance of ToolTipManager. 209: */ 210: public static ToolTipManager sharedInstance() 211: { 212: if (shared == null) 213: shared = new ToolTipManager(); 214: 215: return shared; 216: } 217: 218: /** 219: * This method sets whether ToolTips are enabled or disabled for all 220: * JComponents. 221: * 222: * @param enabled Whether ToolTips are enabled or disabled for all 223: * JComponents. 224: */ 225: public void setEnabled(boolean enabled) 226: { 227: if (! enabled) 228: { 229: enterTimer.stop(); 230: exitTimer.stop(); 231: insideTimer.stop(); 232: } 233: 234: this.enabled = enabled; 235: } 236: 237: /** 238: * This method returns whether ToolTips are enabled. 239: * 240: * @return Whether ToolTips are enabled. 241: */ 242: public boolean isEnabled() 243: { 244: return enabled; 245: } 246: 247: /** 248: * This method returns whether LightweightToolTips are enabled. 249: * 250: * @return Whether LighweightToolTips are enabled. 251: */ 252: public boolean isLightWeightPopupEnabled() 253: { 254: return lightWeightPopupEnabled; 255: } 256: 257: /** 258: * This method sets whether LightweightToolTips are enabled. If you mix 259: * Lightweight and Heavyweight components, you must set this to false to 260: * ensure that the ToolTips popup above all other components. 261: * 262: * @param enabled Whether LightweightToolTips will be enabled. 263: */ 264: public void setLightWeightPopupEnabled(boolean enabled) 265: { 266: lightWeightPopupEnabled = enabled; 267: heavyWeightPopupEnabled = ! enabled; 268: } 269: 270: /** 271: * This method returns the initial delay before the ToolTip is shown when 272: * the mouse enters a Component. 273: * 274: * @return The initial delay before the ToolTip is shown. 275: */ 276: public int getInitialDelay() 277: { 278: return enterTimer.getDelay(); 279: } 280: 281: /** 282: * This method sets the initial delay before the ToolTip is shown when the 283: * mouse enters a Component. 284: * 285: * @param delay The initial delay before the ToolTip is shown. 286: */ 287: public void setInitialDelay(int delay) 288: { 289: enterTimer.setDelay(delay); 290: } 291: 292: /** 293: * This method returns the time the ToolTip will be shown before being 294: * hidden. 295: * 296: * @return The time the ToolTip will be shown before being hidden. 297: */ 298: public int getDismissDelay() 299: { 300: return insideTimer.getDelay(); 301: } 302: 303: /** 304: * This method sets the time the ToolTip will be shown before being hidden. 305: * 306: * @param delay The time the ToolTip will be shown before being hidden. 307: */ 308: public void setDismissDelay(int delay) 309: { 310: insideTimer.setDelay(delay); 311: } 312: 313: /** 314: * This method returns the amount of delay where if the mouse re-enters a 315: * Component, the tooltip will be shown immediately. 316: * 317: * @return The reshow delay. 318: */ 319: public int getReshowDelay() 320: { 321: return exitTimer.getDelay(); 322: } 323: 324: /** 325: * This method sets the amount of delay where if the mouse re-enters a 326: * Component, the tooltip will be shown immediately. 327: * 328: * @param delay The reshow delay. 329: */ 330: public void setReshowDelay(int delay) 331: { 332: exitTimer.setDelay(delay); 333: } 334: 335: /** 336: * This method registers a JComponent with the ToolTipManager. 337: * 338: * @param component The JComponent to register with the ToolTipManager. 339: */ 340: public void registerComponent(JComponent component) 341: { 342: component.addMouseListener(this); 343: component.addMouseMotionListener(this); 344: } 345: 346: /** 347: * This method unregisters a JComponent with the ToolTipManager. 348: * 349: * @param component The JComponent to unregister with the ToolTipManager. 350: */ 351: public void unregisterComponent(JComponent component) 352: { 353: component.removeMouseMotionListener(this); 354: component.removeMouseListener(this); 355: } 356: 357: /** 358: * This method is called whenever the mouse enters a JComponent registered 359: * with the ToolTipManager. When the mouse enters within the period of time 360: * specified by the reshow delay, the tooltip will be displayed 361: * immediately. Otherwise, it must wait for the initial delay before 362: * displaying the tooltip. 363: * 364: * @param event The MouseEvent. 365: */ 366: public void mouseEntered(MouseEvent event) 367: { 368: if (currentComponent != null 369: && getContentPaneDeepestComponent(event) == currentComponent) 370: return; 371: currentPoint = event.getPoint(); 372: currentComponent = (Component) event.getSource(); 373: 374: if (exitTimer.isRunning()) 375: { 376: exitTimer.stop(); 377: insideTimer.start(); 378: return; 379: } 380: 381: // This should always be stopped unless we have just fake-exited. 382: if (! enterTimer.isRunning()) 383: enterTimer.start(); 384: } 385: 386: /** 387: * This method is called when the mouse exits a JComponent registered with 388: * the ToolTipManager. When the mouse exits, the tooltip should be hidden 389: * immediately. 390: * 391: * @param event The MouseEvent. 392: */ 393: public void mouseExited(MouseEvent event) 394: { 395: if (getContentPaneDeepestComponent(event) == currentComponent) 396: return; 397: 398: currentPoint = event.getPoint(); 399: currentComponent = null; 400: hideTip(); 401: 402: if (! enterTimer.isRunning() && insideTimer.isRunning()) 403: exitTimer.start(); 404: if (enterTimer.isRunning()) 405: enterTimer.stop(); 406: if (insideTimer.isRunning()) 407: insideTimer.stop(); 408: } 409: 410: /** 411: * This method is called when the mouse is pressed on a JComponent 412: * registered with the ToolTipManager. When the mouse is pressed, the 413: * tooltip (if it is shown) must be hidden immediately. 414: * 415: * @param event The MouseEvent. 416: */ 417: public void mousePressed(MouseEvent event) 418: { 419: currentPoint = event.getPoint(); 420: if (enterTimer.isRunning()) 421: enterTimer.restart(); 422: else if (insideTimer.isRunning()) 423: { 424: insideTimer.stop(); 425: hideTip(); 426: } 427: } 428: 429: /** 430: * This method is called when the mouse is dragged in a JComponent 431: * registered with the ToolTipManager. 432: * 433: * @param event The MouseEvent. 434: */ 435: public void mouseDragged(MouseEvent event) 436: { 437: currentPoint = event.getPoint(); 438: if (enterTimer.isRunning()) 439: enterTimer.restart(); 440: } 441: 442: /** 443: * This method is called when the mouse is moved in a JComponent registered 444: * with the ToolTipManager. 445: * 446: * @param event The MouseEvent. 447: */ 448: public void mouseMoved(MouseEvent event) 449: { 450: currentPoint = event.getPoint(); 451: if (enterTimer.isRunning()) 452: enterTimer.restart(); 453: } 454: 455: /** 456: * This method displays the ToolTip. It can figure out the method needed to 457: * show it as well (whether to display it in heavyweight/lightweight panel 458: * or a window.) This is package-private to avoid an accessor method. 459: */ 460: void showTip() 461: { 462: if (!enabled || currentComponent == null || !currentComponent.isEnabled() 463: || (currentTip != null && currentTip.isVisible())) 464: return; 465: 466: if (currentTip == null || currentTip.getComponent() != currentComponent 467: && currentComponent instanceof JComponent) 468: currentTip = ((JComponent) currentComponent).createToolTip(); 469: 470: currentTip.setVisible(true); 471: Container parent = currentComponent.getParent(); 472: Point p = currentPoint; 473: Dimension dims = currentTip.getPreferredSize(); 474: 475: if (parent instanceof JPopupMenu) 476: setLightWeightPopupEnabled(((JPopupMenu) parent).isLightWeightPopupEnabled()); 477: else 478: setLightWeightPopupEnabled(true); 479: 480: if (isLightWeightPopupEnabled()) 481: { 482: JLayeredPane pane = null; 483: JRootPane r = ((JRootPane) SwingUtilities. 484: getAncestorOfClass(JRootPane.class, currentComponent)); 485: if (r != null) 486: pane = r.getLayeredPane(); 487: if (pane == null) 488: return; 489: 490: if (containerPanel != null) 491: hideTip(); 492: 493: containerPanel = new Panel(); 494: JRootPane root = new JRootPane(); 495: root.getContentPane().add(currentTip); 496: containerPanel.add(root); 497: 498: LayoutManager lm = containerPanel.getLayout(); 499: if (lm instanceof FlowLayout) 500: { 501: FlowLayout fm = (FlowLayout) lm; 502: fm.setVgap(0); 503: fm.setHgap(0); 504: } 505: 506: p = SwingUtilities.convertPoint(currentComponent, p, pane); 507: p = adjustLocation(p, pane, dims); 508: 509: pane.add(containerPanel); 510: containerPanel.setBounds(p.x, p.y, dims.width, dims.height); 511: currentTip.setBounds(0, 0, dims.width, dims.height); 512: containerPanel.validate(); 513: containerPanel.repaint(); 514: } 515: else if (currentComponent.isShowing()) 516: { 517: SwingUtilities.convertPointToScreen(p, currentComponent); 518: p = adjustLocation(p, SwingUtilities.getWindowAncestor(currentComponent), 519: dims); 520: 521: tooltipWindow = new JDialog(); 522: tooltipWindow.setContentPane(currentTip); 523: tooltipWindow.setUndecorated(true); 524: tooltipWindow.getRootPane(). 525: setWindowDecorationStyle(JRootPane.PLAIN_DIALOG); 526: tooltipWindow.pack(); 527: tooltipWindow.setBounds(p.x, p.y, dims.width, dims.height); 528: tooltipWindow.show(); 529: tooltipWindow.validate(); 530: tooltipWindow.repaint(); 531: currentTip.revalidate(); 532: currentTip.repaint(); 533: } 534: } 535: 536: /** 537: * Adjusts the point to a new location on the component, 538: * using the currentTip's dimensions. 539: * 540: * @param p - the point to convert. 541: * @param c - the component the point is on. 542: * @param d - the dimensions of the currentTip. 543: */ 544: private Point adjustLocation(Point p, Component c, Dimension d) 545: { 546: if (p.x + d.width > c.getWidth()) 547: p.x -= d.width; 548: if (p.x < 0) 549: p.x = 0; 550: if (p.y + d.height < c.getHeight()) 551: p.y += d.height; 552: if (p.y + d.height > c.getHeight()) 553: p.y -= d.height*2; 554: 555: return p; 556: } 557: 558: /** 559: * This method hides the ToolTip. 560: * This is package-private to avoid an accessor method. 561: */ 562: void hideTip() 563: { 564: if (currentTip == null || ! currentTip.isVisible() || ! enabled) 565: return; 566: currentTip.setVisible(false); 567: if (containerPanel != null) 568: { 569: Container parent = containerPanel.getParent(); 570: if (parent == null) 571: return; 572: parent.remove(containerPanel); 573: 574: parent = currentTip.getParent(); 575: if (parent == null) 576: return; 577: parent.remove(currentTip); 578: containerPanel = null; 579: } 580: if (tooltipWindow != null) 581: { 582: tooltipWindow.hide(); 583: tooltipWindow.dispose(); 584: tooltipWindow = null; 585: } 586: currentTip = null; 587: } 588: 589: /** 590: * This method returns the deepest component in the content pane for the 591: * first RootPaneContainer up from the currentComponent. This method is 592: * used in conjunction with one of the mouseXXX methods. 593: * 594: * @param e The MouseEvent. 595: * 596: * @return The deepest component in the content pane. 597: */ 598: private Component getContentPaneDeepestComponent(MouseEvent e) 599: { 600: Component source = (Component) e.getSource(); 601: Container parent = (Container) SwingUtilities.getAncestorOfClass(JRootPane.class, 602: currentComponent); 603: if (parent == null) 604: return null; 605: parent = ((JRootPane) parent).getContentPane(); 606: Point p = e.getPoint(); 607: p = SwingUtilities.convertPoint(source, p, parent); 608: Component target = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y); 609: return target; 610: } 611: }
GNU Classpath (0.19) |