/*
* @(#)LightweightDispatcher.java 1.16 06/10/10
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package java.awt;
import java.awt.event.AWTEventListener;
import java.awt.event.MouseEvent;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import sun.awt.SunToolkit;
/**
* Class to manage the dispatching of events to the lightweight
* components contained by a native container. In basis profile,
* there is only one native container which is the Frame. A
* LightweightDispatcher is associated with the Frame and forwards
* mouse events to the lightweight components contained in it.
*
*/
class LightweightDispatcher implements java.io.Serializable {
/*
* JDK 1.1 serialVersionUID
*/
private static final long serialVersionUID = 5184291520170872969L;
LightweightDispatcher(Window window) {
this.window = window;
mouseEventTarget = null;
}
/**
* Dispatches an event to a lightweight sub-component if necessary, and
* returns whether or not the event was forwarded to a lightweight
* sub-component.
*
* @param e the event
*/
boolean dispatchEvent(AWTEvent e) {
boolean ret = false;
if (e instanceof MouseEvent) {
MouseEvent me = (MouseEvent) e;
ret = processMouseEvent(me);
}
if (e instanceof MouseEvent) {
// find out what component the mouse moved in
MouseEvent me = (MouseEvent) e;
if (me.getID() == MouseEvent.MOUSE_MOVED) {
cursorOn = window.getCursorTarget(me.getX(), me.getY());
updateCursor(cursorOn);
}
}
return ret;
}
Component getFocusOwner() {
return focus;
}
/**
* This method attempts to distribute a mouse event to a lightweight
* component. It tries to avoid doing any unnecessary probes down
* into the component tree to minimize the overhead of determining
* where to route the event, since mouse movement events tend to
* come in large and frequent amounts.
*/
private boolean processMouseEvent(MouseEvent e) {
int id = e.getID();
Component targetOver;
Component lwOver;
targetOver = window.getMouseEventTarget(e.getX(), e.getY(), true);
trackMouseEnterExit(targetOver, e);
if (mouseEventTarget == null) {
if (id == MouseEvent.MOUSE_MOVED ||
id == MouseEvent.MOUSE_PRESSED) {
lwOver = (targetOver != window) ? targetOver : null;
mouseEventTarget = lwOver;
}
}
if (mouseEventTarget != null) {
// pbp specific, check if the mouseEventTarget belongs to a
// different AppContext from the original target
// If they're different, we cannot dispatch the event
// with the current EventQueue's dispatcher thread, and
// need to repost the event with the retargetted component's
// EventQueue.
boolean repost = !appContextEquals((Component)e.getSource(), mouseEventTarget);
// we are currently forwarding to some component, check
// to see if we should continue to forward.
switch (id) {
case MouseEvent.MOUSE_DRAGGED:
if (dragging) {
retargetMouseEvent(mouseEventTarget, id, e, repost);
}
break;
case MouseEvent.MOUSE_PRESSED:
dragging = true;
retargetMouseEvent(mouseEventTarget, id, e, repost);
break;
case MouseEvent.MOUSE_RELEASED:
Component releasedTarget = mouseEventTarget;
dragging = false;
retargetMouseEvent(mouseEventTarget, id, e, repost);
lwOver = window.getMouseEventTarget(e.getX(), e.getY(), false);
mouseEventTarget = lwOver;
// fix 4155217 component was hidden or moved in user
// code MOUSE_RELEASED handling
isClickOrphaned = lwOver != releasedTarget;
break;
case MouseEvent.MOUSE_CLICKED:
if (!isClickOrphaned) {
retargetMouseEvent(mouseEventTarget, id, e, repost);
}
isClickOrphaned = false;
break;
case MouseEvent.MOUSE_ENTERED:
break;
case MouseEvent.MOUSE_EXITED:
if (!dragging) {
mouseEventTarget = null;
}
break;
case MouseEvent.MOUSE_MOVED:
lwOver = window.getMouseEventTarget(e.getX(), e.getY(), false);
mouseEventTarget = lwOver;
retargetMouseEvent(mouseEventTarget, id, e, repost);
break;
}
e.consume();
}
return e.isConsumed();
}
/*
* Generates enter/exit events as mouse moves over lw components
* @param targetOver Target mouse is over (including native container)
* @param e Mouse event in native container
*/
private void trackMouseEnterExit(Component targetOver, MouseEvent e) {
Component targetEnter = null;
int id = e.getID();
if (id != MouseEvent.MOUSE_EXITED &&
id != MouseEvent.MOUSE_DRAGGED &&
isMouseInNativeContainer == false) {
// any event but an exit or drag means we're in the native container
isMouseInNativeContainer = true;
} else if (id == MouseEvent.MOUSE_EXITED) {
isMouseInNativeContainer = false;
}
if (isMouseInNativeContainer) {
targetEnter = targetOver;
}
//System.out.println("targetEnter = " + targetEnter);
//System.out.println("targetLastEntered = " + targetLastEntered);
if (targetLastEntered == targetEnter) {
return;
}
retargetMouseEvent(targetLastEntered, MouseEvent.MOUSE_EXITED, e,
!appContextEquals((Component)e.getSource(), targetLastEntered));
if (id == MouseEvent.MOUSE_EXITED) {
// consume native exit event if we generate one
e.consume();
}
retargetMouseEvent(targetEnter, MouseEvent.MOUSE_ENTERED, e,
!appContextEquals((Component)e.getSource(), targetEnter));
if (id == MouseEvent.MOUSE_ENTERED) {
// consume native enter event if we generate one
e.consume();
}
//System.out.println("targetLastEntered: " + targetLastEntered);
targetLastEntered = targetEnter;
}
/**
* Sends a mouse event to the current mouse event recipient using
* the given event (sent to the windowed host) as a srcEvent. If
* the mouse event target is still in the component tree, the
* coordinates of the event are translated to those of the target.
* If the target has been removed, we don't bother to send the
* message.
* void retargetMouseEvent(Component target, int id, MouseEvent e) {
* retargetMouseEvent(target, id, e, false) {
* }
*/
/*
* @param repost - true if the new target belongs to a different AppContext
* from the original target
*/
void retargetMouseEvent(Component target, int id, MouseEvent e, boolean repost) {
if (target == null) {
return; // mouse is over another hw component
}
int x = e.getX(), y = e.getY();
Component component;
for (component = target;
component != null && component != window;
component = component.getParent()) {
x -= component.x;
y -= component.y;
}
if (component != null) {
MouseEvent retargeted = new MouseEvent(target,
id,
e.getWhen(),
e.getModifiers(),
x,
y,
e.getClickCount(),
e.isPopupTrigger());
if (target == window) {
// avoid recursively calling LightweightDispatcher...
window.dispatchEventToSelf(retargeted);
} else {
//target.dispatchEvent(retargeted);
if (repost) {
/* can't dispatch event with this EventQueue's
* dispatcher thread. Need to repost to the right EQ.
**/
SunToolkit.postEvent(target.appContext, retargeted);
} else {
target.dispatchEvent(retargeted);
}
}
}
}
/**
* Set the cursor for a lightweight component
* Enforce that null cursor means inheriting from parent
*/
void updateCursor(Component comp) {
// if user wants to change the cursor, we do it even mouse is dragging
// so LightweightDispatcher's dragging state is not checked here
// get the current mouse location
Point currentLoc = GraphicsEnvironment.getLocalGraphicsEnvironment().getMouseLocation();
cursorOn = window.getCursorTarget(currentLoc.x, currentLoc.y);
if (comp != cursorOn) {
return;
}
if (comp == null) {
comp = window;
}
Cursor cursor = comp.getCursor();
while (cursor == null && comp != window) {
comp = comp.getParent();
if (comp == null) {
cursor = window.getCursor();
break;
}
cursor = comp.getCursor();
}
if (cursor != lightCursor) {
lightCursor = cursor;
window.changeCursor(lightCursor);
}
}
// --- member variables -------------------------------
/**
* The windowed container that might be hosting events for
* lightweight components.
*/
private Window window;
/**
* The current lightweight component that has focus that is being
* hosted by this container. If this is a null reference then
* there is currently no focus on a lightweight component being
* hosted by this container
*/
private Component focus;
/**
* The current lightweight component being hosted by this windowed
* component that has mouse events being forwarded to it. If this
* is null, there are currently no mouse events being forwarded to
* a lightweight component.
*/
private transient Component mouseEventTarget;
/**
* lightweight component the mouse cursor is on
*/
private transient Component cursorOn;
/**
* The last component entered
*/
private transient Component targetLastEntered;
/**
* Is the mouse over the native container
*/
private transient boolean isMouseInNativeContainer = false;
/**
* Is the next click event orphaned because the component hid/moved
*/
private transient boolean isClickOrphaned = false;
/**
* Indicates if the mouse pointer is currently being dragged...
* this is needed because we may receive exit events while dragging
* and need to keep the current mouse target in this case.
*/
private boolean dragging;
/**
* The cursor that is currently displayed for the lightwieght
* components. Remember this cursor, so we do not need to
* change cursor on every mouse event.
*/
private Cursor lightCursor;
private boolean appContextEquals(Component origTarget, Component newTarget) {
if (origTarget == null || newTarget == null) {
return true;
}
return (origTarget.appContext.getThreadGroup() == newTarget.appContext.getThreadGroup());
}
}