1. /*
  2. * @(#)Cursor.java 1.39 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.awt;
  8. import java.awt.AWTException;
  9. import java.awt.Point;
  10. import java.awt.Toolkit;
  11. import java.io.File;
  12. import java.io.FileInputStream;
  13. import java.util.Enumeration;
  14. import java.util.Hashtable;
  15. import java.util.Properties;
  16. import java.util.StringTokenizer;
  17. import java.security.AccessController;
  18. import sun.awt.DebugHelper;
  19. /**
  20. * A class to encapsulate the bitmap representation of the mouse cursor.
  21. *
  22. * @see Component#setCursor
  23. * @version 1.39, 01/23/03
  24. * @author Amy Fowler
  25. */
  26. public class Cursor implements java.io.Serializable {
  27. /**
  28. * The default cursor type (gets set if no cursor is defined).
  29. */
  30. public static final int DEFAULT_CURSOR = 0;
  31. /**
  32. * The crosshair cursor type.
  33. */
  34. public static final int CROSSHAIR_CURSOR = 1;
  35. /**
  36. * The text cursor type.
  37. */
  38. public static final int TEXT_CURSOR = 2;
  39. /**
  40. * The wait cursor type.
  41. */
  42. public static final int WAIT_CURSOR = 3;
  43. /**
  44. * The south-west-resize cursor type.
  45. */
  46. public static final int SW_RESIZE_CURSOR = 4;
  47. /**
  48. * The south-east-resize cursor type.
  49. */
  50. public static final int SE_RESIZE_CURSOR = 5;
  51. /**
  52. * The north-west-resize cursor type.
  53. */
  54. public static final int NW_RESIZE_CURSOR = 6;
  55. /**
  56. * The north-east-resize cursor type.
  57. */
  58. public static final int NE_RESIZE_CURSOR = 7;
  59. /**
  60. * The north-resize cursor type.
  61. */
  62. public static final int N_RESIZE_CURSOR = 8;
  63. /**
  64. * The south-resize cursor type.
  65. */
  66. public static final int S_RESIZE_CURSOR = 9;
  67. /**
  68. * The west-resize cursor type.
  69. */
  70. public static final int W_RESIZE_CURSOR = 10;
  71. /**
  72. * The east-resize cursor type.
  73. */
  74. public static final int E_RESIZE_CURSOR = 11;
  75. /**
  76. * The hand cursor type.
  77. */
  78. public static final int HAND_CURSOR = 12;
  79. /**
  80. * The move cursor type.
  81. */
  82. public static final int MOVE_CURSOR = 13;
  83. protected static Cursor predefined[] = new Cursor[14];
  84. /* Localization names and default values */
  85. static final String[][] cursorProperties = {
  86. { "AWT.DefaultCursor", "Default Cursor" },
  87. { "AWT.CrosshairCursor", "Crosshair Cursor" },
  88. { "AWT.TextCursor", "Text Cursor" },
  89. { "AWT.WaitCursor", "Wait Cursor" },
  90. { "AWT.SWResizeCursor", "Southwest Resize Cursor" },
  91. { "AWT.SEResizeCursor", "Southeast Resize Cursor" },
  92. { "AWT.NWResizeCursor", "Northwest Resize Cursor" },
  93. { "AWT.NEResizeCursor", "Northeast Resize Cursor" },
  94. { "AWT.NResizeCursor", "North Resize Cursor" },
  95. { "AWT.SResizeCursor", "South Resize Cursor" },
  96. { "AWT.WResizeCursor", "West Resize Cursor" },
  97. { "AWT.EResizeCursor", "East Resize Cursor" },
  98. { "AWT.HandCursor", "Hand Cursor" },
  99. { "AWT.MoveCursor", "Move Cursor" },
  100. };
  101. /**
  102. * The chosen cursor type initially set to
  103. * the <code>DEFAULT_CURSOR</code>.
  104. *
  105. * @serial
  106. * @see #getType()
  107. */
  108. int type = DEFAULT_CURSOR;
  109. /**
  110. * The type associated with all custom cursors.
  111. */
  112. public static final int CUSTOM_CURSOR = -1;
  113. /*
  114. * hashtable, filesystem dir prefix, filename, and properties for custom cursors support
  115. */
  116. private static final Hashtable systemCustomCursors = new Hashtable(1);
  117. private static final String systemCustomCursorDirPrefix = initCursorDir();
  118. private static String initCursorDir() {
  119. String jhome = (String) java.security.AccessController.doPrivileged(
  120. new sun.security.action.GetPropertyAction("java.home"));
  121. return jhome +
  122. File.separator + "lib" + File.separator + "images" +
  123. File.separator + "cursors" + File.separator;
  124. }
  125. private static final String systemCustomCursorPropertiesFile = systemCustomCursorDirPrefix + "cursors.properties";
  126. private static Properties systemCustomCursorProperties = null;
  127. private static final String CursorDotPrefix = "Cursor.";
  128. private static final String DotFileSuffix = ".File";
  129. private static final String DotHotspotSuffix = ".HotSpot";
  130. private static final String DotNameSuffix = ".Name";
  131. /*
  132. * JDK 1.1 serialVersionUID
  133. */
  134. private static final long serialVersionUID = 8028237497568985504L;
  135. private static final DebugHelper dbg = DebugHelper.create(Cursor.class);
  136. static {
  137. /* ensure that the necessary native libraries are loaded */
  138. Toolkit.loadLibraries();
  139. if (!GraphicsEnvironment.isHeadless()) {
  140. initIDs();
  141. }
  142. }
  143. /**
  144. * Initialize JNI field and method IDs for fields that may be
  145. * accessed from C.
  146. */
  147. private static native void initIDs();
  148. /**
  149. * Hook into native data.
  150. */
  151. private transient long pData;
  152. /**
  153. * The user-visible name of the cursor.
  154. *
  155. * @serial
  156. * @see #getName()
  157. */
  158. protected String name;
  159. /**
  160. * Returns a cursor object with the specified predefined type.
  161. *
  162. * @param type the type of predefined cursor
  163. * @return the specified predefined cursor
  164. * @throws IllegalArgumentException if the specified cursor type is
  165. * invalid
  166. */
  167. static public Cursor getPredefinedCursor(int type) {
  168. if (type < Cursor.DEFAULT_CURSOR || type > Cursor.MOVE_CURSOR) {
  169. throw new IllegalArgumentException("illegal cursor type");
  170. }
  171. if (predefined[type] == null) {
  172. predefined[type] = new Cursor(type);
  173. }
  174. return predefined[type];
  175. }
  176. /**
  177. * Returns a system-specific custom cursor object matching the
  178. * specified name. Cursor names are, for example: "Invalid.16x16"
  179. *
  180. * @param name a string describing the desired system-specific custom cursor
  181. * @return the system specific custom cursor named
  182. * @exception HeadlessException if
  183. * <code>GraphicsEnvironment.isHeadless</code> returns true
  184. */
  185. static public Cursor getSystemCustomCursor(final String name)
  186. throws AWTException, HeadlessException {
  187. GraphicsEnvironment.checkHeadless();
  188. Cursor cursor = (Cursor)systemCustomCursors.get(name);
  189. if (cursor == null) {
  190. synchronized(systemCustomCursors) {
  191. if (systemCustomCursorProperties == null)
  192. loadSystemCustomCursorProperties();
  193. }
  194. String prefix = CursorDotPrefix + name;
  195. String key = prefix + DotFileSuffix;
  196. if (!systemCustomCursorProperties.containsKey(key)) {
  197. if (dbg.on) {
  198. dbg.println("Cursor.getSystemCustomCursor(" + name + ") returned null");
  199. }
  200. return null;
  201. }
  202. final String fileName =
  203. systemCustomCursorProperties.getProperty(key);
  204. String localized = (String)systemCustomCursorProperties.getProperty(prefix + DotNameSuffix);
  205. if (localized == null) localized = name;
  206. String hotspot = (String)systemCustomCursorProperties.getProperty(prefix + DotHotspotSuffix);
  207. if (hotspot == null)
  208. throw new AWTException("no hotspot property defined for cursor: " + name);
  209. StringTokenizer st = new StringTokenizer(hotspot, ",");
  210. if (st.countTokens() != 2)
  211. throw new AWTException("failed to parse hotspot property for cursor: " + name);
  212. int x = 0;
  213. int y = 0;
  214. try {
  215. x = Integer.parseInt(st.nextToken());
  216. y = Integer.parseInt(st.nextToken());
  217. } catch (NumberFormatException nfe) {
  218. throw new AWTException("failed to parse hotspot property for cursor: " + name);
  219. }
  220. try {
  221. final int fx = x;
  222. final int fy = y;
  223. final String flocalized = localized;
  224. cursor = (Cursor) java.security.AccessController.doPrivileged(
  225. new java.security.PrivilegedExceptionAction() {
  226. public Object run() throws Exception {
  227. Toolkit toolkit = Toolkit.getDefaultToolkit();
  228. Image image = toolkit.getImage(
  229. systemCustomCursorDirPrefix + fileName);
  230. return toolkit.createCustomCursor(
  231. image, new Point(fx,fy), flocalized);
  232. }
  233. });
  234. } catch (Exception e) {
  235. throw new AWTException(
  236. "Exception: " + e.getClass() + " " + e.getMessage() +
  237. " occurred while creating cursor " + name);
  238. }
  239. if (cursor == null) {
  240. if (dbg.on) {
  241. dbg.println("Cursor.getSystemCustomCursor(" + name + ") returned null");
  242. }
  243. } else {
  244. systemCustomCursors.put(name, cursor);
  245. }
  246. }
  247. return cursor;
  248. }
  249. /**
  250. * Return the system default cursor.
  251. */
  252. static public Cursor getDefaultCursor() {
  253. return getPredefinedCursor(Cursor.DEFAULT_CURSOR);
  254. }
  255. /**
  256. * Creates a new cursor object with the specified type.
  257. * @param type the type of cursor
  258. * @throws IllegalArgumentException if the specified cursor type
  259. * is invalid
  260. */
  261. public Cursor(int type) {
  262. if (type < Cursor.DEFAULT_CURSOR || type > Cursor.MOVE_CURSOR) {
  263. throw new IllegalArgumentException("illegal cursor type");
  264. }
  265. this.type = type;
  266. // Lookup localized name.
  267. name = Toolkit.getProperty(cursorProperties[type][0],
  268. cursorProperties[type][1]);
  269. }
  270. /**
  271. * Creates a new custom cursor object with the specified name.<p>
  272. * Note: this constructor should only be used by AWT implementations
  273. * as part of their support for custom cursors. Applications should
  274. * use Toolkit.createCustomCursor().
  275. * @param name the user-visible name of the cursor.
  276. * @see java.awt.Toolkit#createCustomCursor
  277. */
  278. protected Cursor(String name) {
  279. this.type = Cursor.CUSTOM_CURSOR;
  280. this.name = name;
  281. }
  282. /**
  283. * Returns the type for this cursor.
  284. */
  285. public int getType() {
  286. return type;
  287. }
  288. /**
  289. * Returns the name of this cursor.
  290. * @return a localized description of this cursor.
  291. * @since 1.2
  292. */
  293. public String getName() {
  294. return name;
  295. }
  296. /**
  297. * Returns a string representation of this cursor.
  298. * @return a string representation of this cursor.
  299. * @since 1.2
  300. */
  301. public String toString() {
  302. return getClass().getName() + "[" + getName() + "]";
  303. }
  304. /*
  305. * load the cursor.properties file
  306. */
  307. private static void loadSystemCustomCursorProperties() throws AWTException {
  308. synchronized(systemCustomCursors) {
  309. systemCustomCursorProperties = new Properties();
  310. try {
  311. AccessController.doPrivileged(
  312. new java.security.PrivilegedExceptionAction() {
  313. public Object run() throws Exception {
  314. FileInputStream fis = null;
  315. try {
  316. fis = new FileInputStream(
  317. systemCustomCursorPropertiesFile);
  318. systemCustomCursorProperties.load(fis);
  319. } finally {
  320. if (fis != null)
  321. fis.close();
  322. }
  323. return null;
  324. }
  325. });
  326. } catch (Exception e) {
  327. systemCustomCursorProperties = null;
  328. throw new AWTException("Exception: " + e.getClass() + " " +
  329. e.getMessage() + " occurred while loading: " +
  330. systemCustomCursorPropertiesFile);
  331. }
  332. }
  333. }
  334. // This won't really throw an exception, but it must match the
  335. // signature of Object.finalize
  336. protected void finalize() throws Throwable {
  337. if (!GraphicsEnvironment.isHeadless()) {
  338. finalizeImpl();
  339. }
  340. }
  341. private native void finalizeImpl() throws Throwable;
  342. }