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