1 /* 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 */ 25 package java.awt; 26 27 import java.awt.AWTException; 28 import java.awt.Point; 29 import java.awt.Toolkit; 30 31 import java.io.File; 32 import java.io.FileInputStream; 33 34 import java.beans.ConstructorProperties; 35 import java.util.Hashtable; 36 import java.util.Properties; 37 import java.util.StringTokenizer; 38 39 import java.util.logging.*; 40 41 import 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 */ 49 public 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 /** 122 * @deprecated As of JDK version 1.7, the {@link #getPredefinedCursor(int)} 123 * method should be used instead. 124 */ 125 @Deprecated 126 protected static Cursor predefined[] = new Cursor[14]; 127 128 /** 129 * This field is a private replacement for 'predefined' array. 130 */ 131 private final static Cursor[] predefinedPrivate = new Cursor[14]; 132 133 /* Localization names and default values */ 134 static final String[][] cursorProperties = { 135 { "AWT.DefaultCursor", "Default Cursor" }, 136 { "AWT.CrosshairCursor", "Crosshair Cursor" }, 137 { "AWT.TextCursor", "Text Cursor" }, 138 { "AWT.WaitCursor", "Wait Cursor" }, 139 { "AWT.SWResizeCursor", "Southwest Resize Cursor" }, 140 { "AWT.SEResizeCursor", "Southeast Resize Cursor" }, 141 { "AWT.NWResizeCursor", "Northwest Resize Cursor" }, 142 { "AWT.NEResizeCursor", "Northeast Resize Cursor" }, 143 { "AWT.NResizeCursor", "North Resize Cursor" }, 144 { "AWT.SResizeCursor", "South Resize Cursor" }, 145 { "AWT.WResizeCursor", "West Resize Cursor" }, 146 { "AWT.EResizeCursor", "East Resize Cursor" }, 147 { "AWT.HandCursor", "Hand Cursor" }, 148 { "AWT.MoveCursor", "Move Cursor" }, 149 }; 150 151 /** 152 * The chosen cursor type initially set to 153 * the <code>DEFAULT_CURSOR</code>. 154 * 155 * @serial 156 * @see #getType() 157 */ 158 int type = DEFAULT_CURSOR; 159 160 /** 161 * The type associated with all custom cursors. 162 */ 163 public static final int CUSTOM_CURSOR = -1; 164 165 /* 166 * hashtable, filesystem dir prefix, filename, and properties for custom cursors support 167 */ 168 169 private static final Hashtable systemCustomCursors = new Hashtable(1); 170 private static final String systemCustomCursorDirPrefix = initCursorDir(); 171 172 private static String initCursorDir() { 173 String jhome = (String) java.security.AccessController.doPrivileged( 174 new sun.security.action.GetPropertyAction("java.home")); 175 return jhome + 176 File.separator + "lib" + File.separator + "images" + 177 File.separator + "cursors" + File.separator; 178 } 179 180 private static final String systemCustomCursorPropertiesFile = systemCustomCursorDirPrefix + "cursors.properties"; 181 182 private static Properties systemCustomCursorProperties = null; 183 184 private static final String CursorDotPrefix = "Cursor."; 185 private static final String DotFileSuffix = ".File"; 186 private static final String DotHotspotSuffix = ".HotSpot"; 187 private static final String DotNameSuffix = ".Name"; 188 189 /* 190 * JDK 1.1 serialVersionUID 191 */ 192 private static final long serialVersionUID = 8028237497568985504L; 193 194 private static final Logger log = Logger.getLogger("java.awt.Cursor"); 195 196 static { 197 /* ensure that the necessary native libraries are loaded */ 198 Toolkit.loadLibraries(); 199 if (!GraphicsEnvironment.isHeadless()) { 200 initIDs(); 201 } 202 } 203 204 /** 205 * Initialize JNI field and method IDs for fields that may be 206 * accessed from C. 207 */ 208 private static native void initIDs(); 209 210 /** 211 * Hook into native data. 212 */ 213 private transient long pData; 214 215 private transient Object anchor = new Object(); 216 217 static class CursorDisposer implements sun.java2d.DisposerRecord { 218 volatile long pData; 219 public CursorDisposer(long pData) { 220 this.pData = pData; 221 } 222 public void dispose() { 223 if (pData != 0) { 224 finalizeImpl(pData); 225 } 226 } 227 } 228 transient CursorDisposer disposer; 229 private void setPData(long pData) { 230 this.pData = pData; 231 if (GraphicsEnvironment.isHeadless()) { 232 return; 233 } 234 if (disposer == null) { 235 disposer = new CursorDisposer(pData); 236 // anchor is null after deserialization 237 if (anchor == null) { 238 anchor = new Object(); 239 } 240 sun.java2d.Disposer.addRecord(anchor, disposer); 241 } else { 242 disposer.pData = pData; 243 } 244 } 245 246 /** 247 * The user-visible name of the cursor. 248 * 249 * @serial 250 * @see #getName() 251 */ 252 protected String name; 253 254 /** 255 * Returns a cursor object with the specified predefined type. 256 * 257 * @param type the type of predefined cursor 258 * @return the specified predefined cursor 259 * @throws IllegalArgumentException if the specified cursor type is 260 * invalid 261 */ 262 static public Cursor getPredefinedCursor(int type) { 263 if (type < Cursor.DEFAULT_CURSOR || type > Cursor.MOVE_CURSOR) { 264 throw new IllegalArgumentException("illegal cursor type"); 265 } 266 Cursor c = predefinedPrivate[type]; 267 if (c == null) { 268 predefinedPrivate[type] = c = new Cursor(type); 269 } 270 // fill 'predefined' array for backwards compatibility. 271 if (predefined[type] == null) { 272 predefined[type] = c; 273 } 274 return c; 275 } 276 277 /** 278 * Returns a system-specific custom cursor object matching the 279 * specified name. Cursor names are, for example: "Invalid.16x16" 280 * 281 * @param name a string describing the desired system-specific custom cursor 282 * @return the system specific custom cursor named 283 * @exception HeadlessException if 284 * <code>GraphicsEnvironment.isHeadless</code> returns true 285 */ 286 static public Cursor getSystemCustomCursor(final String name) 287 throws AWTException, HeadlessException { 288 GraphicsEnvironment.checkHeadless(); 289 Cursor cursor = (Cursor)systemCustomCursors.get(name); 290 291 if (cursor == null) { 292 synchronized(systemCustomCursors) { 293 if (systemCustomCursorProperties == null) 294 loadSystemCustomCursorProperties(); 295 } 296 297 String prefix = CursorDotPrefix + name; 298 String key = prefix + DotFileSuffix; 299 300 if (!systemCustomCursorProperties.containsKey(key)) { 301 if (log.isLoggable(Level.FINER)) { 302 log.log(Level.FINER, "Cursor.getSystemCustomCursor(" + name + ") returned null"); 303 } 304 return null; 305 } 306 307 final String fileName = 308 systemCustomCursorProperties.getProperty(key); 309 310 String localized = (String)systemCustomCursorProperties.getProperty(prefix + DotNameSuffix); 311 312 if (localized == null) localized = name; 313 314 String hotspot = (String)systemCustomCursorProperties.getProperty(prefix + DotHotspotSuffix); 315 316 if (hotspot == null) 317 throw new AWTException("no hotspot property defined for cursor: " + name); 318 319 StringTokenizer st = new StringTokenizer(hotspot, ","); 320 321 if (st.countTokens() != 2) 322 throw new AWTException("failed to parse hotspot property for cursor: " + name); 323 324 int x = 0; 325 int y = 0; 326 327 try { 328 x = Integer.parseInt(st.nextToken()); 329 y = Integer.parseInt(st.nextToken()); 330 } catch (NumberFormatException nfe) { 331 throw new AWTException("failed to parse hotspot property for cursor: " + name); 332 } 333 334 try { 335 final int fx = x; 336 final int fy = y; 337 final String flocalized = localized; 338 339 cursor = (Cursor) java.security.AccessController.doPrivileged( 340 new java.security.PrivilegedExceptionAction() { 341 public Object run() throws Exception { 342 Toolkit toolkit = Toolkit.getDefaultToolkit(); 343 Image image = toolkit.getImage( 344 systemCustomCursorDirPrefix + fileName); 345 return toolkit.createCustomCursor( 346 image, new Point(fx,fy), flocalized); 347 } 348 }); 349 } catch (Exception e) { 350 throw new AWTException( 351 "Exception: " + e.getClass() + " " + e.getMessage() + 352 " occurred while creating cursor " + name); 353 } 354 355 if (cursor == null) { 356 if (log.isLoggable(Level.FINER)) { 357 log.log(Level.FINER, "Cursor.getSystemCustomCursor(" + name + ") returned null"); 358 } 359 } else { 360 systemCustomCursors.put(name, cursor); 361 } 362 } 363 364 return cursor; 365 } 366 367 /** 368 * Return the system default cursor. 369 */ 370 static public Cursor getDefaultCursor() { 371 return getPredefinedCursor(Cursor.DEFAULT_CURSOR); 372 } 373 374 /** 375 * Creates a new cursor object with the specified type. 376 * @param type the type of cursor 377 * @throws IllegalArgumentException if the specified cursor type 378 * is invalid 379 */ 380 @ConstructorProperties({"type"}) 381 public Cursor(int type) { 382 if (type < Cursor.DEFAULT_CURSOR || type > Cursor.MOVE_CURSOR) { 383 throw new IllegalArgumentException("illegal cursor type"); 384 } 385 this.type = type; 386 387 // Lookup localized name. 388 name = Toolkit.getProperty(cursorProperties[type][0], 389 cursorProperties[type][1]); 390 } 391 392 /** 393 * Creates a new custom cursor object with the specified name.<p> 394 * Note: this constructor should only be used by AWT implementations 395 * as part of their support for custom cursors. Applications should 396 * use Toolkit.createCustomCursor(). 397 * @param name the user-visible name of the cursor. 398 * @see java.awt.Toolkit#createCustomCursor 399 */ 400 protected Cursor(String name) { 401 this.type = Cursor.CUSTOM_CURSOR; 402 this.name = name; 403 } 404 405 /** 406 * Returns the type for this cursor. 407 */ 408 public int getType() { 409 return type; 410 } 411 412 /** 413 * Returns the name of this cursor. 414 * @return a localized description of this cursor. 415 * @since 1.2 416 */ 417 public String getName() { 418 return name; 419 } 420 421 /** 422 * Returns a string representation of this cursor. 423 * @return a string representation of this cursor. 424 * @since 1.2 425 */ 426 public String toString() { 427 return getClass().getName() + "[" + getName() + "]"; 428 } 429 430 /* 431 * load the cursor.properties file 432 */ 433 private static void loadSystemCustomCursorProperties() throws AWTException { 434 synchronized(systemCustomCursors) { 435 systemCustomCursorProperties = new Properties(); 436 437 try { 438 AccessController.doPrivileged( 439 new java.security.PrivilegedExceptionAction() { 440 public Object run() throws Exception { 441 FileInputStream fis = null; 442 try { 443 fis = new FileInputStream( 444 systemCustomCursorPropertiesFile); 445 systemCustomCursorProperties.load(fis); 446 } finally { 447 if (fis != null) 448 fis.close(); 449 } 450 return null; 451 } 452 }); 453 } catch (Exception e) { 454 systemCustomCursorProperties = null; 455 throw new AWTException("Exception: " + e.getClass() + " " + 456 e.getMessage() + " occurred while loading: " + 457 systemCustomCursorPropertiesFile); 458 } 459 } 460 } 461 462 private native static void finalizeImpl(long pData); 463 }