/*
* JavaXYQ Source Code
* LightweightToolTipManager LightweightToolTipManager.groovy
* by kylixs 2009-10
* All Rights Reserved.
* Please see also http://javaxyq.cn or http://javaxyq.googlecode.com.
* Please email to javaxyq@qq.com.
*/
package com.javaxyq.ui;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JToolTip;
import javax.swing.KeyStroke;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import sun.swing.UIAction;
/**
* @author dewitt
*
*/
public class LightweightToolTipManager extends MouseAdapter implements MouseMotionListener {
static final LightweightToolTipManager sharedInstance = new LightweightToolTipManager();
public static LightweightToolTipManager sharedInstance() {
return sharedInstance;
}
Timer enterTimer, exitTimer, insideTimer;
String toolTipText;
Point preferredLocation;
JComponent insideComponent;
MouseEvent mouseEvent;
boolean showImmediately;
transient Popup tipWindow;
/**
* The Window tip is being displayed in. This will be non-null if the Window
* tip is in differs from that of insideComponent's Window.
*/
private Window window;
JToolTip tip;
private Rectangle popupRect = null;
//private Rectangle popupFrameRect = null;
boolean enabled = true;
private boolean tipShowing = false;
private KeyStroke postTip, hideTip;
private Action postTipAction, hideTipAction;
private FocusListener focusChangeListener = null;
private MouseMotionListener moveBeforeEnterListener = null;
// PENDING(ges)
protected boolean lightWeightPopupEnabled = true;
protected boolean heavyWeightPopupEnabled = false;
LightweightToolTipManager() {
enterTimer = new Timer(750, new insideTimerAction());
enterTimer.setRepeats(false);
exitTimer = new Timer(500, new outsideTimerAction());
exitTimer.setRepeats(false);
insideTimer = new Timer(4000, new stillInsideTimerAction());
insideTimer.setRepeats(false);
// create accessibility actions
postTip = KeyStroke.getKeyStroke(KeyEvent.VK_F1, Event.CTRL_MASK);
postTipAction = new Actions(Actions.SHOW);
hideTip = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
hideTipAction = new Actions(Actions.HIDE);
moveBeforeEnterListener = new MoveBeforeEnterListener();
}
/**
* Enables or disables the tooltip.
*
* @param flag
* true to enable the tip, false otherwise
*/
public void setEnabled(boolean flag) {
enabled = flag;
if (!flag) {
hideTipWindow();
}
}
/**
* Returns true if this object is enabled.
*
* @return true if this object is enabled, false otherwise
*/
public boolean isEnabled() {
return enabled;
}
/**
* When displaying the <code>JToolTip</code>, the
* <code>ToolTipManager</code> chooses to use a lightweight
* <code>JPanel</code> if it fits. This method allows you to disable this
* feature. You have to do disable it if your application mixes light weight
* and heavy weights components.
*
* @param aFlag
* true if a lightweight panel is desired, false otherwise
*
*/
public void setLightWeightPopupEnabled(boolean aFlag) {
lightWeightPopupEnabled = aFlag;
}
/**
* Returns true if lightweight (all-Java) <code>Tooltips</code> are in use,
* or false if heavyweight (native peer) <code>Tooltips</code> are being
* used.
*
* @return true if lightweight <code>ToolTips</code> are in use
*/
public boolean isLightWeightPopupEnabled() {
return lightWeightPopupEnabled;
}
/**
* Specifies the initial delay value.
*
* @param milliseconds
* the number of milliseconds to delay (after the cursor has
* paused) before displaying the tooltip
* @see #getInitialDelay
*/
public void setInitialDelay(int milliseconds) {
enterTimer.setInitialDelay(milliseconds);
}
/**
* Returns the initial delay value.
*
* @return an integer representing the initial delay value, in milliseconds
* @see #setInitialDelay
*/
public int getInitialDelay() {
return enterTimer.getInitialDelay();
}
/**
* Specifies the dismissal delay value.
*
* @param milliseconds
* the number of milliseconds to delay before taking away the
* tooltip
* @see #getDismissDelay
*/
public void setDismissDelay(int milliseconds) {
insideTimer.setInitialDelay(milliseconds);
}
/**
* Returns the dismissal delay value.
*
* @return an integer representing the dismissal delay value, in
* milliseconds
* @see #setDismissDelay
*/
public int getDismissDelay() {
return insideTimer.getInitialDelay();
}
/**
* Used to specify the amount of time before the user has to wait
* <code>initialDelay</code> milliseconds before a tooltip will be shown.
* That is, if the tooltip is hidden, and the user moves into a region of
* the same Component that has a valid tooltip within
* <code>milliseconds</code> milliseconds the tooltip will immediately be
* shown. Otherwise, if the user moves into a region with a valid tooltip
* after <code>milliseconds</code> milliseconds, the user will have to wait
* an additional <code>initialDelay</code> milliseconds before the tooltip
* is shown again.
*
* @param milliseconds
* time in milliseconds
* @see #getReshowDelay
*/
public void setReshowDelay(int milliseconds) {
exitTimer.setInitialDelay(milliseconds);
}
/**
* Returns the reshow delay property.
*
* @return reshown delay property
* @see #setReshowDelay
*/
public int getReshowDelay() {
return exitTimer.getInitialDelay();
}
void showTipWindow() {
if (insideComponent == null || !insideComponent.isShowing())
return;
// ensure tooltip location
ensureApplicableLocation();
String mode = UIManager.getString("ToolTipManager.enableToolTipMode");
if ("activeApplication".equals(mode)) {
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
if (kfm.getFocusedWindow() == null) {
return;
}
}
if (enabled) {
Dimension size;
Point screenLocation = insideComponent.getLocationOnScreen();
Point location = new Point();
GraphicsConfiguration gc;
gc = insideComponent.getGraphicsConfiguration();
Rectangle sBounds = gc.getBounds();
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
// Take into account screen insets, decrease viewport
sBounds.x += screenInsets.left;
sBounds.y += screenInsets.top;
sBounds.width -= (screenInsets.left + screenInsets.right);
sBounds.height -= (screenInsets.top + screenInsets.bottom);
boolean leftToRight = true;// SwingUtilities.isLeftToRight(insideComponent);
// Just to be paranoid
hideTipWindow();
tip = insideComponent.createToolTip();
tip.setTipText(toolTipText);
size = tip.getPreferredSize();
if (preferredLocation != null) {
location.x = screenLocation.x + preferredLocation.x;
location.y = screenLocation.y + preferredLocation.y;
if (!leftToRight) {
location.x -= size.width;
}
} else {
location.x = screenLocation.x + mouseEvent.getX();
location.y = screenLocation.y + mouseEvent.getY() + 20;
if (!leftToRight) {
if (location.x - size.width >= 0) {
location.x -= size.width;
}
}
}
// we do not adjust x/y when using awt.Window tips
if (popupRect == null) {
popupRect = new Rectangle();
}
popupRect.setBounds(location.x, location.y, size.width, size.height);
// Fit as much of the tooltip on screen as possible
if (location.x < sBounds.x) {
location.x = sBounds.x;
} else if (location.x - sBounds.x + size.width > sBounds.width) {
location.x = sBounds.x + Math.max(0, sBounds.width - size.width);
}
if (location.y < sBounds.y) {
location.y = sBounds.y;
} else if (location.y - sBounds.y + size.height > sBounds.height) {
location.y = sBounds.y + Math.max(0, sBounds.height - size.height);
}
PopupFactory popupFactory = PopupFactory.getSharedInstance();
// if (lightWeightPopupEnabled) {
// int y = getPopupFitHeight(popupRect, insideComponent);
// int x = getPopupFitWidth(popupRect, insideComponent);
// if (x > 0 || y > 0) {
// popupFactory.setPopupType(PopupFactory.MEDIUM_WEIGHT_POPUP);
// } else {
// popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
// }
// } else {
// popupFactory.setPopupType(PopupFactory.MEDIUM_WEIGHT_POPUP);
// }
tipWindow = popupFactory.getPopup(insideComponent, tip, location.x, location.y);
// popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
tipWindow.show();
Window componentWindow = SwingUtilities.windowForComponent(insideComponent);
window = SwingUtilities.windowForComponent(tip);
if (window != null && window != componentWindow) {
window.addMouseListener(this);
} else {
window = null;
}
insideTimer.start();
tipShowing = true;
}
}
void hideTipWindow() {
if (tipWindow != null) {
if (window != null) {
window.removeMouseListener(this);
window = null;
}
tipWindow.hide();
tipWindow = null;
tipShowing = false;
tip = null;
insideTimer.stop();
}
}
// add keylistener here to trigger tip for access
/**
* Registers a component for tooltip management.
* <p>
* This will register key bindings to show and hide the tooltip text only if
* <code>component</code> has focus bindings. This is done so that
* components that are not normally focus traversable, such as
* <code>JLabel</code>, are not made focus traversable as a result of
* invoking this method.
*
* @param component
* a <code>JComponent</code> object to add
* @see JComponent#isFocusTraversable
*/
public void registerComponent(JComponent component) {
component.removeMouseListener(this);
component.addMouseListener(this);
component.removeMouseMotionListener(moveBeforeEnterListener);
component.addMouseMotionListener(moveBeforeEnterListener);
if (shouldRegisterBindings(component)) {
// register our accessibility keybindings for this component
// this will apply globally across L&F
// Post Tip: Ctrl+F1
// Unpost Tip: Esc and Ctrl+F1
InputMap inputMap = component.getInputMap(JComponent.WHEN_FOCUSED);
ActionMap actionMap = component.getActionMap();
if (inputMap != null && actionMap != null) {
inputMap.put(postTip, "postTip");
inputMap.put(hideTip, "hideTip");
actionMap.put("postTip", postTipAction);
actionMap.put("hideTip", hideTipAction);
}
}
}
/**
* Removes a component from tooltip control.
*
* @param component
* a <code>JComponent</code> object to remove
*/
public void unregisterComponent(JComponent component) {
component.removeMouseListener(this);
component.removeMouseMotionListener(moveBeforeEnterListener);
if (shouldRegisterBindings(component)) {
InputMap inputMap = component.getInputMap(JComponent.WHEN_FOCUSED);
ActionMap actionMap = component.getActionMap();
if (inputMap != null && actionMap != null) {
inputMap.remove(postTip);
inputMap.remove(hideTip);
actionMap.remove("postTip");
actionMap.remove("hideTip");
}
}
}
/**
* Returns whether or not bindings should be registered on the given
* <code>JComponent</code>. This is implemented to return true if the tool
* tip manager has a binding in any one of the <code>InputMaps</code>
* registered under the condition <code>WHEN_FOCUSED</code>.
* <p>
* This does not use <code>isFocusTraversable</code> as some components may
* override <code>isFocusTraversable</code> and base the return value on
* something other than bindings. For example, <code>JButton</code> bases
* its return value on its enabled state.
*
* @param component
* the <code>JComponent</code> in question
*/
private boolean shouldRegisterBindings(JComponent component) {
InputMap inputMap = component.getInputMap(JComponent.WHEN_FOCUSED);
while (inputMap != null && inputMap.size() == 0) {
inputMap = inputMap.getParent();
}
return (inputMap != null);
}
// implements java.awt.event.MouseListener
/**
* Called when the mouse enters the region of a component. This determines
* whether the tool tip should be shown.
*
* @param event
* the event in question
*/
public void mouseEntered(MouseEvent event) {
initiateToolTip(event);
}
private void initiateToolTip(MouseEvent event) {
if (event.getSource() == window) {
return;
}
JComponent component = (JComponent) event.getSource();
component.removeMouseMotionListener(moveBeforeEnterListener);
exitTimer.stop();
Point location = event.getPoint();
// ensure tooltip shows only in proper place
if (location.x < 0 || location.x >= component.getWidth() || location.y < 0
|| location.y >= component.getHeight()) {
return;
}
if (insideComponent != null) {
enterTimer.stop();
}
// A component in an unactive internal frame is sent two
// mouseEntered events, make sure we don't end up adding
// ourselves an extra time.
component.removeMouseMotionListener(this);
component.addMouseMotionListener(this);
boolean sameComponent = (insideComponent == component);
insideComponent = component;
if (tipWindow != null) {
mouseEvent = event;
if (showImmediately) {
String newToolTipText = component.getToolTipText(event);
Point newPreferredLocation = component.getToolTipLocation(event);
boolean sameLoc = (preferredLocation != null) ? preferredLocation
.equals(newPreferredLocation) : (newPreferredLocation == null);
if (!sameComponent || !toolTipText.equals(newToolTipText) || !sameLoc) {
toolTipText = newToolTipText;
preferredLocation = newPreferredLocation;
showTipWindow();
}
} else {
enterTimer.start();
}
}
}
// implements java.awt.event.MouseListener
/**
* Called when the mouse exits the region of a component. Any tool tip
* showing should be hidden.
*
* @param event
* the event in question
*/
public void mouseExited(MouseEvent event) {
boolean shouldHide = true;
if (insideComponent == null) {
// Drag exit
}
if (window != null && event.getSource() == window) {
// if we get an exit and have a heavy window
// we need to check if it if overlapping the inside component
Container insideComponentWindow = insideComponent.getTopLevelAncestor();
Point location = event.getPoint();
SwingUtilities.convertPointToScreen(location, window);
location.x -= insideComponentWindow.getX();
location.y -= insideComponentWindow.getY();
location = SwingUtilities.convertPoint(null, location, insideComponent);
if (location.x >= 0 && location.x < insideComponent.getWidth() && location.y >= 0
&& location.y < insideComponent.getHeight()) {
shouldHide = false;
} else {
shouldHide = true;
}
} else if (event.getSource() == insideComponent && tipWindow != null) {
Window win = SwingUtilities.getWindowAncestor(insideComponent);
if (win != null) { // insideComponent may have been hidden (e.g. in
// a menu)
Point location = SwingUtilities
.convertPoint(insideComponent, event.getPoint(), win);
Rectangle bounds = insideComponent.getTopLevelAncestor().getBounds();
location.x += bounds.x;
location.y += bounds.y;
Point loc = new Point(0, 0);
SwingUtilities.convertPointToScreen(loc, tip);
bounds.x = loc.x;
bounds.y = loc.y;
bounds.width = tip.getWidth();
bounds.height = tip.getHeight();
if (location.x >= bounds.x && location.x < (bounds.x + bounds.width)
&& location.y >= bounds.y && location.y < (bounds.y + bounds.height)) {
shouldHide = false;
} else {
shouldHide = true;
}
}
}
if (shouldHide) {
enterTimer.stop();
if (insideComponent != null) {
insideComponent.removeMouseMotionListener(this);
}
insideComponent = null;
toolTipText = null;
mouseEvent = null;
hideTipWindow();
exitTimer.restart();
}
}
// implements java.awt.event.MouseListener
/**
* Called when the mouse is pressed. Any tool tip showing should be hidden.
*
* @param event
* the event in question
*/
public void mousePressed(MouseEvent event) {
hideTipWindow();
enterTimer.stop();
showImmediately = false;
insideComponent = null;
mouseEvent = null;
}
// implements java.awt.event.MouseMotionListener
/**
* Called when the mouse is pressed and dragged. Does nothing.
*
* @param event
* the event in question
*/
public void mouseDragged(MouseEvent event) {
}
// implements java.awt.event.MouseMotionListener
/**
* Called when the mouse is moved. Determines whether the tool tip should be
* displayed.
*
* @param event
* the event in question
*/
public void mouseMoved(MouseEvent event) {
if (tipShowing) {
checkForTipChange(event);
} else if (showImmediately) {
JComponent component = (JComponent) event.getSource();
toolTipText = component.getToolTipText(event);
if (toolTipText != null) {
preferredLocation = component.getToolTipLocation(event);
mouseEvent = event;
insideComponent = component;
exitTimer.stop();
showTipWindow();
}
} else {
// Lazily lookup the values from within insideTimerAction
insideComponent = (JComponent) event.getSource();
mouseEvent = event;
toolTipText = null;
enterTimer.restart();
}
}
/**
* Checks to see if the tooltip needs to be changed in response to the
* MouseMoved event <code>event</code>.
*/
private void checkForTipChange(MouseEvent event) {
JComponent component = (JComponent) event.getSource();
String newText = component.getToolTipText(event);
Point newPreferredLocation = component.getToolTipLocation(event);
if (newText != null || newPreferredLocation != null) {
mouseEvent = event;
if (((newText != null && newText.equals(toolTipText)) || newText == null)
&& ((newPreferredLocation != null && newPreferredLocation
.equals(preferredLocation)) || newPreferredLocation == null)) {
if (tipWindow != null) {
insideTimer.restart();
} else {
enterTimer.restart();
}
} else {
toolTipText = newText;
preferredLocation = newPreferredLocation;
if (showImmediately) {
hideTipWindow();
showTipWindow();
exitTimer.stop();
} else {
enterTimer.restart();
}
}
} else {
toolTipText = null;
preferredLocation = null;
mouseEvent = null;
insideComponent = null;
hideTipWindow();
enterTimer.stop();
exitTimer.restart();
}
}
protected class insideTimerAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (insideComponent != null && insideComponent.isShowing()) {
// Lazy lookup
if (toolTipText == null && mouseEvent != null) {
toolTipText = insideComponent.getToolTipText(mouseEvent);
preferredLocation = insideComponent.getToolTipLocation(mouseEvent);
}
if (toolTipText != null) {
showImmediately = true;
showTipWindow();
} else {
insideComponent = null;
toolTipText = null;
preferredLocation = null;
mouseEvent = null;
hideTipWindow();
}
}
}
}
protected class outsideTimerAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
showImmediately = false;
}
}
protected class stillInsideTimerAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
hideTipWindow();
enterTimer.stop();
showImmediately = false;
insideComponent = null;
mouseEvent = null;
}
}
/*
* This listener is registered when the tooltip is first registered on a
* component in order to catch the situation where the tooltip was turned on
* while the mouse was already within the bounds of the component. This way,
* the tooltip will be initiated on a mouse-entered or mouse-moved,
* whichever occurs first. Once the tooltip has been initiated, we can
* remove this listener and rely solely on mouse-entered to initiate the
* tooltip.
*/
private class MoveBeforeEnterListener extends MouseMotionAdapter {
public void mouseMoved(MouseEvent e) {
initiateToolTip(e);
}
}
static Frame frameForComponent(Component component) {
while (!(component instanceof Frame)) {
component = component.getParent();
}
return (Frame) component;
}
private FocusListener createFocusChangeListener() {
return new FocusAdapter() {
public void focusLost(FocusEvent evt) {
hideTipWindow();
insideComponent = null;
JComponent c = (JComponent) evt.getSource();
c.removeFocusListener(focusChangeListener);
}
};
}
// Returns: 0 no adjust
// -1 can't fit
// >0 adjust value by amount returned
// private int getPopupFitWidth(Rectangle popupRectInScreen, Component invoker) {
// if (invoker != null) {
// Container parent;
// for (parent = invoker.getParent(); parent != null; parent = parent.getParent()) {
// // fix internal frame size bug: 4139087 - 4159012
// if (parent instanceof JFrame || parent instanceof JDialog
// || parent instanceof JWindow) { // no check for
// // awt.Frame since we
// // use Heavy tips
// return getWidthAdjust(parent.getBounds(), popupRectInScreen);
// } else if (parent instanceof JApplet || parent instanceof JInternalFrame) {
// if (popupFrameRect == null) {
// popupFrameRect = new Rectangle();
// }
// Point p = parent.getLocationOnScreen();
// popupFrameRect.setBounds(p.x, p.y, parent.getBounds().width,
// parent.getBounds().height);
// return getWidthAdjust(popupFrameRect, popupRectInScreen);
// }
// }
// }
// return 0;
// }
// Returns: 0 no adjust
// >0 adjust by value return
// private int getPopupFitHeight(Rectangle popupRectInScreen, Component invoker) {
// if (invoker != null) {
// Container parent;
// for (parent = invoker.getParent(); parent != null; parent = parent.getParent()) {
// if (parent instanceof JFrame || parent instanceof JDialog
// || parent instanceof JWindow) {
// return getHeightAdjust(parent.getBounds(), popupRectInScreen);
// } else if (parent instanceof JApplet || parent instanceof JInternalFrame) {
// if (popupFrameRect == null) {
// popupFrameRect = new Rectangle();
// }
// Point p = parent.getLocationOnScreen();
// popupFrameRect.setBounds(p.x, p.y, parent.getBounds().width,
// parent.getBounds().height);
// return getHeightAdjust(popupFrameRect, popupRectInScreen);
// }
// }
// }
// return 0;
// }
// private int getHeightAdjust(Rectangle a, Rectangle b) {
// if (b.y >= a.y && (b.y + b.height) <= (a.y + a.height))
// return 0;
// else
// return (((b.y + b.height) - (a.y + a.height)) + 5);
// }
// Return the number of pixels over the edge we are extending.
// If we are over the edge the ToolTipManager can adjust.
// REMIND: what if the Tooltip is just too big to fit at all - we currently
// will just clip
// private int getWidthAdjust(Rectangle a, Rectangle b) {
// // System.out.println("width b.x/b.width: " + b.x + "/" + b.width +
// // "a.x/a.width: " + a.x + "/" + a.width);
// if (b.x >= a.x && (b.x + b.width) <= (a.x + a.width)) {
// return 0;
// } else {
// return (((b.x + b.width) - (a.x + a.width)) + 5);
// }
// }
//
// Actions
//
private void show(JComponent source) {
if (tipWindow != null) { // showing we unshow
hideTipWindow();
insideComponent = null;
} else {
hideTipWindow(); // be safe
enterTimer.stop();
exitTimer.stop();
insideTimer.stop();
insideComponent = source;
if (insideComponent != null) {
toolTipText = insideComponent.getToolTipText();
preferredLocation = new Point(10, insideComponent.getHeight() + 10); // manual
// set
showTipWindow();
// put a focuschange listener on to bring the tip down
if (focusChangeListener == null) {
focusChangeListener = createFocusChangeListener();
}
insideComponent.addFocusListener(focusChangeListener);
}
}
}
private void hide(JComponent source) {
hideTipWindow();
source.removeFocusListener(focusChangeListener);
preferredLocation = null;
insideComponent = null;
}
private static class Actions extends UIAction {
private static String SHOW = "SHOW";
private static String HIDE = "HIDE";
Actions(String key) {
super(key);
}
public void actionPerformed(ActionEvent e) {
String key = getName();
JComponent source = (JComponent) e.getSource();
if (key == SHOW) {
LightweightToolTipManager.sharedInstance().show(source);
} else if (key == HIDE) {
LightweightToolTipManager.sharedInstance().hide(source);
}
}
public boolean isEnabled(Object sender) {
if (getName() == SHOW) {
return true;
}
return LightweightToolTipManager.sharedInstance().tipShowing;
}
}
protected void ensureApplicableLocation() {
Container top = insideComponent.getTopLevelAncestor();
tip = insideComponent.createToolTip();
tip.setPreferredSize(null);
tip.setTipText(insideComponent.getToolTipText());
Dimension size = tip.getPreferredSize();
Point topLocation = top.getLocationOnScreen();
if (preferredLocation == null) {
preferredLocation = mouseEvent.getPoint();
preferredLocation.translate(10, 25);
}
SwingUtilities.convertPointToScreen(preferredLocation, insideComponent);
//System.out.println("tip's size "+size+", on "+preferredLocation.x);
int maxX = preferredLocation.x + size.width + 10;
int maxY = preferredLocation.y + size.height + 10;
int right = topLocation.x + top.getWidth();
if (maxX > right) {
preferredLocation.x = right - size.width - 10;
}
int bottom = topLocation.y + top.getHeight();
//System.out.println(" tip("+maxX+","+maxY+") , edge("+right+","+bottom+")");
if (maxY > bottom) {
preferredLocation.y = bottom - size.height
- 10;
}
//System.out.println("\t=>"+preferredLocation);
SwingUtilities.convertPointFromScreen(preferredLocation, insideComponent);
}
}