Ticket #8931: ToolTipManager.java

File ToolTipManager.java, 29.3 KB (added by bastiK, 13 years ago)

from http://security.ubuntu.com/ubuntu/pool/main/o/openjdk-6/openjdk-6-source_6b27-1.12.6-1ubuntu0.12.04.2_all.deb

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