001: /*
002: * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.awt;
027:
028: import java.awt.*;
029: import java.awt.event.InputEvent;
030: import java.awt.event.InvocationEvent;
031:
032: /**
033: * A stateless class which responds to native mouse moves, Component resizes,
034: * Component moves, showing and hiding of Components, minimizing and
035: * maximizing of top level Windows, addition and removal of Components,
036: * and calls to setCursor().
037: */
038: public abstract class GlobalCursorManager {
039:
040: class NativeUpdater implements Runnable {
041: boolean pending = false;
042:
043: public void run() {
044: boolean shouldUpdate = false;
045: synchronized (this ) {
046: if (pending) {
047: pending = false;
048: shouldUpdate = true;
049: }
050: }
051: if (shouldUpdate) {
052: _updateCursor(false);
053: }
054: }
055:
056: public void postIfNotPending(Component heavy, InvocationEvent in) {
057: boolean shouldPost = false;
058: synchronized (this ) {
059: if (!pending) {
060: pending = shouldPost = true;
061: }
062: }
063: if (shouldPost) {
064: SunToolkit.postEvent(SunToolkit
065: .targetToAppContext(heavy), in);
066: }
067: }
068: }
069:
070: /**
071: * Use a singleton NativeUpdater for better performance. We cannot use
072: * a singleton InvocationEvent because we want each event to have a fresh
073: * timestamp.
074: */
075: private final NativeUpdater nativeUpdater = new NativeUpdater();
076:
077: /**
078: * The last time the cursor was updated, in milliseconds.
079: */
080: private long lastUpdateMillis;
081:
082: /**
083: * Locking object for synchronizing access to lastUpdateMillis. The VM
084: * does not guarantee atomicity of longs.
085: */
086: private final Object lastUpdateLock = new Object();
087:
088: /**
089: * Should be called for any activity at the Java level which may affect
090: * the global cursor, except for Java MOUSE_MOVED events.
091: */
092: public void updateCursorImmediately() {
093: synchronized (nativeUpdater) {
094: nativeUpdater.pending = false;
095: }
096: _updateCursor(false);
097: }
098:
099: /**
100: * Should be called in response to Java MOUSE_MOVED events. The update
101: * will be discarded if the InputEvent is outdated.
102: *
103: * @param e the InputEvent which triggered the cursor update.
104: */
105: public void updateCursorImmediately(InputEvent e) {
106: boolean shouldUpdate;
107: synchronized (lastUpdateLock) {
108: shouldUpdate = (e.getWhen() >= lastUpdateMillis);
109: }
110: if (shouldUpdate) {
111: _updateCursor(true);
112: }
113: }
114:
115: /**
116: * Should be called in response to a native mouse enter or native mouse
117: * button released message. Should not be called during a mouse drag.
118: */
119: public void updateCursorLater(Component heavy) {
120: nativeUpdater.postIfNotPending(heavy, new InvocationEvent(
121: Toolkit.getDefaultToolkit(), nativeUpdater));
122: }
123:
124: protected GlobalCursorManager() {
125: }
126:
127: /**
128: * Set the global cursor to the specified cursor. The component over
129: * which the Cursor current resides is provided as a convenience. Not
130: * all platforms may require the Component.
131: */
132: protected abstract void setCursor(Component comp, Cursor cursor,
133: boolean useCache);
134:
135: /**
136: * Returns the global cursor position, in screen coordinates.
137: */
138: protected abstract void getCursorPos(Point p);
139:
140: protected abstract Component findComponentAt(Container con, int x,
141: int y);
142:
143: protected abstract Point getLocationOnScreen(Component com);
144:
145: /**
146: * Returns the most specific, visible, heavyweight Component
147: * under the cursor. This method should return null iff the cursor is
148: * not over any Java Window.
149: *
150: * @param useCache If true, the implementation is free to use caching
151: * mechanisms because the Z-order, visibility, and enabled state of the
152: * Components has not changed. If false, the implementation should not
153: * make these assumptions.
154: */
155: protected abstract Component findHeavyweightUnderCursor(
156: boolean useCache);
157:
158: /**
159: * Updates the global cursor. We apply a three-step scheme to cursor
160: * updates:<p>
161: *
162: * (1) InputEvent updates which are outdated are discarded by
163: * <code>updateCursorImmediately(InputEvent)</code>.<p>
164: *
165: * (2) If 'useCache' is true, the native code is free to use a cached
166: * value to determine the most specific, visible, enabled heavyweight
167: * because this update is occuring in response to a mouse move. If
168: * 'useCache' is false, the native code must perform a new search given
169: * the current mouse coordinates.
170: *
171: * (3) Once we have determined the most specific, visible, enabled
172: * heavyweight, we use findComponentAt to find the most specific, visible,
173: * enabled Component.
174: */
175: private void _updateCursor(boolean useCache) {
176:
177: synchronized (lastUpdateLock) {
178: lastUpdateMillis = System.currentTimeMillis();
179: }
180:
181: Point queryPos = null, p = null;
182: Component comp;
183:
184: try {
185: comp = findHeavyweightUnderCursor(useCache);
186: if (comp == null) {
187: updateCursorOutOfJava();
188: return;
189: }
190:
191: if (comp instanceof Window) {
192: p = ComponentAccessor.getLocation_NoClientCode(comp);
193: } else if (comp instanceof Container) {
194: p = getLocationOnScreen(comp);
195: }
196: if (p != null) {
197: queryPos = new Point();
198: getCursorPos(queryPos);
199: Component c = findComponentAt((Container) comp,
200: queryPos.x - p.x, queryPos.y - p.y);
201: // If findComponentAt returns null, then something bad has
202: // happened. For example, the heavyweight Component may
203: // have been hidden or disabled by another thread. In that
204: // case, we'll just use the originial heavyweight.
205: if (c != null) {
206: comp = c;
207: }
208: }
209:
210: setCursor(comp, ComponentAccessor
211: .getCursor_NoClientCode(comp), useCache);
212:
213: } catch (IllegalComponentStateException e) {
214: // Shouldn't happen, but if it does, abort.
215: }
216: }
217:
218: protected void updateCursorOutOfJava() {
219: // Cursor is not over a Java Window. Do nothing...usually
220: // But we need to update it in case of grab on X.
221: }
222: }
|