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