import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.AdjustmentEvent;
import javax.swing.ToolTipManager;

/**
 *  This class is used to help generate tooltips on components added to a
 *  scrollpane. Generally tooltips are generated as the mouse if moved over
 *  components that display tooltips. On complex component, like a JTable
 *  the component can generate multiple tooltips depending on which cell the
 *  mouse is positioned over.
 *
 *  However, when the viewport of the scrollpane is moved and the mouse is
 *  not moved the tooltip is not updated even though the mouse is positioned
 *  over a different cell. This might happen for example when the mouse wheel
 *  is used to scroll the viewport.
 *
 *  To force updating of the tooltip, this class will generate a phantom
 *  mouseMoved event which is passed to the ToolTipManager.
 *
 *  This class is actually a 3 in 1 listener and will work slightly different
 *  depending on how it is being used. When used as a:
 *
 *  a) MouseWheelListener - it is added to the scrollpane. In this case the
 *     mouseMoved events are only generated by scrolling of the mouse wheel
 *     and therefore only supports vertical movement of the viewport
 *  b) AdjustmentListener - is added to the vertical and/or horizontal scrollbar.
 *     In this case the viewport can be scrolled by using the mouse wheel or
 *     the keyboard and mouseMoved events will be generated.
 *  c) ComponentListener - it is added to the component. In this case all forms
 *     of viewport movement as well as changes in the component size will cause
 *     the mouseMoved event to be generated.
 *
 */
public class ToolTipListener
	implements ComponentListener, MouseWheelListener, AdjustmentListener
{
	/**
	 *  Create a mouseMoved event to pass to the ToolTipManager.
	 */
	private void phantomMouseMoved(Component component)
	{
		if (component == null) return;

		//  Mouse is in the bounds of the component, generate phantom
		//  mouseMoved event for the ToolTipManager

		Point mouseLocation = component.getMousePosition();

		if (mouseLocation != null)
		{
			MouseEvent phantom = new MouseEvent(
				component,
				MouseEvent.MOUSE_MOVED,
				System.currentTimeMillis(),
				0,
				mouseLocation.x,
				mouseLocation.y,
				0,
				false);

			ToolTipManager.sharedInstance().mouseMoved(phantom);
		}
	}

//  Implement ComponentListener

	public void componentMoved(ComponentEvent e)
	{
		Component component = e.getComponent();
		phantomMouseMoved( component );
	}

	public void componentResized(ComponentEvent e)
	{
		Component component = e.getComponent();
		phantomMouseMoved( component );
	}

	public void componentHidden(ComponentEvent e) {}
	public void componentShown(ComponentEvent e) {}

//  Implement MouseWheelListener

	public void mouseWheelMoved(MouseWheelEvent e)
	{
		JScrollPane scrollPane = (JScrollPane)e.getSource();
		Component component = scrollPane.getViewport().getView();
		phantomMouseMoved( component );
	}

//  Implement AdjustmentListener

	public void adjustmentValueChanged(AdjustmentEvent e)
	{
		JScrollBar scrollBar = (JScrollBar)e.getSource();
		JScrollPane scrollPane = (JScrollPane)scrollBar.getParent();
		Component component = scrollPane.getViewport().getView();
		phantomMouseMoved( component );
	}

}

