Merged gcj-eclipse branch to trunk.
From-SVN: r120621
This commit is contained in:
@@ -38,7 +38,10 @@ exception statement from your version. */
|
||||
|
||||
package java.awt;
|
||||
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseWheelEvent;
|
||||
import java.awt.peer.LightweightPeer;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/**
|
||||
@@ -49,7 +52,7 @@ import java.util.WeakHashMap;
|
||||
*
|
||||
* @author Roman Kennke (kennke@aicas.com)
|
||||
*/
|
||||
class LightweightDispatcher
|
||||
final class LightweightDispatcher
|
||||
{
|
||||
|
||||
/**
|
||||
@@ -59,26 +62,17 @@ class LightweightDispatcher
|
||||
*/
|
||||
private static WeakHashMap instances = new WeakHashMap();
|
||||
|
||||
/**
|
||||
* The component that is the start of a mouse dragging. All MOUSE_DRAGGED
|
||||
* events that follow the initial press must have the source set to this,
|
||||
* as well as the MOUSE_RELEASED event following the dragging.
|
||||
*/
|
||||
private Component dragTarget;
|
||||
|
||||
/**
|
||||
* Stores the button number which started the drag operation. This is needed
|
||||
* because we want to handle only one drag operation and only the button that
|
||||
* started the dragging should be able to stop it (by a button release).
|
||||
*/
|
||||
private int dragButton;
|
||||
|
||||
/**
|
||||
* The last mouse event target. If the target changes, additional
|
||||
* MOUSE_ENTERED and MOUSE_EXITED events must be dispatched.
|
||||
*/
|
||||
private Component lastTarget;
|
||||
|
||||
/**
|
||||
* The current mouseEventTarget.
|
||||
*/
|
||||
private Component mouseEventTarget;
|
||||
|
||||
/**
|
||||
* Returns an instance of LightweightDispatcher for the current thread's
|
||||
* thread group.
|
||||
@@ -113,9 +107,9 @@ class LightweightDispatcher
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
public boolean dispatchEvent(AWTEvent event)
|
||||
public boolean dispatchEvent(final AWTEvent event)
|
||||
{
|
||||
if (event instanceof MouseEvent && event.getSource() instanceof Window)
|
||||
if (event instanceof MouseEvent)
|
||||
{
|
||||
MouseEvent mouseEvent = (MouseEvent) event;
|
||||
return handleMouseEvent(mouseEvent);
|
||||
@@ -130,151 +124,49 @@ class LightweightDispatcher
|
||||
* @param ev the mouse event
|
||||
* @return whether or not we found a lightweight that handled the event.
|
||||
*/
|
||||
private boolean handleMouseEvent(MouseEvent ev)
|
||||
private boolean handleMouseEvent(final MouseEvent ev)
|
||||
{
|
||||
Window window = (Window) ev.getSource();
|
||||
// Find the target for the mouse event. We first seach the deepest
|
||||
// component at the specified location. The we go up to its parent and
|
||||
// try to find a neighbor of the deepest component that is suitable as
|
||||
// mouse event target (it must be showing, at that location and have either
|
||||
// a MouseListener or MouseMotionListener installed). If no such component
|
||||
// is found, then we walk up the container hierarchy and find the next
|
||||
// container that has a MouseListener or MouseMotionListener installed.
|
||||
Component deepest = window.findComponentAt(ev.getX(), ev.getY());
|
||||
if (deepest == null)
|
||||
return false;
|
||||
Container parent = deepest.getParent();
|
||||
Point loc = ev.getPoint();
|
||||
loc = convertPointToChild(window, loc, parent);
|
||||
Component target = null;
|
||||
if (parent != null)
|
||||
Container container = (Container) ev.getSource();
|
||||
Component target = findTarget(container, ev.getX(), ev.getY());
|
||||
trackEnterExit(target, ev);
|
||||
int id = ev.getID();
|
||||
|
||||
// Dont update the mouseEventTarget when dragging. Also, MOUSE_CLICKED
|
||||
// must be dispatched to the original target of MOUSE_PRESSED, so don't
|
||||
// update in this case either.
|
||||
if (! isDragging(ev) && id != MouseEvent.MOUSE_CLICKED)
|
||||
mouseEventTarget = (target != container) ? target : null;
|
||||
|
||||
if (mouseEventTarget != null)
|
||||
{
|
||||
target = findTarget(parent, loc);
|
||||
while (target == null && parent != null)
|
||||
switch (id)
|
||||
{
|
||||
if (parent.mouseListener != null
|
||||
|| parent.mouseMotionListener != null
|
||||
|| (parent.eventMask
|
||||
& (AWTEvent.MOUSE_EVENT_MASK
|
||||
| AWTEvent.MOUSE_MOTION_EVENT_MASK)) != 0)
|
||||
{
|
||||
target = parent;
|
||||
}
|
||||
else
|
||||
parent = parent.getParent();
|
||||
}
|
||||
}
|
||||
if (target == null || target.isLightweight())
|
||||
{
|
||||
// Dispatch additional MOUSE_EXITED and MOUSE_ENTERED if event target
|
||||
// is different from the last event target.
|
||||
if (target != lastTarget)
|
||||
{
|
||||
if (lastTarget != null)
|
||||
{
|
||||
Point p1 = convertPointToChild(window, ev.getPoint(),
|
||||
lastTarget);
|
||||
MouseEvent mouseExited =
|
||||
new MouseEvent(lastTarget, MouseEvent.MOUSE_EXITED,
|
||||
ev.getWhen(), ev.getModifiers(), p1.x, p1.y,
|
||||
ev.getClickCount(), ev.isPopupTrigger());
|
||||
//System.err.println("event: " + mouseExited);
|
||||
lastTarget.dispatchEvent(mouseExited);
|
||||
}
|
||||
|
||||
// If a target exists dispatch the MOUSE_ENTERED event.
|
||||
// Experimenting shows that the MOUSE_ENTERED is also dispatched
|
||||
// when the mouse is dragging.
|
||||
if (target != null)
|
||||
{
|
||||
Point p = convertPointToChild(window, ev.getPoint(), target);
|
||||
MouseEvent mouseEntered =
|
||||
new MouseEvent(target,
|
||||
MouseEvent.MOUSE_ENTERED, ev.getWhen(),
|
||||
ev.getModifiers(), p.x, p.y, ev.getClickCount(),
|
||||
ev.isPopupTrigger());
|
||||
//System.err.println("event: " + mouseEntered);
|
||||
target.dispatchEvent(mouseEntered);
|
||||
}
|
||||
}
|
||||
|
||||
switch (ev.getID())
|
||||
{
|
||||
case MouseEvent.MOUSE_PRESSED:
|
||||
// Handle the start of a drag operation or discard the event if
|
||||
// one is already in progress. This prevents focus changes with the
|
||||
// other mouse buttons when one is used for dragging.
|
||||
if (dragTarget == null)
|
||||
{
|
||||
lastTarget = dragTarget = target;
|
||||
|
||||
// Save the button that started the drag operation.
|
||||
dragButton = ev.getButton();
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
case MouseEvent.MOUSE_ENTERED:
|
||||
case MouseEvent.MOUSE_EXITED:
|
||||
// This is already handled in trackEnterExit().
|
||||
break;
|
||||
case MouseEvent.MOUSE_PRESSED:
|
||||
case MouseEvent.MOUSE_RELEASED:
|
||||
// Stop the drag operation only when the button that started
|
||||
// it was released.
|
||||
if (dragTarget != null && dragButton == ev.getButton())
|
||||
{
|
||||
// Only post MOUSE_RELEASED to dragTarget (set in
|
||||
// MOUSE_PRESSED) when the dragTarget is actually visible.
|
||||
// Otherwise post the event to the normal target.
|
||||
if (dragTarget.isVisible())
|
||||
target = dragTarget;
|
||||
dragTarget = null;
|
||||
}
|
||||
|
||||
lastTarget = target;
|
||||
case MouseEvent.MOUSE_MOVED:
|
||||
redispatch(ev, mouseEventTarget, id);
|
||||
break;
|
||||
case MouseEvent.MOUSE_CLICKED:
|
||||
// When we receive a MOUSE_CLICKED, we set the target to the
|
||||
// previous target, which must have been a MOUSE_RELEASED event.
|
||||
// This is necessary for the case when the MOUSE_RELEASED has
|
||||
// caused the original target (like an internal component) go
|
||||
// away.
|
||||
// This line is the reason why it is not possible to move the
|
||||
// 'lastTarget = target' assignment before the switch-statement.
|
||||
target = lastTarget;
|
||||
// MOUSE_CLICKED must be dispatched to the original target of
|
||||
// MOUSE_PRESSED.
|
||||
if (target == mouseEventTarget)
|
||||
redispatch(ev, mouseEventTarget, id);
|
||||
break;
|
||||
case MouseEvent.MOUSE_DRAGGED:
|
||||
// We consider only dragTarget for redispatching the event still
|
||||
// we have to act in a way that the newly found target component
|
||||
// was handled.
|
||||
lastTarget = target;
|
||||
target = dragTarget;
|
||||
if (isDragging(ev))
|
||||
redispatch(ev, mouseEventTarget, id);
|
||||
break;
|
||||
default:
|
||||
// Only declare current target as the old value in all other
|
||||
// cases.
|
||||
lastTarget = target;
|
||||
break;
|
||||
}
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
Point targetCoordinates = convertPointToChild(window,
|
||||
ev.getPoint(),
|
||||
target);
|
||||
int dx = targetCoordinates.x - ev.getX();
|
||||
int dy = targetCoordinates.y - ev.getY();
|
||||
ev.translatePoint(dx, dy);
|
||||
ev.setSource(target);
|
||||
target.dispatchEvent(ev);
|
||||
|
||||
// We reset the event, so that the normal event dispatching is not
|
||||
// influenced by this modified event.
|
||||
ev.setSource(window);
|
||||
ev.translatePoint(-dx, -dy);
|
||||
case MouseEvent.MOUSE_WHEEL:
|
||||
redispatch(ev, mouseEventTarget, id);
|
||||
}
|
||||
|
||||
return true;
|
||||
ev.consume();
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
return ev.isConsumed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,58 +182,180 @@ class LightweightDispatcher
|
||||
* @return the actual receiver of the mouse event, or null, if no such
|
||||
* component has been found
|
||||
*/
|
||||
private Component findTarget(Container c, Point loc)
|
||||
private Component findTarget(final Container c, final int x, final int y)
|
||||
{
|
||||
int numComponents = c.getComponentCount();
|
||||
Component target = null;
|
||||
if (c != null)
|
||||
|
||||
// First we check the children of the container.
|
||||
|
||||
// Note: It is important that we use the package private Container
|
||||
// fields ncomponents and component here. There are applications
|
||||
// that override getComponentCount()
|
||||
// and getComponent() to hide internal components, which makes
|
||||
// the LightweightDispatcher not work correctly in these cases.
|
||||
// As a positive sideeffect this is slightly more efficient.
|
||||
int nChildren = c.ncomponents;
|
||||
for (int i = 0; i < nChildren && target == null; i++)
|
||||
{
|
||||
for (int i = 0; i < numComponents; i++)
|
||||
Component child = c.component[i];
|
||||
int childX = x - child.x;
|
||||
int childY = y - child.y;
|
||||
if (child != null && child.visible
|
||||
&& child.peer instanceof LightweightPeer
|
||||
&& child.contains(childX, childY))
|
||||
{
|
||||
Component child = c.getComponent(i);
|
||||
if (child.isShowing())
|
||||
// Check if there's a deeper possible target.
|
||||
if (child instanceof Container)
|
||||
{
|
||||
if (child.contains(loc.x - child.getX(), loc.y - child.getY())
|
||||
&& (child.mouseListener != null
|
||||
|| child.mouseMotionListener != null
|
||||
|| (child.eventMask
|
||||
& (AWTEvent.MOUSE_EVENT_MASK
|
||||
| AWTEvent.MOUSE_MOTION_EVENT_MASK)) != 0))
|
||||
{
|
||||
target = child;
|
||||
break;
|
||||
}
|
||||
Component deeper = findTarget((Container) child,
|
||||
childX, childY);
|
||||
if (deeper != null)
|
||||
target = deeper;
|
||||
}
|
||||
// Check if the child itself is interested in mouse events.
|
||||
else if (isMouseListening(child))
|
||||
target = child;
|
||||
}
|
||||
}
|
||||
|
||||
// Check the container itself, if we didn't find a target yet.
|
||||
if (target == null && c.contains(x, y) && isMouseListening(c))
|
||||
target = c;
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a point in the parent's coordinate system to a child coordinate
|
||||
* system. The resulting point is stored in the same Point object and
|
||||
* returned.
|
||||
* Checks if the specified component would be interested in a mouse event.
|
||||
*
|
||||
* @param parent the parent component
|
||||
* @param p the point
|
||||
* @param child the child component
|
||||
* @param c the component to check
|
||||
*
|
||||
* @return the translated point
|
||||
* @return <code>true</code> if the component has mouse listeners installed,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
private Point convertPointToChild(Component parent, Point p,
|
||||
Component child)
|
||||
private boolean isMouseListening(final Component c)
|
||||
{
|
||||
int offX = 0;
|
||||
int offY = 0;
|
||||
Component comp = child;
|
||||
while (comp != null && comp != parent)
|
||||
// Note: It is important to NOT check if the component is listening
|
||||
// for a specific event (for instance, mouse motion events). The event
|
||||
// gets dispatched to the component if the component is listening
|
||||
// for ANY mouse event, even when the component is not listening for the
|
||||
// specific type of event. There are applications that depend on this
|
||||
// (sadly).
|
||||
return c.mouseListener != null
|
||||
|| c.mouseMotionListener != null
|
||||
|| c.mouseWheelListener != null
|
||||
|| (c.eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0
|
||||
|| (c.eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0
|
||||
|| (c.eventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks MOUSE_ENTERED and MOUSE_EXIT as well as MOUSE_MOVED and
|
||||
* MOUSE_DRAGGED and creates synthetic MOUSE_ENTERED and MOUSE_EXITED for
|
||||
* lightweight component.s
|
||||
*
|
||||
* @param target the current mouse event target
|
||||
* @param ev the mouse event
|
||||
*/
|
||||
private void trackEnterExit(final Component target, final MouseEvent ev)
|
||||
{
|
||||
int id = ev.getID();
|
||||
if (target != lastTarget)
|
||||
{
|
||||
offX += comp.getX();
|
||||
offY += comp.getY();
|
||||
comp = comp.getParent();
|
||||
if (lastTarget != null)
|
||||
redispatch(ev, lastTarget, MouseEvent.MOUSE_EXITED);
|
||||
if (id == MouseEvent.MOUSE_EXITED)
|
||||
ev.consume();
|
||||
if (target != null)
|
||||
redispatch(ev, target, MouseEvent.MOUSE_ENTERED);
|
||||
if (id == MouseEvent.MOUSE_ENTERED)
|
||||
ev.consume();
|
||||
lastTarget = target;
|
||||
}
|
||||
p.x -= offX;
|
||||
p.y -= offY;
|
||||
return p;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Redispatches the specified mouse event to the specified target with the
|
||||
* specified id.
|
||||
*
|
||||
* @param ev the mouse event
|
||||
* @param target the new target
|
||||
* @param id the new id
|
||||
*/
|
||||
private void redispatch(MouseEvent ev, Component target, int id)
|
||||
{
|
||||
Component source = ev.getComponent();
|
||||
if (target != null)
|
||||
{
|
||||
// Translate coordinates.
|
||||
int x = ev.getX();
|
||||
int y = ev.getY();
|
||||
for (Component c = target; c != null && c != source; c = c.getParent())
|
||||
{
|
||||
x -= c.x;
|
||||
y -= c.y;
|
||||
}
|
||||
|
||||
// Retarget event.
|
||||
MouseEvent retargeted;
|
||||
if (id == MouseEvent.MOUSE_WHEEL)
|
||||
{
|
||||
MouseWheelEvent mwe = (MouseWheelEvent) ev;
|
||||
retargeted = new MouseWheelEvent(target, id, ev.getWhen(),
|
||||
ev.getModifiers()
|
||||
| ev.getModifiersEx(), x, y,
|
||||
ev.getClickCount(),
|
||||
ev.isPopupTrigger(),
|
||||
mwe.getScrollType(),
|
||||
mwe.getScrollAmount(),
|
||||
mwe.getWheelRotation());
|
||||
}
|
||||
else
|
||||
{
|
||||
retargeted = new MouseEvent(target, id, ev.getWhen(),
|
||||
ev.getModifiers() | ev.getModifiersEx(),
|
||||
x, y, ev.getClickCount(),
|
||||
ev.isPopupTrigger(), ev.getButton());
|
||||
}
|
||||
|
||||
if (target == source)
|
||||
((Container) target).dispatchNoLightweight(retargeted);
|
||||
else
|
||||
target.dispatchEvent(retargeted);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if we are in the middle of a drag operation, that is, if
|
||||
* any of the buttons is held down.
|
||||
*
|
||||
* @param ev the mouse event to check
|
||||
*
|
||||
* @return <code>true</code> if we are in the middle of a drag operation,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
private boolean isDragging(MouseEvent ev)
|
||||
{
|
||||
int mods = ev.getModifiersEx();
|
||||
int id = ev.getID();
|
||||
if (id == MouseEvent.MOUSE_PRESSED || id == MouseEvent.MOUSE_RELEASED)
|
||||
{
|
||||
switch (ev.getButton())
|
||||
{
|
||||
case MouseEvent.BUTTON1:
|
||||
mods ^= InputEvent.BUTTON1_DOWN_MASK;
|
||||
break;
|
||||
case MouseEvent.BUTTON2:
|
||||
mods ^= InputEvent.BUTTON2_DOWN_MASK;
|
||||
break;
|
||||
case MouseEvent.BUTTON3:
|
||||
mods ^= InputEvent.BUTTON3_DOWN_MASK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (mods & (InputEvent.BUTTON1_DOWN_MASK
|
||||
| InputEvent.BUTTON2_DOWN_MASK
|
||||
| InputEvent.BUTTON3_DOWN_MASK)) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user