217 lines
7.6 KiB
Java
217 lines
7.6 KiB
Java
/*
|
|
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code 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. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code 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 in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package sun.awt;
|
|
|
|
import java.awt.*;
|
|
import java.awt.event.InputEvent;
|
|
import java.awt.event.InvocationEvent;
|
|
|
|
/**
|
|
* A stateless class which responds to native mouse moves, Component resizes,
|
|
* Component moves, showing and hiding of Components, minimizing and
|
|
* maximizing of top level Windows, addition and removal of Components,
|
|
* and calls to setCursor().
|
|
*/
|
|
public abstract class GlobalCursorManager {
|
|
|
|
class NativeUpdater implements Runnable {
|
|
boolean pending = false;
|
|
|
|
public void run() {
|
|
boolean shouldUpdate = false;
|
|
synchronized (this) {
|
|
if (pending) {
|
|
pending = false;
|
|
shouldUpdate = true;
|
|
}
|
|
}
|
|
if (shouldUpdate) {
|
|
_updateCursor(false);
|
|
}
|
|
}
|
|
|
|
public void postIfNotPending(Component heavy, InvocationEvent in) {
|
|
boolean shouldPost = false;
|
|
synchronized (this) {
|
|
if (!pending) {
|
|
pending = shouldPost = true;
|
|
}
|
|
}
|
|
if (shouldPost) {
|
|
SunToolkit.postEvent(SunToolkit.targetToAppContext(heavy), in);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Use a singleton NativeUpdater for better performance. We cannot use
|
|
* a singleton InvocationEvent because we want each event to have a fresh
|
|
* timestamp.
|
|
*/
|
|
private final NativeUpdater nativeUpdater = new NativeUpdater();
|
|
|
|
/**
|
|
* The last time the cursor was updated, in milliseconds.
|
|
*/
|
|
private long lastUpdateMillis;
|
|
|
|
/**
|
|
* Locking object for synchronizing access to lastUpdateMillis. The VM
|
|
* does not guarantee atomicity of longs.
|
|
*/
|
|
private final Object lastUpdateLock = new Object();
|
|
|
|
/**
|
|
* Should be called for any activity at the Java level which may affect
|
|
* the global cursor, except for Java MOUSE_MOVED events.
|
|
*/
|
|
public void updateCursorImmediately() {
|
|
synchronized (nativeUpdater) {
|
|
nativeUpdater.pending = false;
|
|
}
|
|
_updateCursor(false);
|
|
}
|
|
|
|
/**
|
|
* Should be called in response to Java MOUSE_MOVED events. The update
|
|
* will be discarded if the InputEvent is outdated.
|
|
*
|
|
* @param e the InputEvent which triggered the cursor update.
|
|
*/
|
|
public void updateCursorImmediately(InputEvent e) {
|
|
boolean shouldUpdate;
|
|
synchronized (lastUpdateLock) {
|
|
shouldUpdate = (e.getWhen() >= lastUpdateMillis);
|
|
}
|
|
if (shouldUpdate) {
|
|
_updateCursor(true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Should be called in response to a native mouse enter or native mouse
|
|
* button released message. Should not be called during a mouse drag.
|
|
*/
|
|
public void updateCursorLater(Component heavy) {
|
|
nativeUpdater.postIfNotPending(heavy, new InvocationEvent
|
|
(Toolkit.getDefaultToolkit(), nativeUpdater));
|
|
}
|
|
|
|
protected GlobalCursorManager() { }
|
|
|
|
/**
|
|
* Set the global cursor to the specified cursor. The component over
|
|
* which the Cursor current resides is provided as a convenience. Not
|
|
* all platforms may require the Component.
|
|
*/
|
|
protected abstract void setCursor(Component comp, Cursor cursor,
|
|
boolean useCache);
|
|
/**
|
|
* Returns the global cursor position, in screen coordinates.
|
|
*/
|
|
protected abstract void getCursorPos(Point p);
|
|
|
|
protected abstract Point getLocationOnScreen(Component com);
|
|
|
|
/**
|
|
* Returns the most specific, visible, heavyweight Component
|
|
* under the cursor. This method should return null iff the cursor is
|
|
* not over any Java Window.
|
|
*
|
|
* @param useCache If true, the implementation is free to use caching
|
|
* mechanisms because the Z-order, visibility, and enabled state of the
|
|
* Components has not changed. If false, the implementation should not
|
|
* make these assumptions.
|
|
*/
|
|
protected abstract Component findHeavyweightUnderCursor(boolean useCache);
|
|
|
|
/**
|
|
* Updates the global cursor. We apply a three-step scheme to cursor
|
|
* updates:<p>
|
|
*
|
|
* (1) InputEvent updates which are outdated are discarded by
|
|
* <code>updateCursorImmediately(InputEvent)</code>.<p>
|
|
*
|
|
* (2) If 'useCache' is true, the native code is free to use a cached
|
|
* value to determine the most specific, visible, enabled heavyweight
|
|
* because this update is occurring in response to a mouse move. If
|
|
* 'useCache' is false, the native code must perform a new search given
|
|
* the current mouse coordinates.
|
|
*
|
|
* (3) Once we have determined the most specific, visible, enabled
|
|
* heavyweight, we use findComponentAt to find the most specific, visible,
|
|
* enabled Component.
|
|
*/
|
|
private void _updateCursor(boolean useCache) {
|
|
|
|
synchronized (lastUpdateLock) {
|
|
lastUpdateMillis = System.currentTimeMillis();
|
|
}
|
|
|
|
Point queryPos = null, p = null;
|
|
Component comp;
|
|
|
|
try {
|
|
comp = findHeavyweightUnderCursor(useCache);
|
|
if (comp == null) {
|
|
updateCursorOutOfJava();
|
|
return;
|
|
}
|
|
|
|
if (comp instanceof Window) {
|
|
p = AWTAccessor.getComponentAccessor().getLocation(comp);
|
|
} else if (comp instanceof Container) {
|
|
p = getLocationOnScreen(comp);
|
|
}
|
|
if (p != null) {
|
|
queryPos = new Point();
|
|
getCursorPos(queryPos);
|
|
Component c = AWTAccessor.getContainerAccessor().
|
|
findComponentAt((Container) comp,
|
|
queryPos.x - p.x, queryPos.y - p.y, false);
|
|
|
|
// If findComponentAt returns null, then something bad has
|
|
// happened. For example, the heavyweight Component may
|
|
// have been hidden or disabled by another thread. In that
|
|
// case, we'll just use the originial heavyweight.
|
|
if (c != null) {
|
|
comp = c;
|
|
}
|
|
}
|
|
|
|
setCursor(comp, AWTAccessor.getComponentAccessor().getCursor(comp), useCache);
|
|
|
|
} catch (IllegalComponentStateException e) {
|
|
// Shouldn't happen, but if it does, abort.
|
|
}
|
|
}
|
|
|
|
protected void updateCursorOutOfJava() {
|
|
// Cursor is not over a Java Window. Do nothing...usually
|
|
// But we need to update it in case of grab on X.
|
|
}
|
|
}
|