001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.swt.graphics;
011:
012: import org.eclipse.swt.internal.carbon.*;
013: import org.eclipse.swt.internal.cocoa.*;
014: import org.eclipse.swt.*;
015:
016: /**
017: * Instances of this class manage operating system resources that
018: * specify the appearance of the on-screen pointer. To create a
019: * cursor you specify the device and either a simple cursor style
020: * describing one of the standard operating system provided cursors
021: * or the image and mask data for the desired appearance.
022: * <p>
023: * Application code must explicitly invoke the <code>Cursor.dispose()</code>
024: * method to release the operating system resources managed by each instance
025: * when those instances are no longer required.
026: * </p>
027: * <dl>
028: * <dt><b>Styles:</b></dt>
029: * <dd>
030: * CURSOR_ARROW, CURSOR_WAIT, CURSOR_CROSS, CURSOR_APPSTARTING, CURSOR_HELP,
031: * CURSOR_SIZEALL, CURSOR_SIZENESW, CURSOR_SIZENS, CURSOR_SIZENWSE, CURSOR_SIZEWE,
032: * CURSOR_SIZEN, CURSOR_SIZES, CURSOR_SIZEE, CURSOR_SIZEW, CURSOR_SIZENE, CURSOR_SIZESE,
033: * CURSOR_SIZESW, CURSOR_SIZENW, CURSOR_UPARROW, CURSOR_IBEAM, CURSOR_NO, CURSOR_HAND
034: * </dd>
035: * </dl>
036: * <p>
037: * Note: Only one of the above styles may be specified.
038: * </p>
039: */
040:
041: public final class Cursor extends Resource {
042:
043: /**
044: * the handle to the OS cursor resource
045: * (Warning: This field is platform dependent)
046: * <p>
047: * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT
048: * public API. It is marked public only so that it can be shared
049: * within the packages provided by SWT. It is not available on all
050: * platforms and should never be accessed from application code.
051: * </p>
052: */
053: public int handle;
054:
055: static boolean initialized;
056:
057: /**
058: * Prevents uninitialized instances from being created outside the package.
059: */
060: Cursor() {
061: }
062:
063: /**
064: * Constructs a new cursor given a device and a style
065: * constant describing the desired cursor appearance.
066: * <p>
067: * You must dispose the cursor when it is no longer required.
068: * </p>
069: *
070: * @param device the device on which to allocate the cursor
071: * @param style the style of cursor to allocate
072: *
073: * @exception IllegalArgumentException <ul>
074: * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
075: * <li>ERROR_INVALID_ARGUMENT - when an unknown style is specified</li>
076: * </ul>
077: * @exception SWTError <ul>
078: * <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li>
079: * </ul>
080: *
081: * @see SWT#CURSOR_ARROW
082: * @see SWT#CURSOR_WAIT
083: * @see SWT#CURSOR_CROSS
084: * @see SWT#CURSOR_APPSTARTING
085: * @see SWT#CURSOR_HELP
086: * @see SWT#CURSOR_SIZEALL
087: * @see SWT#CURSOR_SIZENESW
088: * @see SWT#CURSOR_SIZENS
089: * @see SWT#CURSOR_SIZENWSE
090: * @see SWT#CURSOR_SIZEWE
091: * @see SWT#CURSOR_SIZEN
092: * @see SWT#CURSOR_SIZES
093: * @see SWT#CURSOR_SIZEE
094: * @see SWT#CURSOR_SIZEW
095: * @see SWT#CURSOR_SIZENE
096: * @see SWT#CURSOR_SIZESE
097: * @see SWT#CURSOR_SIZESW
098: * @see SWT#CURSOR_SIZENW
099: * @see SWT#CURSOR_UPARROW
100: * @see SWT#CURSOR_IBEAM
101: * @see SWT#CURSOR_NO
102: * @see SWT#CURSOR_HAND
103: */
104: public Cursor(Device device, int style) {
105: if (device == null)
106: device = Device.getDevice();
107: if (device == null)
108: SWT.error(SWT.ERROR_NULL_ARGUMENT);
109: this .device = device;
110: switch (style) {
111: case SWT.CURSOR_HAND:
112: handle = OS.kThemePointingHandCursor;
113: break;
114: case SWT.CURSOR_ARROW:
115: handle = OS.kThemeArrowCursor;
116: break;
117: case SWT.CURSOR_WAIT:
118: handle = OS.kThemeSpinningCursor;
119: break;
120: case SWT.CURSOR_CROSS:
121: handle = OS.kThemeCrossCursor;
122: break;
123: case SWT.CURSOR_APPSTARTING:
124: handle = OS.kThemeArrowCursor;
125: break;
126: case SWT.CURSOR_HELP:
127: handle = OS.kThemeCrossCursor;
128: break;
129: case SWT.CURSOR_SIZEALL:
130: handle = OS.kThemeCrossCursor;
131: break;
132: case SWT.CURSOR_SIZENESW:
133: handle = OS.kThemeCrossCursor;
134: break;
135: case SWT.CURSOR_SIZENS:
136: handle = OS.kThemeResizeUpDownCursor;
137: break;
138: case SWT.CURSOR_SIZENWSE:
139: handle = OS.kThemeCrossCursor;
140: break;
141: case SWT.CURSOR_SIZEWE:
142: handle = OS.kThemeResizeLeftRightCursor;
143: break;
144: case SWT.CURSOR_SIZEN:
145: handle = OS.kThemeResizeUpCursor;
146: break;
147: case SWT.CURSOR_SIZES:
148: handle = OS.kThemeResizeDownCursor;
149: break;
150: case SWT.CURSOR_SIZEE:
151: handle = OS.kThemeResizeRightCursor;
152: break;
153: case SWT.CURSOR_SIZEW:
154: handle = OS.kThemeResizeLeftCursor;
155: break;
156: case SWT.CURSOR_SIZENE:
157: handle = OS.kThemeCrossCursor;
158: break;
159: case SWT.CURSOR_SIZESE:
160: handle = OS.kThemeCrossCursor;
161: break;
162: case SWT.CURSOR_SIZESW:
163: handle = OS.kThemeCrossCursor;
164: break;
165: case SWT.CURSOR_SIZENW:
166: handle = OS.kThemeCrossCursor;
167: break;
168: case SWT.CURSOR_UPARROW:
169: handle = OS.kThemeCrossCursor;
170: break;
171: case SWT.CURSOR_IBEAM:
172: handle = OS.kThemeIBeamCursor;
173: break;
174: case SWT.CURSOR_NO:
175: handle = OS.kThemeNotAllowedCursor;
176: break;
177: default:
178: SWT.error(SWT.ERROR_INVALID_ARGUMENT);
179: }
180: }
181:
182: /**
183: * Constructs a new cursor given a device, image and mask
184: * data describing the desired cursor appearance, and the x
185: * and y coordinates of the <em>hotspot</em> (that is, the point
186: * within the area covered by the cursor which is considered
187: * to be where the on-screen pointer is "pointing").
188: * <p>
189: * The mask data is allowed to be null, but in this case the source
190: * must be an ImageData representing an icon that specifies both
191: * color data and mask data.
192: * <p>
193: * You must dispose the cursor when it is no longer required.
194: * </p>
195: *
196: * @param device the device on which to allocate the cursor
197: * @param source the color data for the cursor
198: * @param mask the mask data for the cursor (or null)
199: * @param hotspotX the x coordinate of the cursor's hotspot
200: * @param hotspotY the y coordinate of the cursor's hotspot
201: *
202: * @exception IllegalArgumentException <ul>
203: * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
204: * <li>ERROR_NULL_ARGUMENT - if the source is null</li>
205: * <li>ERROR_NULL_ARGUMENT - if the mask is null and the source does not have a mask</li>
206: * <li>ERROR_INVALID_ARGUMENT - if the source and the mask are not the same
207: * size, or if the hotspot is outside the bounds of the image</li>
208: * </ul>
209: * @exception SWTError <ul>
210: * <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li>
211: * </ul>
212: */
213: public Cursor(Device device, ImageData source, ImageData mask,
214: int hotspotX, int hotspotY) {
215: if (device == null)
216: device = Device.getDevice();
217: if (device == null)
218: SWT.error(SWT.ERROR_NULL_ARGUMENT);
219: this .device = device;
220: if (source == null)
221: SWT.error(SWT.ERROR_NULL_ARGUMENT);
222: if (mask == null) {
223: if (source.getTransparencyType() != SWT.TRANSPARENCY_MASK) {
224: SWT.error(SWT.ERROR_NULL_ARGUMENT);
225: }
226: mask = source.getTransparencyMask();
227: }
228: /* Check the bounds. Mask must be the same size as source */
229: if (mask.width != source.width || mask.height != source.height) {
230: SWT.error(SWT.ERROR_INVALID_ARGUMENT);
231: }
232: /* Check the hotspots */
233: if (hotspotX >= source.width || hotspotX < 0
234: || hotspotY >= source.height || hotspotY < 0) {
235: SWT.error(SWT.ERROR_INVALID_ARGUMENT);
236: }
237: if (OS.VERSION >= 0x1040) {
238: byte[] data = new byte[source.width * source.height * 4];
239: for (int y = 0; y < source.height; y++) {
240: int offset = y * source.width * 4;
241: for (int x = 0; x < source.width; x++) {
242: int pixel = source.getPixel(x, y);
243: int maskPixel = mask.getPixel(x, y);
244: if (pixel == 0 && maskPixel == 0) {
245: // BLACK
246: data[offset] = (byte) 0xFF;
247: } else if (pixel == 0 && maskPixel == 1) {
248: // WHITE - cursor color
249: data[offset] = data[offset + 1] = data[offset + 2] = data[offset + 3] = (byte) 0xFF;
250: } else if (pixel == 1 && maskPixel == 0) {
251: // SCREEN
252: } else {
253: /*
254: * Feature in the Macintosh. It is not possible to have
255: * the reverse screen case using NSCursor.
256: * Reverse screen will be the same as screen.
257: */
258: // REVERSE SCREEN -> SCREEN
259: }
260: offset += 4;
261: }
262: }
263: createNSCursor(device, hotspotX, hotspotY, data,
264: source.width, source.height);
265: return;
266: }
267: /* Convert depth to 1 */
268: mask = ImageData.convertMask(mask);
269: source = ImageData.convertMask(source);
270:
271: /* Find the first non transparent pixel if cursor bigger than 16x16. */
272: int width = source.width;
273: int height = source.height;
274: int minX = 0, minY = 0;
275: if (width > 16 || height > 16) {
276: minX = width;
277: minY = height;
278: int maxX = 0, maxY = 0;
279: for (int y = 0; y < height; y++) {
280: for (int x = 0; x < width; x++) {
281: if (!(source.getPixel(x, y) == 1 && mask.getPixel(
282: x, y) == 0)) {
283: minX = Math.min(minX, x);
284: minY = Math.min(minY, y);
285: maxX = Math.max(maxX, x);
286: maxY = Math.max(maxY, y);
287: }
288: }
289: }
290: width = maxX - minX + 1;
291: height = maxY - minY + 1;
292:
293: /* Stretch cursor if still bigger than 16x16. */
294: if (width > 16 || height > 16) {
295: int newWidth = Math.min(width, 16);
296: int newHeight = Math.min(height, 16);
297: ImageData newSource = new ImageData(newWidth,
298: newHeight, source.depth, source.palette, 1,
299: null, 0, null, null, -1, -1, source.type,
300: source.x, source.y, source.disposalMethod,
301: source.delayTime);
302: ImageData newMask = new ImageData(newWidth, newHeight,
303: mask.depth, mask.palette, 1, null, 0, null,
304: null, -1, -1, mask.type, mask.x, mask.y,
305: mask.disposalMethod, mask.delayTime);
306: ImageData.blit(ImageData.BLIT_SRC, source.data,
307: source.depth, source.bytesPerLine, source
308: .getByteOrder(), minX, minY, width,
309: height, null, null, null,
310: ImageData.ALPHA_OPAQUE, null, 0, minX, minY,
311: newSource.data, newSource.depth,
312: newSource.bytesPerLine, newSource
313: .getByteOrder(), 0, 0, newWidth,
314: newHeight, null, null, null, false, false);
315: ImageData.blit(ImageData.BLIT_SRC, mask.data,
316: mask.depth, mask.bytesPerLine, mask
317: .getByteOrder(), minX, minY, width,
318: height, null, null, null,
319: ImageData.ALPHA_OPAQUE, null, 0, minX, minY,
320: newMask.data, newMask.depth,
321: newMask.bytesPerLine, newMask.getByteOrder(),
322: 0, 0, newWidth, newHeight, null, null, null,
323: false, false);
324: width = newWidth;
325: height = newHeight;
326: minX = minY = 0;
327: source = newSource;
328: mask = newMask;
329: }
330: }
331:
332: /* Create the cursor */
333: org.eclipse.swt.internal.carbon.Cursor cursor = new org.eclipse.swt.internal.carbon.Cursor();
334: byte[] srcData = cursor.data;
335: byte[] maskData = cursor.mask;
336: for (int y = 0; y < height; y++) {
337: short d = 0, m = 0;
338: for (int x = 0; x < width; x++) {
339: int bit = 1 << (width - 1 - x);
340: if (source.getPixel(minX + x, minY + y) == 0) {
341: m |= bit;
342: if (mask.getPixel(minX + x, minY + y) == 0)
343: d |= bit;
344: } else if (mask.getPixel(minX + x, minY + y) != 0) {
345: d |= bit;
346: }
347: }
348: srcData[y * 2] = (byte) (d >> 8);
349: srcData[y * 2 + 1] = (byte) (d & 0xFF);
350: maskData[y * 2] = (byte) (m >> 8);
351: maskData[y * 2 + 1] = (byte) (m & 0xFF);
352: }
353: cursor.hotSpot_h = (short) Math.max(0, Math.min(15, hotspotX
354: - minX));
355: cursor.hotSpot_v = (short) Math.max(0, Math.min(15, hotspotY
356: - minY));
357: handle = OS
358: .NewPtr(org.eclipse.swt.internal.carbon.Cursor.sizeof);
359: if (handle == 0)
360: SWT.error(SWT.ERROR_NO_HANDLES);
361: OS.memmove(handle, cursor,
362: org.eclipse.swt.internal.carbon.Cursor.sizeof);
363: }
364:
365: void createNSCursor(Device device, int hotspotX, int hotspotY,
366: byte[] buffer, int width, int height) {
367: if (!initialized) {
368: initialized = true;
369: int window = Cocoa.objc_msgSend(Cocoa.objc_msgSend(
370: Cocoa.C_NSWindow, Cocoa.S_alloc), Cocoa.S_init);
371: Cocoa.objc_msgSend(window, Cocoa.S_release);
372: }
373: int nsImage = Cocoa
374: .objc_msgSend(Cocoa.C_NSImage, Cocoa.S_alloc);
375: if (nsImage == 0)
376: SWT.error(SWT.ERROR_NO_HANDLES);
377: int nsImageRep = Cocoa.objc_msgSend(Cocoa.C_NSBitmapImageRep,
378: Cocoa.S_alloc);
379: if (nsImageRep == 0)
380: SWT.error(SWT.ERROR_NO_HANDLES);
381: this .handle = Cocoa.objc_msgSend(Cocoa.C_NSCursor,
382: Cocoa.S_alloc);
383: if (handle == 0)
384: SWT.error(SWT.ERROR_NO_HANDLES);
385: NSSize size = new NSSize();
386: size.width = width;
387: size.height = height;
388: nsImage = Cocoa.objc_msgSend(nsImage, Cocoa.S_initWithSize,
389: size);
390: nsImageRep = Cocoa.objc_msgSend(nsImageRep,
391: Cocoa.S_initWithBitmapDataPlanes, null, width, height,
392: 8, 4, 1, 0, Cocoa.NSDeviceRGBColorSpace(),
393: Cocoa.NSAlphaFirstBitmapFormat
394: | Cocoa.NSAlphaNonpremultipliedBitmapFormat,
395: width * 4, 32);
396: int bitmapData = Cocoa.objc_msgSend(nsImageRep,
397: Cocoa.S_bitmapData);
398: OS.memmove(bitmapData, buffer, buffer.length);
399: Cocoa.objc_msgSend(nsImage, Cocoa.S_addRepresentation,
400: nsImageRep);
401: NSPoint point = new NSPoint();
402: point.x = hotspotX;
403: point.y = hotspotY;
404: handle = Cocoa.objc_msgSend(handle,
405: Cocoa.S_initWithImage_hotSpot, nsImage, point);
406: Cocoa.objc_msgSend(nsImage, Cocoa.S_release);
407: Cocoa.objc_msgSend(nsImageRep, Cocoa.S_release);
408: }
409:
410: /**
411: * Constructs a new cursor given a device, image data describing
412: * the desired cursor appearance, and the x and y coordinates of
413: * the <em>hotspot</em> (that is, the point within the area
414: * covered by the cursor which is considered to be where the
415: * on-screen pointer is "pointing").
416: * <p>
417: * You must dispose the cursor when it is no longer required.
418: * </p>
419: *
420: * @param device the device on which to allocate the cursor
421: * @param source the image data for the cursor
422: * @param hotspotX the x coordinate of the cursor's hotspot
423: * @param hotspotY the y coordinate of the cursor's hotspot
424: *
425: * @exception IllegalArgumentException <ul>
426: * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
427: * <li>ERROR_NULL_ARGUMENT - if the image is null</li>
428: * <li>ERROR_INVALID_ARGUMENT - if the hotspot is outside the bounds of the
429: * image</li>
430: * </ul>
431: * @exception SWTError <ul>
432: * <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li>
433: * </ul>
434: *
435: * @since 3.0
436: */
437: public Cursor(Device device, ImageData source, int hotspotX,
438: int hotspotY) {
439: if (device == null)
440: device = Device.getDevice();
441: if (device == null)
442: SWT.error(SWT.ERROR_NULL_ARGUMENT);
443: this .device = device;
444: if (source == null)
445: SWT.error(SWT.ERROR_NULL_ARGUMENT);
446: if (hotspotX >= source.width || hotspotX < 0
447: || hotspotY >= source.height || hotspotY < 0) {
448: SWT.error(SWT.ERROR_INVALID_ARGUMENT);
449: }
450:
451: if (OS.VERSION >= 0x1040) {
452: byte[] data = new byte[source.width * source.height * 4];
453: PaletteData palette = source.palette;
454: if (palette.isDirect) {
455: ImageData.blit(ImageData.BLIT_SRC, source.data,
456: source.depth, source.bytesPerLine, source
457: .getByteOrder(), 0, 0, source.width,
458: source.height, palette.redMask,
459: palette.greenMask, palette.blueMask,
460: ImageData.ALPHA_OPAQUE, null, 0, 0, 0, data,
461: 32, source.width * 4, ImageData.MSB_FIRST, 0,
462: 0, source.width, source.height, 0xFF0000,
463: 0xFF00, 0xFF, false, false);
464: } else {
465: RGB[] rgbs = palette.getRGBs();
466: int length = rgbs.length;
467: byte[] srcReds = new byte[length];
468: byte[] srcGreens = new byte[length];
469: byte[] srcBlues = new byte[length];
470: for (int i = 0; i < rgbs.length; i++) {
471: RGB rgb = rgbs[i];
472: if (rgb == null)
473: continue;
474: srcReds[i] = (byte) rgb.red;
475: srcGreens[i] = (byte) rgb.green;
476: srcBlues[i] = (byte) rgb.blue;
477: }
478: ImageData.blit(ImageData.BLIT_SRC, source.data,
479: source.depth, source.bytesPerLine, source
480: .getByteOrder(), 0, 0, source.width,
481: source.height, srcReds, srcGreens, srcBlues,
482: ImageData.ALPHA_OPAQUE, null, 0, 0, 0, data,
483: 32, source.width * 4, ImageData.MSB_FIRST, 0,
484: 0, source.width, source.height, 0xFF0000,
485: 0xFF00, 0xFF, false, false);
486: }
487: if (source.maskData != null
488: || source.transparentPixel != -1) {
489: ImageData mask = source.getTransparencyMask();
490: byte[] maskData = mask.data;
491: int maskBpl = mask.bytesPerLine;
492: int offset = 0, maskOffset = 0;
493: for (int y = 0; y < source.height; y++) {
494: for (int x = 0; x < source.width; x++) {
495: data[offset] = ((maskData[maskOffset + (x >> 3)]) & (1 << (7 - (x & 0x7)))) != 0 ? (byte) 0xff
496: : 0;
497: offset += 4;
498: }
499: maskOffset += maskBpl;
500: }
501: } else if (source.alpha != -1) {
502: byte alpha = (byte) source.alpha;
503: for (int i = 0; i < data.length; i += 4) {
504: data[i] = alpha;
505: }
506: } else if (source.alphaData != null) {
507: byte[] alphaData = source.alphaData;
508: for (int i = 0; i < data.length; i += 4) {
509: data[i] = alphaData[i / 4];
510: }
511: }
512: createNSCursor(device, hotspotX, hotspotY, data,
513: source.width, source.height);
514: return;
515: }
516:
517: ImageData mask = source.getTransparencyMask();
518:
519: /* Ensure depth is equal to 1 */
520: if (source.depth > 1) {
521: /* Create a destination image with no data */
522: ImageData newSource = new ImageData(source.width,
523: source.height, 1, ImageData.bwPalette(), 1, null,
524: 0, null, null, -1, -1, 0, 0, 0, 0, 0);
525:
526: byte[] newReds = new byte[] { 0, (byte) 255 }, newGreens = newReds, newBlues = newReds;
527:
528: /* Convert the source to a black and white image of depth 1 */
529: PaletteData palette = source.palette;
530: if (palette.isDirect) {
531: ImageData.blit(ImageData.BLIT_SRC, source.data,
532: source.depth, source.bytesPerLine, source
533: .getByteOrder(), 0, 0, source.width,
534: source.height, palette.redMask,
535: palette.greenMask, palette.blueMask,
536: ImageData.ALPHA_OPAQUE, null, 0, 0, 0,
537: newSource.data, newSource.depth,
538: newSource.bytesPerLine, newSource
539: .getByteOrder(), 0, 0, newSource.width,
540: newSource.height, newReds, newGreens, newBlues,
541: false, false);
542: } else {
543: RGB[] rgbs = palette.getRGBs();
544: int length = rgbs.length;
545: byte[] srcReds = new byte[length];
546: byte[] srcGreens = new byte[length];
547: byte[] srcBlues = new byte[length];
548: for (int i = 0; i < rgbs.length; i++) {
549: RGB rgb = rgbs[i];
550: if (rgb == null)
551: continue;
552: srcReds[i] = (byte) rgb.red;
553: srcGreens[i] = (byte) rgb.green;
554: srcBlues[i] = (byte) rgb.blue;
555: }
556: ImageData.blit(ImageData.BLIT_SRC, source.data,
557: source.depth, source.bytesPerLine, source
558: .getByteOrder(), 0, 0, source.width,
559: source.height, srcReds, srcGreens, srcBlues,
560: ImageData.ALPHA_OPAQUE, null, 0, 0, 0,
561: newSource.data, newSource.depth,
562: newSource.bytesPerLine, newSource
563: .getByteOrder(), 0, 0, newSource.width,
564: newSource.height, newReds, newGreens, newBlues,
565: false, false);
566: }
567: source = newSource;
568: }
569:
570: /* Find the first non transparent pixel if cursor bigger than 16x16. */
571: int width = source.width;
572: int height = source.height;
573: int minX = 0, minY = 0;
574: if (width > 16 || height > 16) {
575: minX = width;
576: minY = height;
577: int maxX = 0, maxY = 0;
578: for (int y = 0; y < height; y++) {
579: for (int x = 0; x < width; x++) {
580: if (!(source.getPixel(x, y) == 1 && mask.getPixel(
581: x, y) == 0)) {
582: minX = Math.min(minX, x);
583: minY = Math.min(minY, y);
584: maxX = Math.max(maxX, x);
585: maxY = Math.max(maxY, y);
586: }
587: }
588: }
589: width = maxX - minX + 1;
590: height = maxY - minY + 1;
591:
592: /* Stretch cursor if still bigger than 16x16. */
593: if (width > 16 || height > 16) {
594: int newWidth = Math.min(width, 16);
595: int newHeight = Math.min(height, 16);
596: ImageData newSource = new ImageData(newWidth,
597: newHeight, source.depth, source.palette, 1,
598: null, 0, null, null, -1, -1, source.type,
599: source.x, source.y, source.disposalMethod,
600: source.delayTime);
601: ImageData newMask = new ImageData(newWidth, newHeight,
602: mask.depth, mask.palette, 1, null, 0, null,
603: null, -1, -1, mask.type, mask.x, mask.y,
604: mask.disposalMethod, mask.delayTime);
605: ImageData.blit(ImageData.BLIT_SRC, source.data,
606: source.depth, source.bytesPerLine, source
607: .getByteOrder(), minX, minY, width,
608: height, null, null, null,
609: ImageData.ALPHA_OPAQUE, null, 0, minX, minY,
610: newSource.data, newSource.depth,
611: newSource.bytesPerLine, newSource
612: .getByteOrder(), 0, 0, newWidth,
613: newHeight, null, null, null, false, false);
614: ImageData.blit(ImageData.BLIT_SRC, mask.data,
615: mask.depth, mask.bytesPerLine, mask
616: .getByteOrder(), minX, minY, width,
617: height, null, null, null,
618: ImageData.ALPHA_OPAQUE, null, 0, minX, minY,
619: newMask.data, newMask.depth,
620: newMask.bytesPerLine, newMask.getByteOrder(),
621: 0, 0, newWidth, newHeight, null, null, null,
622: false, false);
623: width = newWidth;
624: height = newHeight;
625: minX = minY = 0;
626: source = newSource;
627: mask = newMask;
628: }
629: }
630:
631: /* Create the cursor */
632: org.eclipse.swt.internal.carbon.Cursor cursor = new org.eclipse.swt.internal.carbon.Cursor();
633: byte[] srcData = cursor.data;
634: byte[] maskData = cursor.mask;
635: for (int y = 0; y < height; y++) {
636: short d = 0, m = 0;
637: for (int x = 0; x < width; x++) {
638: int bit = 1 << (width - 1 - x);
639: if (source.getPixel(x + minX, y + minY) == 0) {
640: if (mask.getPixel(x + minX, y + minY) != 0) {
641: d |= bit;
642: m |= bit;
643: }
644: } else {
645: if (mask.getPixel(x + minX, y + minY) != 0)
646: m |= bit;
647: }
648: }
649: srcData[y * 2] = (byte) (d >> 8);
650: srcData[y * 2 + 1] = (byte) (d & 0xFF);
651: maskData[y * 2] = (byte) (m >> 8);
652: maskData[y * 2 + 1] = (byte) (m & 0xFF);
653: }
654: cursor.hotSpot_h = (short) Math.max(0, Math.min(15, hotspotX
655: - minX));
656: cursor.hotSpot_v = (short) Math.max(0, Math.min(15, hotspotY
657: - minY));
658: handle = OS
659: .NewPtr(org.eclipse.swt.internal.carbon.Cursor.sizeof);
660: if (handle == 0)
661: SWT.error(SWT.ERROR_NO_HANDLES);
662: OS.memmove(handle, cursor,
663: org.eclipse.swt.internal.carbon.Cursor.sizeof);
664: }
665:
666: /**
667: * Disposes of the operating system resources associated with
668: * the cursor. Applications must dispose of all cursors which
669: * they allocate.
670: */
671: public void dispose() {
672: if (handle == -1)
673: return;
674: if (device.isDisposed())
675: return;
676: switch (handle) {
677: case OS.kThemePointingHandCursor:
678: case OS.kThemeArrowCursor:
679: case OS.kThemeSpinningCursor:
680: case OS.kThemeCrossCursor:
681: case OS.kThemeWatchCursor:
682: case OS.kThemeIBeamCursor:
683: case OS.kThemeNotAllowedCursor:
684: case OS.kThemeResizeLeftRightCursor:
685: case OS.kThemeResizeLeftCursor:
686: case OS.kThemeResizeRightCursor:
687: case OS.kThemeResizeUpDownCursor:
688: case OS.kThemeResizeUpCursor:
689: case OS.kThemeResizeDownCursor:
690: break;
691: default:
692: if (OS.VERSION >= 0x1040) {
693: Cocoa.objc_msgSend(handle, Cocoa.S_release);
694: } else {
695: OS.DisposePtr(handle);
696: }
697: }
698: handle = -1;
699: device = null;
700: }
701:
702: /**
703: * Compares the argument to the receiver, and returns true
704: * if they represent the <em>same</em> object using a class
705: * specific comparison.
706: *
707: * @param object the object to compare with this object
708: * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise
709: *
710: * @see #hashCode
711: */
712: public boolean equals(Object object) {
713: if (object == this )
714: return true;
715: if (!(object instanceof Cursor))
716: return false;
717: Cursor cursor = (Cursor) object;
718: return device == cursor.device && handle == cursor.handle;
719: }
720:
721: /**
722: * Returns an integer hash code for the receiver. Any two
723: * objects that return <code>true</code> when passed to
724: * <code>equals</code> must return the same value for this
725: * method.
726: *
727: * @return the receiver's hash
728: *
729: * @see #equals
730: */
731: public int hashCode() {
732: return handle;
733: }
734:
735: /**
736: * Returns <code>true</code> if the cursor has been disposed,
737: * and <code>false</code> otherwise.
738: * <p>
739: * This method gets the dispose state for the cursor.
740: * When a cursor has been disposed, it is an error to
741: * invoke any other method using the cursor.
742: *
743: * @return <code>true</code> when the cursor is disposed and <code>false</code> otherwise
744: */
745: public boolean isDisposed() {
746: return handle == -1;
747: }
748:
749: /**
750: * Returns a string containing a concise, human-readable
751: * description of the receiver.
752: *
753: * @return a string representation of the receiver
754: */
755: public String toString() {
756: if (isDisposed())
757: return "Cursor {*DISPOSED*}";
758: return "Cursor {" + handle + "}";
759: }
760:
761: /**
762: * Invokes platform specific functionality to allocate a new cursor.
763: * <p>
764: * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
765: * API for <code>Cursor</code>. It is marked public only so that it
766: * can be shared within the packages provided by SWT. It is not
767: * available on all platforms, and should never be called from
768: * application code.
769: * </p>
770: *
771: * @param device the device on which to allocate the color
772: * @param handle the handle for the cursor
773: *
774: * @private
775: */
776: public static Cursor carbon_new(Device device, int handle) {
777: if (device == null)
778: device = Device.getDevice();
779: Cursor cursor = new Cursor();
780: cursor.handle = handle;
781: cursor.device = device;
782: return cursor;
783: }
784:
785: }
|