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 import java.awt.image.BufferedImage; 30 31 public class CCustomCursor extends Cursor { 32 static Dimension sMaxCursorSize; 33 static Dimension getMaxCursorSize() { 34 if (sMaxCursorSize != null) return sMaxCursorSize; 35 final Rectangle bounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().getBounds(); 36 return sMaxCursorSize = new Dimension(bounds.width / 2, bounds.height / 2); 37 } 38 39 Image fImage; 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). 65 if (tracker.isErrorAny() || width < 0 || height < 0) { 66 fHotspot.x = fHotspot.y = 0; 67 width = height = 1; 68 fImage = createTransparentImage(width, height); 69 } else { 70 // Scale image to nearest supported size 71 final Dimension nativeSize = toolkit.getBestCursorSize(width, height); 72 if (nativeSize.width != width || nativeSize.height != height) { 73 fImage = fImage.getScaledInstance(nativeSize.width, nativeSize.height, Image.SCALE_DEFAULT); 74 width = nativeSize.width; 75 height = nativeSize.height; 76 } 77 } 78 79 // NOTE: this was removed for 3169146, but in 1.5 the JCK tests for an exception and fails if one isn't thrown. 80 // See what JBuilder does. 81 // Verify that the hotspot is within cursor bounds. 82 if (fHotspot.x >= width || fHotspot.y >= height || fHotspot.x < 0 || fHotspot.y < 0) { 83 throw new IndexOutOfBoundsException("invalid hotSpot"); 84 } 85 86 // Must normalize the hotspot 87 if (fHotspot.x >= width) { 88 fHotspot.x = width - 1; // it is zero based. 89 } else if (fHotspot.x < 0) { 90 fHotspot.x = 0; 91 } 92 if (fHotspot.y >= height) { 93 fHotspot.y = height - 1; // it is zero based. 94 } else if (fHotspot.y < 0) { 95 fHotspot.y = 0; 96 } 97 } 98 99 private static BufferedImage createTransparentImage(int w, int h) { 100 GraphicsEnvironment ge = 101 GraphicsEnvironment.getLocalGraphicsEnvironment(); 102 GraphicsDevice gs = ge.getDefaultScreenDevice(); 103 GraphicsConfiguration gc = gs.getDefaultConfiguration(); 104 105 BufferedImage img = gc.createCompatibleImage(w, h, Transparency.BITMASK); 106 Graphics2D g = (Graphics2D)img.getGraphics(); 107 g.setBackground(new Color(0, 0, 0, 0)); 108 g.clearRect(0, 0, w, h); 109 g.dispose(); 110 111 return img; 112 } 113 114 public static Dimension getBestCursorSize(final int preferredWidth, final int preferredHeight) { 115 // With Panther, cursors have no limit on their size. So give the client their 116 // preferred size, but no larger than half the dimensions of the main screen 117 // This will allow large cursors, but not cursors so large that they cover the 118 // screen. Since solaris nor windows allow cursors this big, this shouldn't be 119 // a limitation. 120 // JCK triggers an overflow in the int -- if we get a bizarre value normalize it. 121 final Dimension maxCursorSize = getMaxCursorSize(); 122 final Dimension d = new Dimension(Math.max(1, Math.abs(preferredWidth)), Math.max(1, Math.abs(preferredHeight))); 123 return new Dimension(Math.min(d.width, maxCursorSize.width), Math.min(d.height, maxCursorSize.height)); 124 } 125 126 // Called from native when the cursor is set 127 CImage fCImage; 128 long getImageData() { 129 if (fCImage != null) { 130 return fCImage.ptr; 131 } 132 133 try { 134 fCImage = CImage.getCreator().createFromImage(fImage); 135 if (fCImage == null) { 136 // Something unexpected happened: CCustomCursor constructor 137 // takes care of invalid cursor images, yet createFromImage() 138 // failed to do its job. Return null to keep the cursor unchanged. 139 return 0L; 140 } else { 141 return fCImage.ptr; 142 } 143 } catch (IllegalArgumentException iae) { 144 // see comment above 145 return 0L; 146 } 147 } 148 149 Point getHotSpot() { 150 return fHotspot; 151 } 152 }