blob: cc1b20b999a03ee81e630e30afe0d93afcdb207a [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package java.awt;
26
27import java.awt.AWTException;
28import java.awt.Point;
29import java.awt.Toolkit;
30
31import java.io.File;
32import java.io.FileInputStream;
33
34import java.beans.ConstructorProperties;
35import java.util.Hashtable;
36import java.util.Properties;
37import java.util.StringTokenizer;
38
39import java.util.logging.*;
40
41import java.security.AccessController;
42
43/**
44 * A class to encapsulate the bitmap representation of the mouse cursor.
45 *
46 * @see Component#setCursor
47 * @author Amy Fowler
48 */
49public class Cursor implements java.io.Serializable {
50
51 /**
52 * The default cursor type (gets set if no cursor is defined).
53 */
54 public static final int DEFAULT_CURSOR = 0;
55
56 /**
57 * The crosshair cursor type.
58 */
59 public static final int CROSSHAIR_CURSOR = 1;
60
61 /**
62 * The text cursor type.
63 */
64 public static final int TEXT_CURSOR = 2;
65
66 /**
67 * The wait cursor type.
68 */
69 public static final int WAIT_CURSOR = 3;
70
71 /**
72 * The south-west-resize cursor type.
73 */
74 public static final int SW_RESIZE_CURSOR = 4;
75
76 /**
77 * The south-east-resize cursor type.
78 */
79 public static final int SE_RESIZE_CURSOR = 5;
80
81 /**
82 * The north-west-resize cursor type.
83 */
84 public static final int NW_RESIZE_CURSOR = 6;
85
86 /**
87 * The north-east-resize cursor type.
88 */
89 public static final int NE_RESIZE_CURSOR = 7;
90
91 /**
92 * The north-resize cursor type.
93 */
94 public static final int N_RESIZE_CURSOR = 8;
95
96 /**
97 * The south-resize cursor type.
98 */
99 public static final int S_RESIZE_CURSOR = 9;
100
101 /**
102 * The west-resize cursor type.
103 */
104 public static final int W_RESIZE_CURSOR = 10;
105
106 /**
107 * The east-resize cursor type.
108 */
109 public static final int E_RESIZE_CURSOR = 11;
110
111 /**
112 * The hand cursor type.
113 */
114 public static final int HAND_CURSOR = 12;
115
116 /**
117 * The move cursor type.
118 */
119 public static final int MOVE_CURSOR = 13;
120
121 protected static Cursor predefined[] = new Cursor[14];
122
123 /* Localization names and default values */
124 static final String[][] cursorProperties = {
125 { "AWT.DefaultCursor", "Default Cursor" },
126 { "AWT.CrosshairCursor", "Crosshair Cursor" },
127 { "AWT.TextCursor", "Text Cursor" },
128 { "AWT.WaitCursor", "Wait Cursor" },
129 { "AWT.SWResizeCursor", "Southwest Resize Cursor" },
130 { "AWT.SEResizeCursor", "Southeast Resize Cursor" },
131 { "AWT.NWResizeCursor", "Northwest Resize Cursor" },
132 { "AWT.NEResizeCursor", "Northeast Resize Cursor" },
133 { "AWT.NResizeCursor", "North Resize Cursor" },
134 { "AWT.SResizeCursor", "South Resize Cursor" },
135 { "AWT.WResizeCursor", "West Resize Cursor" },
136 { "AWT.EResizeCursor", "East Resize Cursor" },
137 { "AWT.HandCursor", "Hand Cursor" },
138 { "AWT.MoveCursor", "Move Cursor" },
139 };
140
141 /**
142 * The chosen cursor type initially set to
143 * the <code>DEFAULT_CURSOR</code>.
144 *
145 * @serial
146 * @see #getType()
147 */
148 int type = DEFAULT_CURSOR;
149
150 /**
151 * The type associated with all custom cursors.
152 */
153 public static final int CUSTOM_CURSOR = -1;
154
155 /*
156 * hashtable, filesystem dir prefix, filename, and properties for custom cursors support
157 */
158
159 private static final Hashtable systemCustomCursors = new Hashtable(1);
160 private static final String systemCustomCursorDirPrefix = initCursorDir();
161
162 private static String initCursorDir() {
163 String jhome = (String) java.security.AccessController.doPrivileged(
164 new sun.security.action.GetPropertyAction("java.home"));
165 return jhome +
166 File.separator + "lib" + File.separator + "images" +
167 File.separator + "cursors" + File.separator;
168 }
169
170 private static final String systemCustomCursorPropertiesFile = systemCustomCursorDirPrefix + "cursors.properties";
171
172 private static Properties systemCustomCursorProperties = null;
173
174 private static final String CursorDotPrefix = "Cursor.";
175 private static final String DotFileSuffix = ".File";
176 private static final String DotHotspotSuffix = ".HotSpot";
177 private static final String DotNameSuffix = ".Name";
178
179 /*
180 * JDK 1.1 serialVersionUID
181 */
182 private static final long serialVersionUID = 8028237497568985504L;
183
184 private static final Logger log = Logger.getLogger("java.awt.Cursor");
185
186 static {
187 /* ensure that the necessary native libraries are loaded */
188 Toolkit.loadLibraries();
189 if (!GraphicsEnvironment.isHeadless()) {
190 initIDs();
191 }
192 }
193
194 /**
195 * Initialize JNI field and method IDs for fields that may be
196 * accessed from C.
197 */
198 private static native void initIDs();
199
200 /**
201 * Hook into native data.
202 */
203 private transient long pData;
204
205 private transient Object anchor = new Object();
206
207 static class CursorDisposer implements sun.java2d.DisposerRecord {
208 volatile long pData;
209 public CursorDisposer(long pData) {
210 this.pData = pData;
211 }
212 public void dispose() {
213 if (pData != 0) {
214 finalizeImpl(pData);
215 }
216 }
217 }
218 transient CursorDisposer disposer;
219 private void setPData(long pData) {
220 this.pData = pData;
221 if (GraphicsEnvironment.isHeadless()) {
222 return;
223 }
224 if (disposer == null) {
225 disposer = new CursorDisposer(pData);
226 // anchor is null after deserialization
227 if (anchor == null) {
228 anchor = new Object();
229 }
230 sun.java2d.Disposer.addRecord(anchor, disposer);
231 } else {
232 disposer.pData = pData;
233 }
234 }
235
236 /**
237 * The user-visible name of the cursor.
238 *
239 * @serial
240 * @see #getName()
241 */
242 protected String name;
243
244 /**
245 * Returns a cursor object with the specified predefined type.
246 *
247 * @param type the type of predefined cursor
248 * @return the specified predefined cursor
249 * @throws IllegalArgumentException if the specified cursor type is
250 * invalid
251 */
252 static public Cursor getPredefinedCursor(int type) {
253 if (type < Cursor.DEFAULT_CURSOR || type > Cursor.MOVE_CURSOR) {
254 throw new IllegalArgumentException("illegal cursor type");
255 }
256 if (predefined[type] == null) {
257 predefined[type] = new Cursor(type);
258 }
259 return predefined[type];
260 }
261
262 /**
263 * Returns a system-specific custom cursor object matching the
264 * specified name. Cursor names are, for example: "Invalid.16x16"
265 *
266 * @param name a string describing the desired system-specific custom cursor
267 * @return the system specific custom cursor named
268 * @exception HeadlessException if
269 * <code>GraphicsEnvironment.isHeadless</code> returns true
270 */
271 static public Cursor getSystemCustomCursor(final String name)
272 throws AWTException, HeadlessException {
273 GraphicsEnvironment.checkHeadless();
274 Cursor cursor = (Cursor)systemCustomCursors.get(name);
275
276 if (cursor == null) {
277 synchronized(systemCustomCursors) {
278 if (systemCustomCursorProperties == null)
279 loadSystemCustomCursorProperties();
280 }
281
282 String prefix = CursorDotPrefix + name;
283 String key = prefix + DotFileSuffix;
284
285 if (!systemCustomCursorProperties.containsKey(key)) {
286 if (log.isLoggable(Level.FINER)) {
287 log.log(Level.FINER, "Cursor.getSystemCustomCursor(" + name + ") returned null");
288 }
289 return null;
290 }
291
292 final String fileName =
293 systemCustomCursorProperties.getProperty(key);
294
295 String localized = (String)systemCustomCursorProperties.getProperty(prefix + DotNameSuffix);
296
297 if (localized == null) localized = name;
298
299 String hotspot = (String)systemCustomCursorProperties.getProperty(prefix + DotHotspotSuffix);
300
301 if (hotspot == null)
302 throw new AWTException("no hotspot property defined for cursor: " + name);
303
304 StringTokenizer st = new StringTokenizer(hotspot, ",");
305
306 if (st.countTokens() != 2)
307 throw new AWTException("failed to parse hotspot property for cursor: " + name);
308
309 int x = 0;
310 int y = 0;
311
312 try {
313 x = Integer.parseInt(st.nextToken());
314 y = Integer.parseInt(st.nextToken());
315 } catch (NumberFormatException nfe) {
316 throw new AWTException("failed to parse hotspot property for cursor: " + name);
317 }
318
319 try {
320 final int fx = x;
321 final int fy = y;
322 final String flocalized = localized;
323
324 cursor = (Cursor) java.security.AccessController.doPrivileged(
325 new java.security.PrivilegedExceptionAction() {
326 public Object run() throws Exception {
327 Toolkit toolkit = Toolkit.getDefaultToolkit();
328 Image image = toolkit.getImage(
329 systemCustomCursorDirPrefix + fileName);
330 return toolkit.createCustomCursor(
331 image, new Point(fx,fy), flocalized);
332 }
333 });
334 } catch (Exception e) {
335 throw new AWTException(
336 "Exception: " + e.getClass() + " " + e.getMessage() +
337 " occurred while creating cursor " + name);
338 }
339
340 if (cursor == null) {
341 if (log.isLoggable(Level.FINER)) {
342 log.log(Level.FINER, "Cursor.getSystemCustomCursor(" + name + ") returned null");
343 }
344 } else {
345 systemCustomCursors.put(name, cursor);
346 }
347 }
348
349 return cursor;
350 }
351
352 /**
353 * Return the system default cursor.
354 */
355 static public Cursor getDefaultCursor() {
356 return getPredefinedCursor(Cursor.DEFAULT_CURSOR);
357 }
358
359 /**
360 * Creates a new cursor object with the specified type.
361 * @param type the type of cursor
362 * @throws IllegalArgumentException if the specified cursor type
363 * is invalid
364 */
365 @ConstructorProperties({"type"})
366 public Cursor(int type) {
367 if (type < Cursor.DEFAULT_CURSOR || type > Cursor.MOVE_CURSOR) {
368 throw new IllegalArgumentException("illegal cursor type");
369 }
370 this.type = type;
371
372 // Lookup localized name.
373 name = Toolkit.getProperty(cursorProperties[type][0],
374 cursorProperties[type][1]);
375 }
376
377 /**
378 * Creates a new custom cursor object with the specified name.<p>
379 * Note: this constructor should only be used by AWT implementations
380 * as part of their support for custom cursors. Applications should
381 * use Toolkit.createCustomCursor().
382 * @param name the user-visible name of the cursor.
383 * @see java.awt.Toolkit#createCustomCursor
384 */
385 protected Cursor(String name) {
386 this.type = Cursor.CUSTOM_CURSOR;
387 this.name = name;
388 }
389
390 /**
391 * Returns the type for this cursor.
392 */
393 public int getType() {
394 return type;
395 }
396
397 /**
398 * Returns the name of this cursor.
399 * @return a localized description of this cursor.
400 * @since 1.2
401 */
402 public String getName() {
403 return name;
404 }
405
406 /**
407 * Returns a string representation of this cursor.
408 * @return a string representation of this cursor.
409 * @since 1.2
410 */
411 public String toString() {
412 return getClass().getName() + "[" + getName() + "]";
413 }
414
415 /*
416 * load the cursor.properties file
417 */
418 private static void loadSystemCustomCursorProperties() throws AWTException {
419 synchronized(systemCustomCursors) {
420 systemCustomCursorProperties = new Properties();
421
422 try {
423 AccessController.doPrivileged(
424 new java.security.PrivilegedExceptionAction() {
425 public Object run() throws Exception {
426 FileInputStream fis = null;
427 try {
428 fis = new FileInputStream(
429 systemCustomCursorPropertiesFile);
430 systemCustomCursorProperties.load(fis);
431 } finally {
432 if (fis != null)
433 fis.close();
434 }
435 return null;
436 }
437 });
438 } catch (Exception e) {
439 systemCustomCursorProperties = null;
440 throw new AWTException("Exception: " + e.getClass() + " " +
441 e.getMessage() + " occurred while loading: " +
442 systemCustomCursorPropertiesFile);
443 }
444 }
445 }
446
447 private native static void finalizeImpl(long pData);
448}