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 int fWidth; 42 int fHeight; 43 44 public CCustomCursor(final Image cursor, final Point hotSpot, final String name) throws IndexOutOfBoundsException, HeadlessException { 45 super(name); 46 fImage = cursor; 47 fHotspot = hotSpot; 48 49 // This chunk of code is copied from sun.awt.CustomCursor 50 final Toolkit toolkit = Toolkit.getDefaultToolkit(); 51 52 // Make sure image is fully loaded. 53 final Component c = new Canvas(); // for its imageUpdate method 54 final MediaTracker tracker = new MediaTracker(c); 55 // MediaTracker loads resolution variants from MultiResolution Toolkit image 56 tracker.addImage(fImage, 0); 57 try { 58 tracker.waitForAll(); 59 } catch (final InterruptedException e) {} 60 61 int width = fImage.getWidth(c); 62 int height = fImage.getHeight(c); 63 64 // Fix for bug 4212593 The Toolkit.createCustomCursor does not 65 // check absence of the image of cursor 66 // If the image is invalid, the cursor will be hidden (made completely 67 // transparent). 68 if (tracker.isErrorAny() || width < 0 || height < 0) { 69 fHotspot.x = fHotspot.y = 0; 70 width = height = 1; 71 fImage = createTransparentImage(width, height); 72 } else { 73 // Get the nearest supported cursor size 74 final Dimension nativeSize = toolkit.getBestCursorSize(width, height); 75 width = nativeSize.width; 76 height = nativeSize.height; 77 } 78 79 fWidth = width; 80 fHeight = height; 81 82 // NOTE: this was removed for 3169146, but in 1.5 the JCK tests for an exception and fails if one isn't thrown. 83 // See what JBuilder does. 84 // Verify that the hotspot is within cursor bounds. 85 if (fHotspot.x >= width || fHotspot.y >= height || fHotspot.x < 0 || fHotspot.y < 0) { 86 throw new IndexOutOfBoundsException("invalid hotSpot"); 87 } 88 89 // Must normalize the hotspot 90 if (fHotspot.x >= width) { 91 fHotspot.x = width - 1; // it is zero based. 92 } else if (fHotspot.x < 0) { 93 fHotspot.x = 0; 94 } 95 if (fHotspot.y >= height) { 96 fHotspot.y = height - 1; // it is zero based. 97 } else if (fHotspot.y < 0) { 98 fHotspot.y = 0; 99 } 100 } 101 102 private static BufferedImage createTransparentImage(int w, int h) { 103 GraphicsEnvironment ge = 104 GraphicsEnvironment.getLocalGraphicsEnvironment(); 105 GraphicsDevice gs = ge.getDefaultScreenDevice(); 106 GraphicsConfiguration gc = gs.getDefaultConfiguration(); 107 108 BufferedImage img = gc.createCompatibleImage(w, h, Transparency.BITMASK); 109 Graphics2D g = (Graphics2D)img.getGraphics(); 110 g.setBackground(new Color(0, 0, 0, 0)); 111 g.clearRect(0, 0, w, h); 112 g.dispose(); 113 114 return img; 115 } 116 117 public static Dimension getBestCursorSize(final int preferredWidth, final int preferredHeight) { 118 // With Panther, cursors have no limit on their size. So give the client their 119 // preferred size, but no larger than half the dimensions of the main screen 120 // This will allow large cursors, but not cursors so large that they cover the 121 // screen. Since solaris nor windows allow cursors this big, this shouldn't be 122 // a limitation. 123 // JCK triggers an overflow in the int -- if we get a bizarre value normalize it. 124 final Dimension maxCursorSize = getMaxCursorSize(); 125 final Dimension d = new Dimension(Math.max(1, Math.abs(preferredWidth)), Math.max(1, Math.abs(preferredHeight))); 126 return new Dimension(Math.min(d.width, maxCursorSize.width), Math.min(d.height, maxCursorSize.height)); 127 } 128 129 // Called from native when the cursor is set 130 CImage fCImage; 131 long getImageData() { 132 if (fCImage != null) { 133 return fCImage.ptr; 134 } 135 136 try { 137 fCImage = CImage.getCreator().createFromImage(fImage); 138 if (fCImage == null) { 139 // Something unexpected happened: CCustomCursor constructor 140 // takes care of invalid cursor images, yet createFromImage() 141 // failed to do its job. Return null to keep the cursor unchanged. 142 return 0L; 143 } else { 144 fCImage.resizeRepresentations(fWidth, fHeight); 145 return fCImage.ptr; 146 } 147 } catch (IllegalArgumentException iae) { 148 // see comment above 149 return 0L; 150 } 151 } 152 153 Point getHotSpot() { 154 return fHotspot; 155 } 156 }