Path: blob/master/src/java.desktop/share/classes/java/awt/Cursor.java
41152 views
/*1* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.awt;2627import java.beans.ConstructorProperties;28import java.io.InputStream;29import java.io.Serial;30import java.security.AccessController;31import java.security.PrivilegedAction;32import java.security.PrivilegedExceptionAction;33import java.util.Hashtable;34import java.util.Properties;35import java.util.StringTokenizer;3637import sun.awt.AWTAccessor;38import sun.util.logging.PlatformLogger;3940/**41* A class to encapsulate the bitmap representation of the mouse cursor.42*43* @see Component#setCursor44* @author Amy Fowler45*/46public class Cursor implements java.io.Serializable {4748/**49* The default cursor type (gets set if no cursor is defined).50*/51public static final int DEFAULT_CURSOR = 0;5253/**54* The crosshair cursor type.55*/56public static final int CROSSHAIR_CURSOR = 1;5758/**59* The text cursor type.60*/61public static final int TEXT_CURSOR = 2;6263/**64* The wait cursor type.65*/66public static final int WAIT_CURSOR = 3;6768/**69* The south-west-resize cursor type.70*/71public static final int SW_RESIZE_CURSOR = 4;7273/**74* The south-east-resize cursor type.75*/76public static final int SE_RESIZE_CURSOR = 5;7778/**79* The north-west-resize cursor type.80*/81public static final int NW_RESIZE_CURSOR = 6;8283/**84* The north-east-resize cursor type.85*/86public static final int NE_RESIZE_CURSOR = 7;8788/**89* The north-resize cursor type.90*/91public static final int N_RESIZE_CURSOR = 8;9293/**94* The south-resize cursor type.95*/96public static final int S_RESIZE_CURSOR = 9;9798/**99* The west-resize cursor type.100*/101public static final int W_RESIZE_CURSOR = 10;102103/**104* The east-resize cursor type.105*/106public static final int E_RESIZE_CURSOR = 11;107108/**109* The hand cursor type.110*/111public static final int HAND_CURSOR = 12;112113/**114* The move cursor type.115*/116public static final int MOVE_CURSOR = 13;117118/**119* @deprecated As of JDK version 1.7, the {@link #getPredefinedCursor(int)}120* method should be used instead.121*/122@Deprecated123protected static Cursor[] predefined = new Cursor[14];124125/**126* This field is a private replacement for 'predefined' array.127*/128private static final Cursor[] predefinedPrivate = new Cursor[14];129130/* Localization names and default values */131static 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};147148/**149* The chosen cursor type initially set to150* the {@code DEFAULT_CURSOR}.151*152* @serial153* @see #getType()154*/155int type = DEFAULT_CURSOR;156157/**158* The type associated with all custom cursors.159*/160public static final int CUSTOM_CURSOR = -1;161162/*163* hashtable, resource prefix, filename, and properties for custom cursors164* support165*/166private static final Hashtable<String,Cursor> systemCustomCursors = new Hashtable<>(1);167private static final String RESOURCE_PREFIX = "/sun/awt/resources/cursors/";168private static final String PROPERTIES_FILE = RESOURCE_PREFIX + "cursors.properties";169170private static Properties systemCustomCursorProperties = null;171172private static final String CURSOR_DOT_PREFIX = "Cursor.";173private static final String DOT_FILE_SUFFIX = ".File";174private static final String DOT_HOTSPOT_SUFFIX = ".HotSpot";175private static final String DOT_NAME_SUFFIX = ".Name";176177/**178* Use serialVersionUID from JDK 1.1 for interoperability.179*/180@Serial181private static final long serialVersionUID = 8028237497568985504L;182183private static final PlatformLogger log = PlatformLogger.getLogger("java.awt.Cursor");184185static {186/* ensure that the necessary native libraries are loaded */187Toolkit.loadLibraries();188if (!GraphicsEnvironment.isHeadless()) {189initIDs();190}191192AWTAccessor.setCursorAccessor(193new AWTAccessor.CursorAccessor() {194public long getPData(Cursor cursor) {195return cursor.pData;196}197198public void setPData(Cursor cursor, long pData) {199cursor.pData = pData;200}201202public int getType(Cursor cursor) {203return cursor.type;204}205});206}207208/**209* Initialize JNI field and method IDs for fields that may be210* accessed from C.211*/212private static native void initIDs();213214/**215* Hook into native data.216*/217private transient long pData;218219private transient Object anchor = new Object();220221static class CursorDisposer implements sun.java2d.DisposerRecord {222volatile long pData;223public CursorDisposer(long pData) {224this.pData = pData;225}226public void dispose() {227if (pData != 0) {228finalizeImpl(pData);229}230}231}232transient CursorDisposer disposer;233private void setPData(long pData) {234this.pData = pData;235if (GraphicsEnvironment.isHeadless()) {236return;237}238if (disposer == null) {239disposer = new CursorDisposer(pData);240// anchor is null after deserialization241if (anchor == null) {242anchor = new Object();243}244sun.java2d.Disposer.addRecord(anchor, disposer);245} else {246disposer.pData = pData;247}248}249250/**251* The user-visible name of the cursor.252*253* @serial254* @see #getName()255*/256protected String name;257258/**259* Returns a cursor object with the specified predefined type.260*261* @param type the type of predefined cursor262* @return the specified predefined cursor263* @throws IllegalArgumentException if the specified cursor type is264* invalid265*/266public static Cursor getPredefinedCursor(int type) {267if (type < Cursor.DEFAULT_CURSOR || type > Cursor.MOVE_CURSOR) {268throw new IllegalArgumentException("illegal cursor type");269}270Cursor c = predefinedPrivate[type];271if (c == null) {272predefinedPrivate[type] = c = new Cursor(type);273}274// fill 'predefined' array for backwards compatibility.275if (predefined[type] == null) {276predefined[type] = c;277}278return c;279}280281/**282* Returns a system-specific custom cursor object matching the283* specified name. Cursor names are, for example: "Invalid.16x16"284*285* @param name a string describing the desired system-specific custom cursor286* @return the system specific custom cursor named287* @exception HeadlessException if288* {@code GraphicsEnvironment.isHeadless} returns true289* @exception AWTException in case of erroneous retrieving of the cursor290*/291public static Cursor getSystemCustomCursor(final String name)292throws AWTException, HeadlessException {293GraphicsEnvironment.checkHeadless();294Cursor cursor = systemCustomCursors.get(name);295296if (cursor == null) {297synchronized(systemCustomCursors) {298if (systemCustomCursorProperties == null)299loadSystemCustomCursorProperties();300}301302String prefix = CURSOR_DOT_PREFIX + name;303String key = prefix + DOT_FILE_SUFFIX;304305if (!systemCustomCursorProperties.containsKey(key)) {306if (log.isLoggable(PlatformLogger.Level.FINER)) {307log.finer("Cursor.getSystemCustomCursor(" + name + ") returned null");308}309return null;310}311312final String fileName =313systemCustomCursorProperties.getProperty(key);314315final String localized = systemCustomCursorProperties.getProperty(316prefix + DOT_NAME_SUFFIX, name);317318String hotspot = systemCustomCursorProperties.getProperty(prefix + DOT_HOTSPOT_SUFFIX);319320if (hotspot == null)321throw new AWTException("no hotspot property defined for cursor: " + name);322323StringTokenizer st = new StringTokenizer(hotspot, ",");324325if (st.countTokens() != 2)326throw new AWTException("failed to parse hotspot property for cursor: " + name);327328final Point hotPoint;329try {330hotPoint = new Point(Integer.parseInt(st.nextToken()),331Integer.parseInt(st.nextToken()));332} catch (NumberFormatException nfe) {333throw new AWTException("failed to parse hotspot property for cursor: " + name);334}335final Toolkit toolkit = Toolkit.getDefaultToolkit();336final String file = RESOURCE_PREFIX + fileName;337@SuppressWarnings("removal")338final InputStream in = AccessController.doPrivileged(339(PrivilegedAction<InputStream>) () -> {340return Cursor.class.getResourceAsStream(file);341});342try (in) {343Image image = toolkit.createImage(in.readAllBytes());344cursor = toolkit.createCustomCursor(image, hotPoint, localized);345} catch (Exception e) {346throw new AWTException(347"Exception: " + e.getClass() + " " + e.getMessage() +348" occurred while creating cursor " + name);349}350351if (cursor == null) {352if (log.isLoggable(PlatformLogger.Level.FINER)) {353log.finer("Cursor.getSystemCustomCursor(" + name + ") returned null");354}355} else {356systemCustomCursors.put(name, cursor);357}358}359360return cursor;361}362363/**364* Return the system default cursor.365*366* @return the default cursor367*/368public static Cursor getDefaultCursor() {369return getPredefinedCursor(Cursor.DEFAULT_CURSOR);370}371372/**373* Creates a new cursor object with the specified type.374* @param type the type of cursor375* @throws IllegalArgumentException if the specified cursor type376* is invalid377*/378@ConstructorProperties({"type"})379public Cursor(int type) {380if (type < Cursor.DEFAULT_CURSOR || type > Cursor.MOVE_CURSOR) {381throw new IllegalArgumentException("illegal cursor type");382}383this.type = type;384385// Lookup localized name.386name = Toolkit.getProperty(cursorProperties[type][0],387cursorProperties[type][1]);388}389390/**391* Creates a new custom cursor object with the specified name.<p>392* Note: this constructor should only be used by AWT implementations393* as part of their support for custom cursors. Applications should394* use Toolkit.createCustomCursor().395* @param name the user-visible name of the cursor.396* @see java.awt.Toolkit#createCustomCursor397*/398protected Cursor(String name) {399this.type = Cursor.CUSTOM_CURSOR;400this.name = name;401}402403/**404* Returns the type for this cursor.405*406* @return the cursor type407*/408public int getType() {409return type;410}411412/**413* Returns the name of this cursor.414* @return a localized description of this cursor.415* @since 1.2416*/417public String getName() {418return name;419}420421/**422* Returns a string representation of this cursor.423* @return a string representation of this cursor.424* @since 1.2425*/426public String toString() {427return getClass().getName() + "[" + getName() + "]";428}429430/*431* load the cursor.properties file432*/433@SuppressWarnings("removal")434private static void loadSystemCustomCursorProperties() throws AWTException {435synchronized(systemCustomCursors) {436systemCustomCursorProperties = new Properties();437438try {439AccessController.doPrivileged(440(PrivilegedExceptionAction<Object>) () -> {441try (InputStream is = Cursor.class442.getResourceAsStream(PROPERTIES_FILE)) {443systemCustomCursorProperties.load(is);444}445return null;446});447} catch (Exception e) {448systemCustomCursorProperties = null;449throw new AWTException("Exception: " + e.getClass() + " " +450e.getMessage() + " occurred while loading: " +451PROPERTIES_FILE);452}453}454}455456private static native void finalizeImpl(long pData);457}458459460