1 /* 2 * Copyright (c) 2011, 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 26 package sun.lwawt.macosx; 27 28 import java.awt.*; 29 30 public class CCustomCursor extends Cursor { 31 static Dimension sMaxCursorSize; 32 static Dimension getMaxCursorSize() { 33 if (sMaxCursorSize != null) return sMaxCursorSize; 34 final Rectangle bounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().getBounds(); 35 return sMaxCursorSize = new Dimension(bounds.width / 2, bounds.height / 2); 36 } 37 38 Image fImage; 39 private boolean isImageOk = false; 40 Point fHotspot; 41 42 public CCustomCursor(final Image cursor, final Point hotSpot, final String name) throws IndexOutOfBoundsException, HeadlessException { 43 super(name); 44 fImage = cursor; 45 fHotspot = hotSpot; 46 47 // This chunk of code is copied from sun.awt.CustomCursor 48 final Toolkit toolkit = Toolkit.getDefaultToolkit(); 49 50 // Make sure image is fully loaded. 51 final Component c = new Canvas(); // for its imageUpdate method 52 final MediaTracker tracker = new MediaTracker(c); 53 tracker.addImage(fImage, 0); 54 try { 55 tracker.waitForAll(); 56 } catch (final InterruptedException e) {} 57 58 int width = fImage.getWidth(c); 59 int height = fImage.getHeight(c); 60 61 // Fix for bug 4212593 The Toolkit.createCustomCursor does not 62 // check absence of the image of cursor 63 // If the image is invalid, the cursor will be hidden (made completely 64 // transparent). In this case, getBestCursorSize() will adjust negative w and h, 65 // but we need to set the hotspot inside the image here. 66 if (tracker.isErrorAny() || width < 0 || height < 0) { 67 fHotspot.x = fHotspot.y = 0; 68 isImageOk = false; 69 } else { 70 isImageOk = true; 71 } 72 73 // Scale image to nearest supported size 74 final Dimension nativeSize = toolkit.getBestCursorSize(width, height); 75 if (nativeSize.width != width || nativeSize.height != height) { 76 fImage = fImage.getScaledInstance(nativeSize.width, nativeSize.height, Image.SCALE_DEFAULT); 77 width = nativeSize.width; 78 height = nativeSize.height; 79 } 80 81 // NOTE: this was removed for 3169146, but in 1.5 the JCK tests for an exception and fails if one isn't thrown. 82 // See what JBuilder does. 83 // Verify that the hotspot is within cursor bounds. 84 if (fHotspot.x >= width || fHotspot.y >= height || fHotspot.x < 0 || fHotspot.y < 0) { 85 throw new IndexOutOfBoundsException("invalid hotSpot"); 86 } 87 88 // Must normalize the hotspot 89 if (fHotspot.x >= width) { 90 fHotspot.x = width - 1; // it is zero based. 91 } else if (fHotspot.x < 0) { 92 fHotspot.x = 0; 93 } 94 if (fHotspot.y >= height) { 95 fHotspot.y = height - 1; // it is zero based. 96 } else if (fHotspot.y < 0) { 97 fHotspot.y = 0; 98 } 99 } 100 101 public static Dimension getBestCursorSize(final int preferredWidth, final int preferredHeight) { 102 // With Panther, cursors have no limit on their size. So give the client their 103 // preferred size, but no larger than half the dimensions of the main screen 104 // This will allow large cursors, but not cursors so large that they cover the 105 // screen. Since solaris nor windows allow cursors this big, this shouldn't be 106 // a limitation. 107 // JCK triggers an overflow in the int -- if we get a bizarre value normalize it. 108 final Dimension maxCursorSize = getMaxCursorSize(); 109 final Dimension d = new Dimension(Math.max(1, Math.abs(preferredWidth)), Math.max(1, Math.abs(preferredHeight))); 110 return new Dimension(Math.min(d.width, maxCursorSize.width), Math.min(d.height, maxCursorSize.height)); 111 } 112 113 // Called from native when the cursor is set 114 // Returns long array of [NSImage ptr, x hotspot, y hotspot] 115 CImage fCImage; 116 long getImageData() { 117 if (fCImage != null) { 118 return fCImage.ptr; 119 } 120 121 if (isImageOk) { 122 try { 123 fCImage = CImage.getCreator().createFromImage(fImage); 124 125 if (fCImage == null) { 126 isImageOk = false; 127 return 0L; 128 } else { 129 return fCImage.ptr; 130 } 131 } catch (IllegalArgumentException iae) { 132 // Silently return null - we want to hide cursor by providing an empty 133 // ByteArray or just null 134 return 0L; 135 } 136 } 137 138 return 0L; 139 } 140 141 Point getHotSpot() { 142 return fHotspot; 143 } 144 }