diff options
author | Silenio Quarti <silenio> | 2009-07-01 14:50:54 +0000 |
---|---|---|
committer | Silenio Quarti <silenio> | 2009-07-01 14:50:54 +0000 |
commit | 093c579a4ffd9551acb901bba9617e7aa776989d (patch) | |
tree | 71cf23798b651ef92f188390841a8d130908fb11 /bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse | |
parent | f664d297f7bb009784868bf3fcf0b3e3bb9a646b (diff) | |
download | eclipse.platform.swt-093c579a4ffd9551acb901bba9617e7aa776989d.tar.gz eclipse.platform.swt-093c579a4ffd9551acb901bba9617e7aa776989d.tar.xz eclipse.platform.swt-093c579a4ffd9551acb901bba9617e7aa776989d.zip |
restore HEAD after accidental deletion by error in automated build script
Diffstat (limited to 'bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse')
58 files changed, 56505 insertions, 0 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Color.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Color.java new file mode 100755 index 0000000000..dc1823530d --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Color.java @@ -0,0 +1,266 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + + +import org.eclipse.swt.*; + +/** + * Instances of this class manage the operating system resources that + * implement SWT's RGB color model. To create a color you can either + * specify the individual color components as integers in the range + * 0 to 255 or provide an instance of an <code>RGB</code>. + * <p> + * Application code must explicitly invoke the <code>Color.dispose()</code> + * method to release the operating system resources managed by each instance + * when those instances are no longer required. + * </p> + * + * @see RGB + * @see Device#getSystemColor + * @see <a href="http://www.eclipse.org/swt/snippets/#color">Color and RGB snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: PaintExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + */ +public final class Color extends Resource { + /** + * the handle to the OS color resource + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public float /*double*/ [] handle; + +Color(Device device) { + super(device); +} + +/** + * Constructs a new instance of this class given a device and the + * desired red, green and blue values expressed as ints in the range + * 0 to 255 (where 0 is black and 255 is full brightness). On limited + * color devices, the color instance created by this call may not have + * the same RGB values as the ones specified by the arguments. The + * RGB values on the returned instance will be the color values of + * the operating system color. + * <p> + * You must dispose the color when it is no longer required. + * </p> + * + * @param device the device on which to allocate the color + * @param red the amount of red in the color + * @param green the amount of green in the color + * @param blue the amount of blue in the color + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * <li>ERROR_INVALID_ARGUMENT - if the red, green or blue argument is not between 0 and 255</li> + * </ul> + * + * @see #dispose + */ +public Color(Device device, int red, int green, int blue) { + super(device); + init(red, green, blue); + init(); +} + +/** + * Constructs a new instance of this class given a device and an + * <code>RGB</code> describing the desired red, green and blue values. + * On limited color devices, the color instance created by this call + * may not have the same RGB values as the ones specified by the + * argument. The RGB values on the returned instance will be the color + * values of the operating system color. + * <p> + * You must dispose the color when it is no longer required. + * </p> + * + * @param device the device on which to allocate the color + * @param rgb the RGB values of the desired color + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * <li>ERROR_NULL_ARGUMENT - if the rgb argument is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the red, green or blue components of the argument are not between 0 and 255</li> + * </ul> + * + * @see #dispose + */ +public Color(Device device, RGB rgb) { + super(device); + if (rgb == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + init(rgb.red, rgb.green, rgb.blue); + init(); +} + +void destroy() { + handle = null; +} + +/** + * Compares the argument to the receiver, and returns true + * if they represent the <em>same</em> object using a class + * specific comparison. + * + * @param object the object to compare with this object + * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise + * + * @see #hashCode + */ +public boolean equals(Object object) { + if (object == this) return true; + if (!(object instanceof Color)) return false; + Color color = (Color)object; + float /*double*/ [] rgbColor = color.handle; + if (handle == rgbColor) return true; + return device == color.device && + (int)(handle[0] * 255) == (int)(rgbColor[0] * 255) && + (int)(handle[1] * 255) == (int)(rgbColor[1] * 255) && + (int)(handle[2] * 255) == (int)(rgbColor[2] * 255); +} + +/** + * Returns the amount of blue in the color, from 0 to 255. + * + * @return the blue component of the color + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int getBlue() { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return (int)(handle[2] * 255); +} + +/** + * Returns the amount of green in the color, from 0 to 255. + * + * @return the green component of the color + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int getGreen() { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return (int)(handle[1] * 255); +} + +/** + * Returns the amount of red in the color, from 0 to 255. + * + * @return the red component of the color + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int getRed() { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return (int)(handle[0] * 255); +} + +/** + * Returns an integer hash code for the receiver. Any two + * objects that return <code>true</code> when passed to + * <code>equals</code> must return the same value for this + * method. + * + * @return the receiver's hash + * + * @see #equals + */ +public int hashCode() { + if (isDisposed()) return 0; + return (int)(handle[0] * 255) ^ (int)(handle[1] * 255) ^ (int)(handle[2] * 255); +} + +/** + * Returns an <code>RGB</code> representing the receiver. + * + * @return the RGB for the color + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public RGB getRGB () { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return new RGB(getRed(), getGreen(), getBlue()); +} + +/** + * Invokes platform specific functionality to allocate a new color. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>Color</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @param device the device on which to allocate the color + * @param handle the handle for the color + * + * @private + */ +public static Color cocoa_new(Device device, float /*double*/ [] rgbColor) { + Color color = new Color(device); + color.handle = rgbColor; + return color; +} + +void init(int red, int green, int blue) { + if ((red > 255) || (red < 0) || + (green > 255) || (green < 0) || + (blue > 255) || (blue < 0)) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + float /*double*/ [] rgbColor = new float /*double*/ [4]; + rgbColor[0] = red / 255f; + rgbColor[1] = green / 255f; + rgbColor[2] = blue / 255f; + rgbColor[3] = 1; + handle = rgbColor; +} + +/** + * Returns <code>true</code> if the color has been disposed, + * and <code>false</code> otherwise. + * <p> + * This method gets the dispose state for the color. + * When a color has been disposed, it is an error to + * invoke any other method using the color. + * + * @return <code>true</code> when the color is disposed and <code>false</code> otherwise + */ +public boolean isDisposed() { + return handle == null; +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the receiver + */ +public String toString () { + if (isDisposed()) return "Color {*DISPOSED*}"; + return "Color {" + getRed() + ", " + getGreen() + ", " + getBlue() + "}"; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Cursor.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Cursor.java new file mode 100755 index 0000000000..64e4531750 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Cursor.java @@ -0,0 +1,467 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + + +import org.eclipse.swt.internal.cocoa.*; +import org.eclipse.swt.*; + +/** + * Instances of this class manage operating system resources that + * specify the appearance of the on-screen pointer. To create a + * cursor you specify the device and either a simple cursor style + * describing one of the standard operating system provided cursors + * or the image and mask data for the desired appearance. + * <p> + * Application code must explicitly invoke the <code>Cursor.dispose()</code> + * method to release the operating system resources managed by each instance + * when those instances are no longer required. + * </p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd> + * CURSOR_ARROW, CURSOR_WAIT, CURSOR_CROSS, CURSOR_APPSTARTING, CURSOR_HELP, + * CURSOR_SIZEALL, CURSOR_SIZENESW, CURSOR_SIZENS, CURSOR_SIZENWSE, CURSOR_SIZEWE, + * CURSOR_SIZEN, CURSOR_SIZES, CURSOR_SIZEE, CURSOR_SIZEW, CURSOR_SIZENE, CURSOR_SIZESE, + * CURSOR_SIZESW, CURSOR_SIZENW, CURSOR_UPARROW, CURSOR_IBEAM, CURSOR_NO, CURSOR_HAND + * </dd> + * </dl> + * <p> + * Note: Only one of the above styles may be specified. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#cursor">Cursor snippets</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + */ + +public final class Cursor extends Resource { + + static final byte[] WAIT_SOURCE = new byte[] { + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + }; + + /** + * the handle to the OS cursor resource + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public NSCursor handle; + +/** + * Prevents uninitialized instances from being created outside the package. + */ +Cursor(Device device) { + super(device); +} + +/** + * Constructs a new cursor given a device and a style + * constant describing the desired cursor appearance. + * <p> + * You must dispose the cursor when it is no longer required. + * </p> + * + * @param device the device on which to allocate the cursor + * @param style the style of cursor to allocate + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * <li>ERROR_INVALID_ARGUMENT - when an unknown style is specified</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li> + * </ul> + * + * @see SWT#CURSOR_ARROW + * @see SWT#CURSOR_WAIT + * @see SWT#CURSOR_CROSS + * @see SWT#CURSOR_APPSTARTING + * @see SWT#CURSOR_HELP + * @see SWT#CURSOR_SIZEALL + * @see SWT#CURSOR_SIZENESW + * @see SWT#CURSOR_SIZENS + * @see SWT#CURSOR_SIZENWSE + * @see SWT#CURSOR_SIZEWE + * @see SWT#CURSOR_SIZEN + * @see SWT#CURSOR_SIZES + * @see SWT#CURSOR_SIZEE + * @see SWT#CURSOR_SIZEW + * @see SWT#CURSOR_SIZENE + * @see SWT#CURSOR_SIZESE + * @see SWT#CURSOR_SIZESW + * @see SWT#CURSOR_SIZENW + * @see SWT#CURSOR_UPARROW + * @see SWT#CURSOR_IBEAM + * @see SWT#CURSOR_NO + * @see SWT#CURSOR_HAND + */ +public Cursor(Device device, int style) { + super(device); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + switch (style) { + case SWT.CURSOR_HAND: handle = NSCursor.pointingHandCursor(); break; + case SWT.CURSOR_ARROW: handle = NSCursor.arrowCursor(); break; + case SWT.CURSOR_WAIT: break; + case SWT.CURSOR_CROSS: handle = NSCursor.crosshairCursor(); break; + case SWT.CURSOR_APPSTARTING: handle = NSCursor.arrowCursor(); break; + case SWT.CURSOR_HELP: handle = NSCursor.crosshairCursor(); break; + case SWT.CURSOR_SIZEALL: handle = NSCursor.crosshairCursor(); break; + case SWT.CURSOR_SIZENESW: handle = NSCursor.crosshairCursor(); break; + case SWT.CURSOR_SIZENS: handle = NSCursor.resizeUpDownCursor(); break; + case SWT.CURSOR_SIZENWSE: handle = NSCursor.crosshairCursor(); break; + case SWT.CURSOR_SIZEWE: handle = NSCursor.resizeLeftRightCursor(); break; + case SWT.CURSOR_SIZEN: handle = NSCursor.resizeUpCursor(); break; + case SWT.CURSOR_SIZES: handle = NSCursor.resizeDownCursor(); break; + case SWT.CURSOR_SIZEE: handle = NSCursor.resizeRightCursor(); break; + case SWT.CURSOR_SIZEW: handle = NSCursor.resizeLeftCursor(); break; + case SWT.CURSOR_SIZENE: handle = NSCursor.crosshairCursor(); break; + case SWT.CURSOR_SIZESE: handle = NSCursor.crosshairCursor(); break; + case SWT.CURSOR_SIZESW: handle = NSCursor.crosshairCursor(); break; + case SWT.CURSOR_SIZENW: handle = NSCursor.crosshairCursor(); break; + case SWT.CURSOR_UPARROW: handle = NSCursor.crosshairCursor(); break; + case SWT.CURSOR_IBEAM: handle = NSCursor.IBeamCursor(); break; + case SWT.CURSOR_NO: handle = NSCursor.crosshairCursor(); break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + if (handle == null && style == SWT.CURSOR_WAIT) { + NSImage nsImage = (NSImage)new NSImage().alloc(); + NSBitmapImageRep nsImageRep = (NSBitmapImageRep)new NSBitmapImageRep().alloc(); + handle = (NSCursor)new NSCursor().alloc(); + int width = 16, height = 16; + NSSize size = new NSSize(); + size.width = width; + size.height = height; + nsImage = nsImage.initWithSize(size); + nsImageRep = nsImageRep.initWithBitmapDataPlanes(0, width, height, 8, 4, true, false, OS.NSDeviceRGBColorSpace, + OS.NSAlphaFirstBitmapFormat | OS.NSAlphaNonpremultipliedBitmapFormat, width*4, 32); + OS.memmove(nsImageRep.bitmapData(), WAIT_SOURCE, WAIT_SOURCE.length); + nsImage.addRepresentation(nsImageRep); + NSPoint point = new NSPoint(); + point.x = 0; + point.y = 0; + handle = handle.initWithImage(nsImage, point); + nsImageRep.release(); + nsImage.release(); + } else { + handle.retain(); + } + handle.setOnMouseEntered(true); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Constructs a new cursor given a device, image and mask + * data describing the desired cursor appearance, and the x + * and y coordinates of the <em>hotspot</em> (that is, the point + * within the area covered by the cursor which is considered + * to be where the on-screen pointer is "pointing"). + * <p> + * The mask data is allowed to be null, but in this case the source + * must be an ImageData representing an icon that specifies both + * color data and mask data. + * <p> + * You must dispose the cursor when it is no longer required. + * </p> + * + * @param device the device on which to allocate the cursor + * @param source the color data for the cursor + * @param mask the mask data for the cursor (or null) + * @param hotspotX the x coordinate of the cursor's hotspot + * @param hotspotY the y coordinate of the cursor's hotspot + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * <li>ERROR_NULL_ARGUMENT - if the source is null</li> + * <li>ERROR_NULL_ARGUMENT - if the mask is null and the source does not have a mask</li> + * <li>ERROR_INVALID_ARGUMENT - if the source and the mask are not the same + * size, or if the hotspot is outside the bounds of the image</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li> + * </ul> + */ +public Cursor(Device device, ImageData source, ImageData mask, int hotspotX, int hotspotY) { + super(device); + if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (mask == null) { + if (source.getTransparencyType() != SWT.TRANSPARENCY_MASK) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + mask = source.getTransparencyMask(); + } + /* Check the bounds. Mask must be the same size as source */ + if (mask.width != source.width || mask.height != source.height) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + /* Check the hotspots */ + if (hotspotX >= source.width || hotspotX < 0 || + hotspotY >= source.height || hotspotY < 0) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + byte[] data = new byte[source.width * source.height * 4]; + for (int y = 0; y < source.height; y++) { + int offset = y * source.width * 4; + for (int x = 0; x < source.width; x++) { + int pixel = source.getPixel(x, y); + int maskPixel = mask.getPixel(x, y); + if (pixel == 0 && maskPixel == 0) { + // BLACK + data[offset] = (byte)0xFF; + } else if (pixel == 0 && maskPixel == 1) { + // WHITE - cursor color + data[offset] = data[offset + 1] = data[offset + 2] = data[offset + 3] = (byte)0xFF; + } else if (pixel == 1 && maskPixel == 0) { + // SCREEN + } else { + /* + * Feature in the Macintosh. It is not possible to have + * the reverse screen case using NSCursor. + * Reverse screen will be the same as screen. + */ + // REVERSE SCREEN -> SCREEN + } + offset += 4; + } + } + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + createNSCursor(hotspotX, hotspotY, data, source.width, source.height); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +void createNSCursor(int hotspotX, int hotspotY, byte[] buffer, int width, int height) { + NSImage nsImage = (NSImage)new NSImage().alloc(); + NSBitmapImageRep nsImageRep = (NSBitmapImageRep)new NSBitmapImageRep().alloc(); + handle = (NSCursor)new NSCursor().alloc(); + NSSize size = new NSSize(); + size.width = width; + size.height = height; + nsImage = nsImage.initWithSize(size); + nsImageRep = nsImageRep.initWithBitmapDataPlanes(0, width, height, + 8, 4, true, false, new NSString(OS.NSDeviceRGBColorSpace()), + OS.NSAlphaFirstBitmapFormat | OS.NSAlphaNonpremultipliedBitmapFormat, width * 4, 32); + OS.memmove(nsImageRep.bitmapData(), buffer, buffer.length); + nsImage.addRepresentation(nsImageRep); + NSPoint point = new NSPoint(); + point.x = hotspotX; + point.y = hotspotY; + handle = handle.initWithImage(nsImage, point); + nsImageRep.release(); + nsImage.release(); +} + +/** + * Constructs a new cursor given a device, image data describing + * the desired cursor appearance, and the x and y coordinates of + * the <em>hotspot</em> (that is, the point within the area + * covered by the cursor which is considered to be where the + * on-screen pointer is "pointing"). + * <p> + * You must dispose the cursor when it is no longer required. + * </p> + * + * @param device the device on which to allocate the cursor + * @param source the image data for the cursor + * @param hotspotX the x coordinate of the cursor's hotspot + * @param hotspotY the y coordinate of the cursor's hotspot + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * <li>ERROR_NULL_ARGUMENT - if the image is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the hotspot is outside the bounds of the + * image</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li> + * </ul> + * + * @since 3.0 + */ +public Cursor(Device device, ImageData source, int hotspotX, int hotspotY) { + super(device); + if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (hotspotX >= source.width || hotspotX < 0 || + hotspotY >= source.height || hotspotY < 0) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + byte[] data = new byte[source.width * source.height * 4]; + PaletteData palette = source.palette; + if (palette.isDirect) { + ImageData.blit(ImageData.BLIT_SRC, + source.data, source.depth, source.bytesPerLine, source.getByteOrder(), 0, 0, source.width, source.height, palette.redMask, palette.greenMask, palette.blueMask, + ImageData.ALPHA_OPAQUE, null, 0, 0, 0, + data, 32, source.width * 4, ImageData.MSB_FIRST, 0, 0, source.width, source.height, 0xFF0000, 0xFF00, 0xFF, + false, false); + } else { + RGB[] rgbs = palette.getRGBs(); + int length = rgbs.length; + byte[] srcReds = new byte[length]; + byte[] srcGreens = new byte[length]; + byte[] srcBlues = new byte[length]; + for (int i = 0; i < rgbs.length; i++) { + RGB rgb = rgbs[i]; + if (rgb == null) continue; + srcReds[i] = (byte)rgb.red; + srcGreens[i] = (byte)rgb.green; + srcBlues[i] = (byte)rgb.blue; + } + ImageData.blit(ImageData.BLIT_SRC, + source.data, source.depth, source.bytesPerLine, source.getByteOrder(), 0, 0, source.width, source.height, srcReds, srcGreens, srcBlues, + ImageData.ALPHA_OPAQUE, null, 0, 0, 0, + data, 32, source.width * 4, ImageData.MSB_FIRST, 0, 0, source.width, source.height, 0xFF0000, 0xFF00, 0xFF, + false, false); + } + if (source.maskData != null || source.transparentPixel != -1) { + ImageData mask = source.getTransparencyMask(); + byte[] maskData = mask.data; + int maskBpl = mask.bytesPerLine; + int offset = 0, maskOffset = 0; + for (int y = 0; y<source.height; y++) { + for (int x = 0; x<source.width; x++) { + data[offset] = ((maskData[maskOffset + (x >> 3)]) & (1 << (7 - (x & 0x7)))) != 0 ? (byte)0xff : 0; + offset += 4; + } + maskOffset += maskBpl; + } + } else if (source.alpha != -1) { + byte alpha = (byte)source.alpha; + for (int i=0; i<data.length; i+=4) { + data[i] = alpha; + } + } else if (source.alphaData != null) { + byte[] alphaData = source.alphaData; + for (int i=0; i<data.length; i+=4) { + data[i] = alphaData[i/4]; + } + } + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + createNSCursor(hotspotX, hotspotY, data, source.width, source.height); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +void destroy() { + handle.release(); + handle = null; +} + +/** + * Compares the argument to the receiver, and returns true + * if they represent the <em>same</em> object using a class + * specific comparison. + * + * @param object the object to compare with this object + * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise + * + * @see #hashCode + */ +public boolean equals (Object object) { + if (object == this) return true; + if (!(object instanceof Cursor)) return false; + Cursor cursor = (Cursor) object; + return device == cursor.device && handle == cursor.handle; +} + +/** + * Returns an integer hash code for the receiver. Any two + * objects that return <code>true</code> when passed to + * <code>equals</code> must return the same value for this + * method. + * + * @return the receiver's hash + * + * @see #equals + */ +public int hashCode () { + return handle != null ? (int)/*64*/handle.id : 0; +} + +/** + * Returns <code>true</code> if the cursor has been disposed, + * and <code>false</code> otherwise. + * <p> + * This method gets the dispose state for the cursor. + * When a cursor has been disposed, it is an error to + * invoke any other method using the cursor. + * + * @return <code>true</code> when the cursor is disposed and <code>false</code> otherwise + */ +public boolean isDisposed() { + return handle == null; +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the receiver + */ +public String toString () { + if (isDisposed()) return "Cursor {*DISPOSED*}"; + return "Cursor {" + handle + "}"; +} + +/** + * Invokes platform specific functionality to allocate a new cursor. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>Cursor</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @param device the device on which to allocate the color + * @param handle the handle for the cursor + * + * @private + */ +public static Cursor cocoa_new(Device device, NSCursor handle) { + Cursor cursor = new Cursor(device); + cursor.handle = handle; + return cursor; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Device.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Device.java new file mode 100755 index 0000000000..932e1d49f6 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Device.java @@ -0,0 +1,748 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + +import org.eclipse.swt.*; +import org.eclipse.swt.internal.Compatibility; +import org.eclipse.swt.internal.cocoa.*; + +/** + * This class is the abstract superclass of all device objects, + * such as the Display device and the Printer device. Devices + * can have a graphics context (GC) created for them, and they + * can be drawn on by sending messages to the associated GC. + * + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + */ +public abstract class Device implements Drawable { + + /* Debugging */ + public static boolean DEBUG; + boolean debug = DEBUG; + boolean tracking = DEBUG; + Error [] errors; + Object [] objects; + Object trackingLock; + + /* Disposed flag */ + boolean disposed, warnings; + + Color COLOR_BLACK, COLOR_DARK_RED, COLOR_DARK_GREEN, COLOR_DARK_YELLOW, COLOR_DARK_BLUE; + Color COLOR_DARK_MAGENTA, COLOR_DARK_CYAN, COLOR_GRAY, COLOR_DARK_GRAY, COLOR_RED; + Color COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE; + + /* System Font */ + Font systemFont; + + NSMutableParagraphStyle paragraphStyle; + + /* Device DPI */ + Point dpi; + + /* + * TEMPORARY CODE. When a graphics object is + * created and the device parameter is null, + * the current Display is used. This presents + * a problem because SWT graphics does not + * reference classes in SWT widgets. The correct + * fix is to remove this feature. Unfortunately, + * too many application programs rely on this + * feature. + */ + protected static Device CurrentDevice; + protected static Runnable DeviceFinder; + static { + try { + Class.forName ("org.eclipse.swt.widgets.Display"); + } catch (ClassNotFoundException e) {} + } + +/* +* TEMPORARY CODE. +*/ +static synchronized Device getDevice () { + if (DeviceFinder != null) DeviceFinder.run(); + Device device = CurrentDevice; + CurrentDevice = null; + return device; +} + +/** + * Constructs a new instance of this class. + * <p> + * You must dispose the device when it is no longer required. + * </p> + * + * @see #create + * @see #init + * + * @since 3.1 + */ +public Device() { + this(null); +} + +/** + * Constructs a new instance of this class. + * <p> + * You must dispose the device when it is no longer required. + * </p> + * + * @param data the DeviceData which describes the receiver + * + * @see #create + * @see #init + * @see DeviceData + */ +public Device(DeviceData data) { + synchronized (Device.class) { + if (data != null) { + debug = data.debug; + tracking = data.tracking; + } + if (tracking) { + errors = new Error [128]; + objects = new Object [128]; + trackingLock = new Object (); + } + if (NSThread.isMainThread()) { + NSAutoreleasePool pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + NSThread nsthread = NSThread.currentThread(); + NSMutableDictionary dictionary = nsthread.threadDictionary(); + NSString key = NSString.stringWith("SWT_NSAutoreleasePool"); + id obj = dictionary.objectForKey(key); + if (obj == null) { + NSNumber nsnumber = NSNumber.numberWithInteger(pool.id); + dictionary.setObject(nsnumber, key); + } else { + pool.release(); + } + } + //check and create pool + create (data); + init (); + } +} + +/** + * Throws an <code>SWTException</code> if the receiver can not + * be accessed by the caller. This may include both checks on + * the state of the receiver and more generally on the entire + * execution context. This method <em>should</em> be called by + * device implementors to enforce the standard SWT invariants. + * <p> + * Currently, it is an error to invoke any method (other than + * <code>isDisposed()</code> and <code>dispose()</code>) on a + * device that has had its <code>dispose()</code> method called. + * </p><p> + * In future releases of SWT, there may be more or fewer error + * checks and exceptions may be thrown for different reasons. + * <p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +protected void checkDevice () { + if (disposed) SWT.error(SWT.ERROR_DEVICE_DISPOSED); +} + +/** + * Creates the device in the operating system. If the device + * does not have a handle, this method may do nothing depending + * on the device. + * <p> + * This method is called before <code>init</code>. + * </p><p> + * Subclasses are supposed to reimplement this method and not + * call the <code>super</code> implementation. + * </p> + * + * @param data the DeviceData which describes the receiver + * + * @see #init + */ +protected void create (DeviceData data) { +} + +/** + * Disposes of the operating system resources associated with + * the receiver. After this method has been invoked, the receiver + * will answer <code>true</code> when sent the message + * <code>isDisposed()</code>. + * + * @see #release + * @see #destroy + * @see #checkDevice + */ +public void dispose () { + synchronized (Device.class) { + if (isDisposed()) return; + checkDevice (); + release (); + destroy (); + disposed = true; + if (tracking) { + synchronized (trackingLock) { + printErrors (); + objects = null; + errors = null; + trackingLock = null; + } + } + } +} + +void dispose_Object (Object object) { + synchronized (trackingLock) { + for (int i=0; i<objects.length; i++) { + if (objects [i] == object) { + objects [i] = null; + errors [i] = null; + return; + } + } + } +} + +/** + * Destroys the device in the operating system and releases + * the device's handle. If the device does not have a handle, + * this method may do nothing depending on the device. + * <p> + * This method is called after <code>release</code>. + * </p><p> + * Subclasses are supposed to reimplement this method and not + * call the <code>super</code> implementation. + * </p> + * + * @see #dispose + * @see #release + */ +protected void destroy () { +} + +/** + * Returns a rectangle describing the receiver's size and location. + * + * @return the bounding rectangle + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Rectangle getBounds () { + checkDevice (); + NSRect frame = getPrimaryScreen().frame(); + return new Rectangle((int)frame.x, (int)frame.y, (int)frame.width, (int)frame.height); +} + +/** + * Returns a <code>DeviceData</code> based on the receiver. + * Modifications made to this <code>DeviceData</code> will not + * affect the receiver. + * + * @return a <code>DeviceData</code> containing the device's data and attributes + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see DeviceData + */ +public DeviceData getDeviceData () { + checkDevice(); + DeviceData data = new DeviceData (); + data.debug = debug; + data.tracking = tracking; + if (tracking) { + synchronized (trackingLock) { + int count = 0, length = objects.length; + for (int i=0; i<length; i++) { + if (objects [i] != null) count++; + } + int index = 0; + data.objects = new Object [count]; + data.errors = new Error [count]; + for (int i=0; i<length; i++) { + if (objects [i] != null) { + data.objects [index] = objects [i]; + data.errors [index] = errors [i]; + index++; + } + } + } + } else { + data.objects = new Object [0]; + data.errors = new Error [0]; + } + return data; +} + +/** + * Returns a rectangle which describes the area of the + * receiver which is capable of displaying data. + * + * @return the client area + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #getBounds + */ +public Rectangle getClientArea () { + checkDevice (); + return getBounds (); +} + +/** + * Returns the bit depth of the screen, which is the number of + * bits it takes to represent the number of unique colors that + * the screen is currently capable of displaying. This number + * will typically be one of 1, 8, 15, 16, 24 or 32. + * + * @return the depth of the screen + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int getDepth () { + checkDevice (); + return (int)/*64*/OS.NSBitsPerPixelFromDepth(getPrimaryScreen().depth()); +} + +/** + * Returns a point whose x coordinate is the horizontal + * dots per inch of the display, and whose y coordinate + * is the vertical dots per inch of the display. + * + * @return the horizontal and vertical DPI + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Point getDPI () { + checkDevice (); + return getScreenDPI(); +} + +NSScreen getPrimaryScreen () { + NSArray screens = NSScreen.screens(); + return new NSScreen(screens.objectAtIndex(0)); +} + +/** + * Returns <code>FontData</code> objects which describe + * the fonts that match the given arguments. If the + * <code>faceName</code> is null, all fonts will be returned. + * + * @param faceName the name of the font to look for, or null + * @param scalable if true only scalable fonts are returned, otherwise only non-scalable fonts are returned. + * @return the matching font data + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public FontData[] getFontList (String faceName, boolean scalable) { + checkDevice (); + if (!scalable) return new FontData[0]; + int count = 0; + NSArray families = NSFontManager.sharedFontManager().availableFontFamilies(); + int /*long*/ familyCount = families.count(); + FontData[] fds = new FontData[100]; + for (int i = 0; i < familyCount; i++) { + NSString nsFamily = new NSString(families.objectAtIndex(i)); + String name = nsFamily.getString(); + NSArray fonts = NSFontManager.sharedFontManager().availableMembersOfFontFamily(nsFamily); + int fontCount = (int)/*64*/fonts.count(); + for (int j = 0; j < fontCount; j++) { + NSArray fontDetails = new NSArray(fonts.objectAtIndex(j)); + String nsName = new NSString(fontDetails.objectAtIndex(0)).getString(); + int /*long*/ weight = new NSNumber(fontDetails.objectAtIndex(2)).integerValue(); + int /*long*/ traits = new NSNumber(fontDetails.objectAtIndex(3)).integerValue(); + int style = SWT.NORMAL; + if ((traits & OS.NSItalicFontMask) != 0) style |= SWT.ITALIC; + if (weight == 9) style |= SWT.BOLD; + if (faceName == null || Compatibility.equalsIgnoreCase(faceName, name)) { + FontData data = new FontData(name, 0, style); + data.nsName = nsName; + if (count == fds.length) { + FontData[] newFds = new FontData[fds.length + 100]; + System.arraycopy(fds, 0, newFds, 0, fds.length); + fds = newFds; + } + fds[count++] = data; + } + } + } + if (count == fds.length) return fds; + FontData[] result = new FontData[count]; + System.arraycopy(fds, 0, result, 0, count); + return result; +} + +Point getScreenDPI () { + NSDictionary dictionary = getPrimaryScreen().deviceDescription(); + NSValue value = new NSValue(dictionary.objectForKey(new id(OS.NSDeviceResolution())).id); + NSSize size = value.sizeValue(); + return new Point((int)size.width, (int)size.height); +} + +/** + * Returns the matching standard color for the given + * constant, which should be one of the color constants + * specified in class <code>SWT</code>. Any value other + * than one of the SWT color constants which is passed + * in will result in the color black. This color should + * not be freed because it was allocated by the system, + * not the application. + * + * @param id the color constant + * @return the matching color + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see SWT + */ +public Color getSystemColor (int id) { + checkDevice (); + switch (id) { + case SWT.COLOR_BLACK: return COLOR_BLACK; + case SWT.COLOR_DARK_RED: return COLOR_DARK_RED; + case SWT.COLOR_DARK_GREEN: return COLOR_DARK_GREEN; + case SWT.COLOR_DARK_YELLOW: return COLOR_DARK_YELLOW; + case SWT.COLOR_DARK_BLUE: return COLOR_DARK_BLUE; + case SWT.COLOR_DARK_MAGENTA: return COLOR_DARK_MAGENTA; + case SWT.COLOR_DARK_CYAN: return COLOR_DARK_CYAN; + case SWT.COLOR_GRAY: return COLOR_GRAY; + case SWT.COLOR_DARK_GRAY: return COLOR_DARK_GRAY; + case SWT.COLOR_RED: return COLOR_RED; + case SWT.COLOR_GREEN: return COLOR_GREEN; + case SWT.COLOR_YELLOW: return COLOR_YELLOW; + case SWT.COLOR_BLUE: return COLOR_BLUE; + case SWT.COLOR_MAGENTA: return COLOR_MAGENTA; + case SWT.COLOR_CYAN: return COLOR_CYAN; + case SWT.COLOR_WHITE: return COLOR_WHITE; + } + return COLOR_BLACK; +} + +/** + * Returns a reasonable font for applications to use. + * On some platforms, this will match the "default font" + * or "system font" if such can be found. This font + * should not be freed because it was allocated by the + * system, not the application. + * <p> + * Typically, applications which want the default look + * should simply not set the font on the widgets they + * create. Widgets are always created with the correct + * default font for the class of user-interface component + * they represent. + * </p> + * + * @return a font + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Font getSystemFont () { + checkDevice (); + return systemFont; +} + +/** + * Returns <code>true</code> if the underlying window system prints out + * warning messages on the console, and <code>setWarnings</code> + * had previously been called with <code>true</code>. + * + * @return <code>true</code>if warnings are being handled, and <code>false</code> otherwise + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public boolean getWarnings () { + checkDevice (); + return warnings; +} + +/** + * Initializes any internal resources needed by the + * device. + * <p> + * This method is called after <code>create</code>. + * </p><p> + * If subclasses reimplement this method, they must + * call the <code>super</code> implementation. + * </p> + * + * @see #create + */ +protected void init () { + /* Create the standard colors */ + COLOR_BLACK = new Color (this, 0,0,0); + COLOR_DARK_RED = new Color (this, 0x80,0,0); + COLOR_DARK_GREEN = new Color (this, 0,0x80,0); + COLOR_DARK_YELLOW = new Color (this, 0x80,0x80,0); + COLOR_DARK_BLUE = new Color (this, 0,0,0x80); + COLOR_DARK_MAGENTA = new Color (this, 0x80,0,0x80); + COLOR_DARK_CYAN = new Color (this, 0,0x80,0x80); + COLOR_GRAY = new Color (this, 0xC0,0xC0,0xC0); + COLOR_DARK_GRAY = new Color (this, 0x80,0x80,0x80); + COLOR_RED = new Color (this, 0xFF,0,0); + COLOR_GREEN = new Color (this, 0,0xFF,0); + COLOR_YELLOW = new Color (this, 0xFF,0xFF,0); + COLOR_BLUE = new Color (this, 0,0,0xFF); + COLOR_MAGENTA = new Color (this, 0xFF,0,0xFF); + COLOR_CYAN = new Color (this, 0,0xFF,0xFF); + COLOR_WHITE = new Color (this, 0xFF,0xFF,0xFF); + + paragraphStyle = (NSMutableParagraphStyle)new NSMutableParagraphStyle().alloc().init(); + paragraphStyle.setAlignment(OS.NSLeftTextAlignment); + paragraphStyle.setLineBreakMode(OS.NSLineBreakByClipping); + NSArray tabs = new NSArray(new NSArray().alloc().init()); + paragraphStyle.setTabStops(tabs); + tabs.release(); + + /* Initialize the system font slot */ + boolean smallFonts = System.getProperty("org.eclipse.swt.internal.carbon.smallFonts") != null; + float /*double*/ systemFontSize = smallFonts ? NSFont.smallSystemFontSize() : NSFont.systemFontSize(); + Point dpi = this.dpi = getDPI(), screenDPI = getScreenDPI(); + NSFont font = NSFont.systemFontOfSize(systemFontSize * dpi.y / screenDPI.y); + font.retain(); + systemFont = Font.cocoa_new(this, font); +} + +/** + * Invokes platform specific functionality to allocate a new GC handle. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>Device</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @param data the platform specific GC data + * @return the platform specific GC handle + */ +public abstract int /*long*/ internal_new_GC (GCData data); + +/** + * Invokes platform specific functionality to dispose a GC handle. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>Device</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @param hDC the platform specific GC handle + * @param data the platform specific GC data + */ +public abstract void internal_dispose_GC (int /*long*/ handle, GCData data); + +/** + * Returns <code>true</code> if the device has been disposed, + * and <code>false</code> otherwise. + * <p> + * This method gets the dispose state for the device. + * When a device has been disposed, it is an error to + * invoke any other method using the device. + * + * @return <code>true</code> when the device is disposed and <code>false</code> otherwise + */ +public boolean isDisposed () { + synchronized (Device.class) { + return disposed; + } +} + +/** + * Loads the font specified by a file. The font will be + * present in the list of fonts available to the application. + * + * @param path the font file path + * @return whether the font was successfully loaded + * + * @exception SWTException <ul> + * <li>ERROR_NULL_ARGUMENT - if path is null</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see Font + * + * @since 3.3 + */ +public boolean loadFont (String path) { + checkDevice(); + if (path == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + boolean result = false; + NSString nsPath = NSString.stringWith(path); + int /*long*/ fsRepresentation = nsPath.fileSystemRepresentation(); + + if (fsRepresentation != 0) { + byte [] fsRef = new byte [80]; + boolean [] isDirectory = new boolean[1]; + if (OS.FSPathMakeRef (fsRepresentation, fsRef, isDirectory) == OS.noErr) { + result = OS.ATSFontActivateFromFileReference (fsRef, OS.kATSFontContextLocal, OS.kATSFontFormatUnspecified, 0, OS.kATSOptionFlagsDefault, null) == OS.noErr; + } + } + + return result; +} + +void new_Object (Object object) { + synchronized (trackingLock) { + for (int i=0; i<objects.length; i++) { + if (objects [i] == null) { + objects [i] = object; + errors [i] = new Error (); + return; + } + } + Object [] newObjects = new Object [objects.length + 128]; + System.arraycopy (objects, 0, newObjects, 0, objects.length); + newObjects [objects.length] = object; + objects = newObjects; + Error [] newErrors = new Error [errors.length + 128]; + System.arraycopy (errors, 0, newErrors, 0, errors.length); + newErrors [errors.length] = new Error (); + errors = newErrors; + } +} + +void printErrors () { + if (!DEBUG) return; + if (tracking) { + synchronized (trackingLock) { + if (objects == null || errors == null) return; + int objectCount = 0; + int colors = 0, cursors = 0, fonts = 0, gcs = 0, images = 0; + int paths = 0, patterns = 0, regions = 0, textLayouts = 0, transforms = 0; + for (int i=0; i<objects.length; i++) { + Object object = objects [i]; + if (object != null) { + objectCount++; + if (object instanceof Color) colors++; + if (object instanceof Cursor) cursors++; + if (object instanceof Font) fonts++; + if (object instanceof GC) gcs++; + if (object instanceof Image) images++; + if (object instanceof Path) paths++; + if (object instanceof Pattern) patterns++; + if (object instanceof Region) regions++; + if (object instanceof TextLayout) textLayouts++; + if (object instanceof Transform) transforms++; + } + } + if (objectCount != 0) { + String string = "Summary: "; + if (colors != 0) string += colors + " Color(s), "; + if (cursors != 0) string += cursors + " Cursor(s), "; + if (fonts != 0) string += fonts + " Font(s), "; + if (gcs != 0) string += gcs + " GC(s), "; + if (images != 0) string += images + " Image(s), "; + if (paths != 0) string += paths + " Path(s), "; + if (patterns != 0) string += patterns + " Pattern(s), "; + if (regions != 0) string += regions + " Region(s), "; + if (textLayouts != 0) string += textLayouts + " TextLayout(s), "; + if (transforms != 0) string += transforms + " Transforms(s), "; + if (string.length () != 0) { + string = string.substring (0, string.length () - 2); + System.out.println (string); + } + for (int i=0; i<errors.length; i++) { + if (errors [i] != null) errors [i].printStackTrace (System.out); + } + } + } + } +} + +/** + * Releases any internal resources back to the operating + * system and clears all fields except the device handle. + * <p> + * When a device is destroyed, resources that were acquired + * on behalf of the programmer need to be returned to the + * operating system. For example, if the device allocated a + * font to be used as the system font, this font would be + * freed in <code>release</code>. Also,to assist the garbage + * collector and minimize the amount of memory that is not + * reclaimed when the programmer keeps a reference to a + * disposed device, all fields except the handle are zero'd. + * The handle is needed by <code>destroy</code>. + * </p> + * This method is called before <code>destroy</code>. + * </p><p> + * If subclasses reimplement this method, they must + * call the <code>super</code> implementation. + * </p> + * + * @see #dispose + * @see #destroy + */ +protected void release () { + if (paragraphStyle != null) paragraphStyle.release(); + paragraphStyle = null; + + if (systemFont != null) systemFont.dispose(); + systemFont = null; + + if (COLOR_BLACK != null) COLOR_BLACK.dispose(); + if (COLOR_DARK_RED != null) COLOR_DARK_RED.dispose(); + if (COLOR_DARK_GREEN != null) COLOR_DARK_GREEN.dispose(); + if (COLOR_DARK_YELLOW != null) COLOR_DARK_YELLOW.dispose(); + if (COLOR_DARK_BLUE != null) COLOR_DARK_BLUE.dispose(); + if (COLOR_DARK_MAGENTA != null) COLOR_DARK_MAGENTA.dispose(); + if (COLOR_DARK_CYAN != null) COLOR_DARK_CYAN.dispose(); + if (COLOR_GRAY != null) COLOR_GRAY.dispose(); + if (COLOR_DARK_GRAY != null) COLOR_DARK_GRAY.dispose(); + if (COLOR_RED != null) COLOR_RED.dispose(); + if (COLOR_GREEN != null) COLOR_GREEN.dispose(); + if (COLOR_YELLOW != null) COLOR_YELLOW.dispose(); + if (COLOR_BLUE != null) COLOR_BLUE.dispose(); + if (COLOR_MAGENTA != null) COLOR_MAGENTA.dispose(); + if (COLOR_CYAN != null) COLOR_CYAN.dispose(); + if (COLOR_WHITE != null) COLOR_WHITE.dispose(); + COLOR_BLACK = COLOR_DARK_RED = COLOR_DARK_GREEN = COLOR_DARK_YELLOW = COLOR_DARK_BLUE = + COLOR_DARK_MAGENTA = COLOR_DARK_CYAN = COLOR_GRAY = COLOR_DARK_GRAY = COLOR_RED = + COLOR_GREEN = COLOR_YELLOW = COLOR_BLUE = COLOR_MAGENTA = COLOR_CYAN = COLOR_WHITE = null; +} + +/** + * If the underlying window system supports printing warning messages + * to the console, setting warnings to <code>false</code> prevents these + * messages from being printed. If the argument is <code>true</code> then + * message printing is not blocked. + * + * @param warnings <code>true</code>if warnings should be printed, and <code>false</code> otherwise + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setWarnings (boolean warnings) { + checkDevice (); + this.warnings = warnings; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/DeviceData.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/DeviceData.java new file mode 100755 index 0000000000..ad0c2b2afe --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/DeviceData.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + + +public class DeviceData { + /* + * Debug fields - may not be honoured + * on some SWT platforms. + */ + public boolean debug; + public boolean tracking; + public Error [] errors; + public Object [] objects; +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Font.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Font.java new file mode 100755 index 0000000000..b30ab417f2 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Font.java @@ -0,0 +1,370 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + + +import org.eclipse.swt.internal.cocoa.*; +import org.eclipse.swt.*; + +/** + * Instances of this class manage operating system resources that + * define how text looks when it is displayed. Fonts may be constructed + * by providing a device and either name, size and style information + * or a <code>FontData</code> object which encapsulates this data. + * <p> + * Application code must explicitly invoke the <code>Font.dispose()</code> + * method to release the operating system resources managed by each instance + * when those instances are no longer required. + * </p> + * + * @see FontData + * @see <a href="http://www.eclipse.org/swt/snippets/#font">Font snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Examples: GraphicsExample, PaintExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + */ +public final class Font extends Resource { + + /** + * the handle to the OS font resource + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public NSFont handle; + + /** + * the traits not supported to the OS font resource + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public int extraTraits; + + static final double SYNTHETIC_BOLD = -2.5; + static final double SYNTHETIC_ITALIC = 0.2; + +Font(Device device) { + super(device); +} + +/** + * Constructs a new font given a device and font data + * which describes the desired font's appearance. + * <p> + * You must dispose the font when it is no longer required. + * </p> + * + * @param device the device to create the font on + * @param fd the FontData that describes the desired font (must not be null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * <li>ERROR_NULL_ARGUMENT - if the fd argument is null</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES - if a font could not be created from the given font data</li> + * </ul> + */ +public Font(Device device, FontData fd) { + super(device); + if (fd == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + init(fd.getName(), fd.getHeightF(), fd.getStyle(), fd.nsName); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Constructs a new font given a device and an array + * of font data which describes the desired font's + * appearance. + * <p> + * You must dispose the font when it is no longer required. + * </p> + * + * @param device the device to create the font on + * @param fds the array of FontData that describes the desired font (must not be null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * <li>ERROR_NULL_ARGUMENT - if the fds argument is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the length of fds is zero</li> + * <li>ERROR_NULL_ARGUMENT - if any fd in the array is null</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES - if a font could not be created from the given font data</li> + * </ul> + * + * @since 2.1 + */ +public Font(Device device, FontData[] fds) { + super(device); + if (fds == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (fds.length == 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + for (int i=0; i<fds.length; i++) { + if (fds[i] == null) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + FontData fd = fds[0]; + init(fd.getName(), fd.getHeightF(), fd.getStyle(), fd.nsName); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Constructs a new font given a device, a font name, + * the height of the desired font in points, and a font + * style. + * <p> + * You must dispose the font when it is no longer required. + * </p> + * + * @param device the device to create the font on + * @param name the name of the font (must not be null) + * @param height the font height in points + * @param style a bit or combination of NORMAL, BOLD, ITALIC + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * <li>ERROR_NULL_ARGUMENT - if the name argument is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the height is negative</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES - if a font could not be created from the given arguments</li> + * </ul> + */ +public Font(Device device, String name, int height, int style) { + super(device); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + init(name, height, style, null); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +/*public*/ Font(Device device, String name, float height, int style) { + super(device); + init(name, height, style, null); + init(); +} + +void addTraits(NSMutableAttributedString attrStr, NSRange range) { + if ((extraTraits & OS.NSBoldFontMask) != 0) { + attrStr.addAttribute(OS.NSStrokeWidthAttributeName, NSNumber.numberWithDouble(SYNTHETIC_BOLD), range); + } + if ((extraTraits & OS.NSItalicFontMask) != 0) { + attrStr.addAttribute(OS.NSObliquenessAttributeName, NSNumber.numberWithDouble(SYNTHETIC_ITALIC), range); + } +} + +void addTraits(NSMutableDictionary dict) { + if ((extraTraits & OS.NSBoldFontMask) != 0) { + dict.setObject(NSNumber.numberWithDouble(SYNTHETIC_BOLD), OS.NSStrokeWidthAttributeName); + } + if ((extraTraits & OS.NSItalicFontMask) != 0) { + dict.setObject(NSNumber.numberWithDouble(SYNTHETIC_ITALIC), OS.NSObliquenessAttributeName); + } +} + +void destroy() { + handle.release(); + handle = null; +} + +/** + * Compares the argument to the receiver, and returns true + * if they represent the <em>same</em> object using a class + * specific comparison. + * + * @param object the object to compare with this object + * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise + * + * @see #hashCode + */ +public boolean equals(Object object) { + if (object == this) return true; + if (!(object instanceof Font)) return false; + Font font = (Font)object; + return handle == font.handle; +} + +/** + * Returns an array of <code>FontData</code>s representing the receiver. + * On Windows, only one FontData will be returned per font. On X however, + * a <code>Font</code> object <em>may</em> be composed of multiple X + * fonts. To support this case, we return an array of font data objects. + * + * @return an array of font data objects describing the receiver + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public FontData[] getFontData() { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSString family = handle.familyName(); + String name = family.getString(); + NSString str = handle.fontName(); + String nsName = str.getString(); + NSFontManager manager = NSFontManager.sharedFontManager(); + int /*long*/ traits = manager.traitsOfFont(handle); + int style = SWT.NORMAL; + if ((traits & OS.NSItalicFontMask) != 0) style |= SWT.ITALIC; + if ((traits & OS.NSBoldFontMask) != 0) style |= SWT.BOLD; + if ((extraTraits & OS.NSItalicFontMask) != 0) style |= SWT.ITALIC; + if ((extraTraits & OS.NSBoldFontMask) != 0) style |= SWT.BOLD; + Point dpi = device.dpi, screenDPI = device.getScreenDPI(); + FontData data = new FontData(name, (float)/*64*/handle.pointSize() * screenDPI.y / dpi.y, style); + data.nsName = nsName; + return new FontData[]{data}; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Invokes platform specific functionality to allocate a new font. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>Font</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @param device the device on which to allocate the color + * @param handle the handle for the font + * @param style the style for the font + * @param size the size for the font + * + * @private + */ +public static Font cocoa_new(Device device, NSFont handle) { + Font font = new Font(device); + font.handle = handle; + return font; +} + +/** + * Returns an integer hash code for the receiver. Any two + * objects that return <code>true</code> when passed to + * <code>equals</code> must return the same value for this + * method. + * + * @return the receiver's hash + * + * @see #equals + */ +public int hashCode() { + return handle != null ? (int)/*64*/handle.id : 0; +} + +void init(String name, float height, int style, String nsName) { + if (name == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (height < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + Point dpi = device.dpi, screenDPI = device.getScreenDPI(); + float size = height * dpi.y / screenDPI.y; + if (nsName != null) { + handle = NSFont.fontWithName(NSString.stringWith(nsName), size); + } else { + NSString family = NSString.stringWith(name); + NSFont nsFont = NSFont.fontWithName(family, size); + if (nsFont == null) nsFont = NSFont.systemFontOfSize(size); + NSFontManager manager = NSFontManager.sharedFontManager(); + if (nsFont != null) { + if ((style & (SWT.BOLD | SWT.ITALIC)) == 0) { + handle = nsFont; + } else { + int traits = 0; + if ((style & SWT.ITALIC) != 0) traits |= OS.NSItalicFontMask; + if ((style & SWT.BOLD) != 0) traits |= OS.NSBoldFontMask; + handle = manager.convertFont(nsFont, traits); + if ((style & SWT.ITALIC) != 0 && (handle == null || (manager.traitsOfFont(handle) & OS.NSItalicFontMask) == 0)) { + traits &= ~OS.NSItalicFontMask; + handle = null; + if ((style & SWT.BOLD) != 0) { + handle = manager.convertFont(nsFont, traits); + } + } + if ((style & SWT.BOLD) != 0 && handle == null) { + traits &= ~OS.NSBoldFontMask; + if ((style & SWT.ITALIC) != 0) { + traits |= OS.NSItalicFontMask; + handle = manager.convertFont(nsFont, traits); + } + } + if (handle == null) handle = nsFont; + } + } + if (handle == null) { + handle = NSFont.systemFontOfSize(size); + } + if ((style & SWT.ITALIC) != 0 && (manager.traitsOfFont(handle) & OS.NSItalicFontMask) == 0) { + extraTraits |= OS.NSItalicFontMask; + } + if ((style & SWT.BOLD) != 0 && (manager.traitsOfFont(handle) & OS.NSBoldFontMask) == 0) { + extraTraits |= OS.NSBoldFontMask; + } + } + if (handle == null) { + handle = device.systemFont.handle; + } + handle.retain(); +} + +/** + * Returns <code>true</code> if the font has been disposed, + * and <code>false</code> otherwise. + * <p> + * This method gets the dispose state for the font. + * When a font has been disposed, it is an error to + * invoke any other method using the font. + * + * @return <code>true</code> when the font is disposed and <code>false</code> otherwise + */ +public boolean isDisposed() { + return handle == null; +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the receiver + */ +public String toString () { + if (isDisposed()) return "Font {*DISPOSED*}"; + return "Font {" + handle + "}"; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/FontData.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/FontData.java new file mode 100755 index 0000000000..589f151ebe --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/FontData.java @@ -0,0 +1,447 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + + +import org.eclipse.swt.*; + +/** + * Instances of this class describe operating system fonts. + * <p> + * For platform-independent behaviour, use the get and set methods + * corresponding to the following properties: + * <dl> + * <dt>height</dt><dd>the height of the font in points</dd> + * <dt>name</dt><dd>the face name of the font, which may include the foundry</dd> + * <dt>style</dt><dd>A bitwise combination of NORMAL, ITALIC and BOLD</dd> + * </dl> + * If extra, platform-dependent functionality is required: + * <ul> + * <li>On <em>Windows</em>, the data member of the <code>FontData</code> + * corresponds to a Windows <code>LOGFONT</code> structure whose fields + * may be retrieved and modified.</li> + * <li>On <em>X</em>, the fields of the <code>FontData</code> correspond + * to the entries in the font's XLFD name and may be retrieved and modified. + * </ul> + * Application code does <em>not</em> need to explicitly release the + * resources managed by each instance when those instances are no longer + * required, and thus no <code>dispose()</code> method is provided. + * + * @see Font + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + */ +public final class FontData { + /** + * the font name + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public String name; + + /** + * The height of the font data in points + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public float height; + + /** + * the font style + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public int style; + + /** + * the NSFont font name + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public String nsName; + + /** + * The locales of the font + */ + String lang, country, variant; + +/** + * Constructs a new uninitialized font data. + */ +public FontData () { + this("", 12, SWT.NORMAL); +} + +/** + * Constructs a new FontData given a string representation + * in the form generated by the <code>FontData.toString</code> + * method. + * <p> + * Note that the representation varies between platforms, + * and a FontData can only be created from a string that was + * generated on the same platform. + * </p> + * + * @param string the string representation of a <code>FontData</code> (must not be null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the argument is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the argument does not represent a valid description</li> + * </ul> + * + * @see #toString + */ +public FontData(String string) { + if (string == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + int start = 0; + int end = string.indexOf('|'); + if (end == -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + String version1 = string.substring(start, end); + try { + if (Integer.parseInt(version1) != 1) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } catch (NumberFormatException e) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + String name = string.substring(start, end); + + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + float height = 0; + try { + height = Float.parseFloat(string.substring(start, end)); + } catch (NumberFormatException e) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + int style = 0; + try { + style = Integer.parseInt(string.substring(start, end)); + } catch (NumberFormatException e) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + start = end + 1; + end = string.indexOf('|', start); + setName(name); + setHeight(height); + setStyle(style); + if (end == -1) return; + String platform = string.substring(start, end); + + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) return; + String version2 = string.substring(start, end); + + if (platform.equals("COCOA") && version2.equals("1")) { + start = end + 1; + end = string.length(); + if (start < end) nsName = string.substring(start, end); + } +} + +/** + * Constructs a new font data given a font name, + * the height of the desired font in points, + * and a font style. + * + * @param name the name of the font (must not be null) + * @param height the font height in points + * @param style a bit or combination of NORMAL, BOLD, ITALIC + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - when the font name is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the height is negative</li> + * </ul> + */ +public FontData(String name, int height, int style) { + setName(name); + setHeight(height); + setStyle(style); +} + +/*public*/ FontData(String name, float height, int style) { + setName(name); + setHeight(height); + setStyle(style); +} + +/** + * Compares the argument to the receiver, and returns true + * if they represent the <em>same</em> object using a class + * specific comparison. + * + * @param object the object to compare with this object + * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise + * + * @see #hashCode + */ +public boolean equals (Object object) { + if (object == this) return true; + if (!(object instanceof FontData)) return false; + FontData data = (FontData)object; + return name.equals(data.name) && height == data.height && style == data.style; +} + +/** + * Returns the height of the receiver in points. + * + * @return the height of this FontData + * + * @see #setHeight(int) + */ +public int getHeight() { + return (int)height; +} + +/*public*/ float getHeightF() { + return height; +} + +/** + * Returns the locale of the receiver. + * <p> + * The locale determines which platform character set this + * font is going to use. Widgets and graphics operations that + * use this font will convert UNICODE strings to the platform + * character set of the specified locale. + * </p> + * <p> + * On platforms where there are multiple character sets for a + * given language/country locale, the variant portion of the + * locale will determine the character set. + * </p> + * + * @return the <code>String</code> representing a Locale object + * @since 3.0 + */ +public String getLocale () { + StringBuffer buffer = new StringBuffer (); + char sep = '_'; + if (lang != null) { + buffer.append (lang); + buffer.append (sep); + } + if (country != null) { + buffer.append (country); + buffer.append (sep); + } + if (variant != null) { + buffer.append (variant); + } + + String result = buffer.toString (); + int length = result.length (); + if (length > 0) { + if (result.charAt (length - 1) == sep) { + result = result.substring (0, length - 1); + } + } + return result; +} + +/** + * Returns the name of the receiver. + * On platforms that support font foundries, the return value will + * be the foundry followed by a dash ("-") followed by the face name. + * + * @return the name of this <code>FontData</code> + * + * @see #setName + */ +public String getName() { + return name; +} + +/** + * Returns the style of the receiver which is a bitwise OR of + * one or more of the <code>SWT</code> constants NORMAL, BOLD + * and ITALIC. + * + * @return the style of this <code>FontData</code> + * + * @see #setStyle + */ +public int getStyle() { + return style; +} + +/** + * Returns an integer hash code for the receiver. Any two + * objects that return <code>true</code> when passed to + * <code>equals</code> must return the same value for this + * method. + * + * @return the receiver's hash + * + * @see #equals + */ +public int hashCode () { + return name.hashCode() ^ getHeight() ^ style; +} + +/** + * Sets the height of the receiver. The parameter is + * specified in terms of points, where a point is one + * seventy-second of an inch. + * + * @param height the height of the <code>FontData</code> + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the height is negative</li> + * </ul> + * + * @see #getHeight + */ +public void setHeight(int height) { + if (height < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + this.height = height; +} + +/*public*/ void setHeight(float height) { + if (height < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + this.height = height; +} + +/** + * Sets the locale of the receiver. + * <p> + * The locale determines which platform character set this + * font is going to use. Widgets and graphics operations that + * use this font will convert UNICODE strings to the platform + * character set of the specified locale. + * </p> + * <p> + * On platforms where there are multiple character sets for a + * given language/country locale, the variant portion of the + * locale will determine the character set. + * </p> + * + * @param locale the <code>String</code> representing a Locale object + * @see java.util.Locale#toString + */ +public void setLocale(String locale) { + lang = country = variant = null; + if (locale != null) { + char sep = '_'; + int length = locale.length(); + int firstSep, secondSep; + + firstSep = locale.indexOf(sep); + if (firstSep == -1) { + firstSep = secondSep = length; + } else { + secondSep = locale.indexOf(sep, firstSep + 1); + if (secondSep == -1) secondSep = length; + } + if (firstSep > 0) lang = locale.substring(0, firstSep); + if (secondSep > firstSep + 1) country = locale.substring(firstSep + 1, secondSep); + if (length > secondSep + 1) variant = locale.substring(secondSep + 1); + } +} + +/** + * Sets the name of the receiver. + * <p> + * Some platforms support font foundries. On these platforms, the name + * of the font specified in setName() may have one of the following forms: + * <ol> + * <li>a face name (for example, "courier")</li> + * <li>a foundry followed by a dash ("-") followed by a face name (for example, "adobe-courier")</li> + * </ol> + * In either case, the name returned from getName() will include the + * foundry. + * </p> + * <p> + * On platforms that do not support font foundries, only the face name + * (for example, "courier") is used in <code>setName()</code> and + * <code>getName()</code>. + * </p> + * + * @param name the name of the font data (must not be null) + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - when the font name is null</li> + * </ul> + * + * @see #getName + */ +public void setName(String name) { + if (name == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + this.name = name; + nsName = null; +} + +/** + * Sets the style of the receiver to the argument which must + * be a bitwise OR of one or more of the <code>SWT</code> + * constants NORMAL, BOLD and ITALIC. All other style bits are + * ignored. + * + * @param style the new style for this <code>FontData</code> + * + * @see #getStyle + */ +public void setStyle(int style) { + this.style = style; + nsName = null; +} + +/** + * Returns a string representation of the receiver which is suitable + * for constructing an equivalent instance using the + * <code>FontData(String)</code> constructor. + * + * @return a string representation of the FontData + * + * @see FontData + */ +public String toString() { + StringBuffer buffer = new StringBuffer(128); + buffer.append("1|"); + buffer.append(getName()); + buffer.append("|"); + buffer.append(getHeightF()); + buffer.append("|"); + buffer.append(getStyle()); + buffer.append("|"); + buffer.append("COCOA|1|"); + if (nsName != null) buffer.append(nsName); + return buffer.toString(); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/FontMetrics.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/FontMetrics.java new file mode 100755 index 0000000000..2c0d6a7a3f --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/FontMetrics.java @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + + +/** + * Instances of this class provide measurement information + * about fonts including ascent, descent, height, leading + * space between rows, and average character width. + * <code>FontMetrics</code> are obtained from <code>GC</code>s + * using the <code>getFontMetrics()</code> method. + * + * @see GC#getFontMetrics + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + */ +public final class FontMetrics { + int ascent, descent, averageCharWidth, leading, height; + +FontMetrics() { +} + +public static FontMetrics cocoa_new(int ascent, int descent, int averageCharWidth, int leading, int height) { + FontMetrics fontMetrics = new FontMetrics(); + fontMetrics.ascent = ascent; + fontMetrics.descent = descent; + fontMetrics.averageCharWidth = averageCharWidth; + fontMetrics.leading = leading; + fontMetrics.height = height; + return fontMetrics; +} + +/** + * Compares the argument to the receiver, and returns true + * if they represent the <em>same</em> object using a class + * specific comparison. + * + * @param object the object to compare with this object + * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise + * + * @see #hashCode + */ +public boolean equals (Object object) { + if (object == this) return true; + if (!(object instanceof FontMetrics)) return false; + FontMetrics metrics = (FontMetrics)object; + return ascent == metrics.ascent && descent == metrics.descent && + averageCharWidth == metrics.averageCharWidth && leading == metrics.leading && + height == metrics.height; +} + +/** + * Returns the ascent of the font described by the receiver. A + * font's <em>ascent</em> is the distance from the baseline to the + * top of actual characters, not including any of the leading area, + * measured in pixels. + * + * @return the ascent of the font + */ +public int getAscent() { + return ascent; +} + +/** + * Returns the average character width, measured in pixels, + * of the font described by the receiver. + * + * @return the average character width of the font + */ +public int getAverageCharWidth() { + return averageCharWidth; +} + +/** + * Returns the descent of the font described by the receiver. A + * font's <em>descent</em> is the distance from the baseline to the + * bottom of actual characters, not including any of the leading area, + * measured in pixels. + * + * @return the descent of the font + */ +public int getDescent() { + return descent; +} + +/** + * Returns the height of the font described by the receiver, + * measured in pixels. A font's <em>height</em> is the sum of + * its ascent, descent and leading area. + * + * @return the height of the font + * + * @see #getAscent + * @see #getDescent + * @see #getLeading + */ +public int getHeight() { + return height; +} + +/** + * Returns the leading area of the font described by the + * receiver. A font's <em>leading area</em> is the space + * above its ascent which may include accents or other marks. + * + * @return the leading space of the font + */ +public int getLeading() { + return leading; +} + +/** + * Returns an integer hash code for the receiver. Any two + * objects that return <code>true</code> when passed to + * <code>equals</code> must return the same value for this + * method. + * + * @return the receiver's hash + * + * @see #equals + */ +public int hashCode() { + return ascent ^ descent ^ averageCharWidth ^ leading ^ height; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/GC.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/GC.java new file mode 100755 index 0000000000..0d34639929 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/GC.java @@ -0,0 +1,3918 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + +import org.eclipse.swt.*; +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Class <code>GC</code> is where all of the drawing capabilities that are + * supported by SWT are located. Instances are used to draw on either an + * <code>Image</code>, a <code>Control</code>, or directly on a <code>Display</code>. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>LEFT_TO_RIGHT, RIGHT_TO_LEFT</dd> + * </dl> + * + * <p> + * The SWT drawing coordinate system is the two-dimensional space with the origin + * (0,0) at the top left corner of the drawing area and with (x,y) values increasing + * to the right and downward respectively. + * </p> + * + * <p> + * The result of drawing on an image that was created with an indexed + * palette using a color that is not in the palette is platform specific. + * Some platforms will match to the nearest color while other will draw + * the color itself. This happens because the allocated image might use + * a direct palette on platforms that do not support indexed palette. + * </p> + * + * <p> + * Application code must explicitly invoke the <code>GC.dispose()</code> + * method to release the operating system resources managed by each instance + * when those instances are no longer required. This is <em>particularly</em> + * important on Windows95 and Windows98 where the operating system has a limited + * number of device contexts available. + * </p> + * + * <p> + * Note: Only one of LEFT_TO_RIGHT and RIGHT_TO_LEFT may be specified. + * </p> + * + * @see org.eclipse.swt.events.PaintEvent + * @see <a href="http://www.eclipse.org/swt/snippets/#gc">GC snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Examples: GraphicsExample, PaintExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + */ +public final class GC extends Resource { + /** + * the handle to the OS device context + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public NSGraphicsContext handle; + + Drawable drawable; + GCData data; + + CGPathElement element; + int count, typeCount; + byte[] types; + float /*double*/[] points; + float /*double*/ [] point; + + static final int TAB_COUNT = 32; + + final static int FOREGROUND = 1 << 0; + final static int BACKGROUND = 1 << 1; + final static int FONT = 1 << 2; + final static int LINE_STYLE = 1 << 3; + final static int LINE_CAP = 1 << 4; + final static int LINE_JOIN = 1 << 5; + final static int LINE_WIDTH = 1 << 6; + final static int LINE_MITERLIMIT = 1 << 7; + final static int FOREGROUND_FILL = 1 << 8; + final static int DRAW_OFFSET = 1 << 9; + final static int CLIPPING = 1 << 10; + final static int TRANSFORM = 1 << 11; + final static int VISIBLE_REGION = 1 << 12; + final static int DRAW = CLIPPING | TRANSFORM | FOREGROUND | LINE_WIDTH | LINE_STYLE | LINE_CAP | LINE_JOIN | LINE_MITERLIMIT | DRAW_OFFSET; + final static int FILL = CLIPPING | TRANSFORM | BACKGROUND; + + static final float[] LINE_DOT = new float[]{1, 1}; + static final float[] LINE_DASH = new float[]{3, 1}; + static final float[] LINE_DASHDOT = new float[]{3, 1, 1, 1}; + static final float[] LINE_DASHDOTDOT = new float[]{3, 1, 1, 1, 1, 1}; + static final float[] LINE_DOT_ZERO = new float[]{3, 3}; + static final float[] LINE_DASH_ZERO = new float[]{18, 6}; + static final float[] LINE_DASHDOT_ZERO = new float[]{9, 6, 3, 6}; + static final float[] LINE_DASHDOTDOT_ZERO = new float[]{9, 3, 3, 3, 3, 3}; + +GC() { +} + +/** + * Constructs a new instance of this class which has been + * configured to draw on the specified drawable. Sets the + * foreground color, background color and font in the GC + * to match those in the drawable. + * <p> + * You must dispose the graphics context when it is no longer required. + * </p> + * @param drawable the drawable to draw on + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the drawable is null</li> + * <li>ERROR_NULL_ARGUMENT - if there is no current device</li> + * <li>ERROR_INVALID_ARGUMENT + * - if the drawable is an image that is not a bitmap or an icon + * - if the drawable is an image or printer that is already selected + * into another graphics context</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle could not be obtained for GC creation</li> + * <li>ERROR_THREAD_INVALID_ACCESS if not called from the thread that created the drawable</li> + * </ul> + */ +public GC(Drawable drawable) { + this(drawable, 0); +} + +/** + * Constructs a new instance of this class which has been + * configured to draw on the specified drawable. Sets the + * foreground color, background color and font in the GC + * to match those in the drawable. + * <p> + * You must dispose the graphics context when it is no longer required. + * </p> + * + * @param drawable the drawable to draw on + * @param style the style of GC to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the drawable is null</li> + * <li>ERROR_NULL_ARGUMENT - if there is no current device</li> + * <li>ERROR_INVALID_ARGUMENT + * - if the drawable is an image that is not a bitmap or an icon + * - if the drawable is an image or printer that is already selected + * into another graphics context</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle could not be obtained for GC creation</li> + * <li>ERROR_THREAD_INVALID_ACCESS if not called from the thread that created the drawable</li> + * </ul> + * + * @since 2.1.2 + */ +public GC(Drawable drawable, int style) { + if (drawable == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + GCData data = new GCData(); + data.style = checkStyle(style); + int /*long*/ contextId = drawable.internal_new_GC(data); + Device device = data.device; + if (device == null) device = Device.getDevice(); + if (device == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + this.device = data.device = device; + init(drawable, data, contextId); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +static int checkStyle (int style) { + if ((style & SWT.LEFT_TO_RIGHT) != 0) style &= ~SWT.RIGHT_TO_LEFT; + return style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT); +} + +/** + * Invokes platform specific functionality to allocate a new graphics context. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>GC</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @param drawable the Drawable for the receiver. + * @param data the data for the receiver. + * + * @return a new <code>GC</code> + * + * @private + */ +public static GC cocoa_new(Drawable drawable, GCData data) { + GC gc = new GC(); + int /*long*/ context = drawable.internal_new_GC(data); + gc.device = data.device; + gc.init(drawable, data, context); + return gc; +} + +int /*long*/ applierFunc(int /*long*/ info, int /*long*/ elementPtr) { + OS.memmove(element, elementPtr, CGPathElement.sizeof); + int type = 0, length = 1; + switch (element.type) { + case OS.kCGPathElementMoveToPoint: type = SWT.PATH_MOVE_TO; break; + case OS.kCGPathElementAddLineToPoint: type = SWT.PATH_LINE_TO; break; + case OS.kCGPathElementAddQuadCurveToPoint: type = SWT.PATH_QUAD_TO; length = 2; break; + case OS.kCGPathElementAddCurveToPoint: type = SWT.PATH_CUBIC_TO; length = 3; break; + case OS.kCGPathElementCloseSubpath: type = SWT.PATH_CLOSE; length = 0; break; + } + if (types != null) { + types[typeCount] = (byte)type; + if (length > 0) { + OS.memmove(point, element.points, length * CGPoint.sizeof); + System.arraycopy(point, 0, points, count, length * 2); + } + } + typeCount++; + count += length * 2; + return 0; +} + +NSAutoreleasePool checkGC (int mask) { + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + if (data.flippedContext != null && !handle.isEqual(NSGraphicsContext.currentContext())) { + data.restoreContext = true; + NSGraphicsContext.static_saveGraphicsState(); + NSGraphicsContext.setCurrentContext(handle); + } + if ((mask & (CLIPPING | TRANSFORM)) != 0) { + NSView view = data.view; + if ((data.state & CLIPPING) == 0 || (data.state & TRANSFORM) == 0 || (data.state & VISIBLE_REGION) == 0) { + boolean antialias = handle.shouldAntialias(); + handle.restoreGraphicsState(); + handle.saveGraphicsState(); + handle.setShouldAntialias(antialias); + if (view != null && (data.paintRect == null || !view.isFlipped())) { + NSAffineTransform transform = NSAffineTransform.transform(); + NSRect rect = view.convertRect_toView_(view.bounds(), null); + if (data.paintRect == null) { + transform.translateXBy(rect.x, rect.y + rect.height); + } else { + transform.translateXBy(0, rect.height); + } + transform.scaleXBy(1, -1); + transform.concat(); + if (data.visibleRgn != 0) { + if (data.visiblePath == null || (data.state & VISIBLE_REGION) == 0) { + if (data.visiblePath != null) data.visiblePath.release(); + data.visiblePath = Region.cocoa_new(device, data.visibleRgn).getPath(); + } + data.visiblePath.addClip(); + data.state |= VISIBLE_REGION; + } + } + if (data.clipPath != null) data.clipPath.addClip(); + if (data.transform != null) data.transform.concat(); + mask &= ~(TRANSFORM | CLIPPING); + data.state |= TRANSFORM | CLIPPING; + data.state &= ~(BACKGROUND | FOREGROUND); + } + } + + int state = data.state; + if ((state & mask) == mask) return pool; + state = (state ^ mask) & mask; + data.state |= mask; + + if ((state & FOREGROUND) != 0) { + Pattern pattern = data.foregroundPattern; + if (pattern != null) { + if (pattern.color != null) pattern.color.setStroke(); + } else { + float /*double*/ [] color = data.foreground; + if (data.fg != null) data.fg.release(); + NSColor fg = data.fg = NSColor.colorWithDeviceRed(color[0], color[1], color[2], data.alpha / 255f); + fg.retain(); + fg.setStroke(); + } + } + if ((state & FOREGROUND_FILL) != 0) { + Pattern pattern = data.foregroundPattern; + if (pattern != null) { + if (pattern.color != null) pattern.color.setFill(); + } else { + float /*double*/ [] color = data.foreground; + if (data.fg != null) data.fg.release(); + NSColor fg = data.fg = NSColor.colorWithDeviceRed(color[0], color[1], color[2], data.alpha / 255f); + fg.retain(); + fg.setFill(); + } + data.state &= ~BACKGROUND; + } + if ((state & BACKGROUND) != 0) { + Pattern pattern = data.backgroundPattern; + if (pattern != null) { + if (pattern.color != null) pattern.color.setFill(); + } else { + float /*double*/ [] color = data.background; + if (data.bg != null) data.bg.release(); + NSColor bg = data.bg = NSColor.colorWithDeviceRed(color[0], color[1], color[2], data.alpha / 255f); + bg.retain(); + bg.setFill(); + } + data.state &= ~FOREGROUND_FILL; + } + NSBezierPath path = data.path; + if ((state & LINE_WIDTH) != 0) { + path.setLineWidth(data.lineWidth == 0 ? 1 : data.lineWidth); + switch (data.lineStyle) { + case SWT.LINE_DOT: + case SWT.LINE_DASH: + case SWT.LINE_DASHDOT: + case SWT.LINE_DASHDOTDOT: + state |= LINE_STYLE; + } + } + if ((state & LINE_STYLE) != 0) { + float[] dashes = null; + float width = data.lineWidth; + switch (data.lineStyle) { + case SWT.LINE_SOLID: break; + case SWT.LINE_DASH: dashes = width != 0 ? LINE_DASH : LINE_DASH_ZERO; break; + case SWT.LINE_DOT: dashes = width != 0 ? LINE_DOT : LINE_DOT_ZERO; break; + case SWT.LINE_DASHDOT: dashes = width != 0 ? LINE_DASHDOT : LINE_DASHDOT_ZERO; break; + case SWT.LINE_DASHDOTDOT: dashes = width != 0 ? LINE_DASHDOTDOT : LINE_DASHDOTDOT_ZERO; break; + case SWT.LINE_CUSTOM: dashes = data.lineDashes; break; + } + if (dashes != null) { + float /*double*/[] lengths = new float /*double*/[dashes.length]; + for (int i = 0; i < lengths.length; i++) { + lengths[i] = width == 0 || data.lineStyle == SWT.LINE_CUSTOM ? dashes[i] : dashes[i] * width; + } + path.setLineDash(lengths, lengths.length, data.lineDashesOffset); + } else { + path.setLineDash(null, 0, 0); + } + } + if ((state & LINE_MITERLIMIT) != 0) { + path.setMiterLimit(data.lineMiterLimit); + } + if ((state & LINE_JOIN) != 0) { + int joinStyle = 0; + switch (data.lineJoin) { + case SWT.JOIN_MITER: joinStyle = OS.NSMiterLineJoinStyle; break; + case SWT.JOIN_ROUND: joinStyle = OS.NSRoundLineJoinStyle; break; + case SWT.JOIN_BEVEL: joinStyle = OS.NSBevelLineJoinStyle; break; + } + path.setLineJoinStyle(joinStyle); + } + if ((state & LINE_CAP) != 0) { + int capStyle = 0; + switch (data.lineCap) { + case SWT.CAP_ROUND: capStyle = OS.NSRoundLineCapStyle; break; + case SWT.CAP_FLAT: capStyle = OS.NSButtLineCapStyle; break; + case SWT.CAP_SQUARE: capStyle = OS.NSSquareLineCapStyle; break; + } + path.setLineCapStyle(capStyle); + } + if ((state & DRAW_OFFSET) != 0) { + data.drawXOffset = data.drawYOffset = 0; + NSSize size = new NSSize(); + size.width = size.height = 1; + if (data.transform != null) { + size = data.transform.transformSize(size); + } + float /*double*/ scaling = size.width; + if (scaling < 0) scaling = -scaling; + float /*double*/ strokeWidth = data.lineWidth * scaling; + if (strokeWidth == 0 || ((int)strokeWidth % 2) == 1) { + data.drawXOffset = 0.5f / scaling; + } + scaling = size.height; + if (scaling < 0) scaling = -scaling; + strokeWidth = data.lineWidth * scaling; + if (strokeWidth == 0 || ((int)strokeWidth % 2) == 1) { + data.drawYOffset = 0.5f / scaling; + } + } + return pool; +} + +/** + * Copies a rectangular area of the receiver at the specified + * position into the image, which must be of type <code>SWT.BITMAP</code>. + * + * @param image the image to copy into + * @param x the x coordinate in the receiver of the area to be copied + * @param y the y coordinate in the receiver of the area to be copied + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the image is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the image is not a bitmap or has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void copyArea(Image image, int x, int y) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (image == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (image.type != SWT.BITMAP || image.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = checkGC(TRANSFORM | CLIPPING); + try { + if (data.image != null) { + int srcX = x, srcY = y, destX = 0, destY = 0; + NSSize srcSize = data.image.handle.size(); + int imgHeight = (int)srcSize.height; + int destWidth = (int)srcSize.width - x, destHeight = (int)srcSize.height - y; + int srcWidth = destWidth, srcHeight = destHeight; + NSGraphicsContext context = NSGraphicsContext.graphicsContextWithBitmapImageRep(image.getRepresentation()); + NSGraphicsContext.static_saveGraphicsState(); + NSGraphicsContext.setCurrentContext(context); + NSAffineTransform transform = NSAffineTransform.transform(); + NSSize size = image.handle.size(); + transform.translateXBy(0, size.height-(destHeight + 2 * destY)); + transform.concat(); + NSRect srcRect = new NSRect(); + srcRect.x = srcX; + srcRect.y = imgHeight - (srcY + srcHeight); + srcRect.width = srcWidth; + srcRect.height = srcHeight; + NSRect destRect = new NSRect(); + destRect.x = destX; + destRect.y = destY; + destRect.width = destWidth; + destRect.height = destHeight; + data.image.handle.drawInRect(destRect, srcRect, OS.NSCompositeCopy, 1); + NSGraphicsContext.static_restoreGraphicsState(); + return; + } + if (data.view != null) { + NSPoint pt = new NSPoint(); + pt.x = x; + pt.y = y; + NSWindow window = data.view.window(); + pt = data.view.convertPoint_toView_(pt, window.contentView().superview()); + NSRect frame = window.frame(); + pt.y = frame.height - pt.y; + NSSize size = image.handle.size(); + CGRect destRect = new CGRect(); + destRect.size.width = size.width; + destRect.size.height = size.height; + CGRect srcRect = new CGRect(); + srcRect.origin.x = pt.x; + srcRect.origin.y = pt.y; + srcRect.size.width = size.width; + srcRect.size.height = size.height; + NSBitmapImageRep imageRep = image.getRepresentation(); + NSGraphicsContext context = NSGraphicsContext.graphicsContextWithBitmapImageRep(imageRep); + NSGraphicsContext.static_saveGraphicsState(); + NSGraphicsContext.setCurrentContext(context); + int /*long*/ contextID = OS.objc_msgSend(NSApplication.sharedApplication().id, OS.sel_contextID); + OS.CGContextCopyWindowContentsToRect(context.graphicsPort(), destRect, contextID, window.windowNumber(), srcRect); + NSGraphicsContext.static_restoreGraphicsState(); + return; + } + if (handle.isDrawingToScreen()) { + NSImage imageHandle = image.handle; + NSSize size = imageHandle.size(); + CGRect rect = new CGRect(); + rect.origin.x = x; + rect.origin.y = y; + rect.size.width = size.width; + rect.size.height = size.height; + int displayCount = 16; + int /*long*/ displays = OS.malloc(4 * displayCount), countPtr = OS.malloc(4); + if (OS.CGGetDisplaysWithRect(rect, displayCount, displays, countPtr) != 0) return; + int[] count = new int[1], display = new int[1]; + OS.memmove(count, countPtr, OS.PTR_SIZEOF); + for (int i = 0; i < count[0]; i++) { + OS.memmove(display, displays + (i * 4), 4); + OS.CGDisplayBounds(display[0], rect); + int /*long*/ address = OS.CGDisplayBaseAddress(display[0]); + if (address != 0) { + int /*long*/ width = OS.CGDisplayPixelsWide(display[0]); + int /*long*/ height = OS.CGDisplayPixelsHigh(display[0]); + int /*long*/ bpr = OS.CGDisplayBytesPerRow(display[0]); + int /*long*/ bpp = OS.CGDisplayBitsPerPixel(display[0]); + int /*long*/ bps = OS.CGDisplayBitsPerSample(display[0]); + int bitmapInfo = OS.kCGImageAlphaNoneSkipFirst; + switch ((int)/*63*/bpp) { + case 16: bitmapInfo |= OS.kCGBitmapByteOrder16Host; break; + case 32: bitmapInfo |= OS.kCGBitmapByteOrder32Host; break; + } + int /*long*/ srcImage = 0; + if (OS.__BIG_ENDIAN__() && OS.VERSION >= 0x1040) { + int /*long*/ colorspace = OS.CGColorSpaceCreateDeviceRGB(); + int /*long*/ context = OS.CGBitmapContextCreate(address, width, height, bps, bpr, colorspace, bitmapInfo); + OS.CGColorSpaceRelease(colorspace); + srcImage = OS.CGBitmapContextCreateImage(context); + OS.CGContextRelease(context); + } else { + int /*long*/ provider = OS.CGDataProviderCreateWithData(0, address, bpr * height, 0); + int /*long*/ colorspace = OS.CGColorSpaceCreateDeviceRGB(); + srcImage = OS.CGImageCreate(width, height, bps, bpp, bpr, colorspace, bitmapInfo, provider, 0, true, 0); + OS.CGColorSpaceRelease(colorspace); + OS.CGDataProviderRelease(provider); + } + copyArea(image, x - (int)rect.origin.x, y - (int)rect.origin.y, srcImage); + if (srcImage != 0) OS.CGImageRelease(srcImage); + } + } + OS.free(displays); + OS.free(countPtr); + } + } finally { + uncheckGC(pool); + } +} + +void copyArea (Image image, int x, int y, int /*long*/ srcImage) { + if (srcImage == 0) return; + NSBitmapImageRep rep = image.getRepresentation(); + int /*long*/ bpc = rep.bitsPerSample(); + int /*long*/ width = rep.pixelsWide(); + int /*long*/ height = rep.pixelsHigh(); + int /*long*/ bpr = rep.bytesPerRow(); + int alphaInfo = rep.hasAlpha() ? OS.kCGImageAlphaFirst : OS.kCGImageAlphaNoneSkipFirst; + int /*long*/ colorspace = OS.CGColorSpaceCreateDeviceRGB(); + int /*long*/ context = OS.CGBitmapContextCreate(rep.bitmapData(), width, height, bpc, bpr, colorspace, alphaInfo); + OS.CGColorSpaceRelease(colorspace); + if (context != 0) { + CGRect rect = new CGRect(); + rect.origin.x = -x; + rect.origin.y = y; + rect.size.width = OS.CGImageGetWidth(srcImage); + rect.size.height = OS.CGImageGetHeight(srcImage); + OS.CGContextTranslateCTM(context, 0, -(rect.size.height - height)); + OS.CGContextDrawImage(context, rect, srcImage); + OS.CGContextRelease(context); + } +} + +/** + * Copies a rectangular area of the receiver at the source + * position onto the receiver at the destination position. + * + * @param srcX the x coordinate in the receiver of the area to be copied + * @param srcY the y coordinate in the receiver of the area to be copied + * @param width the width of the area to copy + * @param height the height of the area to copy + * @param destX the x coordinate in the receiver of the area to copy to + * @param destY the y coordinate in the receiver of the area to copy to + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void copyArea(int srcX, int srcY, int width, int height, int destX, int destY) { + copyArea(srcX, srcY, width, height, destX, destY, true); +} +/** + * Copies a rectangular area of the receiver at the source + * position onto the receiver at the destination position. + * + * @param srcX the x coordinate in the receiver of the area to be copied + * @param srcY the y coordinate in the receiver of the area to be copied + * @param width the width of the area to copy + * @param height the height of the area to copy + * @param destX the x coordinate in the receiver of the area to copy to + * @param destY the y coordinate in the receiver of the area to copy to + * @param paint if <code>true</code> paint events will be generated for old and obscured areas + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public void copyArea(int srcX, int srcY, int width, int height, int destX, int destY, boolean paint) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (width <= 0 || height <= 0) return; + int deltaX = destX - srcX, deltaY = destY - srcY; + if (deltaX == 0 && deltaY == 0) return; + NSAutoreleasePool pool = checkGC(TRANSFORM | CLIPPING); + try { + Image image = data.image; + if (image != null) { + NSImage imageHandle = image.handle; + NSSize size = imageHandle.size(); + int imgHeight = (int)size.height; + handle.saveGraphicsState(); + NSAffineTransform transform = NSAffineTransform.transform(); + transform.scaleXBy(1, -1); + transform.translateXBy(0, -(height + 2 * destY)); + transform.concat(); + NSRect srcRect = new NSRect(); + srcRect.x = srcX; + srcRect.y = imgHeight - (srcY + height); + srcRect.width = width; + srcRect.height = height; + NSRect destRect = new NSRect(); + destRect.x = destX; + destRect.y = destY; + destRect.width = width; + destRect.height = height; + imageHandle.drawInRect(destRect, srcRect, OS.NSCompositeCopy, 1); + handle.restoreGraphicsState(); + return; + } + if (data.view != null) { + NSView view = data.view; + NSRect visibleRect = view.visibleRect(); + if (visibleRect.width <= 0 || visibleRect.height <= 0) return; + NSRect damage = new NSRect(); + damage.x = srcX; + damage.y = srcY; + damage.width = width; + damage.height = height; + NSPoint dest = new NSPoint(); + dest.x = destX; + dest.y = destY; + + view.lockFocus(); + OS.NSCopyBits(0, damage , dest); + view.unlockFocus(); + + if (paint) { + boolean disjoint = (destX + width < srcX) || (srcX + width < destX) || (destY + height < srcY) || (srcY + height < destY); + if (disjoint) { + view.setNeedsDisplayInRect(damage); + } else { + if (deltaX != 0) { + int newX = destX - deltaX; + if (deltaX < 0) newX = destX + width; + damage.x = newX; + damage.width = Math.abs(deltaX); + view.setNeedsDisplayInRect(damage); + } + if (deltaY != 0) { + int newY = destY - deltaY; + if (deltaY < 0) newY = destY + height; + damage.x = srcX; + damage.y = newY; + damage.width = width; + damage.height = Math.abs (deltaY); + view.setNeedsDisplayInRect(damage); + } + } + + NSRect srcRect = new NSRect(); + srcRect.x = srcX; + srcRect.y = srcY; + srcRect.width = width; + srcRect.height = height; + OS.NSIntersectionRect(visibleRect, visibleRect, srcRect); + + if (!OS.NSEqualRects(visibleRect, srcRect)) { + if (srcRect.x != visibleRect.x) { + damage.x = srcRect.x + deltaX; + damage.y = srcRect.y + deltaY; + damage.width = visibleRect.x - srcRect.x; + damage.height = srcRect.height; + view.setNeedsDisplayInRect(damage); + } + if (visibleRect.x + visibleRect.width != srcRect.x + srcRect.width) { + damage.x = srcRect.x + visibleRect.width + deltaX; + damage.y = srcRect.y + deltaY; + damage.width = srcRect.width - visibleRect.width; + damage.height = srcRect.height; + view.setNeedsDisplayInRect(damage); + } + if (visibleRect.y != srcRect.y) { + damage.x = visibleRect.x + deltaX; + damage.y = srcRect.y + deltaY; + damage.width = visibleRect.width; + damage.height = visibleRect.y - srcRect.y; + view.setNeedsDisplayInRect(damage); + } + if (visibleRect.y + visibleRect.height != srcRect.y + srcRect.height) { + damage.x = visibleRect.x + deltaX; + damage.y = visibleRect.y + visibleRect.height + deltaY; + damage.width = visibleRect.width; + damage.height = srcRect.y + srcRect.height - (visibleRect.y + visibleRect.height); + view.setNeedsDisplayInRect(damage); + } + } + } + return; + } + } finally { + uncheckGC(pool); + } +} + +static int /*long*/ createCGPathRef(NSBezierPath nsPath) { + int /*long*/ count = nsPath.elementCount(); + if (count > 0) { + int /*long*/ cgPath = OS.CGPathCreateMutable(); + if (cgPath == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ points = OS.malloc(NSPoint.sizeof * 3); + if (points == 0) SWT.error(SWT.ERROR_NO_HANDLES); + float /*double*/ [] pt = new float /*double*/ [6]; + for (int i = 0; i < count; i++) { + int element = (int)/*64*/nsPath.elementAtIndex(i, points); + switch (element) { + case OS.NSMoveToBezierPathElement: + OS.memmove(pt, points, NSPoint.sizeof); + OS.CGPathMoveToPoint(cgPath, 0, pt[0], pt[1]); + break; + case OS.NSLineToBezierPathElement: + OS.memmove(pt, points, NSPoint.sizeof); + OS.CGPathAddLineToPoint(cgPath, 0, pt[0], pt[1]); + break; + case OS.NSCurveToBezierPathElement: + OS.memmove(pt, points, NSPoint.sizeof * 3); + OS.CGPathAddCurveToPoint(cgPath, 0, pt[0], pt[1], pt[2], pt[3], pt[4], pt[5]); + break; + case OS.NSClosePathBezierPathElement: + OS.CGPathCloseSubpath(cgPath); + break; + } + } + OS.free(points); + return cgPath; + } + return 0; +} + + + +NSBezierPath createNSBezierPath (int /*long*/ cgPath) { + Callback callback = new Callback(this, "applierFunc", 2); + int /*long*/ proc = callback.getAddress(); + if (proc == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + count = typeCount = 0; + element = new CGPathElement(); + OS.CGPathApply(cgPath, 0, proc); + types = new byte[typeCount]; + points = new float /*double*/ [count]; + point = new float /*double*/ [6]; + count = typeCount = 0; + OS.CGPathApply(cgPath, 0, proc); + callback.dispose(); + + NSBezierPath bezierPath = NSBezierPath.bezierPath(); + NSPoint nsPoint = new NSPoint(), nsPoint2 = new NSPoint(), nsPoint3 = new NSPoint(); + for (int i = 0, j = 0; i < types.length; i++) { + switch (types[i]) { + case SWT.PATH_MOVE_TO: + nsPoint.x = points[j++]; + nsPoint.y = points[j++]; + bezierPath.moveToPoint(nsPoint); + break; + case SWT.PATH_LINE_TO: + nsPoint.x = points[j++]; + nsPoint.y = points[j++]; + bezierPath.lineToPoint(nsPoint); + break; + case SWT.PATH_CUBIC_TO: + nsPoint2.x = points[j++]; + nsPoint2.y = points[j++]; + nsPoint3.x = points[j++]; + nsPoint3.y = points[j++]; + nsPoint.x = points[j++]; + nsPoint.y = points[j++]; + bezierPath.curveToPoint(nsPoint, nsPoint2, nsPoint3); + break; + case SWT.PATH_QUAD_TO: + float /*double*/ currentX = nsPoint.x; + float /*double*/ currentY = nsPoint.y; + nsPoint2.x = points[j++]; + nsPoint2.y = points[j++]; + nsPoint.x = points[j++]; + nsPoint.y = points[j++]; + float /*double*/ x0 = currentX; + float /*double*/ y0 = currentY; + float /*double*/ cx1 = x0 + 2 * (nsPoint2.x - x0) / 3; + float /*double*/ cy1 = y0 + 2 * (nsPoint2.y - y0) / 3; + float /*double*/ cx2 = cx1 + (nsPoint.x - x0) / 3; + float /*double*/ cy2 = cy1 + (nsPoint.y - y0) / 3; + nsPoint2.x = cx1; + nsPoint2.y = cy1; + nsPoint3.x = cx2; + nsPoint3.y = cy2; + bezierPath.curveToPoint(nsPoint, nsPoint2, nsPoint3); + break; + case SWT.PATH_CLOSE: + bezierPath.closePath(); + break; + default: + dispose(); + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + } + element = null; + types = null; + points = null; + nsPoint = null; + return bezierPath; +} + +NSAttributedString createString(String string, int flags, boolean draw) { + NSMutableDictionary dict = ((NSMutableDictionary)new NSMutableDictionary().alloc()).initWithCapacity(5); + Font font = data.font; + dict.setObject(font.handle, OS.NSFontAttributeName); + font.addTraits(dict); + if (draw) { + Pattern pattern = data.foregroundPattern; + if (pattern != null) { + if (pattern.color != null) dict.setObject(pattern.color, OS.NSForegroundColorAttributeName); + } else { + NSColor fg = data.fg; + if (fg == null) { + float /*double*/ [] color = data.foreground; + fg = data.fg = NSColor.colorWithDeviceRed(color[0], color[1], color[2], data.alpha / 255f); + fg.retain(); + } + dict.setObject(fg, OS.NSForegroundColorAttributeName); + } + } + if ((flags & SWT.DRAW_TAB) == 0) { + dict.setObject(device.paragraphStyle, OS.NSParagraphStyleAttributeName); + } + int length = string.length(); + char[] chars = new char[length]; + string.getChars(0, length, chars, 0); + int breakCount = 0; + int[] breaks = null; + if ((flags & SWT.DRAW_MNEMONIC) !=0 || (flags & SWT.DRAW_DELIMITER) == 0) { + int i=0, j=0; + while (i < chars.length) { + char c = chars [j++] = chars [i++]; + switch (c) { + case '&': { + if ((flags & SWT.DRAW_MNEMONIC) != 0) { + if (i == chars.length) {continue;} + if (chars [i] == '&') {i++; continue;} + j--; + } + break; + } + case '\r': + case '\n': { + if ((flags & SWT.DRAW_DELIMITER) == 0) { + if (c == '\r' && i != chars.length && chars[i] == '\n') i++; + j--; + if (breaks == null) { + breaks = new int[4]; + } else if (breakCount == breaks.length) { + int[] newBreaks = new int[breaks.length + 4]; + System.arraycopy(breaks, 0, newBreaks, 0, breaks.length); + breaks = newBreaks; + } + breaks[breakCount++] = j; + } + break; + } + } + } + length = j; + } + NSString str = ((NSString)new NSString().alloc()).initWithCharacters(chars, length); + NSAttributedString attribStr = ((NSAttributedString)new NSAttributedString().alloc()).initWithString(str, dict); + dict.release(); + str.release(); + return attribStr; +} + +void destroy() { + /* Free resources */ + Image image = data.image; + if (image != null) { + image.memGC = null; + image.createAlpha(); + } + if (data.fg != null) data.fg.release(); + if (data.bg != null) data.bg.release(); + if (data.path != null) data.path.release(); + if (data.clipPath != null) data.clipPath.release(); + if (data.visiblePath != null) data.visiblePath.release(); + if (data.transform != null) data.transform.release(); + if (data.inverseTransform != null) data.inverseTransform.release(); + data.path = data.clipPath = data.visiblePath = null; + data.transform = data.inverseTransform = null; + data.fg = data.bg = null; + + /* Dispose the GC */ + if (drawable != null) drawable.internal_dispose_GC(handle.id, data); + handle.restoreGraphicsState(); + handle.release(); + + drawable = null; + data.image = null; + data = null; + handle = null; +} + +/** + * Draws the outline of a circular or elliptical arc + * within the specified rectangular area. + * <p> + * The resulting arc begins at <code>startAngle</code> and extends + * for <code>arcAngle</code> degrees, using the current color. + * Angles are interpreted such that 0 degrees is at the 3 o'clock + * position. A positive value indicates a counter-clockwise rotation + * while a negative value indicates a clockwise rotation. + * </p><p> + * The center of the arc is the center of the rectangle whose origin + * is (<code>x</code>, <code>y</code>) and whose size is specified by the + * <code>width</code> and <code>height</code> arguments. + * </p><p> + * The resulting arc covers an area <code>width + 1</code> pixels wide + * by <code>height + 1</code> pixels tall. + * </p> + * + * @param x the x coordinate of the upper-left corner of the arc to be drawn + * @param y the y coordinate of the upper-left corner of the arc to be drawn + * @param width the width of the arc to be drawn + * @param height the height of the arc to be drawn + * @param startAngle the beginning angle + * @param arcAngle the angular extent of the arc, relative to the start angle + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + if (width == 0 || height == 0 || arcAngle == 0) return; + NSAutoreleasePool pool = checkGC(DRAW); + try { + handle.saveGraphicsState(); + NSAffineTransform transform = NSAffineTransform.transform(); + float /*double*/ xOffset = data.drawXOffset, yOffset = data.drawYOffset; + transform.translateXBy(x + xOffset + width / 2f, y + yOffset + height / 2f); + transform.scaleXBy(width / 2f, height / 2f); + NSBezierPath path = data.path; + NSPoint center = new NSPoint(); + float sAngle = -startAngle; + float eAngle = -(startAngle + arcAngle); + path.appendBezierPathWithArcWithCenter(center, 1, sAngle, eAngle, arcAngle>0); + path.transformUsingAffineTransform(transform); + Pattern pattern = data.foregroundPattern; + if (pattern != null && pattern.gradient != null) { + strokePattern(path, pattern); + } else { + path.stroke(); + } + path.removeAllPoints(); + handle.restoreGraphicsState(); + } finally { + uncheckGC(pool); + } +} + +/** + * Draws a rectangle, based on the specified arguments, which has + * the appearance of the platform's <em>focus rectangle</em> if the + * platform supports such a notion, and otherwise draws a simple + * rectangle in the receiver's foreground color. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param width the width of the rectangle + * @param height the height of the rectangle + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #drawRectangle(int, int, int, int) + */ +public void drawFocus(int x, int y, int width, int height) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = checkGC(CLIPPING | TRANSFORM); + try { + int[] metric = new int[1]; + OS.GetThemeMetric(OS.kThemeMetricFocusRectOutset, metric); + CGRect rect = new CGRect(); + rect.origin.x = x + metric[0]; + rect.origin.y = y + metric[0]; + rect.size.width = width - metric[0] * 2; + rect.size.height = height - metric[0] * 2; + OS.HIThemeDrawFocusRect(rect, true, handle.graphicsPort(), OS.kHIThemeOrientationNormal); + } finally { + uncheckGC(pool); + } +} + +/** + * Draws the given image in the receiver at the specified + * coordinates. + * + * @param image the image to draw + * @param x the x coordinate of where to draw + * @param y the y coordinate of where to draw + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the image is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> + * <li>ERROR_INVALID_ARGUMENT - if the given coordinates are outside the bounds of the image</li> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES - if no handles are available to perform the operation</li> + * </ul> + */ +public void drawImage(Image image, int x, int y) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (image == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (image.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + drawImage(image, 0, 0, -1, -1, x, y, -1, -1, true); +} + +/** + * Copies a rectangular area from the source image into a (potentially + * different sized) rectangular area in the receiver. If the source + * and destination areas are of differing sizes, then the source + * area will be stretched or shrunk to fit the destination area + * as it is copied. The copy fails if any part of the source rectangle + * lies outside the bounds of the source image, or if any of the width + * or height arguments are negative. + * + * @param image the source image + * @param srcX the x coordinate in the source image to copy from + * @param srcY the y coordinate in the source image to copy from + * @param srcWidth the width in pixels to copy from the source + * @param srcHeight the height in pixels to copy from the source + * @param destX the x coordinate in the destination to copy to + * @param destY the y coordinate in the destination to copy to + * @param destWidth the width in pixels of the destination rectangle + * @param destHeight the height in pixels of the destination rectangle + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the image is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> + * <li>ERROR_INVALID_ARGUMENT - if any of the width or height arguments are negative. + * <li>ERROR_INVALID_ARGUMENT - if the source rectangle is not contained within the bounds of the source image</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES - if no handles are available to perform the operation</li> + * </ul> + */ +public void drawImage(Image image, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (srcWidth == 0 || srcHeight == 0 || destWidth == 0 || destHeight == 0) return; + if (srcX < 0 || srcY < 0 || srcWidth < 0 || srcHeight < 0 || destWidth < 0 || destHeight < 0) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + if (image == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (image.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + drawImage(image, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, false); +} + +void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple) { + NSImage imageHandle = srcImage.handle; + NSSize size = imageHandle.size(); + int imgWidth = (int)size.width; + int imgHeight = (int)size.height; + if (simple) { + srcWidth = destWidth = imgWidth; + srcHeight = destHeight = imgHeight; + } else { + simple = srcX == 0 && srcY == 0 && + srcWidth == destWidth && destWidth == imgWidth && + srcHeight == destHeight && destHeight == imgHeight; + if (srcX + srcWidth > imgWidth || srcY + srcHeight > imgHeight) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + } + NSAutoreleasePool pool = checkGC(CLIPPING | TRANSFORM); + try { + if (srcImage.memGC != null) { + srcImage.createAlpha(); + } + handle.saveGraphicsState(); + NSAffineTransform transform = NSAffineTransform.transform(); + transform.scaleXBy(1, -1); + transform.translateXBy(0, -(destHeight + 2 * destY)); + transform.concat(); + NSRect srcRect = new NSRect(); + srcRect.x = srcX; + srcRect.y = imgHeight - (srcY + srcHeight); + srcRect.width = srcWidth; + srcRect.height = srcHeight; + NSRect destRect = new NSRect(); + destRect.x = destX; + destRect.y = destY; + destRect.width = destWidth; + destRect.height = destHeight; + imageHandle.drawInRect(destRect, srcRect, OS.NSCompositeSourceOver, 1); + handle.restoreGraphicsState(); + } finally { + uncheckGC(pool); + } +} + +/** + * Draws a line, using the foreground color, between the points + * (<code>x1</code>, <code>y1</code>) and (<code>x2</code>, <code>y2</code>). + * + * @param x1 the first point's x coordinate + * @param y1 the first point's y coordinate + * @param x2 the second point's x coordinate + * @param y2 the second point's y coordinate + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void drawLine(int x1, int y1, int x2, int y2) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = checkGC(DRAW); + try { + NSBezierPath path = data.path; + NSPoint pt = new NSPoint(); + pt.x = x1 + data.drawXOffset; + pt.y = y1 + data.drawYOffset; + path.moveToPoint(pt); + pt.x = x2 + data.drawXOffset; + pt.y = y2 + data.drawYOffset; + path.lineToPoint(pt); + Pattern pattern = data.foregroundPattern; + if (pattern != null && pattern.gradient != null) { + strokePattern(path, pattern); + } else { + path.stroke(); + } + path.removeAllPoints(); + } finally { + uncheckGC(pool); + } +} + +/** + * Draws the outline of an oval, using the foreground color, + * within the specified rectangular area. + * <p> + * The result is a circle or ellipse that fits within the + * rectangle specified by the <code>x</code>, <code>y</code>, + * <code>width</code>, and <code>height</code> arguments. + * </p><p> + * The oval covers an area that is <code>width + 1</code> + * pixels wide and <code>height + 1</code> pixels tall. + * </p> + * + * @param x the x coordinate of the upper left corner of the oval to be drawn + * @param y the y coordinate of the upper left corner of the oval to be drawn + * @param width the width of the oval to be drawn + * @param height the height of the oval to be drawn + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void drawOval(int x, int y, int width, int height) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = checkGC(DRAW); + try { + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + NSBezierPath path = data.path; + NSRect rect = new NSRect(); + rect.x = x + data.drawXOffset; + rect.y = y + data.drawXOffset; + rect.width = width; + rect.height = height; + path.appendBezierPathWithOvalInRect(rect); + Pattern pattern = data.foregroundPattern; + if (pattern != null && pattern.gradient != null) { + strokePattern(path, pattern); + } else { + path.stroke(); + } + path.removeAllPoints(); + } finally { + uncheckGC(pool); + } +} + +/** + * Draws the path described by the parameter. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param path the path to draw + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parameter is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * + * @see Path + * + * @since 3.1 + */ +public void drawPath(Path path) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (path == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (path.handle == null) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = checkGC(DRAW); + try { + handle.saveGraphicsState(); + NSAffineTransform transform = NSAffineTransform.transform(); + transform.translateXBy(data.drawXOffset, data.drawYOffset); + transform.concat(); + NSBezierPath drawPath = data.path; + drawPath.appendBezierPath(path.handle); + Pattern pattern = data.foregroundPattern; + if (pattern != null && pattern.gradient != null) { + strokePattern(drawPath, pattern); + } else { + drawPath.stroke(); + } + drawPath.removeAllPoints(); + handle.restoreGraphicsState(); + } finally { + uncheckGC(pool); + } +} + +/** + * Draws a pixel, using the foreground color, at the specified + * point (<code>x</code>, <code>y</code>). + * <p> + * Note that the receiver's line attributes do not affect this + * operation. + * </p> + * + * @param x the point's x coordinate + * @param y the point's y coordinate + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void drawPoint(int x, int y) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = checkGC(FOREGROUND_FILL | CLIPPING | TRANSFORM); + try { + NSRect rect = new NSRect(); + rect.x = x; + rect.y = y; + rect.width = 1; + rect.height = 1; + NSBezierPath path = data.path; + path.appendBezierPathWithRect(rect); + path.fill(); + path.removeAllPoints(); + } finally { + uncheckGC(pool); + } +} + +/** + * Draws the closed polygon which is defined by the specified array + * of integer coordinates, using the receiver's foreground color. The array + * contains alternating x and y values which are considered to represent + * points which are the vertices of the polygon. Lines are drawn between + * each consecutive pair, and between the first pair and last pair in the + * array. + * + * @param pointArray an array of alternating x and y values which are the vertices of the polygon + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT if pointArray is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void drawPolygon(int[] pointArray) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (pointArray == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (pointArray.length < 4) return; + NSAutoreleasePool pool = checkGC(DRAW); + try { + float /*double*/ xOffset = data.drawXOffset, yOffset = data.drawYOffset; + NSBezierPath path = data.path; + NSPoint pt = new NSPoint(); + pt.x = pointArray[0] + xOffset; + pt.y = pointArray[1] + yOffset; + path.moveToPoint(pt); + int end = pointArray.length / 2 * 2; + for (int i = 2; i < end; i+=2) { + pt.x = pointArray[i] + xOffset; + pt.y = pointArray[i+1] + yOffset; + path.lineToPoint(pt); + } + path.closePath(); + Pattern pattern = data.foregroundPattern; + if (pattern != null && pattern.gradient != null) { + strokePattern(path, pattern); + } else { + path.stroke(); + } + path.removeAllPoints(); + } finally { + uncheckGC(pool); + } +} + +/** + * Draws the polyline which is defined by the specified array + * of integer coordinates, using the receiver's foreground color. The array + * contains alternating x and y values which are considered to represent + * points which are the corners of the polyline. Lines are drawn between + * each consecutive pair, but not between the first pair and last pair in + * the array. + * + * @param pointArray an array of alternating x and y values which are the corners of the polyline + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the point array is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void drawPolyline(int[] pointArray) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (pointArray == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (pointArray.length < 4) return; + NSAutoreleasePool pool = checkGC(DRAW); + try { + float /*double*/ xOffset = data.drawXOffset, yOffset = data.drawYOffset; + NSBezierPath path = data.path; + NSPoint pt = new NSPoint(); + pt.x = pointArray[0] + xOffset; + pt.y = pointArray[1] + yOffset; + path.moveToPoint(pt); + int end = pointArray.length / 2 * 2; + for (int i = 2; i < end; i+=2) { + pt.x = pointArray[i] + xOffset; + pt.y = pointArray[i+1] + yOffset; + path.lineToPoint(pt); + } + Pattern pattern = data.foregroundPattern; + if (pattern != null && pattern.gradient != null) { + strokePattern(path, pattern); + } else { + path.stroke(); + } + path.removeAllPoints(); + } finally { + uncheckGC(pool); + } +} + +/** + * Draws the outline of the rectangle specified by the arguments, + * using the receiver's foreground color. The left and right edges + * of the rectangle are at <code>x</code> and <code>x + width</code>. + * The top and bottom edges are at <code>y</code> and <code>y + height</code>. + * + * @param x the x coordinate of the rectangle to be drawn + * @param y the y coordinate of the rectangle to be drawn + * @param width the width of the rectangle to be drawn + * @param height the height of the rectangle to be drawn + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void drawRectangle(int x, int y, int width, int height) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = checkGC(DRAW); + try { + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + NSRect rect = new NSRect(); + rect.x = x + data.drawXOffset; + rect.y = y + data.drawYOffset; + rect.width = width; + rect.height = height; + NSBezierPath path = data.path; + path.appendBezierPathWithRect(rect); + Pattern pattern = data.foregroundPattern; + if (pattern != null && pattern.gradient != null) { + strokePattern(path, pattern); + } else { + path.stroke(); + } + path.removeAllPoints(); + } finally { + uncheckGC(pool); + } +} + +/** + * Draws the outline of the specified rectangle, using the receiver's + * foreground color. The left and right edges of the rectangle are at + * <code>rect.x</code> and <code>rect.x + rect.width</code>. The top + * and bottom edges are at <code>rect.y</code> and + * <code>rect.y + rect.height</code>. + * + * @param rect the rectangle to draw + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the rectangle is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void drawRectangle(Rectangle rect) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (rect == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + drawRectangle (rect.x, rect.y, rect.width, rect.height); +} + +/** + * Draws the outline of the round-cornered rectangle specified by + * the arguments, using the receiver's foreground color. The left and + * right edges of the rectangle are at <code>x</code> and <code>x + width</code>. + * The top and bottom edges are at <code>y</code> and <code>y + height</code>. + * The <em>roundness</em> of the corners is specified by the + * <code>arcWidth</code> and <code>arcHeight</code> arguments, which + * are respectively the width and height of the ellipse used to draw + * the corners. + * + * @param x the x coordinate of the rectangle to be drawn + * @param y the y coordinate of the rectangle to be drawn + * @param width the width of the rectangle to be drawn + * @param height the height of the rectangle to be drawn + * @param arcWidth the width of the arc + * @param arcHeight the height of the arc + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void drawRoundRectangle(int x, int y, int width, int height, int arcWidth, int arcHeight) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (arcWidth == 0 || arcHeight == 0) { + drawRectangle(x, y, width, height); + return; + } + NSAutoreleasePool pool = checkGC(DRAW); + try { + NSBezierPath path = data.path; + NSRect rect = new NSRect(); + rect.x = x + data.drawXOffset; + rect.y = y + data.drawYOffset; + rect.width = width; + rect.height = height; + path.appendBezierPathWithRoundedRect(rect, arcWidth / 2f, arcHeight / 2f); + Pattern pattern = data.foregroundPattern; + if (pattern != null && pattern.gradient != null) { + strokePattern(path, pattern); + } else { + path.stroke(); + } + path.removeAllPoints(); + } finally { + uncheckGC(pool); + } +} + +/** + * Draws the given string, using the receiver's current font and + * foreground color. No tab expansion or carriage return processing + * will be performed. The background of the rectangular area where + * the string is being drawn will be filled with the receiver's + * background color. + * + * @param string the string to be drawn + * @param x the x coordinate of the top left corner of the rectangular area where the string is to be drawn + * @param y the y coordinate of the top left corner of the rectangular area where the string is to be drawn + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void drawString (String string, int x, int y) { + drawString(string, x, y, false); +} + +/** + * Draws the given string, using the receiver's current font and + * foreground color. No tab expansion or carriage return processing + * will be performed. If <code>isTransparent</code> is <code>true</code>, + * then the background of the rectangular area where the string is being + * drawn will not be modified, otherwise it will be filled with the + * receiver's background color. + * + * @param string the string to be drawn + * @param x the x coordinate of the top left corner of the rectangular area where the string is to be drawn + * @param y the y coordinate of the top left corner of the rectangular area where the string is to be drawn + * @param isTransparent if <code>true</code> the background will be transparent, otherwise it will be opaque + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void drawString(String string, int x, int y, boolean isTransparent) { + drawText(string, x, y, isTransparent ? SWT.DRAW_TRANSPARENT : 0); +} + +/** + * Draws the given string, using the receiver's current font and + * foreground color. Tab expansion and carriage return processing + * are performed. The background of the rectangular area where + * the text is being drawn will be filled with the receiver's + * background color. + * + * @param string the string to be drawn + * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn + * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void drawText(String string, int x, int y) { + drawText(string, x, y, SWT.DRAW_DELIMITER | SWT.DRAW_TAB); +} + +/** + * Draws the given string, using the receiver's current font and + * foreground color. Tab expansion and carriage return processing + * are performed. If <code>isTransparent</code> is <code>true</code>, + * then the background of the rectangular area where the text is being + * drawn will not be modified, otherwise it will be filled with the + * receiver's background color. + * + * @param string the string to be drawn + * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn + * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn + * @param isTransparent if <code>true</code> the background will be transparent, otherwise it will be opaque + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void drawText(String string, int x, int y, boolean isTransparent) { + int flags = SWT.DRAW_DELIMITER | SWT.DRAW_TAB; + if (isTransparent) flags |= SWT.DRAW_TRANSPARENT; + drawText(string, x, y, flags); +} + +/** + * Draws the given string, using the receiver's current font and + * foreground color. Tab expansion, line delimiter and mnemonic + * processing are performed according to the specified flags. If + * <code>flags</code> includes <code>DRAW_TRANSPARENT</code>, + * then the background of the rectangular area where the text is being + * drawn will not be modified, otherwise it will be filled with the + * receiver's background color. + * <p> + * The parameter <code>flags</code> may be a combination of: + * <dl> + * <dt><b>DRAW_DELIMITER</b></dt> + * <dd>draw multiple lines</dd> + * <dt><b>DRAW_TAB</b></dt> + * <dd>expand tabs</dd> + * <dt><b>DRAW_MNEMONIC</b></dt> + * <dd>underline the mnemonic character</dd> + * <dt><b>DRAW_TRANSPARENT</b></dt> + * <dd>transparent background</dd> + * </dl> + * </p> + * + * @param string the string to be drawn + * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn + * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn + * @param flags the flags specifying how to process the text + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void drawText (String string, int x, int y, int flags) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (string == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + NSAutoreleasePool pool = checkGC(CLIPPING | TRANSFORM | FONT); + try { + handle.saveGraphicsState(); + boolean mode = true; + switch (data.textAntialias) { + case SWT.DEFAULT: + /* Printer is off by default */ + if (!handle.isDrawingToScreen()) mode = false; + break; + case SWT.OFF: mode = false; break; + case SWT.ON: mode = true; break; + } + handle.setShouldAntialias(mode); + NSAttributedString str = createString(string, flags, true); + if ((flags & SWT.DRAW_TRANSPARENT) == 0) { + NSSize size = str.size(); + NSRect rect = new NSRect(); + rect.x = x; + rect.y = y; + rect.width = size.width; + rect.height = size.height; + NSColor bg = data.bg; + if (bg == null) { + float /*double*/ [] color = data.background; + bg = data.bg = NSColor.colorWithDeviceRed(color[0], color[1], color[2], data.alpha / 255f); + bg.retain(); + } + bg.setFill(); + NSBezierPath.fillRect(rect); + str.drawInRect(rect); + } else { + NSPoint pt = new NSPoint(); + pt.x = x; + pt.y = y; + str.drawAtPoint(pt); + } + str.release(); + handle.restoreGraphicsState(); + } finally { + uncheckGC(pool); + } +} + +/** + * Compares the argument to the receiver, and returns true + * if they represent the <em>same</em> object using a class + * specific comparison. + * + * @param object the object to compare with this object + * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise + * + * @see #hashCode + */ +public boolean equals(Object object) { + if (object == this) return true; + if (!(object instanceof GC)) return false; + return handle == ((GC)object).handle; +} + +/** + * Fills the interior of a circular or elliptical arc within + * the specified rectangular area, with the receiver's background + * color. + * <p> + * The resulting arc begins at <code>startAngle</code> and extends + * for <code>arcAngle</code> degrees, using the current color. + * Angles are interpreted such that 0 degrees is at the 3 o'clock + * position. A positive value indicates a counter-clockwise rotation + * while a negative value indicates a clockwise rotation. + * </p><p> + * The center of the arc is the center of the rectangle whose origin + * is (<code>x</code>, <code>y</code>) and whose size is specified by the + * <code>width</code> and <code>height</code> arguments. + * </p><p> + * The resulting arc covers an area <code>width + 1</code> pixels wide + * by <code>height + 1</code> pixels tall. + * </p> + * + * @param x the x coordinate of the upper-left corner of the arc to be filled + * @param y the y coordinate of the upper-left corner of the arc to be filled + * @param width the width of the arc to be filled + * @param height the height of the arc to be filled + * @param startAngle the beginning angle + * @param arcAngle the angular extent of the arc, relative to the start angle + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #drawArc + */ +public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + if (width == 0 || height == 0 || arcAngle == 0) return; + NSAutoreleasePool pool = checkGC(FILL); + try { + handle.saveGraphicsState(); + NSAffineTransform transform = NSAffineTransform.transform(); + float /*double*/ xOffset = data.drawXOffset, yOffset = data.drawYOffset; + transform.translateXBy(x + xOffset + width / 2f, y + yOffset + height / 2f); + transform.scaleXBy(width / 2f, height / 2f); + NSBezierPath path = data.path; + NSPoint center = new NSPoint(); + path.moveToPoint(center); + float sAngle = -startAngle; + float eAngle = -(startAngle + arcAngle); + path.appendBezierPathWithArcWithCenter(center, 1, sAngle, eAngle, arcAngle>0); + path.closePath(); + path.transformUsingAffineTransform(transform); + Pattern pattern = data.backgroundPattern; + if (pattern != null && pattern.gradient != null) { + fillPattern(path, pattern); + } else { + path.fill(); + } + path.removeAllPoints(); + handle.restoreGraphicsState(); + } finally { + uncheckGC(pool); + } +} + +/** + * Fills the interior of the specified rectangle with a gradient + * sweeping from left to right or top to bottom progressing + * from the receiver's foreground color to its background color. + * + * @param x the x coordinate of the rectangle to be filled + * @param y the y coordinate of the rectangle to be filled + * @param width the width of the rectangle to be filled, may be negative + * (inverts direction of gradient if horizontal) + * @param height the height of the rectangle to be filled, may be negative + * (inverts direction of gradient if vertical) + * @param vertical if true sweeps from top to bottom, else + * sweeps from left to right + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #drawRectangle(int, int, int, int) + */ +public void fillGradientRectangle(int x, int y, int width, int height, boolean vertical) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if ((width == 0) || (height == 0)) return; + NSAutoreleasePool pool = checkGC(CLIPPING | TRANSFORM); + try { + RGB backgroundRGB, foregroundRGB; + backgroundRGB = getBackground().getRGB(); + foregroundRGB = getForeground().getRGB(); + + RGB fromRGB, toRGB; + fromRGB = foregroundRGB; + toRGB = backgroundRGB; + boolean swapColors = false; + if (width < 0) { + x += width; width = -width; + if (! vertical) swapColors = true; + } + if (height < 0) { + y += height; height = -height; + if (vertical) swapColors = true; + } + if (swapColors) { + fromRGB = backgroundRGB; + toRGB = foregroundRGB; + } + if (fromRGB.equals(toRGB)) { + fillRectangle(x, y, width, height); + } else { + NSColor startingColor = NSColor.colorWithDeviceRed(fromRGB.red / 255f, fromRGB.green / 255f, fromRGB.blue / 255f, data.alpha / 255f); + NSColor endingColor = NSColor.colorWithDeviceRed(toRGB.red / 255f, toRGB.green / 255f, toRGB.blue / 255f, data.alpha / 255f); + NSGradient gradient = ((NSGradient)new NSGradient().alloc()).initWithStartingColor(startingColor, endingColor); + NSRect rect = new NSRect(); + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + gradient.drawInRect(rect, vertical ? 90 : 0); + gradient.release(); + } + } finally { + uncheckGC(pool); + } +} + +/** + * Fills the interior of an oval, within the specified + * rectangular area, with the receiver's background + * color. + * + * @param x the x coordinate of the upper left corner of the oval to be filled + * @param y the y coordinate of the upper left corner of the oval to be filled + * @param width the width of the oval to be filled + * @param height the height of the oval to be filled + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #drawOval + */ +public void fillOval(int x, int y, int width, int height) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = checkGC(FILL); + try { + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + NSBezierPath path = data.path; + NSRect rect = new NSRect(); + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + path.appendBezierPathWithOvalInRect(rect); + Pattern pattern = data.backgroundPattern; + if (pattern != null && pattern.gradient != null) { + fillPattern(path, pattern); + } else { + path.fill(); + } + path.removeAllPoints(); + } finally { + uncheckGC(pool); + } +} + +void fillPattern(NSBezierPath path, Pattern pattern) { + handle.saveGraphicsState(); + path.addClip(); + NSRect bounds = path.bounds(); + NSPoint start = new NSPoint(); + start.x = pattern.pt1.x; + start.y = pattern.pt1.y; + NSPoint end = new NSPoint(); + end.x = pattern.pt2.x; + end.y = pattern.pt2.y; + float /*double*/ difx = end.x - start.x; + float /*double*/ dify = end.y - start.y; + if (difx == 0 && dify == 0) { + float /*double*/ [] color = pattern.color1; + NSColor.colorWithDeviceRed(color[0], color[1], color[2], data.alpha / 255f).setFill(); + path.fill(); + handle.restoreGraphicsState(); + return; + } + float /*double*/ startx, starty, endx, endy; + if (difx == 0 || dify == 0) { + startx = bounds.x; + starty = bounds.y; + endx = bounds.x + bounds.width; + endy = bounds.y + bounds.height; + if (difx < 0 || dify < 0) { + startx = endx; + starty = endy; + endx = bounds.x; + endy = bounds.y; + } + } else { + float /*double*/ m = (end.y-start.y)/(end.x - start.x); + float /*double*/ b = end.y - (m * end.x); + float /*double*/ m2 = -1/m; //perpendicular slope + float /*double*/ b2 = bounds.y - (m2 * bounds.x); + startx = endx = (b - b2) / (m2 - m); + b2 = (bounds.y + bounds.height) - (m2 * bounds.x); + float /*double*/ x2 = (b - b2) / (m2 - m); + startx = difx > 0 ? Math.min(startx, x2) : Math.max(startx, x2); + endx = difx < 0 ? Math.min(endx, x2) : Math.max(endx, x2); + b2 = bounds.y - (m2 * (bounds.x + bounds.width)); + x2 = (b - b2) / (m2 - m); + startx = difx > 0 ? Math.min(startx, x2) : Math.max(startx, x2); + endx = difx < 0 ? Math.min(endx, x2) : Math.max(endx, x2); + b2 = (bounds.y + bounds.height) - (m2 * (bounds.x + bounds.width)); + x2 = (b - b2) / (m2 - m); + startx = difx > 0 ? Math.min(startx, x2) : Math.max(startx, x2); + endx = difx < 0 ? Math.min(endx, x2) : Math.max(endx, x2); + starty = (m * startx) + b; + endy = (m * endx) + b; + } + if (difx != 0) { + while ((difx > 0 && start.x >= startx) || (difx < 0 && start.x <= startx)) { + start.x -= difx; + start.y -= dify; + } + } else { + while ((dify > 0 && start.y >= starty) || (dify < 0 && start.y <= starty)) { + start.x -= difx; + start.y -= dify; + } + } + end.x = start.x; + end.y = start.y; + do { + end.x += difx; + end.y += dify; + pattern.gradient.drawFromPoint(start, end, 0); + start.x = end.x; + start.y = end.y; + } while ( + (difx > 0 && end.x <= endx) || + (difx < 0 && end.x >= endx) || + (difx == 0 && ((dify > 0 && end.y <= endy) || (dify < 0 && end.y >= endy))) + ); + handle.restoreGraphicsState(); +} + +/** + * Fills the path described by the parameter. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param path the path to fill + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parameter is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * + * @see Path + * + * @since 3.1 + */ +public void fillPath(Path path) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (path == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (path.handle == null) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = checkGC(FILL); + try { + NSBezierPath drawPath = data.path; + drawPath.appendBezierPath(path.handle); + Pattern pattern = data.backgroundPattern; + if (pattern != null && pattern.gradient != null) { + fillPattern(drawPath, pattern); + } else { + drawPath.fill(); + } + drawPath.removeAllPoints(); + } finally { + uncheckGC(pool); + } +} + +/** + * Fills the interior of the closed polygon which is defined by the + * specified array of integer coordinates, using the receiver's + * background color. The array contains alternating x and y values + * which are considered to represent points which are the vertices of + * the polygon. Lines are drawn between each consecutive pair, and + * between the first pair and last pair in the array. + * + * @param pointArray an array of alternating x and y values which are the vertices of the polygon + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT if pointArray is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #drawPolygon + */ +public void fillPolygon(int[] pointArray) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (pointArray == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (pointArray.length < 4) return; + NSAutoreleasePool pool = checkGC(FILL); + try { + NSBezierPath path = data.path; + NSPoint pt = new NSPoint(); + pt.x = pointArray[0]; + pt.y = pointArray[1]; + path.moveToPoint(pt); + int end = pointArray.length / 2 * 2; + for (int i = 2; i < end; i+=2) { + pt.x = pointArray[i]; + pt.y = pointArray[i+1]; + path.lineToPoint(pt); + } + path.closePath(); + Pattern pattern = data.backgroundPattern; + if (pattern != null && pattern.gradient != null) { + fillPattern(path, pattern); + } else { + path.fill(); + } + path.removeAllPoints(); + } finally { + uncheckGC(pool); + } +} + +/** + * Fills the interior of the rectangle specified by the arguments, + * using the receiver's background color. + * + * @param x the x coordinate of the rectangle to be filled + * @param y the y coordinate of the rectangle to be filled + * @param width the width of the rectangle to be filled + * @param height the height of the rectangle to be filled + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #drawRectangle(int, int, int, int) + */ +public void fillRectangle(int x, int y, int width, int height) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = checkGC(FILL); + try { + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + NSRect rect = new NSRect(); + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + NSBezierPath path = data.path; + path.appendBezierPathWithRect(rect); + Pattern pattern = data.backgroundPattern; + if (pattern != null && pattern.gradient != null) { + fillPattern(path, pattern); + } else { + path.fill(); + } + path.removeAllPoints(); + } finally { + uncheckGC(pool); + } +} + +/** + * Fills the interior of the specified rectangle, using the receiver's + * background color. + * + * @param rect the rectangle to be filled + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the rectangle is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #drawRectangle(int, int, int, int) + */ +public void fillRectangle(Rectangle rect) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (rect == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + fillRectangle(rect.x, rect.y, rect.width, rect.height); +} + +/** + * Fills the interior of the round-cornered rectangle specified by + * the arguments, using the receiver's background color. + * + * @param x the x coordinate of the rectangle to be filled + * @param y the y coordinate of the rectangle to be filled + * @param width the width of the rectangle to be filled + * @param height the height of the rectangle to be filled + * @param arcWidth the width of the arc + * @param arcHeight the height of the arc + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #drawRoundRectangle + */ +public void fillRoundRectangle(int x, int y, int width, int height, int arcWidth, int arcHeight) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (arcWidth == 0 || arcHeight == 0) { + fillRectangle(x, y, width, height); + return; + } + NSAutoreleasePool pool = checkGC(FILL); + try { + NSBezierPath path = data.path; + NSRect rect = new NSRect(); + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + path.appendBezierPathWithRoundedRect(rect, arcWidth / 2f, arcHeight / 2f); + Pattern pattern = data.backgroundPattern; + if (pattern != null && pattern.gradient != null) { + fillPattern(path, pattern); + } else { + path.fill(); + } + path.removeAllPoints(); + } finally { + uncheckGC(pool); + } +} + +void strokePattern(NSBezierPath path, Pattern pattern) { + handle.saveGraphicsState(); + int /*long*/ cgPath = createCGPathRef(path); + int /*long*/ cgContext = handle.graphicsPort(); + OS.CGContextSaveGState(cgContext); + initCGContext(cgContext); + OS.CGContextAddPath(cgContext, cgPath); + OS.CGContextReplacePathWithStrokedPath(cgContext); + OS.CGPathRelease(cgPath); + cgPath = 0; + cgPath = OS.CGContextCopyPath(cgContext); + if (cgPath == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.CGContextRestoreGState(cgContext); + NSBezierPath strokePath = createNSBezierPath(cgPath); + OS.CGPathRelease(cgPath); + fillPattern(strokePath, pattern); + handle.restoreGraphicsState(); +} + +void flush () { + handle.flushGraphics(); +} + +/** + * Returns the <em>advance width</em> of the specified character in + * the font which is currently selected into the receiver. + * <p> + * The advance width is defined as the horizontal distance the cursor + * should move after printing the character in the selected font. + * </p> + * + * @param ch the character to measure + * @return the distance in the x direction to move past the character before painting the next + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int getAdvanceWidth(char ch) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + //NOT DONE + return stringExtent(new String(new char[]{ch})).x; +} + +/** + * Returns the background color. + * + * @return the receiver's background color + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Color getBackground() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return Color.cocoa_new (data.device, data.background); +} + +/** + * Returns the background pattern. The default value is + * <code>null</code>. + * + * @return the receiver's background pattern + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see Pattern + * + * @since 3.1 + */ +public Pattern getBackgroundPattern() { + if (handle == null) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + return data.backgroundPattern; +} + +/** + * Returns <code>true</code> if receiver is using the operating system's + * advanced graphics subsystem. Otherwise, <code>false</code> is returned + * to indicate that normal graphics are in use. + * <p> + * Advanced graphics may not be installed for the operating system. In this + * case, <code>false</code> is always returned. Some operating system have + * only one graphics subsystem. If this subsystem supports advanced graphics, + * then <code>true</code> is always returned. If any graphics operation such + * as alpha, antialias, patterns, interpolation, paths, clipping or transformation + * has caused the receiver to switch from regular to advanced graphics mode, + * <code>true</code> is returned. If the receiver has been explicitly switched + * to advanced mode and this mode is supported, <code>true</code> is returned. + * </p> + * + * @return the advanced value + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #setAdvanced + * + * @since 3.1 + */ +public boolean getAdvanced() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return true; +} + +/** + * Returns the receiver's alpha value. The alpha value + * is between 0 (transparent) and 255 (opaque). + * + * @return the alpha value + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public int getAlpha() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return data.alpha; +} + +/** + * Returns the receiver's anti-aliasing setting value, which will be + * one of <code>SWT.DEFAULT</code>, <code>SWT.OFF</code> or + * <code>SWT.ON</code>. Note that this controls anti-aliasing for all + * <em>non-text drawing</em> operations. + * + * @return the anti-aliasing setting + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #getTextAntialias + * + * @since 3.1 + */ +public int getAntialias() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return data.antialias; +} + +/** + * Returns the width of the specified character in the font + * selected into the receiver. + * <p> + * The width is defined as the space taken up by the actual + * character, not including the leading and tailing whitespace + * or overhang. + * </p> + * + * @param ch the character to measure + * @return the width of the character + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int getCharWidth(char ch) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + //NOT DONE + return stringExtent(new String(new char[]{ch})).x; +} + +/** + * Returns the bounding rectangle of the receiver's clipping + * region. If no clipping region is set, the return value + * will be a rectangle which covers the entire bounds of the + * object the receiver is drawing on. + * + * @return the bounding rectangle of the clipping region + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Rectangle getClipping() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSRect rect = null; + if (data.view != null) { + rect = data.view.visibleRect(); + } else { + rect = new NSRect(); + if (data.image != null) { + NSSize size = data.image.handle.size(); + rect.width = size.width; + rect.height = size.height; + } else if (data.size != null) { + rect.width = data.size.width; + rect.height = data.size.height; + } + } + if (data.paintRect != null || data.clipPath != null || data.inverseTransform != null) { + if (data.paintRect != null) { + OS.NSIntersectionRect(rect, rect, data.paintRect); + } + if (data.clipPath != null) { + NSRect clip = data.clipPath.bounds(); + OS.NSIntersectionRect(rect, rect, clip); + } + if (data.inverseTransform != null && rect.width > 0 && rect.height > 0) { + NSPoint pt = new NSPoint(); + pt.x = rect.x; + pt.y = rect.y; + NSSize size = new NSSize(); + size.width = rect.width; + size.height = rect.height; + pt = data.inverseTransform.transformPoint(pt); + size = data.inverseTransform.transformSize(size); + rect.x = pt.x; + rect.y = pt.y; + rect.width = size.width; + rect.height = size.height; + } + } + return new Rectangle((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the region managed by the argument to the current + * clipping region of the receiver. + * + * @param region the region to fill with the clipping region + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the region is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the region is disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void getClipping(Region region) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (region == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + region.subtract(region); + NSRect rect = null; + if (data.view != null) { + rect = data.view.visibleRect(); + } else { + rect = new NSRect(); + if (data.image != null) { + NSSize size = data.image.handle.size(); + rect.width = size.width; + rect.height = size.height; + } else if (data.size != null) { + rect.width = data.size.width; + rect.height = data.size.height; + } + } + region.add((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height); + NSRect paintRect = data.paintRect; + if (paintRect != null) { + region.intersect((int)paintRect.x, (int)paintRect.y, (int)paintRect.width, (int)paintRect.height); + } + if (data.clipPath != null) { + NSBezierPath clip = data.clipPath.bezierPathByFlatteningPath(); + int count = (int)/*64*/clip.elementCount(); + int pointCount = 0; + Region clipRgn = new Region(device); + int[] pointArray = new int[count * 2]; + int /*long*/ points = OS.malloc(NSPoint.sizeof); + if (points == 0) SWT.error(SWT.ERROR_NO_HANDLES); + NSPoint pt = new NSPoint(); + for (int i = 0; i < count; i++) { + int element = (int)/*64*/clip.elementAtIndex(i, points); + switch (element) { + case OS.NSMoveToBezierPathElement: + if (pointCount != 0) clipRgn.add(pointArray, pointCount); + pointCount = 0; + OS.memmove(pt, points, NSPoint.sizeof); + pointArray[pointCount++] = (int)pt.x; + pointArray[pointCount++] = (int)pt.y; + break; + case OS.NSLineToBezierPathElement: + OS.memmove(pt, points, NSPoint.sizeof); + pointArray[pointCount++] = (int)pt.x; + pointArray[pointCount++] = (int)pt.y; + break; + case OS.NSClosePathBezierPathElement: + if (pointCount != 0) clipRgn.add(pointArray, pointCount); + pointCount = 0; + break; + } + } + if (pointCount != 0) clipRgn.add(pointArray, pointCount); + OS.free(points); + region.intersect(clipRgn); + clipRgn.dispose(); + } + if (data.inverseTransform != null) { + region.convertRgn(data.inverseTransform); + } + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns the receiver's fill rule, which will be one of + * <code>SWT.FILL_EVEN_ODD</code> or <code>SWT.FILL_WINDING</code>. + * + * @return the receiver's fill rule + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public int getFillRule() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return data.fillRule; +} + +/** + * Returns the font currently being used by the receiver + * to draw and measure text. + * + * @return the receiver's font + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Font getFont() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return data.font; +} + +/** + * Returns a FontMetrics which contains information + * about the font currently being used by the receiver + * to draw and measure text. + * + * @return font metrics for the receiver's font + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public FontMetrics getFontMetrics() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = checkGC(FONT); + try { + NSFont font = data.font.handle; + int ascent = (int)(0.5f + font.ascender()); + int descent = (int)(0.5f + (-font.descender() + font.leading())); + String s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + int averageCharWidth = stringExtent(s).x / s.length(); + return FontMetrics.cocoa_new(ascent, descent, averageCharWidth, 0, ascent + descent); + } finally { + uncheckGC(pool); + } +} + +/** + * Returns the receiver's foreground color. + * + * @return the color used for drawing foreground things + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Color getForeground() { + if (handle == null) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + return Color.cocoa_new(data.device, data.foreground); +} + +/** + * Returns the foreground pattern. The default value is + * <code>null</code>. + * + * @return the receiver's foreground pattern + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see Pattern + * + * @since 3.1 + */ +public Pattern getForegroundPattern() { + if (handle == null) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + return data.foregroundPattern; +} + +/** + * Returns the GCData. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>GC</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @return the receiver's GCData + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see GCData + * + * @since 3.2 + * @noreference This method is not intended to be referenced by clients. + */ +public GCData getGCData() { + if (handle == null) SWT.error(SWT.ERROR_WIDGET_DISPOSED); + return data; +} + +/** + * Returns the receiver's interpolation setting, which will be one of + * <code>SWT.DEFAULT</code>, <code>SWT.NONE</code>, + * <code>SWT.LOW</code> or <code>SWT.HIGH</code>. + * + * @return the receiver's interpolation setting + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public int getInterpolation() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + int interpolation = (int)/*64*/handle.imageInterpolation(); + switch (interpolation) { + case OS.NSImageInterpolationDefault: return SWT.DEFAULT; + case OS.NSImageInterpolationNone: return SWT.NONE; + case OS.NSImageInterpolationLow: return SWT.LOW; + case OS.NSImageInterpolationHigh: return SWT.HIGH; + } + return SWT.DEFAULT; +} + +/** + * Returns the receiver's line attributes. + * + * @return the line attributes used for drawing lines + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.3 + */ +public LineAttributes getLineAttributes() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + float[] dashes = null; + if (data.lineDashes != null) { + dashes = new float[data.lineDashes.length]; + System.arraycopy(data.lineDashes, 0, dashes, 0, dashes.length); + } + return new LineAttributes(data.lineWidth, data.lineCap, data.lineJoin, data.lineStyle, dashes, data.lineDashesOffset, data.lineMiterLimit); +} + +/** + * Returns the receiver's line cap style, which will be one + * of the constants <code>SWT.CAP_FLAT</code>, <code>SWT.CAP_ROUND</code>, + * or <code>SWT.CAP_SQUARE</code>. + * + * @return the cap style used for drawing lines + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public int getLineCap() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return data.lineCap; +} + +/** + * Returns the receiver's line dash style. The default value is + * <code>null</code>. + * + * @return the line dash style used for drawing lines + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public int[] getLineDash() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (data.lineDashes == null) return null; + int[] lineDashes = new int[data.lineDashes.length]; + for (int i = 0; i < lineDashes.length; i++) { + lineDashes[i] = (int)data.lineDashes[i]; + } + return lineDashes; +} + +/** + * Returns the receiver's line join style, which will be one + * of the constants <code>SWT.JOIN_MITER</code>, <code>SWT.JOIN_ROUND</code>, + * or <code>SWT.JOIN_BEVEL</code>. + * + * @return the join style used for drawing lines + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public int getLineJoin() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return data.lineJoin; +} + +/** + * Returns the receiver's line style, which will be one + * of the constants <code>SWT.LINE_SOLID</code>, <code>SWT.LINE_DASH</code>, + * <code>SWT.LINE_DOT</code>, <code>SWT.LINE_DASHDOT</code> or + * <code>SWT.LINE_DASHDOTDOT</code>. + * + * @return the style used for drawing lines + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int getLineStyle() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return data.lineStyle; +} + +/** + * Returns the width that will be used when drawing lines + * for all of the figure drawing operations (that is, + * <code>drawLine</code>, <code>drawRectangle</code>, + * <code>drawPolyline</code>, and so forth. + * + * @return the receiver's line width + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int getLineWidth() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return (int)data.lineWidth; +} + +/** + * Returns the receiver's style information. + * <p> + * Note that the value which is returned by this method <em>may + * not match</em> the value which was provided to the constructor + * when the receiver was created. This can occur when the underlying + * operating system does not support a particular combination of + * requested styles. + * </p> + * + * @return the style bits + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 2.1.2 + */ +public int getStyle () { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return data.style; +} + +/** + * Returns the receiver's text drawing anti-aliasing setting value, + * which will be one of <code>SWT.DEFAULT</code>, <code>SWT.OFF</code> or + * <code>SWT.ON</code>. Note that this controls anti-aliasing + * <em>only</em> for text drawing operations. + * + * @return the anti-aliasing setting + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #getAntialias + * + * @since 3.1 + */ +public int getTextAntialias() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return data.textAntialias; +} + +/** + * Sets the parameter to the transform that is currently being + * used by the receiver. + * + * @param transform the destination to copy the transform into + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parameter is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see Transform + * + * @since 3.1 + */ +public void getTransform (Transform transform) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (transform == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (transform.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAffineTransform cmt = data.transform; + if (cmt != null) { + NSAffineTransformStruct struct = cmt.transformStruct(); + transform.handle.setTransformStruct(struct); + } else { + transform.setElements(1, 0, 0, 1, 0, 0); + } +} + +/** + * Returns <code>true</code> if this GC is drawing in the mode + * where the resulting color in the destination is the + * <em>exclusive or</em> of the color values in the source + * and the destination, and <code>false</code> if it is + * drawing in the mode where the destination color is being + * replaced with the source color value. + * + * @return <code>true</code> true if the receiver is in XOR mode, and false otherwise + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public boolean getXORMode() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return data.xorMode; +} + +/** + * Returns an integer hash code for the receiver. Any two + * objects that return <code>true</code> when passed to + * <code>equals</code> must return the same value for this + * method. + * + * @return the receiver's hash + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #equals + */ +public int hashCode() { + return handle != null ? (int)/*64*/handle.id : 0; +} + +void init(Drawable drawable, GCData data, int /*long*/ context) { + if (data.foreground != null) data.state &= ~(FOREGROUND | FOREGROUND_FILL); + if (data.background != null) data.state &= ~BACKGROUND; + if (data.font != null) data.state &= ~FONT; + data.state &= ~DRAW_OFFSET; + + Image image = data.image; + if (image != null) image.memGC = this; + this.drawable = drawable; + this.data = data; + handle = new NSGraphicsContext(context); + handle.retain(); + handle.saveGraphicsState(); + data.path = NSBezierPath.bezierPath(); + data.path.setWindingRule(data.fillRule == SWT.FILL_WINDING ? OS.NSNonZeroWindingRule : OS.NSEvenOddWindingRule); + data.path.retain(); +} + +void initCGContext(int /*long*/ cgContext) { + int state = data.state; + if ((state & LINE_WIDTH) != 0) { + OS.CGContextSetLineWidth(cgContext, data.lineWidth == 0 ? 1 : data.lineWidth); + switch (data.lineStyle) { + case SWT.LINE_DOT: + case SWT.LINE_DASH: + case SWT.LINE_DASHDOT: + case SWT.LINE_DASHDOTDOT: + state |= LINE_STYLE; + } + } + if ((state & LINE_STYLE) != 0) { + float[] dashes = null; + float width = data.lineWidth; + switch (data.lineStyle) { + case SWT.LINE_SOLID: break; + case SWT.LINE_DASH: dashes = width != 0 ? LINE_DASH : LINE_DASH_ZERO; break; + case SWT.LINE_DOT: dashes = width != 0 ? LINE_DOT : LINE_DOT_ZERO; break; + case SWT.LINE_DASHDOT: dashes = width != 0 ? LINE_DASHDOT : LINE_DASHDOT_ZERO; break; + case SWT.LINE_DASHDOTDOT: dashes = width != 0 ? LINE_DASHDOTDOT : LINE_DASHDOTDOT_ZERO; break; + case SWT.LINE_CUSTOM: dashes = data.lineDashes; break; + } + if (dashes != null) { + float[] lengths = new float[dashes.length]; + for (int i = 0; i < lengths.length; i++) { + lengths[i] = width == 0 || data.lineStyle == SWT.LINE_CUSTOM ? dashes[i] : dashes[i] * width; + } + OS.CGContextSetLineDash(cgContext, data.lineDashesOffset, lengths, lengths.length); + } else { + OS.CGContextSetLineDash(cgContext, 0, null, 0); + } + } + if ((state & LINE_MITERLIMIT) != 0) { + OS.CGContextSetMiterLimit(cgContext, data.lineMiterLimit); + } + if ((state & LINE_JOIN) != 0) { + int joinStyle = 0; + switch (data.lineJoin) { + case SWT.JOIN_MITER: joinStyle = OS.kCGLineJoinMiter; break; + case SWT.JOIN_ROUND: joinStyle = OS.kCGLineJoinRound; break; + case SWT.JOIN_BEVEL: joinStyle = OS.kCGLineJoinBevel; break; + } + OS.CGContextSetLineJoin(cgContext, joinStyle); + } + if ((state & LINE_CAP) != 0) { + int capStyle = 0; + switch (data.lineCap) { + case SWT.CAP_ROUND: capStyle = OS.kCGLineCapRound; break; + case SWT.CAP_FLAT: capStyle = OS.kCGLineCapButt; break; + case SWT.CAP_SQUARE: capStyle = OS.kCGLineCapSquare; break; + } + OS.CGContextSetLineCap(cgContext, capStyle); + } +} + +/** + * Returns <code>true</code> if the receiver has a clipping + * region set into it, and <code>false</code> otherwise. + * If this method returns false, the receiver will draw on all + * available space in the destination. If it returns true, + * it will draw only in the area that is covered by the region + * that can be accessed with <code>getClipping(region)</code>. + * + * @return <code>true</code> if the GC has a clipping region, and <code>false</code> otherwise + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public boolean isClipped() { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return data.clipPath != null; +} + +/** + * Returns <code>true</code> if the GC has been disposed, + * and <code>false</code> otherwise. + * <p> + * This method gets the dispose state for the GC. + * When a GC has been disposed, it is an error to + * invoke any other method using the GC. + * + * @return <code>true</code> when the GC is disposed and <code>false</code> otherwise + */ +public boolean isDisposed() { + return handle == null; +} + +boolean isIdentity(float[] transform) { + return transform[0] == 1 && transform[1] == 0 && transform[2] == 0 + && transform[3] == 1 && transform[4] == 0 && transform[5] == 0; +} + +/** + * Sets the receiver to always use the operating system's advanced graphics + * subsystem for all graphics operations if the argument is <code>true</code>. + * If the argument is <code>false</code>, the advanced graphics subsystem is + * no longer used, advanced graphics state is cleared and the normal graphics + * subsystem is used from now on. + * <p> + * Normally, the advanced graphics subsystem is invoked automatically when + * any one of the alpha, antialias, patterns, interpolation, paths, clipping + * or transformation operations in the receiver is requested. When the receiver + * is switched into advanced mode, the advanced graphics subsystem performs both + * advanced and normal graphics operations. Because the two subsystems are + * different, their output may differ. Switching to advanced graphics before + * any graphics operations are performed ensures that the output is consistent. + * </p><p> + * Advanced graphics may not be installed for the operating system. In this + * case, this operation does nothing. Some operating system have only one + * graphics subsystem, so switching from normal to advanced graphics does + * nothing. However, switching from advanced to normal graphics will always + * clear the advanced graphics state, even for operating systems that have + * only one graphics subsystem. + * </p> + * + * @param advanced the new advanced graphics state + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #setAlpha + * @see #setAntialias + * @see #setBackgroundPattern + * @see #setClipping(Path) + * @see #setForegroundPattern + * @see #setLineAttributes + * @see #setInterpolation + * @see #setTextAntialias + * @see #setTransform + * @see #getAdvanced + * + * @since 3.1 + */ +public void setAdvanced(boolean advanced) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (!advanced) { + setAlpha(0xFF); + setAntialias(SWT.DEFAULT); + setBackgroundPattern(null); + setClipping((Rectangle)null); + setForegroundPattern(null); + setInterpolation(SWT.DEFAULT); + setTextAntialias(SWT.DEFAULT); + setTransform(null); + } +} + +/** + * Sets the receiver's alpha value which must be + * between 0 (transparent) and 255 (opaque). + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * @param alpha the alpha value + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * + * @see #getAdvanced + * @see #setAdvanced + * + * @since 3.1 + */ +public void setAlpha(int alpha) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + data.alpha = alpha & 0xFF; + data.state &= ~(BACKGROUND | FOREGROUND | FOREGROUND_FILL); + +} + +/** + * Sets the receiver's anti-aliasing value to the parameter, + * which must be one of <code>SWT.DEFAULT</code>, <code>SWT.OFF</code> + * or <code>SWT.ON</code>. Note that this controls anti-aliasing for all + * <em>non-text drawing</em> operations. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param antialias the anti-aliasing setting + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the parameter is not one of <code>SWT.DEFAULT</code>, + * <code>SWT.OFF</code> or <code>SWT.ON</code></li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * + * @see #getAdvanced + * @see #setAdvanced + * @see #setTextAntialias + * + * @since 3.1 + */ +public void setAntialias(int antialias) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + boolean mode = true; + switch (antialias) { + case SWT.DEFAULT: + /* Printer is off by default */ + if (!handle.isDrawingToScreen()) mode = false; + break; + case SWT.OFF: mode = false; break; + case SWT.ON: mode = true; break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + data.antialias = antialias; + handle.setShouldAntialias(mode); +} + +/** + * Sets the background color. The background color is used + * for fill operations and as the background color when text + * is drawn. + * + * @param color the new background color for the receiver + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the color is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the color has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setBackground(Color color) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (color == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + data.background = color.handle; + data.backgroundPattern = null; + if (data.bg != null) data.bg.release(); + data.bg = null; + data.state &= ~BACKGROUND; +} + +/** + * Sets the background pattern. The default value is <code>null</code>. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param pattern the new background pattern + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * + * @see Pattern + * @see #getAdvanced + * @see #setAdvanced + * + * @since 3.1 + */ +public void setBackgroundPattern(Pattern pattern) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (pattern != null && pattern.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (data.backgroundPattern == pattern) return; + data.backgroundPattern = pattern; + data.state &= ~BACKGROUND; +} + +/** + * Sets the area of the receiver which can be changed + * by drawing operations to the rectangular area specified + * by the arguments. + * + * @param x the x coordinate of the clipping rectangle + * @param y the y coordinate of the clipping rectangle + * @param width the width of the clipping rectangle + * @param height the height of the clipping rectangle + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setClipping(int x, int y, int width, int height) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + NSRect rect = new NSRect(); + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + NSBezierPath path = NSBezierPath.bezierPathWithRect(rect); + path.retain(); + setClipping(path); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the area of the receiver which can be changed + * by drawing operations to the path specified + * by the argument. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param path the clipping path. + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the path has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * + * @see Path + * @see #getAdvanced + * @see #setAdvanced + * + * @since 3.1 + */ +public void setClipping(Path path) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (path != null && path.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + setClipping(new NSBezierPath(path.handle.copy().id)); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the area of the receiver which can be changed + * by drawing operations to the rectangular area specified + * by the argument. Specifying <code>null</code> for the + * rectangle reverts the receiver's clipping area to its + * original value. + * + * @param rect the clipping rectangle or <code>null</code> + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setClipping(Rectangle rect) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (rect == null) { + setClipping((NSBezierPath)null); + } else { + setClipping(rect.x, rect.y, rect.width, rect.height); + } +} + +/** + * Sets the area of the receiver which can be changed + * by drawing operations to the region specified + * by the argument. Specifying <code>null</code> for the + * region reverts the receiver's clipping area to its + * original value. + * + * @param region the clipping region or <code>null</code> + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the region has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setClipping(Region region) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (region != null && region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + setClipping(region != null ? region.getPath() : null); + } finally { + if (pool != null) pool.release(); + } +} + +void setClipping(NSBezierPath path) { + if (data.clipPath != null) { + data.clipPath.release(); + data.clipPath = null; + } + if (path != null) { + data.clipPath = path; + if (data.transform != null) { + data.clipPath.transformUsingAffineTransform(data.transform); + } + } + data.state &= ~CLIPPING; +} + +/** + * Sets the receiver's fill rule to the parameter, which must be one of + * <code>SWT.FILL_EVEN_ODD</code> or <code>SWT.FILL_WINDING</code>. + * + * @param rule the new fill rule + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the rule is not one of <code>SWT.FILL_EVEN_ODD</code> + * or <code>SWT.FILL_WINDING</code></li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public void setFillRule(int rule) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + switch (rule) { + case SWT.FILL_WINDING: + case SWT.FILL_EVEN_ODD: break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + data.fillRule = rule; + data.path.setWindingRule(rule == SWT.FILL_WINDING ? OS.NSNonZeroWindingRule : OS.NSEvenOddWindingRule); +} + +/** + * Sets the font which will be used by the receiver + * to draw and measure text to the argument. If the + * argument is null, then a default font appropriate + * for the platform will be used instead. + * + * @param font the new font for the receiver, or null to indicate a default font + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the font has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setFont(Font font) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (font != null && font.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + data.font = font != null ? font : data.device.systemFont; + data.state &= ~FONT; +} + +/** + * Sets the foreground color. The foreground color is used + * for drawing operations including when text is drawn. + * + * @param color the new foreground color for the receiver + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the color is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the color has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setForeground(Color color) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (color == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + data.foreground = color.handle; + data.foregroundPattern = null; + if (data.fg != null) data.fg.release(); + data.fg = null; + data.state &= ~(FOREGROUND | FOREGROUND_FILL); +} + +/** + * Sets the foreground pattern. The default value is <code>null</code>. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * @param pattern the new foreground pattern + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * + * @see Pattern + * @see #getAdvanced + * @see #setAdvanced + * + * @since 3.1 + */ +public void setForegroundPattern(Pattern pattern) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (pattern != null && pattern.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (data.foregroundPattern == pattern) return; + data.foregroundPattern = pattern; + data.state &= ~(FOREGROUND | FOREGROUND_FILL); +} + +/** + * Sets the receiver's interpolation setting to the parameter, which + * must be one of <code>SWT.DEFAULT</code>, <code>SWT.NONE</code>, + * <code>SWT.LOW</code> or <code>SWT.HIGH</code>. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param interpolation the new interpolation setting + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the rule is not one of <code>SWT.DEFAULT</code>, + * <code>SWT.NONE</code>, <code>SWT.LOW</code> or <code>SWT.HIGH</code> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * + * @see #getAdvanced + * @see #setAdvanced + * + * @since 3.1 + */ +public void setInterpolation(int interpolation) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + int quality = 0; + switch (interpolation) { + case SWT.DEFAULT: quality = OS.NSImageInterpolationDefault; break; + case SWT.NONE: quality = OS.NSImageInterpolationNone; break; + case SWT.LOW: quality = OS.NSImageInterpolationLow; break; + case SWT.HIGH: quality = OS.NSImageInterpolationHigh; break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + handle.setImageInterpolation(quality); +} + +/** + * Sets the receiver's line attributes. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * @param attributes the line attributes + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the attributes is null</li> + * <li>ERROR_INVALID_ARGUMENT - if any of the line attributes is not valid</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * + * @see LineAttributes + * @see #getAdvanced + * @see #setAdvanced + * + * @since 3.3 + */ +public void setLineAttributes(LineAttributes attributes) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (attributes == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + int mask = 0; + float lineWidth = attributes.width; + if (lineWidth != data.lineWidth) { + mask |= LINE_WIDTH | DRAW_OFFSET; + } + int lineStyle = attributes.style; + if (lineStyle != data.lineStyle) { + mask |= LINE_STYLE; + switch (lineStyle) { + case SWT.LINE_SOLID: + case SWT.LINE_DASH: + case SWT.LINE_DOT: + case SWT.LINE_DASHDOT: + case SWT.LINE_DASHDOTDOT: + break; + case SWT.LINE_CUSTOM: + if (attributes.dash == null) lineStyle = SWT.LINE_SOLID; + break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + } + int join = attributes.join; + if (join != data.lineJoin) { + mask |= LINE_JOIN; + switch (join) { + case SWT.CAP_ROUND: + case SWT.CAP_FLAT: + case SWT.CAP_SQUARE: + break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + } + int cap = attributes.cap; + if (cap != data.lineCap) { + mask |= LINE_CAP; + switch (cap) { + case SWT.JOIN_MITER: + case SWT.JOIN_ROUND: + case SWT.JOIN_BEVEL: + break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + } + float[] dashes = attributes.dash; + float[] lineDashes = data.lineDashes; + if (dashes != null && dashes.length > 0) { + boolean changed = lineDashes == null || lineDashes.length != dashes.length; + for (int i = 0; i < dashes.length; i++) { + float dash = dashes[i]; + if (dash <= 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (!changed && lineDashes[i] != dash) changed = true; + } + if (changed) { + float[] newDashes = new float[dashes.length]; + System.arraycopy(dashes, 0, newDashes, 0, dashes.length); + dashes = newDashes; + mask |= LINE_STYLE; + } else { + dashes = lineDashes; + } + } else { + if (lineDashes != null && lineDashes.length > 0) { + mask |= LINE_STYLE; + } else { + dashes = lineDashes; + } + } + float dashOffset = attributes.dashOffset; + if (dashOffset != data.lineDashesOffset) { + mask |= LINE_STYLE; + } + float miterLimit = attributes.miterLimit; + if (miterLimit != data.lineMiterLimit) { + mask |= LINE_MITERLIMIT; + } + if (mask == 0) return; + data.lineWidth = lineWidth; + data.lineStyle = lineStyle; + data.lineCap = cap; + data.lineJoin = join; + data.lineDashes = dashes; + data.lineDashesOffset = dashOffset; + data.lineMiterLimit = miterLimit; + data.state &= ~mask; +} + +/** + * Sets the receiver's line cap style to the argument, which must be one + * of the constants <code>SWT.CAP_FLAT</code>, <code>SWT.CAP_ROUND</code>, + * or <code>SWT.CAP_SQUARE</code>. + * + * @param cap the cap style to be used for drawing lines + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the style is not valid</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public void setLineCap(int cap) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (data.lineCap == cap) return; + switch (cap) { + case SWT.CAP_ROUND: + case SWT.CAP_FLAT: + case SWT.CAP_SQUARE: + break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + data.lineCap = cap; + data.state &= ~LINE_CAP; +} + +/** + * Sets the receiver's line dash style to the argument. The default + * value is <code>null</code>. If the argument is not <code>null</code>, + * the receiver's line style is set to <code>SWT.LINE_CUSTOM</code>, otherwise + * it is set to <code>SWT.LINE_SOLID</code>. + * + * @param dashes the dash style to be used for drawing lines + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if any of the values in the array is less than or equal 0</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public void setLineDash(int[] dashes) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + float[] lineDashes = data.lineDashes; + if (dashes != null && dashes.length > 0) { + boolean changed = data.lineStyle != SWT.LINE_CUSTOM || lineDashes == null || lineDashes.length != dashes.length; + for (int i = 0; i < dashes.length; i++) { + int dash = dashes[i]; + if (dash <= 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (!changed && lineDashes[i] != dash) changed = true; + } + if (!changed) return; + data.lineDashes = new float[dashes.length]; + for (int i = 0; i < dashes.length; i++) { + data.lineDashes[i] = dashes[i]; + } + data.lineStyle = SWT.LINE_CUSTOM; + } else { + if (data.lineStyle == SWT.LINE_SOLID && (lineDashes == null || lineDashes.length == 0)) return; + data.lineDashes = null; + data.lineStyle = SWT.LINE_SOLID; + } + data.state &= ~LINE_STYLE; +} + +/** + * Sets the receiver's line join style to the argument, which must be one + * of the constants <code>SWT.JOIN_MITER</code>, <code>SWT.JOIN_ROUND</code>, + * or <code>SWT.JOIN_BEVEL</code>. + * + * @param join the join style to be used for drawing lines + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the style is not valid</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public void setLineJoin(int join) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (data.lineJoin == join) return; + switch (join) { + case SWT.JOIN_MITER: + case SWT.JOIN_ROUND: + case SWT.JOIN_BEVEL: + break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + data.lineJoin = join; + data.state &= ~LINE_JOIN; +} + +/** + * Sets the receiver's line style to the argument, which must be one + * of the constants <code>SWT.LINE_SOLID</code>, <code>SWT.LINE_DASH</code>, + * <code>SWT.LINE_DOT</code>, <code>SWT.LINE_DASHDOT</code> or + * <code>SWT.LINE_DASHDOTDOT</code>. + * + * @param lineStyle the style to be used for drawing lines + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the style is not valid</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setLineStyle(int lineStyle) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (data.lineStyle == lineStyle) return; + switch (lineStyle) { + case SWT.LINE_SOLID: + case SWT.LINE_DASH: + case SWT.LINE_DOT: + case SWT.LINE_DASHDOT: + case SWT.LINE_DASHDOTDOT: + break; + case SWT.LINE_CUSTOM: + if (data.lineDashes == null) lineStyle = SWT.LINE_SOLID; + break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + data.lineStyle = lineStyle; + data.state &= ~LINE_STYLE; +} + +/** + * Sets the width that will be used when drawing lines + * for all of the figure drawing operations (that is, + * <code>drawLine</code>, <code>drawRectangle</code>, + * <code>drawPolyline</code>, and so forth. + * <p> + * Note that line width of zero is used as a hint to + * indicate that the fastest possible line drawing + * algorithms should be used. This means that the + * output may be different from line width one. + * </p> + * + * @param lineWidth the width of a line + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setLineWidth(int lineWidth) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (data.lineWidth == lineWidth) return; + data.lineWidth = lineWidth; + data.state &= ~(LINE_WIDTH | DRAW_OFFSET); +} + +/** + * If the argument is <code>true</code>, puts the receiver + * in a drawing mode where the resulting color in the destination + * is the <em>exclusive or</em> of the color values in the source + * and the destination, and if the argument is <code>false</code>, + * puts the receiver in a drawing mode where the destination color + * is replaced with the source color value. + * <p> + * Note that this mode in fundamentally unsupportable on certain + * platforms, notably Carbon (Mac OS X). Clients that want their + * code to run on all platforms need to avoid this method. + * </p> + * + * @param xor if <code>true</code>, then <em>xor</em> mode is used, otherwise <em>source copy</em> mode is used + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @deprecated this functionality is not supported on some platforms + */ +public void setXORMode(boolean xor) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + data.xorMode = xor; +} + +/** + * Sets the receiver's text anti-aliasing value to the parameter, + * which must be one of <code>SWT.DEFAULT</code>, <code>SWT.OFF</code> + * or <code>SWT.ON</code>. Note that this controls anti-aliasing only + * for all <em>text drawing</em> operations. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param antialias the anti-aliasing setting + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the parameter is not one of <code>SWT.DEFAULT</code>, + * <code>SWT.OFF</code> or <code>SWT.ON</code></li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * + * @see #getAdvanced + * @see #setAdvanced + * @see #setAntialias + * + * @since 3.1 + */ +public void setTextAntialias(int antialias) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + switch (antialias) { + case SWT.DEFAULT: + case SWT.OFF: + case SWT.ON: + break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + data.textAntialias = antialias; +} + +/** + * Sets the transform that is currently being used by the receiver. If + * the argument is <code>null</code>, the current transform is set to + * the identity transform. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param transform the transform to set + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * + * @see Transform + * @see #getAdvanced + * @see #setAdvanced + * + * @since 3.1 + */ +public void setTransform(Transform transform) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (transform != null && transform.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (transform != null) { + if (data.transform != null) data.transform.release(); + if (data.inverseTransform != null) data.inverseTransform.release(); + data.transform = ((NSAffineTransform)new NSAffineTransform().alloc()).initWithTransform(transform.handle); + data.inverseTransform = ((NSAffineTransform)new NSAffineTransform().alloc()).initWithTransform(transform.handle); + NSAffineTransformStruct struct = data.inverseTransform.transformStruct(); + if ((struct.m11 * struct.m22 - struct.m12 * struct.m21) != 0) { + data.inverseTransform.invert(); + } + } else { + data.transform = data.inverseTransform = null; + } + data.state &= ~(TRANSFORM | DRAW_OFFSET); +} + +/** + * Returns the extent of the given string. No tab + * expansion or carriage return processing will be performed. + * <p> + * The <em>extent</em> of a string is the width and height of + * the rectangular area it would cover if drawn in a particular + * font (in this case, the current font in the receiver). + * </p> + * + * @param string the string to measure + * @return a point containing the extent of the string + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Point stringExtent(String string) { + return textExtent(string, 0); +} + +/** + * Returns the extent of the given string. Tab expansion and + * carriage return processing are performed. + * <p> + * The <em>extent</em> of a string is the width and height of + * the rectangular area it would cover if drawn in a particular + * font (in this case, the current font in the receiver). + * </p> + * + * @param string the string to measure + * @return a point containing the extent of the string + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Point textExtent(String string) { + return textExtent(string, SWT.DRAW_DELIMITER | SWT.DRAW_TAB); +} + +/** + * Returns the extent of the given string. Tab expansion, line + * delimiter and mnemonic processing are performed according to + * the specified flags, which can be a combination of: + * <dl> + * <dt><b>DRAW_DELIMITER</b></dt> + * <dd>draw multiple lines</dd> + * <dt><b>DRAW_TAB</b></dt> + * <dd>expand tabs</dd> + * <dt><b>DRAW_MNEMONIC</b></dt> + * <dd>underline the mnemonic character</dd> + * <dt><b>DRAW_TRANSPARENT</b></dt> + * <dd>transparent background</dd> + * </dl> + * <p> + * The <em>extent</em> of a string is the width and height of + * the rectangular area it would cover if drawn in a particular + * font (in this case, the current font in the receiver). + * </p> + * + * @param string the string to measure + * @param flags the flags specifying how to process the text + * @return a point containing the extent of the string + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Point textExtent(String string, int flags) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (string == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + NSAutoreleasePool pool = checkGC(FONT); + try { + NSAttributedString str = createString(string, flags, false); + NSSize size = str.size(); + str.release(); + return new Point((int)size.width, (int)size.height); + } finally { + uncheckGC(pool); + } +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the receiver + */ +public String toString () { + if (isDisposed()) return "GC {*DISPOSED*}"; + return "GC {" + handle + "}"; +} + +void uncheckGC(NSAutoreleasePool pool) { + if (data.flippedContext != null && data.restoreContext) { + NSGraphicsContext.static_restoreGraphicsState(); + data.restoreContext = false; + } + NSView view = data.view; + if (view != null && data.paintRect == null) { + if (data.thread != Thread.currentThread()) flush(); + } + if (pool != null) pool.release(); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/GCData.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/GCData.java new file mode 100755 index 0000000000..9e44658e63 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/GCData.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + + +import org.eclipse.swt.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class are descriptions of GCs in terms + * of unallocated platform-specific data fields. + * <p> + * <b>IMPORTANT:</b> This class is <em>not</em> part of the public + * API for SWT. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms, and should never be called from application code. + * </p> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noinstantiate This class is not intended to be instantiated by clients. + */ +public final class GCData { + public Device device; + public int style, state = -1; + public float /*double*/ [] foreground; + public float /*double*/ [] background; + public Pattern foregroundPattern; + public Pattern backgroundPattern; + public Font font; + public int alpha = 0xFF; + public float lineWidth; + public int lineStyle = SWT.LINE_SOLID; + public int lineCap = SWT.CAP_FLAT; + public int lineJoin = SWT.JOIN_MITER; + public float lineDashesOffset; + public float[] lineDashes; + public float lineMiterLimit = 10; + public boolean xorMode; + public int antialias = SWT.DEFAULT; + public int textAntialias = SWT.DEFAULT; + public int fillRule = SWT.FILL_EVEN_ODD; + public Image image; + + public NSColor fg, bg; + public float /*double*/ drawXOffset, drawYOffset; + public NSRect paintRect; + public NSBezierPath path; + public NSAffineTransform transform, inverseTransform; + public NSBezierPath clipPath, visiblePath; + public int /*long*/ visibleRgn; + public NSView view; + public NSSize size; + public Thread thread; + public NSGraphicsContext flippedContext; + public boolean restoreContext; +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java new file mode 100755 index 0000000000..646c5de74b --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java @@ -0,0 +1,1192 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + + +import org.eclipse.swt.internal.cocoa.*; +import org.eclipse.swt.*; +import java.io.*; + +/** + * Instances of this class are graphics which have been prepared + * for display on a specific device. That is, they are ready + * to paint using methods such as <code>GC.drawImage()</code> + * and display on widgets with, for example, <code>Button.setImage()</code>. + * <p> + * If loaded from a file format that supports it, an + * <code>Image</code> may have transparency, meaning that certain + * pixels are specified as being transparent when drawn. Examples + * of file formats that support transparency are GIF and PNG. + * </p><p> + * There are two primary ways to use <code>Images</code>. + * The first is to load a graphic file from disk and create an + * <code>Image</code> from it. This is done using an <code>Image</code> + * constructor, for example: + * <pre> + * Image i = new Image(device, "C:\\graphic.bmp"); + * </pre> + * A graphic file may contain a color table specifying which + * colors the image was intended to possess. In the above example, + * these colors will be mapped to the closest available color in + * SWT. It is possible to get more control over the mapping of + * colors as the image is being created, using code of the form: + * <pre> + * ImageData data = new ImageData("C:\\graphic.bmp"); + * RGB[] rgbs = data.getRGBs(); + * // At this point, rgbs contains specifications of all + * // the colors contained within this image. You may + * // allocate as many of these colors as you wish by + * // using the Color constructor Color(RGB), then + * // create the image: + * Image i = new Image(device, data); + * </pre> + * <p> + * Applications which require even greater control over the image + * loading process should use the support provided in class + * <code>ImageLoader</code>. + * </p><p> + * Application code must explicitly invoke the <code>Image.dispose()</code> + * method to release the operating system resources managed by each instance + * when those instances are no longer required. + * </p> + * + * @see Color + * @see ImageData + * @see ImageLoader + * @see <a href="http://www.eclipse.org/swt/snippets/#image">Image snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Examples: GraphicsExample, ImageAnalyzer</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + */ +public final class Image extends Resource implements Drawable { + + /** + * specifies whether the receiver is a bitmap or an icon + * (one of <code>SWT.BITMAP</code>, <code>SWT.ICON</code>) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public int type; + + /** + * the handle to the OS image resource + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public NSImage handle; + + /** + * specifies the transparent pixel + */ + int transparentPixel = -1; + + /** + * The GC the image is currently selected in. + */ + GC memGC; + + /** + * The alpha data of the image. + */ + byte[] alphaData; + + /** + * The global alpha value to be used for every pixel. + */ + int alpha = -1; + + /** + * The width of the image. + */ + int width = -1; + + /** + * The height of the image. + */ + int height = -1; + + /** + * Specifies the default scanline padding. + */ + static final int DEFAULT_SCANLINE_PAD = 4; + +Image(Device device) { + super(device); +} + +/** + * Constructs an empty instance of this class with the + * specified width and height. The result may be drawn upon + * by creating a GC and using any of its drawing operations, + * as shown in the following example: + * <pre> + * Image i = new Image(device, width, height); + * GC gc = new GC(i); + * gc.drawRectangle(0, 0, 50, 50); + * gc.dispose(); + * </pre> + * <p> + * Note: Some platforms may have a limitation on the size + * of image that can be created (size depends on width, height, + * and depth). For example, Windows 95, 98, and ME do not allow + * images larger than 16M. + * </p> + * + * @param device the device on which to create the image + * @param width the width of the new image + * @param height the height of the new image + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * <li>ERROR_INVALID_ARGUMENT - if either the width or height is negative or zero</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li> + * </ul> + */ +public Image(Device device, int width, int height) { + super(device); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + init(width, height); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Constructs a new instance of this class based on the + * provided image, with an appearance that varies depending + * on the value of the flag. The possible flag values are: + * <dl> + * <dt><b>{@link SWT#IMAGE_COPY}</b></dt> + * <dd>the result is an identical copy of srcImage</dd> + * <dt><b>{@link SWT#IMAGE_DISABLE}</b></dt> + * <dd>the result is a copy of srcImage which has a <em>disabled</em> look</dd> + * <dt><b>{@link SWT#IMAGE_GRAY}</b></dt> + * <dd>the result is a copy of srcImage which has a <em>gray scale</em> look</dd> + * </dl> + * + * @param device the device on which to create the image + * @param srcImage the image to use as the source + * @param flag the style, either <code>IMAGE_COPY</code>, <code>IMAGE_DISABLE</code> or <code>IMAGE_GRAY</code> + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * <li>ERROR_NULL_ARGUMENT - if srcImage is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the flag is not one of <code>IMAGE_COPY</code>, <code>IMAGE_DISABLE</code> or <code>IMAGE_GRAY</code></li> + * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_INVALID_IMAGE - if the image is not a bitmap or an icon, or is otherwise in an invalid state</li> + * <li>ERROR_UNSUPPORTED_DEPTH - if the depth of the image is not supported</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li> + * </ul> + */ +public Image(Device device, Image srcImage, int flag) { + super(device); + if (srcImage == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (srcImage.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + switch (flag) { + case SWT.IMAGE_COPY: + case SWT.IMAGE_DISABLE: + case SWT.IMAGE_GRAY: + break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + device = this.device; + this.type = srcImage.type; + /* Get source image size */ + NSSize size = srcImage.handle.size(); + int width = (int)size.width; + int height = (int)size.height; + NSBitmapImageRep srcRep = srcImage.getRepresentation(); + int /*long*/ bpr = srcRep.bytesPerRow(); + + /* Copy transparent pixel and alpha data when necessary */ + transparentPixel = srcImage.transparentPixel; + alpha = srcImage.alpha; + if (srcImage.alphaData != null) { + alphaData = new byte[srcImage.alphaData.length]; + System.arraycopy(srcImage.alphaData, 0, alphaData, 0, alphaData.length); + } + + /* Create the image */ + handle = (NSImage)new NSImage().alloc(); + handle = handle.initWithSize(size); + NSBitmapImageRep rep = (NSBitmapImageRep)new NSBitmapImageRep().alloc(); + rep = rep.initWithBitmapDataPlanes(0, width, height, srcRep.bitsPerSample(), srcRep.samplesPerPixel(), srcRep.samplesPerPixel() == 4, srcRep.isPlanar(), OS.NSDeviceRGBColorSpace, OS.NSAlphaFirstBitmapFormat | OS.NSAlphaNonpremultipliedBitmapFormat, srcRep.bytesPerRow(), srcRep.bitsPerPixel()); + handle.addRepresentation(rep); + rep.release(); + handle.setCacheMode(OS.NSImageCacheNever); + + int /*long*/ data = rep.bitmapData(); + OS.memmove(data, srcRep.bitmapData(), width * height * 4); + if (flag != SWT.IMAGE_COPY) { + + /* Apply transformation */ + switch (flag) { + case SWT.IMAGE_DISABLE: { + Color zeroColor = device.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW); + RGB zeroRGB = zeroColor.getRGB(); + byte zeroRed = (byte)zeroRGB.red; + byte zeroGreen = (byte)zeroRGB.green; + byte zeroBlue = (byte)zeroRGB.blue; + Color oneColor = device.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); + RGB oneRGB = oneColor.getRGB(); + byte oneRed = (byte)oneRGB.red; + byte oneGreen = (byte)oneRGB.green; + byte oneBlue = (byte)oneRGB.blue; + byte[] line = new byte[(int)/*64*/bpr]; + for (int y=0; y<height; y++) { + OS.memmove(line, data + (y * bpr), bpr); + int offset = 0; + for (int x=0; x<width; x++) { + int red = line[offset+1] & 0xFF; + int green = line[offset+2] & 0xFF; + int blue = line[offset+3] & 0xFF; + int intensity = red * red + green * green + blue * blue; + if (intensity < 98304) { + line[offset+1] = zeroRed; + line[offset+2] = zeroGreen; + line[offset+3] = zeroBlue; + } else { + line[offset+1] = oneRed; + line[offset+2] = oneGreen; + line[offset+3] = oneBlue; + } + offset += 4; + } + OS.memmove(data + (y * bpr), line, bpr); + } + break; + } + case SWT.IMAGE_GRAY: { + byte[] line = new byte[(int)/*64*/bpr]; + for (int y=0; y<height; y++) { + OS.memmove(line, data + (y * bpr), bpr); + int offset = 0; + for (int x=0; x<width; x++) { + int red = line[offset+1] & 0xFF; + int green = line[offset+2] & 0xFF; + int blue = line[offset+3] & 0xFF; + byte intensity = (byte)((red+red+green+green+green+green+green+blue) >> 3); + line[offset+1] = line[offset+2] = line[offset+3] = intensity; + offset += 4; + } + OS.memmove(data + (y * bpr), line, bpr); + } + break; + } + } + } + init(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Constructs an empty instance of this class with the + * width and height of the specified rectangle. The result + * may be drawn upon by creating a GC and using any of its + * drawing operations, as shown in the following example: + * <pre> + * Image i = new Image(device, boundsRectangle); + * GC gc = new GC(i); + * gc.drawRectangle(0, 0, 50, 50); + * gc.dispose(); + * </pre> + * <p> + * Note: Some platforms may have a limitation on the size + * of image that can be created (size depends on width, height, + * and depth). For example, Windows 95, 98, and ME do not allow + * images larger than 16M. + * </p> + * + * @param device the device on which to create the image + * @param bounds a rectangle specifying the image's width and height (must not be null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * <li>ERROR_NULL_ARGUMENT - if the bounds rectangle is null</li> + * <li>ERROR_INVALID_ARGUMENT - if either the rectangle's width or height is negative</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li> + * </ul> + */ +public Image(Device device, Rectangle bounds) { + super(device); + if (bounds == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + init(bounds.width, bounds.height); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Constructs an instance of this class from the given + * <code>ImageData</code>. + * + * @param device the device on which to create the image + * @param data the image data to create the image from (must not be null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * <li>ERROR_NULL_ARGUMENT - if the image data is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_UNSUPPORTED_DEPTH - if the depth of the ImageData is not supported</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li> + * </ul> + */ +public Image(Device device, ImageData data) { + super(device); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + init(data); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Constructs an instance of this class, whose type is + * <code>SWT.ICON</code>, from the two given <code>ImageData</code> + * objects. The two images must be the same size. Pixel transparency + * in either image will be ignored. + * <p> + * The mask image should contain white wherever the icon is to be visible, + * and black wherever the icon is to be transparent. In addition, + * the source image should contain black wherever the icon is to be + * transparent. + * </p> + * + * @param device the device on which to create the icon + * @param source the color data for the icon + * @param mask the mask data for the icon + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * <li>ERROR_NULL_ARGUMENT - if either the source or mask is null </li> + * <li>ERROR_INVALID_ARGUMENT - if source and mask are different sizes</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li> + * </ul> + */ +public Image(Device device, ImageData source, ImageData mask) { + super(device); + if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (mask == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (source.width != mask.width || source.height != mask.height) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + mask = ImageData.convertMask(mask); + ImageData image = new ImageData(source.width, source.height, source.depth, source.palette, source.scanlinePad, source.data); + image.maskPad = mask.scanlinePad; + image.maskData = mask.data; + init(image); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Constructs an instance of this class by loading its representation + * from the specified input stream. Throws an error if an error + * occurs while loading the image, or if the result is an image + * of an unsupported type. Application code is still responsible + * for closing the input stream. + * <p> + * This constructor is provided for convenience when loading a single + * image only. If the stream contains multiple images, only the first + * one will be loaded. To load multiple images, use + * <code>ImageLoader.load()</code>. + * </p><p> + * This constructor may be used to load a resource as follows: + * </p> + * <pre> + * static Image loadImage (Display display, Class clazz, String string) { + * InputStream stream = clazz.getResourceAsStream (string); + * if (stream == null) return null; + * Image image = null; + * try { + * image = new Image (display, stream); + * } catch (SWTException ex) { + * } finally { + * try { + * stream.close (); + * } catch (IOException ex) {} + * } + * return image; + * } + * </pre> + * + * @param device the device on which to create the image + * @param stream the input stream to load the image from + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * <li>ERROR_NULL_ARGUMENT - if the stream is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_IO - if an IO error occurs while reading from the stream</li> + * <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data </li> + * <li>ERROR_UNSUPPORTED_DEPTH - if the image stream describes an image with an unsupported depth</li> + * <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li> + * </ul> + */ +public Image(Device device, InputStream stream) { + super(device); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + init(new ImageData(stream)); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Constructs an instance of this class by loading its representation + * from the file with the specified name. Throws an error if an error + * occurs while loading the image, or if the result is an image + * of an unsupported type. + * <p> + * This constructor is provided for convenience when loading + * a single image only. If the specified file contains + * multiple images, only the first one will be used. + * + * @param device the device on which to create the image + * @param filename the name of the file to load the image from + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * <li>ERROR_NULL_ARGUMENT - if the file name is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_IO - if an IO error occurs while reading from the file</li> + * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data </li> + * <li>ERROR_UNSUPPORTED_DEPTH - if the image file describes an image with an unsupported depth</li> + * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li> + * </ul> + */ +public Image(Device device, String filename) { + super(device); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + initNative(filename); + if (this.handle == null) init(new ImageData(filename)); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +void createAlpha () { + if (transparentPixel == -1 && alpha == -1 && alphaData == null) return; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSBitmapImageRep imageRep = getRepresentation(); + int /*long*/ height = imageRep.pixelsHigh(); + int /*long*/ bpr = imageRep.bytesPerRow(); + int /*long*/ dataSize = height * bpr; + byte[] srcData = new byte[(int)/*64*/dataSize]; + OS.memmove(srcData, imageRep.bitmapData(), dataSize); + if (transparentPixel != -1) { + for (int i=0; i<dataSize; i+=4) { + int pixel = ((srcData[i+1] & 0xFF) << 16) | ((srcData[i+2] & 0xFF) << 8) | (srcData[i+3] & 0xFF); + srcData[i] = (byte)(pixel == transparentPixel ? 0 : 0xFF); + } + } else if (alpha != -1) { + byte a = (byte)this.alpha; + for (int i=0; i<dataSize; i+=4) { + srcData[i] = a; + } + } else { + int /*long*/ width = imageRep.pixelsWide(); + int offset = 0, alphaOffset = 0; + for (int y = 0; y<height; y++) { + for (int x = 0; x<width; x++) { + srcData[offset] = alphaData[alphaOffset]; + offset += 4; + alphaOffset += 1; + } + } + } + + // Since we just calculated alpha for the image rep, tell it that it now has an alpha component. + imageRep.setAlpha(true); + + OS.memmove(imageRep.bitmapData(), srcData, dataSize); + } finally { + if (pool != null) pool.release(); + } +} + +void destroy() { + if (memGC != null) memGC.dispose(); + handle.release(); + handle = null; + memGC = null; +} + +/** + * Compares the argument to the receiver, and returns true + * if they represent the <em>same</em> object using a class + * specific comparison. + * + * @param object the object to compare with this object + * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise + * + * @see #hashCode + */ +public boolean equals (Object object) { + if (object == this) return true; + if (!(object instanceof Image)) return false; + Image image = (Image)object; + return device == image.device && handle == image.handle && + transparentPixel == image.transparentPixel; +} + +/** + * Returns the color to which to map the transparent pixel, or null if + * the receiver has no transparent pixel. + * <p> + * There are certain uses of Images that do not support transparency + * (for example, setting an image into a button or label). In these cases, + * it may be desired to simulate transparency by using the background + * color of the widget to paint the transparent pixels of the image. + * Use this method to check which color will be used in these cases + * in place of transparency. This value may be set with setBackground(). + * <p> + * + * @return the background color of the image, or null if there is no transparency in the image + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Color getBackground() { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (transparentPixel == -1) return null; + int red = (transparentPixel >> 16) & 0xFF; + int green = (transparentPixel >> 8) & 0xFF; + int blue = (transparentPixel >> 0) & 0xFF; + return Color.cocoa_new(device, new float /*double*/ []{red / 255f, green / 255f, blue / 255f, 1}); +} + +/** + * Returns the bounds of the receiver. The rectangle will always + * have x and y values of 0, and the width and height of the + * image. + * + * @return a rectangle specifying the image's bounds + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_INVALID_IMAGE - if the image is not a bitmap or an icon</li> + * </ul> + */ +public Rectangle getBounds() { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + if (width != -1 && height != -1) { + return new Rectangle(0, 0, width, height); + } + NSSize size = handle.size(); + return new Rectangle(0, 0, width = (int)size.width, height = (int)size.height); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns an <code>ImageData</code> based on the receiver + * Modifications made to this <code>ImageData</code> will not + * affect the Image. + * + * @return an <code>ImageData</code> containing the image's data and attributes + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_INVALID_IMAGE - if the image is not a bitmap or an icon</li> + * </ul> + * + * @see ImageData + */ +public ImageData getImageData() { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSBitmapImageRep imageRep = getRepresentation(); + int /*long*/ width = imageRep.pixelsWide(); + int /*long*/ height = imageRep.pixelsHigh(); + int /*long*/ bpr = imageRep.bytesPerRow(); + int /*long*/ bpp = imageRep.bitsPerPixel(); + int /*long*/ dataSize = height * bpr; + + byte[] srcData = new byte[(int)/*64*/dataSize]; + OS.memmove(srcData, imageRep.bitmapData(), dataSize); + + PaletteData palette = new PaletteData(0xFF0000, 0xFF00, 0xFF); + ImageData data = new ImageData((int)/*64*/width, (int)/*64*/height, (int)/*64*/bpp, palette, 4, srcData); + data.bytesPerLine = (int)/*64*/bpr; + + data.transparentPixel = transparentPixel; + if (transparentPixel == -1 && type == SWT.ICON) { + /* Get the icon mask data */ + int maskPad = 2; + int /*long*/ maskBpl = (((width + 7) / 8) + (maskPad - 1)) / maskPad * maskPad; + byte[] maskData = new byte[(int)/*64*/(height * maskBpl)]; + int offset = 0, maskOffset = 0; + for (int y = 0; y<height; y++) { + for (int x = 0; x<width; x++) { + if (srcData[offset] != 0) { + maskData[maskOffset + (x >> 3)] |= (1 << (7 - (x & 0x7))); + } else { + maskData[maskOffset + (x >> 3)] &= ~(1 << (7 - (x & 0x7))); + } + offset += 4; + } + maskOffset += maskBpl; + } + data.maskData = maskData; + data.maskPad = maskPad; + } + for (int i = 0; i < srcData.length; i+= 4) { + srcData[i] = 0; + } + data.alpha = alpha; + if (alpha == -1 && alphaData != null) { + data.alphaData = new byte[alphaData.length]; + System.arraycopy(alphaData, 0, data.alphaData, 0, alphaData.length); + } + return data; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Invokes platform specific functionality to allocate a new image. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>Image</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @param device the device on which to allocate the color + * @param type the type of the image (<code>SWT.BITMAP</code> or <code>SWT.ICON</code>) + * @param handle the OS handle for the image + * @param data the OS data for the image + * + * @private + */ +public static Image cocoa_new(Device device, int type, NSImage nsImage) { + Image image = new Image(device); + image.type = type; + image.handle = nsImage; + return image; +} + +NSBitmapImageRep getRepresentation () { + NSImageRep rep = handle.bestRepresentationForDevice(null); + if (!rep.isKindOfClass(OS.class_NSBitmapImageRep)) { + SWT.error(SWT.ERROR_UNSPECIFIED); + } + return new NSBitmapImageRep(rep); +} + +/** + * Returns an integer hash code for the receiver. Any two + * objects that return <code>true</code> when passed to + * <code>equals</code> must return the same value for this + * method. + * + * @return the receiver's hash + * + * @see #equals + */ +public int hashCode () { + return handle != null ? (int)/*64*/handle.id : 0; +} + +void init(int width, int height) { + if (width <= 0 || height <= 0) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + this.type = SWT.BITMAP; + this.width = width; + this.height = height; + + handle = (NSImage)new NSImage().alloc(); + NSSize size = new NSSize(); + size.width = width; + size.height = height; + handle = handle.initWithSize(size); + NSBitmapImageRep rep = (NSBitmapImageRep)new NSBitmapImageRep().alloc(); + rep = rep.initWithBitmapDataPlanes(0, width, height, 8, 3, false, false, OS.NSDeviceRGBColorSpace, OS.NSAlphaFirstBitmapFormat | OS.NSAlphaNonpremultipliedBitmapFormat, width * 4, 32); + OS.memset(rep.bitmapData(), 0xFF, width * height * 4); + handle.addRepresentation(rep); + rep.release(); + handle.setCacheMode(OS.NSImageCacheNever); +} + +void init(ImageData image) { + if (image == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + this.width = image.width; + this.height = image.height; + PaletteData palette = image.palette; + if (!(((image.depth == 1 || image.depth == 2 || image.depth == 4 || image.depth == 8) && !palette.isDirect) || + ((image.depth == 8) || (image.depth == 16 || image.depth == 24 || image.depth == 32) && palette.isDirect))) + SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); + + /* Create the image */ + int dataSize = width * height * 4; + + /* Initialize data */ + int bpr = width * 4; + byte[] buffer = new byte[dataSize]; + if (palette.isDirect) { + ImageData.blit(ImageData.BLIT_SRC, + image.data, image.depth, image.bytesPerLine, image.getByteOrder(), 0, 0, width, height, palette.redMask, palette.greenMask, palette.blueMask, + ImageData.ALPHA_OPAQUE, null, 0, 0, 0, + buffer, 32, bpr, ImageData.MSB_FIRST, 0, 0, width, height, 0xFF0000, 0xFF00, 0xFF, + false, false); + } else { + RGB[] rgbs = palette.getRGBs(); + int length = rgbs.length; + byte[] srcReds = new byte[length]; + byte[] srcGreens = new byte[length]; + byte[] srcBlues = new byte[length]; + for (int i = 0; i < rgbs.length; i++) { + RGB rgb = rgbs[i]; + if (rgb == null) continue; + srcReds[i] = (byte)rgb.red; + srcGreens[i] = (byte)rgb.green; + srcBlues[i] = (byte)rgb.blue; + } + ImageData.blit(ImageData.BLIT_SRC, + image.data, image.depth, image.bytesPerLine, image.getByteOrder(), 0, 0, width, height, srcReds, srcGreens, srcBlues, + ImageData.ALPHA_OPAQUE, null, 0, 0, 0, + buffer, 32, bpr, ImageData.MSB_FIRST, 0, 0, width, height, 0xFF0000, 0xFF00, 0xFF, + false, false); + } + + /* Initialize transparency */ + int transparency = image.getTransparencyType(); + boolean hasAlpha = transparency != SWT.TRANSPARENCY_NONE; + if (transparency == SWT.TRANSPARENCY_MASK || image.transparentPixel != -1) { + this.type = image.transparentPixel != -1 ? SWT.BITMAP : SWT.ICON; + if (image.transparentPixel != -1) { + int transRed = 0, transGreen = 0, transBlue = 0; + if (palette.isDirect) { + RGB rgb = palette.getRGB(image.transparentPixel); + transRed = rgb.red; + transGreen = rgb.green; + transBlue = rgb.blue; + } else { + RGB[] rgbs = palette.getRGBs(); + if (image.transparentPixel < rgbs.length) { + RGB rgb = rgbs[image.transparentPixel]; + transRed = rgb.red; + transGreen = rgb.green; + transBlue = rgb.blue; + } + } + transparentPixel = transRed << 16 | transGreen << 8 | transBlue; + } + ImageData maskImage = image.getTransparencyMask(); + byte[] maskData = maskImage.data; + int maskBpl = maskImage.bytesPerLine; + int offset = 0, maskOffset = 0; + for (int y = 0; y<height; y++) { + for (int x = 0; x<width; x++) { + buffer[offset] = ((maskData[maskOffset + (x >> 3)]) & (1 << (7 - (x & 0x7)))) != 0 ? (byte)0xff : 0; + offset += 4; + } + maskOffset += maskBpl; + } + } else { + this.type = SWT.BITMAP; + if (image.alpha != -1) { + hasAlpha = true; + this.alpha = image.alpha; + byte a = (byte)this.alpha; + for (int dataIndex=0; dataIndex<buffer.length; dataIndex+=4) { + buffer[dataIndex] = a; + } + } else if (image.alphaData != null) { + hasAlpha = true; + this.alphaData = new byte[image.alphaData.length]; + System.arraycopy(image.alphaData, 0, this.alphaData, 0, alphaData.length); + int offset = 0, alphaOffset = 0; + for (int y = 0; y<height; y++) { + for (int x = 0; x<width; x++) { + buffer[offset] = alphaData[alphaOffset]; + offset += 4; + alphaOffset += 1; + } + } + } + } + + if (handle != null) handle.release(); + + handle = (NSImage)new NSImage().alloc(); + NSSize size = new NSSize(); + size.width = width; + size.height = height; + handle = handle.initWithSize(size); + NSBitmapImageRep rep = (NSBitmapImageRep)new NSBitmapImageRep().alloc(); + rep = rep.initWithBitmapDataPlanes(0, width, height, 8, hasAlpha ? 4 : 3, hasAlpha, false, OS.NSDeviceRGBColorSpace, OS.NSAlphaFirstBitmapFormat | OS.NSAlphaNonpremultipliedBitmapFormat, bpr, 32); + OS.memmove(rep.bitmapData(), buffer, dataSize); + handle.addRepresentation(rep); + rep.release(); + handle.setCacheMode(OS.NSImageCacheNever); +} + +void initNative(String filename) { + NSAutoreleasePool pool = null; + NSImage nativeImage = null; + + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + nativeImage = new NSImage(); + nativeImage.alloc(); + + // initByReferencingFile returns null if the file can't be found or is + // not an image. + nativeImage = nativeImage.initWithContentsOfFile(NSString.stringWith(filename)); + if (nativeImage == null) { + // In order to get the same kind of exception, let the file format try to load and throw + // the appropriate exception. It is possible file format supports some image formats + // that is not natively supported as well. + return; + } + + NSImageRep nativeRep = nativeImage.bestRepresentationForDevice(null); + if (!nativeRep.isKindOfClass(OS.class_NSBitmapImageRep)) { + return; + } + + width = (int)/*64*/nativeRep.pixelsWide(); + height = (int)/*64*/nativeRep.pixelsHigh(); + + boolean hasAlpha = nativeRep.hasAlpha(); + int bpr = width * 4; + handle = (NSImage)new NSImage().alloc(); + NSSize size = new NSSize(); + size.width = width; + size.height = height; + handle = handle.initWithSize(size); + NSBitmapImageRep rep = (NSBitmapImageRep)new NSBitmapImageRep().alloc(); + rep = rep.initWithBitmapDataPlanes(0, width, height, 8, hasAlpha ? 4 : 3, hasAlpha, false, OS.NSDeviceRGBColorSpace, OS.NSAlphaFirstBitmapFormat | OS.NSAlphaNonpremultipliedBitmapFormat, bpr, 32); + handle.addRepresentation(rep); + rep.release(); + handle.setCacheMode(OS.NSImageCacheNever); + NSRect rect = new NSRect(); + rect.width = width; + rect.height = height; + + /* Compute the pixels */ + int /*long*/ colorspace = OS.CGColorSpaceCreateDeviceRGB(); + int /*long*/ ctx = OS.CGBitmapContextCreate(rep.bitmapData(), width, height, 8, bpr, colorspace, OS.kCGImageAlphaNoneSkipFirst); + OS.CGColorSpaceRelease(colorspace); + NSGraphicsContext.static_saveGraphicsState(); + NSGraphicsContext.setCurrentContext(NSGraphicsContext.graphicsContextWithGraphicsPort(ctx, false)); + if (hasAlpha) OS.objc_msgSend(nativeRep.id, OS.sel_setAlpha_, 0); + nativeRep.drawInRect(rect); + if (hasAlpha) OS.objc_msgSend(nativeRep.id, OS.sel_setAlpha_, 1); + NSGraphicsContext.static_restoreGraphicsState(); + OS.CGContextRelease(ctx); + + if (hasAlpha) { + /* Compute the alpha values */ + int /*long*/ bitmapBytesPerRow = width; + int /*long*/ bitmapByteCount = bitmapBytesPerRow * height; + int /*long*/ alphaBitmapData = OS.malloc(bitmapByteCount); + int /*long*/ alphaBitmapCtx = OS.CGBitmapContextCreate(alphaBitmapData, width, height, 8, bitmapBytesPerRow, 0, OS.kCGImageAlphaOnly); + NSGraphicsContext.static_saveGraphicsState(); + NSGraphicsContext.setCurrentContext(NSGraphicsContext.graphicsContextWithGraphicsPort(alphaBitmapCtx, false)); + nativeRep.drawInRect(rect); + NSGraphicsContext.static_restoreGraphicsState(); + byte[] alphaData = new byte[(int)/*64*/bitmapByteCount]; + OS.memmove(alphaData, alphaBitmapData, bitmapByteCount); + OS.free(alphaBitmapData); + OS.CGContextRelease(alphaBitmapCtx); + + /* Merge the alpha values with the pixels */ + byte[] srcData = new byte[height * bpr]; + OS.memmove(srcData, rep.bitmapData(), srcData.length); + for (int a = 0, p = 0; a < alphaData.length; a++, p += 4) { + srcData[p] = alphaData[a]; + } + OS.memmove(rep.bitmapData(), srcData, srcData.length); + + // If the alpha has only 0 or 255 (-1) for alpha values, compute the transparent pixel color instead + // of a continuous alpha range. + int transparentOffset = -1, i = 0; + for (i = 0; i < alphaData.length; i++) { + int alpha = alphaData[i]; + if (transparentOffset == -1 && alpha == 0) transparentOffset = i; + if (!(alpha == 0 || alpha == -1)) break; + } + this.alpha = -1; + if (i == alphaData.length && transparentOffset != -1) { + NSColor color = rep.colorAtX(transparentOffset % width, transparentOffset / width); + int red = (int) (color.redComponent() * 255); + int green = (int) (color.greenComponent() * 255); + int blue = (int) (color.blueComponent() * 255); + this.transparentPixel = (red << 16) + (green << 8) + blue; + } else { + this.alphaData = alphaData; + } + } + + // For compatibility, images created from .ico files are treated as SWT.ICON format, even though + // they are no different than other bitmaps in Cocoa. + if (filename.toLowerCase().endsWith(".ico")) { + this.type = SWT.ICON; + } else { + this.type = SWT.BITMAP; + } + } finally { + if (nativeImage != null) nativeImage.release(); + if (pool != null) pool.release(); + } + +} + +/** + * Invokes platform specific functionality to allocate a new GC handle. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>Image</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @param data the platform specific GC data + * @return the platform specific GC handle + */ +public int /*long*/ internal_new_GC (GCData data) { + if (handle == null) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (type != SWT.BITMAP || memGC != null) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSBitmapImageRep imageRep = getRepresentation(); + + // Can't perform transforms on image reps with alpha. + imageRep.setAlpha(false); + + NSGraphicsContext context = NSGraphicsContext.graphicsContextWithBitmapImageRep(imageRep); + NSGraphicsContext flippedContext = NSGraphicsContext.graphicsContextWithGraphicsPort(context.graphicsPort(), true); + context = flippedContext; + context.retain(); + if (data != null) data.flippedContext = flippedContext; + NSGraphicsContext.static_saveGraphicsState(); + NSGraphicsContext.setCurrentContext(context); + NSAffineTransform transform = NSAffineTransform.transform(); + NSSize size = handle.size(); + transform.translateXBy(0, size.height); + transform.scaleXBy(1, -1); + transform.set(); + NSGraphicsContext.static_restoreGraphicsState(); + if (data != null) { + int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT; + if ((data.style & mask) == 0) { + data.style |= SWT.LEFT_TO_RIGHT; + } + data.device = device; + data.background = device.COLOR_WHITE.handle; + data.foreground = device.COLOR_BLACK.handle; + data.font = device.systemFont; + data.image = this; + } + return context.id; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Invokes platform specific functionality to dispose a GC handle. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>Image</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @param hDC the platform specific GC handle + * @param data the platform specific GC data + */ +public void internal_dispose_GC (int /*long*/ context, GCData data) { + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + if (context != 0) { + NSGraphicsContext contextObj = new NSGraphicsContext(context); + contextObj.release(); + } +// handle.setCacheMode(OS.NSImageCacheDefault); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns <code>true</code> if the image has been disposed, + * and <code>false</code> otherwise. + * <p> + * This method gets the dispose state for the image. + * When an image has been disposed, it is an error to + * invoke any other method using the image. + * + * @return <code>true</code> when the image is disposed and <code>false</code> otherwise + */ +public boolean isDisposed() { + return handle == null; +} + +/** + * Sets the color to which to map the transparent pixel. + * <p> + * There are certain uses of <code>Images</code> that do not support + * transparency (for example, setting an image into a button or label). + * In these cases, it may be desired to simulate transparency by using + * the background color of the widget to paint the transparent pixels + * of the image. This method specifies the color that will be used in + * these cases. For example: + * <pre> + * Button b = new Button(); + * image.setBackground(b.getBackground()); + * b.setImage(image); + * </pre> + * </p><p> + * The image may be modified by this operation (in effect, the + * transparent regions may be filled with the supplied color). Hence + * this operation is not reversible and it is not legal to call + * this function twice or with a null argument. + * </p><p> + * This method has no effect if the receiver does not have a transparent + * pixel value. + * </p> + * + * @param color the color to use when a transparent pixel is specified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the color is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the color has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setBackground(Color color) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (color == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (transparentPixel == -1) return; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + byte red = (byte)((transparentPixel >> 16) & 0xFF); + byte green = (byte)((transparentPixel >> 8) & 0xFF); + byte blue = (byte)((transparentPixel >> 0) & 0xFF); + byte newRed = (byte)((int)(color.handle[0] * 255) & 0xFF); + byte newGreen = (byte)((int)(color.handle[1] * 255) & 0xFF); + byte newBlue = (byte)((int)(color.handle[2] * 255) & 0xFF); + NSBitmapImageRep imageRep = getRepresentation(); + int /*long*/ bpr = imageRep.bytesPerRow(); + int /*long*/ data = imageRep.bitmapData(); + byte[] line = new byte[(int)bpr]; + for (int i = 0, offset = 0; i < height; i++, offset += bpr) { + OS.memmove(line, data + offset, bpr); + for (int j = 0; j < line.length; j += 4) { + if (line[j+ 1] == red && line[j + 2] == green && line[j + 3] == blue) { + line[j + 1] = newRed; + line[j + 2] = newGreen; + line[j + 3] = newBlue; + } + } + OS.memmove(data + offset, line, bpr); + } + transparentPixel = (newRed & 0xFF) << 16 | (newGreen & 0xFF) << 8 | (newBlue & 0xFF); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the receiver + */ +public String toString () { + if (isDisposed()) return "Image {*DISPOSED*}"; + return "Image {" + handle + "}"; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Path.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Path.java new file mode 100755 index 0000000000..7ab8a8b4ea --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Path.java @@ -0,0 +1,759 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + +import org.eclipse.swt.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class represent paths through the two-dimensional + * coordinate system. Paths do not have to be continuous, and can be + * described using lines, rectangles, arcs, cubic or quadratic bezier curves, + * glyphs, or other paths. + * <p> + * Application code must explicitly invoke the <code>Path.dispose()</code> + * method to release the operating system resources managed by each instance + * when those instances are no longer required. + * </p> + * <p> + * This class requires the operating system's advanced graphics subsystem + * which may not be available on some platforms. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#path">Path, Pattern snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: GraphicsExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.1 + */ +public class Path extends Resource { + + /** + * the OS resource for the Path + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public NSBezierPath handle; + +/** + * Constructs a new empty Path. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param device the device on which to allocate the path + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the device is null and there is no current device</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle for the path could not be obtained</li> + * </ul> + * + * @see #dispose() + */ +public Path (Device device) { + super(device); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + handle = NSBezierPath.bezierPath(); + if (handle == null) SWT.error(SWT.ERROR_NO_HANDLES); + handle.retain(); + handle.moveToPoint(new NSPoint()); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Constructs a new Path that is a copy of <code>path</code>. If + * <code>flatness</code> is less than or equal to zero, an unflatten + * copy of the path is created. Otherwise, it specifies the maximum + * error between the path and its flatten copy. Smaller numbers give + * better approximation. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param device the device on which to allocate the path + * @param path the path to make a copy + * @param flatness the flatness value + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the device is null and there is no current device</li> + * <li>ERROR_NULL_ARGUMENT - if the path is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the path has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle for the path could not be obtained</li> + * </ul> + * + * @see #dispose() + * @since 3.4 + */ +public Path (Device device, Path path, float flatness) { + super(device); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + if (path == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (path.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + flatness = Math.max(0, flatness); + if (flatness == 0) { + handle = new NSBezierPath(path.handle.copy().id); + } else { + float /*double*/ defaultFlatness = NSBezierPath.defaultFlatness(); + NSBezierPath.setDefaultFlatness(flatness); + handle = path.handle.bezierPathByFlatteningPath(); + handle.retain(); + NSBezierPath.setDefaultFlatness(defaultFlatness); + } + if (handle == null) SWT.error(SWT.ERROR_NO_HANDLES); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Constructs a new Path with the specifed PathData. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param device the device on which to allocate the path + * @param data the data for the path + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the device is null and there is no current device</li> + * <li>ERROR_NULL_ARGUMENT - if the data is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle for the path could not be obtained</li> + * </ul> + * + * @see #dispose() + * @since 3.4 + */ +public Path (Device device, PathData data) { + this(device); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + if (data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + init(data); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Adds to the receiver a circular or elliptical arc that lies within + * the specified rectangular area. + * <p> + * The resulting arc begins at <code>startAngle</code> and extends + * for <code>arcAngle</code> degrees. + * Angles are interpreted such that 0 degrees is at the 3 o'clock + * position. A positive value indicates a counter-clockwise rotation + * while a negative value indicates a clockwise rotation. + * </p><p> + * The center of the arc is the center of the rectangle whose origin + * is (<code>x</code>, <code>y</code>) and whose size is specified by the + * <code>width</code> and <code>height</code> arguments. + * </p><p> + * The resulting arc covers an area <code>width + 1</code> pixels wide + * by <code>height + 1</code> pixels tall. + * </p> + * + * @param x the x coordinate of the upper-left corner of the arc + * @param y the y coordinate of the upper-left corner of the arc + * @param width the width of the arc + * @param height the height of the arc + * @param startAngle the beginning angle + * @param arcAngle the angular extent of the arc, relative to the start angle + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void addArc(float x, float y, float width, float height, float startAngle, float arcAngle) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSAffineTransform transform = NSAffineTransform.transform(); + transform.translateXBy(x + width / 2f, y + height / 2f); + transform.scaleXBy(width / 2f, height / 2f); + NSBezierPath path = NSBezierPath.bezierPath(); + NSPoint center = new NSPoint(); + float sAngle = -startAngle; + float eAngle = -(startAngle + arcAngle); + path.appendBezierPathWithArcWithCenter(center, 1, sAngle, eAngle, arcAngle>0); + path.transformUsingAffineTransform(transform); + handle.appendBezierPath(path); + if (Math.abs(arcAngle) >= 360) handle.closePath(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Adds to the receiver the path described by the parameter. + * + * @param path the path to add to the receiver + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parameter is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void addPath(Path path) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (path == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (path.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + handle.appendBezierPath(path.handle); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Adds to the receiver the rectangle specified by x, y, width and height. + * + * @param x the x coordinate of the rectangle to add + * @param y the y coordinate of the rectangle to add + * @param width the width of the rectangle to add + * @param height the height of the rectangle to add + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void addRectangle(float x, float y, float width, float height) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSRect rect = new NSRect(); + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + handle.appendBezierPathWithRect(rect); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Adds to the receiver the pattern of glyphs generated by drawing + * the given string using the given font starting at the point (x, y). + * + * @param string the text to use + * @param x the x coordinate of the starting point + * @param y the y coordinate of the starting point + * @param font the font to use + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the font is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the font has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void addString(String string, float x, float y, Font font) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (font == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (font.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSString str = NSString.stringWith(string); + NSTextStorage textStorage = (NSTextStorage)new NSTextStorage().alloc().init(); + NSLayoutManager layoutManager = (NSLayoutManager)new NSLayoutManager().alloc().init(); + NSTextContainer textContainer = (NSTextContainer)new NSTextContainer().alloc(); + NSSize size = new NSSize(); + size.width = Float.MAX_VALUE; + size.height = Float.MAX_VALUE; + textContainer.initWithContainerSize(size); + textStorage.addLayoutManager(layoutManager); + layoutManager.addTextContainer(textContainer); + NSRange range = new NSRange(); + range.length = str.length(); + /* + * Feature in Cocoa. Adding attributes directly to a NSTextStorage causes + * output to the console and eventually a segmentation fault when printing + * on a thread other than the main thread. The fix is to add attributes to + * a separate NSMutableAttributedString and add it to text storage when done. + */ + NSMutableAttributedString attrStr = (NSMutableAttributedString)new NSMutableAttributedString().alloc(); + attrStr.id = attrStr.initWithString(str).id; + attrStr.beginEditing(); + attrStr.addAttribute(OS.NSFontAttributeName, font.handle, range); + font.addTraits(attrStr, range); + attrStr.endEditing(); + textStorage.setAttributedString(attrStr); + attrStr.release(); + range = layoutManager.glyphRangeForTextContainer(textContainer); + if (range.length != 0) { + int /*long*/ glyphs = OS.malloc(4 * range.length * 2); + layoutManager.getGlyphs(glyphs, range); + NSBezierPath path = NSBezierPath.bezierPath(); + NSPoint point = new NSPoint(); + path.moveToPoint(point); + path.appendBezierPathWithGlyphs(glyphs, range.length, font.handle); + NSAffineTransform transform = NSAffineTransform.transform(); + transform.scaleXBy(1, -1); + float /*double*/ baseline = layoutManager.defaultBaselineOffsetForFont(font.handle); + transform.translateXBy(x, -(y + baseline)); + path.transformUsingAffineTransform(transform); + OS.free(glyphs); + handle.appendBezierPath(path); + } + textContainer.release(); + layoutManager.release(); + textStorage.release(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Closes the current sub path by adding to the receiver a line + * from the current point of the path back to the starting point + * of the sub path. + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void close() { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + handle.closePath(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns <code>true</code> if the specified point is contained by + * the receiver and false otherwise. + * <p> + * If outline is <code>true</code>, the point (x, y) checked for containment in + * the receiver's outline. If outline is <code>false</code>, the point is + * checked to see if it is contained within the bounds of the (closed) area + * covered by the receiver. + * + * @param x the x coordinate of the point to test for containment + * @param y the y coordinate of the point to test for containment + * @param gc the GC to use when testing for containment + * @param outline controls whether to check the outline or contained area of the path + * @return <code>true</code> if the path contains the point and <code>false</code> otherwise + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the gc is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the gc has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public boolean contains(float x, float y, GC gc, boolean outline) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (gc == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (gc.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + //TODO - see windows + if (outline) { + int /*long*/ pixel = OS.malloc(4); + if (pixel == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int[] buffer = new int[]{0xFFFFFFFF}; + OS.memmove(pixel, buffer, 4); + int /*long*/ colorspace = OS.CGColorSpaceCreateDeviceRGB(); + int /*long*/ context = OS.CGBitmapContextCreate(pixel, 1, 1, 8, 4, colorspace, OS.kCGImageAlphaNoneSkipFirst); + OS.CGColorSpaceRelease(colorspace); + if (context == 0) { + OS.free(pixel); + SWT.error(SWT.ERROR_NO_HANDLES); + } + GCData data = gc.data; + int capStyle = 0; + switch (data.lineCap) { + case SWT.CAP_ROUND: capStyle = OS.kCGLineCapRound; break; + case SWT.CAP_FLAT: capStyle = OS.kCGLineCapButt; break; + case SWT.CAP_SQUARE: capStyle = OS.kCGLineCapSquare; break; + } + OS.CGContextSetLineCap(context, capStyle); + int joinStyle = 0; + switch (data.lineJoin) { + case SWT.JOIN_MITER: joinStyle = OS.kCGLineJoinMiter; break; + case SWT.JOIN_ROUND: joinStyle = OS.kCGLineJoinRound; break; + case SWT.JOIN_BEVEL: joinStyle = OS.kCGLineJoinBevel; break; + } + OS.CGContextSetLineJoin(context, joinStyle); + OS.CGContextSetLineWidth(context, data.lineWidth); + OS.CGContextTranslateCTM(context, -x + 0.5f, -y + 0.5f); + int /*long*/ path = GC.createCGPathRef(handle); + OS.CGContextAddPath(context, path); + OS.CGPathRelease(path); + OS.CGContextStrokePath(context); + OS.CGContextRelease(context); + OS.memmove(buffer, pixel, 4); + OS.free(pixel); + return buffer[0] != 0xFFFFFFFF; + } else { + NSPoint point = new NSPoint(); + point.x = x; + point.y = y; + return handle.containsPoint(point); + } + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Adds to the receiver a cubic bezier curve based on the parameters. + * + * @param cx1 the x coordinate of the first control point of the spline + * @param cy1 the y coordinate of the first control of the spline + * @param cx2 the x coordinate of the second control of the spline + * @param cy2 the y coordinate of the second control of the spline + * @param x the x coordinate of the end point of the spline + * @param y the y coordinate of the end point of the spline + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSPoint pt = new NSPoint(); + pt.x = x; + pt.y = y; + NSPoint ct1 = new NSPoint(); + ct1.x = cx1; + ct1.y = cy1; + NSPoint ct2 = new NSPoint(); + ct2.x = cx2; + ct2.y = cy2; + handle.curveToPoint(pt, ct1, ct2); + } finally { + if (pool != null) pool.release(); + } +} + +void destroy() { + handle.release(); + handle = null; +} + +/** + * Replaces the first four elements in the parameter with values that + * describe the smallest rectangle that will completely contain the + * receiver (i.e. the bounding box). + * + * @param bounds the array to hold the result + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parameter is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the parameter is too small to hold the bounding box</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void getBounds(float[] bounds) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (bounds == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (bounds.length < 4) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSRect rect = handle.controlPointBounds(); + bounds[0] = (float)/*64*/rect.x; + bounds[1] = (float)/*64*/rect.y; + bounds[2] = (float)/*64*/rect.width; + bounds[3] = (float)/*64*/rect.height; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Replaces the first two elements in the parameter with values that + * describe the current point of the path. + * + * @param point the array to hold the result + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parameter is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the parameter is too small to hold the end point</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void getCurrentPoint(float[] point) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (point == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (point.length < 2) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSPoint pt = handle.currentPoint(); + point[0] = (float)/*64*/pt.x; + point[1] = (float)/*64*/pt.y; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns a device independent representation of the receiver. + * + * @return the PathData for the receiver + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see PathData + */ +public PathData getPathData() { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + int count = (int)/*64*/handle.elementCount(); + int pointCount = 0, typeCount = 0; + byte[] types = new byte[count]; + float[] pointArray = new float[count * 6]; + int /*long*/ points = OS.malloc(3 * NSPoint.sizeof); + if (points == 0) SWT.error(SWT.ERROR_NO_HANDLES); + NSPoint pt = new NSPoint(); + for (int i = 0; i < count; i++) { + int element = (int)/*64*/handle.elementAtIndex(i, points); + switch (element) { + case OS.NSMoveToBezierPathElement: + types[typeCount++] = SWT.PATH_MOVE_TO; + OS.memmove(pt, points, NSPoint.sizeof); + pointArray[pointCount++] = (int)pt.x; + pointArray[pointCount++] = (int)pt.y; + break; + case OS.NSLineToBezierPathElement: + types[typeCount++] = SWT.PATH_LINE_TO; + OS.memmove(pt, points, NSPoint.sizeof); + pointArray[pointCount++] = (int)pt.x; + pointArray[pointCount++] = (int)pt.y; + break; + case OS.NSCurveToBezierPathElement: + types[typeCount++] = SWT.PATH_CUBIC_TO; + OS.memmove(pt, points, NSPoint.sizeof); + pointArray[pointCount++] = (int)pt.x; + pointArray[pointCount++] = (int)pt.y; + OS.memmove(pt, points + NSPoint.sizeof, NSPoint.sizeof); + pointArray[pointCount++] = (int)pt.x; + pointArray[pointCount++] = (int)pt.y; + OS.memmove(pt, points + NSPoint.sizeof + NSPoint.sizeof, NSPoint.sizeof); + pointArray[pointCount++] = (int)pt.x; + pointArray[pointCount++] = (int)pt.y; + break; + case OS.NSClosePathBezierPathElement: + types[typeCount++] = SWT.PATH_CLOSE; + break; + } + } + OS.free(points); + if (pointCount != pointArray.length) { + float[] temp = new float[pointCount]; + System.arraycopy(pointArray, 0, temp, 0, pointCount); + pointArray = temp; + } + PathData data = new PathData(); + data.types = types; + data.points = pointArray; + return data; + } finally { + if (pool != null) pool.release(); + } +} + +void init(PathData data) { + byte[] types = data.types; + float[] points = data.points; + for (int i = 0, j = 0; i < types.length; i++) { + switch (types[i]) { + case SWT.PATH_MOVE_TO: + moveTo(points[j++], points[j++]); + break; + case SWT.PATH_LINE_TO: + lineTo(points[j++], points[j++]); + break; + case SWT.PATH_CUBIC_TO: + cubicTo(points[j++], points[j++], points[j++], points[j++], points[j++], points[j++]); + break; + case SWT.PATH_QUAD_TO: + quadTo(points[j++], points[j++], points[j++], points[j++]); + break; + case SWT.PATH_CLOSE: + close(); + break; + default: + dispose(); + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + } +} + +/** + * Returns <code>true</code> if the Path has been disposed, + * and <code>false</code> otherwise. + * <p> + * This method gets the dispose state for the Path. + * When a Path has been disposed, it is an error to + * invoke any other method using the Path. + * + * @return <code>true</code> when the Path is disposed, and <code>false</code> otherwise + */ +public boolean isDisposed() { + return handle == null; +} + +/** + * Adds to the receiver a line from the current point to + * the point specified by (x, y). + * + * @param x the x coordinate of the end of the line to add + * @param y the y coordinate of the end of the line to add + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void lineTo(float x, float y) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSPoint pt = new NSPoint(); + pt.x = x; + pt.y = y; + handle.lineToPoint(pt); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the current point of the receiver to the point + * specified by (x, y). Note that this starts a new + * sub path. + * + * @param x the x coordinate of the new end point + * @param y the y coordinate of the new end point + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void moveTo(float x, float y) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSPoint pt = new NSPoint(); + pt.x = x; + pt.y = y; + handle.moveToPoint(pt); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Adds to the receiver a quadratic curve based on the parameters. + * + * @param cx the x coordinate of the control point of the spline + * @param cy the y coordinate of the control point of the spline + * @param x the x coordinate of the end point of the spline + * @param y the y coordinate of the end point of the spline + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void quadTo(float cx, float cy, float x, float y) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSPoint pt = new NSPoint(); + pt.x = x; + pt.y = y; + NSPoint ct = new NSPoint(); + ct.x = cx; + ct.y = cy; + handle.curveToPoint(pt, ct, ct); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the receiver + */ +public String toString () { + if (isDisposed()) return "Path {*DISPOSED*}"; + return "Path {" + handle + "}"; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Pattern.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Pattern.java new file mode 100755 index 0000000000..1670e9ab87 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Pattern.java @@ -0,0 +1,217 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + +import org.eclipse.swt.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class represent patterns to use while drawing. Patterns + * can be specified either as bitmaps or gradients. + * <p> + * Application code must explicitly invoke the <code>Pattern.dispose()</code> + * method to release the operating system resources managed by each instance + * when those instances are no longer required. + * </p> + * <p> + * This class requires the operating system's advanced graphics subsystem + * which may not be available on some platforms. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#path">Path, Pattern snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: GraphicsExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.1 + */ +public class Pattern extends Resource { + NSColor color; + NSGradient gradient; + NSPoint pt1, pt2; + Image image; + float /*double*/ [] color1, color2; + int alpha1, alpha2; + +/** + * Constructs a new Pattern given an image. Drawing with the resulting + * pattern will cause the image to be tiled over the resulting area. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param device the device on which to allocate the pattern + * @param image the image that the pattern will draw + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the device is null and there is no current device, or the image is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle for the pattern could not be obtained</li> + * </ul> + * + * @see #dispose() + */ +public Pattern(Device device, Image image) { + super(device); + if (image == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (image.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + this.image = image; + color = NSColor.colorWithPatternImage(image.handle); + color.retain(); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Constructs a new Pattern that represents a linear, two color + * gradient. Drawing with the pattern will cause the resulting area to be + * tiled with the gradient specified by the arguments. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param device the device on which to allocate the pattern + * @param x1 the x coordinate of the starting corner of the gradient + * @param y1 the y coordinate of the starting corner of the gradient + * @param x2 the x coordinate of the ending corner of the gradient + * @param y2 the y coordinate of the ending corner of the gradient + * @param color1 the starting color of the gradient + * @param color2 the ending color of the gradient + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the device is null and there is no current device, + * or if either color1 or color2 is null</li> + * <li>ERROR_INVALID_ARGUMENT - if either color1 or color2 has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle for the pattern could not be obtained</li> + * </ul> + * + * @see #dispose() + */ +public Pattern(Device device, float x1, float y1, float x2, float y2, Color color1, Color color2) { + this(device, x1, y1, x2, y2, color1, 0xFF, color2, 0xFF); +} +/** + * Constructs a new Pattern that represents a linear, two color + * gradient. Drawing with the pattern will cause the resulting area to be + * tiled with the gradient specified by the arguments. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param device the device on which to allocate the pattern + * @param x1 the x coordinate of the starting corner of the gradient + * @param y1 the y coordinate of the starting corner of the gradient + * @param x2 the x coordinate of the ending corner of the gradient + * @param y2 the y coordinate of the ending corner of the gradient + * @param color1 the starting color of the gradient + * @param alpha1 the starting alpha value of the gradient + * @param color2 the ending color of the gradient + * @param alpha2 the ending alpha value of the gradient + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the device is null and there is no current device, + * or if either color1 or color2 is null</li> + * <li>ERROR_INVALID_ARGUMENT - if either color1 or color2 has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle for the pattern could not be obtained</li> + * </ul> + * + * @see #dispose() + * + * @since 3.2 + */ +public Pattern(Device device, float x1, float y1, float x2, float y2, Color color1, int alpha1, Color color2, int alpha2) { + super(device); + if (color1 == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (color1.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (color2 == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (color2.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + pt1 = new NSPoint(); + pt2 = new NSPoint(); + pt1.x = x1; + pt1.y = y1; + pt2.x = x2; + pt2.y = y2; + this.color1 = color1.handle; + this.color2 = color2.handle; + this.alpha1 = alpha1; + this.alpha2 = alpha2; + NSColor start = NSColor.colorWithDeviceRed(color1.handle[0], color1.handle[1], color1.handle[2], alpha1 / 255f); + NSColor end = NSColor.colorWithDeviceRed(color2.handle[0], color2.handle[1], color2.handle[2], alpha2 / 255f); + gradient = ((NSGradient)new NSGradient().alloc()).initWithStartingColor(start, end); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +void destroy() { + if (color != null) color.release(); + color = null; + if (gradient != null) gradient.release(); + gradient = null; + image = null; + color1 = color2 = null; +} + +/** + * Returns <code>true</code> if the Pattern has been disposed, + * and <code>false</code> otherwise. + * <p> + * This method gets the dispose state for the Pattern. + * When a Pattern has been disposed, it is an error to + * invoke any other method using the Pattern. + * + * @return <code>true</code> when the Pattern is disposed, and <code>false</code> otherwise + */ +public boolean isDisposed() { + return device == null; +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the receiver + */ +public String toString() { + if (isDisposed()) return "Pattern {*DISPOSED*}"; + return "Pattern {" + (color != null ? color.id : gradient.id) + "}"; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Region.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Region.java new file mode 100755 index 0000000000..c8de98bb3a --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Region.java @@ -0,0 +1,839 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + + +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.cocoa.*; +import org.eclipse.swt.*; + +/** + * Instances of this class represent areas of an x-y coordinate + * system that are aggregates of the areas covered by a number + * of polygons. + * <p> + * Application code must explicitly invoke the <code>Region.dispose()</code> + * method to release the operating system resources managed by each instance + * when those instances are no longer required. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: GraphicsExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + */ +public final class Region extends Resource { + /** + * the OS resource for the region + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public int /*long*/ handle; + +/** + * Constructs a new empty region. + * + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle could not be obtained for region creation</li> + * </ul> + */ +public Region() { + this(null); +} + +/** + * Constructs a new empty region. + * <p> + * You must dispose the region when it is no longer required. + * </p> + * + * @param device the device on which to allocate the region + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle could not be obtained for region creation</li> + * </ul> + * + * @see #dispose + * + * @since 3.0 + */ +public Region(Device device) { + super(device); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + handle = OS.NewRgn(); + if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +Region(Device device, int /*long*/ handle) { + super(device); + this.handle = handle; +} + +public static Region cocoa_new(Device device, int /*long*/ handle) { + return new Region(device, handle); +} + +static int /*long*/ polyToRgn(int[] poly, int length) { + short[] r = new short[4]; + int /*long*/ polyRgn = OS.NewRgn(), rectRgn = OS.NewRgn(); + int minY = poly[1], maxY = poly[1]; + for (int y = 3; y < length; y += 2) { + if (poly[y] < minY) minY = poly[y]; + if (poly[y] > maxY) maxY = poly[y]; + } + int[] inter = new int[length + 1]; + for (int y = minY; y <= maxY; y++) { + int count = 0; + int x1 = poly[0], y1 = poly[1]; + for (int p = 2; p < length; p += 2) { + int x2 = poly[p], y2 = poly[p + 1]; + if (y1 != y2 && ((y1 <= y && y < y2) || (y2 <= y && y < y1))) { + inter[count++] = (int)((((y - y1) / (float)(y2 - y1)) * (x2 - x1)) + x1 + 0.5f); + } + x1 = x2; + y1 = y2; + } + int x2 = poly[0], y2 = poly[1]; + if (y1 != y2 && ((y1 <= y && y < y2) || (y2 <= y && y < y1))) { + inter[count++] = (int)((((y - y1) / (float)(y2 - y1)) * (x2 - x1)) + x1 + 0.5f); + } + for (int gap=count/2; gap>0; gap/=2) { + for (int i=gap; i<count; i++) { + for (int j=i-gap; j>=0; j-=gap) { + if ((inter[j] - inter[j + gap]) <= 0) + break; + int temp = inter[j]; + inter[j] = inter[j + gap]; + inter[j + gap] = temp; + } + } + } + for (int i = 0; i < count; i += 2) { + OS.SetRect(r, (short)inter[i], (short)y, (short)(inter[i + 1]),(short)(y + 1)); + OS.RectRgn(rectRgn, r); + OS.UnionRgn(polyRgn, rectRgn, polyRgn); + } + } + OS.DisposeRgn(rectRgn); + return polyRgn; +} + +static int /*long*/ polyRgn(int[] pointArray, int count) { + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + int /*long*/ polyRgn; + if (C.PTR_SIZEOF == 4) { + polyRgn = OS.NewRgn(); + OS.OpenRgn(); + OS.MoveTo((short)pointArray[0], (short)pointArray[1]); + for (int i = 1; i < count / 2; i++) { + OS.LineTo((short)pointArray[2 * i], (short)pointArray[2 * i + 1]); + } + OS.LineTo((short)pointArray[0], (short)pointArray[1]); + OS.CloseRgn(polyRgn); + } else { + polyRgn = polyToRgn(pointArray, count); + } + return polyRgn; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Adds the given polygon to the collection of polygons + * the receiver maintains to describe its area. + * + * @param pointArray points that describe the polygon to merge with the receiver + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the argument is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.0 +* + */ +public void add (int[] pointArray) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (pointArray == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + add(pointArray, pointArray.length); + } finally { + if (pool != null) pool.release(); + } +} + +void add(int[] pointArray, int count) { + if (count <= 2) return; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + int /*long*/ polyRgn = polyRgn(pointArray, count); + OS.UnionRgn(handle, polyRgn, handle); + OS.DisposeRgn(polyRgn); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Adds the given rectangle to the collection of polygons + * the receiver maintains to describe its area. + * + * @param rect the rectangle to merge with the receiver + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the argument is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the rectangle's width or height is negative</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void add(Rectangle rect) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (rect == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (rect.width < 0 || rect.height < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + add (rect.x, rect.y, rect.width, rect.height); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Adds the given rectangle to the collection of polygons + * the receiver maintains to describe its area. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param width the width coordinate of the rectangle + * @param height the height coordinate of the rectangle + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the rectangle's width or height is negative</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public void add(int x, int y, int width, int height) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (width < 0 || height < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + int /*long*/ rectRgn = OS.NewRgn(); + short[] r = new short[4]; + OS.SetRect(r, (short)x, (short)y, (short)(x + width),(short)(y + height)); + OS.RectRgn(rectRgn, r); + OS.UnionRgn(handle, rectRgn, handle); + OS.DisposeRgn(rectRgn); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Adds all of the polygons which make up the area covered + * by the argument to the collection of polygons the receiver + * maintains to describe its area. + * + * @param region the region to merge + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the argument is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void add(Region region) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (region == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + OS.UnionRgn(handle, region.handle, handle); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns <code>true</code> if the point specified by the + * arguments is inside the area specified by the receiver, + * and <code>false</code> otherwise. + * + * @param x the x coordinate of the point to test for containment + * @param y the y coordinate of the point to test for containment + * @return <code>true</code> if the region contains the point and <code>false</code> otherwise + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public boolean contains(int x, int y) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + short[] point = new short[]{(short)y, (short)x}; + return OS.PtInRgn(point, handle); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns <code>true</code> if the given point is inside the + * area specified by the receiver, and <code>false</code> + * otherwise. + * + * @param pt the point to test for containment + * @return <code>true</code> if the region contains the point and <code>false</code> otherwise + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the argument is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public boolean contains(Point pt) { + if (pt == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + return contains(pt.x, pt.y); +} + +NSAffineTransform transform; +void convertRgn(NSAffineTransform transform) { + int /*long*/ newRgn = OS.NewRgn(); + Callback callback = new Callback(this, "convertRgn", 4); + int /*long*/ proc = callback.getAddress(); + if (proc == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + this.transform = transform; + OS.QDRegionToRects(handle, OS.kQDParseRegionFromTopLeft, proc, newRgn); + this.transform = null; + callback.dispose(); + OS.CopyRgn(newRgn, handle); + OS.DisposeRgn(newRgn); +} + +int /*long*/ convertRgn(int /*long*/ message, int /*long*/ rgn, int /*long*/ r, int /*long*/ newRgn) { + if (message == OS.kQDRegionToRectsMsgParse) { + short[] rect = new short[4]; + OS.memmove(rect, r, rect.length * 2); + int i = 0; + NSPoint point = new NSPoint(); + int[] points = new int[10]; + point.x = rect[1]; + point.y = rect[0]; + point = transform.transformPoint(point); + short startX, startY; + points[i++] = startX = (short)point.x; + points[i++] = startY = (short)point.y; + point.x = rect[3]; + point.y = rect[0]; + point = transform.transformPoint(point); + points[i++] = (short)Math.round(point.x); + points[i++] = (short)point.y; + point.x = rect[3]; + point.y = rect[2]; + point = transform.transformPoint(point); + points[i++] = (short)Math.round(point.x); + points[i++] = (short)Math.round(point.y); + point.x = rect[1]; + point.y = rect[2]; + point = transform.transformPoint(point); + points[i++] = (short)point.x; + points[i++] = (short)Math.round(point.y); + points[i++] = startX; + points[i++] = startY; + int /*long*/ polyRgn = polyRgn(points, points.length); + OS.UnionRgn(newRgn, polyRgn, newRgn); + OS.DisposeRgn(polyRgn); + } + return 0; +} + +void destroy() { + OS.DisposeRgn(handle); + handle = 0; +} + +/** + * Compares the argument to the receiver, and returns true + * if they represent the <em>same</em> object using a class + * specific comparison. + * + * @param object the object to compare with this object + * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise + * + * @see #hashCode + */ +public boolean equals(Object object) { + if (this == object) return true; + if (!(object instanceof Region)) return false; + Region region = (Region)object; + return handle == region.handle; +} + +/** + * Returns a rectangle which represents the rectangular + * union of the collection of polygons the receiver + * maintains to describe its area. + * + * @return a bounding rectangle for the region + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see Rectangle#union + */ +public Rectangle getBounds() { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + short[] bounds = new short[4]; + OS.GetRegionBounds(handle, bounds); + int width = bounds[3] - bounds[1]; + int height = bounds[2] - bounds[0]; + return new Rectangle(bounds[1], bounds[0], width, height); + } finally { + if (pool != null) pool.release(); + } +} + +NSBezierPath getPath() { + Callback callback = new Callback(this, "regionToRects", 4); + if (callback.getAddress() == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + NSBezierPath path = NSBezierPath.bezierPath(); + path.retain(); + OS.QDRegionToRects(handle, OS.kQDParseRegionFromTopLeft, callback.getAddress(), path.id); + callback.dispose(); + if (path.isEmpty()) path.appendBezierPathWithRect(new NSRect()); + return path; +} + +NSPoint pt = new NSPoint(); +short[] rect = new short[4]; +int /*long*/ regionToRects(int /*long*/ message, int /*long*/ rgn, int /*long*/ r, int /*long*/ path) { + if (message == OS.kQDRegionToRectsMsgParse) { + OS.memmove(rect, r, rect.length * 2); + pt.x = rect[1]; + pt.y = rect[0]; + OS.objc_msgSend(path, OS.sel_moveToPoint_, pt); + pt.x = rect[3]; + OS.objc_msgSend(path, OS.sel_lineToPoint_, pt); + pt.x = rect[3]; + pt.y = rect[2]; + OS.objc_msgSend(path, OS.sel_lineToPoint_, pt); + pt.x = rect[1]; + OS.objc_msgSend(path, OS.sel_lineToPoint_, pt); + OS.objc_msgSend(path, OS.sel_closePath); + } + return 0; +} + +/** + * Returns an integer hash code for the receiver. Any two + * objects that return <code>true</code> when passed to + * <code>equals</code> must return the same value for this + * method. + * + * @return the receiver's hash + * + * @see #equals + */ +public int hashCode() { + return (int)/*64*/handle; +} + +/** + * Intersects the given rectangle to the collection of polygons + * the receiver maintains to describe its area. + * + * @param rect the rectangle to intersect with the receiver + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the argument is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the rectangle's width or height is negative</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void intersect(Rectangle rect) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (rect == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + intersect (rect.x, rect.y, rect.width, rect.height); +} + +/** + * Intersects the given rectangle to the collection of polygons + * the receiver maintains to describe its area. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param width the width coordinate of the rectangle + * @param height the height coordinate of the rectangle + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the rectangle's width or height is negative</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public void intersect(int x, int y, int width, int height) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (width < 0 || height < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + int /*long*/ rectRgn = OS.NewRgn(); + short[] r = new short[4]; + OS.SetRect(r, (short)x, (short)y, (short)(x + width),(short)(y + height)); + OS.RectRgn(rectRgn, r); + OS.SectRgn(handle, rectRgn, handle); + OS.DisposeRgn(rectRgn); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Intersects all of the polygons which make up the area covered + * by the argument to the collection of polygons the receiver + * maintains to describe its area. + * + * @param region the region to intersect + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the argument is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void intersect(Region region) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (region == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + OS.SectRgn(handle, region.handle, handle); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns <code>true</code> if the rectangle described by the + * arguments intersects with any of the polygons the receiver + * maintains to describe its area, and <code>false</code> otherwise. + * + * @param x the x coordinate of the origin of the rectangle + * @param y the y coordinate of the origin of the rectangle + * @param width the width of the rectangle + * @param height the height of the rectangle + * @return <code>true</code> if the rectangle intersects with the receiver, and <code>false</code> otherwise + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see Rectangle#intersects(Rectangle) + */ +public boolean intersects (int x, int y, int width, int height) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + short[] r = new short[4]; + OS.SetRect(r, (short)x, (short)y, (short)(x + width),(short)(y + height)); + return OS.RectInRgn(r, handle); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns <code>true</code> if the given rectangle intersects + * with any of the polygons the receiver maintains to describe + * its area and <code>false</code> otherwise. + * + * @param rect the rectangle to test for intersection + * @return <code>true</code> if the rectangle intersects with the receiver, and <code>false</code> otherwise + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the argument is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see Rectangle#intersects(Rectangle) + */ +public boolean intersects(Rectangle rect) { + if (rect == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + return intersects(rect.x, rect.y, rect.width, rect.height); +} + +/** + * Returns <code>true</code> if the region has been disposed, + * and <code>false</code> otherwise. + * <p> + * This method gets the dispose state for the region. + * When a region has been disposed, it is an error to + * invoke any other method using the region. + * + * @return <code>true</code> when the region is disposed, and <code>false</code> otherwise + */ +public boolean isDisposed() { + return handle == 0; +} + +/** + * Returns <code>true</code> if the receiver does not cover any + * area in the (x, y) coordinate plane, and <code>false</code> if + * the receiver does cover some area in the plane. + * + * @return <code>true</code> if the receiver is empty, and <code>false</code> otherwise + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public boolean isEmpty() { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + return OS.EmptyRgn(handle); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Subtracts the given polygon from the collection of polygons + * the receiver maintains to describe its area. + * + * @param pointArray points that describe the polygon to merge with the receiver + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the argument is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void subtract (int[] pointArray) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (pointArray == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (pointArray.length < 2) return; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + int /*long*/ polyRgn = polyRgn(pointArray, pointArray.length); + OS.DiffRgn(handle, polyRgn, handle); + OS.DisposeRgn(polyRgn); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Subtracts the given rectangle from the collection of polygons + * the receiver maintains to describe its area. + * + * @param rect the rectangle to subtract from the receiver + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the argument is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the rectangle's width or height is negative</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void subtract(Rectangle rect) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (rect == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + subtract (rect.x, rect.y, rect.width, rect.height); +} + +/** + * Subtracts the given rectangle from the collection of polygons + * the receiver maintains to describe its area. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param width the width coordinate of the rectangle + * @param height the height coordinate of the rectangle + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the rectangle's width or height is negative</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public void subtract(int x, int y, int width, int height) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (width < 0 || height < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + int /*long*/ rectRgn = OS.NewRgn(); + short[] r = new short[4]; + OS.SetRect(r, (short)x, (short)y, (short)(x + width),(short)(y + height)); + OS.RectRgn(rectRgn, r); + OS.DiffRgn(handle, rectRgn, handle); + OS.DisposeRgn(rectRgn); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Subtracts all of the polygons which make up the area covered + * by the argument from the collection of polygons the receiver + * maintains to describe its area. + * + * @param region the region to subtract + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the argument is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public void subtract(Region region) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (region == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + OS.DiffRgn(handle, region.handle, handle); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Translate all of the polygons the receiver maintains to describe + * its area by the specified point. + * + * @param x the x coordinate of the point to translate + * @param y the y coordinate of the point to translate + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public void translate (int x, int y) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + OS.OffsetRgn (handle, (short)x, (short)y); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Translate all of the polygons the receiver maintains to describe + * its area by the specified point. + * + * @param pt the point to translate + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the argument is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public void translate (Point pt) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (pt == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + translate (pt.x, pt.y); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the receiver + */ +public String toString () { + if (isDisposed()) return "Region {*DISPOSED*}"; + return "Region {" + handle + "}"; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/TextLayout.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/TextLayout.java new file mode 100755 index 0000000000..1d8b96f356 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/TextLayout.java @@ -0,0 +1,1980 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + +import org.eclipse.swt.internal.C; +import org.eclipse.swt.internal.Compatibility; +import org.eclipse.swt.internal.cocoa.*; +import org.eclipse.swt.*; + +/** + * <code>TextLayout</code> is a graphic object that represents + * styled text. + * <p> + * Instances of this class provide support for drawing, cursor + * navigation, hit testing, text wrapping, alignment, tab expansion + * line breaking, etc. These are aspects required for rendering internationalized text. + * </p><p> + * Application code must explicitly invoke the <code>TextLayout#dispose()</code> + * method to release the operating system resources managed by each instance + * when those instances are no longer required. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#textlayout">TextLayout, TextStyle snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: CustomControlExample, StyledText tab</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.0 + */ +public final class TextLayout extends Resource { + + NSTextStorage textStorage; + NSLayoutManager layoutManager; + NSTextContainer textContainer; + Font font; + String text; + StyleItem[] styles; + int spacing, ascent, descent, indent; + boolean justify; + int alignment; + int[] tabs; + int[] segments; + int wrapWidth; + int orientation; + + int[] lineOffsets; + NSRect[] lineBounds; + + static final int UNDERLINE_THICK = 1 << 16; + static final RGB LINK_FOREGROUND = new RGB (0, 51, 153); + int[] invalidOffsets; + static final char LTR_MARK = '\u200E', RTL_MARK = '\u200F', ZWS = '\u200B'; + + static class StyleItem { + TextStyle style; + int start; + + public String toString () { + return "StyleItem {" + start + ", " + style + "}"; + } + } + +/** + * Constructs a new instance of this class on the given device. + * <p> + * You must dispose the text layout when it is no longer required. + * </p> + * + * @param device the device on which to allocate the text layout + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * </ul> + * + * @see #dispose() + */ +public TextLayout (Device device) { + super(device); + wrapWidth = ascent = descent = -1; + alignment = SWT.LEFT; + orientation = SWT.LEFT_TO_RIGHT; + text = ""; + styles = new StyleItem[2]; + styles[0] = new StyleItem(); + styles[1] = new StyleItem(); + init(); +} + +void checkLayout() { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); +} + +float[] computePolyline(int left, int top, int right, int bottom) { + int height = bottom - top; // can be any number + int width = 2 * height; // must be even + int peaks = Compatibility.ceil(right - left, width); + if (peaks == 0 && right - left > 2) { + peaks = 1; + } + int length = ((2 * peaks) + 1) * 2; + if (length < 0) return new float[0]; + + float[] coordinates = new float[length]; + for (int i = 0; i < peaks; i++) { + int index = 4 * i; + coordinates[index] = left + (width * i); + coordinates[index+1] = bottom; + coordinates[index+2] = coordinates[index] + width / 2; + coordinates[index+3] = top; + } + coordinates[length-2] = left + (width * peaks); + coordinates[length-1] = bottom; + return coordinates; +} + + +void computeRuns() { + if (textStorage != null) return; + String segmentsText = getSegmentsText(); + NSString str = NSString.stringWith(segmentsText); + textStorage = (NSTextStorage)new NSTextStorage().alloc().init(); + layoutManager = (NSLayoutManager)new NSLayoutManager().alloc().init(); + layoutManager.setBackgroundLayoutEnabled(NSThread.isMainThread()); + textContainer = (NSTextContainer)new NSTextContainer().alloc(); + NSSize size = new NSSize(); + size.width = wrapWidth != -1 ? wrapWidth : Float.MAX_VALUE; + size.height = Float.MAX_VALUE; + textContainer.initWithContainerSize(size); + textStorage.addLayoutManager(layoutManager); + layoutManager.addTextContainer(textContainer); + + /* + * Bug in Cocoa. Adding attributes directly to a NSTextStorage causes + * output to the console and eventually a segmentation fault when printing + * on a thread other than the main thread. The fix is to add attributes to + * a separate NSMutableAttributedString and add it to text storage when done. + */ + NSMutableAttributedString attrStr = (NSMutableAttributedString)new NSMutableAttributedString().alloc(); + attrStr.id = attrStr.initWithString(str).id; + attrStr.beginEditing(); + Font defaultFont = font != null ? font : device.systemFont; + NSRange range = new NSRange(); + range.length = str.length(); + attrStr.addAttribute(OS.NSFontAttributeName, defaultFont.handle, range); + defaultFont.addTraits(attrStr, range); + //TODO ascend descent wrap + NSMutableParagraphStyle paragraph = (NSMutableParagraphStyle)new NSMutableParagraphStyle().alloc().init(); + int align = OS.NSLeftTextAlignment; + if (justify) { + align = OS.NSJustifiedTextAlignment; + } else { + switch (alignment) { + case SWT.CENTER: + align = OS.NSCenterTextAlignment; + break; + case SWT.RIGHT: + align = OS.NSRightTextAlignment; + } + } + paragraph.setAlignment(align); + paragraph.setLineSpacing(spacing); + paragraph.setFirstLineHeadIndent(indent); + paragraph.setLineBreakMode(wrapWidth != -1 ? OS.NSLineBreakByWordWrapping : OS.NSLineBreakByClipping); + paragraph.setTabStops(NSArray.array()); + if (tabs != null) { + int count = tabs.length; + for (int i = 0, pos = 0; i < count; i++) { + pos += tabs[i]; + NSTextTab tab = (NSTextTab)new NSTextTab().alloc(); + tab = tab.initWithType(OS.NSLeftTabStopType, pos); + paragraph.addTabStop(tab); + tab.release(); + } + int width = count - 2 >= 0 ? tabs[count - 1] - tabs[count - 2] : tabs[count - 1]; + paragraph.setDefaultTabInterval(width); + } + attrStr.addAttribute(OS.NSParagraphStyleAttributeName, paragraph, range); + paragraph.release(); + int /*long*/ textLength = str.length(); + for (int i = 0; i < styles.length - 1; i++) { + StyleItem run = styles[i]; + if (run.style == null) continue; + TextStyle style = run.style; + range.location = textLength != 0 ? translateOffset(run.start) : 0; + range.length = translateOffset(styles[i + 1].start) - range.location; + Font font = style.font; + if (font != null) { + attrStr.addAttribute(OS.NSFontAttributeName, font.handle, range); + font.addTraits(attrStr, range); + } + Color foreground = style.foreground; + if (foreground != null) { + NSColor color = NSColor.colorWithDeviceRed(foreground.handle[0], foreground.handle[1], foreground.handle[2], 1); + attrStr.addAttribute(OS.NSForegroundColorAttributeName, color, range); + } + Color background = style.background; + if (background != null) { + NSColor color = NSColor.colorWithDeviceRed(background.handle[0], background.handle[1], background.handle[2], 1); + attrStr.addAttribute(OS.NSBackgroundColorAttributeName, color, range); + } + if (style.strikeout) { + attrStr.addAttribute(OS.NSStrikethroughStyleAttributeName, NSNumber.numberWithInt(OS.NSUnderlineStyleSingle), range); + Color strikeColor = style.strikeoutColor; + if (strikeColor != null) { + NSColor color = NSColor.colorWithDeviceRed(strikeColor.handle[0], strikeColor.handle[1], strikeColor.handle[2], 1); + attrStr.addAttribute(OS.NSStrikethroughColorAttributeName, color, range); + } + } + if (isUnderlineSupported(style)) { + int underlineStyle = 0; + switch (style.underlineStyle) { + case SWT.UNDERLINE_SINGLE: + underlineStyle = OS.NSUnderlineStyleSingle; + break; + case SWT.UNDERLINE_DOUBLE: + underlineStyle = OS.NSUnderlineStyleDouble; + break; + case UNDERLINE_THICK: + underlineStyle = OS.NSUnderlineStyleThick; + break; + case SWT.UNDERLINE_LINK: { + underlineStyle = OS.NSUnderlineStyleSingle; + if (foreground == null) { + NSColor color = NSColor.colorWithDeviceRed(LINK_FOREGROUND.red / 255f, LINK_FOREGROUND.green / 255f, LINK_FOREGROUND.blue / 255f, 1); + attrStr.addAttribute(OS.NSForegroundColorAttributeName, color, range); + } + break; + } + } + if (underlineStyle != 0) { + attrStr.addAttribute(OS.NSUnderlineStyleAttributeName, NSNumber.numberWithInt(underlineStyle), range); + Color underlineColor = style.underlineColor; + if (underlineColor != null) { + NSColor color = NSColor.colorWithDeviceRed(underlineColor.handle[0], underlineColor.handle[1], underlineColor.handle[2], 1); + attrStr.addAttribute(OS.NSUnderlineColorAttributeName, color, range); + } + } + } + if (style.rise != 0) { + attrStr.addAttribute(OS.NSBaselineOffsetAttributeName, NSNumber.numberWithInt(style.rise), range); + } + if (style.metrics != null) { + //TODO implement metrics + } + } + attrStr.endEditing(); + textStorage.setAttributedString(attrStr); + attrStr.release(); + + textContainer.setLineFragmentPadding(0); + layoutManager.glyphRangeForTextContainer(textContainer); + + int numberOfLines; + int /*long*/ numberOfGlyphs = layoutManager.numberOfGlyphs(), index; + int /*long*/ rangePtr = OS.malloc(NSRange.sizeof); + NSRange lineRange = new NSRange(); + for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){ + layoutManager.lineFragmentUsedRectForGlyphAtIndex(index, rangePtr, true); + OS.memmove(lineRange, rangePtr, NSRange.sizeof); + index = lineRange.location + lineRange.length; + } + if (numberOfLines == 0) numberOfLines++; + int[] offsets = new int[numberOfLines + 1]; + NSRect[] bounds = new NSRect[numberOfLines]; + for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){ + bounds[numberOfLines] = layoutManager.lineFragmentUsedRectForGlyphAtIndex(index, rangePtr, true); + if (numberOfLines < bounds.length - 1) bounds[numberOfLines].height -= spacing; + OS.memmove(lineRange, rangePtr, NSRange.sizeof); + offsets[numberOfLines] = (int)/*64*/lineRange.location; + index = lineRange.location + lineRange.length; + } + if (numberOfLines == 0) { + Font font = this.font != null ? this.font : device.systemFont; + NSFont nsFont = font.handle; + bounds[0] = new NSRect(); + bounds[0].height = Math.max(layoutManager.defaultLineHeightForFont(nsFont), ascent + descent); + } + OS.free(rangePtr); + offsets[numberOfLines] = (int)/*64*/textStorage.length(); + this.lineOffsets = offsets; + this.lineBounds = bounds; +} + +void destroy() { + freeRuns(); + font = null; + text = null; + styles = null; +} + +/** + * Draws the receiver's text using the specified GC at the specified + * point. + * + * @param gc the GC to draw + * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn + * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the gc is null</li> + * </ul> + */ +public void draw(GC gc, int x, int y) { + draw(gc, x, y, -1, -1, null, null); +} + +/** + * Draws the receiver's text using the specified GC at the specified + * point. + * + * @param gc the GC to draw + * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn + * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn + * @param selectionStart the offset where the selections starts, or -1 indicating no selection + * @param selectionEnd the offset where the selections ends, or -1 indicating no selection + * @param selectionForeground selection foreground, or NULL to use the system default color + * @param selectionBackground selection background, or NULL to use the system default color + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the gc is null</li> + * </ul> + */ +public void draw(GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground) { + draw(gc, x, y, selectionStart, selectionEnd, selectionForeground, selectionBackground, 0); +} + +/** + * Draws the receiver's text using the specified GC at the specified + * point. + * <p> + * The parameter <code>flags</code> can include one of <code>SWT.DELIMITER_SELECTION</code> + * or <code>SWT.FULL_SELECTION</code> to specify the selection behavior on all lines except + * for the last line, and can also include <code>SWT.LAST_LINE_SELECTION</code> to extend + * the specified selection behavior to the last line. + * </p> + * @param gc the GC to draw + * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn + * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn + * @param selectionStart the offset where the selections starts, or -1 indicating no selection + * @param selectionEnd the offset where the selections ends, or -1 indicating no selection + * @param selectionForeground selection foreground, or NULL to use the system default color + * @param selectionBackground selection background, or NULL to use the system default color + * @param flags drawing options + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the gc is null</li> + * </ul> + * + * @since 3.3 + */ +public void draw(GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground, int flags) { + checkLayout (); + if (gc == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (gc.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (selectionForeground != null && selectionForeground.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (selectionBackground != null && selectionBackground.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = gc.checkGC(GC.CLIPPING | GC.TRANSFORM | GC.FOREGROUND); + try { + computeRuns(); + int length = translateOffset(text.length()); + if (length == 0 && flags == 0) return; + gc.handle.saveGraphicsState(); + NSPoint pt = new NSPoint(); + pt.x = x; + pt.y = y; + NSRange range = new NSRange(); + int /*long*/ numberOfGlyphs = layoutManager.numberOfGlyphs(); + if (numberOfGlyphs > 0) { + range.location = 0; + range.length = numberOfGlyphs; + layoutManager.drawBackgroundForGlyphRange(range, pt); + } + boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; + if (hasSelection || (flags & SWT.LAST_LINE_SELECTION) != 0) { + if (selectionBackground == null) selectionBackground = device.getSystemColor(SWT.COLOR_LIST_SELECTION); + NSColor selectionColor = NSColor.colorWithDeviceRed(selectionBackground.handle[0], selectionBackground.handle[1], selectionBackground.handle[2], selectionBackground.handle[3]); + NSBezierPath path = NSBezierPath.bezierPath(); + NSRect rect = new NSRect(); + if (hasSelection) { + int /*long*/ pRectCount = OS.malloc(C.PTR_SIZEOF); + range.location = translateOffset(selectionStart); + range.length = translateOffset(selectionEnd - selectionStart + 1); + int /*long*/ pArray = layoutManager.rectArrayForCharacterRange(range, range, textContainer, pRectCount); + int /*long*/ [] rectCount = new int /*long*/ [1]; + OS.memmove(rectCount, pRectCount, C.PTR_SIZEOF); + OS.free(pRectCount); + for (int k = 0; k < rectCount[0]; k++, pArray += NSRect.sizeof) { + OS.memmove(rect, pArray, NSRect.sizeof); + fixRect(rect); + rect.x += pt.x; + rect.y += pt.y; + rect.height = Math.max(rect.height, ascent + descent); + path.appendBezierPathWithRect(rect); + } + } + //TODO draw full selection for wrapped text + if ((flags & SWT.LAST_LINE_SELECTION) != 0) { + NSRect bounds = lineBounds[lineBounds.length - 1]; + rect.x = pt.x + bounds.x + bounds.width; + rect.y = y + bounds.y; + rect.width = (flags & SWT.FULL_SELECTION) != 0 ? 0x7fffffff : bounds.height / 3; + rect.height = Math.max(bounds.height, ascent + descent); + path.appendBezierPathWithRect(rect); + } + selectionColor.setFill(); + path.fill(); + } + if (numberOfGlyphs > 0) { + range.location = 0; + range.length = numberOfGlyphs; + float /*double*/ [] fg = gc.data.foreground; + boolean defaultFg = fg[0] == 0 && fg[1] == 0 && fg[2] == 0 && fg[3] == 1; + if (!defaultFg) { + for (int i = 0; i < styles.length - 1; i++) { + StyleItem run = styles[i]; + if (run.style != null && run.style.foreground != null) continue; + if (run.style != null && run.style.underline && run.style.underlineStyle == SWT.UNDERLINE_LINK) continue; + range.location = length != 0 ? translateOffset(run.start) : 0; + range.length = translateOffset(styles[i + 1].start) - range.location; + layoutManager.addTemporaryAttribute(OS.NSForegroundColorAttributeName, gc.data.fg, range); + } + } + range.location = 0; + range.length = numberOfGlyphs; + layoutManager.drawGlyphsForGlyphRange(range, pt); + if (!defaultFg) { + range.location = 0; + range.length = length; + layoutManager.removeTemporaryAttribute(OS.NSForegroundColorAttributeName, range); + } + NSPoint point = new NSPoint(); + for (int j = 0; j < styles.length; j++) { + StyleItem run = styles[j]; + TextStyle style = run.style; + if (style == null) continue; + boolean drawUnderline = style.underline && !isUnderlineSupported(style); + drawUnderline = drawUnderline && (j + 1 == styles.length || !style.isAdherentUnderline(styles[j + 1].style)); + boolean drawBorder = style.borderStyle != SWT.NONE; + drawBorder = drawBorder && (j + 1 == styles.length || !style.isAdherentBorder(styles[j + 1].style)); + if (!drawUnderline && !drawBorder) continue; + int end = j + 1 < styles.length ? translateOffset(styles[j + 1].start - 1) : length; + for (int i = 0; i < lineOffsets.length - 1; i++) { + int lineStart = untranslateOffset(lineOffsets[i]); + int lineEnd = untranslateOffset(lineOffsets[i + 1] - 1); + if (drawUnderline) { + int start = run.start; + for (int k = j; k > 0 && style.isAdherentUnderline(styles[k - 1].style); k--) { + start = styles[k - 1].start; + } + start = translateOffset(start); + if (!(start > lineEnd || end < lineStart)) { + range.location = Math.max(lineStart, start); + range.length = Math.min(lineEnd, end) + 1 - range.location; + if (range.length > 0) { + int /*long*/ pRectCount = OS.malloc(C.PTR_SIZEOF); + int /*long*/ pArray = layoutManager.rectArrayForCharacterRange(range, range, textContainer, pRectCount); + int /*long*/ [] rectCount = new int /*long*/ [1]; + OS.memmove(rectCount, pRectCount, C.PTR_SIZEOF); + OS.free(pRectCount); + NSRect rect = new NSRect(); + gc.handle.saveGraphicsState(); + float /*double*/ baseline = layoutManager.typesetter().baselineOffsetInLayoutManager(layoutManager, lineStart); + float /*double*/ [] color = null; + if (style.underlineColor != null) color = style.underlineColor.handle; + if (color == null && style.foreground != null) color = style.foreground.handle; + if (color != null) { + NSColor.colorWithDeviceRed(color[0], color[1], color[2], color[3]).setStroke(); + } + for (int k = 0; k < rectCount[0]; k++, pArray += NSRect.sizeof) { + OS.memmove(rect, pArray, NSRect.sizeof); + fixRect(rect); + float /*double*/ underlineX = pt.x + rect.x; + float /*double*/ underlineY = pt.y + rect.y + rect.height - baseline + 1; + NSBezierPath path = NSBezierPath.bezierPath(); + switch (style.underlineStyle) { + case SWT.UNDERLINE_ERROR: { + path.setLineWidth(2f); + path.setLineCapStyle(OS.NSRoundLineCapStyle); + path.setLineJoinStyle(OS.NSRoundLineJoinStyle); + path.setLineDash(new float /*double*/ []{1, 3f}, 2, 0); + point.x = underlineX; + point.y = underlineY + 0.5f; + path.moveToPoint(point); + point.x = underlineX + rect.width; + point.y = underlineY + 0.5f; + path.lineToPoint(point); + break; + } + case SWT.UNDERLINE_SQUIGGLE: { + gc.handle.setShouldAntialias(false); + path.setLineWidth(1.0f); + path.setLineCapStyle(OS.NSButtLineCapStyle); + path.setLineJoinStyle(OS.NSMiterLineJoinStyle); + float /*double*/ lineBottom = pt.y + rect.y + rect.height; + float squigglyThickness = 1; + float squigglyHeight = 2 * squigglyThickness; + float /*double*/ squigglyY = Math.min(underlineY - squigglyHeight / 2, lineBottom - squigglyHeight - 1); + float[] points = computePolyline((int)underlineX, (int)squigglyY, (int)(underlineX + rect.width), (int)(squigglyY + squigglyHeight)); + point.x = points[0] + 0.5f; + point.y = points[1] + 0.5f; + path.moveToPoint(point); + for (int p = 2; p < points.length; p+=2) { + point.x = points[p] + 0.5f; + point.y = points[p+1] + 0.5f; + path.lineToPoint(point); + } + break; + } + } + path.stroke(); + } + gc.handle.restoreGraphicsState(); + } + } + } + if (drawBorder) { + int start = run.start; + for (int k = j; k > 0 && style.isAdherentBorder(styles[k - 1].style); k--) { + start = styles[k - 1].start; + } + start = translateOffset(start); + if (!(start > lineEnd || end < lineStart)) { + range.location = Math.max(lineStart, start); + range.length = Math.min(lineEnd, end) + 1 - range.location; + if (range.length > 0) { + int /*long*/ pRectCount = OS.malloc(C.PTR_SIZEOF); + int /*long*/ pArray = layoutManager.rectArrayForCharacterRange(range, range, textContainer, pRectCount); + int /*long*/ [] rectCount = new int /*long*/ [1]; + OS.memmove(rectCount, pRectCount, C.PTR_SIZEOF); + OS.free(pRectCount); + NSRect rect = new NSRect(); + gc.handle.saveGraphicsState(); + float /*double*/ [] color = null; + if (style.borderColor != null) color = style.borderColor.handle; + if (color == null && style.foreground != null) color = style.foreground.handle; + if (color != null) { + NSColor.colorWithDeviceRed(color[0], color[1], color[2], color[3]).setStroke(); + } + int width = 1; + float[] dashes = null; + switch (style.borderStyle) { + case SWT.BORDER_SOLID: break; + case SWT.BORDER_DASH: dashes = width != 0 ? GC.LINE_DASH : GC.LINE_DASH_ZERO; break; + case SWT.BORDER_DOT: dashes = width != 0 ? GC.LINE_DOT : GC.LINE_DOT_ZERO; break; + } + float /*double*/ [] lengths = null; + if (dashes != null) { + lengths = new float /*double*/[dashes.length]; + for (int k = 0; k < lengths.length; k++) { + lengths[k] = width == 0 ? dashes[k] : dashes[k] * width; + } + } + for (int k = 0; k < rectCount[0]; k++, pArray += NSRect.sizeof) { + OS.memmove(rect, pArray, NSRect.sizeof); + fixRect(rect); + rect.x += pt.x + 0.5f; + rect.y += pt.y + 0.5f; + rect.width -= 0.5f; + rect.height -= 0.5f; + NSBezierPath path = NSBezierPath.bezierPath(); + path.setLineDash(lengths, lengths != null ? lengths.length : 0, 0); + path.appendBezierPathWithRect(rect); + path.stroke(); + } + gc.handle.restoreGraphicsState(); + } + } + } + } + } + } + gc.handle.restoreGraphicsState(); + } finally { + gc.uncheckGC(pool); + } +} + +void fixRect(NSRect rect) { + for (int j = 0; j < lineBounds.length; j++) { + NSRect line = lineBounds[j]; + if (line.y <= rect.y && rect.y < line.y + line.height) { + if (rect.x + rect.width > line.x + line.width) { + rect.width = line.x + line.width - rect.x; + } + } + } +} + +void freeRuns() { + if (textStorage == null) return; + if (textStorage != null) { + textStorage.release(); + } + if (layoutManager != null) { + layoutManager.release(); + } + if (textContainer != null) { + textContainer.release(); + } + textStorage = null; + layoutManager = null; + textContainer = null; +} + +/** + * Returns the receiver's horizontal text alignment, which will be one + * of <code>SWT.LEFT</code>, <code>SWT.CENTER</code> or + * <code>SWT.RIGHT</code>. + * + * @return the alignment used to positioned text horizontally + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int getAlignment() { + checkLayout(); + return alignment; +} + +/** + * Returns the ascent of the receiver. + * + * @return the ascent + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #getDescent() + * @see #setDescent(int) + * @see #setAscent(int) + * @see #getLineMetrics(int) + */ +public int getAscent () { + checkLayout(); + return ascent; +} + +/** + * Returns the bounds of the receiver. The width returned is either the + * width of the longest line or the width set using {@link TextLayout#setWidth(int)}. + * To obtain the text bounds of a line use {@link TextLayout#getLineBounds(int)}. + * + * @return the bounds of the receiver + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #setWidth(int) + * @see #getLineBounds(int) + */ +public Rectangle getBounds() { + checkLayout(); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + NSRect rect = layoutManager.usedRectForTextContainer(textContainer); + if (wrapWidth != -1) rect.width = wrapWidth; + if (text.length() == 0) { + Font font = this.font != null ? this.font : device.systemFont; + NSFont nsFont = font.handle; + rect.height = layoutManager.defaultLineHeightForFont(nsFont); + } + rect.height = Math.max(rect.height, ascent + descent) + spacing; + return new Rectangle(0, 0, (int)Math.ceil(rect.width), (int)Math.ceil(rect.height)); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns the bounds for the specified range of characters. The + * bounds is the smallest rectangle that encompasses all characters + * in the range. The start and end offsets are inclusive and will be + * clamped if out of range. + * + * @param start the start offset + * @param end the end offset + * @return the bounds of the character range + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Rectangle getBounds(int start, int end) { + checkLayout(); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + int length = text.length(); + if (length == 0) return new Rectangle(0, 0, 0, 0); + if (start > end) return new Rectangle(0, 0, 0, 0); + start = Math.min(Math.max(0, start), length - 1); + end = Math.min(Math.max(0, end), length - 1); + start = translateOffset(start); + end = translateOffset(end); + NSRange range = new NSRange(); + range.location = start; + range.length = end - start + 1; + int /*long*/ pRectCount = OS.malloc(C.PTR_SIZEOF); + int /*long*/ pArray = layoutManager.rectArrayForCharacterRange(range, range, textContainer, pRectCount); + int /*long*/ [] rectCount = new int /*long*/ [1]; + OS.memmove(rectCount, pRectCount, C.PTR_SIZEOF); + OS.free(pRectCount); + NSRect rect = new NSRect(); + int left = 0x7FFFFFFF, right = 0; + int top = 0x7FFFFFFF, bottom = 0; + for (int i = 0; i < rectCount[0]; i++, pArray += NSRect.sizeof) { + OS.memmove(rect, pArray, NSRect.sizeof); + fixRect(rect); + left = Math.min(left, (int)rect.x); + right = Math.max(right, (int)Math.ceil(rect.x + rect.width)); + top = Math.min(top, (int)rect.y); + bottom = Math.max(bottom, (int)Math.ceil(rect.y + rect.height)); + } + return new Rectangle(left, top, right - left, bottom - top); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns the descent of the receiver. + * + * @return the descent + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #getAscent() + * @see #setAscent(int) + * @see #setDescent(int) + * @see #getLineMetrics(int) + */ +public int getDescent () { + checkLayout(); + return descent; +} + +/** + * Returns the default font currently being used by the receiver + * to draw and measure text. + * + * @return the receiver's font + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Font getFont () { + checkLayout(); + return font; +} + +/** +* Returns the receiver's indent. +* +* @return the receiver's indent +* +* @exception SWTException <ul> +* <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> +* </ul> +* +* @since 3.2 +*/ +public int getIndent () { + checkLayout(); + return indent; +} + +/** +* Returns the receiver's justification. +* +* @return the receiver's justification +* +* @exception SWTException <ul> +* <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> +* </ul> +* +* @since 3.2 +*/ +public boolean getJustify () { + checkLayout(); + return justify; +} + +/** + * Returns the embedding level for the specified character offset. The + * embedding level is usually used to determine the directionality of a + * character in bidirectional text. + * + * @param offset the character offset + * @return the embedding level + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the character offset is out of range</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + */ +public int getLevel(int offset) { + checkLayout(); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + int length = text.length(); + if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE); + offset = translateOffset(offset); + int /*long*/ glyphOffset = layoutManager.glyphIndexForCharacterAtIndex(offset); + NSRange range = new NSRange(); + range.location = glyphOffset; + range.length = 1; + int /*long*/ pBidiLevels = OS.malloc(1); + byte[] bidiLevels = new byte[1]; + int /*long*/ result = layoutManager.getGlyphsInRange(range, 0, 0, 0, 0, pBidiLevels); + if (result > 0) { + OS.memmove(bidiLevels, pBidiLevels, 1); + } + OS.free(pBidiLevels); + return bidiLevels[0]; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns the line offsets. Each value in the array is the + * offset for the first character in a line except for the last + * value, which contains the length of the text. + * + * @return the line offsets + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int[] getLineOffsets() { + checkLayout (); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + int[] offsets = new int[lineOffsets.length]; + for (int i = 0; i < offsets.length; i++) { + offsets[i] = untranslateOffset(lineOffsets[i]); + } + return offsets; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns the index of the line that contains the specified + * character offset. + * + * @param offset the character offset + * @return the line index + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the character offset is out of range</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int getLineIndex(int offset) { + checkLayout (); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + int length = text.length(); + if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE); + offset = translateOffset(offset); + for (int line=0; line<lineOffsets.length - 1; line++) { + if (lineOffsets[line + 1] > offset) { + return line; + } + } + return lineBounds.length - 1; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns the bounds of the line for the specified line index. + * + * @param lineIndex the line index + * @return the line bounds + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the line index is out of range</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Rectangle getLineBounds(int lineIndex) { + checkLayout(); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + if (!(0 <= lineIndex && lineIndex < lineBounds.length)) SWT.error(SWT.ERROR_INVALID_RANGE); + NSRect rect = lineBounds[lineIndex]; + int height = Math.max((int)Math.ceil(rect.height), ascent + descent); + return new Rectangle((int)rect.x, (int)rect.y, (int)Math.ceil(rect.width), height); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns the receiver's line count. This includes lines caused + * by wrapping. + * + * @return the line count + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int getLineCount() { + checkLayout (); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + return lineOffsets.length - 1; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns the font metrics for the specified line index. + * + * @param lineIndex the line index + * @return the font metrics + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the line index is out of range</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public FontMetrics getLineMetrics (int lineIndex) { + checkLayout (); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + int lineCount = getLineCount(); + if (!(0 <= lineIndex && lineIndex < lineCount)) SWT.error(SWT.ERROR_INVALID_RANGE); + int length = text.length(); + if (length == 0) { + Font font = this.font != null ? this.font : device.systemFont; + NSFont nsFont = font.handle; + int ascent = (int)(0.5f + nsFont.ascender()); + int descent = (int)(0.5f + (-nsFont.descender() + nsFont.leading())); + ascent = Math.max(ascent, this.ascent); + descent = Math.max(descent, this.descent); + return FontMetrics.cocoa_new(ascent, descent, 0, 0, ascent + descent); + } + Rectangle rect = getLineBounds(lineIndex); + int baseline = (int)layoutManager.typesetter().baselineOffsetInLayoutManager(layoutManager, getLineOffsets()[lineIndex]); + return FontMetrics.cocoa_new(rect.height - baseline, baseline, 0, 0, rect.height); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns the location for the specified character offset. The + * <code>trailing</code> argument indicates whether the offset + * corresponds to the leading or trailing edge of the cluster. + * + * @param offset the character offset + * @param trailing the trailing flag + * @return the location of the character offset + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #getOffset(Point, int[]) + * @see #getOffset(int, int, int[]) + */ +public Point getLocation(int offset, boolean trailing) { + checkLayout(); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + int length = text.length(); + if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE); + if (length == 0) return new Point(0, 0); + offset = translateOffset(offset); + int /*long*/ glyphIndex = layoutManager.glyphIndexForCharacterAtIndex(offset); + NSRect rect = layoutManager.lineFragmentUsedRectForGlyphAtIndex(glyphIndex, 0); + NSPoint point = layoutManager.locationForGlyphAtIndex(glyphIndex); + if (trailing) { + NSRange range = new NSRange(); + range.location = offset; + range.length = 1; + int /*long*/ pRectCount = OS.malloc(C.PTR_SIZEOF); + int /*long*/ pArray = layoutManager.rectArrayForCharacterRange(range, range, textContainer, pRectCount); + int /*long*/ [] rectCount = new int /*long*/ [1]; + OS.memmove(rectCount, pRectCount, C.PTR_SIZEOF); + OS.free(pRectCount); + if (rectCount[0] > 0) { + NSRect bounds = new NSRect(); + OS.memmove(bounds, pArray, NSRect.sizeof); + fixRect(bounds); + point.x += bounds.width; + } + } + return new Point((int)point.x, (int)rect.y); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns the next offset for the specified offset and movement + * type. The movement is one of <code>SWT.MOVEMENT_CHAR</code>, + * <code>SWT.MOVEMENT_CLUSTER</code>, <code>SWT.MOVEMENT_WORD</code>, + * <code>SWT.MOVEMENT_WORD_END</code> or <code>SWT.MOVEMENT_WORD_START</code>. + * + * @param offset the start offset + * @param movement the movement type + * @return the next offset + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the offset is out of range</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #getPreviousOffset(int, int) + */ +public int getNextOffset (int offset, int movement) { + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + return _getOffset(offset, movement, true); + } finally { + if (pool != null) pool.release(); + } +} + +int _getOffset (int offset, int movement, boolean forward) { + checkLayout(); + computeRuns(); + int length = text.length(); + if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE); + if (length == 0) return 0; + offset = translateOffset(offset); + length = translateOffset(length); + switch (movement) { + case SWT.MOVEMENT_CLUSTER://TODO cluster + case SWT.MOVEMENT_CHAR: { + boolean invalid = false; + do { + int newOffset = offset; + if (forward) { + if (newOffset < length) newOffset++; + } else { + if (newOffset > 0) newOffset--; + } + if (newOffset == offset) break; + offset = newOffset; + invalid = false; + if (invalidOffsets != null) { + for (int i = 0; i < invalidOffsets.length; i++) { + if (offset == invalidOffsets[i]) { + invalid = true; + break; + } + } + } + } while (invalid); + return untranslateOffset(offset); + } + case SWT.MOVEMENT_WORD: { + return untranslateOffset((int)/*64*/textStorage.nextWordFromIndex(offset, forward)); + } + case SWT.MOVEMENT_WORD_END: { + NSRange range = textStorage.doubleClickAtIndex(length == offset ? length - 1 : offset); + return untranslateOffset((int)/*64*/(range.location + range.length)); + } + case SWT.MOVEMENT_WORD_START: { + NSRange range = textStorage.doubleClickAtIndex(length == offset ? length - 1 : offset); + return untranslateOffset((int)/*64*/range.location); + } + } + return untranslateOffset(offset); +} + +/** + * Returns the character offset for the specified point. + * For a typical character, the trailing argument will be filled in to + * indicate whether the point is closer to the leading edge (0) or + * the trailing edge (1). When the point is over a cluster composed + * of multiple characters, the trailing argument will be filled with the + * position of the character in the cluster that is closest to + * the point. + * + * @param point the point + * @param trailing the trailing buffer + * @return the character offset + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the trailing length is less than <code>1</code></li> + * <li>ERROR_NULL_ARGUMENT - if the point is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #getLocation(int, boolean) + */ +public int getOffset(Point point, int[] trailing) { + checkLayout(); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + if (point == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + return getOffset(point.x, point.y, trailing); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns the character offset for the specified point. + * For a typical character, the trailing argument will be filled in to + * indicate whether the point is closer to the leading edge (0) or + * the trailing edge (1). When the point is over a cluster composed + * of multiple characters, the trailing argument will be filled with the + * position of the character in the cluster that is closest to + * the point. + * + * @param x the x coordinate of the point + * @param y the y coordinate of the point + * @param trailing the trailing buffer + * @return the character offset + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the trailing length is less than <code>1</code></li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #getLocation(int, boolean) + */ +public int getOffset(int x, int y, int[] trailing) { + checkLayout(); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + if (trailing != null && trailing.length < 1) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + int length = text.length(); + if (length == 0) return 0; + NSPoint pt = new NSPoint(); + pt.x = x; + pt.y = y; + float /*double*/[] partialFration = new float /*double*/[1]; + int /*long*/ glyphIndex = layoutManager.glyphIndexForPoint(pt, textContainer, partialFration); + int /*long*/ offset = layoutManager.characterIndexForGlyphAtIndex(glyphIndex); + if (trailing != null) trailing[0] = Math.round((float)/*64*/partialFration[0]); + return Math.min(untranslateOffset((int)/*64*/offset), length - 1); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns the orientation of the receiver. + * + * @return the orientation style + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int getOrientation() { + checkLayout(); + return orientation; +} + +/** + * Returns the previous offset for the specified offset and movement + * type. The movement is one of <code>SWT.MOVEMENT_CHAR</code>, + * <code>SWT.MOVEMENT_CLUSTER</code> or <code>SWT.MOVEMENT_WORD</code>, + * <code>SWT.MOVEMENT_WORD_END</code> or <code>SWT.MOVEMENT_WORD_START</code>. + * + * @param offset the start offset + * @param movement the movement type + * @return the previous offset + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the offset is out of range</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #getNextOffset(int, int) + */ +public int getPreviousOffset (int index, int movement) { + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + return _getOffset(index, movement, false); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Gets the ranges of text that are associated with a <code>TextStyle</code>. + * + * @return the ranges, an array of offsets representing the start and end of each + * text style. + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #getStyles() + * + * @since 3.2 + */ +public int[] getRanges () { + checkLayout(); + int[] result = new int[styles.length * 2]; + int count = 0; + for (int i=0; i<styles.length - 1; i++) { + if (styles[i].style != null) { + result[count++] = styles[i].start; + result[count++] = styles[i + 1].start - 1; + } + } + if (count != result.length) { + int[] newResult = new int[count]; + System.arraycopy(result, 0, newResult, 0, count); + result = newResult; + } + return result; +} + +/** + * Returns the text segments offsets of the receiver. + * + * @return the text segments offsets + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int[] getSegments() { + checkLayout(); + return segments; +} + +String getSegmentsText() { + if (segments == null) return text; + int nSegments = segments.length; + if (nSegments <= 1) return text; + int length = text.length(); + if (length == 0) return text; + if (nSegments == 2) { + if (segments[0] == 0 && segments[1] == length) return text; + } + invalidOffsets = new int[nSegments]; + char[] oldChars = new char[length]; + text.getChars(0, length, oldChars, 0); + char[] newChars = new char[length + nSegments]; + int charCount = 0, segmentCount = 0; + char separator = getOrientation() == SWT.RIGHT_TO_LEFT ? RTL_MARK : LTR_MARK; + while (charCount < length) { + if (segmentCount < nSegments && charCount == segments[segmentCount]) { + invalidOffsets[segmentCount] = charCount + segmentCount; + newChars[charCount + segmentCount++] = separator; + } else { + newChars[charCount + segmentCount] = oldChars[charCount++]; + } + } + if (segmentCount < nSegments) { + invalidOffsets[segmentCount] = charCount + segmentCount; + segments[segmentCount] = charCount; + newChars[charCount + segmentCount++] = separator; + } + if (segmentCount != nSegments) { + int[] tmp = new int [segmentCount]; + System.arraycopy(invalidOffsets, 0, tmp, 0, segmentCount); + invalidOffsets = tmp; + } + return new String(newChars, 0, Math.min(charCount + segmentCount, newChars.length)); +} + +/** + * Returns the line spacing of the receiver. + * + * @return the line spacing + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int getSpacing () { + checkLayout(); + return spacing; +} + +/** + * Gets the style of the receiver at the specified character offset. + * + * @param offset the text offset + * @return the style or <code>null</code> if not set + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the character offset is out of range</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public TextStyle getStyle (int offset) { + checkLayout(); + int length = text.length(); + if (!(0 <= offset && offset < length)) SWT.error(SWT.ERROR_INVALID_RANGE); + for (int i=1; i<styles.length; i++) { + StyleItem item = styles[i]; + if (item.start > offset) { + return styles[i - 1].style; + } + } + return null; +} + +/** + * Gets all styles of the receiver. + * + * @return the styles + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #getRanges() + * + * @since 3.2 + */ +public TextStyle[] getStyles () { + checkLayout(); + TextStyle[] result = new TextStyle[styles.length]; + int count = 0; + for (int i=0; i<styles.length; i++) { + if (styles[i].style != null) { + result[count++] = styles[i].style; + } + } + if (count != result.length) { + TextStyle[] newResult = new TextStyle[count]; + System.arraycopy(result, 0, newResult, 0, count); + result = newResult; + } + return result; +} + +/** + * Returns the tab list of the receiver. + * + * @return the tab list + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int[] getTabs() { + checkLayout(); + return tabs; +} + +/** + * Gets the receiver's text, which will be an empty + * string if it has never been set. + * + * @return the receiver's text + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public String getText () { + checkLayout (); + return text; +} + +/** + * Returns the width of the receiver. + * + * @return the width + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int getWidth () { + checkLayout(); + return wrapWidth; +} + +/** + * Returns <code>true</code> if the text layout has been disposed, + * and <code>false</code> otherwise. + * <p> + * This method gets the dispose state for the text layout. + * When a text layout has been disposed, it is an error to + * invoke any other method using the text layout. + * </p> + * + * @return <code>true</code> when the text layout is disposed and <code>false</code> otherwise + */ +public boolean isDisposed () { + return device == null; +} + +/* + * Returns true if the underline style is supported natively + */ +boolean isUnderlineSupported (TextStyle style) { + if (style != null && style.underline) { + int uStyle = style.underlineStyle; + return uStyle == SWT.UNDERLINE_SINGLE || uStyle == SWT.UNDERLINE_DOUBLE || uStyle == SWT.UNDERLINE_LINK || uStyle == UNDERLINE_THICK; + } + return false; +} + +/** + * Sets the text alignment for the receiver. The alignment controls + * how a line of text is positioned horizontally. The argument should + * be one of <code>SWT.LEFT</code>, <code>SWT.RIGHT</code> or <code>SWT.CENTER</code>. + * <p> + * The default alignment is <code>SWT.LEFT</code>. Note that the receiver's + * width must be set in order to use <code>SWT.RIGHT</code> or <code>SWT.CENTER</code> + * alignment. + * </p> + * + * @param alignment the new alignment + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #setWidth(int) + */ +public void setAlignment (int alignment) { + checkLayout(); + int mask = SWT.LEFT | SWT.CENTER | SWT.RIGHT; + alignment &= mask; + if (alignment == 0) return; + if ((alignment & SWT.LEFT) != 0) alignment = SWT.LEFT; + if ((alignment & SWT.RIGHT) != 0) alignment = SWT.RIGHT; + if (this.alignment == alignment) return; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.alignment = alignment; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the ascent of the receiver. The ascent is distance in pixels + * from the baseline to the top of the line and it is applied to all + * lines. The default value is <code>-1</code> which means that the + * ascent is calculated from the line fonts. + * + * @param ascent the new ascent + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the ascent is less than <code>-1</code></li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #setDescent(int) + * @see #getLineMetrics(int) + */ +public void setAscent (int ascent) { + checkLayout (); + if (ascent < -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (this.ascent == ascent) return; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.ascent = ascent; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the descent of the receiver. The descent is distance in pixels + * from the baseline to the bottom of the line and it is applied to all + * lines. The default value is <code>-1</code> which means that the + * descent is calculated from the line fonts. + * + * @param descent the new descent + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the descent is less than <code>-1</code></li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #setAscent(int) + * @see #getLineMetrics(int) + */ +public void setDescent (int descent) { + checkLayout (); + if (descent < -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (this.descent == descent) return; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.descent = descent; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the default font which will be used by the receiver + * to draw and measure text. If the + * argument is null, then a default font appropriate + * for the platform will be used instead. Note that a text + * style can override the default font. + * + * @param font the new font for the receiver, or null to indicate a default font + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the font has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setFont (Font font) { + checkLayout (); + if (font != null && font.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + Font oldFont = this.font; + if (oldFont == font) return; + this.font = font; + if (oldFont != null && oldFont.equals(font)) return; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the indent of the receiver. This indent it applied of the first line of + * each paragraph. + * + * @param indent new indent + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.2 + */ +public void setIndent (int indent) { + checkLayout (); + if (indent < 0) return; + if (this.indent == indent) return; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.indent = indent; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the justification of the receiver. Note that the receiver's + * width must be set in order to use justification. + * + * @param justify new justify + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.2 + */ +public void setJustify (boolean justify) { + checkLayout (); + if (justify == this.justify) return; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.justify = justify; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the orientation of the receiver, which must be one + * of <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>. + * + * @param orientation new orientation style + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setOrientation(int orientation) { + checkLayout(); + int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT; + orientation &= mask; + if (orientation == 0) return; + if ((orientation & SWT.LEFT_TO_RIGHT) != 0) orientation = SWT.LEFT_TO_RIGHT; + if (this.orientation == orientation) return; + this.orientation = orientation; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the offsets of the receiver's text segments. Text segments are used to + * override the default behaviour of the bidirectional algorithm. + * Bidirectional reordering can happen within a text segment but not + * between two adjacent segments. + * <p> + * Each text segment is determined by two consecutive offsets in the + * <code>segments</code> arrays. The first element of the array should + * always be zero and the last one should always be equals to length of + * the text. + * </p> + * + * @param segments the text segments offset + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setSegments(int[] segments) { + checkLayout(); + if (this.segments == null && segments == null) return; + if (this.segments != null && segments !=null) { + if (this.segments.length == segments.length) { + int i; + for (i = 0; i <segments.length; i++) { + if (this.segments[i] != segments[i]) break; + } + if (i == segments.length) return; + } + } + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.segments = segments; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the line spacing of the receiver. The line spacing + * is the space left between lines. + * + * @param spacing the new line spacing + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the spacing is negative</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setSpacing (int spacing) { + checkLayout(); + if (spacing < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (this.spacing == spacing) return; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.spacing = spacing; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the style of the receiver for the specified range. Styles previously + * set for that range will be overwritten. The start and end offsets are + * inclusive and will be clamped if out of range. + * + * @param style the style + * @param start the start offset + * @param end the end offset + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setStyle (TextStyle style, int start, int end) { + checkLayout(); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + int length = text.length(); + if (length == 0) return; + if (start > end) return; + start = Math.min(Math.max(0, start), length - 1); + end = Math.min(Math.max(0, end), length - 1); + int low = -1; + int high = styles.length; + while (high - low > 1) { + int index = (high + low) / 2; + if (styles[index + 1].start > start) { + high = index; + } else { + low = index; + } + } + if (0 <= high && high < styles.length) { + StyleItem item = styles[high]; + if (item.start == start && styles[high + 1].start - 1 == end) { + if (style == null) { + if (item.style == null) return; + } else { + if (style.equals(item.style)) return; + } + } + } + freeRuns(); + int modifyStart = high; + int modifyEnd = modifyStart; + while (modifyEnd < styles.length) { + if (styles[modifyEnd + 1].start > end) break; + modifyEnd++; + } + if (modifyStart == modifyEnd) { + int styleStart = styles[modifyStart].start; + int styleEnd = styles[modifyEnd + 1].start - 1; + if (styleStart == start && styleEnd == end) { + styles[modifyStart].style = style; + return; + } + if (styleStart != start && styleEnd != end) { + StyleItem[] newStyles = new StyleItem[styles.length + 2]; + System.arraycopy(styles, 0, newStyles, 0, modifyStart + 1); + StyleItem item = new StyleItem(); + item.start = start; + item.style = style; + newStyles[modifyStart + 1] = item; + item = new StyleItem(); + item.start = end + 1; + item.style = styles[modifyStart].style; + newStyles[modifyStart + 2] = item; + System.arraycopy(styles, modifyEnd + 1, newStyles, modifyEnd + 3, styles.length - modifyEnd - 1); + styles = newStyles; + return; + } + } + if (start == styles[modifyStart].start) modifyStart--; + if (end == styles[modifyEnd + 1].start - 1) modifyEnd++; + int newLength = styles.length + 1 - (modifyEnd - modifyStart - 1); + StyleItem[] newStyles = new StyleItem[newLength]; + System.arraycopy(styles, 0, newStyles, 0, modifyStart + 1); + StyleItem item = new StyleItem(); + item.start = start; + item.style = style; + newStyles[modifyStart + 1] = item; + styles[modifyEnd].start = end + 1; + System.arraycopy(styles, modifyEnd, newStyles, modifyStart + 2, styles.length - modifyEnd); + styles = newStyles; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the receiver's tab list. Each value in the tab list specifies + * the space in pixels from the origin of the text layout to the respective + * tab stop. The last tab stop width is repeated continuously. + * + * @param tabs the new tab list + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setTabs(int[] tabs) { + checkLayout(); + if (this.tabs == null && tabs == null) return; + if (this.tabs != null && tabs !=null) { + if (this.tabs.length == tabs.length) { + int i; + for (i = 0; i < tabs.length; i++) { + if (this.tabs[i] != tabs[i]) break; + } + if (i == tabs.length) return; + } + } + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.tabs = tabs; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the receiver's text. + *<p> + * Note: Setting the text also clears all the styles. This method + * returns without doing anything if the new text is the same as + * the current text. + * </p> + * + * @param text the new text + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the text is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setText (String text) { + checkLayout (); + if (text == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (text.equals(this.text)) return; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.text = text; + styles = new StyleItem[2]; + styles[0] = new StyleItem(); + styles[1] = new StyleItem(); + styles[styles.length - 1].start = text.length(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Sets the line width of the receiver, which determines how + * text should be wrapped and aligned. The default value is + * <code>-1</code> which means wrapping is disabled. + * + * @param width the new width + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the width is <code>0</code> or less than <code>-1</code></li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #setAlignment(int) + */ +public void setWidth (int width) { + checkLayout(); + if (width < -1 || width == 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (this.wrapWidth == width) return; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.wrapWidth = width; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the receiver + */ +public String toString () { + if (isDisposed()) return "TextLayout {*DISPOSED*}"; + return "TextLayout {" + text + "}"; +} + +/* + * Translate a client offset to an internal offset + */ +int translateOffset (int offset) { + int length = text.length(); + if (length == 0) return offset; + if (invalidOffsets == null) return offset; + for (int i = 0; i < invalidOffsets.length; i++) { + if (offset < invalidOffsets[i]) break; + offset++; + } + return offset; +} + +/* + * Translate an internal offset to a client offset + */ +int untranslateOffset (int offset) { + int length = text.length(); + if (length == 0) return offset; + if (invalidOffsets == null) return offset; + for (int i = 0; i < invalidOffsets.length; i++) { + if (offset == invalidOffsets[i]) { + offset++; + continue; + } + if (offset < invalidOffsets[i]) { + return offset - i; + } + } + return offset - invalidOffsets.length; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Transform.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Transform.java new file mode 100755 index 0000000000..b4eee32e72 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Transform.java @@ -0,0 +1,477 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.graphics; + +import org.eclipse.swt.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class represent transformation matrices for + * points expressed as (x, y) pairs of floating point numbers. + * <p> + * Application code must explicitly invoke the <code>Transform.dispose()</code> + * method to release the operating system resources managed by each instance + * when those instances are no longer required. + * </p> + * <p> + * This class requires the operating system's advanced graphics subsystem + * which may not be available on some platforms. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: GraphicsExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.1 + */ +public class Transform extends Resource { + /** + * the OS resource for the Transform + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public NSAffineTransform handle; + +/** + * Constructs a new identity Transform. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param device the device on which to allocate the Transform + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle for the Transform could not be obtained</li> + * </ul> + * + * @see #dispose() + */ +public Transform (Device device) { + this(device, 1, 0, 0, 1, 0, 0); +} + +/** + * Constructs a new Transform given an array of elements that represent the + * matrix that describes the transformation. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param device the device on which to allocate the Transform + * @param elements an array of floats that describe the transformation matrix + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device, or the elements array is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the elements array is too small to hold the matrix values</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle for the Transform could not be obtained</li> + * </ul> + * + * @see #dispose() + */ +public Transform(Device device, float[] elements) { + this (device, checkTransform(elements)[0], elements[1], elements[2], elements[3], elements[4], elements[5]); +} + +/** + * Constructs a new Transform given all of the elements that represent the + * matrix that describes the transformation. + * <p> + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + * </p> + * + * @param device the device on which to allocate the Transform + * @param m11 the first element of the first row of the matrix + * @param m12 the second element of the first row of the matrix + * @param m21 the first element of the second row of the matrix + * @param m22 the second element of the second row of the matrix + * @param dx the third element of the first row of the matrix + * @param dy the third element of the second row of the matrix + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle for the Transform could not be obtained</li> + * </ul> + * + * @see #dispose() + */ +public Transform (Device device, float m11, float m12, float m21, float m22, float dx, float dy) { + super(device); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + handle = NSAffineTransform.transform(); + if (handle == null) SWT.error(SWT.ERROR_NO_HANDLES); + handle.retain(); + setElements(m11, m12, m21, m22, dx, dy); + init(); + } finally { + if (pool != null) pool.release(); + } +} + +static float[] checkTransform(float[] elements) { + if (elements == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (elements.length < 6) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + return elements; +} + +void destroy() { + handle.release(); + handle = null; +} + +/** + * Fills the parameter with the values of the transformation matrix + * that the receiver represents, in the order {m11, m12, m21, m22, dx, dy}. + * + * @param elements array to hold the matrix values + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parameter is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the parameter is too small to hold the matrix values</li> + * </ul> + */ +public void getElements(float[] elements) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (elements == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (elements.length < 6) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSAffineTransformStruct struct = handle.transformStruct(); + elements[0] = (float)/*64*/struct.m11; + elements[1] = (float)/*64*/struct.m12; + elements[2] = (float)/*64*/struct.m21; + elements[3] = (float)/*64*/struct.m22; + elements[4] = (float)/*64*/struct.tX; + elements[5] = (float)/*64*/struct.tY; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Modifies the receiver such that the matrix it represents becomes the + * identity matrix. + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.4 + */ +public void identity() { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSAffineTransformStruct struct = new NSAffineTransformStruct(); + struct.m11 = 1; + struct.m22 = 1; + handle.setTransformStruct(struct); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Modifies the receiver such that the matrix it represents becomes + * the mathematical inverse of the matrix it previously represented. + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_CANNOT_INVERT_MATRIX - if the matrix is not invertible</li> + * </ul> + */ +public void invert() { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSAffineTransformStruct struct = handle.transformStruct(); + if ((struct.m11 * struct.m22 - struct.m12 * struct.m21) == 0) { + SWT.error(SWT.ERROR_CANNOT_INVERT_MATRIX); + } + handle.invert(); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns <code>true</code> if the Transform has been disposed, + * and <code>false</code> otherwise. + * <p> + * This method gets the dispose state for the Transform. + * When a Transform has been disposed, it is an error to + * invoke any other method using the Transform. + * + * @return <code>true</code> when the Transform is disposed, and <code>false</code> otherwise + */ +public boolean isDisposed() { + return handle == null; +} + +/** + * Returns <code>true</code> if the Transform represents the identity matrix + * and false otherwise. + * + * @return <code>true</code> if the receiver is an identity Transform, and <code>false</code> otherwise + */ +public boolean isIdentity() { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSAffineTransformStruct struct = handle.transformStruct(); + return struct.m11 == 1 && struct.m12 == 0 && struct.m21 == 0 && struct.m22 == 1 && struct.tX == 0 && struct.tY == 0; + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Modifies the receiver such that the matrix it represents becomes the + * the result of multiplying the matrix it previously represented by the + * argument. + * + * @param matrix the matrix to multiply the receiver by + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parameter is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li> + * </ul> + */ +public void multiply(Transform matrix) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (matrix == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (matrix.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + handle.prependTransform(matrix.handle); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Modifies the receiver so that it represents a transformation that is + * equivalent to its previous transformation rotated by the specified angle. + * The angle is specified in degrees and for the identity transform 0 degrees + * is at the 3 o'clock position. A positive value indicates a clockwise rotation + * while a negative value indicates a counter-clockwise rotation. + * + * @param angle the angle to rotate the transformation by + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void rotate(float angle) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + handle.rotateByDegrees(angle); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Modifies the receiver so that it represents a transformation that is + * equivalent to its previous transformation scaled by (scaleX, scaleY). + * + * @param scaleX the amount to scale in the X direction + * @param scaleY the amount to scale in the Y direction + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void scale(float scaleX, float scaleY) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + handle.scaleXBy(scaleX, scaleY); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Modifies the receiver to represent a new transformation given all of + * the elements that represent the matrix that describes that transformation. + * + * @param m11 the first element of the first row of the matrix + * @param m12 the second element of the first row of the matrix + * @param m21 the first element of the second row of the matrix + * @param m22 the second element of the second row of the matrix + * @param dx the third element of the first row of the matrix + * @param dy the third element of the second row of the matrix + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void setElements(float m11, float m12, float m21, float m22, float dx, float dy) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSAffineTransformStruct struct = new NSAffineTransformStruct(); + struct.m11 = m11; + struct.m12 = m12; + struct.m21 = m21; + struct.m22 = m22; + struct.tX = dx; + struct.tY = dy; + handle.setTransformStruct(struct); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Modifies the receiver so that it represents a transformation that is + * equivalent to its previous transformation sheared by (shearX, shearY). + * + * @param shearX the shear factor in the X direction + * @param shearY the shear factor in the Y direction + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.4 + */ +public void shear(float shearX, float shearY) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSAffineTransformStruct struct = new NSAffineTransformStruct(); + struct.m11 = 1; + struct.m12 = shearX; + struct.m21 = shearY; + struct.m22 = 1; + NSAffineTransform matrix = NSAffineTransform.transform(); + matrix.setTransformStruct(struct); + handle.prependTransform(matrix); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Given an array containing points described by alternating x and y values, + * modify that array such that each point has been replaced with the result of + * applying the transformation represented by the receiver to that point. + * + * @param pointArray an array of alternating x and y values to be transformed + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the point array is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void transform(float[] pointArray) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (pointArray == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + NSPoint point = new NSPoint(); + int length = pointArray.length / 2; + for (int i = 0, j = 0; i < length; i++, j += 2) { + point.x = pointArray[j]; + point.y = pointArray[j + 1]; + point = handle.transformPoint(point); + pointArray[j] = (float)/*64*/point.x; + pointArray[j + 1] = (float)/*64*/point.y; + } + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Modifies the receiver so that it represents a transformation that is + * equivalent to its previous transformation translated by (offsetX, offsetY). + * + * @param offsetX the distance to translate in the X direction + * @param offsetY the distance to translate in the Y direction + * + * @exception SWTException <ul> + * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void translate(float offsetX, float offsetY) { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + handle.translateXBy(offsetX, offsetY); + } finally { + if (pool != null) pool.release(); + } +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the receiver + */ +public String toString() { + if (isDisposed()) return "Transform {*DISPOSED*}"; + float[] elements = new float[6]; + getElements(elements); + return "Transform {" + elements [0] + ", " + elements [1] + ", " +elements [2] + ", " +elements [3] + ", " +elements [4] + ", " +elements [5] + "}"; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Button.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Button.java new file mode 100755 index 0000000000..c6386e25a4 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Button.java @@ -0,0 +1,846 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.*; +import org.eclipse.swt.accessibility.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class represent a selectable user interface object that + * issues notification when pressed and released. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>ARROW, CHECK, PUSH, RADIO, TOGGLE, FLAT</dd> + * <dd>UP, DOWN, LEFT, RIGHT, CENTER</dd> + * <dt><b>Events:</b></dt> + * <dd>Selection</dd> + * </dl> + * <p> + * Note: Only one of the styles ARROW, CHECK, PUSH, RADIO, and TOGGLE + * may be specified. + * </p><p> + * Note: Only one of the styles LEFT, RIGHT, and CENTER may be specified. + * </p><p> + * Note: Only one of the styles UP, DOWN, LEFT, and RIGHT may be specified + * when the ARROW style is specified. + * </p><p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the SWT implementation. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#button">Button snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Button extends Control { + String text; + Image image; + boolean grayed; + + static final int EXTRA_HEIGHT = 2; + static final int EXTRA_WIDTH = 6; + static final int IMAGE_GAP = 2; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#ARROW + * @see SWT#CHECK + * @see SWT#PUSH + * @see SWT#RADIO + * @see SWT#TOGGLE + * @see SWT#FLAT + * @see SWT#UP + * @see SWT#DOWN + * @see SWT#LEFT + * @see SWT#RIGHT + * @see SWT#CENTER + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Button (Composite parent, int style) { + super (parent, checkStyle (style)); +} + +int /*long*/ accessibilityAttributeValue (int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + NSString nsAttributeName = new NSString(arg0); + + if (accessible != null) { + id returnObject = accessible.internal_accessibilityAttributeValue(nsAttributeName, ACC.CHILDID_SELF); + if (returnObject != null) return returnObject.id; + } + + if (nsAttributeName.isEqualToString (OS.NSAccessibilityRoleAttribute) || nsAttributeName.isEqualToString (OS.NSAccessibilityRoleDescriptionAttribute)) { + NSString role = null; + + if ((style & SWT.RADIO) != 0) { + role = OS.NSAccessibilityRadioButtonRole; + } else if ((style & SWT.ARROW) != 0) { + role = OS.NSAccessibilityButtonRole; + } + + if (role != null) { + if (nsAttributeName.isEqualToString (OS.NSAccessibilityRoleAttribute)) + return role.id; + else { + return OS.NSAccessibilityRoleDescription(role.id, 0); + } + } + } + + return super.accessibilityAttributeValue(id, sel, arg0); +} + + +/** + * Adds the listener to the collection of listeners who will + * be notified when the control is selected by the user, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * <code>widgetSelected</code> is called when the control is selected by the user. + * <code>widgetDefaultSelected</code> is not called. + * </p> + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener(listener); + addListener(SWT.Selection,typedListener); + addListener(SWT.DefaultSelection,typedListener); +} + +NSSize cellSize (int /*long*/ id, int /*long*/ sel) { + NSSize size = super.cellSize(id, sel); + if (image != null && ((style & (SWT.CHECK|SWT.RADIO)) !=0)) { + NSSize imageSize = image.handle.size(); + size.width += imageSize.width + IMAGE_GAP; + size.height = Math.max(size.height, imageSize.height); + } + return size; +} + +static int checkStyle (int style) { + style = checkBits (style, SWT.PUSH, SWT.ARROW, SWT.CHECK, SWT.RADIO, SWT.TOGGLE, 0); + if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) { + return checkBits (style, SWT.CENTER, SWT.LEFT, SWT.RIGHT, 0, 0, 0); + } + if ((style & (SWT.CHECK | SWT.RADIO)) != 0) { + return checkBits (style, SWT.LEFT, SWT.RIGHT, SWT.CENTER, 0, 0, 0); + } + if ((style & SWT.ARROW) != 0) { + style |= SWT.NO_FOCUS; + return checkBits (style, SWT.UP, SWT.DOWN, SWT.LEFT, SWT.RIGHT, 0, 0); + } + return style; +} + +void click () { + postEvent (SWT.Selection); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget(); + if ((style & SWT.ARROW) != 0) { + // TODO use some OS metric instead of hardcoded values + int width = wHint != SWT.DEFAULT ? wHint : 14; + int height = hHint != SWT.DEFAULT ? hHint : 14; + return new Point (width, height); + } + NSSize size = ((NSButton)view).cell ().cellSize (); + int width = (int)Math.ceil (size.width); + int height = (int)Math.ceil (size.height); + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0 && (style & SWT.FLAT) == 0) { + if (display.smallFonts) height += EXTRA_HEIGHT; + width += EXTRA_WIDTH; + } + return new Point (width, height); +} + +NSAttributedString createString() { + NSAttributedString attribStr = createString(text, null, foreground, style, true, true); + attribStr.autorelease(); + return attribStr; +} + +void createHandle () { + if ((style & SWT.PUSH) == 0) state |= THEME_BACKGROUND; + NSButton widget = (NSButton)new SWTButton().alloc(); + widget.init(); + /* + * Feature in Cocoa. Images touch the edge of rounded buttons + * when set to small size. The fix to subclass the button cell + * and offset the image drawing. + */ +// if (display.smallFonts && (style & (SWT.PUSH | SWT.TOGGLE)) != 0 && (style & SWT.FLAT) == 0) { + NSButtonCell cell = (NSButtonCell)new SWTButtonCell ().alloc ().init (); + widget.setCell (cell); + cell.release (); +// } + int type = OS.NSMomentaryLightButton; + if ((style & SWT.PUSH) != 0) { + if ((style & SWT.FLAT) != 0) { + widget.setBezelStyle(OS.NSShadowlessSquareBezelStyle); +// if ((style & SWT.BORDER) == 0) widget.setShowsBorderOnlyWhileMouseInside(true); + } else { + widget.setBezelStyle(OS.NSRoundedBezelStyle); + } + } else if ((style & SWT.CHECK) != 0) { + type = OS.NSSwitchButton; + } else if ((style & SWT.RADIO) != 0) { + type = OS.NSRadioButton; + } else if ((style & SWT.TOGGLE) != 0) { + type = OS.NSPushOnPushOffButton; + if ((style & SWT.FLAT) != 0) { + widget.setBezelStyle(OS.NSShadowlessSquareBezelStyle); +// if ((style & SWT.BORDER) == 0) widget.setShowsBorderOnlyWhileMouseInside(true); + } else { + widget.setBezelStyle(OS.NSRoundedBezelStyle); + } + } else if ((style & SWT.ARROW) != 0) { + widget.setBezelStyle(OS.NSShadowlessSquareBezelStyle); + } + widget.setButtonType(type); + widget.setTitle(NSString.stringWith("")); + widget.setImagePosition(OS.NSImageLeft); + widget.setTarget(widget); + widget.setAction(OS.sel_sendSelection); + view = widget; + _setAlignment(style); +} + +void createWidget() { + text = ""; + super.createWidget (); +} + +NSFont defaultNSFont() { + return display.buttonFont; +} + +void deregister () { + super.deregister (); + display.removeWidget(((NSControl)view).cell()); +} + +boolean dragDetect(int x, int y, boolean filter, boolean[] consume) { + boolean dragging = super.dragDetect(x, y, filter, consume); + consume[0] = dragging; + return dragging; +} + +void drawImageWithFrameInView (int /*long*/ id, int /*long*/ sel, int /*long*/ image, NSRect rect, int /*long*/ view) { + /* + * Feature in Cocoa. Images touch the edge of rounded buttons + * when set to small size. The fix to subclass the button cell + * and offset the image drawing. + */ + if (display.smallFonts && (style & (SWT.PUSH | SWT.TOGGLE)) != 0 && (style & SWT.FLAT) == 0) { + rect.y += EXTRA_HEIGHT / 2; + rect.height += EXTRA_HEIGHT; + } + callSuper (id, sel, image, rect, view); +} + +void drawInteriorWithFrame_inView (int /*long*/ id, int /*long*/ sel, NSRect cellRect, int /*long*/ viewid) { + super.drawInteriorWithFrame_inView(id, sel, cellRect, viewid); + if (image != null && ((style & (SWT.CHECK|SWT.RADIO)) !=0)) { + NSSize imageSize = image.handle.size(); + NSCell nsCell = new NSCell(id); + float /*double*/ x = 0; + float /*double*/ y = (imageSize.height - cellRect.height)/2f; + NSRect imageRect = nsCell.imageRectForBounds(cellRect); + NSSize stringSize = ((NSButton)view).attributedTitle().size(); + switch (style & (SWT.LEFT|SWT.RIGHT|SWT.CENTER)) { + case SWT.LEFT: + x = imageRect.x + imageRect.width + IMAGE_GAP; + break; + case SWT.CENTER: + x = cellRect.x + imageRect.x + imageRect.width + ((cellRect.width-stringSize.width)/2f) - imageSize.width - IMAGE_GAP; + break; + case SWT.RIGHT: + x = cellRect.x + cellRect.width - stringSize.width - imageSize.width - IMAGE_GAP; + break; + } + NSRect destRect = new NSRect(); + destRect.x = x; + destRect.y = y; + destRect.width = imageSize.width; + destRect.height = imageSize.height; + NSGraphicsContext.static_saveGraphicsState(); + NSAffineTransform transform = NSAffineTransform.transform(); + transform.scaleXBy(1, -1); + transform.translateXBy(0, -imageSize.height); + transform.concat(); + image.handle.drawInRect(destRect, new NSRect(), OS.NSCompositeSourceOver, 1); + NSGraphicsContext.static_restoreGraphicsState(); + } + +} + +void drawWidget (int /*long*/ id, NSGraphicsContext context, NSRect rect) { + if ((style & SWT.ARROW) != 0) { + NSRect frame = view.frame(); + int arrowSize = Math.min((int)frame.height, (int)frame.width) / 2; + context.saveGraphicsState(); + NSPoint p1 = new NSPoint(); + p1.x = -arrowSize / 2; + p1.y = -arrowSize / 2; + NSPoint p2 = new NSPoint(); + p2.x = arrowSize / 2; + p2.y = p1.y; + NSPoint p3 = new NSPoint(); + p3.y = arrowSize / 2; + + NSBezierPath path = NSBezierPath.bezierPath(); + path.moveToPoint(p1); + path.lineToPoint(p2); + path.lineToPoint(p3); + path.closePath(); + + NSAffineTransform transform = NSAffineTransform.transform(); + if ((style & SWT.LEFT) != 0) { + transform.rotateByDegrees(90); + } else if ((style & SWT.UP) != 0) { + transform.rotateByDegrees(180); + } else if ((style & SWT.RIGHT) != 0) { + transform.rotateByDegrees(-90); + } + path.transformUsingAffineTransform(transform); + transform = NSAffineTransform.transform(); + transform.translateXBy(frame.width / 2, frame.height / 2); + path.transformUsingAffineTransform(transform); + + NSColor color = isEnabled() ? NSColor.blackColor() : NSColor.disabledControlTextColor(); + color.set(); + path.fill(); + context.restoreGraphicsState(); + } + super.drawWidget (id, context, rect); +} + +/** + * Returns a value which describes the position of the + * text or image in the receiver. The value will be one of + * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code> + * unless the receiver is an <code>ARROW</code> button, in + * which case, the alignment will indicate the direction of + * the arrow (one of <code>LEFT</code>, <code>RIGHT</code>, + * <code>UP</code> or <code>DOWN</code>). + * + * @return the alignment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getAlignment () { + checkWidget (); + if ((style & SWT.ARROW) != 0) { + if ((style & SWT.UP) != 0) return SWT.UP; + if ((style & SWT.DOWN) != 0) return SWT.DOWN; + if ((style & SWT.LEFT) != 0) return SWT.LEFT; + if ((style & SWT.RIGHT) != 0) return SWT.RIGHT; + return SWT.UP; + } + if ((style & SWT.LEFT) != 0) return SWT.LEFT; + if ((style & SWT.CENTER) != 0) return SWT.CENTER; + if ((style & SWT.RIGHT) != 0) return SWT.RIGHT; + return SWT.LEFT; +} + +/** + * Returns <code>true</code> if the receiver is grayed, + * and false otherwise. When the widget does not have + * the <code>CHECK</code> style, return false. + * + * @return the grayed state of the checkbox + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public boolean getGrayed() { + checkWidget (); + if ((style & SWT.CHECK) == 0) return false; + return grayed; +} + +/** + * Returns the receiver's image if it has one, or null + * if it does not. + * + * @return the receiver's image + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Image getImage () { + checkWidget(); + return image; +} + +String getNameText () { + return getText (); +} + +/** + * Returns <code>true</code> if the receiver is selected, + * and false otherwise. + * <p> + * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>, + * it is selected when it is checked. When it is of type <code>TOGGLE</code>, + * it is selected when it is pushed in. If the receiver is of any other type, + * this method returns false. + * + * @return the selection state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getSelection () { + checkWidget (); + if ((style & (SWT.CHECK | SWT.RADIO | SWT.TOGGLE)) == 0) return false; + if ((style & SWT.CHECK) != 0 && grayed) return ((NSButton)view).state() == OS.NSMixedState; + return ((NSButton)view).state() == OS.NSOnState; +} + +/** + * Returns the receiver's text, which will be an empty + * string if it has never been set or if the receiver is + * an <code>ARROW</code> button. + * + * @return the receiver's text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getText () { + checkWidget (); + return text; +} + +boolean isDescribedByLabel () { + return false; +} + +/* + * Feature in Cocoa. If a checkbox is in multi-state mode, nextState cycles from off to mixed to on and back to off again. + * This will cause the on state to momentarily appear while clicking on the checkbox. To avoid this, we override [NSCell nextState] + * to go directly to the desired state if we have a grayed checkbox. + */ +int /*long*/ nextState(int /*long*/ id, int /*long*/ sel) { + if ((style & SWT.CHECK) != 0 && grayed) { + return ((NSButton)view).state() == OS.NSMixedState ? OS.NSOffState : OS.NSMixedState; + } + + return super.nextState(id, sel); +} + +void register() { + super.register(); + display.addWidget(((NSControl)view).cell(), this); +} + +void releaseWidget () { + super.releaseWidget (); + image = null; + text = null; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is selected by the user. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook(SWT.Selection, listener); + eventTable.unhook(SWT.DefaultSelection,listener); +} + +void selectRadio () { + /* + * This code is intentionally commented. When two groups + * of radio buttons with the same parent are separated by + * another control, the correct behavior should be that + * the two groups act independently. This is consistent + * with radio tool and menu items. The commented code + * implements this behavior. + */ +// int index = 0; +// Control [] children = parent._getChildren (); +// while (index < children.length && children [index] != this) index++; +// int i = index - 1; +// while (i >= 0 && children [i].setRadioSelection (false)) --i; +// int j = index + 1; +// while (j < children.length && children [j].setRadioSelection (false)) j++; +// setSelection (true); + Control [] children = parent._getChildren (); + for (int i=0; i<children.length; i++) { + Control child = children [i]; + if (this != child) child.setRadioSelection (false); + } + setSelection (true); +} + +void sendSelection () { + if ((style & SWT.RADIO) != 0) { + if ((parent.getStyle () & SWT.NO_RADIO_GROUP) == 0) { + selectRadio (); + } + } + if ((style & SWT.CHECK) != 0) { + if (grayed && ((NSButton)view).state() == OS.NSOnState) { + ((NSButton)view).setState(OS.NSOffState); + } + if (!grayed && ((NSButton)view).state() == OS.NSMixedState) { + ((NSButton)view).setState(OS.NSOnState); + } + } + postEvent (SWT.Selection); +} + + +/** + * Controls how text, images and arrows will be displayed + * in the receiver. The argument should be one of + * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code> + * unless the receiver is an <code>ARROW</code> button, in + * which case, the argument indicates the direction of + * the arrow (one of <code>LEFT</code>, <code>RIGHT</code>, + * <code>UP</code> or <code>DOWN</code>). + * + * @param alignment the new alignment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setAlignment (int alignment) { + checkWidget (); + _setAlignment (alignment); + redraw (); +} + +void _setAlignment (int alignment) { + if ((style & SWT.ARROW) != 0) { + if ((style & (SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT)) == 0) return; + style &= ~(SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT); + style |= alignment & (SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT); +// int orientation = OS.kThemeDisclosureRight; +// if ((style & SWT.UP) != 0) orientation = OS.kThemeDisclosureUp; +// if ((style & SWT.DOWN) != 0) orientation = OS.kThemeDisclosureDown; +// if ((style & SWT.LEFT) != 0) orientation = OS.kThemeDisclosureLeft; +// OS.SetControl32BitValue (handle, orientation); + return; + } + if ((alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER)) == 0) return; + style &= ~(SWT.LEFT | SWT.RIGHT | SWT.CENTER); + style |= alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER); + /* text is still null when this is called from createHandle() */ + if (text != null) { + ((NSButton)view).setAttributedTitle(createString()); + } +// /* Alignment not honoured when image and text is visible */ +// boolean bothVisible = text != null && text.length () > 0 && image != null; +// if (bothVisible) { +// if ((style & (SWT.RADIO | SWT.CHECK)) != 0) alignment = SWT.LEFT; +// if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) alignment = SWT.CENTER; +// } +// int textAlignment = 0; +// int graphicAlignment = 0; +// if ((alignment & SWT.LEFT) != 0) { +// textAlignment = OS.kControlBevelButtonAlignTextFlushLeft; +// graphicAlignment = OS.kControlBevelButtonAlignLeft; +// } +// if ((alignment & SWT.CENTER) != 0) { +// textAlignment = OS.kControlBevelButtonAlignTextCenter; +// graphicAlignment = OS.kControlBevelButtonAlignCenter; +// } +// if ((alignment & SWT.RIGHT) != 0) { +// textAlignment = OS.kControlBevelButtonAlignTextFlushRight; +// graphicAlignment = OS.kControlBevelButtonAlignRight; +// } +// OS.SetControlData (handle, OS.kControlEntireControl, OS.kControlBevelButtonTextAlignTag, 2, new short [] {(short)textAlignment}); +// OS.SetControlData (handle, OS.kControlEntireControl, OS.kControlBevelButtonGraphicAlignTag, 2, new short [] {(short)graphicAlignment}); +// if (bothVisible) { +// OS.SetControlData (handle, OS.kControlEntireControl, OS.kControlBevelButtonTextPlaceTag, 2, new short [] {(short)OS.kControlBevelButtonPlaceToRightOfGraphic}); +// } +} + +void updateBackground () { + NSColor nsColor = null; + if (backgroundImage != null) { + nsColor = NSColor.colorWithPatternImage(backgroundImage.handle); + } else if (background != null) { + nsColor = NSColor.colorWithDeviceRed(background[0], background[1], background[2], background[3]); + } else { + return; // TODO set to OS default + } + NSButtonCell cell = new NSButtonCell(((NSButton)view).cell()); + cell.setBackgroundColor(nsColor); +} + +void setFont (NSFont font) { + if (text != null) { + ((NSButton)view).setAttributedTitle(createString()); + } +} + +void setForeground (float /*double*/ [] color) { + ((NSButton)view).setAttributedTitle(createString()); +} + +/** + * Sets the grayed state of the receiver. This state change + * only applies if the control was created with the SWT.CHECK + * style. + * + * @param grayed the new grayed state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public void setGrayed(boolean grayed) { + checkWidget (); + if ((style & SWT.CHECK) == 0) return; + boolean checked = getSelection (); + this.grayed = grayed; + ((NSButton) view).setAllowsMixedState(grayed); + + if (checked) { + if (grayed) { + ((NSButton) view).setState (OS.NSMixedState); + } else { + ((NSButton) view).setState (OS.NSOnState); + } + } +} + +/** + * Sets the receiver's image to the argument, which may be + * <code>null</code> indicating that no image should be displayed. + * <p> + * Note that a Button can display an image and text simultaneously + * on Windows (starting with XP), GTK+ and OSX. On other platforms, + * a Button that has an image and text set into it will display the + * image or text that was set most recently. + * </p> + * @param image the image to display on the receiver (may be <code>null</code>) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setImage (Image image) { + checkWidget(); + if (image != null && image.isDisposed ()) { + error (SWT.ERROR_INVALID_ARGUMENT); + } + if ((style & SWT.ARROW) != 0) return; + this.image = image; + if ((style & (SWT.RADIO|SWT.CHECK)) == 0) { + /* + * Feature in Cocoa. If the NSImage object being set into the button is + * the same NSImage object that is already there then the button does not + * redraw itself. This results in the button's image not visually updating + * if the NSImage object's content has changed since it was last set + * into the button. The workaround is to explicitly redraw the button. + */ + ((NSButton)view).setImage(image != null ? image.handle : null); + view.setNeedsDisplay(true); + } else { + ((NSButton)view).setAttributedTitle(createString()); + } + updateAlignment (); +} + +boolean setRadioSelection (boolean value){ + if ((style & SWT.RADIO) == 0) return false; + if (getSelection () != value) { + setSelection (value); + postEvent (SWT.Selection); + } + return true; +} + +/** + * Sets the selection state of the receiver, if it is of type <code>CHECK</code>, + * <code>RADIO</code>, or <code>TOGGLE</code>. + * + * <p> + * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>, + * it is selected when it is checked. When it is of type <code>TOGGLE</code>, + * it is selected when it is pushed in. + * + * @param selected the new selection state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSelection (boolean selected) { + checkWidget(); + if ((style & (SWT.CHECK | SWT.RADIO | SWT.TOGGLE)) == 0) return; + if (grayed) { + ((NSButton)view).setState (selected ? OS.NSMixedState : OS.NSOffState); + } else { + ((NSButton)view).setState (selected ? OS.NSOnState : OS.NSOffState); + } +} + +/** + * Sets the receiver's text. + * <p> + * This method sets the button label. The label may include + * the mnemonic character but must not contain line delimiters. + * </p> + * <p> + * Mnemonics are indicated by an '&' that causes the next + * character to be the mnemonic. When the user presses a + * key sequence that matches the mnemonic, a selection + * event occurs. On most platforms, the mnemonic appears + * underlined but may be emphasized in a platform specific + * manner. The mnemonic indicator character '&' can be + * escaped by doubling it in the string, causing a single + * '&' to be displayed. + * </p><p> + * Note that a Button can display an image and text simultaneously + * on Windows (starting with XP), GTK+ and OSX. On other platforms, + * a Button that has an image and text set into it will display the + * image or text that was set most recently. + * </p> + * @param string the new text + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the text is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setText (String string) { + checkWidget(); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if ((style & SWT.ARROW) != 0) return; + text = string; + ((NSButton)view).setAttributedTitle(createString()); + updateAlignment (); +} + +NSRect titleRectForBounds (int /*long*/ id, int /*long*/ sel, NSRect cellFrame) { + NSRect rect = super.titleRectForBounds(id, sel, cellFrame); + if (image != null && ((style & (SWT.CHECK|SWT.RADIO)) !=0)) { + NSSize imageSize = image.handle.size(); + rect.x += imageSize.width + IMAGE_GAP; + rect.width -= (imageSize.width + IMAGE_GAP); + rect.width = Math.max(0f, rect.width); + } + return rect; +} + +int traversalCode (int key, NSEvent theEvent) { + int code = super.traversalCode (key, theEvent); + if ((style & SWT.ARROW) != 0) code &= ~(SWT.TRAVERSE_TAB_NEXT | SWT.TRAVERSE_TAB_PREVIOUS); + if ((style & SWT.RADIO) != 0) code |= SWT.TRAVERSE_ARROW_NEXT | SWT.TRAVERSE_ARROW_PREVIOUS; + return code; +} + +void updateAlignment () { + NSButton widget = (NSButton)view; + if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) { + if (text.length() != 0 && image != null) { + widget.setImagePosition(OS.NSImageLeft); + } else { + widget.setImagePosition(text.length() != 0 ? OS.NSNoImage : OS.NSImageOnly); + } + } +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Canvas.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Canvas.java new file mode 100755 index 0000000000..97a2300553 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Canvas.java @@ -0,0 +1,512 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class provide a surface for drawing + * arbitrary graphics. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>(none)</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * This class may be subclassed by custom control implementors + * who are building controls that are <em>not</em> constructed + * from aggregates of other controls. That is, they are either + * painted using SWT graphics calls or are handled by native + * methods. + * </p> + * + * @see Composite + * @see <a href="http://www.eclipse.org/swt/snippets/#canvas">Canvas snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + */ +public class Canvas extends Composite { + Caret caret; + IME ime; + NSOpenGLContext context; + +Canvas () { + /* Do nothing */ +} + +int /*long*/ attributedSubstringFromRange (int /*long*/ id, int /*long*/ sel, int /*long*/ range) { + if (ime != null) return ime.attributedSubstringFromRange (id, sel, range); + return super.attributedSubstringFromRange(id, sel, range); +} + +void sendFocusEvent(int type) { + if (caret != null) { + if (type == SWT.FocusIn) { + caret.setFocus(); + } else { + caret.killFocus(); + } + } + super.sendFocusEvent(type); +} + + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * </ul> + * + * @see SWT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Canvas (Composite parent, int style) { + super (parent, style); +} + +int /*long*/ characterIndexForPoint (int /*long*/ id, int /*long*/ sel, int /*long*/ point) { + if (ime != null) return ime.characterIndexForPoint (id, sel, point); + return super.characterIndexForPoint (id, sel, point); +} + +/** + * Fills the interior of the rectangle specified by the arguments, + * with the receiver's background. + * + * @param gc the gc where the rectangle is to be filled + * @param x the x coordinate of the rectangle to be filled + * @param y the y coordinate of the rectangle to be filled + * @param width the width of the rectangle to be filled + * @param height the height of the rectangle to be filled + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the gc is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the gc has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public void drawBackground (GC gc, int x, int y, int width, int height) { + checkWidget (); + if (gc == null) error (SWT.ERROR_NULL_ARGUMENT); + if (gc.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + Control control = findBackgroundControl (); + if (control != null) { + NSRect rect = new NSRect(); + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + int imgHeight = -1; + GCData data = gc.getGCData(); + if (data.image != null) imgHeight = data.image.getBounds().height; + NSGraphicsContext context = gc.handle; + if (data.flippedContext != null) { + NSGraphicsContext.static_saveGraphicsState(); + NSGraphicsContext.setCurrentContext(context); + } + control.fillBackground (view, context, rect, imgHeight); + if (data.flippedContext != null) { + NSGraphicsContext.static_restoreGraphicsState(); + } + } else { + gc.fillRectangle (x, y, width, height); + } +} + +void drawRect (int /*long*/ id, int /*long*/ sel, NSRect rect) { + if (context != null && context.view() == null) context.setView(view); + super.drawRect(id, sel, rect); +} + +void drawWidget (int /*long*/ id, NSGraphicsContext context, NSRect rect) { + if (id != view.id) return; + super.drawWidget (id, context, rect); + if (caret == null) return; + if (caret.isShowing) { + Image image = caret.image; + if (image != null) { + NSImage imageHandle = image.handle; + NSImageRep imageRep = imageHandle.bestRepresentationForDevice(null); + if (!imageRep.isKindOfClass(OS.class_NSBitmapImageRep)) return; + NSBitmapImageRep rep = new NSBitmapImageRep(imageRep); + CGRect destRect = new CGRect (); + destRect.origin.x = caret.x; + destRect.origin.y = caret.y; + NSSize size = imageHandle.size(); + destRect.size.width = size.width; + destRect.size.height = size.height; + int /*long*/ data = rep.bitmapData(); + int /*long*/ bpr = rep.bytesPerRow(); + int alphaInfo = rep.hasAlpha() ? OS.kCGImageAlphaFirst : OS.kCGImageAlphaNoneSkipFirst; + int /*long*/ provider = OS.CGDataProviderCreateWithData(0, data, bpr * (int)size.height, 0); + int /*long*/ colorspace = OS.CGColorSpaceCreateDeviceRGB(); + int /*long*/ cgImage = OS.CGImageCreate((int)size.width, (int)size.height, rep.bitsPerSample(), rep.bitsPerPixel(), bpr, colorspace, alphaInfo, provider, 0, true, 0); + OS.CGColorSpaceRelease(colorspace); + OS.CGDataProviderRelease(provider); + int /*long*/ ctx = context.graphicsPort(); + OS.CGContextSaveGState(ctx); + OS.CGContextScaleCTM (ctx, 1, -1); + OS.CGContextTranslateCTM (ctx, 0, -(size.height + 2 * destRect.origin.y)); + OS.CGContextSetBlendMode (ctx, OS.kCGBlendModeDifference); + OS.CGContextDrawImage (ctx, destRect, cgImage); + OS.CGContextRestoreGState(ctx); + OS.CGImageRelease(cgImage); + } else { + context.saveGraphicsState(); + context.setCompositingOperation(OS.NSCompositeXOR); + NSRect drawRect = new NSRect(); + drawRect.x = caret.x; + drawRect.y = caret.y; + drawRect.width = caret.width != 0 ? caret.width : Caret.DEFAULT_WIDTH; + drawRect.height = caret.height; + context.setShouldAntialias(false); + NSColor color = NSColor.colorWithDeviceRed(1, 1, 1, 1); + color.set(); + NSBezierPath.fillRect(drawRect); + context.restoreGraphicsState(); + } + } +} + +NSRect firstRectForCharacterRange (int /*long*/ id, int /*long*/ sel, int /*long*/ range) { + if (ime != null) return ime.firstRectForCharacterRange (id, sel, range); + return super.firstRectForCharacterRange (id, sel, range); +} + +/** + * Returns the caret. + * <p> + * The caret for the control is automatically hidden + * and shown when the control is painted or resized, + * when focus is gained or lost and when an the control + * is scrolled. To avoid drawing on top of the caret, + * the programmer must hide and show the caret when + * drawing in the window any other time. + * </p> + * + * @return the caret for the receiver, may be null + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Caret getCaret () { + checkWidget(); + return caret; +} + +/** + * Returns the IME. + * + * @return the IME + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public IME getIME () { + checkWidget(); + return ime; +} + +boolean hasMarkedText (int /*long*/ id, int /*long*/ sel) { + if (ime != null) return ime.hasMarkedText (id, sel); + return super.hasMarkedText (id, sel); +} + +boolean imeInComposition () { + return ime != null && ime.isInlineEnabled () && ime.startOffset != -1; +} + +boolean insertText (int /*long*/ id, int /*long*/ sel, int /*long*/ string) { + if (ime != null) { + if (!ime.insertText (id, sel, string)) return false; + } + return super.insertText (id, sel, string); +} + +boolean isOpaque (int /*long*/ id, int /*long*/ sel) { + if (context != null) return true; + return super.isOpaque(id, sel); +} + +NSRange markedRange (int /*long*/ id, int /*long*/ sel) { + if (ime != null) return ime.markedRange (id, sel); + return super.markedRange (id, sel); +} + +void releaseChildren (boolean destroy) { + if (caret != null) { + caret.release (false); + caret = null; + } + if (ime != null) { + ime.release (false); + ime = null; + } + super.releaseChildren (destroy); +} + +/** + * Scrolls a rectangular area of the receiver by first copying + * the source area to the destination and then causing the area + * of the source which is not covered by the destination to + * be repainted. Children that intersect the rectangle are + * optionally moved during the operation. In addition, outstanding + * paint events are flushed before the source area is copied to + * ensure that the contents of the canvas are drawn correctly. + * + * @param destX the x coordinate of the destination + * @param destY the y coordinate of the destination + * @param x the x coordinate of the source + * @param y the y coordinate of the source + * @param width the width of the area + * @param height the height of the area + * @param all <code>true</code>if children should be scrolled, and <code>false</code> otherwise + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void scroll (int destX, int destY, int x, int y, int width, int height, boolean all) { + checkWidget(); + if (width <= 0 || height <= 0) return; + int deltaX = destX - x, deltaY = destY - y; + if (deltaX == 0 && deltaY == 0) return; + if (!isDrawing ()) return; + NSRect visibleRect = view.visibleRect(); + if (visibleRect.width <= 0 || visibleRect.height <= 0) return; + boolean isFocus = caret != null && caret.isFocusCaret (); + if (isFocus) caret.killFocus (); + Rectangle clientRect = getClientArea (); + Rectangle sourceRect = new Rectangle (x, y, width, height); + if (sourceRect.intersects (clientRect)) { + update (all); + } + Control control = findBackgroundControl (); + boolean redraw = control != null && control.backgroundImage != null; + if (!redraw) redraw = isObscured (); + if (redraw) { + redrawWidget (view, x, y, width, height, false); + redrawWidget (view, destX, destY, width, height, false); + } else { + NSRect damage = new NSRect(); + damage.x = x; + damage.y = y; + damage.width = width; + damage.height = height; + NSPoint dest = new NSPoint(); + dest.x = destX; + dest.y = destY; + + view.lockFocus(); + OS.NSCopyBits(0, damage , dest); + view.unlockFocus(); + + boolean disjoint = (destX + width < x) || (x + width < destX) || (destY + height < y) || (y + height < destY); + if (disjoint) { + view.setNeedsDisplayInRect(damage); + } else { + if (deltaX != 0) { + int newX = destX - deltaX; + if (deltaX < 0) newX = destX + width; + damage.x = newX; + damage.width = Math.abs(deltaX); + view.setNeedsDisplayInRect(damage); + } + if (deltaY != 0) { + int newY = destY - deltaY; + if (deltaY < 0) newY = destY + height; + damage.x = x; + damage.y = newY; + damage.width = width; + damage.height = Math.abs (deltaY); + view.setNeedsDisplayInRect(damage); + } + } + + NSRect srcRect = new NSRect(); + srcRect.x = sourceRect.x; + srcRect.y = sourceRect.y; + srcRect.width = sourceRect.width; + srcRect.height = sourceRect.height; + OS.NSIntersectionRect(visibleRect, visibleRect, srcRect); + + if (!OS.NSEqualRects(visibleRect, srcRect)) { + if (srcRect.x != visibleRect.x) { + damage.x = srcRect.x + deltaX; + damage.y = srcRect.y + deltaY; + damage.width = visibleRect.x - srcRect.x; + damage.height = srcRect.height; + view.setNeedsDisplayInRect(damage); + } + if (visibleRect.x + visibleRect.width != srcRect.x + srcRect.width) { + damage.x = srcRect.x + visibleRect.width + deltaX; + damage.y = srcRect.y + deltaY; + damage.width = srcRect.width - visibleRect.width; + damage.height = srcRect.height; + view.setNeedsDisplayInRect(damage); + } + if (visibleRect.y != srcRect.y) { + damage.x = visibleRect.x + deltaX; + damage.y = srcRect.y + deltaY; + damage.width = visibleRect.width; + damage.height = visibleRect.y - srcRect.y; + view.setNeedsDisplayInRect(damage); + } + if (visibleRect.y + visibleRect.height != srcRect.y + srcRect.height) { + damage.x = visibleRect.x + deltaX; + damage.y = visibleRect.y + visibleRect.height + deltaY; + damage.width = visibleRect.width; + damage.height = srcRect.y + srcRect.height - (visibleRect.y + visibleRect.height); + view.setNeedsDisplayInRect(damage); + } + } + } + + if (all) { + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + Control child = children [i]; + Rectangle rect = child.getBounds (); + if (Math.min(x + width, rect.x + rect.width) >= Math.max (x, rect.x) && + Math.min(y + height, rect.y + rect.height) >= Math.max (y, rect.y)) { + child.setLocation (rect.x + deltaX, rect.y + deltaY); + } + } + } + if (isFocus) caret.setFocus (); +} + +NSRange selectedRange (int /*long*/ id, int /*long*/ sel) { + if (ime != null) return ime.selectedRange (id, sel); + return super.selectedRange (id, sel); +} + +boolean sendKeyEvent (NSEvent nsEvent, int type) { + if (caret != null) NSCursor.setHiddenUntilMouseMoves (true); + return super.sendKeyEvent (nsEvent, type); +} + +/** + * Sets the receiver's caret. + * <p> + * The caret for the control is automatically hidden + * and shown when the control is painted or resized, + * when focus is gained or lost and when an the control + * is scrolled. To avoid drawing on top of the caret, + * the programmer must hide and show the caret when + * drawing in the window any other time. + * </p> + * @param caret the new caret for the receiver, may be null + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the caret has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setCaret (Caret caret) { + checkWidget(); + Caret newCaret = caret; + Caret oldCaret = this.caret; + this.caret = newCaret; + if (hasFocus ()) { + if (oldCaret != null) oldCaret.killFocus (); + if (newCaret != null) { + if (newCaret.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + newCaret.setFocus (); + } + } +} + +public void setFont (Font font) { + checkWidget (); + if (caret != null) caret.setFont (font); + super.setFont (font); +} + +void setOpenGLContext(Object value) { + context = (NSOpenGLContext)value; +} + +/** + * Sets the receiver's IME. + * + * @param ime the new IME for the receiver, may be null + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the IME has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public void setIME (IME ime) { + checkWidget (); + if (ime != null && ime.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + this.ime = ime; +} + +boolean setMarkedText_selectedRange (int /*long*/ id, int /*long*/ sel, int /*long*/ string, int /*long*/ range) { + if (ime != null) { + if (!ime.setMarkedText_selectedRange (id, sel, string, range)) return false; + } + return super.setMarkedText_selectedRange (id, sel, string, range); +} + +int /*long*/ validAttributesForMarkedText (int /*long*/ id, int /*long*/ sel) { + if (ime != null) return ime.validAttributesForMarkedText (id, sel); + return super.validAttributesForMarkedText(id, sel); +} + +void updateOpenGLContext(int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { + if (context != null) ((NSOpenGLContext)context).update(); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Caret.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Caret.java new file mode 100755 index 0000000000..410caaa566 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Caret.java @@ -0,0 +1,505 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * Instances of this class provide an i-beam that is typically used + * as the insertion point for text. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>(none)</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the SWT implementation. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#caret">Caret snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample, Canvas tab</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Caret extends Widget { + Canvas parent; + int x, y, width, height; + boolean isVisible, isShowing; + int blinkRate; + Image image; + Font font; + + static final int DEFAULT_WIDTH = 1; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Caret (Canvas parent, int style) { + super (parent, style); + this.parent = parent; + createWidget (); +} + +boolean blinkCaret () { + if (!isVisible) return true; + if (!isShowing) return showCaret (); + if (blinkRate == 0) return true; + return hideCaret (); +} + +void createWidget () { + super.createWidget (); + blinkRate = display.getCaretBlinkTime (); + isVisible = true; + if (parent.getCaret () == null) { + parent.setCaret (this); + } +} + +boolean drawCaret () { + if (parent == null) return false; + if (parent.isDisposed ()) return false; + int nWidth = width, nHeight = height; + if (nWidth <= 0) nWidth = DEFAULT_WIDTH; + if (image != null) { + NSSize size = image.handle.size(); + nWidth = (int)size.width; + nHeight = (int)size.height; + } + NSRect rect = new NSRect(); + rect.x = x; + rect.y = y; + rect.width = nWidth; + rect.height = nHeight; + parent.view.setNeedsDisplayInRect(rect); + return true; +} + +/** + * Returns a rectangle describing the receiver's size and location + * relative to its parent (or its display if its parent is null). + * + * @return the receiver's bounding rectangle + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Rectangle getBounds () { + checkWidget(); + if (image != null) { + Rectangle rect = image.getBounds (); + return new Rectangle (x, y, rect.width, rect.height); + } else { + if (width == 0) { + return new Rectangle (x, y, DEFAULT_WIDTH, height); + } + } + return new Rectangle (x, y, width, height); +} + +/** + * Returns the font that the receiver will use to paint textual information. + * + * @return the receiver's font + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Font getFont () { + checkWidget(); + if (font != null) return font; + return parent.getFont (); +} + +/** + * Returns the image that the receiver will use to paint the caret. + * + * @return the receiver's image + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Image getImage () { + checkWidget(); + return image; +} + +/** + * Returns a point describing the receiver's location relative + * to its parent (or its display if its parent is null). + * + * @return the receiver's location + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Point getLocation () { + checkWidget(); + return new Point (x, y); +} + +/** + * Returns the receiver's parent, which must be a <code>Canvas</code>. + * + * @return the receiver's parent + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Canvas getParent () { + checkWidget(); + return parent; +} + +/** + * Returns a point describing the receiver's size. + * + * @return the receiver's size + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Point getSize () { + checkWidget(); + if (image != null) { + Rectangle rect = image.getBounds (); + return new Point (rect.width, rect.height); + } else { + if (width == 0) { + return new Point (DEFAULT_WIDTH, height); + } + } + return new Point (width, height); +} + +/** + * Returns <code>true</code> if the receiver is visible, and + * <code>false</code> otherwise. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, this method + * may still indicate that it is considered visible even though + * it may not actually be showing. + * </p> + * + * @return the receiver's visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getVisible () { + checkWidget(); + return isVisible; +} + +boolean hideCaret () { + if (!isShowing) return true; + isShowing = false; + return drawCaret (); +} + +/** + * Returns <code>true</code> if the receiver is visible and all + * of the receiver's ancestors are visible and <code>false</code> + * otherwise. + * + * @return the receiver's visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #getVisible + */ +public boolean isVisible () { + checkWidget(); + return isVisible && parent.isVisible () && parent.hasFocus (); +} + +boolean isFocusCaret () { + return this == display.currentCaret; +} + +void killFocus () { + if (display.currentCaret != this) return; + display.setCurrentCaret (null); + if (isVisible) hideCaret (); +} + +void releaseParent () { + super.releaseParent (); + if (this == parent.getCaret ()) parent.setCaret (null); +} + +void releaseWidget () { + super.releaseWidget (); + if (display.currentCaret == this) { + hideCaret (); + display.setCurrentCaret (null); + } + parent = null; + image = null; +} + +/** + * Sets the receiver's size and location to the rectangular + * area specified by the arguments. The <code>x</code> and + * <code>y</code> arguments are relative to the receiver's + * parent (or its display if its parent is null). + * + * @param x the new x coordinate for the receiver + * @param y the new y coordinate for the receiver + * @param width the new width for the receiver + * @param height the new height for the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setBounds (int x, int y, int width, int height) { + checkWidget(); + if (this.x == x && this.y == y && this.width == width && this.height == height) return; + boolean isFocus = isFocusCaret (); + if (isFocus && isVisible) hideCaret (); + this.x = x; + this.y = y; + this.width = width; + this.height = height; + if (isFocus && isVisible) showCaret (); +} + +/** + * Sets the receiver's size and location to the rectangular + * area specified by the argument. The <code>x</code> and + * <code>y</code> fields of the rectangle are relative to + * the receiver's parent (or its display if its parent is null). + * + * @param rect the new bounds for the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setBounds (Rectangle rect) { + checkWidget(); + if (rect == null) error (SWT.ERROR_NULL_ARGUMENT); + setBounds (rect.x, rect.y, rect.width, rect.height); +} + +void setFocus () { + if (display.currentCaret == this) return; + display.setCurrentCaret (this); + if (isVisible) showCaret (); +} + +/** + * Sets the font that the receiver will use to paint textual information + * to the font specified by the argument, or to the default font for that + * kind of control if the argument is null. + * + * @param font the new font (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the font has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setFont (Font font) { + checkWidget(); + if (font != null && font.isDisposed ()) { + error (SWT.ERROR_INVALID_ARGUMENT); + } + this.font = font; +} + +/** + * Sets the image that the receiver will use to paint the caret + * to the image specified by the argument, or to the default + * which is a filled rectangle if the argument is null + * + * @param image the new image (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setImage (Image image) { + checkWidget(); + if (image != null && image.isDisposed ()) { + error (SWT.ERROR_INVALID_ARGUMENT); + } + boolean isFocus = isFocusCaret (); + if (isFocus && isVisible) hideCaret (); + this.image = image; + if (isFocus && isVisible) showCaret (); +} + +/** + * Sets the receiver's location to the point specified by + * the arguments which are relative to the receiver's + * parent (or its display if its parent is null). + * + * @param x the new x coordinate for the receiver + * @param y the new y coordinate for the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setLocation (int x, int y) { + checkWidget(); + setBounds (x, y, width, height); +} + +/** + * Sets the receiver's location to the point specified by + * the argument which is relative to the receiver's + * parent (or its display if its parent is null). + * + * @param location the new location for the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setLocation (Point location) { + checkWidget(); + if (location == null) error (SWT.ERROR_NULL_ARGUMENT); + setLocation (location.x, location.y); +} + +/** + * Sets the receiver's size to the point specified by the arguments. + * + * @param width the new width for the receiver + * @param height the new height for the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSize (int width, int height) { + checkWidget(); + setBounds (x, y, width, height); +} + +/** + * Sets the receiver's size to the point specified by the argument. + * + * @param size the new extent for the receiver + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the point is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSize (Point size) { + checkWidget(); + if (size == null) error (SWT.ERROR_NULL_ARGUMENT); + setSize (size.x, size.y); +} + +/** + * Marks the receiver as visible if the argument is <code>true</code>, + * and marks it invisible otherwise. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, marking + * it visible may not actually cause it to be displayed. + * </p> + * + * @param visible the new visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setVisible (boolean visible) { + checkWidget(); + if (visible == isVisible) return; + isVisible = visible; + if (!isFocusCaret ()) return; + if (isVisible) { + showCaret (); + } else { + hideCaret (); + } +} + +boolean showCaret () { + if (isShowing) return true; + isShowing = true; + return drawCaret (); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ColorDialog.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ColorDialog.java new file mode 100755 index 0000000000..6ae406f67f --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ColorDialog.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class allow the user to select a color + * from a predefined set of available colors. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>(none)</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the SWT implementation. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample, Dialog tab</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class ColorDialog extends Dialog { + RGB rgb; + boolean selected; + +/** + * Constructs a new instance of this class given only its parent. + * + * @param parent a composite control which will be the parent of the new instance + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public ColorDialog(Shell parent) { + this(parent, SWT.APPLICATION_MODAL); +} + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public ColorDialog(Shell parent, int style) { + super (parent, checkStyle (parent, style)); + checkSubclass (); +} + +void changeColor(int /*long*/ id, int /*long*/ sel, int /*long*/ sender) { + selected = true; +} + +/** + * Returns the currently selected color in the receiver. + * + * @return the RGB value for the selected color, may be null + * + * @see PaletteData#getRGBs + */ +public RGB getRGB() { + return rgb; +} + +/** + * Makes the receiver visible and brings it to the front + * of the display. + * + * @return the selected color, or null if the dialog was + * cancelled, no color was selected, or an error + * occurred + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public RGB open() { + NSColorPanel panel = NSColorPanel.sharedColorPanel(); + if (rgb != null) { + NSColor color = NSColor.colorWithDeviceRed(rgb.red / 255f, rgb.green / 255f, rgb.blue / 255f, 1); + panel.setColor(color); + } + SWTPanelDelegate delegate = (SWTPanelDelegate)new SWTPanelDelegate().alloc().init(); + int /*long*/ jniRef = OS.NewGlobalRef(this); + if (jniRef == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.object_setInstanceVariable(delegate.id, Display.SWT_OBJECT, jniRef); + panel.setDelegate(delegate); + rgb = null; + selected = false; + panel.orderFront(null); + NSApplication.sharedApplication().runModalForWindow(panel); + panel.setDelegate(null); + delegate.release(); + OS.DeleteGlobalRef(jniRef); + if (selected) { + NSColor color = panel.color(); + if (color != null) { + color = color.colorUsingColorSpaceName(OS.NSCalibratedRGBColorSpace); + rgb = new RGB((int)(color.redComponent() * 255), (int)(color.greenComponent() * 255), (int)(color.blueComponent() * 255)); + } + } + return rgb; +} + +/** + * Sets the receiver's selected color to be the argument. + * + * @param rgb the new RGB value for the selected color, may be + * null to let the platform select a default when + * open() is called + * @see PaletteData#getRGBs + */ +public void setRGB(RGB rgb) { + this.rgb = rgb; +} + +void windowWillClose(int /*long*/ id, int /*long*/ sel, int /*long*/ sender) { + NSApplication.sharedApplication().stop(null); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Combo.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Combo.java new file mode 100755 index 0000000000..2773e1b3c3 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Combo.java @@ -0,0 +1,1617 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class are controls that allow the user + * to choose an item from a list of items, or optionally + * enter a new value by typing it into an editable text + * field. Often, <code>Combo</code>s are used in the same place + * where a single selection <code>List</code> widget could + * be used but space is limited. A <code>Combo</code> takes + * less space than a <code>List</code> widget and shows + * similar information. + * <p> + * Note: Since <code>Combo</code>s can contain both a list + * and an editable text field, it is possible to confuse methods + * which access one versus the other (compare for example, + * <code>clearSelection()</code> and <code>deselectAll()</code>). + * The API documentation is careful to indicate either "the + * receiver's list" or the "the receiver's text field" to + * distinguish between the two cases. + * </p><p> + * Note that although this class is a subclass of <code>Composite</code>, + * it does not make sense to add children to it, or set a layout on it. + * </p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>DROP_DOWN, READ_ONLY, SIMPLE</dd> + * <dt><b>Events:</b></dt> + * <dd>DefaultSelection, Modify, Selection, Verify</dd> + * </dl> + * <p> + * Note: Only one of the styles DROP_DOWN and SIMPLE may be specified. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see List + * @see <a href="http://www.eclipse.org/swt/snippets/#combo">Combo snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Combo extends Composite { + int textLimit = LIMIT; + boolean receivingFocus; + boolean ignoreVerify, ignoreSelection; + NSRange selectionRange; + + /** + * the operating system limit for the number of characters + * that the text field in an instance of this class can hold + */ + public static final int LIMIT; + + /* + * These values can be different on different platforms. + * Therefore they are not initialized in the declaration + * to stop the compiler from inlining. + */ + static { + LIMIT = 0x7FFFFFFF; + } + + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#DROP_DOWN + * @see SWT#READ_ONLY + * @see SWT#SIMPLE + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Combo (Composite parent, int style) { + super (parent, checkStyle (style)); +} + +/** + * Adds the argument to the end of the receiver's list. + * + * @param string the new item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #add(String,int) + */ +public void add (String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + NSString str = NSString.stringWith(string); + if ((style & SWT.READ_ONLY) != 0) { + NSPopUpButton widget = (NSPopUpButton)view; + int /*long*/ selection = widget.indexOfSelectedItem(); + NSMenu nsMenu = widget.menu(); + NSMenuItem nsItem = (NSMenuItem)new NSMenuItem().alloc(); + nsItem.initWithTitle(str, 0, NSString.stringWith("")); + nsMenu.addItem(nsItem); + nsItem.release(); + if (selection == -1) widget.selectItemAtIndex(-1); + } else { + ((NSComboBox)view).addItemWithObjectValue(str); + } +} + +/** + * Adds the argument to the receiver's list at the given + * zero-relative index. + * <p> + * Note: To add an item at the end of the list, use the + * result of calling <code>getItemCount()</code> as the + * index or use <code>add(String)</code>. + * </p> + * + * @param string the new item + * @param index the index for the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #add(String) + */ +public void add (String string, int index) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + int count = getItemCount (); + if (0 > index || index > count) error (SWT.ERROR_INVALID_RANGE); + NSString str = NSString.stringWith(string); + if ((style & SWT.READ_ONLY) != 0) { + NSPopUpButton widget = (NSPopUpButton)view; + int /*long*/ selection = widget.indexOfSelectedItem(); + NSMenu nsMenu = widget.menu(); + NSMenuItem nsItem = (NSMenuItem)new NSMenuItem().alloc(); + nsItem.initWithTitle(str, 0, NSString.stringWith("")); + nsMenu.insertItem(nsItem, index); + nsItem.release(); + if (selection == -1) widget.selectItemAtIndex(-1); + } else { + ((NSComboBox)view).insertItemWithObjectValue(str, index); + } +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the receiver's text is modified, by sending + * it one of the messages defined in the <code>ModifyListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ModifyListener + * @see #removeModifyListener + */ +public void addModifyListener (ModifyListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Modify, typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the user changes the receiver's selection, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * <code>widgetSelected</code> is called when the user changes the combo's list selection. + * <code>widgetDefaultSelected</code> is typically called when ENTER is pressed the combo's text area. + * </p> + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Selection,typedListener); + addListener (SWT.DefaultSelection,typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the receiver's text is verified, by sending + * it one of the messages defined in the <code>VerifyListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see VerifyListener + * @see #removeVerifyListener + * + * @since 3.1 + */ +public void addVerifyListener (VerifyListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Verify, typedListener); +} + +boolean becomeFirstResponder (int /*long*/ id, int /*long*/ sel) { + receivingFocus = true; + boolean result = super.becomeFirstResponder (id, sel); + receivingFocus = false; + return result; +} + +static int checkStyle (int style) { + /* + * Feature in Windows. It is not possible to create + * a combo box that has a border using Windows style + * bits. All combo boxes draw their own border and + * do not use the standard Windows border styles. + * Therefore, no matter what style bits are specified, + * clear the BORDER bits so that the SWT style will + * match the Windows widget. + * + * The Windows behavior is currently implemented on + * all platforms. + */ + style &= ~SWT.BORDER; + + /* + * Even though it is legal to create this widget + * with scroll bars, they serve no useful purpose + * because they do not automatically scroll the + * widget's client area. The fix is to clear + * the SWT style. + */ + style &= ~(SWT.H_SCROLL | SWT.V_SCROLL); + style = checkBits (style, SWT.DROP_DOWN, SWT.SIMPLE, 0, 0, 0, 0); + if ((style & SWT.SIMPLE) != 0) return style & ~SWT.READ_ONLY; + return style; +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +/** + * Sets the selection in the receiver's text field to an empty + * selection starting just before the first character. If the + * text field is editable, this has the effect of placing the + * i-beam at the start of the text. + * <p> + * Note: To clear the selected items in the receiver's list, + * use <code>deselectAll()</code>. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #deselectAll + */ +public void clearSelection () { + checkWidget(); + if ((style & SWT.READ_ONLY) == 0) { + Point selection = getSelection (); + selection.y = selection.x; + setSelection (selection); + } +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + int width = 0, height = 0; + NSControl widget = (NSControl)view; + NSCell viewCell = widget.cell (); + NSSize size = viewCell.cellSize (); + width = (int)Math.ceil (size.width); + height = (int)Math.ceil (size.height); + + if ((style & SWT.READ_ONLY) == 0) { + ignoreVerify = true; + NSComboBoxCell cell = new NSComboBoxCell (viewCell.id); + NSArray array = cell.objectValues (); + int length = (int)/*64*/array.count (); + if (length > 0) { + cell = new NSComboBoxCell (cell.copy ()); + for (int i = 0; i < length; i++) { + id object = array.objectAtIndex (i); + cell.setTitle (new NSString (object)); + size = cell.cellSize (); + width = Math.max (width, (int)Math.ceil (size.width)); + } + cell.release (); + } + ignoreVerify = false; + } + + /* + * Feature in Cocoa. Attempting to create an NSComboBox with a + * height > 27 spews a very long warning message to stdout and + * often draws the combo incorrectly. The workaround is to limit + * the returned height of editable Combos to the height that is + * required to display their text, even if a larger hHint is specified. + */ + if (hHint != SWT.DEFAULT) { + if ((style & SWT.READ_ONLY) != 0 || hHint < height) height = hHint; + } + if (wHint != SWT.DEFAULT) width = wHint; + return new Point (width, height); +} + +/** + * Copies the selected text. + * <p> + * The current selection is copied to the clipboard. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.1 + */ +public void copy () { + checkWidget (); + Point selection = getSelection (); + if (selection.x == selection.y) return; + copyToClipboard (getText (selection.x, selection.y)); +} + +void createHandle () { + if ((style & SWT.READ_ONLY) != 0) { + NSPopUpButton widget = (NSPopUpButton)new SWTPopUpButton().alloc(); + widget.initWithFrame(new NSRect(), false); + widget.menu().setAutoenablesItems(false); + widget.setTarget(widget); + widget.setAction(OS.sel_sendSelection); + view = widget; + } else { + NSComboBox widget = (NSComboBox)new SWTComboBox().alloc(); + widget.init(); + widget.setDelegate(widget); + widget.setTarget(widget); + widget.setAction(OS.sel_sendSelection); + view = widget; + } +} + +/** + * Cuts the selected text. + * <p> + * The current selection is first copied to the + * clipboard and then deleted from the widget. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.1 + */ +public void cut () { + checkWidget (); + if ((style & SWT.READ_ONLY) != 0) return; + Point selection = getSelection (); + if (selection.x == selection.y) return; + int start = selection.x, end = selection.y; + String text = getText (); + String leftText = text.substring (0, start); + String rightText = text.substring (end, text.length ()); + String oldText = text.substring (start, end); + String newText = ""; + if (hooks (SWT.Verify) || filters (SWT.Verify)) { + newText = verifyText (newText, start, end, null); + if (newText == null) return; + } + char [] buffer = new char [oldText.length ()]; + oldText.getChars (0, buffer.length, buffer, 0); + copyToClipboard (buffer); + setText (leftText + newText + rightText, false); + start += newText.length (); + setSelection (new Point (start, start)); + sendEvent (SWT.Modify); +} + +Color defaultBackground () { + return display.getWidgetColor (SWT.COLOR_LIST_BACKGROUND); +} + +NSFont defaultNSFont() { + if ((style & SWT.READ_ONLY) != 0) return display.popUpButtonFont; + return display.comboBoxFont; +} + +Color defaultForeground () { + return display.getWidgetColor (SWT.COLOR_LIST_FOREGROUND); +} + +void deregister() { + super.deregister(); + display.removeWidget(((NSControl)view).cell()); +} + +/** + * Deselects the item at the given zero-relative index in the receiver's + * list. If the item at the index was already deselected, it remains + * deselected. Indices that are out of range are ignored. + * + * @param index the index of the item to deselect + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void deselect (int index) { + checkWidget (); + if (index == -1) return; + if (index == getSelectionIndex ()) { + if ((style & SWT.READ_ONLY) != 0) { + ((NSPopUpButton)view).selectItem(null); + sendEvent (SWT.Modify); + } else { + ((NSComboBox)view).deselectItemAtIndex(index); + } + } +} + +/** + * Deselects all selected items in the receiver's list. + * <p> + * Note: To clear the selection in the receiver's text field, + * use <code>clearSelection()</code>. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #clearSelection + */ +public void deselectAll () { + checkWidget (); + if ((style & SWT.READ_ONLY) != 0) { + ((NSPopUpButton)view).selectItem(null); + sendEvent (SWT.Modify); + } else { + NSComboBox widget = (NSComboBox)view; + int /*long*/ index = widget.indexOfSelectedItem(); + if (index != -1) widget.deselectItemAtIndex(index); + } +} + +boolean dragDetect(int x, int y, boolean filter, boolean[] consume) { + if ((style & SWT.READ_ONLY) == 0) { + NSText fieldEditor = ((NSControl)view).currentEditor(); + if (fieldEditor != null) { + NSRange selectedRange = fieldEditor.selectedRange(); + if (selectedRange.length > 0) { + NSPoint mouseLocation = NSEvent.mouseLocation(); + NSTextView feAsTextView = new NSTextView(fieldEditor); + int /*long*/ charPosition = feAsTextView.characterIndexForInsertionAtPoint(mouseLocation); + if (charPosition != OS.NSNotFound && charPosition >= selectedRange.location && charPosition < (selectedRange.location + selectedRange.length)) { + if (super.dragDetect(x, y, filter, consume)) { + if (consume != null) consume[0] = true; + return true; + } + } + } + } + return false; + } + + return super.dragDetect(x, y, filter, consume); +} + +int getCharCount() { + NSString str; + if ((style & SWT.READ_ONLY) != 0) { + str = ((NSPopUpButton)view).titleOfSelectedItem(); + } else { + str = new NSCell(((NSComboBox)view).cell()).title(); + } + if (str == null) return 0; + return (int)/*64*/str.length(); +} + +/** + * Returns the item at the given, zero-relative index in the + * receiver's list. Throws an exception if the index is out + * of range. + * + * @param index the index of the item to return + * @return the item at the given index + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getItem (int index) { + checkWidget (); + int count = getItemCount (); + if (0 > index || index >= count) error (SWT.ERROR_INVALID_RANGE); + NSString str; + if ((style & SWT.READ_ONLY) != 0) { + str = ((NSPopUpButton)view).itemTitleAtIndex(index); + } else { + str = new NSString(((NSComboBox)view).itemObjectValueAtIndex(index)); + } + if (str == null) error(SWT.ERROR_CANNOT_GET_ITEM); + return str.getString(); +} + +/** + * Returns the number of items contained in the receiver's list. + * + * @return the number of items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getItemCount () { + checkWidget (); + if ((style & SWT.READ_ONLY) != 0) { + return (int)/*64*/((NSPopUpButton)view).numberOfItems(); + } else { + return (int)/*64*/((NSComboBox)view).numberOfItems(); + } +} + +/** + * Returns the height of the area which would be used to + * display <em>one</em> of the items in the receiver's list. + * + * @return the height of one item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getItemHeight () { + checkWidget (); + //TODO - not supported by the OS + return 26; +} + +/** + * Returns a (possibly empty) array of <code>String</code>s which are + * the items in the receiver's list. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its list of items, so modifying the array will + * not affect the receiver. + * </p> + * + * @return the items in the receiver's list + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String [] getItems () { + checkWidget (); + int count = getItemCount (); + String [] result = new String [count]; + for (int i=0; i<count; i++) result [i] = getItem (i); + return result; +} + +/** + * Returns <code>true</code> if the receiver's list is visible, + * and <code>false</code> otherwise. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, this method + * may still indicate that it is considered visible even though + * it may not actually be showing. + * </p> + * + * @return the receiver's list's visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public boolean getListVisible () { + //TODO + return false; +} + +int getMininumHeight () { + return getTextHeight (); +} + +/** + * Returns the orientation of the receiver. + * + * @return the orientation style + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.1.2 + */ +public int getOrientation () { + checkWidget(); + return style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT); +} + +/** + * Returns a <code>Point</code> whose x coordinate is the + * character position representing the start of the selection + * in the receiver's text field, and whose y coordinate is the + * character position representing the end of the selection. + * An "empty" selection is indicated by the x and y coordinates + * having the same value. + * <p> + * Indexing is zero based. The range of a selection is from + * 0..N where N is the number of characters in the widget. + * </p> + * + * @return a point representing the selection start and end + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Point getSelection () { + checkWidget (); + if ((style & SWT.READ_ONLY) != 0) { + return new Point (0, getCharCount ()); + } else { + if (selectionRange == null) { + NSString str = new NSTextFieldCell (((NSTextField) view).cell ()).title (); + return new Point((int)/*64*/str.length (), (int)/*64*/str.length ()); + } + return new Point((int)/*64*/selectionRange.location, (int)/*64*/(selectionRange.location + selectionRange.length)); + } +} + +/** + * Returns the zero-relative index of the item which is currently + * selected in the receiver's list, or -1 if no item is selected. + * + * @return the index of the selected item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getSelectionIndex () { + checkWidget (); + if ((style & SWT.READ_ONLY) != 0) { + return (int)/*64*/((NSPopUpButton)view).indexOfSelectedItem(); + } else { + return (int)/*64*/((NSComboBox)view).indexOfSelectedItem(); + } +} + +/** + * Returns a string containing a copy of the contents of the + * receiver's text field, or an empty string if there are no + * contents. + * + * @return the receiver's text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getText () { + checkWidget (); + return new String (getText(0, -1)); +} + +char [] getText (int start, int end) { + NSString str; + if ((style & SWT.READ_ONLY) != 0) { + str = ((NSPopUpButton)view).titleOfSelectedItem(); + } else { + str = new NSCell(((NSComboBox)view).cell()).title(); + } + if (str == null) return new char[0]; + NSRange range = new NSRange (); + range.location = start; + if (end == -1) { + int /*long*/ length = str.length(); + range.length = length - start; + } else { + range.length = end - start; + } + char [] buffer= new char [(int)/*64*/range.length]; + str.getCharacters(buffer, range); + return buffer; +} + +/** + * Returns the height of the receivers's text field. + * + * @return the text height + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getTextHeight () { + checkWidget(); + NSCell cell; + if ((style & SWT.READ_ONLY) != 0) { + cell = ((NSPopUpButton)view).cell(); + } else { + cell = ((NSComboBox)view).cell(); + } + return (int)cell.cellSize().height; +} + +/** + * Returns the maximum number of characters that the receiver's + * text field is capable of holding. If this has not been changed + * by <code>setTextLimit()</code>, it will be the constant + * <code>Combo.LIMIT</code>. + * + * @return the text limit + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #LIMIT + */ +public int getTextLimit () { + checkWidget(); + return textLimit; +} + +/** + * Gets the number of items that are visible in the drop + * down portion of the receiver's list. + * <p> + * Note: This operation is a hint and is not supported on + * platforms that do not have this concept. + * </p> + * + * @return the number of items that are visible + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public int getVisibleItemCount () { + checkWidget (); + if ((style & SWT.READ_ONLY) != 0) { + return getItemCount (); + } else { + return (int)/*64*/((NSComboBox)view).numberOfVisibleItems(); + } +} + +/** + * Searches the receiver's list starting at the first item + * (index 0) until an item is found that is equal to the + * argument, and returns the index of that item. If no item + * is found, returns -1. + * + * @param string the search item + * @return the index of the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int indexOf (String string) { + return indexOf (string, 0); +} + +/** + * Searches the receiver's list starting at the given, + * zero-relative index until an item is found that is equal + * to the argument, and returns the index of that item. If + * no item is found or the starting index is out of range, + * returns -1. + * + * @param string the search item + * @param start the zero-relative index at which to begin the search + * @return the index of the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int indexOf (String string, int start) { + checkWidget(); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + int count = getItemCount (); + if (!(0 <= start && start < count)) return -1; + for (int i=start; i<count; i++) { + if (string.equals (getItem (i))) { + return i; + } + } + return -1; +} + +void insertEditText (String string) { + ignoreVerify = true; + int length = string.length (); + Point selection = getSelection (); + if (hasFocus ()) { + if (textLimit != LIMIT) { + int charCount = getCharCount(); + if (charCount - (selection.y - selection.x) + length > textLimit) { + length = textLimit - charCount + (selection.y - selection.x); + } + } + char [] buffer = new char [length]; + string.getChars (0, buffer.length, buffer, 0); + NSString nsstring = NSString.stringWithCharacters (buffer, buffer.length); + NSText fieldEditor = ((NSTextField) view).currentEditor (); + fieldEditor.replaceCharactersInRange (fieldEditor.selectedRange (), nsstring); + selectionRange = null; + } else { + String oldText = getText (); + if (textLimit != LIMIT) { + int charCount = oldText.length (); + if (charCount - (selection.y - selection.x) + length > textLimit) { + string = string.substring(0, textLimit - charCount + (selection.y - selection.x)); + } + } + String newText = oldText.substring (0, selection.x) + string + oldText.substring (selection.y); + NSString nsstring = NSString.stringWith(newText); + new NSCell (((NSTextField) view).cell ()).setTitle (nsstring); + selectionRange = null; + setSelection (new Point(selection.x + string.length (), 0)); + } + ignoreVerify = false; +} + +boolean isEventView (int /*long*/ id) { + return true; +} + +void mouseDown(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + // If this is a combo box with an editor field and the control is disposed + // while the view's cell editor is open we crash while tearing down the + // popup window. Fix is to retain the view before letting Cocoa track + // the mouse events. + + // 'view' will be cleared if disposed during the mouseDown so cache it. + NSView viewCopy = view; + viewCopy.retain(); + super.mouseDown(id, sel, theEvent); + viewCopy.release(); +} + +/** + * Pastes text from clipboard. + * <p> + * The selected text is deleted from the widget + * and new text inserted from the clipboard. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.1 + */ +public void paste () { + checkWidget (); + if ((style & SWT.READ_ONLY) != 0) return; + Point selection = getSelection (); + int start = selection.x, end = selection.y; + String text = getText (); + String leftText = text.substring (0, start); + String rightText = text.substring (end, text.length ()); + String newText = getClipboardText (); + if (newText == null) return; + if (hooks (SWT.Verify) || filters (SWT.Verify)) { + newText = verifyText (newText, start, end, null); + if (newText == null) return; + } + if (textLimit != LIMIT) { + int charCount = text.length (); + if (charCount - (end - start) + newText.length() > textLimit) { + newText = newText.substring(0, textLimit - charCount + (end - start)); + } + } + setText (leftText + newText + rightText, false); + start += newText.length (); + setSelection (new Point (start, start)); + sendEvent (SWT.Modify); +} + +void register() { + super.register(); + display.addWidget(((NSControl)view).cell(), this); +} + +void releaseWidget () { + super.releaseWidget (); + if ((style & SWT.READ_ONLY) == 0) { + ((NSControl)view).abortEditing(); + } + selectionRange = null; +} + +/** + * Removes the item from the receiver's list at the given + * zero-relative index. + * + * @param index the index for the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void remove (int index) { + checkWidget (); + if (index == -1) error (SWT.ERROR_INVALID_RANGE); + int count = getItemCount (); + if (0 > index || index >= count) error (SWT.ERROR_INVALID_RANGE); + if ((style & SWT.READ_ONLY) != 0) { + ((NSPopUpButton)view).removeItemAtIndex(index); + } else { + ((NSComboBox)view).removeItemAtIndex(index); + } +} + +/** + * Removes the items from the receiver's list which are + * between the given zero-relative start and end + * indices (inclusive). + * + * @param start the start of the range + * @param end the end of the range + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void remove (int start, int end) { + checkWidget(); + if (start > end) return; + int count = getItemCount (); + if (!(0 <= start && start <= end && end < count)) { + error (SWT.ERROR_INVALID_RANGE); + } + int newEnd = Math.min (end, count - 1); + for (int i=newEnd; i>=start; i--) { + remove(i); + } +} + +/** + * Searches the receiver's list starting at the first item + * until an item is found that is equal to the argument, + * and removes that item from the list. + * + * @param string the item to remove + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the string is not found in the list</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void remove (String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + int index = indexOf (string, 0); + if (index == -1) error (SWT.ERROR_INVALID_ARGUMENT); + remove (index); +} + +/** + * Removes all of the items from the receiver's list and clear the + * contents of receiver's text field. + * <p> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void removeAll () { + checkWidget (); + if ((style & SWT.READ_ONLY) != 0) { + ((NSPopUpButton)view).removeAllItems(); + } else { + setText ("", true); + ((NSComboBox)view).removeAllItems(); + } +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the receiver's text is modified. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ModifyListener + * @see #addModifyListener + */ +public void removeModifyListener (ModifyListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Modify, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the user changes the receiver's selection. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener (SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Selection, listener); + eventTable.unhook (SWT.DefaultSelection,listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is verified. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see VerifyListener + * @see #addVerifyListener + * + * @since 3.1 + */ +public void removeVerifyListener (VerifyListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Verify, listener); +} + +/** + * Selects the item at the given zero-relative index in the receiver's + * list. If the item at the index was already selected, it remains + * selected. Indices that are out of range are ignored. + * + * @param index the index of the item to select + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void select (int index) { + checkWidget (); + int count = getItemCount (); + ignoreSelection = true; + if (0 <= index && index < count) { + if ((style & SWT.READ_ONLY) != 0) { + ((NSPopUpButton)view).selectItemAtIndex(index); + } else { + ((NSComboBox)view).selectItemAtIndex(index); + } + } + ignoreSelection = false; + sendEvent (SWT.Modify); +} + +void sendSelection () { + sendEvent(SWT.Modify); + if (!ignoreSelection) postEvent(SWT.Selection); +} + +boolean sendKeyEvent (NSEvent nsEvent, int type) { + boolean result = super.sendKeyEvent (nsEvent, type); + if (!result) return result; + int stateMask = 0; + int /*long*/ modifierFlags = nsEvent.modifierFlags(); + if ((modifierFlags & OS.NSAlternateKeyMask) != 0) stateMask |= SWT.ALT; + if ((modifierFlags & OS.NSShiftKeyMask) != 0) stateMask |= SWT.SHIFT; + if ((modifierFlags & OS.NSControlKeyMask) != 0) stateMask |= SWT.CONTROL; + if ((modifierFlags & OS.NSCommandKeyMask) != 0) stateMask |= SWT.COMMAND; + if (type != SWT.KeyDown) return result; + short keyCode = nsEvent.keyCode (); + if (stateMask == SWT.COMMAND) { + switch (keyCode) { + case 7: /* X */ + cut (); + return false; + case 8: /* C */ + copy (); + return false; + case 9: /* V */ + paste (); + return false; + case 0: /* A */ + if ((style & SWT.READ_ONLY) == 0) { + ((NSComboBox)view).selectText(null); + return false; + } + } + } + switch (keyCode) { + case 76: /* KP Enter */ + case 36: /* Return */ + postEvent (SWT.DefaultSelection); + } + return result; +} + +void updateBackground () { + NSColor nsColor = null; + if (backgroundImage != null) { + nsColor = NSColor.colorWithPatternImage(backgroundImage.handle); + } else if (background != null) { + nsColor = NSColor.colorWithDeviceRed(background[0], background[1], background[2], background[3]); + } else { + nsColor = NSColor.textBackgroundColor (); + } + + if ((style & SWT.READ_ONLY) != 0) { + //TODO + } else { + ((NSTextField)view).setBackgroundColor(nsColor); + } +} + +void setBounds (int x, int y, int width, int height, boolean move, boolean resize) { + /* + * Feature in Cocoa. Attempting to create an NSComboBox with a + * height > 27 spews a very long warning message to stdout and + * often draws the combo incorrectly. The workaround is to limit + * the height of editable Combos to the height that is required + * to display their text. + */ + if ((style & SWT.READ_ONLY) == 0) { + NSControl widget = (NSControl)view; + NSSize size = widget.cell ().cellSize (); + height = Math.min (height, (int)Math.ceil (size.height)); + } + super.setBounds (x, y, width, height, move, resize); +} + +void setForeground (float /*double*/ [] color) { + NSColor nsColor; + if (color == null) { + nsColor = NSColor.textColor (); + } else { + nsColor = NSColor.colorWithDeviceRed(color[0], color[1], color[2], 1); + } + if ((style & SWT.READ_ONLY) != 0) { + //TODO + } else { + ((NSTextField)view).setTextColor(nsColor); + } +} + +/** + * Sets the text of the item in the receiver's list at the given + * zero-relative index to the string argument. + * + * @param index the index for the item + * @param string the new text for the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setItem (int index, String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + int count = getItemCount (); + if (0 > index || index >= count) error (SWT.ERROR_INVALID_RANGE); + NSString str = NSString.stringWith(string); + if ((style & SWT.READ_ONLY) != 0) { + NSMenuItem nsItem = ((NSPopUpButton)view).itemAtIndex(index); + nsItem.setTitle(str); + } else { + NSComboBox widget = (NSComboBox)view; + widget.insertItemWithObjectValue(str, index); + widget.removeItemAtIndex(index + 1); + } +} + +/** + * Sets the receiver's list to be the given array of items. + * + * @param items the array of items + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the items array is null</li> + * <li>ERROR_INVALID_ARGUMENT - if an item in the items array is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setItems (String [] items) { + checkWidget(); + if (items == null) error (SWT.ERROR_NULL_ARGUMENT); + for (int i=0; i<items.length; i++) { + if (items [i] == null) error (SWT.ERROR_INVALID_ARGUMENT); + } + removeAll(); + if (items.length == 0) return; + for (int i= 0; i < items.length; i++) { + NSString str = NSString.stringWith(items[i]); + if ((style & SWT.READ_ONLY) != 0) { + NSMenu nsMenu = ((NSPopUpButton)view).menu(); + NSMenuItem nsItem = (NSMenuItem)new NSMenuItem().alloc(); + nsItem.initWithTitle(str, 0, NSString.stringWith("")); + nsMenu.addItem(nsItem); + nsItem.release(); + //clear the selection + ((NSPopUpButton)view).selectItemAtIndex(-1); + } else { + ((NSComboBox)view).addItemWithObjectValue(str); + } + } +} + +/** + * Marks the receiver's list as visible if the argument is <code>true</code>, + * and marks it invisible otherwise. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, marking + * it visible may not actually cause it to be displayed. + * </p> + * + * @param visible the new visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public void setListVisible (boolean visible) { + checkWidget (); + if ((style & SWT.READ_ONLY) != 0) { + ((NSPopUpButton)view).setPullsDown(visible); + } else { + } +} + +/** + * Sets the orientation of the receiver, which must be one + * of the constants <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>. + * <p> + * + * @param orientation new orientation style + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.1.2 + */ +public void setOrientation (int orientation) { + checkWidget(); +} + +/** + * Sets the selection in the receiver's text field to the + * range specified by the argument whose x coordinate is the + * start of the selection and whose y coordinate is the end + * of the selection. + * + * @param selection a point representing the new selection start and end + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the point is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSelection (Point selection) { + checkWidget (); + if (selection == null) error (SWT.ERROR_NULL_ARGUMENT); + if ((style & SWT.READ_ONLY) == 0) { + NSComboBox widget = (NSComboBox)view; + NSString str = new NSCell(widget.cell()).title(); + int length = (int)/*64*/str.length(); + int start = Math.min (Math.max (Math.min (selection.x, selection.y), 0), length); + int end = Math.min (Math.max (Math.max (selection.x, selection.y), 0), length); + selectionRange = new NSRange(); + selectionRange.location = start; + selectionRange.length = end - start; + NSText fieldEditor = widget.currentEditor(); + if (fieldEditor != null) fieldEditor.setSelectedRange(selectionRange); + } +} + +/** + * Sets the contents of the receiver's text field to the + * given string. + * <p> + * This call is ignored when the receiver is read only and + * the given string is not in the receiver's list. + * </p> + * <p> + * Note: The text field in a <code>Combo</code> is typically + * only capable of displaying a single line of text. Thus, + * setting the text to a string containing line breaks or + * other special characters will probably cause it to + * display incorrectly. + * </p> + * + * @param string the new text + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setText (String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + setText (string, true); +} + +void setText (String string, boolean notify) { + ignoreVerify = true; + if (notify) { + if (hooks (SWT.Verify) || filters (SWT.Verify)) { + string = verifyText (string, 0, getCharCount (), null); + if (string == null) return; + } + } + if ((style & SWT.READ_ONLY) != 0) { + int index = indexOf (string); + if (index != -1 && index != getSelectionIndex ()) { + select (index); + if (notify) sendEvent (SWT.Modify); + } + } else { + char[] buffer = new char [Math.min(string.length (), textLimit)]; + string.getChars (0, buffer.length, buffer, 0); + NSString nsstring = NSString.stringWithCharacters (buffer, buffer.length); + new NSCell(((NSComboBox)view).cell()).setTitle(nsstring); + if (notify) sendEvent (SWT.Modify); + } + selectionRange = null; + ignoreVerify = false; +} + +/** + * Sets the maximum number of characters that the receiver's + * text field is capable of holding to be the argument. + * <p> + * To reset this value to the default, use <code>setTextLimit(Combo.LIMIT)</code>. + * Specifying a limit value larger than <code>Combo.LIMIT</code> sets the + * receiver's limit to <code>Combo.LIMIT</code>. + * </p> + * @param limit new text limit + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_CANNOT_BE_ZERO - if the limit is zero</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #LIMIT + */ +public void setTextLimit (int limit) { + checkWidget (); + if (limit == 0) error (SWT.ERROR_CANNOT_BE_ZERO); + textLimit = limit; +} + +/** + * Sets the number of items that are visible in the drop + * down portion of the receiver's list. + * <p> + * Note: This operation is a hint and is not supported on + * platforms that do not have this concept. + * </p> + * + * @param count the new number of items to be visible + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public void setVisibleItemCount (int count) { + checkWidget (); + if (count < 0) return; + if ((style & SWT.READ_ONLY) != 0) { + //TODO + } else { + ((NSComboBox)view).setNumberOfVisibleItems(count); + } +} + +boolean shouldChangeTextInRange_replacementString(int /*long*/ id, int /*long*/ sel, int /*long*/ affectedCharRange, int /*long*/ replacementString) { + NSRange range = new NSRange(); + OS.memmove(range, affectedCharRange, NSRange.sizeof); + boolean result = callSuperBoolean(id, sel, range, replacementString); + if (hooks (SWT.Verify)) { + String text = new NSString(replacementString).getString(); + NSEvent currentEvent = display.application.currentEvent(); + int /*long*/ type = currentEvent.type(); + if (type != OS.NSKeyDown && type != OS.NSKeyUp) currentEvent = null; + String newText = verifyText(text, (int)/*64*/range.location, (int)/*64*/(range.location+range.length), currentEvent); + if (newText == null) return false; + if (text != newText) { + insertEditText(newText); + result = false; + } + if (!result) sendEvent (SWT.Modify); + } + return result; +} + +void textViewDidChangeSelection(int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { + NSNotification notification = new NSNotification(aNotification); + NSText editor = new NSText(notification.object().id); + selectionRange = editor.selectedRange(); +} + +void textDidChange (int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { + super.textDidChange (id, sel, aNotification); + postEvent (SWT.Modify); +} + +NSRange textView_willChangeSelectionFromCharacterRange_toCharacterRange(int /*long*/ id, int /*long*/ sel, int /*long*/ aTextView, int /*long*/ oldSelectedCharRange, int /*long*/ newSelectedCharRange) { + /* + * If the selection is changing as a result of the receiver getting focus + * then return the receiver's last selection range, otherwise the full + * text will be automatically selected. + */ + if (receivingFocus && selectionRange != null) return selectionRange; + + /* allow the selection change to proceed */ + NSRange result = new NSRange(); + OS.memmove(result, newSelectedCharRange, NSRange.sizeof); + return result; +} + +String verifyText (String string, int start, int end, NSEvent keyEvent) { + Event event = new Event (); + if (keyEvent != null) setKeyState(event, SWT.MouseDown, keyEvent); + event.text = string; + event.start = start; + event.end = end; + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the verify + * event. If this happens, answer null to cancel + * the operation. + */ + sendEvent (SWT.Verify, event); + if (!event.doit || isDisposed ()) return null; + return event.text; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Composite.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Composite.java new file mode 100755 index 0000000000..2929e285bd --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Composite.java @@ -0,0 +1,1005 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.*; +import org.eclipse.swt.accessibility.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class are controls which are capable + * of containing other controls. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>NO_BACKGROUND, NO_FOCUS, NO_MERGE_PAINTS, NO_REDRAW_RESIZE, NO_RADIO_GROUP, EMBEDDED, DOUBLE_BUFFERED</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * Note: The <code>NO_BACKGROUND</code>, <code>NO_FOCUS</code>, <code>NO_MERGE_PAINTS</code>, + * and <code>NO_REDRAW_RESIZE</code> styles are intended for use with <code>Canvas</code>. + * They can be used with <code>Composite</code> if you are drawing your own, but their + * behavior is undefined if they are used with subclasses of <code>Composite</code> other + * than <code>Canvas</code>. + * </p><p> + * Note: The <code>CENTER</code> style, although undefined for composites, has the + * same value as <code>EMBEDDED</code> which is used to embed widgets from other + * widget toolkits into SWT. On some operating systems (GTK, Motif), this may cause + * the children of this composite to be obscured. + * </p><p> + * This class may be subclassed by custom control implementors + * who are building controls that are constructed from aggregates + * of other controls. + * </p> + * + * @see Canvas + * @see <a href="http://www.eclipse.org/swt/snippets/#composite">Composite snippets</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + */ +public class Composite extends Scrollable { + Layout layout; + Control[] tabList; + int layoutCount, backgroundMode; + +Composite () { + /* Do nothing */ +} + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a widget which will be the parent of the new instance (cannot be null) + * @param style the style of widget to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * </ul> + * + * @see SWT#NO_BACKGROUND + * @see SWT#NO_FOCUS + * @see SWT#NO_MERGE_PAINTS + * @see SWT#NO_REDRAW_RESIZE + * @see SWT#NO_RADIO_GROUP + * @see SWT#EMBEDDED + * @see SWT#DOUBLE_BUFFERED + * @see Widget#getStyle + */ +public Composite (Composite parent, int style) { + super (parent, style); +} + +Control [] _getChildren () { + NSArray views = contentView().subviews(); + int count = (int)/*64*/views.count(); + Control [] children = new Control [count]; + if (count == 0) return children; + int j = 0; + for (int i=0; i<count; i++){ + Widget widget = display.getWidget (views.objectAtIndex (count - i - 1).id); + if (widget != null && widget != this && widget instanceof Control) { + children [j++] = (Control) widget; + } + } + if (j == count) return children; + Control [] newChildren = new Control [j]; + System.arraycopy (children, 0, newChildren, 0, j); + return newChildren; +} + +Control [] _getTabList () { + if (tabList == null) return null; + int count = 0; + for (int i=0; i<tabList.length; i++) { + if (!tabList [i].isDisposed ()) count++; + } + if (count == tabList.length) return tabList; + Control [] newList = new Control [count]; + int index = 0; + for (int i=0; i<tabList.length; i++) { + if (!tabList [i].isDisposed ()) { + newList [index++] = tabList [i]; + } + } + tabList = newList; + return tabList; +} + +boolean acceptsFirstResponder (int /*long*/ id, int /*long*/ sel) { + if ((state & CANVAS) != 0) { + if ((style & SWT.NO_FOCUS) == 0 && hooksKeys ()) { + if (contentView().subviews().count() == 0) return true; + } + return false; + } + return super.acceptsFirstResponder (id, sel); +} + +int /*long*/ accessibilityAttributeNames(int /*long*/ id, int /*long*/ sel) { + + if (id == view.id) { + if (accessible != null) { + // If there is an accessible, it may provide its own list of attributes if it's a lightweight control. + // If not, let Cocoa handle it for this view. + id returnObject = accessible.internal_accessibilityAttributeNames(ACC.CHILDID_SELF); + if (returnObject != null) return returnObject.id; + } + } + + return super.accessibilityAttributeNames(id, sel); +} + +boolean accessibilityIsIgnored(int /*long*/ id, int /*long*/ sel) { + // If we have an accessible and it represents a valid accessible role, this view is not ignored. + if (view != null && id == view.id) { + if (accessible != null) { + id role = accessible.internal_accessibilityAttributeValue(OS.NSAccessibilityRoleAttribute, ACC.CHILDID_SELF); + if (role != null) return false; + } + } + + return super.accessibilityIsIgnored(id, sel); +} + +/** + * Clears any data that has been cached by a Layout for all widgets that + * are in the parent hierarchy of the changed control up to and including the + * receiver. If an ancestor does not have a layout, it is skipped. + * + * @param changed an array of controls that changed state and require a recalculation of size + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the changed array is null any of its controls are null or have been disposed</li> + * <li>ERROR_INVALID_PARENT - if any control in changed is not in the widget tree of the receiver</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public void changed (Control[] changed) { + checkWidget (); + if (changed == null) error (SWT.ERROR_INVALID_ARGUMENT); + for (int i=0; i<changed.length; i++) { + Control control = changed [i]; + if (control == null) error (SWT.ERROR_INVALID_ARGUMENT); + if (control.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + boolean ancestor = false; + Composite composite = control.parent; + while (composite != null) { + ancestor = composite == this; + if (ancestor) break; + composite = composite.parent; + } + if (!ancestor) error (SWT.ERROR_INVALID_PARENT); + } + for (int i=0; i<changed.length; i++) { + Control child = changed [i]; + Composite composite = child.parent; + while (child != this) { + if (composite.layout == null || !composite.layout.flushCache (child)) { + composite.state |= LAYOUT_CHANGED; + } + child = composite; + composite = child.parent; + } + } +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget(); + Point size; + if (layout != null) { + if ((wHint == SWT.DEFAULT) || (hHint == SWT.DEFAULT)) { + changed |= (state & LAYOUT_CHANGED) != 0; + size = layout.computeSize (this, wHint, hHint, changed); + state &= ~LAYOUT_CHANGED; + } else { + size = new Point (wHint, hHint); + } + } else { + size = minimumSize (wHint, hHint, changed); + } + if (size.x == 0) size.x = DEFAULT_WIDTH; + if (size.y == 0) size.y = DEFAULT_HEIGHT; + if (wHint != SWT.DEFAULT) size.x = wHint; + if (hHint != SWT.DEFAULT) size.y = hHint; + Rectangle trim = computeTrim (0, 0, size.x, size.y); + return new Point (trim.width, trim.height); +} + +protected void checkSubclass () { + /* Do nothing - Subclassing is allowed */ +} + +Widget [] computeTabList () { + Widget result [] = super.computeTabList (); + if (result.length == 0) return result; + Control [] list = tabList != null ? _getTabList () : _getChildren (); + for (int i=0; i<list.length; i++) { + Control child = list [i]; + Widget [] childList = child.computeTabList (); + if (childList.length != 0) { + Widget [] newResult = new Widget [result.length + childList.length]; + System.arraycopy (result, 0, newResult, 0, result.length); + System.arraycopy (childList, 0, newResult, result.length, childList.length); + result = newResult; + } + } + return result; +} + +void createHandle () { + state |= CANVAS; + boolean scrolled = (style & (SWT.V_SCROLL | SWT.H_SCROLL)) != 0; + if (!scrolled) state |= THEME_BACKGROUND; + NSRect rect = new NSRect(); + if (scrolled || hasBorder ()) { + NSScrollView scrollWidget = (NSScrollView)new SWTScrollView().alloc(); + scrollWidget.initWithFrame (rect); + scrollWidget.setDrawsBackground(false); + if ((style & SWT.H_SCROLL) != 0) scrollWidget.setHasHorizontalScroller(true); + if ((style & SWT.V_SCROLL) != 0) scrollWidget.setHasVerticalScroller(true); + scrollWidget.setBorderType(hasBorder() ? OS.NSBezelBorder : OS.NSNoBorder); + scrollView = scrollWidget; + } + NSView widget = (NSView)new SWTCanvasView().alloc(); + widget.initWithFrame (rect); +// widget.setFocusRingType(OS.NSFocusRingTypeExterior); + view = widget; + if (scrollView != null) { + NSClipView contentView = scrollView.contentView(); + contentView.setAutoresizesSubviews(true); + view.setAutoresizingMask(OS.NSViewWidthSizable | OS.NSViewHeightSizable); + } +} + +void drawBackground (int /*long*/ id, NSGraphicsContext context, NSRect rect) { + if (id != view.id) return; + if ((state & CANVAS) != 0) { + if ((style & SWT.NO_BACKGROUND) == 0) { + fillBackground (view, context, rect, -1); + } + } +} + +Composite findDeferredControl () { + return layoutCount > 0 ? this : parent.findDeferredControl (); +} + +Menu [] findMenus (Control control) { + if (control == this) return new Menu [0]; + Menu result [] = super.findMenus (control); + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + Control child = children [i]; + Menu [] menuList = child.findMenus (control); + if (menuList.length != 0) { + Menu [] newResult = new Menu [result.length + menuList.length]; + System.arraycopy (result, 0, newResult, 0, result.length); + System.arraycopy (menuList, 0, newResult, result.length, menuList.length); + result = newResult; + } + } + return result; +} + +void fixChildren (Shell newShell, Shell oldShell, Decorations newDecorations, Decorations oldDecorations, Menu [] menus) { + super.fixChildren (newShell, oldShell, newDecorations, oldDecorations, menus); + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + children [i].fixChildren (newShell, oldShell, newDecorations, oldDecorations, menus); + } +} + +void fixTabList (Control control) { + if (tabList == null) return; + int count = 0; + for (int i=0; i<tabList.length; i++) { + if (tabList [i] == control) count++; + } + if (count == 0) return; + Control [] newList = null; + int length = tabList.length - count; + if (length != 0) { + newList = new Control [length]; + int index = 0; + for (int i=0; i<tabList.length; i++) { + if (tabList [i] != control) { + newList [index++] = tabList [i]; + } + } + } + tabList = newList; +} + +/** + * Returns the receiver's background drawing mode. This + * will be one of the following constants defined in class + * <code>SWT</code>: + * <code>INHERIT_NONE</code>, <code>INHERIT_DEFAULT</code>, + * <code>INHERTIT_FORCE</code>. + * + * @return the background mode + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SWT + * + * @since 3.2 + */ +public int getBackgroundMode () { + checkWidget (); + return backgroundMode; +} + +/** + * Returns a (possibly empty) array containing the receiver's children. + * Children are returned in the order that they are drawn. The topmost + * control appears at the beginning of the array. Subsequent controls + * draw beneath this control and appear later in the array. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its list of children, so modifying the array will + * not affect the receiver. + * </p> + * + * @return an array of children + * + * @see Control#moveAbove + * @see Control#moveBelow + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Control [] getChildren () { + checkWidget(); + return _getChildren (); +} + +/** + * Returns layout which is associated with the receiver, or + * null if one has not been set. + * + * @return the receiver's layout or null + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Layout getLayout () { + checkWidget(); + return layout; +} + +/** + * Returns <code>true</code> if the receiver has deferred + * the performing of layout, and <code>false</code> otherwise. + * + * @return the receiver's deferred layout state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setLayoutDeferred(boolean) + * @see #isLayoutDeferred() + * + * @since 3.1 + */ +public boolean getLayoutDeferred () { + checkWidget (); + return layoutCount > 0 ; +} + +/** + * Gets the (possibly empty) tabbing order for the control. + * + * @return tabList the ordered list of controls representing the tab order + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setTabList + */ +public Control [] getTabList () { + checkWidget (); + Control [] tabList = _getTabList (); + if (tabList == null) { + int count = 0; + Control [] list =_getChildren (); + for (int i=0; i<list.length; i++) { + if (list [i].isTabGroup ()) count++; + } + tabList = new Control [count]; + int index = 0; + for (int i=0; i<list.length; i++) { + if (list [i].isTabGroup ()) { + tabList [index++] = list [i]; + } + } + } + return tabList; +} + +boolean hooksKeys () { + return hooks (SWT.KeyDown) || hooks (SWT.KeyUp); +} + +void invalidateChildrenVisibleRegion () { + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + Control child = children [i]; + child.resetVisibleRegion (); + child.invalidateChildrenVisibleRegion (); + } +} + +/** + * Returns <code>true</code> if the receiver or any ancestor + * up to and including the receiver's nearest ancestor shell + * has deferred the performing of layouts. Otherwise, <code>false</code> + * is returned. + * + * @return the receiver's deferred layout state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setLayoutDeferred(boolean) + * @see #getLayoutDeferred() + * + * @since 3.1 + */ +public boolean isLayoutDeferred () { + checkWidget (); + return findDeferredControl () != null; +} + +boolean isOpaque (int /*long*/ id, int /*long*/ sel) { + if ((state & CANVAS) != 0) { + if (id == view.id) { + if (region == null && background != null && background[3] == 1) { + return true; + } + } + } + return super.isOpaque (id, sel); +} + +boolean isTabGroup () { + if ((state & CANVAS) != 0) return true; + return super.isTabGroup (); +} + +void keyDown (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (view.window ().firstResponder ().id == id) { + if ((state & CANVAS) != 0) { + Shell s = this.getShell(); + NSArray array = NSArray.arrayWithObject (new NSEvent (theEvent)); + s.keyInputHappened = false; + view.interpretKeyEvents (array); + if (imeInComposition ()) return; + if (!s.keyInputHappened) { + NSEvent nsEvent = new NSEvent (theEvent); + boolean [] consume = new boolean [1]; + if (translateTraversal (nsEvent.keyCode (), nsEvent, consume)) return; + if (isDisposed ()) return; + if (!sendKeyEvent (nsEvent, SWT.KeyDown)) return; + if (consume [0]) return; + } + return; + } + } + super.keyDown (id, sel, theEvent); +} + +/** + * If the receiver has a layout, asks the layout to <em>lay out</em> + * (that is, set the size and location of) the receiver's children. + * If the receiver does not have a layout, do nothing. + * <p> + * This is equivalent to calling <code>layout(true)</code>. + * </p> + * <p> + * Note: Layout is different from painting. If a child is + * moved or resized such that an area in the parent is + * exposed, then the parent will paint. If no child is + * affected, the parent will not paint. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void layout () { + checkWidget (); + layout (true); +} + +/** + * If the receiver has a layout, asks the layout to <em>lay out</em> + * (that is, set the size and location of) the receiver's children. + * If the argument is <code>true</code> the layout must not rely + * on any information it has cached about the immediate children. If it + * is <code>false</code> the layout may (potentially) optimize the + * work it is doing by assuming that none of the receiver's + * children has changed state since the last layout. + * If the receiver does not have a layout, do nothing. + * <p> + * If a child is resized as a result of a call to layout, the + * resize event will invoke the layout of the child. The layout + * will cascade down through all child widgets in the receiver's widget + * tree until a child is encountered that does not resize. Note that + * a layout due to a resize will not flush any cached information + * (same as <code>layout(false)</code>). + * </p> + * <p> + * Note: Layout is different from painting. If a child is + * moved or resized such that an area in the parent is + * exposed, then the parent will paint. If no child is + * affected, the parent will not paint. + * </p> + * + * @param changed <code>true</code> if the layout must flush its caches, and <code>false</code> otherwise + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void layout (boolean changed) { + checkWidget (); + if (layout == null) return; + layout (changed, false); +} + +/** + * If the receiver has a layout, asks the layout to <em>lay out</em> + * (that is, set the size and location of) the receiver's children. + * If the changed argument is <code>true</code> the layout must not rely + * on any information it has cached about its children. If it + * is <code>false</code> the layout may (potentially) optimize the + * work it is doing by assuming that none of the receiver's + * children has changed state since the last layout. + * If the all argument is <code>true</code> the layout will cascade down + * through all child widgets in the receiver's widget tree, regardless of + * whether the child has changed size. The changed argument is applied to + * all layouts. If the all argument is <code>false</code>, the layout will + * <em>not</em> cascade down through all child widgets in the receiver's widget + * tree. However, if a child is resized as a result of a call to layout, the + * resize event will invoke the layout of the child. Note that + * a layout due to a resize will not flush any cached information + * (same as <code>layout(false)</code>). + * </p> + * <p> + * Note: Layout is different from painting. If a child is + * moved or resized such that an area in the parent is + * exposed, then the parent will paint. If no child is + * affected, the parent will not paint. + * </p> + * + * @param changed <code>true</code> if the layout must flush its caches, and <code>false</code> otherwise + * @param all <code>true</code> if all children in the receiver's widget tree should be laid out, and <code>false</code> otherwise + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public void layout (boolean changed, boolean all) { + checkWidget (); + if (layout == null && !all) return; + markLayout (changed, all); + updateLayout (all); +} + +/** + * Forces a lay out (that is, sets the size and location) of all widgets that + * are in the parent hierarchy of the changed control up to and including the + * receiver. The layouts in the hierarchy must not rely on any information + * cached about the changed control or any of its ancestors. The layout may + * (potentially) optimize the work it is doing by assuming that none of the + * peers of the changed control have changed state since the last layout. + * If an ancestor does not have a layout, skip it. + * <p> + * Note: Layout is different from painting. If a child is + * moved or resized such that an area in the parent is + * exposed, then the parent will paint. If no child is + * affected, the parent will not paint. + * </p> + * + * @param changed a control that has had a state change which requires a recalculation of its size + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the changed array is null any of its controls are null or have been disposed</li> + * <li>ERROR_INVALID_PARENT - if any control in changed is not in the widget tree of the receiver</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public void layout (Control [] changed) { + checkWidget (); + if (changed == null) error (SWT.ERROR_INVALID_ARGUMENT); + for (int i=0; i<changed.length; i++) { + Control control = changed [i]; + if (control == null) error (SWT.ERROR_INVALID_ARGUMENT); + if (control.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + boolean ancestor = false; + Composite composite = control.parent; + while (composite != null) { + ancestor = composite == this; + if (ancestor) break; + composite = composite.parent; + } + if (!ancestor) error (SWT.ERROR_INVALID_PARENT); + } + int updateCount = 0; + Composite [] update = new Composite [16]; + for (int i=0; i<changed.length; i++) { + Control child = changed [i]; + Composite composite = child.parent; + while (child != this) { + if (composite.layout != null) { + composite.state |= LAYOUT_NEEDED; + if (!composite.layout.flushCache (child)) { + composite.state |= LAYOUT_CHANGED; + } + } + if (updateCount == update.length) { + Composite [] newUpdate = new Composite [update.length + 16]; + System.arraycopy (update, 0, newUpdate, 0, update.length); + update = newUpdate; + } + child = update [updateCount++] = composite; + composite = child.parent; + } + } + for (int i=updateCount-1; i>=0; i--) { + update [i].updateLayout (false); + } +} + +void markLayout (boolean changed, boolean all) { + if (layout != null) { + state |= LAYOUT_NEEDED; + if (changed) state |= LAYOUT_CHANGED; + } + if (all) { + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + children [i].markLayout (changed, all); + } + } +} + +Point minimumSize (int wHint, int Hint, boolean changed) { + Control [] children = _getChildren (); + int width = 0, height = 0; + for (int i=0; i<children.length; i++) { + Rectangle rect = children [i].getBounds (); + width = Math.max (width, rect.x + rect.width); + height = Math.max (height, rect.y + rect.height); + } + return new Point (width, height); +} + +boolean mouseEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent, int type) { + boolean result = super.mouseEvent (id, sel, theEvent, type); + return (state & CANVAS) == 0 ? result : new NSEvent (theEvent).type () != OS.NSLeftMouseDown; +} + +void pageDown(int /*long*/ id, int /*long*/ sel, int /*long*/ sender) { + if ((state & CANVAS) != 0) return; + super.pageDown(id, sel, sender); +} + +void pageUp(int /*long*/ id, int /*long*/ sel, int /*long*/ sender) { + if ((state & CANVAS) != 0) return; + super.pageUp(id, sel, sender); +} + +void reflectScrolledClipView (int /*long*/ id, int /*long*/ sel, int /*long*/ aClipView) { + if ((state & CANVAS) != 0) return; + super.reflectScrolledClipView (id, sel, aClipView); +} + +void releaseChildren (boolean destroy) { + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + Control child = children [i]; + if (child != null && !child.isDisposed ()) { + child.release (false); + } + } + super.releaseChildren (destroy); +} + +void releaseWidget () { + super.releaseWidget (); + layout = null; + tabList = null; +} + +void removeControl (Control control) { + fixTabList (control); +} + +void resized () { + super.resized (); + if (layout != null) { + markLayout (false, false); + updateLayout (false); + } +} + +void scrollWheel (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if ((state & CANVAS) != 0) { + NSView view = scrollView != null ? scrollView : this.view; + if (id == view.id) { + NSEvent nsEvent = new NSEvent(theEvent); + float /*double*/ delta = nsEvent.deltaY(); + if (delta != 0) { + if (hooks (SWT.MouseWheel) || filters (SWT.MouseWheel)) { + if (!sendMouseEvent(nsEvent, SWT.MouseWheel, true)) { + return; + } + } + } + boolean handled = false; + ScrollBar bar = verticalBar; + if (delta != 0 && bar != null && bar.getEnabled ()) { + if (-1 < delta && delta < 0) delta = -1; + if (0 < delta && delta < 1) delta = 1; + int selection = Math.max (0, (int)(0.5f + bar.getSelection () - bar.getIncrement () * delta)); + bar.setSelection (selection); + Event event = new Event (); + event.detail = delta > 0 ? SWT.PAGE_UP : SWT.PAGE_DOWN; + bar.sendEvent (SWT.Selection, event); + handled = true; + } + bar = horizontalBar; + delta = nsEvent.deltaX (); + if (delta != 0 && bar != null && bar.getEnabled ()) { + int selection = Math.max (0, (int)(0.5f + bar.getSelection () - bar.getIncrement () * delta)); + bar.setSelection (selection); + Event event = new Event (); + event.detail = delta > 0 ? SWT.PAGE_UP : SWT.PAGE_DOWN; + bar.sendEvent (SWT.Selection, event); + handled = true; + } + if (!handled) view.superview().scrollWheel(nsEvent); + return; + } + callSuper(id, sel, theEvent); + return; + } + super.scrollWheel (id, sel, theEvent); +} + +/** + * Sets the background drawing mode to the argument which should + * be one of the following constants defined in class <code>SWT</code>: + * <code>INHERIT_NONE</code>, <code>INHERIT_DEFAULT</code>, + * <code>INHERIT_FORCE</code>. + * + * @param mode the new background mode + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SWT + * + * @since 3.2 + */ +public void setBackgroundMode (int mode) { + checkWidget (); + backgroundMode = mode; + Control [] children = _getChildren (); + for (int i = 0; i < children.length; i++) { + children [i].updateBackgroundMode (); + } +} + +public boolean setFocus () { + checkWidget (); + Control [] children = _getChildren (); + for (int i= 0; i < children.length; i++) { + if (children [i].setFocus ()) return true; + } + return super.setFocus (); +} + +/** + * Sets the layout which is associated with the receiver to be + * the argument which may be null. + * + * @param layout the receiver's new layout or null + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setLayout (Layout layout) { + checkWidget(); + this.layout = layout; +} + +/** + * If the argument is <code>true</code>, causes subsequent layout + * operations in the receiver or any of its children to be ignored. + * No layout of any kind can occur in the receiver or any of its + * children until the flag is set to false. + * Layout operations that occurred while the flag was + * <code>true</code> are remembered and when the flag is set to + * <code>false</code>, the layout operations are performed in an + * optimized manner. Nested calls to this method are stacked. + * + * @param defer the new defer state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #layout(boolean) + * @see #layout(Control[]) + * + * @since 3.1 + */ +public void setLayoutDeferred (boolean defer) { + if (!defer) { + if (--layoutCount == 0) { + if ((state & LAYOUT_CHILD) != 0 || (state & LAYOUT_NEEDED) != 0) { + updateLayout (true); + } + } + } else { + layoutCount++; + } +} + +boolean setScrollBarVisible (ScrollBar bar, boolean visible) { + boolean changed = super.setScrollBarVisible (bar, visible); + if (changed && layout != null) { + markLayout (false, false); + updateLayout (false); + } + return changed; +} + +boolean setTabGroupFocus () { + if (isTabItem ()) return setTabItemFocus (); + boolean takeFocus = (style & SWT.NO_FOCUS) == 0; + if ((state & CANVAS) != 0) takeFocus = hooksKeys (); + if (takeFocus && setTabItemFocus ()) return true; + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + Control child = children [i]; + if (child.isTabItem () && child.setTabItemFocus ()) return true; + } + return false; +} + +/** + * Sets the tabbing order for the specified controls to + * match the order that they occur in the argument list. + * + * @param tabList the ordered list of controls representing the tab order or null + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if a widget in the tabList is null or has been disposed</li> + * <li>ERROR_INVALID_PARENT - if widget in the tabList is not in the same widget tree</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setTabList (Control [] tabList) { + checkWidget (); + if (tabList != null) { + for (int i=0; i<tabList.length; i++) { + Control control = tabList [i]; + if (control == null) error (SWT.ERROR_INVALID_ARGUMENT); + if (control.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + if (control.parent != this) error (SWT.ERROR_INVALID_PARENT); + } + Control [] newList = new Control [tabList.length]; + System.arraycopy (tabList, 0, newList, 0, tabList.length); + tabList = newList; + } + this.tabList = tabList; +} + +int traversalCode (int key, NSEvent theEvent) { + if ((state & CANVAS) != 0) { + if ((style & SWT.NO_FOCUS) != 0) return 0; + if (hooksKeys ()) return 0; + } + return super.traversalCode (key, theEvent); +} + +void updateBackgroundMode () { + super.updateBackgroundMode (); + Control [] children = _getChildren (); + for (int i = 0; i < children.length; i++) { + children [i].updateBackgroundMode (); + } +} + +void updateCursorRects (boolean enabled) { + super.updateCursorRects (enabled); + Control [] children = _getChildren (); + for (int i = 0; i < children.length; i++) { + Control control = children [i]; + control.updateCursorRects (enabled && control.isEnabled ()); + } +} + +void updateLayout (boolean all) { + Composite parent = findDeferredControl (); + if (parent != null) { + parent.state |= LAYOUT_CHILD; + return; + } + if ((state & LAYOUT_NEEDED) != 0) { + boolean changed = (state & LAYOUT_CHANGED) != 0; + state &= ~(LAYOUT_NEEDED | LAYOUT_CHANGED); + layout.layout (this, changed); + } + if (all) { + state &= ~LAYOUT_CHILD; + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + children [i].updateLayout (all); + } + } +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Control.java new file mode 100755 index 0000000000..c3666bc092 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Control.java @@ -0,0 +1,4114 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import org.eclipse.swt.*; +import org.eclipse.swt.accessibility.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Control is the abstract superclass of all windowed user interface classes. + * <p> + * <dl> + * <dt><b>Styles:</b> + * <dd>BORDER</dd> + * <dd>LEFT_TO_RIGHT, RIGHT_TO_LEFT</dd> + * <dt><b>Events:</b> + * <dd>DragDetect, FocusIn, FocusOut, Help, KeyDown, KeyUp, MenuDetect, MouseDoubleClick, MouseDown, MouseEnter, + * MouseExit, MouseHover, MouseUp, MouseMove, Move, Paint, Resize, Traverse</dd> + * </dl> + * </p><p> + * Only one of LEFT_TO_RIGHT or RIGHT_TO_LEFT may be specified. + * </p><p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the SWT implementation. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#control">Control snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public abstract class Control extends Widget implements Drawable { + /** + * the handle to the OS resource + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public NSView view; + Composite parent; + String toolTipText; + Object layoutData; + int drawCount; + Menu menu; + float /*double*/ [] foreground, background; + Image backgroundImage; + Font font; + Cursor cursor; + Region region; + NSBezierPath regionPath; + int /*long*/ visibleRgn; + Accessible accessible; + + final static int CLIPPING = 1 << 10; + final static int VISIBLE_REGION = 1 << 12; + + /** + * Magic number comes from experience. There's no API for this value in Cocoa or Carbon. + */ + static final int DEFAULT_DRAG_HYSTERESIS = 5; + +Control () { + /* Do nothing */ +} + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#BORDER + * @see SWT#LEFT_TO_RIGHT + * @see SWT#RIGHT_TO_LEFT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Control (Composite parent, int style) { + super (parent, style); + this.parent = parent; + createWidget (); +} + +boolean acceptsFirstMouse (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + Shell shell = getShell (); + if ((shell.style & SWT.ON_TOP) != 0) return true; + return super.acceptsFirstMouse (id, sel, theEvent); +} + +int /*long*/ accessibilityActionNames(int /*long*/ id, int /*long*/ sel) { + if (accessible != null) { + NSArray returnValue = accessible.internal_accessibilityActionNames(ACC.CHILDID_SELF); + if (returnValue != null) return returnValue.id; + } + + return super.accessibilityActionNames(id, sel); +} + +int /*long*/ accessibilityAttributeNames(int /*long*/ id, int /*long*/ sel) { + + if (id == view.id || (view instanceof NSControl && ((NSControl)view).cell() != null && ((NSControl)view).cell().id == id)) { + if (accessible != null) { + + // First, see if the accessible is going to define a set of attributes for the control. + // If it does, return that. + NSArray returnValue = accessible.internal_accessibilityAttributeNames(ACC.CHILDID_SELF); + if (returnValue != null) return returnValue.id; + + // If not, see if it will override or augment the standard list. + // Help, title, and description can be overridden. + NSMutableArray extraAttributes = NSMutableArray.arrayWithCapacity(3); + extraAttributes.addObject(OS.NSAccessibilityHelpAttribute); + extraAttributes.addObject(OS.NSAccessibilityDescriptionAttribute); + extraAttributes.addObject(OS.NSAccessibilityTitleAttribute); + + for (int i = (int)/*64*/extraAttributes.count() - 1; i >= 0; i--) { + NSString attribute = new NSString(extraAttributes.objectAtIndex(i).id); + if (accessible.internal_accessibilityAttributeValue(attribute, ACC.CHILDID_SELF) == null) { + extraAttributes.removeObjectAtIndex(i); + } + } + + if (extraAttributes.count() > 0) { + int /*long*/ superResult = super.accessibilityAttributeNames(id, sel); + NSArray baseAttributes = new NSArray(superResult); + NSMutableArray mutableAttributes = NSMutableArray.arrayWithCapacity(baseAttributes.count() + 1); + mutableAttributes.addObjectsFromArray(baseAttributes); + + for (int i = 0; i < extraAttributes.count(); i++) { + id currAttribute = extraAttributes.objectAtIndex(i); + if (!mutableAttributes.containsObject(currAttribute)) { + mutableAttributes.addObject(currAttribute); + } + } + + return mutableAttributes.id; + } + } + } + + return super.accessibilityAttributeNames(id, sel); +} + +int /*long*/ accessibilityParameterizedAttributeNames(int /*long*/ id, int /*long*/ sel) { + + if (id == view.id || (view instanceof NSControl && ((NSControl)view).cell() != null && ((NSControl)view).cell().id == id)) { + if (accessible != null) { + NSArray returnValue = accessible.internal_accessibilityParameterizedAttributeNames(ACC.CHILDID_SELF); + if (returnValue != null) return returnValue.id; + } + } + + return super.accessibilityParameterizedAttributeNames(id, sel); +} + +int /*long*/ accessibilityFocusedUIElement(int /*long*/ id, int /*long*/ sel) { + id returnValue = null; + + if (id == view.id || (view instanceof NSControl && ((NSControl)view).cell() != null && ((NSControl)view).cell().id == id)) { + if (accessible != null) { + returnValue = accessible.internal_accessibilityFocusedUIElement(ACC.CHILDID_SELF); + } + } + + // If we had an accessible and it didn't handle the attribute request, let the + // superclass handle it. + if (returnValue == null) + return super.accessibilityFocusedUIElement(id, sel); + else + return returnValue.id; +} + +int /*long*/ accessibilityHitTest(int /*long*/ id, int /*long*/ sel, NSPoint point) { + id returnValue = null; + + if (id == view.id || (view instanceof NSControl && ((NSControl)view).cell() != null && ((NSControl)view).cell().id == id)) { + if (accessible != null) { + returnValue = accessible.internal_accessibilityHitTest(point, ACC.CHILDID_SELF); + } + } + + // If we had an accessible and it didn't handle the attribute request, let the + // superclass handle it. + if (returnValue == null) + return super.accessibilityHitTest(id, sel, point); + else + return returnValue.id; +} + +int /*long*/ accessibilityAttributeValue(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + NSString attribute = new NSString(arg0); + int /*long*/ returnValue = 0; + id returnObject = null; + + if (accessible != null) { + returnObject = accessible.internal_accessibilityAttributeValue(attribute, ACC.CHILDID_SELF); + } + + // If we had an accessible and it didn't handle the attribute request, let the + // superclass handle it. + if (returnObject == null) { + returnValue = super.accessibilityAttributeValue(id, sel, arg0); + } else { + returnValue = returnObject.id; + } + + return returnValue; +} + +int /*long*/ accessibilityAttributeValue_forParameter(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1) { + NSString attribute = new NSString(arg0); + + id returnValue = null; + + if (accessible != null) { + id parameter = new id(arg1); + returnValue = accessible.internal_accessibilityAttributeValue_forParameter(attribute, parameter, ACC.CHILDID_SELF); + } + + // If we had an accessible and it didn't handle the attribute request, let the + // superclass handle it. + if (returnValue == null) + return super.accessibilityAttributeValue_forParameter(id, sel, arg0, arg1); + else + return returnValue.id; +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the control is moved or resized, by sending + * it one of the messages defined in the <code>ControlListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ControlListener + * @see #removeControlListener + */ +public void addControlListener(ControlListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Resize,typedListener); + addListener (SWT.Move,typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when a drag gesture occurs, by sending it + * one of the messages defined in the <code>DragDetectListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see DragDetectListener + * @see #removeDragDetectListener + * + * @since 3.3 + */ +public void addDragDetectListener (DragDetectListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.DragDetect,typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the control gains or loses focus, by sending + * it one of the messages defined in the <code>FocusListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see FocusListener + * @see #removeFocusListener + */ +public void addFocusListener(FocusListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener(SWT.FocusIn,typedListener); + addListener(SWT.FocusOut,typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when help events are generated for the control, + * by sending it one of the messages defined in the + * <code>HelpListener</code> interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see HelpListener + * @see #removeHelpListener + */ +public void addHelpListener (HelpListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Help, typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when keys are pressed and released on the system keyboard, by sending + * it one of the messages defined in the <code>KeyListener</code> + * interface. + * <p> + * When a key listener is added to a control, the control + * will take part in widget traversal. By default, all + * traversal keys (such as the tab key and so on) are + * delivered to the control. In order for a control to take + * part in traversal, it should listen for traversal events. + * Otherwise, the user can traverse into a control but not + * out. Note that native controls such as table and tree + * implement key traversal in the operating system. It is + * not necessary to add traversal listeners for these controls, + * unless you want to override the default traversal. + * </p> + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see KeyListener + * @see #removeKeyListener + */ +public void addKeyListener(KeyListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener(SWT.KeyUp,typedListener); + addListener(SWT.KeyDown,typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the platform-specific context menu trigger + * has occurred, by sending it one of the messages defined in + * the <code>MenuDetectListener</code> interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see MenuDetectListener + * @see #removeMenuDetectListener + * + * @since 3.3 + */ +public void addMenuDetectListener (MenuDetectListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.MenuDetect, typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when mouse buttons are pressed and released, by sending + * it one of the messages defined in the <code>MouseListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see MouseListener + * @see #removeMouseListener + */ +public void addMouseListener(MouseListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener(SWT.MouseDown,typedListener); + addListener(SWT.MouseUp,typedListener); + addListener(SWT.MouseDoubleClick,typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the mouse passes or hovers over controls, by sending + * it one of the messages defined in the <code>MouseTrackListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see MouseTrackListener + * @see #removeMouseTrackListener + */ +public void addMouseTrackListener (MouseTrackListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.MouseEnter,typedListener); + addListener (SWT.MouseExit,typedListener); + addListener (SWT.MouseHover,typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the mouse moves, by sending it one of the + * messages defined in the <code>MouseMoveListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see MouseMoveListener + * @see #removeMouseMoveListener + */ +public void addMouseMoveListener(MouseMoveListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener(SWT.MouseMove,typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the mouse wheel is scrolled, by sending + * it one of the messages defined in the + * <code>MouseWheelListener</code> interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see MouseWheelListener + * @see #removeMouseWheelListener + * + * @since 3.3 + */ +public void addMouseWheelListener (MouseWheelListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.MouseWheel, typedListener); +} + +void addRelation (Control control) { +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the receiver needs to be painted, by sending it + * one of the messages defined in the <code>PaintListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see PaintListener + * @see #removePaintListener + */ +public void addPaintListener(PaintListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener(SWT.Paint,typedListener); +} + +static final double SYNTHETIC_BOLD = -2.5; +static final double SYNTHETIC_ITALIC = 0.2; + +void addTraits(NSMutableDictionary dict, Font font) { + if ((font.extraTraits & OS.NSBoldFontMask) != 0) { + dict.setObject(NSNumber.numberWithDouble(SYNTHETIC_BOLD), OS.NSStrokeWidthAttributeName); + } + if ((font.extraTraits & OS.NSItalicFontMask) != 0) { + dict.setObject(NSNumber.numberWithDouble(SYNTHETIC_ITALIC), OS.NSObliquenessAttributeName); + } +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when traversal events occur, by sending it + * one of the messages defined in the <code>TraverseListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see TraverseListener + * @see #removeTraverseListener + */ +public void addTraverseListener (TraverseListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Traverse,typedListener); +} + +boolean becomeFirstResponder (int /*long*/ id, int /*long*/ sel) { + if ((state & DISABLED) != 0) return false; + return super.becomeFirstResponder (id, sel); +} + +void calculateVisibleRegion (NSView view, int /*long*/ visibleRgn, boolean clipChildren) { + int /*long*/ tempRgn = OS.NewRgn (); + if (!view.isHiddenOrHasHiddenAncestor() && isDrawing()) { + int /*long*/ childRgn = OS.NewRgn (); + NSWindow window = view.window (); + NSView contentView = window.contentView(); + NSView frameView = contentView.superview(); + NSRect bounds = contentView.visibleRect(); + bounds = contentView.convertRect_toView_(bounds, view); + short[] rect = new short[4]; + OS.SetRect(rect, (short)bounds.x, (short)bounds.y, (short)(bounds.x + bounds.width), (short)(bounds.y + bounds.height)); + OS.RectRgn(visibleRgn, rect); + NSView tempView = view, lastControl = null; + while (tempView.id != frameView.id) { + bounds = tempView.visibleRect(); + bounds = tempView.convertRect_toView_(bounds, view); + OS.SetRect(rect, (short)bounds.x, (short)bounds.y, (short)(bounds.x + bounds.width), (short)(bounds.y + bounds.height)); + OS.RectRgn(tempRgn, rect); + OS.SectRgn (tempRgn, visibleRgn, visibleRgn); + if (OS.EmptyRgn (visibleRgn)) break; + if (clipChildren || tempView.id != view.id) { + NSArray subviews = tempView.subviews(); + int /*long*/ count = subviews.count(); + for (int i = 0; i < count; i++) { + NSView child = new NSView (subviews.objectAtIndex(count - i - 1)); + if (lastControl != null && child.id == lastControl.id) break; + if (child.isHidden()) continue; + bounds = child.visibleRect(); + bounds = child.convertRect_toView_(bounds, view); + OS.SetRect(rect, (short)bounds.x, (short)bounds.y, (short)(bounds.x + bounds.width), (short)(bounds.y + bounds.height)); + OS.RectRgn(tempRgn, rect); + OS.UnionRgn (tempRgn, childRgn, childRgn); + } + } + lastControl = tempView; + tempView = tempView.superview(); + } + OS.DiffRgn (visibleRgn, childRgn, visibleRgn); + OS.DisposeRgn (childRgn); + } else { + OS.CopyRgn (tempRgn, visibleRgn); + } + OS.DisposeRgn (tempRgn); +} + +void checkBackground () { + Shell shell = getShell (); + if (this == shell) return; + state &= ~PARENT_BACKGROUND; + Composite composite = parent; + do { + int mode = composite.backgroundMode; + if (mode != 0) { + if (mode == SWT.INHERIT_DEFAULT) { + Control control = this; + do { + if ((control.state & THEME_BACKGROUND) == 0) { + return; + } + control = control.parent; + } while (control != composite); + } + state |= PARENT_BACKGROUND; + return; + } + if (composite == shell) break; + composite = composite.parent; + } while (true); +} + +void checkBuffered () { + style |= SWT.DOUBLE_BUFFERED; +} + +void checkToolTip (Widget target) { + if (isVisible () && display.tooltipControl == this && (target == null || display.tooltipTarget == target)) { + Shell shell = getShell (); + shell.sendToolTipEvent (false); + shell.sendToolTipEvent (true); + } +} + +/** + * Returns the preferred size of the receiver. + * <p> + * The <em>preferred size</em> of a control is the size that it would + * best be displayed at. The width hint and height hint arguments + * allow the caller to ask a control questions such as "Given a particular + * width, how high does the control need to be to show all of the contents?" + * To indicate that the caller does not wish to constrain a particular + * dimension, the constant <code>SWT.DEFAULT</code> is passed for the hint. + * </p> + * + * @param wHint the width hint (can be <code>SWT.DEFAULT</code>) + * @param hHint the height hint (can be <code>SWT.DEFAULT</code>) + * @return the preferred size of the control + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Layout + * @see #getBorderWidth + * @see #getBounds + * @see #getSize + * @see #pack(boolean) + * @see "computeTrim, getClientArea for controls that implement them" + */ +public Point computeSize (int wHint, int hHint) { + return computeSize (wHint, hHint, true); +} + +/** + * Returns the preferred size of the receiver. + * <p> + * The <em>preferred size</em> of a control is the size that it would + * best be displayed at. The width hint and height hint arguments + * allow the caller to ask a control questions such as "Given a particular + * width, how high does the control need to be to show all of the contents?" + * To indicate that the caller does not wish to constrain a particular + * dimension, the constant <code>SWT.DEFAULT</code> is passed for the hint. + * </p><p> + * If the changed flag is <code>true</code>, it indicates that the receiver's + * <em>contents</em> have changed, therefore any caches that a layout manager + * containing the control may have been keeping need to be flushed. When the + * control is resized, the changed flag will be <code>false</code>, so layout + * manager caches can be retained. + * </p> + * + * @param wHint the width hint (can be <code>SWT.DEFAULT</code>) + * @param hHint the height hint (can be <code>SWT.DEFAULT</code>) + * @param changed <code>true</code> if the control's contents have changed, and <code>false</code> otherwise + * @return the preferred size of the control. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Layout + * @see #getBorderWidth + * @see #getBounds + * @see #getSize + * @see #pack(boolean) + * @see "computeTrim, getClientArea for controls that implement them" + */ +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + int width = DEFAULT_WIDTH; + int height = DEFAULT_HEIGHT; + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + int border = getBorderWidth (); + width += border * 2; + height += border * 2; + return new Point (width, height); +} + +Widget computeTabGroup () { + if (isTabGroup()) return this; + return parent.computeTabGroup (); +} + +Widget[] computeTabList() { + if (isTabGroup()) { + if (getVisible() && getEnabled()) { + return new Widget[] {this}; + } + } + return new Widget[0]; +} + +Control computeTabRoot () { + Control[] tabList = parent._getTabList(); + if (tabList != null) { + int index = 0; + while (index < tabList.length) { + if (tabList [index] == this) break; + index++; + } + if (index == tabList.length) { + if (isTabGroup ()) return this; + } + } + return parent.computeTabRoot (); +} + +NSView contentView () { + return view; +} + +NSAttributedString createString (String string, Font font, float /*double*/ [] foreground, int style, boolean enabled, boolean mnemonics) { + NSMutableDictionary dict = ((NSMutableDictionary)new NSMutableDictionary().alloc()).initWithCapacity(5); + if (font == null) font = this.font != null ? this.font : defaultFont(); + dict.setObject (font.handle, OS.NSFontAttributeName); + addTraits(dict, font); + if (enabled) { + if (foreground != null) { + NSColor color = NSColor.colorWithDeviceRed(foreground[0], foreground[1], foreground[2], foreground[3]); + dict.setObject (color, OS.NSForegroundColorAttributeName); + } + } else { + dict.setObject (NSColor.disabledControlTextColor (), OS.NSForegroundColorAttributeName); + } + if (style != 0) { + NSMutableParagraphStyle paragraphStyle = (NSMutableParagraphStyle)new NSMutableParagraphStyle ().alloc ().init (); + paragraphStyle.setLineBreakMode (OS.NSLineBreakByClipping); + int alignment = SWT.LEFT; + if ((style & SWT.CENTER) != 0) { + alignment = OS.NSCenterTextAlignment; + } else if ((style & SWT.RIGHT) != 0) { + alignment = OS.NSRightTextAlignment; + } + paragraphStyle.setAlignment (alignment); + dict.setObject (paragraphStyle, OS.NSParagraphStyleAttributeName); + paragraphStyle.release (); + } + int length = string.length (); + char [] chars = new char [length]; + string.getChars (0, chars.length, chars, 0); + if (mnemonics) length = fixMnemonic (chars); + NSString str = ((NSString)new NSString().alloc()).initWithCharacters(chars, length); + NSAttributedString attribStr = ((NSAttributedString) new NSAttributedString ().alloc ()).initWithString (str, dict); + str.release(); + dict.release(); + return attribStr; +} + +void createWidget () { + state |= DRAG_DETECT; + checkOrientation (parent); + super.createWidget (); + checkBackground (); + checkBuffered (); + setDefaultFont (); + setZOrder (); + setRelations (); + display.clearPool (); +} + +Color defaultBackground () { + return display.getWidgetColor (SWT.COLOR_WIDGET_BACKGROUND); +} + +Font defaultFont () { + if (display.smallFonts) return display.getSystemFont (); + return Font.cocoa_new (display, defaultNSFont ()); +} + +Color defaultForeground () { + return display.getWidgetColor (SWT.COLOR_WIDGET_FOREGROUND); +} + +NSFont defaultNSFont () { + return display.getSystemFont().handle; +} + +void deregister () { + super.deregister (); + display.removeWidget (view); +} + +void destroyWidget () { + NSView view = topView (); + view.removeFromSuperview (); + releaseHandle (); +} + +void doCommandBySelector (int /*long*/ id, int /*long*/ sel, int /*long*/ selector) { + if (view.window ().firstResponder ().id == id) { + if (imeInComposition ()) return; + Shell s = this.getShell(); + NSEvent nsEvent = NSApplication.sharedApplication ().currentEvent (); + if (nsEvent != null && nsEvent.type () == OS.NSKeyDown) { + /* + * Feature in Cocoa. Pressing Alt+UpArrow invokes doCommandBySelector + * twice, with selectors moveBackward and moveToBeginningOfParagraph + * (Alt+DownArrow behaves similarly). In order to avoid sending + * multiple events for these keys, do not send a KeyDown if we already sent one + * during this keystroke. This rule does not apply if the command key + * is down, because we likely triggered the current key sequence via flagsChanged. + */ + int /*long*/ modifiers = nsEvent.modifierFlags(); + if (s.keyInputHappened == false || (modifiers & OS.NSCommandKeyMask) != 0) { + s.keyInputHappened = true; + boolean [] consume = new boolean [1]; + if (translateTraversal (nsEvent.keyCode (), nsEvent, consume)) return; + if (isDisposed ()) return; + if (!sendKeyEvent (nsEvent, SWT.KeyDown)) return; + if (consume [0]) return; + } + } + if ((state & CANVAS) != 0) return; + } + super.doCommandBySelector (id, sel, selector); +} + +/** + * Detects a drag and drop gesture. This method is used + * to detect a drag gesture when called from within a mouse + * down listener. + * + * <p>By default, a drag is detected when the gesture + * occurs anywhere within the client area of a control. + * Some controls, such as tables and trees, override this + * behavior. In addition to the operating system specific + * drag gesture, they require the mouse to be inside an + * item. Custom widget writers can use <code>setDragDetect</code> + * to disable the default detection, listen for mouse down, + * and then call <code>dragDetect()</code> from within the + * listener to conditionally detect a drag. + * </p> + * + * @param event the mouse down event + * + * @return <code>true</code> if the gesture occurred, and <code>false</code> otherwise. + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT when the event is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see DragDetectListener + * @see #addDragDetectListener + * + * @see #getDragDetect + * @see #setDragDetect + * + * @since 3.3 + */ +public boolean dragDetect (Event event) { + checkWidget (); + if (event == null) error (SWT.ERROR_NULL_ARGUMENT); + return dragDetect (event.button, event.count, event.stateMask, event.x, event.y); +} + +/** + * Detects a drag and drop gesture. This method is used + * to detect a drag gesture when called from within a mouse + * down listener. + * + * <p>By default, a drag is detected when the gesture + * occurs anywhere within the client area of a control. + * Some controls, such as tables and trees, override this + * behavior. In addition to the operating system specific + * drag gesture, they require the mouse to be inside an + * item. Custom widget writers can use <code>setDragDetect</code> + * to disable the default detection, listen for mouse down, + * and then call <code>dragDetect()</code> from within the + * listener to conditionally detect a drag. + * </p> + * + * @param event the mouse down event + * + * @return <code>true</code> if the gesture occurred, and <code>false</code> otherwise. + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT when the event is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see DragDetectListener + * @see #addDragDetectListener + * + * @see #getDragDetect + * @see #setDragDetect + * + * @since 3.3 + */ +public boolean dragDetect (MouseEvent event) { + checkWidget (); + if (event == null) error (SWT.ERROR_NULL_ARGUMENT); + return dragDetect (event.button, event.count, event.stateMask, event.x, event.y); +} + +boolean dragDetect (int button, int count, int stateMask, int x, int y) { + if (button != 1 || count != 1) return false; + if (!dragDetect (x, y, false, null)) return false; + return sendDragEvent (button, stateMask, x, y); +} + +boolean dragDetect (int x, int y, boolean filter, boolean [] consume) { + /** + * Feature in Cocoa. Mouse drag events do not account for hysteresis. + * As soon as the mouse drags a mouse dragged event is fired. Fix is to + * check for another mouse drag event that is at least 5 pixels away + * from the start of the drag. + */ + NSApplication application = NSApplication.sharedApplication(); + boolean dragging = false; + int /*long*/ eventType = OS.NSLeftMouseDown; + float /*double*/ dragX = x; + float /*double*/ dragY = y; + + /** + * To check for an actual drag we need to pull off mouse moved and mouse up events + * to detect if the user dragged outside of a 10 x 10 box centered on the mouse down location. + * We still want the view to see the events, so save them and re-post when done checking. + */ + NSEvent mouseUpEvent = null; + NSMutableArray dragEvents = NSMutableArray.arrayWithCapacity(10); + + while (eventType != OS.NSLeftMouseUp) { + NSEvent event = application.nextEventMatchingMask((OS.NSLeftMouseUpMask | OS.NSLeftMouseDraggedMask), + NSDate.distantFuture(), OS.NSEventTrackingRunLoopMode, true); + eventType = event.type(); + + if (eventType == OS.NSLeftMouseDragged) { + dragEvents.addObject(event); + NSPoint windowLoc = event.locationInWindow(); + NSPoint viewLoc = view.convertPoint_fromView_(windowLoc, null); + if (!view.isFlipped ()) { + viewLoc.y = view.bounds().height - viewLoc.y; + } + if ((Math.abs(viewLoc.x - dragX) > DEFAULT_DRAG_HYSTERESIS) || (Math.abs(viewLoc.y - dragY) > DEFAULT_DRAG_HYSTERESIS)) { + dragging = true; + break; + } + } else if (eventType == OS.NSLeftMouseUp) { + mouseUpEvent = event; + } + } + + // Push back any events we took out of the queue so the control can receive them. + if (mouseUpEvent != null) application.postEvent(mouseUpEvent, true); + + if (dragEvents.count() > 0) { + while (dragEvents.count() > 0) { + NSEvent currEvent = new NSEvent(dragEvents.objectAtIndex(dragEvents.count() - 1).id); + dragEvents.removeLastObject(); + application.postEvent(currEvent, true); + } + } + + return dragging; +} + +boolean drawGripper (int x, int y, int width, int height, boolean vertical) { + return false; +} + +void drawWidget (int /*long*/ id, NSGraphicsContext context, NSRect rect) { + if (id != paintView().id) return; + if (!hooks (SWT.Paint) && !filters (SWT.Paint)) return; + + /* Send paint event */ + GCData data = new GCData (); + data.paintRect = rect; + GC gc = GC.cocoa_new (this, data); + Event event = new Event (); + event.gc = gc; + event.x = (int)rect.x; + event.y = (int)rect.y; + event.width = (int)rect.width; + event.height = (int)rect.height; + sendEvent (SWT.Paint, event); + event.gc = null; + gc.dispose (); +} + +void enableWidget (boolean enabled) { + if (view instanceof NSControl) { + ((NSControl)view).setEnabled(enabled); + } + updateCursorRects (isEnabled ()); +} + +boolean equals(float /*double*/ [] color1, float /*double*/ [] color2) { + if (color1 == color2) return true; + if (color1 == null) return color2 == null; + if (color2 == null) return color1 == null; + for (int i = 0; i < color1.length; i++) { + if (color1 [i] != color2 [i]) return false; + } + return true; +} + +NSView eventView () { + return view; +} + +void fillBackground (NSView view, NSGraphicsContext context, NSRect rect, int imgHeight) { + Control control = findBackgroundControl(); + if (control == null) control = this; + Image image = control.backgroundImage; + if (image != null && !image.isDisposed()) { + context.saveGraphicsState(); + NSColor.colorWithPatternImage(image.handle).setFill(); + NSPoint phase = new NSPoint(); + NSView controlView = control.view; + if (imgHeight == -1) { + NSView contentView = controlView.window().contentView(); + phase = controlView.convertPoint_toView_(phase, contentView); + phase.y = contentView.bounds().height - phase.y; + } else { + phase = view.convertPoint_toView_(phase, controlView); + phase.y += imgHeight - backgroundImage.getBounds().height; + } + context.setPatternPhase(phase); + NSBezierPath.fillRect(rect); + context.restoreGraphicsState(); + return; + } + + float /*double*/ [] background = control.background; + float /*double*/ alpha; + if (background == null) { + background = control.defaultBackground ().handle; + alpha = getThemeAlpha (); + } else { + alpha = background[3]; + } + context.saveGraphicsState (); + NSColor.colorWithDeviceRed (background [0], background [1], background [2], alpha).setFill (); + NSBezierPath.fillRect (rect); + context.restoreGraphicsState (); +} + +Cursor findCursor () { + if (cursor != null) return cursor; + return parent.findCursor (); +} + +Control findBackgroundControl () { + if (backgroundImage != null || background != null) return this; + return (state & PARENT_BACKGROUND) != 0 ? parent.findBackgroundControl () : null; +} + +Menu [] findMenus (Control control) { + if (menu != null && this != control) return new Menu [] {menu}; + return new Menu [0]; +} + +Widget findTooltip (NSPoint pt) { + return this; +} + +void fixChildren (Shell newShell, Shell oldShell, Decorations newDecorations, Decorations oldDecorations, Menu [] menus) { + oldShell.fixShell (newShell, this); + oldDecorations.fixDecorations (newDecorations, this, menus); +} + +void fixFocus (Control focusControl) { + Shell shell = getShell (); + Control control = this; + while (control != shell && (control = control.parent) != null) { + if (control.setFocus ()) return; + } + shell.setSavedFocus (focusControl); +// int window = OS.GetControlOwner (handle); +// OS.ClearKeyboardFocus (window); +} + +void flagsChanged (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (view.window ().firstResponder ().id == id) { + if ((state & SAFARI_EVENTS_FIX) == 0) { + Shell s = this.getShell(); + s.keyInputHappened = false; + int mask = 0; + NSEvent nsEvent = new NSEvent (theEvent); + int /*long*/ modifiers = nsEvent.modifierFlags (); + int keyCode = Display.translateKey (nsEvent.keyCode ()); + switch (keyCode) { + case SWT.ALT: mask = OS.NSAlternateKeyMask; break; + case SWT.CONTROL: mask = OS.NSControlKeyMask; break; + case SWT.COMMAND: mask = OS.NSCommandKeyMask; break; + case SWT.SHIFT: mask = OS.NSShiftKeyMask; break; + case SWT.CAPS_LOCK: + Event event = new Event(); + event.keyCode = keyCode; + setInputState (event, nsEvent, SWT.KeyDown); + sendKeyEvent (SWT.KeyDown, event); + setInputState (event, nsEvent, SWT.KeyUp); + sendKeyEvent (SWT.KeyUp, event); + break; + } + if (mask != 0) { + s.keyInputHappened = true; + int type = (mask & modifiers) != 0 ? SWT.KeyDown : SWT.KeyUp; + if (type == SWT.KeyDown) s.keyInputHappened = true; + Event event = new Event(); + event.keyCode = keyCode; + setInputState (event, nsEvent, type); + if (!sendKeyEvent (type, event)) return; + } + } + } + super.flagsChanged (id, sel, theEvent); +} + +NSView focusView () { + return view; +} + +/** + * Forces the receiver to have the <em>keyboard focus</em>, causing + * all keyboard events to be delivered to it. + * + * @return <code>true</code> if the control got focus, and <code>false</code> if it was unable to. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setFocus + */ +public boolean forceFocus () { + checkWidget(); + if (display.focusEvent == SWT.FocusOut) return false; + Decorations shell = menuShell (); + shell.setSavedFocus (this); + if (!isEnabled () || !isVisible () || !isActive ()) return false; + if (isFocusControl ()) return true; + shell.setSavedFocus (null); + NSView focusView = focusView (); + if (!focusView.canBecomeKeyView()) return false; + boolean result = view.window ().makeFirstResponder (focusView); + if (isDisposed ()) return false; + shell.bringToTop (false); + if (isDisposed ()) return false; + shell.setSavedFocus (this); + return result; +} + +/** + * Returns the accessible object for the receiver. + * If this is the first time this object is requested, + * then the object is created and returned. + * + * @return the accessible object + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Accessible#addAccessibleListener + * @see Accessible#addAccessibleControlListener + * + * @since 2.0 + */ +public Accessible getAccessible () { + checkWidget (); + if (accessible == null) accessible = new_Accessible (this); + return accessible; +} + +/** + * Returns the receiver's background color. + * <p> + * Note: This operation is a hint and may be overridden by the platform. + * For example, on some versions of Windows the background of a TabFolder, + * is a gradient rather than a solid color. + * </p> + * @return the background color + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Color getBackground () { + checkWidget(); + Control control = findBackgroundControl (); + if (control == null) control = this; + return control.getBackgroundColor (); +} + +Color getBackgroundColor () { + return background != null ? Color.cocoa_new (display, background) : defaultBackground (); +} + +/** + * Returns the receiver's background image. + * + * @return the background image + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public Image getBackgroundImage () { + checkWidget(); + Control control = findBackgroundControl (); + if (control == null) control = this; + return control.backgroundImage; +} + +/** + * Returns the receiver's border width. + * + * @return the border width + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getBorderWidth () { + checkWidget(); + return 0; +} + +/** + * Returns a rectangle describing the receiver's size and location + * relative to its parent (or its display if its parent is null), + * unless the receiver is a shell. In this case, the location is + * relative to the display. + * + * @return the receiver's bounding rectangle + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Rectangle getBounds () { + checkWidget(); + NSRect rect = topView().frame(); + return new Rectangle((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height); +} + +/** + * Returns <code>true</code> if the receiver is detecting + * drag gestures, and <code>false</code> otherwise. + * + * @return the receiver's drag detect state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.3 + */ +public boolean getDragDetect () { + checkWidget (); + return (state & DRAG_DETECT) != 0; +} + +boolean getDrawing () { + return drawCount <= 0; +} + +/** + * Returns the receiver's cursor, or null if it has not been set. + * <p> + * When the mouse pointer passes over a control its appearance + * is changed to match the control's cursor. + * </p> + * + * @return the receiver's cursor or <code>null</code> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.3 + */ +public Cursor getCursor () { + checkWidget(); + return cursor; +} + +/** + * Returns <code>true</code> if the receiver is enabled, and + * <code>false</code> otherwise. A disabled control is typically + * not selectable from the user interface and draws with an + * inactive or "grayed" look. + * + * @return the receiver's enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #isEnabled + */ +public boolean getEnabled () { + checkWidget(); + return (state & DISABLED) == 0; +} + +/** + * Returns the font that the receiver will use to paint textual information. + * + * @return the receiver's font + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Font getFont () { + checkWidget(); + return font != null ? font : defaultFont (); +} + +/** + * Returns the foreground color that the receiver will use to draw. + * + * @return the receiver's foreground color + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Color getForeground () { + checkWidget(); + return getForegroundColor (); +} + +Color getForegroundColor () { + return foreground != null ? Color.cocoa_new (display, foreground) : defaultForeground (); +} + +/** + * Returns layout data which is associated with the receiver. + * + * @return the receiver's layout data + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Object getLayoutData () { + checkWidget(); + return layoutData; +} + +/** + * Returns a point describing the receiver's location relative + * to its parent (or its display if its parent is null), unless + * the receiver is a shell. In this case, the point is + * relative to the display. + * + * @return the receiver's location + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Point getLocation () { + checkWidget(); + NSRect rect = topView().frame(); + return new Point((int)rect.x, (int)rect.y); +} + +/** + * Returns the receiver's pop up menu if it has one, or null + * if it does not. All controls may optionally have a pop up + * menu that is displayed when the user requests one for + * the control. The sequence of key strokes, button presses + * and/or button releases that are used to request a pop up + * menu is platform specific. + * + * @return the receiver's menu + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Menu getMenu () { + checkWidget(); + return menu; +} + +int getMininumHeight () { + return 0; +} + +/** + * Returns the receiver's monitor. + * + * @return the receiver's monitor + * + * @since 3.0 + */ +public Monitor getMonitor () { + checkWidget(); + Monitor [] monitors = display.getMonitors (); + if (monitors.length == 1) return monitors [0]; + int index = -1, value = -1; + Rectangle bounds = getBounds (); + if (this != getShell ()) { + bounds = display.map (this.parent, null, bounds); + } + for (int i=0; i<monitors.length; i++) { + Rectangle rect = bounds.intersection (monitors [i].getBounds ()); + int area = rect.width * rect.height; + if (area > 0 && area > value) { + index = i; + value = area; + } + } + if (index >= 0) return monitors [index]; + int centerX = bounds.x + bounds.width / 2, centerY = bounds.y + bounds.height / 2; + for (int i=0; i<monitors.length; i++) { + Rectangle rect = monitors [i].getBounds (); + int x = centerX < rect.x ? rect.x - centerX : centerX > rect.x + rect.width ? centerX - rect.x - rect.width : 0; + int y = centerY < rect.y ? rect.y - centerY : centerY > rect.y + rect.height ? centerY - rect.y - rect.height : 0; + int distance = x * x + y * y; + if (index == -1 || distance < value) { + index = i; + value = distance; + } + } + return monitors [index]; +} + +/** + * Returns the receiver's parent, which must be a <code>Composite</code> + * or null when the receiver is a shell that was created with null or + * a display for a parent. + * + * @return the receiver's parent + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Composite getParent () { + checkWidget(); + return parent; +} + +Control [] getPath () { + int count = 0; + Shell shell = getShell (); + Control control = this; + while (control != shell) { + count++; + control = control.parent; + } + control = this; + Control [] result = new Control [count]; + while (control != shell) { + result [--count] = control; + control = control.parent; + } + return result; +} + +NSBezierPath getPath(Region region) { + if (region == null) return null; + return getPath(region.handle); +} + +NSBezierPath getPath(int /*long*/ region) { + Callback callback = new Callback(this, "regionToRects", 4); + if (callback.getAddress() == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + NSBezierPath path = NSBezierPath.bezierPath(); + path.retain(); + OS.QDRegionToRects(region, OS.kQDParseRegionFromTopLeft, callback.getAddress(), path.id); + callback.dispose(); + if (path.isEmpty()) path.appendBezierPathWithRect(new NSRect()); + return path; +} + +/** + * Returns the region that defines the shape of the control, + * or null if the control has the default shape. + * + * @return the region that defines the shape of the shell (or null) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public Region getRegion () { + checkWidget (); + return region; +} + +/** + * Returns the receiver's shell. For all controls other than + * shells, this simply returns the control's nearest ancestor + * shell. Shells return themselves, even if they are children + * of other shells. + * + * @return the receiver's shell + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #getParent + */ +public Shell getShell () { + checkWidget(); + return parent.getShell (); +} + +/** + * Returns a point describing the receiver's size. The + * x coordinate of the result is the width of the receiver. + * The y coordinate of the result is the height of the + * receiver. + * + * @return the receiver's size + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Point getSize () { + checkWidget(); + NSRect rect = topView().frame(); + return new Point((int)rect.width, (int)rect.height); +} + +float getThemeAlpha () { + return 1 * parent.getThemeAlpha (); +} + +/** + * Returns the receiver's tool tip text, or null if it has + * not been set. + * + * @return the receiver's tool tip text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getToolTipText () { + checkWidget(); + return toolTipText; +} + +/** + * Returns <code>true</code> if the receiver is visible, and + * <code>false</code> otherwise. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, this method + * may still indicate that it is considered visible even though + * it may not actually be showing. + * </p> + * + * @return the receiver's visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getVisible () { + checkWidget(); + return (state & HIDDEN) == 0; +} + +int /*long*/ getVisibleRegion () { + if (visibleRgn == 0) { + visibleRgn = OS.NewRgn (); + calculateVisibleRegion (view, visibleRgn, true); + } + int /*long*/ result = OS.NewRgn (); + OS.CopyRgn (visibleRgn, result); + return result; +} + +boolean hasBorder () { + return (style & SWT.BORDER) != 0; +} + +boolean hasFocus () { + return display.getFocusControl() == this; +} + +int /*long*/ hitTest (int /*long*/ id, int /*long*/ sel, NSPoint point) { + if ((state & DISABLED) != 0) return 0; + if (!isActive ()) return 0; + if (regionPath != null) { + NSView superview = new NSView(id).superview(); + if (superview != null) { + NSPoint pt = superview.convertPoint_toView_(point, view); + if (!view.isFlipped ()) { + pt.y = view.bounds().height - pt.y; + } + if (!regionPath.containsPoint(pt)) return 0; + } + } + return super.hitTest(id, sel, point); +} + +boolean imeInComposition () { + return false; +} + +boolean insertText (int /*long*/ id, int /*long*/ sel, int /*long*/ string) { + if (view.window ().firstResponder ().id == id) { + Shell s = this.getShell(); + NSEvent nsEvent = NSApplication.sharedApplication ().currentEvent (); + if (nsEvent != null) { + int /*long*/ type = nsEvent.type (); + if ((!s.keyInputHappened && type == OS.NSKeyDown) || type == OS.NSSystemDefined) { + NSString str = new NSString (string); + if (str.isKindOfClass (OS.objc_getClass ("NSAttributedString"))) { + str = new NSAttributedString (string).string (); + } + int length = (int)/*64*/str.length (); + char[] buffer = new char [length]; + str.getCharacters(buffer); + for (int i = 0; i < buffer.length; i++) { + s.keyInputHappened = true; + Event event = new Event (); + if (i == 0 && type == OS.NSKeyDown) setKeyState (event, SWT.KeyDown, nsEvent); + event.character = buffer [i]; + if (!sendKeyEvent (SWT.KeyDown, event)) return false; + } + } + } + if ((state & CANVAS) != 0) return true; + } + return super.insertText (id, sel, string); +} + +/** + * Invokes platform specific functionality to allocate a new GC handle. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>Control</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @param data the platform specific GC data + * @return the platform specific GC handle + */ +public int /*long*/ internal_new_GC (GCData data) { + checkWidget(); + NSView view = paintView(); + int /*long*/ context = 0; + if (data != null && data.paintRect != null) { + NSGraphicsContext graphicsContext = NSGraphicsContext.currentContext(); + context = graphicsContext.id; + if (!view.isFlipped()) data.state &= ~VISIBLE_REGION; + } else { + NSGraphicsContext graphicsContext = NSGraphicsContext.graphicsContextWithWindow (view.window ()); + NSGraphicsContext flippedContext = NSGraphicsContext.graphicsContextWithGraphicsPort(graphicsContext.graphicsPort(), true); + graphicsContext = flippedContext; + context = graphicsContext.id; + if (data != null) { + data.flippedContext = flippedContext; + data.state &= ~VISIBLE_REGION; + data.visibleRgn = getVisibleRegion(); + display.addContext (data); + } + } + if (data != null) { + int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT; + if ((data.style & mask) == 0) { + data.style |= style & (mask | SWT.MIRRORED); + } + data.device = display; + data.thread = display.thread; + data.view = view; + data.foreground = getForegroundColor ().handle; + Control control = findBackgroundControl (); + if (control == null) control = this; + data.background = control.getBackgroundColor ().handle; + data.font = font != null ? font : defaultFont (); + } + return context; +} + +/** + * Invokes platform specific functionality to dispose a GC handle. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>Control</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @param hDC the platform specific GC handle + * @param data the platform specific GC data + */ +public void internal_dispose_GC (int /*long*/ context, GCData data) { + checkWidget (); + NSGraphicsContext graphicsContext = new NSGraphicsContext (context); + display.removeContext (data); + if (data != null) { + if (data.paintRect == null) graphicsContext.flushGraphics (); + if (data.visibleRgn != 0) OS.DisposeRgn(data.visibleRgn); + data.visibleRgn = 0; + } +} + +void invalidateChildrenVisibleRegion () { +} + +void invalidateVisibleRegion () { + int index = 0; + Control[] siblings = parent._getChildren (); + while (index < siblings.length && siblings [index] != this) index++; + for (int i=index; i<siblings.length; i++) { + Control sibling = siblings [i]; + sibling.resetVisibleRegion (); + sibling.invalidateChildrenVisibleRegion (); + } + parent.resetVisibleRegion (); +} + +boolean isActive () { + return getShell().getModalShell() == null; +} + +/* + * Answers a boolean indicating whether a Label that precedes the receiver in + * a layout should be read by screen readers as the recevier's label. + */ +boolean isDescribedByLabel () { + return true; +} + +boolean isDrawing () { + return getDrawing() && parent.isDrawing(); +} + +/** + * Returns <code>true</code> if the receiver is enabled and all + * ancestors up to and including the receiver's nearest ancestor + * shell are enabled. Otherwise, <code>false</code> is returned. + * A disabled control is typically not selectable from the user + * interface and draws with an inactive or "grayed" look. + * + * @return the receiver's enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #getEnabled + */ +public boolean isEnabled () { + checkWidget(); + return getEnabled () && parent.isEnabled (); +} + +boolean isEnabledCursor () { + return isEnabled (); +} + +boolean isFocusAncestor (Control control) { + while (control != null && control != this && !(control instanceof Shell)) { + control = control.parent; + } + return control == this; +} + +/** + * Returns <code>true</code> if the receiver has the user-interface + * focus, and <code>false</code> otherwise. + * + * @return the receiver's focus state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean isFocusControl () { + checkWidget(); + Control focusControl = display.focusControl; + if (focusControl != null && !focusControl.isDisposed ()) { + return this == focusControl; + } + return hasFocus (); +} + +boolean isObscured () { + int /*long*/ visibleRgn = getVisibleRegion(), boundsRgn = OS.NewRgn(); + short[] rect = new short[4]; + NSRect bounds = view.visibleRect(); + OS.SetRect(rect, (short)bounds.x, (short)bounds.y, (short)(bounds.x + bounds.width), (short)(bounds.y + bounds.height)); + OS.RectRgn(boundsRgn, rect); + OS.DiffRgn(boundsRgn, visibleRgn, boundsRgn); + boolean obscured = !OS.EmptyRgn (boundsRgn); + OS.DisposeRgn(boundsRgn); + OS.DisposeRgn(visibleRgn); + return obscured; +} + +/** + * Returns <code>true</code> if the underlying operating + * system supports this reparenting, otherwise <code>false</code> + * + * @return <code>true</code> if the widget can be reparented, otherwise <code>false</code> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean isReparentable () { + checkWidget(); + return true; +} + +boolean isShowing () { + /* + * This is not complete. Need to check if the + * widget is obscurred by a parent or sibling. + */ + if (!isVisible ()) return false; + Control control = this; + while (control != null) { + Point size = control.getSize (); + if (size.x == 0 || size.y == 0) { + return false; + } + control = control.parent; + } + return true; +} + +boolean isTabGroup () { + Control [] tabList = parent._getTabList (); + if (tabList != null) { + for (int i=0; i<tabList.length; i++) { + if (tabList [i] == this) return true; + } + } + int code = traversalCode (0, null); + if ((code & (SWT.TRAVERSE_ARROW_PREVIOUS | SWT.TRAVERSE_ARROW_NEXT)) != 0) return false; + return (code & (SWT.TRAVERSE_TAB_PREVIOUS | SWT.TRAVERSE_TAB_NEXT)) != 0; +} + +boolean isTabItem () { + Control [] tabList = parent._getTabList (); + if (tabList != null) { + for (int i=0; i<tabList.length; i++) { + if (tabList [i] == this) return false; + } + } + int code = traversalCode (0, null); + return (code & (SWT.TRAVERSE_ARROW_PREVIOUS | SWT.TRAVERSE_ARROW_NEXT)) != 0; +} + +boolean isTrim (NSView view) { + return false; +} + +/** + * Returns <code>true</code> if the receiver is visible and all + * ancestors up to and including the receiver's nearest ancestor + * shell are visible. Otherwise, <code>false</code> is returned. + * + * @return the receiver's visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #getVisible + */ +public boolean isVisible () { + checkWidget(); + return getVisible () && parent.isVisible (); +} + +void keyDown (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (view.window ().firstResponder ().id == id) { + Shell s = this.getShell(); + s.keyInputHappened = false; + boolean textInput = OS.objc_msgSend (id, OS.sel_conformsToProtocol_, OS.objc_getProtocol ("NSTextInput")) != 0; + if (!textInput) { + // Not a text field, so send a key event here. + NSEvent nsEvent = new NSEvent (theEvent); + boolean [] consume = new boolean [1]; + if (translateTraversal (nsEvent.keyCode (), nsEvent, consume)) return; + if (isDisposed ()) return; + if (!sendKeyEvent (nsEvent, SWT.KeyDown)) return; + if (consume [0]) return; + } else { + // Control is some kind of text field, so the key event will be sent from insertText: or doCommandBySelector: + super.keyDown (id, sel, theEvent); + + if (imeInComposition ()) return; + // If none of those methods triggered a key event send one now. + if (!s.keyInputHappened) { + NSEvent nsEvent = new NSEvent (theEvent); + boolean [] consume = new boolean [1]; + if (translateTraversal (nsEvent.keyCode (), nsEvent, consume)) return; + if (isDisposed ()) return; + if (!sendKeyEvent (nsEvent, SWT.KeyDown)) return; + if (consume [0]) return; + } + + return; + } + } + super.keyDown (id, sel, theEvent); +} + +void keyUp (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (view.window ().firstResponder ().id == id) { + NSEvent nsEvent = new NSEvent (theEvent); + if (!sendKeyEvent (nsEvent, SWT.KeyUp)) return; + } + super.keyUp (id, sel, theEvent); +} + +void markLayout (boolean changed, boolean all) { + /* Do nothing */ +} + +int /*long*/ menuForEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!isEnabled ()) return 0; + + NSPoint pt = NSEvent.mouseLocation(); + pt.y = (int) (display.getPrimaryFrame().height - pt.y); + int x = (int) pt.x; + int y = (int) pt.y; + Event event = new Event (); + event.x = x; + event.y = y; + sendEvent (SWT.MenuDetect, event); + //widget could be disposed at this point + if (isDisposed ()) return 0; + if (!event.doit) return 0; + Menu menu = getMenu (); + if (menu != null && !menu.isDisposed ()) { + if (x != event.x || y != event.y) { + menu.setLocation (event.x, event.y); + } + menu.setVisible(true); + return 0; + } + return super.menuForEvent (id, sel, theEvent); +} + +Decorations menuShell () { + return parent.menuShell (); +} + +void scrollWheel (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (id == view.id) { + if (hooks (SWT.MouseWheel) || filters (SWT.MouseWheel)) { + NSEvent nsEvent = new NSEvent(theEvent); + if (nsEvent.deltaY() != 0) { + if (!sendMouseEvent(nsEvent, SWT.MouseWheel, true)) { + return; + } + } + } + } + super.scrollWheel(id, sel, theEvent); +} + +boolean isEventView (int /*long*/ id) { + return true; +} + +boolean mouseEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent, int type) { + if (!display.sendEvent) return true; + display.sendEvent = false; + if (!isEventView (id)) return true; + boolean dragging = false; + boolean[] consume = null; + NSEvent nsEvent = new NSEvent(theEvent); + int nsType = (int)/*64*/nsEvent.type(); + NSInputManager manager = NSInputManager.currentInputManager (); + if (manager != null && manager.wantsToHandleMouseEvents ()) { + if (manager.handleMouseEvent (nsEvent)) { + return true; + } + } + switch (nsType) { + case OS.NSLeftMouseDown: + if (nsEvent.clickCount() == 1 && (state & DRAG_DETECT) != 0 && hooks (SWT.DragDetect)) { + consume = new boolean[1]; + NSPoint location = view.convertPoint_fromView_(nsEvent.locationInWindow(), null); + if (!view.isFlipped ()) { + location.y = view.bounds().height - location.y; + } + dragging = dragDetect((int)location.x, (int)location.y, false, consume); + } + break; + case OS.NSLeftMouseDragged: + case OS.NSRightMouseDragged: + case OS.NSOtherMouseDragged: + display.checkEnterExit (this, nsEvent, false); + break; + case OS.NSLeftMouseUp: + case OS.NSRightMouseUp: + case OS.NSOtherMouseUp: + display.checkEnterExit (display.findControl(true), nsEvent, false); + break; + } + sendMouseEvent (nsEvent, type, false); + if (type == SWT.MouseDown && nsEvent.clickCount() == 2) { + sendMouseEvent (nsEvent, SWT.MouseDoubleClick, false); + } + if (dragging) sendMouseEvent(nsEvent, SWT.DragDetect, false); + if (consume != null && consume[0]) return false; + return true; +} + +void mouseDown(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!mouseEvent(id, sel, theEvent, SWT.MouseDown)) return; + boolean tracking = isEventView (id); + Display display = this.display; + if (tracking) display.trackingControl = this; + super.mouseDown(id, sel, theEvent); + if (tracking) display.trackingControl = null; +} + +void mouseUp(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!mouseEvent(id, sel, theEvent, SWT.MouseUp)) return; + super.mouseUp(id, sel, theEvent); +} + +void mouseDragged(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!mouseEvent(id, sel, theEvent, SWT.MouseMove)) return; + super.mouseDragged(id, sel, theEvent); +} + +void rightMouseDown(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!mouseEvent(id, sel, theEvent, SWT.MouseDown)) return; + super.rightMouseDown(id, sel, theEvent); +} + +void rightMouseUp(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!mouseEvent(id, sel, theEvent, SWT.MouseUp)) return; + super.rightMouseUp(id, sel, theEvent); +} + +void rightMouseDragged(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!mouseEvent(id, sel, theEvent, SWT.MouseMove)) return; + super.rightMouseDragged(id, sel, theEvent); +} + +void otherMouseDown(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!mouseEvent(id, sel, theEvent, SWT.MouseDown)) return; + super.otherMouseDown(id, sel, theEvent); +} + +void otherMouseUp(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!mouseEvent(id, sel, theEvent, SWT.MouseUp)) return; + super.otherMouseUp(id, sel, theEvent); +} + +void otherMouseDragged(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!mouseEvent(id, sel, theEvent, SWT.MouseMove)) return; + super.otherMouseDragged(id, sel, theEvent); +} + +void moved () { + sendEvent (SWT.Move); +} + +/** + * Moves the receiver above the specified control in the + * drawing order. If the argument is null, then the receiver + * is moved to the top of the drawing order. The control at + * the top of the drawing order will not be covered by other + * controls even if they occupy intersecting areas. + * + * @param control the sibling control (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Control#moveBelow + * @see Composite#getChildren + */ +public void moveAbove (Control control) { + checkWidget(); + if (control != null) { + if (control.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + if (parent != control.parent) return; + } + setZOrder (control, true); +} + +/** + * Moves the receiver below the specified control in the + * drawing order. If the argument is null, then the receiver + * is moved to the bottom of the drawing order. The control at + * the bottom of the drawing order will be covered by all other + * controls which occupy intersecting areas. + * + * @param control the sibling control (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Control#moveAbove + * @see Composite#getChildren + */ +public void moveBelow (Control control) { + checkWidget(); + if (control != null) { + if (control.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + if (parent != control.parent) return; + } + setZOrder (control, false); +} + +Accessible new_Accessible (Control control) { + return Accessible.internal_new_Accessible (this); +} + +/** + * Causes the receiver to be resized to its preferred size. + * For a composite, this involves computing the preferred size + * from its layout, if there is one. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #computeSize(int, int, boolean) + */ +public void pack () { + checkWidget(); + pack (true); +} + +/** + * Causes the receiver to be resized to its preferred size. + * For a composite, this involves computing the preferred size + * from its layout, if there is one. + * <p> + * If the changed flag is <code>true</code>, it indicates that the receiver's + * <em>contents</em> have changed, therefore any caches that a layout manager + * containing the control may have been keeping need to be flushed. When the + * control is resized, the changed flag will be <code>false</code>, so layout + * manager caches can be retained. + * </p> + * + * @param changed whether or not the receiver's contents have changed + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #computeSize(int, int, boolean) + */ +public void pack (boolean changed) { + checkWidget(); + setSize (computeSize (SWT.DEFAULT, SWT.DEFAULT, changed)); +} + +NSView paintView () { + return eventView (); +} + +/** + * Prints the receiver and all children. + * + * @param gc the gc where the drawing occurs + * @return <code>true</code> if the operation was successful and <code>false</code> otherwise + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the gc is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the gc has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public boolean print (GC gc) { + checkWidget (); + if (gc == null) error (SWT.ERROR_NULL_ARGUMENT); + if (gc.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + + gc.handle.saveGraphicsState(); + NSGraphicsContext.setCurrentContext(gc.handle); + NSAffineTransform transform = NSAffineTransform.transform (); + transform.translateXBy (0, view.bounds().height); + transform.scaleXBy (1, -1); + transform.concat (); + view.displayRectIgnoringOpacity(view.bounds(), gc.handle); + gc.handle.restoreGraphicsState(); + return true; +} + +/** + * Causes the entire bounds of the receiver to be marked + * as needing to be redrawn. The next time a paint request + * is processed, the control will be completely painted, + * including the background. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #update() + * @see PaintListener + * @see SWT#Paint + * @see SWT#NO_BACKGROUND + * @see SWT#NO_REDRAW_RESIZE + * @see SWT#NO_MERGE_PAINTS + * @see SWT#DOUBLE_BUFFERED + */ +public void redraw () { + checkWidget(); + view.setNeedsDisplay(true); +} + +void redraw (boolean children) { +// checkWidget(); + view.setNeedsDisplay(true); +} + +/** + * Causes the rectangular area of the receiver specified by + * the arguments to be marked as needing to be redrawn. + * The next time a paint request is processed, that area of + * the receiver will be painted, including the background. + * If the <code>all</code> flag is <code>true</code>, any + * children of the receiver which intersect with the specified + * area will also paint their intersecting areas. If the + * <code>all</code> flag is <code>false</code>, the children + * will not be painted. + * + * @param x the x coordinate of the area to draw + * @param y the y coordinate of the area to draw + * @param width the width of the area to draw + * @param height the height of the area to draw + * @param all <code>true</code> if children should redraw, and <code>false</code> otherwise + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #update() + * @see PaintListener + * @see SWT#Paint + * @see SWT#NO_BACKGROUND + * @see SWT#NO_REDRAW_RESIZE + * @see SWT#NO_MERGE_PAINTS + * @see SWT#DOUBLE_BUFFERED + */ +public void redraw (int x, int y, int width, int height, boolean all) { + checkWidget (); + NSRect rect = new NSRect(); + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + view.setNeedsDisplayInRect(rect); +} + +int /*long*/ regionToRects(int /*long*/ message, int /*long*/ rgn, int /*long*/ r, int /*long*/ path) { + NSPoint pt = new NSPoint(); + short[] rect = new short[4]; + if (message == OS.kQDRegionToRectsMsgParse) { + OS.memmove(rect, r, rect.length * 2); + pt.x = rect[1]; + pt.y = rect[0]; + OS.objc_msgSend(path, OS.sel_moveToPoint_, pt); + pt.x = rect[3]; + OS.objc_msgSend(path, OS.sel_lineToPoint_, pt); + pt.x = rect[3]; + pt.y = rect[2]; + OS.objc_msgSend(path, OS.sel_lineToPoint_, pt); + pt.x = rect[1]; + OS.objc_msgSend(path, OS.sel_lineToPoint_, pt); + OS.objc_msgSend(path, OS.sel_closePath); + } + return 0; +} + +void register () { + super.register (); + display.addWidget (view, this); +} + +void release (boolean destroy) { + Control next = null, previous = null; + if (destroy && parent != null) { + Control[] children = parent._getChildren (); + int index = 0; + while (index < children.length) { + if (children [index] == this) break; + index++; + } + if (0 < index && (index + 1) < children.length) { + next = children [index + 1]; + previous = children [index - 1]; + } + } + super.release (destroy); + if (destroy) { + if (previous != null) previous.addRelation (next); + } +} + +void releaseHandle () { + super.releaseHandle (); + if (view != null) view.release(); + view = null; + parent = null; +} + +void releaseParent () { + invalidateVisibleRegion (); + parent.removeControl (this); +} + +void releaseWidget () { + super.releaseWidget (); + if (display.currentControl == this) { + display.currentControl = null; + display.timerExec(-1, display.hoverTimer); + } + if (display.trackingControl == this) display.trackingControl = null; + if (display.tooltipControl == this) display.tooltipControl = null; + if (menu != null && !menu.isDisposed ()) { + menu.dispose (); + } + menu = null; + if (visibleRgn != 0) OS.DisposeRgn (visibleRgn); + visibleRgn = 0; + layoutData = null; + if (accessible != null) { + accessible.internal_dispose_Accessible (); + } + accessible = null; + region = null; + if (regionPath != null) regionPath.release(); + regionPath = null; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is moved or resized. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ControlListener + * @see #addControlListener + */ +public void removeControlListener (ControlListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Move, listener); + eventTable.unhook (SWT.Resize, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when a drag gesture occurs. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see DragDetectListener + * @see #addDragDetectListener + * + * @since 3.3 + */ +public void removeDragDetectListener(DragDetectListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.DragDetect, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control gains or loses focus. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see FocusListener + * @see #addFocusListener + */ +public void removeFocusListener(FocusListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook(SWT.FocusIn, listener); + eventTable.unhook(SWT.FocusOut, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the help events are generated for the control. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see HelpListener + * @see #addHelpListener + */ +public void removeHelpListener (HelpListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Help, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when keys are pressed and released on the system keyboard. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see KeyListener + * @see #addKeyListener + */ +public void removeKeyListener(KeyListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook(SWT.KeyUp, listener); + eventTable.unhook(SWT.KeyDown, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the platform-specific context menu trigger has + * occurred. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see MenuDetectListener + * @see #addMenuDetectListener + * + * @since 3.3 + */ +public void removeMenuDetectListener (MenuDetectListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.MenuDetect, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when mouse buttons are pressed and released. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see MouseListener + * @see #addMouseListener + */ +public void removeMouseListener(MouseListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook(SWT.MouseDown, listener); + eventTable.unhook(SWT.MouseUp, listener); + eventTable.unhook(SWT.MouseDoubleClick, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the mouse moves. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see MouseMoveListener + * @see #addMouseMoveListener + */ +public void removeMouseMoveListener(MouseMoveListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook(SWT.MouseMove, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the mouse passes or hovers over controls. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see MouseTrackListener + * @see #addMouseTrackListener + */ +public void removeMouseTrackListener(MouseTrackListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.MouseEnter, listener); + eventTable.unhook (SWT.MouseExit, listener); + eventTable.unhook (SWT.MouseHover, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the mouse wheel is scrolled. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see MouseWheelListener + * @see #addMouseWheelListener + * + * @since 3.3 + */ +public void removeMouseWheelListener (MouseWheelListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.MouseWheel, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the receiver needs to be painted. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see PaintListener + * @see #addPaintListener + */ +public void removePaintListener(PaintListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook(SWT.Paint, listener); +} + +/* + * Remove "Labeled by" relations from the receiver. + */ +void removeRelation () { + if (!isDescribedByLabel()) return; + NSObject accessibleElement = focusView(); + + if (accessibleElement instanceof NSControl) { + NSControl viewAsControl = (NSControl) accessibleElement; + if (viewAsControl.cell() != null) accessibleElement = viewAsControl.cell(); + } + + accessibleElement.accessibilitySetOverrideValue(accessibleElement, OS.NSAccessibilityTitleUIElementAttribute); +} + + +/** + * Removes the listener from the collection of listeners who will + * be notified when traversal events occur. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see TraverseListener + * @see #addTraverseListener + */ +public void removeTraverseListener(TraverseListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Traverse, listener); +} + +void resetVisibleRegion () { + if (visibleRgn != 0) { + OS.DisposeRgn (visibleRgn); + visibleRgn = 0; + } + GCData[] gcs = display.contexts; + if (gcs != null) { + int /*long*/ visibleRgn = 0; + for (int i=0; i<gcs.length; i++) { + GCData data = gcs [i]; + if (data != null) { + if (data.view == view) { + if (visibleRgn == 0) visibleRgn = getVisibleRegion (); + data.state &= ~VISIBLE_REGION; + OS.CopyRgn (visibleRgn, data.visibleRgn); + } + } + } + if (visibleRgn != 0) OS.DisposeRgn (visibleRgn); + } +} + +void resized () { + sendEvent (SWT.Resize); +} + +boolean sendDragEvent (int button, int stateMask, int x, int y) { + Event event = new Event (); + event.button = button; + event.x = x; + event.y = y; + event.stateMask = stateMask; + postEvent (SWT.DragDetect, event); + return event.doit; +} + +void sendFocusEvent (int type) { + Display display = this.display; + Shell shell = getShell (); + + display.focusEvent = type; + display.focusControl = this; + sendEvent (type); + // widget could be disposed at this point + display.focusEvent = SWT.None; + display.focusControl = null; + + /* + * It is possible that the shell may be + * disposed at this point. If this happens + * don't send the activate and deactivate + * events. + */ + if (!shell.isDisposed ()) { + switch (type) { + case SWT.FocusIn: + shell.setActiveControl (this); + break; + case SWT.FocusOut: + if (shell != display.getActiveShell ()) { + shell.setActiveControl (null); + } + break; + } + } +} + +boolean sendMouseEvent (NSEvent nsEvent, int type, boolean send) { + Shell shell = null; + Event event = new Event (); + switch (type) { + case SWT.MouseDown: + shell = getShell (); + //FALL THROUGH + case SWT.MouseUp: + case SWT.MouseDoubleClick: + case SWT.DragDetect: + int button = (int)/*64*/nsEvent.buttonNumber(); + switch (button) { + case 0: event.button = 1; break; + case 1: event.button = 3; break; + case 2: event.button = 2; break; + case 3: event.button = 4; break; + case 4: event.button = 5; break; + } + break; + case SWT.MouseWheel: + event.detail = SWT.SCROLL_LINE; + float /*double*/ delta = nsEvent.deltaY(); + event.count = delta > 0 ? Math.max (1, (int)delta) : Math.min (-1, (int)delta); + break; + } + if (event.button != 0) event.count = (int)/*64*/nsEvent.clickCount(); + NSPoint windowPoint; + NSView view = eventView (); + if (nsEvent == null || nsEvent.type() == OS.NSMouseMoved) { + NSWindow window = view.window(); + windowPoint = window.convertScreenToBase(NSEvent.mouseLocation()); + } else { + windowPoint = nsEvent.locationInWindow(); + } + NSPoint point = view.convertPoint_fromView_(windowPoint, null); + if (!view.isFlipped ()) { + point.y = view.bounds().height - point.y; + } + event.x = (int) point.x; + event.y = (int) point.y; + setInputState (event, nsEvent, type); + if (send) { + sendEvent (type, event); + if (isDisposed ()) return false; + } else { + postEvent (type, event); + } + if (shell != null) shell.setActiveControl(this); + return event.doit; +} + +void setBackground () { +// redrawWidget (handle, false); +} + +/** + * Sets the receiver's background color to the color specified + * by the argument, or to the default system color for the control + * if the argument is null. + * <p> + * Note: This operation is a hint and may be overridden by the platform. + * For example, on Windows the background of a Button cannot be changed. + * </p> + * @param color the new color (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setBackground (Color color) { + checkWidget(); + if (color != null) { + if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + float /*double*/ [] background = color != null ? color.handle : null; + if (equals (background, this.background)) return; + this.background = background; + updateBackground (); + redrawWidget(view, true); +} + +/** + * Sets the receiver's background image to the image specified + * by the argument, or to the default system color for the control + * if the argument is null. The background image is tiled to fill + * the available space. + * <p> + * Note: This operation is a hint and may be overridden by the platform. + * For example, on Windows the background of a Button cannot be changed. + * </p> + * @param image the new image (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * <li>ERROR_INVALID_ARGUMENT - if the argument is not a bitmap</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public void setBackgroundImage (Image image) { + checkWidget(); + if (image != null && image.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (image == backgroundImage) return; + backgroundImage = image; + updateBackground(); + redrawWidget(view, false); +} + +void updateBackground () { +} + +void setBackground (NSColor nsColor) { +} + +/** + * Sets the receiver's size and location to the rectangular + * area specified by the arguments. The <code>x</code> and + * <code>y</code> arguments are relative to the receiver's + * parent (or its display if its parent is null), unless + * the receiver is a shell. In this case, the <code>x</code> + * and <code>y</code> arguments are relative to the display. + * <p> + * Note: Attempting to set the width or height of the + * receiver to a negative number will cause that + * value to be set to zero instead. + * </p> + * + * @param x the new x coordinate for the receiver + * @param y the new y coordinate for the receiver + * @param width the new width for the receiver + * @param height the new height for the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setBounds (int x, int y, int width, int height) { + checkWidget(); + setBounds (x, y, Math.max (0, width), Math.max (0, height), true, true); +} + +void setBounds (int x, int y, int width, int height, boolean move, boolean resize) { + NSView topView = topView(); + if (move && resize) { + NSRect rect = new NSRect(); + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + topView.setFrame (rect); + } else if (move) { + NSPoint point = new NSPoint(); + point.x = x; + point.y = y; + topView.setFrameOrigin(point); + } else if (resize) { + NSSize size = new NSSize(); + size.width = width; + size.height = height; + topView.setFrameSize(size); + } +} + +/** + * Sets the receiver's size and location to the rectangular + * area specified by the argument. The <code>x</code> and + * <code>y</code> fields of the rectangle are relative to + * the receiver's parent (or its display if its parent is null). + * <p> + * Note: Attempting to set the width or height of the + * receiver to a negative number will cause that + * value to be set to zero instead. + * </p> + * + * @param rect the new bounds for the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setBounds (Rectangle rect) { + checkWidget (); + if (rect == null) error (SWT.ERROR_NULL_ARGUMENT); + setBounds (rect.x, rect.y, Math.max (0, rect.width), Math.max (0, rect.height), true, true); +} + +/** + * If the argument is <code>true</code>, causes the receiver to have + * all mouse events delivered to it until the method is called with + * <code>false</code> as the argument. Note that on some platforms, + * a mouse button must currently be down for capture to be assigned. + * + * @param capture <code>true</code> to capture the mouse, and <code>false</code> to release it + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setCapture (boolean capture) { + checkWidget(); +} + +void setClipRegion (float /*double*/ x, float /*double*/ y) { + if (regionPath != null) { + NSAffineTransform transform = NSAffineTransform.transform(); + transform.translateXBy(-x, -y); + regionPath.transformUsingAffineTransform(transform); + regionPath.addClip(); + transform.translateXBy(2*x, 2*y); + regionPath.transformUsingAffineTransform(transform); + } + NSRect frame = topView().frame(); + parent.setClipRegion(frame.x + x, frame.y + y); +} + +/** + * Sets the receiver's cursor to the cursor specified by the + * argument, or to the default cursor for that kind of control + * if the argument is null. + * <p> + * When the mouse pointer passes over a control its appearance + * is changed to match the control's cursor. + * </p> + * + * @param cursor the new cursor (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setCursor (Cursor cursor) { + checkWidget(); + if (cursor != null && cursor.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + this.cursor = cursor; + if (!isEnabled()) return; + if (!view.window().areCursorRectsEnabled()) return; + display.setCursor (display.currentControl); +} + +void setDefaultFont () { + if (display.smallFonts) { + setFont (defaultFont ().handle); + setSmallSize (); + } +} + +/** + * Sets the receiver's drag detect state. If the argument is + * <code>true</code>, the receiver will detect drag gestures, + * otherwise these gestures will be ignored. + * + * @param dragDetect the new drag detect state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.3 + */ +public void setDragDetect (boolean dragDetect) { + checkWidget (); + if (dragDetect) { + state |= DRAG_DETECT; + } else { + state &= ~DRAG_DETECT; + } +} + +/** + * Enables the receiver if the argument is <code>true</code>, + * and disables it otherwise. A disabled control is typically + * not selectable from the user interface and draws with an + * inactive or "grayed" look. + * + * @param enabled the new enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setEnabled (boolean enabled) { + checkWidget(); + if (((state & DISABLED) == 0) == enabled) return; + Control control = null; + boolean fixFocus = false; + if (!enabled) { + if (display.focusEvent != SWT.FocusOut) { + control = display.getFocusControl (); + fixFocus = isFocusAncestor (control); + } + } + if (enabled) { + state &= ~DISABLED; + } else { + state |= DISABLED; + } + enableWidget (enabled); + if (fixFocus) fixFocus (control); +} + +/** + * Causes the receiver to have the <em>keyboard focus</em>, + * such that all keyboard events will be delivered to it. Focus + * reassignment will respect applicable platform constraints. + * + * @return <code>true</code> if the control got focus, and <code>false</code> if it was unable to. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #forceFocus + */ +public boolean setFocus () { + checkWidget(); + if ((style & SWT.NO_FOCUS) != 0) return false; + return forceFocus (); +} + +/** + * Sets the font that the receiver will use to paint textual information + * to the font specified by the argument, or to the default font for that + * kind of control if the argument is null. + * + * @param font the new font (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setFont (Font font) { + checkWidget(); + if (font != null) { + if (font.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + this.font = font; + setFont (font != null ? font.handle : defaultFont().handle); +} + +void setFont (NSFont font) { + if (view instanceof NSControl) { + ((NSControl)view).setFont(font); + } +} + +/** + * Sets the receiver's foreground color to the color specified + * by the argument, or to the default system color for the control + * if the argument is null. + * <p> + * Note: This operation is a hint and may be overridden by the platform. + * </p> + * @param color the new color (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setForeground (Color color) { + checkWidget(); + if (color != null) { + if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + float /*double*/ [] foreground = color != null ? color.handle : null; + if (equals (foreground, this.foreground)) return; + this.foreground = foreground; + setForeground (foreground); + redrawWidget (view, false); +} + +void setForeground (float /*double*/ [] color) { +} + +void setFrameOrigin (int /*long*/ id, int /*long*/ sel, NSPoint point) { + NSView topView = topView (); + if (topView.id != id) { + super.setFrameOrigin(id, sel, point); + return; + } + NSRect frame = topView.frame(); + super.setFrameOrigin(id, sel, point); + if (frame.x != point.x || frame.y != point.y) { + invalidateVisibleRegion(); + moved (); + } +} + +void setFrameSize (int /*long*/ id, int /*long*/ sel, NSSize size) { + NSView topView = topView (); + if (topView.id != id) { + super.setFrameSize(id, sel, size); + return; + } + NSRect frame = topView.frame(); + super.setFrameSize(id, sel, size); + if (frame.width != size.width || frame.height != size.height) { + invalidateVisibleRegion(); + resized (); + } +} + +/** + * Sets the layout data associated with the receiver to the argument. + * + * @param layoutData the new layout data for the receiver. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setLayoutData (Object layoutData) { + checkWidget(); + this.layoutData = layoutData; +} + +/** + * Sets the receiver's location to the point specified by + * the arguments which are relative to the receiver's + * parent (or its display if its parent is null), unless + * the receiver is a shell. In this case, the point is + * relative to the display. + * + * @param x the new x coordinate for the receiver + * @param y the new y coordinate for the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setLocation (int x, int y) { + checkWidget(); + setBounds (x, y, 0, 0, true, false); +} + +/** + * Sets the receiver's location to the point specified by + * the arguments which are relative to the receiver's + * parent (or its display if its parent is null), unless + * the receiver is a shell. In this case, the point is + * relative to the display. + * + * @param location the new location for the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setLocation (Point location) { + checkWidget(); + if (location == null) error (SWT.ERROR_NULL_ARGUMENT); + setBounds (location.x, location.y, 0, 0, true, false); +} + +/** + * Sets the receiver's pop up menu to the argument. + * All controls may optionally have a pop up + * menu that is displayed when the user requests one for + * the control. The sequence of key strokes, button presses + * and/or button releases that are used to request a pop up + * menu is platform specific. + * <p> + * Note: Disposing of a control that has a pop up menu will + * dispose of the menu. To avoid this behavior, set the + * menu to null before the control is disposed. + * </p> + * + * @param menu the new pop up menu + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_MENU_NOT_POP_UP - the menu is not a pop up menu</li> + * <li>ERROR_INVALID_PARENT - if the menu is not in the same widget tree</li> + * <li>ERROR_INVALID_ARGUMENT - if the menu has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setMenu (Menu menu) { + checkWidget(); + if (menu != null) { + if (menu.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if ((menu.style & SWT.POP_UP) == 0) { + error (SWT.ERROR_MENU_NOT_POP_UP); + } + if (menu.parent != menuShell ()) { + error (SWT.ERROR_INVALID_PARENT); + } + } + this.menu = menu; +} + +/** + * Changes the parent of the widget to be the one provided if + * the underlying operating system supports this feature. + * Returns <code>true</code> if the parent is successfully changed. + * + * @param parent the new parent for the control. + * @return <code>true</code> if the parent is changed and <code>false</code> otherwise. + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * <li>ERROR_NULL_ARGUMENT - if the parent is <code>null</code></li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean setParent (Composite parent) { + checkWidget(); + if (parent == null) error (SWT.ERROR_NULL_ARGUMENT); + if (parent.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (this.parent == parent) return true; + if (!isReparentable ()) return false; + releaseParent (); + Shell newShell = parent.getShell (), oldShell = getShell (); + Decorations newDecorations = parent.menuShell (), oldDecorations = menuShell (); + if (oldShell != newShell || oldDecorations != newDecorations) { + Menu [] menus = oldShell.findMenus (this); + fixChildren (newShell, oldShell, newDecorations, oldDecorations, menus); + } + NSView topView = topView (); + topView.retain(); + topView.removeFromSuperview(); + parent.contentView().addSubview(topView, OS.NSWindowBelow, null); + topView.release(); + this.parent = parent; + return true; +} + +/** + * If the argument is <code>false</code>, causes subsequent drawing + * operations in the receiver to be ignored. No drawing of any kind + * can occur in the receiver until the flag is set to true. + * Graphics operations that occurred while the flag was + * <code>false</code> are lost. When the flag is set to <code>true</code>, + * the entire widget is marked as needing to be redrawn. Nested calls + * to this method are stacked. + * <p> + * Note: This operation is a hint and may not be supported on some + * platforms or for some widgets. + * </p> + * + * @param redraw the new redraw state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #redraw(int, int, int, int, boolean) + * @see #update() + */ +public void setRedraw (boolean redraw) { + checkWidget(); + if (redraw) { + if (--drawCount == 0) { + invalidateVisibleRegion (); + redrawWidget(topView (), true); + } + } else { + if (drawCount == 0) { + invalidateVisibleRegion (); + } + drawCount++; + } +} + +/** + * Sets the shape of the control to the region specified + * by the argument. When the argument is null, the + * default shape of the control is restored. + * + * @param region the region that defines the shape of the control (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the region has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public void setRegion (Region region) { + checkWidget (); + if (region != null && region.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT); + this.region = region; + if (regionPath != null) regionPath.release(); + regionPath = getPath(region); + redrawWidget(view, true); +} + +void setRelations () { + if (parent == null) return; + Control [] children = parent._getChildren (); + int count = children.length; + if (count > 1) { + /* + * the receiver is the last item in the list, so its predecessor will + * be the second-last item in the list + */ + Control child = children [count - 2]; + if (child != this) { + child.addRelation (this); + } + } +} + +boolean setRadioSelection (boolean value){ + return false; +} + +/** + * Sets the receiver's size to the point specified by the arguments. + * <p> + * Note: Attempting to set the width or height of the + * receiver to a negative number will cause that + * value to be set to zero instead. + * </p> + * + * @param width the new width for the receiver + * @param height the new height for the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSize (int width, int height) { + checkWidget(); + setBounds (0, 0, Math.max (0, width), Math.max (0, height), false, true); +} + +/** + * Sets the receiver's size to the point specified by the argument. + * <p> + * Note: Attempting to set the width or height of the + * receiver to a negative number will cause them to be + * set to zero instead. + * </p> + * + * @param size the new size for the receiver + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the point is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSize (Point size) { + checkWidget (); + if (size == null) error (SWT.ERROR_NULL_ARGUMENT); + setBounds (0, 0, Math.max (0, size.x), Math.max (0, size.y), false, true); +} + +void setSmallSize () { + if (view instanceof NSControl) { + NSCell cell = ((NSControl)view).cell(); + if (cell != null) cell.setControlSize (OS.NSSmallControlSize); + } +} + +boolean setTabItemFocus () { + if (!isShowing ()) return false; + return forceFocus (); +} + +/** + * Sets the receiver's tool tip text to the argument, which + * may be null indicating that the default tool tip for the + * control will be shown. For a control that has a default + * tool tip, such as the Tree control on Windows, setting + * the tool tip text to an empty string replaces the default, + * causing no tool tip text to be shown. + * <p> + * The mnemonic indicator (character '&') is not displayed in a tool tip. + * To display a single '&' in the tool tip, the character '&' can be + * escaped by doubling it in the string. + * </p> + * + * @param string the new tool tip text (or null) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setToolTipText (String string) { + checkWidget(); + toolTipText = string; + checkToolTip (null); +} + +/** + * Marks the receiver as visible if the argument is <code>true</code>, + * and marks it invisible otherwise. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, marking + * it visible may not actually cause it to be displayed. + * </p> + * + * @param visible the new visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setVisible (boolean visible) { + checkWidget(); + if (visible) { + if ((state & HIDDEN) == 0) return; + state &= ~HIDDEN; + } else { + if ((state & HIDDEN) != 0) return; + state |= HIDDEN; + } + if (visible) { + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the show + * event. If this happens, just return. + */ + sendEvent (SWT.Show); + if (isDisposed ()) return; + } + + /* + * Feature in the Macintosh. If the receiver has focus, hiding + * the receiver causes no control to have focus. Also, the focus + * needs to be cleared from any TXNObject so that it stops blinking + * the caret. The fix is to assign focus to the first ancestor + * control that takes focus. If no control will take focus, clear + * the focus control. + */ + Control control = null; + boolean fixFocus = false; + if (!visible) { + if (display.focusEvent != SWT.FocusOut) { + control = display.getFocusControl (); + fixFocus = isFocusAncestor (control); + } + } + topView().setHidden(!visible); + invalidateVisibleRegion(); + if (!visible) { + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the show + * event. If this happens, just return. + */ + sendEvent (SWT.Hide); + if (isDisposed ()) return; + } + if (fixFocus) fixFocus (control); +} + +void setZOrder () { + NSView topView = topView (); + parent.contentView().addSubview(topView, OS.NSWindowBelow, null); +} + +boolean shouldDelayWindowOrderingForEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + Shell shell = getShell (); + if ((shell.style & SWT.ON_TOP) != 0) return false; + return super.shouldDelayWindowOrderingForEvent (id, sel, theEvent); +} + +void setZOrder (Control sibling, boolean above) { + int index = 0, siblingIndex = 0, oldNextIndex = -1; + Control[] children = null; + /* determine the receiver's and sibling's indexes in the parent */ + children = parent._getChildren (); + while (index < children.length) { + if (children [index] == this) break; + index++; + } + if (sibling != null) { + while (siblingIndex < children.length) { + if (children [siblingIndex] == sibling) break; + siblingIndex++; + } + } + /* remove "Labeled by" relationships that will no longer be valid */ + removeRelation (); + if (index + 1 < children.length) { + oldNextIndex = index + 1; + children [oldNextIndex].removeRelation (); + } + if (sibling != null) { + if (above) { + sibling.removeRelation (); + } else { + if (siblingIndex + 1 < children.length) { + children [siblingIndex + 1].removeRelation (); + } + } + } + + NSView otherView = sibling == null ? null : sibling.topView (); + view.retain(); + view.removeFromSuperview(); + parent.contentView().addSubview(view, above ? OS.NSWindowAbove : OS.NSWindowBelow, otherView); + view.release(); + invalidateVisibleRegion(); + + /* determine the receiver's new index in the parent */ + if (sibling != null) { + if (above) { + index = siblingIndex - (index < siblingIndex ? 1 : 0); + } else { + index = siblingIndex + (siblingIndex < index ? 1 : 0); + } + } else { + if (above) { + index = 0; + } else { + index = children.length - 1; + } + } + + /* add new "Labeled by" relations as needed */ + children = parent._getChildren (); + if (0 < index) { + children [index - 1].addRelation (this); + } + if (index + 1 < children.length) { + addRelation (children [index + 1]); + } + if (oldNextIndex != -1) { + if (oldNextIndex <= index) oldNextIndex--; + /* the last two conditions below ensure that duplicate relations are not hooked */ + if (0 < oldNextIndex && oldNextIndex != index && oldNextIndex != index + 1) { + children [oldNextIndex - 1].addRelation (children [oldNextIndex]); + } + } +} + +void sort (int [] items) { + /* Shell Sort from K&R, pg 108 */ + int length = items.length; + for (int gap=length/2; gap>0; gap/=2) { + for (int i=gap; i<length; i++) { + for (int j=i-gap; j>=0; j-=gap) { + if (items [j] <= items [j + gap]) { + int swap = items [j]; + items [j] = items [j + gap]; + items [j + gap] = swap; + } + } + } + } +} + +NSSize textExtent (String string) { + NSAttributedString attribStr = createString(string, null, null, 0, true, false); + NSSize size = attribStr.size(); + attribStr.release(); + return size; +} + +String tooltipText () { + return toolTipText; +} + +/** + * Returns a point which is the result of converting the + * argument, which is specified in display relative coordinates, + * to coordinates relative to the receiver. + * <p> + * @param x the x coordinate to be translated + * @param y the y coordinate to be translated + * @return the translated coordinates + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.1 + */ +public Point toControl (int x, int y) { + checkWidget(); + return display.map (null, this, x, y); +} + +/** + * Returns a point which is the result of converting the + * argument, which is specified in display relative coordinates, + * to coordinates relative to the receiver. + * <p> + * @param point the point to be translated (must not be null) + * @return the translated coordinates + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the point is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Point toControl (Point point) { + checkWidget(); + if (point == null) error (SWT.ERROR_NULL_ARGUMENT); + return toControl (point.x, point.y); +} + +/** + * Returns a point which is the result of converting the + * argument, which is specified in coordinates relative to + * the receiver, to display relative coordinates. + * <p> + * @param x the x coordinate to be translated + * @param y the y coordinate to be translated + * @return the translated coordinates + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.1 + */ +public Point toDisplay (int x, int y) { + checkWidget(); + return display.map (this, null, x, y); +} + +/** + * Returns a point which is the result of converting the + * argument, which is specified in coordinates relative to + * the receiver, to display relative coordinates. + * <p> + * @param point the point to be translated (must not be null) + * @return the translated coordinates + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the point is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Point toDisplay (Point point) { + checkWidget(); + if (point == null) error (SWT.ERROR_NULL_ARGUMENT); + return toDisplay (point.x, point.y); +} + +NSView topView () { + return view; +} + +boolean translateTraversal (int key, NSEvent theEvent, boolean [] consume) { + int detail = SWT.TRAVERSE_NONE; + int code = traversalCode (key, theEvent); + boolean all = false; + switch (key) { + case 53: /* Esc */ { + all = true; + detail = SWT.TRAVERSE_ESCAPE; + break; + } + case 76: /* KP Enter */ + case 36: /* Return */ { + all = true; + detail = SWT.TRAVERSE_RETURN; + break; + } + case 48: /* Tab */ { + int /*long*/ modifiers = theEvent.modifierFlags (); + boolean next = (modifiers & OS.NSShiftKeyMask) == 0; + detail = next ? SWT.TRAVERSE_TAB_NEXT : SWT.TRAVERSE_TAB_PREVIOUS; + break; + } + case 126: /* Up arrow */ + case 123: /* Left arrow */ + case 125: /* Down arrow */ + case 124: /* Right arrow */ { + boolean next = key == 125 /* Down arrow */ || key == 124 /* Right arrow */; + detail = next ? SWT.TRAVERSE_ARROW_NEXT : SWT.TRAVERSE_ARROW_PREVIOUS; + break; + } + case 116: /* Page up */ + case 121: /* Page down */ { + all = true; + int /*long*/ modifiers = theEvent.modifierFlags (); + if ((modifiers & OS.NSControlKeyMask) == 0) return false; + detail = key == 121 /* Page down */ ? SWT.TRAVERSE_PAGE_NEXT : SWT.TRAVERSE_PAGE_PREVIOUS; + break; + } + default: + return false; + } + Event event = new Event (); + event.doit = consume [0] = (code & detail) != 0; + event.detail = detail; + if (!setKeyState (event, SWT.Traverse, theEvent)) return false; + Shell shell = getShell (); + Control control = this; + do { + if (control.traverse (event)) return true; + if (!event.doit && control.hooks (SWT.Traverse)) { + return false; + } + if (control == shell) return false; + control = control.parent; + } while (all && control != null); + return false; +} + +int traversalCode (int key, NSEvent theEvent) { + int code = SWT.TRAVERSE_RETURN | SWT.TRAVERSE_TAB_NEXT | SWT.TRAVERSE_TAB_PREVIOUS | SWT.TRAVERSE_PAGE_NEXT | SWT.TRAVERSE_PAGE_PREVIOUS; + Shell shell = getShell (); + if (shell.parent != null) code |= SWT.TRAVERSE_ESCAPE; + return code; +} + +boolean traverseMnemonic (char key) { + return false; +} + +/** + * Based on the argument, perform one of the expected platform + * traversal action. The argument should be one of the constants: + * <code>SWT.TRAVERSE_ESCAPE</code>, <code>SWT.TRAVERSE_RETURN</code>, + * <code>SWT.TRAVERSE_TAB_NEXT</code>, <code>SWT.TRAVERSE_TAB_PREVIOUS</code>, + * <code>SWT.TRAVERSE_ARROW_NEXT</code> and <code>SWT.TRAVERSE_ARROW_PREVIOUS</code>. + * + * @param traversal the type of traversal + * @return true if the traversal succeeded + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean traverse (int traversal) { + checkWidget(); + Event event = new Event (); + event.doit = true; + event.detail = traversal; + return traverse (event); +} + +boolean traverse (Event event) { + sendEvent (SWT.Traverse, event); + if (isDisposed ()) return true; + if (!event.doit) return false; + switch (event.detail) { + case SWT.TRAVERSE_NONE: return true; + case SWT.TRAVERSE_ESCAPE: return traverseEscape (); + case SWT.TRAVERSE_RETURN: return traverseReturn (); + case SWT.TRAVERSE_TAB_NEXT: return traverseGroup (true); + case SWT.TRAVERSE_TAB_PREVIOUS: return traverseGroup (false); + case SWT.TRAVERSE_ARROW_NEXT: return traverseItem (true); + case SWT.TRAVERSE_ARROW_PREVIOUS: return traverseItem (false); + case SWT.TRAVERSE_MNEMONIC: return traverseMnemonic (event); + case SWT.TRAVERSE_PAGE_NEXT: return traversePage (true); + case SWT.TRAVERSE_PAGE_PREVIOUS: return traversePage (false); + } + return false; +} + +boolean traverseEscape () { + return false; +} + +boolean traverseGroup (boolean next) { + Control root = computeTabRoot (); + Widget group = computeTabGroup (); + Widget [] list = root.computeTabList (); + int length = list.length; + int index = 0; + while (index < length) { + if (list [index] == group) break; + index++; + } + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in focus in + * or out events. Ensure that a disposed widget is + * not accessed. + */ + if (index == length) return false; + int start = index, offset = (next) ? 1 : -1; + while ((index = ((index + offset + length) % length)) != start) { + Widget widget = list [index]; + if (!widget.isDisposed () && widget.setTabGroupFocus ()) { + return true; + } + } + if (group.isDisposed ()) return false; + return group.setTabGroupFocus (); +} + +boolean traverseItem (boolean next) { + Control [] children = parent._getChildren (); + int length = children.length; + int index = 0; + while (index < length) { + if (children [index] == this) break; + index++; + } + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in focus in + * or out events. Ensure that a disposed widget is + * not accessed. + */ + if (index == length) return false; + int start = index, offset = (next) ? 1 : -1; + while ((index = (index + offset + length) % length) != start) { + Control child = children [index]; + if (!child.isDisposed () && child.isTabItem ()) { + if (child.setTabItemFocus ()) return true; + } + } + return false; +} + +boolean traverseReturn () { + return false; +} + +boolean traversePage (boolean next) { + return false; +} + +boolean traverseMnemonic (Event event) { + return false; +} + +/** + * Forces all outstanding paint requests for the widget + * to be processed before this method returns. If there + * are no outstanding paint request, this method does + * nothing. + * <p> + * Note: This method does not cause a redraw. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #redraw() + * @see #redraw(int, int, int, int, boolean) + * @see PaintListener + * @see SWT#Paint + */ +public void update () { + checkWidget(); + update (false); +} + +void update (boolean all) { +// checkWidget(); + if (display.isPainting.containsObject(view)) return; + //TODO - not all + view.displayIfNeeded (); +} + +void updateBackgroundMode () { + int oldState = state & PARENT_BACKGROUND; + checkBackground (); + if (oldState != (state & PARENT_BACKGROUND)) { + setBackground (); + } +} + +void resetCursorRects (int /*long*/ id, int /*long*/ sel) { + if (isEnabled ()) callSuper (id, sel); +} + +void updateTrackingAreas (int /*long*/ id, int /*long*/ sel) { + if (isEnabled ()) callSuper (id, sel); +} + +void updateCursorRects (boolean enabled) { + updateCursorRects (enabled, view); +} + +void updateCursorRects (boolean enabled, NSView widget) { + if (enabled) { + widget.resetCursorRects (); + widget.updateTrackingAreas (); + } else { + widget.discardCursorRects (); + NSArray areas = widget.trackingAreas (); + for (int i = 0; i < areas.count(); i++) { + widget.removeTrackingArea (new NSTrackingArea (areas.objectAtIndex (i))); + } + } +} + +void updateLayout (boolean all) { + /* Do nothing */ +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/DateTime.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/DateTime.java new file mode 100755 index 0000000000..5604b200af --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/DateTime.java @@ -0,0 +1,591 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class are selectable user interface + * objects that allow the user to enter and modify date + * or time values. + * <p> + * Note that although this class is a subclass of <code>Composite</code>, + * it does not make sense to add children to it, or set a layout on it. + * </p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>DATE, TIME, CALENDAR, SHORT, MEDIUM, LONG, DROP_DOWN</dd> + * <dt><b>Events:</b></dt> + * <dd>DefaultSelection, Selection</dd> + * </dl> + * <p> + * Note: Only one of the styles DATE, TIME, or CALENDAR may be specified, + * and only one of the styles SHORT, MEDIUM, or LONG may be specified. + * The DROP_DOWN style is a <em>HINT</em>, and it is only valid with the DATE style. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#datetime">DateTime snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.3 + * @noextend This class is not intended to be subclassed by clients. + */ +public class DateTime extends Composite { + static final int MIN_YEAR = 1752; // Gregorian switchover in North America: September 19, 1752 + static final int MAX_YEAR = 9999; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#DATE + * @see SWT#TIME + * @see SWT#CALENDAR + * @see SWT#SHORT + * @see SWT#MEDIUM + * @see SWT#LONG + * @see SWT#DROP_DOWN + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public DateTime (Composite parent, int style) { + super (parent, checkStyle (style)); +} + +static int checkStyle (int style) { + /* + * Even though it is legal to create this widget + * with scroll bars, they serve no useful purpose + * because they do not automatically scroll the + * widget's client area. The fix is to clear + * the SWT style. + */ + style &= ~(SWT.H_SCROLL | SWT.V_SCROLL); + style = checkBits (style, SWT.MEDIUM, SWT.SHORT, SWT.LONG, 0, 0, 0); + return checkBits (style, SWT.DATE, SWT.TIME, SWT.CALENDAR, 0, 0, 0); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the control is selected by the user, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * <code>widgetSelected</code> is called when the user changes the control's value. + * <code>widgetDefaultSelected</code> is typically called when ENTER is pressed. + * </p> + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener (SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Selection, typedListener); + addListener (SWT.DefaultSelection, typedListener); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + int width = 0, height = 0; + NSControl widget = (NSControl)view; + NSSize size = widget.cell ().cellSize (); + width = (int)Math.ceil (size.width); + height = (int)Math.ceil (size.height); + if (width == 0) width = DEFAULT_WIDTH; + if (height == 0) height = DEFAULT_HEIGHT; + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + int border = getBorderWidth (); + width += border * 2; height += border * 2; + return new Point (width, height); +} + +void createHandle () { + NSDatePicker widget = (NSDatePicker)new SWTDatePicker().alloc(); + widget.init(); + int pickerStyle = OS.NSTextFieldAndStepperDatePickerStyle; + int elementFlags = 0; + if ((style & SWT.CALENDAR) != 0) { + pickerStyle = OS.NSClockAndCalendarDatePickerStyle; + elementFlags = OS.NSYearMonthDayDatePickerElementFlag; + } else { + if ((style & SWT.TIME) != 0) { + elementFlags = (style & SWT.SHORT) != 0 ? OS.NSHourMinuteDatePickerElementFlag : OS.NSHourMinuteSecondDatePickerElementFlag; + } + if ((style & SWT.DATE) != 0) { + elementFlags = (style & SWT.SHORT) != 0 ? OS.NSYearMonthDatePickerElementFlag : OS.NSYearMonthDayDatePickerElementFlag; + } + } + widget.setDrawsBackground(true); + widget.setDatePickerStyle(pickerStyle); + widget.setDatePickerElements(elementFlags); + NSDate date = NSCalendarDate.calendarDate(); + widget.setDateValue(date); + widget.setTarget(widget); + widget.setAction(OS.sel_sendSelection); + view = widget; +} + +NSFont defaultNSFont() { + return display.datePickerFont; +} + +NSCalendarDate getCalendarDate () { + NSDate date = ((NSDatePicker)view).dateValue(); + return date.dateWithCalendarFormat(null, null); +} + +/** + * Returns the receiver's date, or day of the month. + * <p> + * The first day of the month is 1, and the last day depends on the month and year. + * </p> + * + * @return a positive integer beginning with 1 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getDay () { + checkWidget (); + return (int)/*64*/getCalendarDate().dayOfMonth(); +} + +/** + * Returns the receiver's hours. + * <p> + * Hours is an integer between 0 and 23. + * </p> + * + * @return an integer between 0 and 23 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getHours () { + checkWidget (); + return (int)/*64*/getCalendarDate().hourOfDay(); +} + +/** + * Returns the receiver's minutes. + * <p> + * Minutes is an integer between 0 and 59. + * </p> + * + * @return an integer between 0 and 59 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getMinutes () { + checkWidget (); + return (int)/*64*/getCalendarDate().minuteOfHour(); +} + +/** + * Returns the receiver's month. + * <p> + * The first month of the year is 0, and the last month is 11. + * </p> + * + * @return an integer between 0 and 11 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getMonth () { + checkWidget (); + return (int)/*64*/getCalendarDate().monthOfYear() - 1; +} + +String getNameText() { + return (style & SWT.TIME) != 0 ? getHours() + ":" + getMinutes() + ":" + getSeconds() + : (getMonth() + 1) + "/" + getDay() + "/" + getYear(); +} + +/** + * Returns the receiver's seconds. + * <p> + * Seconds is an integer between 0 and 59. + * </p> + * + * @return an integer between 0 and 59 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getSeconds () { + checkWidget (); + return (int)/*64*/getCalendarDate().secondOfMinute(); +} + +/** + * Returns the receiver's year. + * <p> + * The first year is 1752 and the last year is 9999. + * </p> + * + * @return an integer between 1752 and 9999 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getYear () { + checkWidget (); + return (int)/*64*/getCalendarDate().yearOfCommonEra(); +} + +boolean isEventView (int /*long*/ id) { + return true; +} + +boolean isFlipped (int /*long*/ id, int /*long*/ sel) { + if ((style & SWT.CALENDAR) != 0) return super.isFlipped (id, sel); + return true; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is selected by the user. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener (SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Selection, listener); + eventTable.unhook (SWT.DefaultSelection, listener); +} + +boolean sendKeyEvent (NSEvent nsEvent, int type) { + boolean result = super.sendKeyEvent (nsEvent, type); + if (!result) return result; + if (type != SWT.KeyDown) return result; + if ((style & SWT.CALENDAR) == 0) { + short keyCode = nsEvent.keyCode (); + switch (keyCode) { + case 76: /* KP Enter */ + case 36: /* Return */ + postEvent (SWT.DefaultSelection); + } + } + return result; +} + +void sendSelection () { + NSEvent event = NSApplication.sharedApplication().currentEvent(); + if (event != null && (style & SWT.CALENDAR) != 0) { + if (event.clickCount() == 2) { + postEvent (SWT.DefaultSelection); + } else if (event.type() == OS.NSLeftMouseUp) { + postEvent (SWT.Selection); + } + } else { // SWT.DATE or SWT.TIME + postEvent (SWT.Selection); + } +} + +void updateBackground () { + NSColor nsColor = null; + if (backgroundImage != null) { + nsColor = NSColor.colorWithPatternImage(backgroundImage.handle); + } else if (background != null) { + nsColor = NSColor.colorWithDeviceRed(background[0], background[1], background[2], background[3]); + } else { + if ((style & SWT.CALENDAR) != 0) { + nsColor = NSColor.controlBackgroundColor (); + } else { + nsColor = NSColor.textBackgroundColor (); + } + + } + ((NSDatePicker)view).setBackgroundColor(nsColor); +} + +/** + * Sets the receiver's year, month, and day in a single operation. + * <p> + * This is the recommended way to set the date, because setting the year, + * month, and day separately may result in invalid intermediate dates. + * </p> + * + * @param year an integer between 1752 and 9999 + * @param month an integer between 0 and 11 + * @param day a positive integer beginning with 1 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public void setDate (int year, int month, int day) { + checkWidget (); + if (year < MIN_YEAR || year > MAX_YEAR) return; + NSCalendarDate date = getCalendarDate(); + NSCalendarDate newDate = NSCalendarDate.dateWithYear(year, month + 1, day, + date.hourOfDay(), date.minuteOfHour(), date.secondOfMinute(), date.timeZone()); + if (newDate.yearOfCommonEra() == year && newDate.monthOfYear() == month + 1 && newDate.dayOfMonth() == day) { + ((NSDatePicker)view).setDateValue(newDate); + } +} + +/** + * Sets the receiver's date, or day of the month, to the specified day. + * <p> + * The first day of the month is 1, and the last day depends on the month and year. + * If the specified day is not valid for the receiver's month and year, then it is ignored. + * </p> + * + * @param day a positive integer beginning with 1 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setDate + */ +public void setDay (int day) { + checkWidget (); + NSCalendarDate date = getCalendarDate(); + NSCalendarDate newDate = NSCalendarDate.dateWithYear(date.yearOfCommonEra(), date.monthOfYear(), day, + date.hourOfDay(), date.minuteOfHour(), date.secondOfMinute(), date.timeZone()); + if (newDate.yearOfCommonEra() == date.yearOfCommonEra() && newDate.monthOfYear() == date.monthOfYear() && newDate.dayOfMonth() == day) { + ((NSDatePicker)view).setDateValue(newDate); + } +} + +void setForeground (float /*double*/ [] color) { + NSColor nsColor; + if (color == null) { + if ((style & SWT.CALENDAR) != 0) { + nsColor = NSColor.controlTextColor (); + } else { + nsColor = NSColor.textColor (); + } + } else { + nsColor = NSColor.colorWithDeviceRed(color[0], color[1], color[2], 1); + } + ((NSDatePicker)view).setTextColor(nsColor); +} + +/** + * Sets the receiver's hours. + * <p> + * Hours is an integer between 0 and 23. + * </p> + * + * @param hours an integer between 0 and 23 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setHours (int hours) { + checkWidget (); + if (hours < 0 || hours > 23) return; + NSCalendarDate date = getCalendarDate(); + NSCalendarDate newDate = NSCalendarDate.dateWithYear(date.yearOfCommonEra(), date.monthOfYear(), date.dayOfMonth(), + hours, date.minuteOfHour(), date.secondOfMinute(), date.timeZone()); + ((NSDatePicker)view).setDateValue(newDate); +} + +/** + * Sets the receiver's minutes. + * <p> + * Minutes is an integer between 0 and 59. + * </p> + * + * @param minutes an integer between 0 and 59 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setMinutes (int minutes) { + checkWidget (); + if (minutes < 0 || minutes > 59) return; + NSCalendarDate date = getCalendarDate(); + NSCalendarDate newDate = NSCalendarDate.dateWithYear(date.yearOfCommonEra(), date.monthOfYear(), date.dayOfMonth(), + date.hourOfDay(), minutes, date.secondOfMinute(), date.timeZone()); + ((NSDatePicker)view).setDateValue(newDate); +} + +/** + * Sets the receiver's month. + * <p> + * The first month of the year is 0, and the last month is 11. + * If the specified month is not valid for the receiver's day and year, then it is ignored. + * </p> + * + * @param month an integer between 0 and 11 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setDate + */ +public void setMonth (int month) { + checkWidget (); + NSCalendarDate date = getCalendarDate(); + NSCalendarDate newDate = NSCalendarDate.dateWithYear(date.yearOfCommonEra(), month + 1, date.dayOfMonth(), + date.hourOfDay(), date.minuteOfHour(), date.secondOfMinute(), date.timeZone()); + if (newDate.yearOfCommonEra() == date.yearOfCommonEra() && newDate.monthOfYear() == month + 1 && newDate.dayOfMonth() == date.dayOfMonth()) { + ((NSDatePicker)view).setDateValue(newDate); + } +} + +/** + * Sets the receiver's seconds. + * <p> + * Seconds is an integer between 0 and 59. + * </p> + * + * @param seconds an integer between 0 and 59 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSeconds (int seconds) { + checkWidget (); + if (seconds < 0 || seconds > 59) return; + NSCalendarDate date = getCalendarDate(); + NSCalendarDate newDate = NSCalendarDate.dateWithYear(date.yearOfCommonEra(), date.monthOfYear(), date.dayOfMonth(), + date.hourOfDay(), date.minuteOfHour(), seconds, date.timeZone()); + ((NSDatePicker)view).setDateValue(newDate); +} + +/** + * Sets the receiver's hours, minutes, and seconds in a single operation. + * + * @param hours an integer between 0 and 23 + * @param minutes an integer between 0 and 59 + * @param seconds an integer between 0 and 59 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public void setTime (int hours, int minutes, int seconds) { + checkWidget (); + if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59 || seconds < 0 || seconds > 59) return; + NSCalendarDate date = getCalendarDate(); + NSCalendarDate newDate = NSCalendarDate.dateWithYear(date.yearOfCommonEra(), date.monthOfYear(), date.dayOfMonth(), + hours, minutes, seconds, date.timeZone()); + ((NSDatePicker)view).setDateValue(newDate); +} + +/** + * Sets the receiver's year. + * <p> + * The first year is 1752 and the last year is 9999. + * If the specified year is not valid for the receiver's day and month, then it is ignored. + * </p> + * + * @param year an integer between 1752 and 9999 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setDate + */ +public void setYear (int year) { + checkWidget (); + if (year < MIN_YEAR || year > MAX_YEAR) return; + NSCalendarDate date = getCalendarDate(); + NSCalendarDate newDate = NSCalendarDate.dateWithYear(year, date.monthOfYear(), date.dayOfMonth(), + date.hourOfDay(), date.minuteOfHour(), date.secondOfMinute(), date.timeZone()); + if (newDate.yearOfCommonEra() == year && newDate.monthOfYear() == date.monthOfYear() && newDate.dayOfMonth() == date.dayOfMonth()) { + ((NSDatePicker)view).setDateValue(newDate); + } +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Decorations.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Decorations.java new file mode 100755 index 0000000000..e0c6c74c29 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Decorations.java @@ -0,0 +1,682 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class provide the appearance and + * behavior of <code>Shells</code>, but are not top + * level shells or dialogs. Class <code>Shell</code> + * shares a significant amount of code with this class, + * and is a subclass. + * <p> + * IMPORTANT: This class was intended to be abstract and + * should <em>never</em> be referenced or instantiated. + * Instead, the class <code>Shell</code> should be used. + * </p> + * <p> + * Instances are always displayed in one of the maximized, + * minimized or normal states: + * <ul> + * <li> + * When an instance is marked as <em>maximized</em>, the + * window manager will typically resize it to fill the + * entire visible area of the display, and the instance + * is usually put in a state where it can not be resized + * (even if it has style <code>RESIZE</code>) until it is + * no longer maximized. + * </li><li> + * When an instance is in the <em>normal</em> state (neither + * maximized or minimized), its appearance is controlled by + * the style constants which were specified when it was created + * and the restrictions of the window manager (see below). + * </li><li> + * When an instance has been marked as <em>minimized</em>, + * its contents (client area) will usually not be visible, + * and depending on the window manager, it may be + * "iconified" (that is, replaced on the desktop by a small + * simplified representation of itself), relocated to a + * distinguished area of the screen, or hidden. Combinations + * of these changes are also possible. + * </li> + * </ul> + * </p> + * Note: The styles supported by this class must be treated + * as <em>HINT</em>s, since the window manager for the + * desktop on which the instance is visible has ultimate + * control over the appearance and behavior of decorations. + * For example, some window managers only support resizable + * windows and will always assume the RESIZE style, even if + * it is not set. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>BORDER, CLOSE, MIN, MAX, NO_TRIM, RESIZE, TITLE, ON_TOP, TOOL</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * Class <code>SWT</code> provides two "convenience constants" + * for the most commonly required style combinations: + * <dl> + * <dt><code>SHELL_TRIM</code></dt> + * <dd> + * the result of combining the constants which are required + * to produce a typical application top level shell: (that + * is, <code>CLOSE | TITLE | MIN | MAX | RESIZE</code>) + * </dd> + * <dt><code>DIALOG_TRIM</code></dt> + * <dd> + * the result of combining the constants which are required + * to produce a typical application dialog shell: (that + * is, <code>TITLE | CLOSE | BORDER</code>) + * </dd> + * </dl> + * <p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the SWT implementation. + * </p> + * + * @see #getMinimized + * @see #getMaximized + * @see Shell + * @see SWT + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Decorations extends Canvas { + Image image; + Image [] images = new Image [0]; + Menu menuBar; + String text = ""; + boolean minimized, maximized; + Control savedFocus; + Button defaultButton; + +Decorations () { + /* Do nothing */ +} + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#BORDER + * @see SWT#CLOSE + * @see SWT#MIN + * @see SWT#MAX + * @see SWT#RESIZE + * @see SWT#TITLE + * @see SWT#NO_TRIM + * @see SWT#SHELL_TRIM + * @see SWT#DIALOG_TRIM + * @see SWT#ON_TOP + * @see SWT#TOOL + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Decorations (Composite parent, int style) { + super (parent, checkStyle (style)); +} + +static int checkStyle (int style) { + if ((style & SWT.NO_TRIM) != 0) { + style &= ~(SWT.CLOSE | SWT.TITLE | SWT.MIN | SWT.MAX | SWT.RESIZE | SWT.BORDER); + } + if ((style & (SWT.MENU | SWT.MIN | SWT.MAX | SWT.CLOSE)) != 0) { + style |= SWT.TITLE; + } + return style; +} + +void bringToTop (boolean force) { + moveAbove (null); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +int compare (ImageData data1, ImageData data2) { + if (data1.width == data2.width && data1.height == data2.height) { + int transparent1 = data1.getTransparencyType (); + int transparent2 = data2.getTransparencyType (); + if (transparent1 == SWT.TRANSPARENCY_ALPHA) return -1; + if (transparent2 == SWT.TRANSPARENCY_ALPHA) return 1; + if (transparent1 == SWT.TRANSPARENCY_MASK) return -1; + if (transparent2 == SWT.TRANSPARENCY_MASK) return 1; + if (transparent1 == SWT.TRANSPARENCY_PIXEL) return -1; + if (transparent2 == SWT.TRANSPARENCY_PIXEL) return 1; + return 0; + } + return data1.width > data2.width || data1.height > data2.height ? -1 : 1; +} + +Widget computeTabGroup () { + return this; +} + +Control computeTabRoot () { + return this; +} + +void fixDecorations (Decorations newDecorations, Control control, Menu [] menus) { + if (this == newDecorations) return; + if (control == savedFocus) savedFocus = null; + if (control == defaultButton) defaultButton = null; + if (menus == null) return; + Menu menu = control.menu; + if (menu != null) { + int index = 0; + while (index < menus.length) { + if (menus [index] == menu) { + control.setMenu (null); + return; + } + index++; + } + menu.fixMenus (newDecorations); + } +} + +/** + * Returns the receiver's default button if one had + * previously been set, otherwise returns null. + * + * @return the default button or null + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setDefaultButton(Button) + */ +public Button getDefaultButton () { + checkWidget(); + return defaultButton; +} + +/** + * Returns the receiver's image if it had previously been + * set using <code>setImage()</code>. The image is typically + * displayed by the window manager when the instance is + * marked as iconified, and may also be displayed somewhere + * in the trim when the instance is in normal or maximized + * states. + * <p> + * Note: This method will return null if called before + * <code>setImage()</code> is called. It does not provide + * access to a window manager provided, "default" image + * even if one exists. + * </p> + * + * @return the image + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Image getImage () { + checkWidget(); + return image; +} + +/** + * Returns the receiver's images if they had previously been + * set using <code>setImages()</code>. Images are typically + * displayed by the window manager when the instance is + * marked as iconified, and may also be displayed somewhere + * in the trim when the instance is in normal or maximized + * states. Depending where the icon is displayed, the platform + * chooses the icon with the "best" attributes. It is expected + * that the array will contain the same icon rendered at different + * sizes, with different depth and transparency attributes. + * + * <p> + * Note: This method will return an empty array if called before + * <code>setImages()</code> is called. It does not provide + * access to a window manager provided, "default" image + * even if one exists. + * </p> + * + * @return the images + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public Image [] getImages () { + checkWidget (); + if (images == null) return new Image [0]; + Image [] result = new Image [images.length]; + System.arraycopy (images, 0, result, 0, images.length); + return result; +} + +/** + * Returns <code>true</code> if the receiver is currently + * maximized, and false otherwise. + * <p> + * + * @return the maximized state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setMaximized + */ +public boolean getMaximized () { + checkWidget(); + return maximized; +} + +/** + * Returns the receiver's menu bar if one had previously + * been set, otherwise returns null. + * + * @return the menu bar or null + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Menu getMenuBar () { + checkWidget(); + return menuBar; +} + +/** + * Returns <code>true</code> if the receiver is currently + * minimized, and false otherwise. + * <p> + * + * @return the minimized state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setMinimized + */ +public boolean getMinimized () { + checkWidget(); + return minimized; +} + +String getNameText () { + return getText (); +} + +/** + * Returns the receiver's text, which is the string that the + * window manager will typically display as the receiver's + * <em>title</em>. If the text has not previously been set, + * returns an empty string. + * + * @return the text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getText () { + checkWidget(); + return text; +} + +public boolean isReparentable () { + checkWidget(); + return false; +} + +boolean isTabGroup () { + return true; +} + +boolean isTabItem () { + return false; +} + +Decorations menuShell () { + return this; +} + +void releaseChildren (boolean destroy) { + if (menuBar != null) { + menuBar.dispose (); + menuBar = null; + } + Display display = this.display; + super.releaseChildren (destroy); + Menu [] menus = display.getMenus (this); + if (menus != null) { + for (int i=0; i<menus.length; i++) { + Menu menu = menus [i]; + if (menu != null && !menu.isDisposed ()) { + menu.dispose (); + } + } + menus = null; + } +} +void releaseWidget () { + super.releaseWidget (); + image = null; + images = null; + savedFocus = null; + defaultButton = null; +} + +boolean restoreFocus () { + if (savedFocus != null && savedFocus.isDisposed ()) savedFocus = null; + if (savedFocus == null) return false; + return savedFocus.forceFocus (); +} + +void saveFocus () { +// int window = OS.GetControlOwner (handle); +// Control control = display.getFocusControl (window, false); +// if (control != null && control != this && this == control.menuShell ()) { +// setSavedFocus (control); +// } +} + +/** + * If the argument is not null, sets the receiver's default + * button to the argument, and if the argument is null, sets + * the receiver's default button to the first button which + * was set as the receiver's default button (called the + * <em>saved default button</em>). If no default button had + * previously been set, or the saved default button was + * disposed, the receiver's default button will be set to + * null. + * <p> + * The default button is the button that is selected when + * the receiver is active and the user presses ENTER. + * </p> + * + * @param button the new default button + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the button has been disposed</li> + * <li>ERROR_INVALID_PARENT - if the control is not in the same widget tree</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setDefaultButton (Button button) { + checkWidget(); + if (button != null) { + if (button.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + if (button.menuShell () != this) error (SWT.ERROR_INVALID_PARENT); + if ((button.style & SWT.PUSH) == 0) return; + } + if (button == defaultButton) return; + defaultButton = button; + NSButtonCell cell = null; + if (defaultButton != null && (defaultButton.style & SWT.PUSH) != 0) { + cell = new NSButtonCell (((NSButton)defaultButton.view).cell ()); + } + view.window().setDefaultButtonCell (cell); + display.updateDefaultButton(); +} + +/** + * Sets the receiver's image to the argument, which may + * be null. The image is typically displayed by the window + * manager when the instance is marked as iconified, and + * may also be displayed somewhere in the trim when the + * instance is in normal or maximized states. + * + * @param image the new image (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setImage (Image image) { + checkWidget(); + if (image != null && image.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + this.image = image; + if (parent != null) return; + if (display.dockImage == null) { + display.application.setApplicationIconImage (image != null ? image.handle : null); + } +} + +/** + * Sets the receiver's images to the argument, which may + * be an empty array. Images are typically displayed by the + * window manager when the instance is marked as iconified, + * and may also be displayed somewhere in the trim when the + * instance is in normal or maximized states. Depending where + * the icon is displayed, the platform chooses the icon with + * the "best" attributes. It is expected that the array will + * contain the same icon rendered at different sizes, with + * different depth and transparency attributes. + * + * @param images the new image array + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the array of images is null</li> + * <li>ERROR_INVALID_ARGUMENT - if one of the images is null or has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public void setImages (Image [] images) { + checkWidget(); + if (images == null) error(SWT.ERROR_INVALID_ARGUMENT); + for (int i = 0; i < images.length; i++) { + if (images [i] == null || images [i].isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + } + this.images = images; + if (parent != null) return; + if (display.dockImage == null) { + if (images != null && images.length > 1) { + Image [] bestImages = new Image [images.length]; + System.arraycopy (images, 0, bestImages, 0, images.length); + sort (bestImages); + images = bestImages; + } + if (images != null && images.length > 0) { + display.application.setApplicationIconImage (images [0].handle); + } else { + display.application.setApplicationIconImage (null); + } + } +} + +/** + * Sets the maximized state of the receiver. + * If the argument is <code>true</code> causes the receiver + * to switch to the maximized state, and if the argument is + * <code>false</code> and the receiver was previously maximized, + * causes the receiver to switch back to either the minimized + * or normal states. + * <p> + * Note: The result of intermixing calls to <code>setMaximized(true)</code> + * and <code>setMinimized(true)</code> will vary by platform. Typically, + * the behavior will match the platform user's expectations, but not + * always. This should be avoided if possible. + * </p> + * + * @param maximized the new maximized state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setMinimized + */ +public void setMaximized (boolean maximized) { + checkWidget(); + this.maximized = maximized; +} + +/** + * Sets the receiver's menu bar to the argument, which + * may be null. + * + * @param menu the new menu bar + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the menu has been disposed</li> + * <li>ERROR_INVALID_PARENT - if the menu is not in the same widget tree</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setMenuBar (Menu menu) { + checkWidget(); + if (menuBar == menu) return; + if (menu != null) { + if (menu.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + if ((menu.style & SWT.BAR) == 0) error (SWT.ERROR_MENU_NOT_BAR); + if (menu.parent != this) error (SWT.ERROR_INVALID_PARENT); + } + menuBar = menu; +} + +/** + * Sets the minimized stated of the receiver. + * If the argument is <code>true</code> causes the receiver + * to switch to the minimized state, and if the argument is + * <code>false</code> and the receiver was previously minimized, + * causes the receiver to switch back to either the maximized + * or normal states. + * <p> + * Note: The result of intermixing calls to <code>setMaximized(true)</code> + * and <code>setMinimized(true)</code> will vary by platform. Typically, + * the behavior will match the platform user's expectations, but not + * always. This should be avoided if possible. + * </p> + * + * @param minimized the new maximized state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setMaximized + */ +public void setMinimized (boolean minimized) { + checkWidget(); + this.minimized = minimized; +} + +void setSavedFocus (Control control) { + savedFocus = control; +} + +/** + * Sets the receiver's text, which is the string that the + * window manager will typically display as the receiver's + * <em>title</em>, to the argument, which must not be null. + * + * @param string the new text + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the text is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setText (String string) { + checkWidget(); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + text = string; +} + +void sort (Image [] images) { + /* Shell Sort from K&R, pg 108 */ + int length = images.length; + if (length <= 1) return; + ImageData [] datas = new ImageData [length]; + for (int i = 0; i < length; i++) { + datas [i] = images [i].getImageData (); + } + for (int gap=length/2; gap>0; gap/=2) { + for (int i=gap; i<length; i++) { + for (int j=i-gap; j>=0; j-=gap) { + if (compare (datas [j], datas [j + gap]) >= 0) { + Image swap = images [j]; + images [j] = images [j + gap]; + images [j + gap] = swap; + ImageData swapData = datas [j]; + datas [j] = datas [j + gap]; + datas [j + gap] = swapData; + } + } + } + } +} + +boolean traverseItem (boolean next) { + return false; +} + +boolean traverseReturn () { + if (defaultButton == null || defaultButton.isDisposed ()) return false; + if (!defaultButton.isVisible () || !defaultButton.isEnabled ()) return false; + defaultButton.click (); + return true; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/DirectoryDialog.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/DirectoryDialog.java new file mode 100755 index 0000000000..e12f40c639 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/DirectoryDialog.java @@ -0,0 +1,182 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.internal.cocoa.*; +import org.eclipse.swt.*; + + +/** + * Instances of this class allow the user to navigate + * the file system and select a directory. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>(none)</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the SWT implementation. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#directorydialog">DirectoryDialog snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample, Dialog tab</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class DirectoryDialog extends Dialog { + String message = "", filterPath = ""; + +/** + * Constructs a new instance of this class given only its parent. + * + * @param parent a shell which will be the parent of the new instance + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + */ +public DirectoryDialog (Shell parent) { + this (parent, SWT.APPLICATION_MODAL); +} + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a shell which will be the parent of the new instance + * @param style the style of dialog to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + */ +public DirectoryDialog (Shell parent, int style) { + super (parent, checkStyle (parent, style)); + if (Display.getSheetEnabled ()) { + if (parent != null && (style & SWT.SHEET) != 0) this.style |= SWT.SHEET; + } + checkSubclass (); +} + +/** + * Returns the path which the dialog will use to filter + * the directories it shows. + * + * @return the filter path + * + * @see #setFilterPath + */ +public String getFilterPath () { + return filterPath; +} + +/** + * Returns the dialog's message, which is a description of + * the purpose for which it was opened. This message will be + * visible on the dialog while it is open. + * + * @return the message + */ +public String getMessage () { + return message; +} + +/** + * Makes the dialog visible and brings it to the front + * of the display. + * + * @return a string describing the absolute path of the selected directory, + * or null if the dialog was cancelled or an error occurred + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the dialog has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the dialog</li> + * </ul> + */ +public String open () { + String directoryPath = null; + NSOpenPanel panel = NSOpenPanel.openPanel(); + panel.setCanCreateDirectories(true); + panel.setAllowsMultipleSelection((style & SWT.MULTI) != 0); + panel.setTitle(NSString.stringWith(title != null ? title : "")); + panel.setMessage(NSString.stringWith(message != null ? message : "")); + panel.setCanChooseFiles(false); + panel.setCanChooseDirectories(true); + NSApplication application = NSApplication.sharedApplication(); + if (parent != null && (style & SWT.SHEET) != 0) { + application.beginSheet(panel, parent.window, null, 0, 0); + } + NSString dir = filterPath != null ? NSString.stringWith(filterPath) : null; + int /*long*/ response = panel.runModalForDirectory(dir, null); + if (parent != null && (style & SWT.SHEET) != 0) { + application.endSheet(panel, 0); + } + if (response == OS.NSFileHandlingPanelOKButton) { + NSString filename = panel.filename(); + directoryPath = filterPath = filename.getString(); + } +// options.optionFlags = OS.kNavSupportPackages | OS.kNavAllowOpenPackages | OS.kNavAllowInvisibleFiles; + return directoryPath; +} + +/** + * Sets the dialog's message, which is a description of + * the purpose for which it was opened. This message will be + * visible on the dialog while it is open. + * + * @param string the message + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + */ +public void setMessage (String string) { + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + message = string; +} + +/** + * Sets the path that the dialog will use to filter + * the directories it shows to the argument, which may + * be null. If the string is null, then the operating + * system's default filter path will be used. + * <p> + * Note that the path string is platform dependent. + * For convenience, either '/' or '\' can be used + * as a path separator. + * </p> + * + * @param string the filter path + */ +public void setFilterPath (String string) { + filterPath = string; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java new file mode 100755 index 0000000000..5c82734c1c --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java @@ -0,0 +1,4900 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class are responsible for managing the + * connection between SWT and the underlying operating + * system. Their most important function is to implement + * the SWT event loop in terms of the platform event model. + * They also provide various methods for accessing information + * about the operating system, and have overall control over + * the operating system resources which SWT allocates. + * <p> + * Applications which are built with SWT will <em>almost always</em> + * require only a single display. In particular, some platforms + * which SWT supports will not allow more than one <em>active</em> + * display. In other words, some platforms do not support + * creating a new display if one already exists that has not been + * sent the <code>dispose()</code> message. + * <p> + * In SWT, the thread which creates a <code>Display</code> + * instance is distinguished as the <em>user-interface thread</em> + * for that display. + * </p> + * The user-interface thread for a particular display has the + * following special attributes: + * <ul> + * <li> + * The event loop for that display must be run from the thread. + * </li> + * <li> + * Some SWT API methods (notably, most of the public methods in + * <code>Widget</code> and its subclasses), may only be called + * from the thread. (To support multi-threaded user-interface + * applications, class <code>Display</code> provides inter-thread + * communication methods which allow threads other than the + * user-interface thread to request that it perform operations + * on their behalf.) + * </li> + * <li> + * The thread is not allowed to construct other + * <code>Display</code>s until that display has been disposed. + * (Note that, this is in addition to the restriction mentioned + * above concerning platform support for multiple displays. Thus, + * the only way to have multiple simultaneously active displays, + * even on platforms which support it, is to have multiple threads.) + * </li> + * </ul> + * Enforcing these attributes allows SWT to be implemented directly + * on the underlying operating system's event model. This has + * numerous benefits including smaller footprint, better use of + * resources, safer memory management, clearer program logic, + * better performance, and fewer overall operating system threads + * required. The down side however, is that care must be taken + * (only) when constructing multi-threaded applications to use the + * inter-thread communication mechanisms which this class provides + * when required. + * </p><p> + * All SWT API methods which may only be called from the user-interface + * thread are distinguished in their documentation by indicating that + * they throw the "<code>ERROR_THREAD_INVALID_ACCESS</code>" + * SWT exception. + * </p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>(none)</dd> + * <dt><b>Events:</b></dt> + * <dd>Close, Dispose, Settings</dd> + * </dl> + * <p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * @see #syncExec + * @see #asyncExec + * @see #wake + * @see #readAndDispatch + * @see #sleep + * @see Device#dispose + * @see <a href="http://www.eclipse.org/swt/snippets/#display">Display snippets</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Display extends Device { + + /* Windows and Events */ + Event [] eventQueue; + EventTable eventTable, filterTable; + boolean disposing; + int sendEventCount; + + /* Key event management */ + int [] deadKeyState = new int[1]; + int currentKeyboardUCHRdata; + boolean eventSourceDelaySet; + + /* Sync/Async Widget Communication */ + Synchronizer synchronizer; + Thread thread; + boolean allowTimers, runAsyncMessages; + + GCData[] contexts; + + Caret currentCaret; + + boolean sendEvent; + Control currentControl, trackingControl, tooltipControl; + Widget tooltipTarget; + + NSMutableArray isPainting, needsDisplay, needsDisplayInRect; + + NSDictionary markedAttributes; + + /* Fonts */ + boolean smallFonts; + NSFont buttonFont, popUpButtonFont, textFieldFont, secureTextFieldFont; + NSFont searchFieldFont, comboBoxFont, sliderFont, scrollerFont; + NSFont textViewFont, tableViewFont, outlineViewFont, datePickerFont; + NSFont boxFont, tabViewFont, progressIndicatorFont; + + Shell [] modalShells; + + Menu menuBar; + Menu[] menus, popups; + + NSApplication application; + int /*long*/ applicationClass; + NSImage dockImage; + boolean isEmbedded; + static boolean launched = false; + + /* Focus */ + Control focusControl, currentFocusControl; + int focusEvent; + + NSWindow screenWindow, keyWindow; + + NSAutoreleasePool[] pools; + int poolCount, loopCount; + + int[] screenID = new int[32]; + NSPoint[] screenCascade = new NSPoint[32]; + + int /*long*/ runLoopObserver; + Callback observerCallback; + + boolean lockCursor = true; + int /*long*/ oldCursorSetProc; + Callback cursorSetCallback; + + // the following Callbacks are never freed + static Callback windowCallback2, windowCallback3, windowCallback4, windowCallback5, windowCallback6; + static Callback dialogCallback3, dialogCallback4, dialogCallback5; + static Callback applicationCallback2, applicationCallback3, applicationCallback6; + static Callback fieldEditorCallback3, fieldEditorCallback4; + + /* Display Shutdown */ + Runnable [] disposeList; + + /* System Tray */ + Tray tray; + TrayItem currentTrayItem; + Menu trayItemMenu; + + /* System Resources */ + Image errorImage, infoImage, warningImage; + Cursor [] cursors = new Cursor [SWT.CURSOR_HAND + 1]; + + /* System Colors */ + float /*double*/ [][] colors; + float /*double*/ [] alternateSelectedControlTextColor, selectedControlTextColor; + float /*double*/ [] alternateSelectedControlColor, secondarySelectedControlColor; + + /* Key Mappings. */ + static int [] [] KeyTable = { + + /* Keyboard and Mouse Masks */ + {58, SWT.ALT}, + {56, SWT.SHIFT}, + {59, SWT.CONTROL}, + {55, SWT.COMMAND}, + {61, SWT.ALT}, + {62, SWT.CONTROL}, + {60, SWT.SHIFT}, + {54, SWT.COMMAND}, + + /* Non-Numeric Keypad Keys */ + {126, SWT.ARROW_UP}, + {125, SWT.ARROW_DOWN}, + {123, SWT.ARROW_LEFT}, + {124, SWT.ARROW_RIGHT}, + {116, SWT.PAGE_UP}, + {121, SWT.PAGE_DOWN}, + {115, SWT.HOME}, + {119, SWT.END}, +// {??, SWT.INSERT}, + + /* Virtual and Ascii Keys */ + {51, SWT.BS}, + {36, SWT.CR}, + {117, SWT.DEL}, + {53, SWT.ESC}, + {76, SWT.LF}, + {48, SWT.TAB}, + + /* Functions Keys */ + {122, SWT.F1}, + {120, SWT.F2}, + {99, SWT.F3}, + {118, SWT.F4}, + {96, SWT.F5}, + {97, SWT.F6}, + {98, SWT.F7}, + {100, SWT.F8}, + {101, SWT.F9}, + {109, SWT.F10}, + {103, SWT.F11}, + {111, SWT.F12}, + {105, SWT.F13}, + {107, SWT.F14}, + {113, SWT.F15}, + + /* Numeric Keypad Keys */ + {67, SWT.KEYPAD_MULTIPLY}, + {69, SWT.KEYPAD_ADD}, + {76, SWT.KEYPAD_CR}, + {78, SWT.KEYPAD_SUBTRACT}, + {65, SWT.KEYPAD_DECIMAL}, + {75, SWT.KEYPAD_DIVIDE}, + {82, SWT.KEYPAD_0}, + {83, SWT.KEYPAD_1}, + {84, SWT.KEYPAD_2}, + {85, SWT.KEYPAD_3}, + {86, SWT.KEYPAD_4}, + {87, SWT.KEYPAD_5}, + {88, SWT.KEYPAD_6}, + {89, SWT.KEYPAD_7}, + {91, SWT.KEYPAD_8}, + {92, SWT.KEYPAD_9}, + {81, SWT.KEYPAD_EQUAL}, + + /* Other keys */ + {57, SWT.CAPS_LOCK}, + {71, SWT.NUM_LOCK}, +// {??, SWT.SCROLL_LOCK}, +// {??, SWT.PAUSE}, +// {??, SWT.BREAK}, +// {??, SWT.PRINT_SCREEN}, + {114, SWT.HELP}, + + }; + + static String APP_NAME; + static final String ADD_WIDGET_KEY = "org.eclipse.swt.internal.addWidget"; //$NON-NLS-1$ + static final byte[] SWT_OBJECT = {'S', 'W', 'T', '_', 'O', 'B', 'J', 'E', 'C', 'T', '\0'}; + static final byte[] SWT_IMAGE = {'S', 'W', 'T', '_', 'I', 'M', 'A', 'G', 'E', '\0'}; + static final byte[] SWT_ROW = {'S', 'W', 'T', '_', 'R', 'O', 'W', '\0'}; + static final byte[] SWT_COLUMN = {'S', 'W', 'T', '_', 'C', 'O', 'L', 'U', 'M', 'N', '\0'}; + + /* Multiple Displays. */ + static Display Default; + static Display [] Displays = new Display [4]; + + /* Package Name */ + static final String PACKAGE_PREFIX = "org.eclipse.swt.widgets."; + + /* Timer */ + Runnable timerList []; + NSTimer nsTimers []; + SWTWindowDelegate timerDelegate; + static SWTApplicationDelegate applicationDelegate; + + /* Settings */ + boolean runSettings; + SWTWindowDelegate settingsDelegate; + + static final int DEFAULT_BUTTON_INTERVAL = 30; + + /* Display Data */ + Object data; + String [] keys; + Object [] values; + + /* + * TEMPORARY CODE. Install the runnable that + * gets the current display. This code will + * be removed in the future. + */ + static { + DeviceFinder = new Runnable () { + public void run () { + Device device = getCurrent (); + if (device == null) { + device = getDefault (); + } + setDevice (device); + } + }; + } + +/* +* TEMPORARY CODE. +*/ +static void setDevice (Device device) { + CurrentDevice = device; +} + +static byte [] ascii (String name) { + int length = name.length (); + char [] chars = new char [length]; + name.getChars (0, length, chars, 0); + byte [] buffer = new byte [length + 1]; + for (int i=0; i<length; i++) { + buffer [i] = (byte) chars [i]; + } + return buffer; +} + +static int translateKey (int key) { + for (int i=0; i<KeyTable.length; i++) { + if (KeyTable [i] [0] == key) return KeyTable [i] [1]; + } + return 0; +} + +static int untranslateKey (int key) { + for (int i=0; i<KeyTable.length; i++) { + if (KeyTable [i] [1] == key) return KeyTable [i] [0]; + } + return 0; +} + +void addContext (GCData context) { + if (contexts == null) contexts = new GCData [12]; + for (int i=0; i<contexts.length; i++) { + if (contexts[i] != null && contexts [i] == context) { + contexts [i] = context; + return; + } + } + GCData [] newContexts = new GCData [contexts.length + 12]; + newContexts [contexts.length] = context; + System.arraycopy (contexts, 0, newContexts, 0, contexts.length); + contexts = newContexts; +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when an event of the given type occurs anywhere + * in a widget. The event type is one of the event constants + * defined in class <code>SWT</code>. When the event does occur, + * the listener is notified by sending it the <code>handleEvent()</code> + * message. + * <p> + * Setting the type of an event to <code>SWT.None</code> from + * within the <code>handleEvent()</code> method can be used to + * change the event type and stop subsequent Java listeners + * from running. Because event filters run before other listeners, + * event filters can both block other listeners and set arbitrary + * fields within an event. For this reason, event filters are both + * powerful and dangerous. They should generally be avoided for + * performance, debugging and code maintenance reasons. + * </p> + * + * @param eventType the type of event to listen for + * @param listener the listener which should be notified when the event occurs + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see Listener + * @see SWT + * @see #removeFilter + * @see #removeListener + * + * @since 3.0 + */ +public void addFilter (int eventType, Listener listener) { + checkDevice (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (filterTable == null) filterTable = new EventTable (); + filterTable.hook (eventType, listener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when an event of the given type occurs. The event + * type is one of the event constants defined in class <code>SWT</code>. + * When the event does occur in the display, the listener is notified by + * sending it the <code>handleEvent()</code> message. + * + * @param eventType the type of event to listen for + * @param listener the listener which should be notified when the event occurs + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see Listener + * @see SWT + * @see #removeListener + * + * @since 2.0 + */ +public void addListener (int eventType, Listener listener) { + checkDevice (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) eventTable = new EventTable (); + eventTable.hook (eventType, listener); +} + +void addMenu (Menu menu) { + if (menus == null) menus = new Menu [12]; + for (int i=0; i<menus.length; i++) { + if (menus [i] == null) { + menus [i] = menu; + return; + } + } + Menu [] newMenus = new Menu [menus.length + 12]; + newMenus [menus.length] = menu; + System.arraycopy (menus, 0, newMenus, 0, menus.length); + menus = newMenus; +} + +void addPool () { + addPool ((NSAutoreleasePool)new NSAutoreleasePool().alloc().init()); +} + +void addPool (NSAutoreleasePool pool) { + if (pools == null) pools = new NSAutoreleasePool [4]; + if (poolCount == pools.length) { + NSAutoreleasePool[] temp = new NSAutoreleasePool [poolCount + 4]; + System.arraycopy (pools, 0, temp, 0, poolCount); + pools = temp; + } + if (poolCount == 0) { + NSMutableDictionary dictionary = NSThread.currentThread().threadDictionary(); + dictionary.setObject(NSNumber.numberWithInteger(pool.id), NSString.stringWith("SWT_NSAutoreleasePool")); + } + pools [poolCount++] = pool; +} + +void addPopup (Menu menu) { + if (popups == null) popups = new Menu [4]; + int length = popups.length; + for (int i=0; i<length; i++) { + if (popups [i] == menu) return; + } + int index = 0; + while (index < length) { + if (popups [index] == null) break; + index++; + } + if (index == length) { + Menu [] newPopups = new Menu [length + 4]; + System.arraycopy (popups, 0, newPopups, 0, length); + popups = newPopups; + } + popups [index] = menu; +} + +void addWidget (NSObject view, Widget widget) { + if (view == null) return; + OS.object_setInstanceVariable (view.id, SWT_OBJECT, widget.jniRef); +} + +/** + * Causes the <code>run()</code> method of the runnable to + * be invoked by the user-interface thread at the next + * reasonable opportunity. The caller of this method continues + * to run in parallel, and is not notified when the + * runnable has completed. Specifying <code>null</code> as the + * runnable simply wakes the user-interface thread when run. + * <p> + * Note that at the time the runnable is invoked, widgets + * that have the receiver as their display may have been + * disposed. Therefore, it is necessary to check for this + * case inside the runnable before accessing the widget. + * </p> + * + * @param runnable code to run on the user-interface thread or <code>null</code> + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #syncExec + */ +public void asyncExec (Runnable runnable) { + synchronized (Device.class) { + if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED); + synchronizer.asyncExec (runnable); + } +} + +/** + * Causes the system hardware to emit a short sound + * (if it supports this capability). + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void beep () { + checkDevice (); + OS.NSBeep (); +} + +void cascadeWindow (NSWindow window, NSScreen screen) { + NSDictionary dictionary = screen.deviceDescription(); + int screenNumber = new NSNumber(dictionary.objectForKey(NSString.stringWith("NSScreenNumber")).id).intValue(); + int index = 0; + while (screenID[index] != 0 && screenID[index] != screenNumber) index++; + screenID[index] = screenNumber; + NSPoint cascade = screenCascade[index]; + if (cascade == null) { + NSRect frame = screen.frame(); + cascade = new NSPoint(); + cascade.x = frame.x; + cascade.y = frame.y + frame.height; + } + screenCascade[index] = window.cascadeTopLeftFromPoint(cascade); +} + +protected void checkDevice () { + if (thread == null) error (SWT.ERROR_WIDGET_DISPOSED); + if (thread != Thread.currentThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS); + if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED); +} + +void checkEnterExit (Control control, NSEvent nsEvent, boolean send) { + if (control != currentControl) { + if (currentControl != null && !currentControl.isDisposed()) { + currentControl.sendMouseEvent (nsEvent, SWT.MouseExit, send); + } + if (control != null && control.isDisposed()) control = null; + currentControl = control; + if (control != null) { + control.sendMouseEvent (nsEvent, SWT.MouseEnter, send); + } + setCursor (control); + } + timerExec (control != null && !control.isDisposed() ? getToolTipTime () : -1, hoverTimer); +} + +void checkFocus () { + Control oldControl = currentFocusControl; + Control newControl = getFocusControl (); + if (oldControl != newControl) { + if (oldControl != null && !oldControl.isDisposed ()) { + oldControl.sendFocusEvent (SWT.FocusOut); + } + currentFocusControl = newControl; + if (newControl != null && !newControl.isDisposed ()) { + newControl.sendFocusEvent (SWT.FocusIn); + } + } +} + +/** + * Checks that this class can be subclassed. + * <p> + * IMPORTANT: See the comment in <code>Widget.checkSubclass()</code>. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see Widget#checkSubclass + */ +protected void checkSubclass () { + if (!Display.isValidClass (getClass ())) error (SWT.ERROR_INVALID_SUBCLASS); +} + +/** + * Constructs a new instance of this class. + * <p> + * Note: The resulting display is marked as the <em>current</em> + * display. If this is the first display which has been + * constructed since the application started, it is also + * marked as the <em>default</em> display. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if called from a thread that already created an existing display</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see #getCurrent + * @see #getDefault + * @see Widget#checkSubclass + * @see Shell + */ +public Display () { + this (null); +} + +/** + * Constructs a new instance of this class using the parameter. + * + * @param data the device data + */ +public Display (DeviceData data) { + super (data); +} + +static void checkDisplay (Thread thread, boolean multiple) { + synchronized (Device.class) { + for (int i=0; i<Displays.length; i++) { + if (Displays [i] != null) { + if (!multiple) SWT.error (SWT.ERROR_NOT_IMPLEMENTED, null, " [multiple displays]"); + if (Displays [i].thread == thread) SWT.error (SWT.ERROR_THREAD_INVALID_ACCESS); + } + } + } +} + +static String convertToLf(String text) { + char Cr = '\r'; + char Lf = '\n'; + int length = text.length (); + if (length == 0) return text; + + /* Check for an LF or CR/LF. Assume the rest of the string + * is formated that way. This will not work if the string + * contains mixed delimiters. */ + int i = text.indexOf (Lf, 0); + if (i == -1 || i == 0) return text; + if (text.charAt (i - 1) != Cr) return text; + + /* The string is formatted with CR/LF. + * Create a new string with the LF line delimiter. */ + i = 0; + StringBuffer result = new StringBuffer (); + while (i < length) { + int j = text.indexOf (Cr, i); + if (j == -1) j = length; + String s = text.substring (i, j); + result.append (s); + i = j + 2; + result.append (Lf); + } + return result.toString (); +} + +void clearModal (Shell shell) { + if (modalShells == null) return; + int index = 0, length = modalShells.length; + while (index < length) { + if (modalShells [index] == shell) break; + if (modalShells [index] == null) return; + index++; + } + if (index == length) return; + System.arraycopy (modalShells, index + 1, modalShells, index, --length - index); + modalShells [length] = null; + if (index == 0 && modalShells [0] == null) modalShells = null; + Shell [] shells = getShells (); + for (int i=0; i<shells.length; i++) shells [i].updateModal (); +} + +void clearPool () { + if (sendEventCount == 0 && loopCount == poolCount - 1 && Callback.getEntryCount () == 0) { + removePool (); + addPool (); + } +} + +/** + * Requests that the connection between SWT and the underlying + * operating system be closed. + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see Device#dispose + * + * @since 2.0 + */ +public void close () { + checkDevice (); + Event event = new Event (); + sendEvent (SWT.Close, event); + if (event.doit) dispose (); +} + +/** + * Creates the device in the operating system. If the device + * does not have a handle, this method may do nothing depending + * on the device. + * <p> + * This method is called before <code>init</code>. + * </p> + * + * @param data the DeviceData which describes the receiver + * + * @see #init + */ +protected void create (DeviceData data) { + checkSubclass (); + checkDisplay (thread = Thread.currentThread (), false); + createDisplay (data); + register (this); + synchronizer = new Synchronizer (this); + if (Default == null) Default = this; +} + +void createDisplay (DeviceData data) { + if (OS.VERSION < 0x1050) { + System.out.println ("***WARNING: SWT requires MacOS X version " + 10 + "." + 5 + " or greater"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + System.out.println ("***WARNING: Detected: " + Integer.toHexString((OS.VERSION & 0xFF00) >> 8) + "." + Integer.toHexString((OS.VERSION & 0xF0) >> 4) + "." + Integer.toHexString(OS.VERSION & 0xF)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + error(SWT.ERROR_NOT_IMPLEMENTED); + } + + NSThread nsthread = NSThread.currentThread(); + NSMutableDictionary dictionary = nsthread.threadDictionary(); + NSString key = NSString.stringWith("SWT_NSAutoreleasePool"); + NSNumber id = new NSNumber(dictionary.objectForKey(key)); + addPool(new NSAutoreleasePool(id.integerValue())); + + application = NSApplication.sharedApplication(); + + /* + * TODO: If an NSApplication is already running we don't want to create another NSApplication. + * But if we don't we won't get mouse events, since we currently need to subclass NSApplication and intercept sendEvent to + * deliver mouse events correctly to widgets. + */ + if (!application.isRunning()) { + /* + * Feature in the Macintosh. On OS 10.2, it is necessary + * to explicitly check in with the Process Manager and set + * the current process to be the front process in order for + * windows to come to the front by default. The fix is call + * both GetCurrentProcess() and SetFrontProcess(). + * + * NOTE: It is not actually necessary to use the process + * serial number returned by GetCurrentProcess() in the + * call to SetFrontProcess() (ie. kCurrentProcess can be + * used) but both functions must be called in order for + * windows to come to the front. + */ + int [] psn = new int [2]; + if (OS.GetCurrentProcess (psn) == OS.noErr) { + int pid = OS.getpid (); + int /*long*/ ptr = getAppName().UTF8String(); + if (ptr != 0) OS.CPSSetProcessName (psn, ptr); + OS.TransformProcessType (psn, OS.kProcessTransformToForegroundApplication); + OS.SetFrontProcess (psn); + ptr = OS.getenv (ascii ("APP_ICON_" + pid)); + if (ptr != 0) { + NSString path = NSString.stringWithUTF8String (ptr); + NSImage image = (NSImage) new NSImage().alloc(); + image = image.initByReferencingFile(path); + dockImage = image; + application.setApplicationIconImage(image); + } + } + + String className = "SWTApplication"; + int /*long*/ cls; + if ((cls = OS.objc_lookUpClass (className)) == 0) { + Class clazz = getClass(); + applicationCallback2 = new Callback(clazz, "applicationProc", 2); + int /*long*/ proc2 = applicationCallback2.getAddress(); + if (proc2 == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + applicationCallback3 = new Callback(clazz, "applicationProc", 3); + int /*long*/ proc3 = applicationCallback3.getAddress(); + if (proc3 == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + applicationCallback6 = new Callback(clazz, "applicationProc", 6); + int /*long*/ proc6 = applicationCallback6.getAddress(); + if (proc6 == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + cls = OS.objc_allocateClassPair(OS.class_NSApplication, className, 0); + OS.class_addMethod(cls, OS.sel_sendEvent_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_nextEventMatchingMask_untilDate_inMode_dequeue_, proc6, "@:i@@B"); + OS.class_addMethod(cls, OS.sel_isRunning, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_finishLaunching, proc2, "@:"); + OS.objc_registerClassPair(cls); + } + applicationClass = OS.object_setClass(application.id, cls); + + className = "SWTApplicationDelegate"; + if (OS.objc_lookUpClass (className) == 0) { + int /*long*/ appProc3 = applicationCallback3.getAddress(); + if (appProc3 == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + cls = OS.objc_allocateClassPair(OS.class_NSObject, className, 0); + OS.class_addMethod(cls, OS.sel_applicationWillFinishLaunching_, appProc3, "@:@"); + OS.class_addMethod(cls, OS.sel_terminate_, appProc3, "@:@"); + OS.class_addMethod(cls, OS.sel_quitRequested_, appProc3, "@:@"); + OS.class_addMethod(cls, OS.sel_orderFrontStandardAboutPanel_, appProc3, "@:@"); + OS.class_addMethod(cls, OS.sel_hideOtherApplications_, appProc3, "@:@"); + OS.class_addMethod(cls, OS.sel_hide_, appProc3, "@:@"); + OS.class_addMethod(cls, OS.sel_unhideAllApplications_, appProc3, "@:@"); + OS.class_addMethod(cls, OS.sel_applicationDidBecomeActive_, appProc3, "@:@"); + OS.class_addMethod(cls, OS.sel_applicationDidResignActive_, appProc3, "@:@"); + OS.objc_registerClassPair(cls); + } + if (applicationDelegate == null) { + applicationDelegate = (SWTApplicationDelegate)new SWTApplicationDelegate().alloc().init(); + application.setDelegate(applicationDelegate); + } + } else { + isEmbedded = true; + } +} + +void createMainMenu () { + NSString appName = getAppName(); + NSString emptyStr = NSString.stringWith(""); + NSMenu mainMenu = (NSMenu)new NSMenu().alloc(); + mainMenu.initWithTitle(emptyStr); + + NSMenuItem menuItem; + NSMenu appleMenu; + NSString format = NSString.stringWith("%@ %@"), title; + + NSMenuItem appItem = menuItem = mainMenu.addItemWithTitle(emptyStr, 0, emptyStr); + appleMenu = (NSMenu)new NSMenu().alloc(); + appleMenu.initWithTitle(emptyStr); + OS.objc_msgSend(application.id, OS.sel_registerName("setAppleMenu:"), appleMenu.id); + + title = new NSString(OS.objc_msgSend(OS.class_NSString, OS.sel_stringWithFormat_, format.id, NSString.stringWith(SWT.getMessage("About")).id, appName.id)); + menuItem = appleMenu.addItemWithTitle(title, OS.sel_orderFrontStandardAboutPanel_, emptyStr); + menuItem.setTarget(applicationDelegate); + + appleMenu.addItem(NSMenuItem.separatorItem()); + + title = NSString.stringWith(SWT.getMessage("Preferences...")); + menuItem = appleMenu.addItemWithTitle(title, 0, NSString.stringWith(",")); + + appleMenu.addItem(NSMenuItem.separatorItem()); + + title = NSString.stringWith(SWT.getMessage("Services")); + menuItem = appleMenu.addItemWithTitle(title, 0, emptyStr); + NSMenu servicesMenu = (NSMenu)new NSMenu().alloc(); + servicesMenu.initWithTitle(emptyStr); + appleMenu.setSubmenu(servicesMenu, menuItem); + servicesMenu.release(); + application.setServicesMenu(servicesMenu); + + appleMenu.addItem(NSMenuItem.separatorItem()); + + title = new NSString(OS.objc_msgSend(OS.class_NSString, OS.sel_stringWithFormat_, format.id, NSString.stringWith(SWT.getMessage("Hide")).id, appName.id)); + menuItem = appleMenu.addItemWithTitle(title, OS.sel_hide_, NSString.stringWith("h")); + menuItem.setTarget(applicationDelegate); + + title = NSString.stringWith(SWT.getMessage("Hide Others")); + menuItem = appleMenu.addItemWithTitle(title, OS.sel_hideOtherApplications_, NSString.stringWith("h")); + menuItem.setKeyEquivalentModifierMask(OS.NSCommandKeyMask | OS.NSAlternateKeyMask); + menuItem.setTarget(applicationDelegate); + + title = NSString.stringWith(SWT.getMessage("Show All")); + menuItem = appleMenu.addItemWithTitle(title, OS.sel_unhideAllApplications_, emptyStr); + menuItem.setTarget(applicationDelegate); + + appleMenu.addItem(NSMenuItem.separatorItem()); + + title = new NSString(OS.objc_msgSend(OS.class_NSString, OS.sel_stringWithFormat_, format.id, NSString.stringWith(SWT.getMessage("Quit")).id, appName.id)); + menuItem = appleMenu.addItemWithTitle(title, OS.sel_quitRequested_, NSString.stringWith("q")); + menuItem.setTarget(applicationDelegate); + + mainMenu.setSubmenu(appleMenu, appItem); + appleMenu.release(); + application.setMainMenu(mainMenu); + mainMenu.release(); +} + +int /*long*/ cursorSetProc (int /*long*/ id, int /*long*/ sel) { + if (lockCursor) { + if (currentControl != null) { + Cursor cursor = currentControl.findCursor (); + if (cursor != null && cursor.handle.id != id) return 0; + } + } + OS.call (oldCursorSetProc, id, sel); + return 0; +} + +static void deregister (Display display) { + synchronized (Device.class) { + for (int i=0; i<Displays.length; i++) { + if (display == Displays [i]) Displays [i] = null; + } + } +} + +/** + * Destroys the device in the operating system and releases + * the device's handle. If the device does not have a handle, + * this method may do nothing depending on the device. + * <p> + * This method is called after <code>release</code>. + * </p> + * @see Device#dispose + * @see #release + */ +protected void destroy () { + if (this == Default) Default = null; + deregister (this); + destroyDisplay (); +} + +void destroyDisplay () { + application = null; +} + +/** + * Causes the <code>run()</code> method of the runnable to + * be invoked by the user-interface thread just before the + * receiver is disposed. Specifying a <code>null</code> runnable + * is ignored. + * + * @param runnable code to run at dispose time. + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public void disposeExec (Runnable runnable) { + checkDevice (); + if (disposeList == null) disposeList = new Runnable [4]; + for (int i=0; i<disposeList.length; i++) { + if (disposeList [i] == null) { + disposeList [i] = runnable; + return; + } + } + Runnable [] newDisposeList = new Runnable [disposeList.length + 4]; + System.arraycopy (disposeList, 0, newDisposeList, 0, disposeList.length); + newDisposeList [disposeList.length] = runnable; + disposeList = newDisposeList; +} + +void error (int code) { + SWT.error(code); +} + +boolean filterEvent (Event event) { + if (filterTable != null) filterTable.sendEvent (event); + return false; +} + +boolean filters (int eventType) { + if (filterTable == null) return false; + return filterTable.hooks (eventType); +} + +/** + * Given the operating system handle for a widget, returns + * the instance of the <code>Widget</code> subclass which + * represents it in the currently running application, if + * such exists, or null if no matching widget can be found. + * <p> + * <b>IMPORTANT:</b> This method should not be called from + * application code. The arguments are platform-specific. + * </p> + * + * @param handle the handle for the widget + * @return the SWT widget that the handle represents + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Widget findWidget (int /*long*/ handle) { + checkDevice (); + return getWidget (handle); +} + +/** + * Given the operating system handle for a widget, + * and widget-specific id, returns the instance of + * the <code>Widget</code> subclass which represents + * the handle/id pair in the currently running application, + * if such exists, or null if no matching widget can be found. + * <p> + * <b>IMPORTANT:</b> This method should not be called from + * application code. The arguments are platform-specific. + * </p> + * + * @param handle the handle for the widget + * @param id the id for the subwidget (usually an item) + * @return the SWT widget that the handle/id pair represents + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.1 + */ +public Widget findWidget (int /*long*/ handle, int id) { + checkDevice (); + return getWidget (handle); +} + +/** + * Given a widget and a widget-specific id, returns the + * instance of the <code>Widget</code> subclass which represents + * the widget/id pair in the currently running application, + * if such exists, or null if no matching widget can be found. + * + * @param widget the widget + * @param id the id for the subwidget (usually an item) + * @return the SWT subwidget (usually an item) that the widget/id pair represents + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.3 + */ +public Widget findWidget (Widget widget, int id) { + checkDevice (); + return null; +} + +/** + * Returns the display which the given thread is the + * user-interface thread for, or null if the given thread + * is not a user-interface thread for any display. Specifying + * <code>null</code> as the thread will return <code>null</code> + * for the display. + * + * @param thread the user-interface thread + * @return the display for the given thread + */ +public static Display findDisplay (Thread thread) { + synchronized (Device.class) { + for (int i=0; i<Displays.length; i++) { + Display display = Displays [i]; + if (display != null && display.thread == thread) { + return display; + } + } + return null; + } +} + +/** + * Returns the currently active <code>Shell</code>, or null + * if no shell belonging to the currently running application + * is active. + * + * @return the active shell or null + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Shell getActiveShell () { + checkDevice (); + NSWindow window = keyWindow != null ? keyWindow : application.keyWindow(); + if (window != null) { + Widget widget = getWidget(window.contentView()); + if (widget instanceof Shell) { + return (Shell)widget; + } + } + return null; +} + +/** + * Returns a rectangle describing the receiver's size and location. Note that + * on multi-monitor systems the origin can be negative. + * + * @return the bounding rectangle + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Rectangle getBounds () { + checkDevice (); + NSArray screens = NSScreen.screens(); + return getBounds (screens); +} + +Rectangle getBounds (NSArray screens) { + NSRect primaryFrame = new NSScreen(screens.objectAtIndex(0)).frame(); + float /*double*/ minX = Float.MAX_VALUE, maxX = Float.MIN_VALUE; + float /*double*/ minY = Float.MAX_VALUE, maxY = Float.MIN_VALUE; + int /*long*/ count = screens.count(); + for (int i = 0; i < count; i++) { + NSScreen screen = new NSScreen(screens.objectAtIndex(i)); + NSRect frame = screen.frame(); + float /*double*/ x1 = frame.x, x2 = frame.x + frame.width; + float /*double*/ y1 = primaryFrame.height - frame.y, y2 = primaryFrame.height - (frame.y + frame.height); + if (x1 < minX) minX = x1; + if (x2 < minX) minX = x2; + if (x1 > maxX) maxX = x1; + if (x2 > maxX) maxX = x2; + if (y1 < minY) minY = y1; + if (y2 < minY) minY = y2; + if (y1 > maxY) maxY = y1; + if (y2 > maxY) maxY = y2; + } + return new Rectangle ((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY)); +} + +/** + * Returns the display which the currently running thread is + * the user-interface thread for, or null if the currently + * running thread is not a user-interface thread for any display. + * + * @return the current display + */ +public static Display getCurrent () { + return findDisplay (Thread.currentThread ()); +} + +int getCaretBlinkTime () { +// checkDevice (); + return 560; +} + +/** + * Returns a rectangle which describes the area of the + * receiver which is capable of displaying data. + * + * @return the client area + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #getBounds + */ +public Rectangle getClientArea () { + checkDevice (); + NSArray screens = NSScreen.screens(); + if (screens.count() != 1) return getBounds (screens); + NSScreen screen = new NSScreen(screens.objectAtIndex(0)); + NSRect frame = screen.frame(); + NSRect visibleFrame = screen.visibleFrame(); + float /*double*/ y = frame.height - (visibleFrame.y + visibleFrame.height); + return new Rectangle((int)visibleFrame.x, (int)y, (int)visibleFrame.width, (int)visibleFrame.height); +} + +/** + * Returns the control which the on-screen pointer is currently + * over top of, or null if it is not currently over one of the + * controls built by the currently running application. + * + * @return the control under the cursor + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Control getCursorControl () { + checkDevice(); + return findControl(false); +} + +/** + * Returns the location of the on-screen pointer relative + * to the top left corner of the screen. + * + * @return the cursor location + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Point getCursorLocation () { + checkDevice (); + NSPoint location = NSEvent.mouseLocation(); + NSRect primaryFrame = getPrimaryFrame(); + return new Point ((int) location.x, (int) (primaryFrame.height - location.y)); +} + +/** + * Returns an array containing the recommended cursor sizes. + * + * @return the array of cursor sizes + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public Point [] getCursorSizes () { + checkDevice (); + return new Point [] {new Point (16, 16)}; +} + +/** + * Returns the default display. One is created (making the + * thread that invokes this method its user-interface thread) + * if it did not already exist. + * + * @return the default display + */ +public static Display getDefault () { + synchronized (Device.class) { + if (Default == null) Default = new Display (); + return Default; + } +} + +/** + * Returns the application defined property of the receiver + * with the specified name, or null if it has not been set. + * <p> + * Applications may have associated arbitrary objects with the + * receiver in this fashion. If the objects stored in the + * properties need to be notified when the display is disposed + * of, it is the application's responsibility to provide a + * <code>disposeExec()</code> handler which does so. + * </p> + * + * @param key the name of the property + * @return the value of the property or null if it has not been set + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the key is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #setData(String, Object) + * @see #disposeExec(Runnable) + */ +public Object getData (String key) { + checkDevice (); + if (key == null) error (SWT.ERROR_NULL_ARGUMENT); + if (keys == null) return null; + for (int i=0; i<keys.length; i++) { + if (keys [i].equals (key)) return values [i]; + } + return null; +} + +/** + * Returns the application defined, display specific data + * associated with the receiver, or null if it has not been + * set. The <em>display specific data</em> is a single, + * unnamed field that is stored with every display. + * <p> + * Applications may put arbitrary objects in this field. If + * the object stored in the display specific data needs to + * be notified when the display is disposed of, it is the + * application's responsibility to provide a + * <code>disposeExec()</code> handler which does so. + * </p> + * + * @return the display specific data + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #setData(Object) + * @see #disposeExec(Runnable) + */ +public Object getData () { + checkDevice (); + return data; +} + +/** + * Returns the button dismissal alignment, one of <code>LEFT</code> or <code>RIGHT</code>. + * The button dismissal alignment is the ordering that should be used when positioning the + * default dismissal button for a dialog. For example, in a dialog that contains an OK and + * CANCEL button, on platforms where the button dismissal alignment is <code>LEFT</code>, the + * button ordering should be OK/CANCEL. When button dismissal alignment is <code>RIGHT</code>, + * the button ordering should be CANCEL/OK. + * + * @return the button dismissal order + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 2.1 + */ +public int getDismissalAlignment () { + checkDevice (); + return SWT.RIGHT; +} + +/** + * Returns the longest duration, in milliseconds, between + * two mouse button clicks that will be considered a + * <em>double click</em> by the underlying operating system. + * + * @return the double click time + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public int getDoubleClickTime () { + checkDevice (); + return OS.GetDblTime () * 1000 / 60; +} + +/** + * Returns the control which currently has keyboard focus, + * or null if keyboard events are not currently going to + * any of the controls built by the currently running + * application. + * + * @return the control under the cursor + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Control getFocusControl () { + checkDevice (); + if (focusControl != null && !focusControl.isDisposed ()) { + return focusControl; + } + NSWindow window = keyWindow != null ? keyWindow : application.keyWindow(); + return _getFocusControl(window); +} + +Control _getFocusControl (NSWindow window) { + if (window != null) { + NSResponder responder = window.firstResponder(); + if (responder != null && !responder.respondsToSelector(OS.sel_superview)) { + return null; + } + NSView view = new NSView(responder.id); + if (view != null) { + do { + Widget widget = GetWidget (view.id); + if (widget instanceof Control) { + return (Control)widget; + } + view = view.superview(); + } while (view != null); + } + } + return null; +} + +/** + * Returns true when the high contrast mode is enabled. + * Otherwise, false is returned. + * <p> + * Note: This operation is a hint and is not supported on + * platforms that do not have this concept. + * </p> + * + * @return the high contrast mode + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public boolean getHighContrast () { + checkDevice (); + return false; +} + +/** + * Returns the maximum allowed depth of icons on this display, in bits per pixel. + * On some platforms, this may be different than the actual depth of the display. + * + * @return the maximum icon depth + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see Device#getDepth + */ +public int getIconDepth () { + return getDepth (); +} + +/** + * Returns an array containing the recommended icon sizes. + * + * @return the array of icon sizes + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see Decorations#setImages(Image[]) + * + * @since 3.0 + */ +public Point [] getIconSizes () { + checkDevice (); + return new Point [] { + new Point (16, 16), new Point (32, 32), + new Point (64, 64), new Point (128, 128)}; +} + +int getLastEventTime () { + NSEvent event = application.currentEvent(); + return event != null ? (int)(event.timestamp() * 1000) : 0; +} + +Menu [] getMenus (Decorations shell) { + if (menus == null) return new Menu [0]; + int count = 0; + for (int i = 0; i < menus.length; i++) { + Menu menu = menus[i]; + if (menu != null && menu.parent == shell) count++; + } + int index = 0; + Menu[] result = new Menu[count]; + for (int i = 0; i < menus.length; i++) { + Menu menu = menus[i]; + if (menu != null && menu.parent == shell) { + result[index++] = menu; + } + } + return result; +} + +int getMessageCount () { + return synchronizer.getMessageCount (); +} + +/** + * Returns an array of monitors attached to the device. + * + * @return the array of monitors + * + * @since 3.0 + */ +public Monitor [] getMonitors () { + checkDevice (); + NSArray screens = NSScreen.screens(); + NSRect primaryFrame = new NSScreen(screens.objectAtIndex(0)).frame(); + int count = (int)/*64*/screens.count(); + Monitor [] monitors = new Monitor [count]; + for (int i=0; i<count; i++) { + Monitor monitor = new Monitor (); + NSScreen screen = new NSScreen(screens.objectAtIndex(i)); + NSRect frame = screen.frame(); + monitor.x = (int)frame.x; + monitor.y = (int)(primaryFrame.height - (frame.y + frame.height)); + monitor.width = (int)frame.width; + monitor.height = (int)frame.height; + NSRect visibleFrame = screen.visibleFrame(); + monitor.clientX = (int)visibleFrame.x; + monitor.clientY = (int)(primaryFrame.height - (visibleFrame.y + visibleFrame.height)); + monitor.clientWidth = (int)visibleFrame.width; + monitor.clientHeight = (int)visibleFrame.height; + monitors [i] = monitor; + } + return monitors; +} + +NSRect getPrimaryFrame () { + NSArray screens = NSScreen.screens(); + return new NSScreen(screens.objectAtIndex(0)).frame(); +} + +/** + * Returns the primary monitor for that device. + * + * @return the primary monitor + * + * @since 3.0 + */ +public Monitor getPrimaryMonitor () { + checkDevice (); + Monitor monitor = new Monitor (); + NSArray screens = NSScreen.screens(); + NSScreen screen = new NSScreen(screens.objectAtIndex(0)); + NSRect frame = screen.frame(); + monitor.x = (int)frame.x; + monitor.y = (int)(frame.height - (frame.y + frame.height)); + monitor.width = (int)frame.width; + monitor.height = (int)frame.height; + NSRect visibleFrame = screen.visibleFrame(); + monitor.clientX = (int)visibleFrame.x; + monitor.clientY = (int)(frame.height - (visibleFrame.y + visibleFrame.height)); + monitor.clientWidth = (int)visibleFrame.width; + monitor.clientHeight = (int)visibleFrame.height; + return monitor; +} + +/** + * Returns a (possibly empty) array containing all shells which have + * not been disposed and have the receiver as their display. + * + * @return the receiver's shells + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Shell [] getShells () { + checkDevice (); + NSArray windows = application.windows(); + int index = 0; + Shell [] result = new Shell [(int)/*64*/windows.count()]; + for (int i = 0; i < result.length; i++) { + NSWindow window = new NSWindow(windows.objectAtIndex(i)); + Widget widget = getWidget(window.contentView()); + if (widget instanceof Shell) { + result[index++] = (Shell)widget; + } + } + if (index == result.length) return result; + Shell [] newResult = new Shell [index]; + System.arraycopy (result, 0, newResult, 0, index); + return newResult; +} + +static boolean getSheetEnabled () { + return !"false".equals(System.getProperty("org.eclipse.swt.sheet")); +} + +/** + * Gets the synchronizer used by the display. + * + * @return the receiver's synchronizer + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.4 + */ +public Synchronizer getSynchronizer () { + checkDevice (); + return synchronizer; +} + +/** + * Returns the thread that has invoked <code>syncExec</code> + * or null if no such runnable is currently being invoked by + * the user-interface thread. + * <p> + * Note: If a runnable invoked by asyncExec is currently + * running, this method will return null. + * </p> + * + * @return the receiver's sync-interface thread + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Thread getSyncThread () { + synchronized (Device.class) { + if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED); + return synchronizer.syncThread; + } +} + +/** + * Returns the matching standard color for the given + * constant, which should be one of the color constants + * specified in class <code>SWT</code>. Any value other + * than one of the SWT color constants which is passed + * in will result in the color black. This color should + * not be free'd because it was allocated by the system, + * not the application. + * + * @param id the color constant + * @return the matching color + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see SWT + */ +public Color getSystemColor (int id) { + checkDevice (); + Color color = getWidgetColor (id); + if (color != null) return color; + return super.getSystemColor (id); +} + +Color getWidgetColor (int id) { + if (0 <= id && id < colors.length && colors [id] != null) { + return Color.cocoa_new (this, colors [id]); + } + return null; +} + +float /*double*/ [] getWidgetColorRGB (int id) { + NSColor color = null; + switch (id) { + case SWT.COLOR_INFO_FOREGROUND: color = NSColor.blackColor (); break; + case SWT.COLOR_INFO_BACKGROUND: return new float /*double*/ [] {0xFF / 255f, 0xFF / 255f, 0xE1 / 255f, 1}; + case SWT.COLOR_TITLE_FOREGROUND: color = NSColor.windowFrameTextColor(); break; + case SWT.COLOR_TITLE_BACKGROUND: color = NSColor.alternateSelectedControlColor(); break; + case SWT.COLOR_TITLE_BACKGROUND_GRADIENT: color = NSColor.selectedControlColor(); break; + case SWT.COLOR_TITLE_INACTIVE_FOREGROUND: color = NSColor.disabledControlTextColor(); break; + case SWT.COLOR_TITLE_INACTIVE_BACKGROUND: color = NSColor.secondarySelectedControlColor(); break; + case SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT: color = NSColor.secondarySelectedControlColor(); break; + case SWT.COLOR_WIDGET_DARK_SHADOW: color = NSColor.controlDarkShadowColor(); break; + case SWT.COLOR_WIDGET_NORMAL_SHADOW: color = NSColor.controlShadowColor(); break; + case SWT.COLOR_WIDGET_LIGHT_SHADOW: color = NSColor.controlHighlightColor(); break; + case SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW: color = NSColor.controlLightHighlightColor(); break; + case SWT.COLOR_WIDGET_BACKGROUND: color = NSColor.controlHighlightColor(); break; + case SWT.COLOR_WIDGET_FOREGROUND: color = NSColor.controlTextColor(); break; + case SWT.COLOR_WIDGET_BORDER: color = NSColor.blackColor (); break; + case SWT.COLOR_LIST_FOREGROUND: color = NSColor.textColor(); break; + case SWT.COLOR_LIST_BACKGROUND: color = NSColor.textBackgroundColor(); break; + case SWT.COLOR_LIST_SELECTION_TEXT: color = NSColor.selectedTextColor(); break; + case SWT.COLOR_LIST_SELECTION: color = NSColor.selectedTextBackgroundColor(); break; + } + return getWidgetColorRGB (color); +} + +float /*double*/ [] getWidgetColorRGB (NSColor color) { + if (color == null) return null; + color = color.colorUsingColorSpace(NSColorSpace.deviceRGBColorSpace()); + if (color == null) return null; + float /*double*/[] components = new float /*double*/[(int)/*64*/color.numberOfComponents()]; + color.getComponents(components); + return new float /*double*/ []{components[0], components[1], components[2], components[3]}; +} + +/** + * Returns the matching standard platform cursor for the given + * constant, which should be one of the cursor constants + * specified in class <code>SWT</code>. This cursor should + * not be free'd because it was allocated by the system, + * not the application. A value of <code>null</code> will + * be returned if the supplied constant is not an SWT cursor + * constant. + * + * @param id the SWT cursor constant + * @return the corresponding cursor or <code>null</code> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see SWT#CURSOR_ARROW + * @see SWT#CURSOR_WAIT + * @see SWT#CURSOR_CROSS + * @see SWT#CURSOR_APPSTARTING + * @see SWT#CURSOR_HELP + * @see SWT#CURSOR_SIZEALL + * @see SWT#CURSOR_SIZENESW + * @see SWT#CURSOR_SIZENS + * @see SWT#CURSOR_SIZENWSE + * @see SWT#CURSOR_SIZEWE + * @see SWT#CURSOR_SIZEN + * @see SWT#CURSOR_SIZES + * @see SWT#CURSOR_SIZEE + * @see SWT#CURSOR_SIZEW + * @see SWT#CURSOR_SIZENE + * @see SWT#CURSOR_SIZESE + * @see SWT#CURSOR_SIZESW + * @see SWT#CURSOR_SIZENW + * @see SWT#CURSOR_UPARROW + * @see SWT#CURSOR_IBEAM + * @see SWT#CURSOR_NO + * @see SWT#CURSOR_HAND + * + * @since 3.0 + */ +public Cursor getSystemCursor (int id) { + checkDevice (); + if (!(0 <= id && id < cursors.length)) return null; + if (cursors [id] == null) { + cursors [id] = new Cursor (this, id); + } + return cursors [id]; +} + +/** + * Returns the matching standard platform image for the given + * constant, which should be one of the icon constants + * specified in class <code>SWT</code>. This image should + * not be free'd because it was allocated by the system, + * not the application. A value of <code>null</code> will + * be returned either if the supplied constant is not an + * SWT icon constant or if the platform does not define an + * image that corresponds to the constant. + * + * @param id the SWT icon constant + * @return the corresponding image or <code>null</code> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see SWT#ICON_ERROR + * @see SWT#ICON_INFORMATION + * @see SWT#ICON_QUESTION + * @see SWT#ICON_WARNING + * @see SWT#ICON_WORKING + * + * @since 3.0 + */ +public Image getSystemImage (int id) { + checkDevice (); + switch(id) { + case SWT.ICON_ERROR: { + if (errorImage != null) return errorImage; + NSImage nsImage = NSWorkspace.sharedWorkspace ().iconForFileType (new NSString (OS.NSFileTypeForHFSTypeCode (OS.kAlertStopIcon))); + if (nsImage == null) return null; + nsImage.retain (); + return errorImage = Image.cocoa_new (this, SWT.ICON, nsImage); + } + case SWT.ICON_INFORMATION: + case SWT.ICON_QUESTION: + case SWT.ICON_WORKING: { + if (infoImage != null) return infoImage; + NSImage nsImage = NSWorkspace.sharedWorkspace ().iconForFileType (new NSString (OS.NSFileTypeForHFSTypeCode (OS.kAlertNoteIcon))); + if (nsImage == null) return null; + nsImage.retain (); + return infoImage = Image.cocoa_new (this, SWT.ICON, nsImage); + } + case SWT.ICON_WARNING: { + if (warningImage != null) return warningImage; + NSImage nsImage = NSWorkspace.sharedWorkspace ().iconForFileType (new NSString (OS.NSFileTypeForHFSTypeCode (OS.kAlertCautionIcon))); + if (nsImage == null) return null; + nsImage.retain (); + return warningImage = Image.cocoa_new (this, SWT.ICON, nsImage); + } + } + return null; +} + +/** + * Returns the single instance of the system tray or null + * when there is no system tray available for the platform. + * + * @return the system tray or <code>null</code> + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.0 + */ +public Tray getSystemTray () { + checkDevice (); + if (tray != null) return tray; + return tray = new Tray (this, SWT.NONE); +} + +/** + * Returns the user-interface thread for the receiver. + * + * @return the receiver's user-interface thread + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Thread getThread () { + synchronized (Device.class) { + if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED); + return thread; + } +} + +int getToolTipTime () { + checkDevice (); + //TODO get OS value (NSTooltipManager?) + return 560; +} + +Widget getWidget (int /*long*/ id) { + return GetWidget (id); +} + +static Widget GetWidget (int /*long*/ id) { + if (id == 0) return null; + int /*long*/ [] jniRef = new int /*long*/ [1]; + OS.object_getInstanceVariable(id, SWT_OBJECT, jniRef); + if (jniRef[0] == 0) return null; + return (Widget)OS.JNIGetObject(jniRef[0]); +} + +Widget getWidget (NSView view) { + if (view == null) return null; + return getWidget(view.id); +} + +boolean hasDefaultButton () { + NSArray windows = application.windows(); + int /*long*/ count = windows.count(); + for (int i = 0; i < count; i++) { + NSWindow window = new NSWindow(windows.objectAtIndex(i)); + if (window.defaultButtonCell() != null) { + return true; + } + } + return false; +} + +/** + * Initializes any internal resources needed by the + * device. + * <p> + * This method is called after <code>create</code>. + * </p> + * + * @see #create + */ +protected void init () { + super.init (); + initClasses (); + initColors (); + initFonts (); + + if (!isEmbedded) { + /* + * Feature in Cocoa: NSApplication.finishLaunching() adds an apple menu to the menu bar that isn't accessible via NSMenu. + * If Display objects are created and disposed of multiple times in a single process, another apple menu is added to the menu bar. + * It must be called or the dock icon will continue to bounce. So, it should only be called once per process, not just once per + * creation of a Display. Use a static so creation of additional Display objects won't affect the menu bar. + */ + if (!Display.launched) { + application.finishLaunching(); + Display.launched = true; + + /* only add the shutdown hook once */ + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + NSApplication.sharedApplication().terminate(null); + } + }); + } + } + + observerCallback = new Callback (this, "observerProc", 3); //$NON-NLS-1$ + int /*long*/ observerProc = observerCallback.getAddress (); + if (observerProc == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + int activities = OS.kCFRunLoopBeforeWaiting; + runLoopObserver = OS.CFRunLoopObserverCreate (0, activities, true, 0, observerProc, 0); + if (runLoopObserver == 0) error (SWT.ERROR_NO_HANDLES); + OS.CFRunLoopAddObserver (OS.CFRunLoopGetCurrent (), runLoopObserver, OS.kCFRunLoopCommonModes ()); + + cursorSetCallback = new Callback(this, "cursorSetProc", 2); + int /*long*/ cursorSetProc = cursorSetCallback.getAddress(); + if (cursorSetProc == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + int /*long*/ method = OS.class_getInstanceMethod(OS.class_NSCursor, OS.sel_set); + if (method != 0) oldCursorSetProc = OS.method_setImplementation(method, cursorSetProc); + + timerDelegate = (SWTWindowDelegate)new SWTWindowDelegate().alloc().init(); + + settingsDelegate = (SWTWindowDelegate)new SWTWindowDelegate().alloc().init(); + NSNotificationCenter defaultCenter = NSNotificationCenter.defaultCenter(); + defaultCenter.addObserver(settingsDelegate, OS.sel_systemSettingsChanged_, OS.NSSystemColorsDidChangeNotification, null); + defaultCenter.addObserver(settingsDelegate, OS.sel_systemSettingsChanged_, OS.NSApplicationDidChangeScreenParametersNotification, null); + + NSTextView textView = (NSTextView)new NSTextView().alloc(); + textView.init (); + markedAttributes = textView.markedTextAttributes (); + markedAttributes.retain (); + textView.release (); + + isPainting = (NSMutableArray)new NSMutableArray().alloc(); + isPainting = isPainting.initWithCapacity(12); +} + +void addEventMethods (int /*long*/ cls, int /*long*/ proc2, int /*long*/ proc3, int /*long*/ drawRectProc, int /*long*/ hitTestProc, int /*long*/ needsDisplayInRectProc) { + if (proc3 != 0) { + OS.class_addMethod(cls, OS.sel_mouseDown_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_mouseUp_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_scrollWheel_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_rightMouseDown_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_rightMouseUp_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_rightMouseDragged_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_otherMouseDown_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_otherMouseUp_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_otherMouseDragged_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_mouseDragged_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_mouseMoved_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_mouseEntered_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_mouseExited_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_menuForEvent_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_keyDown_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_keyUp_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_flagsChanged_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_cursorUpdate_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_setNeedsDisplay_, proc3, "@:B"); + OS.class_addMethod(cls, OS.sel_shouldDelayWindowOrderingForEvent_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_acceptsFirstMouse_, proc3, "@:@"); + } + if (proc2 != 0) { + OS.class_addMethod(cls, OS.sel_resignFirstResponder, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_becomeFirstResponder, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_resetCursorRects, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_updateTrackingAreas, proc2, "@:"); + } + if (needsDisplayInRectProc != 0) { + OS.class_addMethod(cls, OS.sel_setNeedsDisplayInRect_, needsDisplayInRectProc, "@:{NSRect}"); + } + if (drawRectProc != 0) { + OS.class_addMethod(cls, OS.sel_drawRect_, drawRectProc, "@:{NSRect}"); + } + if (hitTestProc != 0) { + OS.class_addMethod(cls, OS.sel_hitTest_, hitTestProc, "@:{NSPoint}"); + } +} + +void addFrameMethods(int /*long*/ cls, int /*long*/ setFrameOriginProc, int /*long*/ setFrameSizeProc) { + OS.class_addMethod(cls, OS.sel_setFrameOrigin_, setFrameOriginProc, "@:{NSPoint}"); + OS.class_addMethod(cls, OS.sel_setFrameSize_, setFrameSizeProc, "@:{NSSize}"); +} + +void addAccessibilityMethods(int /*long*/ cls, int /*long*/ proc2, int /*long*/ proc3, int /*long*/ proc4, int /*long*/ accessibilityHitTestProc) { + OS.class_addMethod(cls, OS.sel_accessibilityActionNames, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_accessibilityAttributeNames, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_accessibilityParameterizedAttributeNames, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_accessibilityFocusedUIElement, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_accessibilityIsIgnored, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_accessibilityAttributeValue_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_accessibilityHitTest_, accessibilityHitTestProc, "@:{NSPoint}"); + OS.class_addMethod(cls, OS.sel_accessibilityAttributeValue_forParameter_, proc4, "@:@@"); + OS.class_addMethod(cls, OS.sel_accessibilityPerformAction_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_accessibilityActionDescription_, proc3, "@:@"); +} + +int /*long*/ registerCellSubclass(int /*long*/ cellClass, int size, int align, byte[] types) { + String cellClassName = OS.class_getName(cellClass); + int /*long*/ cls = OS.objc_allocateClassPair(cellClass, "SWTAccessible" + cellClassName, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.objc_registerClassPair(cls); + return cls; +} + +void initClasses () { + if (OS.objc_lookUpClass ("SWTView") != 0) return; + + Class clazz = getClass (); + dialogCallback3 = new Callback(clazz, "dialogProc", 3); + int /*long*/ dialogProc3 = dialogCallback3.getAddress(); + if (dialogProc3 == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + dialogCallback4 = new Callback(clazz, "dialogProc", 4); + int /*long*/ dialogProc4 = dialogCallback4.getAddress(); + if (dialogProc4 == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + dialogCallback5 = new Callback(clazz, "dialogProc", 5); + int /*long*/ dialogProc5 = dialogCallback5.getAddress(); + if (dialogProc5 == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + windowCallback3 = new Callback(clazz, "windowProc", 3); + int /*long*/ proc3 = windowCallback3.getAddress(); + if (proc3 == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + windowCallback2 = new Callback(clazz, "windowProc", 2); + int /*long*/ proc2 = windowCallback2.getAddress(); + if (proc2 == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + windowCallback4 = new Callback(clazz, "windowProc", 4); + int /*long*/ proc4 = windowCallback4.getAddress(); + if (proc4 == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + windowCallback5 = new Callback(clazz, "windowProc", 5); + int /*long*/ proc5 = windowCallback5.getAddress(); + if (proc5 == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + windowCallback6 = new Callback(clazz, "windowProc", 6); + int /*long*/ proc6 = windowCallback6.getAddress(); + if (proc6 == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + fieldEditorCallback3 = new Callback(clazz, "fieldEditorProc", 3); + int /*long*/ fieldEditorProc3 = fieldEditorCallback3.getAddress(); + if (fieldEditorProc3 == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + fieldEditorCallback4 = new Callback(clazz, "fieldEditorProc", 4); + int /*long*/ fieldEditorProc4 = fieldEditorCallback4.getAddress(); + if (fieldEditorProc4 == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + + int /*long*/ isFlippedProc = OS.isFlipped_CALLBACK(); + int /*long*/ drawRectProc = OS.CALLBACK_drawRect_(proc3); + int /*long*/ drawInteriorWithFrameInViewProc = OS.CALLBACK_drawInteriorWithFrame_inView_ (proc4); + int /*long*/ drawWithExpansionFrameProc = OS.CALLBACK_drawWithExpansionFrame_inView_ (proc4); + int /*long*/ imageRectForBoundsProc = OS.CALLBACK_imageRectForBounds_ (proc3); + int /*long*/ titleRectForBoundsProc = OS.CALLBACK_titleRectForBounds_ (proc3); + int /*long*/ hitTestForEvent_inRect_ofViewProc = OS.CALLBACK_hitTestForEvent_inRect_ofView_ (proc5); + int /*long*/ cellSizeProc = OS.CALLBACK_cellSize (proc2); + int /*long*/ drawImageWithFrameInViewProc = OS.CALLBACK_drawImage_withFrame_inView_ (proc5); + int /*long*/ setFrameOriginProc = OS.CALLBACK_setFrameOrigin_(proc3); + int /*long*/ setFrameSizeProc = OS.CALLBACK_setFrameSize_(proc3); + int /*long*/ hitTestProc = OS.CALLBACK_hitTest_(proc3); + int /*long*/ markedRangeProc = OS.CALLBACK_markedRange (proc2); + int /*long*/ selectedRangeProc = OS.CALLBACK_selectedRange (proc2); + int /*long*/ highlightSelectionInClipRectProc = OS.CALLBACK_highlightSelectionInClipRect_ (proc3); + int /*long*/ setMarkedText_selectedRangeProc = OS.CALLBACK_setMarkedText_selectedRange_(proc4); + int /*long*/ attributedSubstringFromRangeProc = OS.CALLBACK_attributedSubstringFromRange_(proc3); + int /*long*/ characterIndexForPointProc = OS.CALLBACK_characterIndexForPoint_(proc3); + int /*long*/ firstRectForCharacterRangeProc = OS.CALLBACK_firstRectForCharacterRange_(proc3); + int /*long*/ textWillChangeSelectionProc = OS.CALLBACK_textView_willChangeSelectionFromCharacterRange_toCharacterRange_(proc5); + int /*long*/ accessibilityHitTestProc = OS.CALLBACK_accessibilityHitTest_(proc3); + int /*long*/ shouldChangeTextInRange_replacementString_Proc = OS.CALLBACK_shouldChangeTextInRange_replacementString_(fieldEditorProc4); + int /*long*/ shouldChangeTextInRange_replacementString_fieldEditorProc = shouldChangeTextInRange_replacementString_Proc; + int /*long*/ view_stringForToolTip_point_userDataProc = OS.CALLBACK_view_stringForToolTip_point_userData_(proc6); + int /*long*/ canDragRowsWithIndexes_atPoint_Proc = OS.CALLBACK_canDragRowsWithIndexes_atPoint_(proc4); + int /*long*/ setNeedsDisplayInRectProc = OS.CALLBACK_setNeedsDisplayInRect_(proc3); + int /*long*/ expansionFrameWithFrameProc = OS.CALLBACK_expansionFrameWithFrame_inView_ (proc4); + + byte[] types = {'*','\0'}; + int size = C.PTR_SIZEOF, align = C.PTR_SIZEOF == 4 ? 2 : 3; + + String className; + int /*long*/ cls; + + className = "SWTBox"; + cls = OS.objc_allocateClassPair(OS.class_NSBox, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + className = "SWTButton"; + cls = OS.objc_allocateClassPair(OS.class_NSButton, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_sendSelection, proc2, "@:"); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + OS.objc_registerClassPair(cls); + + cls = registerCellSubclass(NSButton.cellClass(), size, align, types); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.class_addMethod(cls, OS.sel_nextState, proc2, "@:"); + NSButton.setCellClass(cls); + + className = "SWTButtonCell"; + cls = OS.objc_allocateClassPair (OS.class_NSButtonCell, className, 0); + OS.class_addIvar (cls, SWT_OBJECT, size, (byte)align, types); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.class_addMethod (cls, OS.sel_drawImage_withFrame_inView_, drawImageWithFrameInViewProc, "@:@{NSFrame}@"); + OS.class_addMethod(cls, OS.sel_cellSize, cellSizeProc, "@:"); + OS.class_addMethod(cls, OS.sel_drawInteriorWithFrame_inView_, drawInteriorWithFrameInViewProc, "@:{NSRect}@"); + OS.class_addMethod(cls, OS.sel_titleRectForBounds_, titleRectForBoundsProc, "@:{NSRect}"); + OS.objc_registerClassPair (cls); + + className = "SWTCanvasView"; + cls = OS.objc_allocateClassPair(OS.class_NSView, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + //NSTextInput protocol + OS.class_addProtocol(cls, OS.objc_getProtocol("NSTextInput")); + OS.class_addMethod(cls, OS.sel_hasMarkedText, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_markedRange, markedRangeProc, "@:"); + OS.class_addMethod(cls, OS.sel_selectedRange, selectedRangeProc, "@:"); + OS.class_addMethod(cls, OS.sel_setMarkedText_selectedRange_, setMarkedText_selectedRangeProc, "@:@{NSRange}"); + OS.class_addMethod(cls, OS.sel_unmarkText, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_validAttributesForMarkedText, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_attributedSubstringFromRange_, attributedSubstringFromRangeProc, "@:{NSRange}"); + OS.class_addMethod(cls, OS.sel_insertText_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_characterIndexForPoint_, characterIndexForPointProc, "@:{NSPoint}"); + OS.class_addMethod(cls, OS.sel_firstRectForCharacterRange_, firstRectForCharacterRangeProc, "@:{NSRange}"); + OS.class_addMethod(cls, OS.sel_doCommandBySelector_, proc3, "@::"); + //NSTextInput protocol end + OS.class_addMethod(cls, OS.sel_canBecomeKeyView, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_isFlipped, isFlippedProc, "@:"); + OS.class_addMethod(cls, OS.sel_acceptsFirstResponder, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_isOpaque, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_updateOpenGLContext_, proc3, "@:@"); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + className = "SWTComboBox"; + cls = OS.objc_allocateClassPair(OS.class_NSComboBox, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_sendSelection, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_textDidChange_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_textViewDidChangeSelection_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_textView_willChangeSelectionFromCharacterRange_toCharacterRange_, textWillChangeSelectionProc, "@:@{NSRange}{NSRange}"); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + cls = registerCellSubclass(NSComboBox.cellClass(), size, align, types); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + NSComboBox.setCellClass(cls); + + className = "SWTDatePicker"; + cls = OS.objc_allocateClassPair(OS.class_NSDatePicker, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_isFlipped, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_sendSelection, proc2, "@:"); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + className = "SWTEditorView"; + cls = OS.objc_allocateClassPair(OS.class_NSTextView, className, 0); + //TODO hitTestProc and drawRectProc should be set Control.setRegion()? + addEventMethods(cls, 0, fieldEditorProc3, 0, 0, 0); + OS.class_addMethod(cls, OS.sel_insertText_, fieldEditorProc3, "@:@"); + OS.class_addMethod(cls, OS.sel_doCommandBySelector_, fieldEditorProc3, "@::"); + OS.class_addMethod(cls, OS.sel_shouldChangeTextInRange_replacementString_, shouldChangeTextInRange_replacementString_fieldEditorProc, "@:{NSRange}@"); + OS.objc_registerClassPair(cls); + + className = "SWTImageView"; + cls = OS.objc_allocateClassPair(OS.class_NSImageView, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_isFlipped, isFlippedProc, "@:"); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + cls = registerCellSubclass(NSImageView.cellClass(), size, align, types); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + NSImageView.setCellClass(cls); + + className = "SWTImageTextCell"; + cls = OS.objc_allocateClassPair (OS.class_NSTextFieldCell, className, 0); + OS.class_addIvar (cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addIvar (cls, SWT_IMAGE, size, (byte)align, types); + OS.class_addIvar (cls, SWT_ROW, size, (byte)align, types); + OS.class_addIvar (cls, SWT_COLUMN, size, (byte)align, types); + OS.class_addMethod (cls, OS.sel_drawInteriorWithFrame_inView_, drawInteriorWithFrameInViewProc, "@:{NSRect}@"); + OS.class_addMethod (cls, OS.sel_drawWithExpansionFrame_inView_, drawWithExpansionFrameProc, "@:{NSRect}@"); + OS.class_addMethod (cls, OS.sel_imageRectForBounds_, imageRectForBoundsProc, "@:{NSRect}"); + OS.class_addMethod (cls, OS.sel_titleRectForBounds_, titleRectForBoundsProc, "@:{NSRect}"); + OS.class_addMethod (cls, OS.sel_hitTestForEvent_inRect_ofView_, hitTestForEvent_inRect_ofViewProc, "@:@{NSRect}@"); + OS.class_addMethod (cls, OS.sel_cellSize, cellSizeProc, "@:"); + OS.class_addMethod (cls, OS.sel_image, proc2, "@:"); + OS.class_addMethod (cls, OS.sel_setImage_, proc3, "@:@"); + OS.class_addMethod (cls, OS.sel_expansionFrameWithFrame_inView_, expansionFrameWithFrameProc, "@:{NSRect}@"); + OS.objc_registerClassPair (cls); + + className = "SWTMenu"; + cls = OS.objc_allocateClassPair(OS.class_NSMenu, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_menuWillOpen_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_menuDidClose_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_menu_willHighlightItem_, proc4, "@:@@"); + OS.class_addMethod(cls, OS.sel_menuNeedsUpdate_, proc3, "@:@"); + OS.objc_registerClassPair(cls); + + className = "SWTMenuItem"; + cls = OS.objc_allocateClassPair(OS.class_NSMenuItem, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_sendSelection, proc2, "@:"); + OS.objc_registerClassPair(cls); + + className = "SWTOutlineView"; + cls = OS.objc_allocateClassPair(OS.class_NSOutlineView, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_highlightSelectionInClipRect_, highlightSelectionInClipRectProc, "@:{NSRect}"); + OS.class_addMethod(cls, OS.sel_sendDoubleSelection, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_outlineViewSelectionDidChange_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_outlineView_child_ofItem_, proc5, "@:@i@"); + OS.class_addMethod(cls, OS.sel_outlineView_isItemExpandable_, proc4, "@:@@"); + OS.class_addMethod(cls, OS.sel_outlineView_numberOfChildrenOfItem_, proc4, "@:@@"); + OS.class_addMethod(cls, OS.sel_outlineView_objectValueForTableColumn_byItem_, proc5, "@:@@@"); + OS.class_addMethod(cls, OS.sel_outlineView_willDisplayCell_forTableColumn_item_, proc6, "@:@@@@"); + OS.class_addMethod(cls, OS.sel_outlineView_setObjectValue_forTableColumn_byItem_, proc6, "@:@@@@"); + OS.class_addMethod(cls, OS.sel_outlineViewColumnDidMove_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_outlineViewColumnDidResize_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_outlineView_didClickTableColumn_, proc4, "@:@@"); + OS.class_addMethod(cls, OS.sel_canDragRowsWithIndexes_atPoint_, canDragRowsWithIndexes_atPoint_Proc, "@:@{NSPoint=ff}"); + OS.class_addMethod(cls, OS.sel_outlineView_writeItems_toPasteboard_, proc5, "@:@@@"); + OS.class_addMethod(cls, OS.sel_expandItem_expandChildren_, proc4, "@:@Z"); + OS.class_addMethod(cls, OS.sel_collapseItem_collapseChildren_, proc4, "@:@Z"); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + className = "SWTPanelDelegate"; + cls = OS.objc_allocateClassPair(OS.class_NSObject, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_windowWillClose_, dialogProc3, "@:@"); + OS.class_addMethod(cls, OS.sel_changeColor_, dialogProc3, "@:@"); + OS.class_addMethod(cls, OS.sel_changeFont_, dialogProc3, "@:@"); + OS.class_addMethod(cls, OS.sel_sendSelection_, dialogProc3, "@:@"); + OS.class_addMethod(cls, OS.sel_panel_shouldShowFilename_, dialogProc4, "@:@@"); + OS.class_addMethod(cls, OS.sel_panelDidEnd_returnCode_contextInfo_, dialogProc5, "@:@i@"); + OS.objc_registerClassPair(cls); + + className = "SWTPopUpButton"; + cls = OS.objc_allocateClassPair(OS.class_NSPopUpButton, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_sendSelection, proc2, "@:"); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + cls = registerCellSubclass(NSPopUpButton.cellClass(), size, align, types); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + NSPopUpButton.setCellClass(cls); + + className = "SWTProgressIndicator"; + cls = OS.objc_allocateClassPair(OS.class_NSProgressIndicator, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_viewDidMoveToWindow, proc2, "@:"); + OS.class_addMethod(cls, OS.sel__drawThemeProgressArea_, proc3, "@:c"); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + className = "SWTScroller"; + cls = OS.objc_allocateClassPair(OS.class_NSScroller, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_sendSelection, proc2, "@:"); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + className = "SWTScrollView"; + cls = OS.objc_allocateClassPair(OS.class_NSScrollView, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_sendVerticalSelection, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_sendHorizontalSelection, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_pageDown_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_pageUp_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_reflectScrolledClipView_, proc3, "@:@"); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + className = "SWTSearchField"; + cls = OS.objc_allocateClassPair(OS.class_NSSearchField, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.class_addMethod(cls, OS.sel_textDidChange_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_textViewDidChangeSelection_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_textView_willChangeSelectionFromCharacterRange_toCharacterRange_, textWillChangeSelectionProc, "@:@{NSRange}{NSRange}"); + OS.class_addMethod(cls, OS.sel_sendSearchSelection, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_sendCancelSelection, proc2, "@:"); + OS.objc_registerClassPair(cls); + + cls = registerCellSubclass(NSSearchField.cellClass(), size, align, types); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + NSSearchField.setCellClass(cls); + + // Don't subclass NSSecureTextFieldCell -- you'll get an NSException from [NSSecureTextField setCellClass:]! + className = "SWTSecureTextField"; + cls = OS.objc_allocateClassPair(OS.class_NSSecureTextField, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.class_addMethod(cls, OS.sel_textDidChange_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_textViewDidChangeSelection_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_textView_willChangeSelectionFromCharacterRange_toCharacterRange_, textWillChangeSelectionProc, "@:@{NSRange}{NSRange}"); + OS.objc_registerClassPair(cls); + + className = "SWTSlider"; + cls = OS.objc_allocateClassPair(OS.class_NSSlider, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_sendSelection, proc2, "@:"); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + cls = registerCellSubclass(NSSlider.cellClass(), size, align, types); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + NSSlider.setCellClass(cls); + + className = "SWTStepper"; + cls = OS.objc_allocateClassPair(OS.class_NSStepper, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_sendSelection, proc2, "@:"); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + cls = registerCellSubclass(NSStepper.cellClass(), size, align, types); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + NSStepper.setCellClass(cls); + + className = "SWTTableHeaderCell"; + cls = OS.objc_allocateClassPair (OS.class_NSTableHeaderCell, className, 0); + OS.class_addIvar (cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod (cls, OS.sel_drawInteriorWithFrame_inView_, drawInteriorWithFrameInViewProc, "@:{NSRect}@"); + OS.objc_registerClassPair (cls); + + className = "SWTTableHeaderView"; + cls = OS.objc_allocateClassPair(OS.class_NSTableHeaderView, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_mouseDown_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_resetCursorRects, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_updateTrackingAreas, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_menuForEvent_, proc3, "@:@"); + //TODO hitTestProc and drawRectProc should be set Control.setRegion()? + OS.objc_registerClassPair(cls); + + className = "SWTTableView"; + cls = OS.objc_allocateClassPair(OS.class_NSTableView, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_highlightSelectionInClipRect_, highlightSelectionInClipRectProc, "@:{NSRect}"); + OS.class_addMethod(cls, OS.sel_sendDoubleSelection, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_numberOfRowsInTableView_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_tableView_objectValueForTableColumn_row_, proc5, "@:@:@:@"); + OS.class_addMethod(cls, OS.sel_tableView_shouldEditTableColumn_row_, proc5, "@:@:@:@"); + OS.class_addMethod(cls, OS.sel_tableViewSelectionDidChange_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_tableView_willDisplayCell_forTableColumn_row_, proc6, "@:@@@i"); + OS.class_addMethod(cls, OS.sel_tableView_setObjectValue_forTableColumn_row_, proc6, "@:@@@i"); + OS.class_addMethod(cls, OS.sel_tableViewColumnDidMove_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_tableViewColumnDidResize_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_tableView_didClickTableColumn_, proc4, "@:@"); + OS.class_addMethod(cls, OS.sel_canDragRowsWithIndexes_atPoint_, canDragRowsWithIndexes_atPoint_Proc, "@:@{NSPoint=ff}"); + OS.class_addMethod(cls, OS.sel_tableView_writeRowsWithIndexes_toPasteboard_, proc5, "@:@@@"); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + className = "SWTTabView"; + cls = OS.objc_allocateClassPair(OS.class_NSTabView, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_tabView_willSelectTabViewItem_, proc4, "@:@@"); + OS.class_addMethod(cls, OS.sel_tabView_didSelectTabViewItem_, proc4, "@:@@"); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + className = "SWTTextView"; + cls = OS.objc_allocateClassPair(OS.class_NSTextView, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.class_addMethod(cls, OS.sel_insertText_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_doCommandBySelector_, proc3, "@::"); + OS.class_addMethod(cls, OS.sel_textDidChange_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_textView_clickedOnLink_atIndex_, proc5, "@:@@@"); + OS.class_addMethod(cls, OS.sel_dragSelectionWithEvent_offset_slideBack_, proc5, "@:@@@"); + OS.class_addMethod(cls, OS.sel_shouldChangeTextInRange_replacementString_, shouldChangeTextInRange_replacementString_Proc, "@:{NSRange}@"); + OS.objc_registerClassPair(cls); + + className = "SWTTextField"; + cls = OS.objc_allocateClassPair(OS.class_NSTextField, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.class_addMethod(cls, OS.sel_acceptsFirstResponder, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_textDidChange_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_textDidEndEditing_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_textViewDidChangeSelection_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_textView_willChangeSelectionFromCharacterRange_toCharacterRange_, textWillChangeSelectionProc, "@:@{NSRange}{NSRange}"); + OS.objc_registerClassPair(cls); + + cls = registerCellSubclass(NSTextField.cellClass(), size, align, types); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + NSTextField.setCellClass(cls); + + className = "SWTTreeItem"; + cls = OS.objc_allocateClassPair(OS.class_NSObject, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.objc_registerClassPair(cls); + + className = "SWTView"; + cls = OS.objc_allocateClassPair(OS.class_NSView, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_canBecomeKeyView, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_isFlipped, isFlippedProc, "@:"); + OS.class_addMethod(cls, OS.sel_acceptsFirstResponder, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_isOpaque, proc2, "@:"); + addEventMethods(cls, proc2, proc3, drawRectProc, hitTestProc, setNeedsDisplayInRectProc); + addFrameMethods(cls, setFrameOriginProc, setFrameSizeProc); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + className = "SWTWindow"; + cls = OS.objc_allocateClassPair(OS.class_NSWindow, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_sendEvent_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_helpRequested_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_canBecomeKeyWindow, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_becomeKeyWindow, proc2, "@:"); + OS.class_addMethod(cls, OS.sel_makeFirstResponder_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_noResponderFor_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_view_stringForToolTip_point_userData_, view_stringForToolTip_point_userDataProc, "@:@i{NSPoint}@"); + addAccessibilityMethods(cls, proc2, proc3, proc4, accessibilityHitTestProc); + OS.objc_registerClassPair(cls); + + className = "SWTWindowDelegate"; + cls = OS.objc_allocateClassPair(OS.class_NSObject, className, 0); + OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); + OS.class_addMethod(cls, OS.sel_windowDidResize_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_windowDidMove_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_windowShouldClose_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_windowWillClose_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_windowDidResignKey_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_windowDidBecomeKey_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_timerProc_, proc3, "@:@"); + OS.class_addMethod(cls, OS.sel_systemSettingsChanged_, proc3, "@:@"); + OS.objc_registerClassPair(cls); +} + +NSFont getFont (int /*long*/ cls, int /*long*/ sel) { + int /*long*/ widget = OS.objc_msgSend (OS.objc_msgSend (cls, OS.sel_alloc), OS.sel_initWithFrame_, new NSRect()); + int /*long*/ font = 0; + if (OS.objc_msgSend_bool (widget, OS.sel_respondsToSelector_, sel)) { + font = OS.objc_msgSend (widget, sel); + } + NSFont result = null; + if (font != 0) { + result = new NSFont (font); + } else { + result = NSFont.systemFontOfSize (NSFont.systemFontSizeForControlSize (OS.NSRegularControlSize)); + } + result.retain (); + OS.objc_msgSend (widget, OS.sel_release); + return result; +} + +void initColors () { + colors = new float /*double*/ [SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT + 1][]; + colors[SWT.COLOR_INFO_FOREGROUND] = getWidgetColorRGB(SWT.COLOR_INFO_FOREGROUND); + colors[SWT.COLOR_INFO_BACKGROUND] = getWidgetColorRGB(SWT.COLOR_INFO_BACKGROUND); + colors[SWT.COLOR_TITLE_FOREGROUND] = getWidgetColorRGB(SWT.COLOR_TITLE_FOREGROUND); + colors[SWT.COLOR_TITLE_BACKGROUND] = getWidgetColorRGB(SWT.COLOR_TITLE_BACKGROUND); + colors[SWT.COLOR_TITLE_BACKGROUND_GRADIENT] = getWidgetColorRGB(SWT.COLOR_TITLE_BACKGROUND_GRADIENT); + colors[SWT.COLOR_TITLE_INACTIVE_FOREGROUND] = getWidgetColorRGB(SWT.COLOR_TITLE_INACTIVE_FOREGROUND); + colors[SWT.COLOR_TITLE_INACTIVE_BACKGROUND] = getWidgetColorRGB(SWT.COLOR_TITLE_INACTIVE_BACKGROUND); + colors[SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT] = getWidgetColorRGB(SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT); + colors[SWT.COLOR_WIDGET_DARK_SHADOW] = getWidgetColorRGB(SWT.COLOR_WIDGET_DARK_SHADOW); + colors[SWT.COLOR_WIDGET_NORMAL_SHADOW] = getWidgetColorRGB(SWT.COLOR_WIDGET_NORMAL_SHADOW); + colors[SWT.COLOR_WIDGET_LIGHT_SHADOW] = getWidgetColorRGB(SWT.COLOR_WIDGET_LIGHT_SHADOW); + colors[SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW] = getWidgetColorRGB(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW); + colors[SWT.COLOR_WIDGET_BACKGROUND] = getWidgetColorRGB(SWT.COLOR_WIDGET_BACKGROUND); + colors[SWT.COLOR_WIDGET_FOREGROUND] = getWidgetColorRGB(SWT.COLOR_WIDGET_FOREGROUND); + colors[SWT.COLOR_WIDGET_BORDER] = getWidgetColorRGB(SWT.COLOR_WIDGET_BORDER); + colors[SWT.COLOR_LIST_FOREGROUND] = getWidgetColorRGB(SWT.COLOR_LIST_FOREGROUND); + colors[SWT.COLOR_LIST_BACKGROUND] = getWidgetColorRGB(SWT.COLOR_LIST_BACKGROUND); + colors[SWT.COLOR_LIST_SELECTION_TEXT] = getWidgetColorRGB(SWT.COLOR_LIST_SELECTION_TEXT); + colors[SWT.COLOR_LIST_SELECTION] = getWidgetColorRGB(SWT.COLOR_LIST_SELECTION); + + alternateSelectedControlColor = getWidgetColorRGB(NSColor.alternateSelectedControlColor()); + alternateSelectedControlTextColor = getWidgetColorRGB(NSColor.alternateSelectedControlTextColor()); + secondarySelectedControlColor = getWidgetColorRGB(NSColor.secondarySelectedControlColor()); + selectedControlTextColor = getWidgetColorRGB(NSColor.selectedControlTextColor()); +} + +void initFonts () { + smallFonts = System.getProperty("org.eclipse.swt.internal.carbon.smallFonts") != null; + buttonFont = getFont (OS.class_NSButton, OS.sel_font); + popUpButtonFont = getFont (OS.class_NSPopUpButton, OS.sel_font); + textFieldFont = getFont (OS.class_NSTextField, OS.sel_font); + secureTextFieldFont = getFont (OS.class_NSSecureTextField, OS.sel_font); + searchFieldFont = getFont (OS.class_NSSearchField, OS.sel_font); + comboBoxFont = getFont (OS.class_NSComboBox, OS.sel_font); + sliderFont = getFont (OS.class_NSSlider, OS.sel_font); + scrollerFont = getFont (OS.class_NSScroller, OS.sel_font); + textViewFont = getFont (OS.class_NSTextView, OS.sel_font); + tableViewFont = getFont (OS.class_NSTableView, OS.sel_font); + outlineViewFont = getFont (OS.class_NSOutlineView, OS.sel_font); + datePickerFont = getFont (OS.class_NSDatePicker, OS.sel_font); + boxFont = getFont (OS.class_NSBox, OS.sel_titleFont); + tabViewFont = getFont (OS.class_NSTabView, OS.sel_font); + progressIndicatorFont = getFont (OS.class_NSProgressIndicator, OS.sel_font); +} + +/** + * Invokes platform specific functionality to allocate a new GC handle. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>Display</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @param data the platform specific GC data + * @return the platform specific GC handle + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_NO_HANDLES if a handle could not be obtained for gc creation</li> + * </ul> + */ +public int /*long*/ internal_new_GC (GCData data) { + if (isDisposed()) SWT.error(SWT.ERROR_DEVICE_DISPOSED); + if (screenWindow == null) { + NSWindow window = (NSWindow) new NSWindow ().alloc (); + NSRect rect = new NSRect(); + window = window.initWithContentRect(rect, OS.NSBorderlessWindowMask, OS.NSBackingStoreBuffered, false); + window.setReleasedWhenClosed(false); + screenWindow = window; + } + NSGraphicsContext context = screenWindow.graphicsContext(); +// NSAffineTransform transform = NSAffineTransform.transform(); +// NSSize size = handle.size(); +// transform.translateXBy(0, size.height); +// transform.scaleXBy(1, -1); +// transform.set(); + if (data != null) { + int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT; + if ((data.style & mask) == 0) { + data.style |= SWT.LEFT_TO_RIGHT; + } + data.device = this; + data.background = getSystemColor(SWT.COLOR_WHITE).handle; + data.foreground = getSystemColor(SWT.COLOR_BLACK).handle; + data.font = getSystemFont(); + } + return context.id; +} + +/** + * Invokes platform specific functionality to dispose a GC handle. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>Display</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @param hDC the platform specific GC handle + * @param data the platform specific GC data + */ +public void internal_dispose_GC (int /*long*/ context, GCData data) { + if (isDisposed()) SWT.error(SWT.ERROR_DEVICE_DISPOSED); + +} + +static boolean isValidClass (Class clazz) { + String name = clazz.getName (); + int index = name.lastIndexOf ('.'); + return name.substring (0, index + 1).equals (PACKAGE_PREFIX); +} + +boolean isValidThread () { + return thread == Thread.currentThread (); +} + +/** + * Generate a low level system event. + * + * <code>post</code> is used to generate low level keyboard + * and mouse events. The intent is to enable automated UI + * testing by simulating the input from the user. Most + * SWT applications should never need to call this method. + * <p> + * Note that this operation can fail when the operating system + * fails to generate the event for any reason. For example, + * this can happen when there is no such key or mouse button + * or when the system event queue is full. + * </p> + * <p> + * <b>Event Types:</b> + * <p>KeyDown, KeyUp + * <p>The following fields in the <code>Event</code> apply: + * <ul> + * <li>(in) type KeyDown or KeyUp</li> + * <p> Either one of: + * <li>(in) character a character that corresponds to a keyboard key</li> + * <li>(in) keyCode the key code of the key that was typed, + * as defined by the key code constants in class <code>SWT</code></li> + * </ul> + * <p>MouseDown, MouseUp</p> + * <p>The following fields in the <code>Event</code> apply: + * <ul> + * <li>(in) type MouseDown or MouseUp + * <li>(in) button the button that is pressed or released + * </ul> + * <p>MouseMove</p> + * <p>The following fields in the <code>Event</code> apply: + * <ul> + * <li>(in) type MouseMove + * <li>(in) x the x coordinate to move the mouse pointer to in screen coordinates + * <li>(in) y the y coordinate to move the mouse pointer to in screen coordinates + * </ul> + * <p>MouseWheel</p> + * <p>The following fields in the <code>Event</code> apply: + * <ul> + * <li>(in) type MouseWheel + * <li>(in) detail either SWT.SCROLL_LINE or SWT.SCROLL_PAGE + * <li>(in) count the number of lines or pages to scroll + * </ul> + * </dl> + * + * @param event the event to be generated + * + * @return true if the event was generated or false otherwise + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the event is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 3.0 + * + */ +public boolean post(Event event) { + synchronized (Device.class) { + if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED); + if (event == null) error (SWT.ERROR_NULL_ARGUMENT); + + // TODO: Not sure if these calls have any effect on event posting. + if (!eventSourceDelaySet) { + OS.CGSetLocalEventsSuppressionInterval(0.0); + OS.CGEnableEventStateCombining(1); + OS.CGSetLocalEventsFilterDuringSuppressionState(OS.kCGEventFilterMaskPermitLocalKeyboardEvents | OS.kCGEventFilterMaskPermitLocalMouseEvents | OS.kCGEventFilterMaskPermitSystemDefinedEvents, OS.kCGEventSuppressionStateSuppressionInterval); + OS.CGSetLocalEventsFilterDuringSuppressionState(OS.kCGEventFilterMaskPermitLocalKeyboardEvents | OS.kCGEventFilterMaskPermitLocalMouseEvents | OS.kCGEventFilterMaskPermitSystemDefinedEvents, OS.kCGEventSuppressionStateRemoteMouseDrag); + eventSourceDelaySet = true; + } + + int type = event.type; + switch (type) { + case SWT.KeyDown: + case SWT.KeyUp: { + short vKey = (short)Display.untranslateKey (event.keyCode); + if (vKey == 0) { + int /*long*/ uchrPtr = 0; + int /*long*/ currentKbd = OS.TISCopyCurrentKeyboardInputSource(); + int /*long*/ uchrCFData = OS.TISGetInputSourceProperty(currentKbd, OS.kTISPropertyUnicodeKeyLayoutData()); + + if (uchrCFData == 0) return false; + uchrPtr = OS.CFDataGetBytePtr(uchrCFData); + if (uchrPtr == 0) return false; + if (OS.CFDataGetLength(uchrCFData) == 0) return false; + int maxStringLength = 256; + vKey = -1; + char [] output = new char [maxStringLength]; + int [] actualStringLength = new int [1]; + for (short i = 0 ; i <= 0x7F ; i++) { + OS.UCKeyTranslate (uchrPtr, i, (short)(type == SWT.KeyDown ? OS.kUCKeyActionDown : OS.kUCKeyActionUp), 0, OS.LMGetKbdType(), 0, deadKeyState, maxStringLength, actualStringLength, output); + if (output[0] == event.character) { + vKey = i; + break; + } + } + if (vKey == -1) { + for (short i = 0 ; i <= 0x7F ; i++) { + OS.UCKeyTranslate (uchrPtr, i, (short)(type == SWT.KeyDown ? OS.kUCKeyActionDown : OS.kUCKeyActionUp), OS.shiftKey, OS.LMGetKbdType(), 0, deadKeyState, maxStringLength, actualStringLength, output); + if (output[0] == event.character) { + vKey = i; + break; + } + } + } + } + + /** + * Bug(?) in UCKeyTranslate: If event.keyCode doesn't map to a valid SWT constant and event.characer is 0 we still need to post an event. + * In Carbon, KeyTranslate eventually found a key that generated 0 but UCKeyTranslate never generates 0. + * When that happens, post an event from key 127, which does nothing. + */ + if (vKey == -1 && event.character == 0) { + vKey = 127; + } + + if (vKey == -1) return false; + + return OS.CGPostKeyboardEvent((short)0, vKey, type == SWT.KeyDown) == 0; + } + case SWT.MouseDown: + case SWT.MouseMove: + case SWT.MouseUp: { + CGPoint mouseCursorPosition = new CGPoint (); + int chord = OS.GetCurrentButtonState (); + + if (type == SWT.MouseMove) { + mouseCursorPosition.x = event.x; + mouseCursorPosition.y = event.y; + return OS.CGPostMouseEvent (mouseCursorPosition, true, 5, (chord & 0x1) != 0, (chord & 0x2) != 0, (chord & 0x4) != 0, (chord & 0x8) != 0, (chord & 0x10) != 0) == 0; + } else { + int button = event.button; + if (button < 1 || button > 5) return false; + boolean button1 = false, button2 = false, button3 = false, button4 = false, button5 = false; + switch (button) { + case 1: { + button1 = type == SWT.MouseDown; + button2 = (chord & 0x4) != 0; + button3 = (chord & 0x2) != 0; + button4 = (chord & 0x8) != 0; + button5 = (chord & 0x10) != 0; + break; + } + case 2: { + button1 = (chord & 0x1) != 0; + button2 = type == SWT.MouseDown; + button3 = (chord & 0x2) != 0; + button4 = (chord & 0x8) != 0; + button5 = (chord & 0x10) != 0; + break; + } + case 3: { + button1 = (chord & 0x1) != 0; + button2 = (chord & 0x4) != 0; + button3 = type == SWT.MouseDown; + button4 = (chord & 0x8) != 0; + button5 = (chord & 0x10) != 0; + break; + } + case 4: { + button1 = (chord & 0x1) != 0; + button2 = (chord & 0x4) != 0; + button3 = (chord & 0x2) != 0; + button4 = type == SWT.MouseDown; + button5 = (chord & 0x10) != 0; + break; + } + case 5: { + button1 = (chord & 0x1) != 0; + button2 = (chord & 0x4) != 0; + button3 = (chord & 0x2) != 0; + button4 = (chord & 0x8) != 0; + button5 = type == SWT.MouseDown; + break; + } + } + + NSPoint nsCursorPosition = NSEvent.mouseLocation(); + NSRect primaryFrame = getPrimaryFrame(); + mouseCursorPosition.x = nsCursorPosition.x; + mouseCursorPosition.y = (int) (primaryFrame.height - nsCursorPosition.y); + return OS.CGPostMouseEvent (mouseCursorPosition, true, 5, button1, button3, button2, button4, button5) == 0; + } + } + case SWT.MouseWheel: { + return OS.CGPostScrollWheelEvent(1, event.count) == 0; + } + } + return false; + } +} + +void postEvent (Event event) { + /* + * Place the event at the end of the event queue. + * This code is always called in the Display's + * thread so it must be re-enterant but does not + * need to be synchronized. + */ + if (eventQueue == null) eventQueue = new Event [4]; + int index = 0; + int length = eventQueue.length; + while (index < length) { + if (eventQueue [index] == null) break; + index++; + } + if (index == length) { + Event [] newQueue = new Event [length + 4]; + System.arraycopy (eventQueue, 0, newQueue, 0, length); + eventQueue = newQueue; + } + eventQueue [index] = event; +} + +/** + * Maps a point from one coordinate system to another. + * When the control is null, coordinates are mapped to + * the display. + * <p> + * NOTE: On right-to-left platforms where the coordinate + * systems are mirrored, special care needs to be taken + * when mapping coordinates from one control to another + * to ensure the result is correctly mirrored. + * + * Mapping a point that is the origin of a rectangle and + * then adding the width and height is not equivalent to + * mapping the rectangle. When one control is mirrored + * and the other is not, adding the width and height to a + * point that was mapped causes the rectangle to extend + * in the wrong direction. Mapping the entire rectangle + * instead of just one point causes both the origin and + * the corner of the rectangle to be mapped. + * </p> + * + * @param from the source <code>Control</code> or <code>null</code> + * @param to the destination <code>Control</code> or <code>null</code> + * @param point to be mapped + * @return point with mapped coordinates + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the point is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the Control from or the Control to have been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 2.1.2 + */ +public Point map (Control from, Control to, Point point) { + checkDevice (); + if (point == null) error (SWT.ERROR_NULL_ARGUMENT); + return map (from, to, point.x, point.y); +} + +/** + * Maps a point from one coordinate system to another. + * When the control is null, coordinates are mapped to + * the display. + * <p> + * NOTE: On right-to-left platforms where the coordinate + * systems are mirrored, special care needs to be taken + * when mapping coordinates from one control to another + * to ensure the result is correctly mirrored. + * + * Mapping a point that is the origin of a rectangle and + * then adding the width and height is not equivalent to + * mapping the rectangle. When one control is mirrored + * and the other is not, adding the width and height to a + * point that was mapped causes the rectangle to extend + * in the wrong direction. Mapping the entire rectangle + * instead of just one point causes both the origin and + * the corner of the rectangle to be mapped. + * </p> + * + * @param from the source <code>Control</code> or <code>null</code> + * @param to the destination <code>Control</code> or <code>null</code> + * @param x coordinates to be mapped + * @param y coordinates to be mapped + * @return point with mapped coordinates + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the Control from or the Control to have been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 2.1.2 + */ +public Point map (Control from, Control to, int x, int y) { + checkDevice (); + if (from != null && from.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT); + if (to != null && to.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT); + Point point = new Point (x, y); + if (from == to) return point; + NSPoint pt = new NSPoint(); + pt.x = x; + pt.y = y; + NSWindow fromWindow = from != null ? from.view.window() : null; + NSWindow toWindow = to != null ? to.view.window() : null; + if (toWindow != null && fromWindow != null && toWindow.id == fromWindow.id) { + if (!from.view.isFlipped ()) { + pt.y = from.view.bounds().height - pt.y; + } + pt = from.view.convertPoint_toView_(pt, to.view); + if (!to.view.isFlipped ()) { + pt.y = to.view.bounds().height - pt.y; + } + } else { + NSRect primaryFrame = getPrimaryFrame(); + if (from != null) { + NSView view = from.eventView (); + if (!view.isFlipped ()) { + pt.y = view.bounds().height - pt.y; + } + pt = view.convertPoint_toView_(pt, null); + pt = fromWindow.convertBaseToScreen(pt); + pt.y = primaryFrame.height - pt.y; + } + if (to != null) { + NSView view = to.eventView (); + pt.y = primaryFrame.height - pt.y; + pt = toWindow.convertScreenToBase(pt); + pt = view.convertPoint_fromView_(pt, null); + if (!view.isFlipped ()) { + pt.y = view.bounds().height - pt.y; + } + } + } + point.x = (int)pt.x; + point.y = (int)pt.y; + return point; +} + +/** + * Maps a point from one coordinate system to another. + * When the control is null, coordinates are mapped to + * the display. + * <p> + * NOTE: On right-to-left platforms where the coordinate + * systems are mirrored, special care needs to be taken + * when mapping coordinates from one control to another + * to ensure the result is correctly mirrored. + * + * Mapping a point that is the origin of a rectangle and + * then adding the width and height is not equivalent to + * mapping the rectangle. When one control is mirrored + * and the other is not, adding the width and height to a + * point that was mapped causes the rectangle to extend + * in the wrong direction. Mapping the entire rectangle + * instead of just one point causes both the origin and + * the corner of the rectangle to be mapped. + * </p> + * + * @param from the source <code>Control</code> or <code>null</code> + * @param to the destination <code>Control</code> or <code>null</code> + * @param rectangle to be mapped + * @return rectangle with mapped coordinates + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the rectangle is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the Control from or the Control to have been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 2.1.2 + */ +public Rectangle map (Control from, Control to, Rectangle rectangle) { + checkDevice (); + if (rectangle == null) error (SWT.ERROR_NULL_ARGUMENT); + return map (from, to, rectangle.x, rectangle.y, rectangle.width, rectangle.height); +} + +/** + * Maps a point from one coordinate system to another. + * When the control is null, coordinates are mapped to + * the display. + * <p> + * NOTE: On right-to-left platforms where the coordinate + * systems are mirrored, special care needs to be taken + * when mapping coordinates from one control to another + * to ensure the result is correctly mirrored. + * + * Mapping a point that is the origin of a rectangle and + * then adding the width and height is not equivalent to + * mapping the rectangle. When one control is mirrored + * and the other is not, adding the width and height to a + * point that was mapped causes the rectangle to extend + * in the wrong direction. Mapping the entire rectangle + * instead of just one point causes both the origin and + * the corner of the rectangle to be mapped. + * </p> + * + * @param from the source <code>Control</code> or <code>null</code> + * @param to the destination <code>Control</code> or <code>null</code> + * @param x coordinates to be mapped + * @param y coordinates to be mapped + * @param width coordinates to be mapped + * @param height coordinates to be mapped + * @return rectangle with mapped coordinates + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the Control from or the Control to have been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 2.1.2 + */ +public Rectangle map (Control from, Control to, int x, int y, int width, int height) { + checkDevice (); + if (from != null && from.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT); + if (to != null && to.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT); + Rectangle rectangle = new Rectangle (x, y, width, height); + if (from == to) return rectangle; + NSPoint pt = new NSPoint(); + pt.x = x; + pt.y = y; + NSWindow fromWindow = from != null ? from.view.window() : null; + NSWindow toWindow = to != null ? to.view.window() : null; + if (toWindow != null && fromWindow != null && toWindow.id == fromWindow.id) { + if (!from.view.isFlipped ()) { + pt.y = from.view.bounds().height - pt.y; + } + pt = from.view.convertPoint_toView_(pt, to.view); + if (!to.view.isFlipped ()) { + pt.y = to.view.bounds().height - pt.y; + } + } else { + NSRect primaryFrame = getPrimaryFrame(); + if (from != null) { + NSView view = from.eventView (); + if (!view.isFlipped ()) { + pt.y = view.bounds().height - pt.y; + } + pt = view.convertPoint_toView_(pt, null); + pt = fromWindow.convertBaseToScreen(pt); + pt.y = primaryFrame.height - pt.y; + } + if (to != null) { + NSView view = to.eventView (); + pt.y = primaryFrame.height - pt.y; + pt = toWindow.convertScreenToBase(pt); + pt = view.convertPoint_fromView_(pt, null); + if (!view.isFlipped ()) { + pt.y = view.bounds().height - pt.y; + } + } + } + rectangle.x = (int)pt.x; + rectangle.y = (int)pt.y; + return rectangle; +} + +int /*long*/ observerProc (int /*long*/ observer, int /*long*/ activity, int /*long*/ info) { + switch ((int)/*64*/activity) { + case OS.kCFRunLoopBeforeWaiting: + if (runAsyncMessages) { + if (runAsyncMessages (false)) wakeThread (); + } + break; + } + return 0; +} + +/** + * Reads an event from the operating system's event queue, + * dispatches it appropriately, and returns <code>true</code> + * if there is potentially more work to do, or <code>false</code> + * if the caller can sleep until another event is placed on + * the event queue. + * <p> + * In addition to checking the system event queue, this method also + * checks if any inter-thread messages (created by <code>syncExec()</code> + * or <code>asyncExec()</code>) are waiting to be processed, and if + * so handles them before returning. + * </p> + * + * @return <code>false</code> if the caller can sleep upon return from this method + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_FAILED_EXEC - if an exception occurred while running an inter-thread message</li> + * </ul> + * + * @see #sleep + * @see #wake + */ +public boolean readAndDispatch () { + checkDevice (); + if (sendEventCount == 0 && loopCount == poolCount - 1 && Callback.getEntryCount () == 0) removePool (); + addPool (); + loopCount++; + boolean events = false; + try { + events |= runSettings (); + events |= runTimers (); + events |= runContexts (); + events |= runPopups (); + NSEvent event = application.nextEventMatchingMask(0, null, OS.NSDefaultRunLoopMode, true); + if (event != null) { + events = true; + application.sendEvent(event); + } + events |= runPaint (); + events |= runDeferredEvents (); + if (!events) { + events = isDisposed () || runAsyncMessages (false); + } + } finally { + removePool (); + loopCount--; + if (sendEventCount == 0 && loopCount == poolCount && Callback.getEntryCount () == 0) addPool (); + } + return events; +} + +static void register (Display display) { + synchronized (Device.class) { + for (int i=0; i<Displays.length; i++) { + if (Displays [i] == null) { + Displays [i] = display; + return; + } + } + Display [] newDisplays = new Display [Displays.length + 4]; + System.arraycopy (Displays, 0, newDisplays, 0, Displays.length); + newDisplays [Displays.length] = display; + Displays = newDisplays; + } +} + +/** + * Releases any internal resources back to the operating + * system and clears all fields except the device handle. + * <p> + * Disposes all shells which are currently open on the display. + * After this method has been invoked, all related related shells + * will answer <code>true</code> when sent the message + * <code>isDisposed()</code>. + * </p><p> + * When a device is destroyed, resources that were acquired + * on behalf of the programmer need to be returned to the + * operating system. For example, if the device allocated a + * font to be used as the system font, this font would be + * freed in <code>release</code>. Also,to assist the garbage + * collector and minimize the amount of memory that is not + * reclaimed when the programmer keeps a reference to a + * disposed device, all fields except the handle are zero'd. + * The handle is needed by <code>destroy</code>. + * </p> + * This method is called before <code>destroy</code>. + * + * @see Device#dispose + * @see #destroy + */ +protected void release () { + disposing = true; + sendEvent (SWT.Dispose, new Event ()); + Shell [] shells = getShells (); + for (int i=0; i<shells.length; i++) { + Shell shell = shells [i]; + if (!shell.isDisposed ()) shell.dispose (); + } + if (tray != null) tray.dispose (); + tray = null; + while (readAndDispatch ()) {} + if (disposeList != null) { + for (int i=0; i<disposeList.length; i++) { + if (disposeList [i] != null) disposeList [i].run (); + } + } + disposeList = null; + synchronizer.releaseSynchronizer (); + synchronizer = null; + releaseDisplay (); + super.release (); +} + +void releaseDisplay () { + /* Release the System Images */ + if (errorImage != null) errorImage.dispose (); + if (infoImage != null) infoImage.dispose (); + if (warningImage != null) warningImage.dispose (); + errorImage = infoImage = warningImage = null; + + currentCaret = null; + + /* Release Timers */ + if (hoverTimer != null) timerExec(-1, hoverTimer); + hoverTimer = null; + if (caretTimer != null) timerExec(-1, caretTimer); + caretTimer = null; + if (nsTimers != null) { + for (int i=0; i<nsTimers.length; i++) { + if (nsTimers [i] != null) { + nsTimers [i].invalidate(); + nsTimers [i].release(); + } + } + } + nsTimers = null; + if (timerDelegate != null) timerDelegate.release(); + timerDelegate = null; + + /* Release the System Cursors */ + for (int i = 0; i < cursors.length; i++) { + if (cursors [i] != null) cursors [i].dispose (); + } + cursors = null; + + /* Release default fonts */ + if (buttonFont != null) buttonFont.release (); + if (popUpButtonFont != null) popUpButtonFont.release (); + if (textFieldFont != null) textFieldFont.release (); + if (secureTextFieldFont != null) secureTextFieldFont.release (); + if (searchFieldFont != null) searchFieldFont.release (); + if (comboBoxFont != null) comboBoxFont.release (); + if (sliderFont != null) sliderFont.release (); + if (scrollerFont != null) scrollerFont.release (); + if (textViewFont != null) textViewFont.release (); + if (tableViewFont != null) tableViewFont.release (); + if (outlineViewFont != null) outlineViewFont.release (); + if (datePickerFont != null) datePickerFont.release (); + if (boxFont != null) boxFont.release (); + if (tabViewFont != null) tabViewFont.release (); + if (progressIndicatorFont != null) progressIndicatorFont.release (); + buttonFont = popUpButtonFont = textFieldFont = secureTextFieldFont = null; + searchFieldFont = comboBoxFont = sliderFont = scrollerFont; + textViewFont = tableViewFont = outlineViewFont = datePickerFont = null; + boxFont = tabViewFont = progressIndicatorFont = null; + + /* Release Dock image */ + if (dockImage != null) dockImage.release(); + dockImage = null; + + if (screenWindow != null) screenWindow.release(); + screenWindow = null; + + if (needsDisplay != null) needsDisplay.release(); + if (needsDisplayInRect != null) needsDisplayInRect.release(); + if (isPainting != null) isPainting.release(); + needsDisplay = needsDisplayInRect = isPainting = null; + + modalShells = null; + menuBar = null; + menus = null; + + if (markedAttributes != null) markedAttributes.release(); + markedAttributes = null; + + if (oldCursorSetProc != 0) { + int /*long*/ method = OS.class_getInstanceMethod(OS.class_NSCursor, OS.sel_set); + OS.method_setImplementation(method, oldCursorSetProc); + } + if (cursorSetCallback != null) cursorSetCallback.dispose(); + cursorSetCallback = null; + + deadKeyState = null; + + if (settingsDelegate != null) { + NSNotificationCenter.defaultCenter().removeObserver(settingsDelegate); + settingsDelegate.release(); + } + settingsDelegate = null; + + // Clear the menu bar if we created it. + if (!isEmbedded) { + //remove all existing menu items except the application menu + NSMenu menubar = application.mainMenu(); + int /*long*/ count = menubar.numberOfItems(); + while (count > 1) { + menubar.removeItemAtIndex(count - 1); + count--; + } + } + + // The autorelease pool is cleaned up when we call NSApplication.terminate(). + + if (application != null && applicationClass != 0) { + OS.object_setClass (application.id, applicationClass); + } + application = null; + applicationClass = 0; + + if (runLoopObserver != 0) { + OS.CFRunLoopObserverInvalidate (runLoopObserver); + OS.CFRelease (runLoopObserver); + } + runLoopObserver = 0; + if (observerCallback != null) observerCallback.dispose(); + observerCallback = null; +} + +void removeContext (GCData context) { + if (contexts == null) return; + int count = 0; + for (int i = 0; i < contexts.length; i++) { + if (contexts[i] != null) { + if (contexts [i] == context) { + contexts[i] = null; + } else { + count++; + } + } + } + if (count == 0) contexts = null; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when an event of the given type occurs anywhere in + * a widget. The event type is one of the event constants defined + * in class <code>SWT</code>. + * + * @param eventType the type of event to listen for + * @param listener the listener which should no longer be notified when the event occurs + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Listener + * @see SWT + * @see #addFilter + * @see #addListener + * + * @since 3.0 + */ +public void removeFilter (int eventType, Listener listener) { + checkDevice (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (filterTable == null) return; + filterTable.unhook (eventType, listener); + if (filterTable.size () == 0) filterTable = null; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when an event of the given type occurs. The event type + * is one of the event constants defined in class <code>SWT</code>. + * + * @param eventType the type of event to listen for + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see Listener + * @see SWT + * @see #addListener + * + * @since 2.0 + */ +public void removeListener (int eventType, Listener listener) { + checkDevice (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (eventType, listener); +} + +Widget removeWidget (NSObject view) { + if (view == null) return null; + int /*long*/ [] jniRef = new int /*long*/ [1]; + OS.object_getInstanceVariable(view.id, SWT_OBJECT, jniRef); + if (jniRef[0] == 0) return null; + Widget widget = (Widget)OS.JNIGetObject(jniRef[0]); + OS.object_setInstanceVariable(view.id, SWT_OBJECT, 0); + return widget; +} + +void removeMenu (Menu menu) { + if (menus == null) return; + for (int i = 0; i < menus.length; i++) { + if (menus [i] == menu) { + menus[i] = null; + break; + } + } +} + +void removePool () { + NSAutoreleasePool pool = pools [poolCount - 1]; + pools [--poolCount] = null; + if (poolCount == 0) { + NSMutableDictionary dictionary = NSThread.currentThread().threadDictionary(); + dictionary.removeObjectForKey(NSString.stringWith("SWT_NSAutoreleasePool")); + } + pool.release (); +} + +void removePopup (Menu menu) { + if (popups == null) return; + for (int i=0; i<popups.length; i++) { + if (popups [i] == menu) { + popups [i] = null; + return; + } + } +} + +boolean runAsyncMessages (boolean all) { + return synchronizer.runAsyncMessages (all); +} + +boolean runContexts () { + if (contexts != null) { + for (int i = 0; i < contexts.length; i++) { + if (contexts[i] != null && contexts[i].flippedContext != null) { + contexts[i].flippedContext.flushGraphics(); + } + } + } + return false; +} + +boolean runDeferredEvents () { + boolean run = false; + /* + * Run deferred events. This code is always + * called in the Display's thread so it must + * be re-enterant need not be synchronized. + */ + while (eventQueue != null) { + + /* Take an event off the queue */ + Event event = eventQueue [0]; + if (event == null) break; + int length = eventQueue.length; + System.arraycopy (eventQueue, 1, eventQueue, 0, --length); + eventQueue [length] = null; + + /* Run the event */ + Widget widget = event.widget; + if (widget != null && !widget.isDisposed ()) { + Widget item = event.item; + if (item == null || !item.isDisposed ()) { + run = true; + widget.notifyListeners (event.type, event); + } + } + + /* + * At this point, the event queue could + * be null due to a recursive invokation + * when running the event. + */ + } + + /* Clear the queue */ + eventQueue = null; + return run; +} + +boolean runPaint () { + if (needsDisplay == null && needsDisplayInRect == null) return false; + if (needsDisplay != null) { + int /*long*/ count = needsDisplay.count(); + for (int i = 0; i < count; i++) { + OS.objc_msgSend(needsDisplay.objectAtIndex(i).id, OS.sel_setNeedsDisplay_, true); + } + needsDisplay.release(); + needsDisplay = null; + } + if (needsDisplayInRect != null) { + int /*long*/ count = needsDisplayInRect.count(); + for (int i = 0; i < count; i+=2) { + NSValue value = new NSValue(needsDisplayInRect.objectAtIndex(i+1)); + OS.objc_msgSend(needsDisplayInRect.objectAtIndex(i).id, OS.sel_setNeedsDisplayInRect_, value.rectValue()); + } + needsDisplayInRect.release(); + needsDisplayInRect = null; + } + return true; +} + +boolean runPopups () { + if (popups == null) return false; + boolean result = false; + while (popups != null) { + Menu menu = popups [0]; + if (menu == null) break; + runDeferredEvents (); + int length = popups.length; + System.arraycopy (popups, 1, popups, 0, --length); + popups [length] = null; + if (!menu.isDisposed ()) menu._setVisible (true); + result = true; + } + popups = null; + return result; +} + +boolean runSettings () { + if (!runSettings) return false; + runSettings = false; + initColors (); + sendEvent (SWT.Settings, null); + Shell [] shells = getShells (); + for (int i=0; i<shells.length; i++) { + Shell shell = shells [i]; + if (!shell.isDisposed ()) { + shell.redraw (true); + shell.layout (true, true); + } + } + return true; +} + +boolean runTimers () { + if (timerList == null) return false; + boolean result = false; + for (int i=0; i<timerList.length; i++) { + if (nsTimers [i] == null && timerList [i] != null) { + Runnable runnable = timerList [i]; + timerList [i] = null; + if (runnable != null) { + result = true; + runnable.run (); + } + } + } + return result; +} + +void sendEvent (int eventType, Event event) { + if (eventTable == null && filterTable == null) { + return; + } + if (event == null) event = new Event (); + event.display = this; + event.type = eventType; + if (event.time == 0) event.time = getLastEventTime (); + sendEvent(eventTable, event); +} + +void sendEvent (EventTable table, Event event) { + try { + sendEventCount++; + if (!filterEvent (event)) { + if (table != null) table.sendEvent (event); + } + } finally { + sendEventCount--; + } +} + +static NSString getAppName() { + NSString name = null; + int pid = OS.getpid (); + int /*long*/ ptr = OS.getenv (ascii ("APP_NAME_" + pid)); + if (ptr != 0) name = NSString.stringWithUTF8String(ptr); + if (name == null && APP_NAME != null) name = NSString.stringWith(APP_NAME); + if (name == null) { + id value = NSBundle.mainBundle().objectForInfoDictionaryKey(NSString.stringWith("CFBundleName")); + if (value != null) { + name = new NSString(value); + } + } + if (name == null) name = NSString.stringWith("SWT"); + return name; +} + +/** + * On platforms which support it, sets the application name + * to be the argument. On Motif, for example, this can be used + * to set the name used for resource lookup. Specifying + * <code>null</code> for the name clears it. + * + * @param name the new app name or <code>null</code> + */ +public static void setAppName (String name) { + APP_NAME = name; +} + +//TODO use custom timer instead of timerExec +Runnable hoverTimer = new Runnable () { + public void run () { + if (currentControl != null && !currentControl.isDisposed()) { + currentControl.sendMouseEvent (NSApplication.sharedApplication().currentEvent(), SWT.MouseHover, trackingControl != null && !trackingControl.isDisposed()); + } + } +}; +//TODO - use custom timer instead of timerExec +Runnable caretTimer = new Runnable () { + public void run () { + if (currentCaret != null) { + if (currentCaret == null || currentCaret.isDisposed()) return; + if (currentCaret.blinkCaret ()) { + int blinkRate = currentCaret.blinkRate; + if (blinkRate != 0) timerExec (blinkRate, this); + } else { + currentCaret = null; + } + } + + } +}; + +//TODO - use custom timer instead of timerExec +Runnable defaultButtonTimer = new Runnable() { + public void run() { + if (isDisposed ()) return; + Shell shell = getActiveShell(); + if (shell != null && !shell.isDisposed()) { + Button defaultButton = shell.defaultButton; + if (defaultButton != null && !defaultButton.isDisposed()) { + NSView view = defaultButton.view; + view.display(); + } + } + if (isDisposed ()) return; + if (hasDefaultButton()) timerExec(DEFAULT_BUTTON_INTERVAL, this); + } +}; + +void setCurrentCaret (Caret caret) { + currentCaret = caret; + int blinkRate = currentCaret != null ? currentCaret.blinkRate : -1; + timerExec (blinkRate, caretTimer); +} + +void setCursor (Control control) { + Cursor cursor = null; + if (control != null && !control.isDisposed()) cursor = control.findCursor (); + if (cursor == null) { + NSWindow window = application.keyWindow(); + if (window != null) { + if (window.areCursorRectsEnabled ()) { + window.disableCursorRects (); + window.enableCursorRects (); + } + return; + } + cursor = getSystemCursor (SWT.CURSOR_ARROW); + } + lockCursor = false; + cursor.handle.set (); + lockCursor = true; +} + +/** + * Sets the location of the on-screen pointer relative to the top left corner + * of the screen. <b>Note: It is typically considered bad practice for a + * program to move the on-screen pointer location.</b> + * + * @param x the new x coordinate for the cursor + * @param y the new y coordinate for the cursor + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 2.1 + */ +public void setCursorLocation (int x, int y) { + checkDevice (); + CGPoint pt = new CGPoint (); + pt.x = x; pt.y = y; + OS.CGWarpMouseCursorPosition (pt); +} + +/** + * Sets the location of the on-screen pointer relative to the top left corner + * of the screen. <b>Note: It is typically considered bad practice for a + * program to move the on-screen pointer location.</b> + * + * @param point new position + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_NULL_ARGUMENT - if the point is null + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @since 2.0 + */ +public void setCursorLocation (Point point) { + checkDevice (); + if (point == null) error (SWT.ERROR_NULL_ARGUMENT); + setCursorLocation (point.x, point.y); +} + +/** + * Sets the application defined property of the receiver + * with the specified name to the given argument. + * <p> + * Applications may have associated arbitrary objects with the + * receiver in this fashion. If the objects stored in the + * properties need to be notified when the display is disposed + * of, it is the application's responsibility provide a + * <code>disposeExec()</code> handler which does so. + * </p> + * + * @param key the name of the property + * @param value the new value for the property + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the key is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #getData(String) + * @see #disposeExec(Runnable) + */ +public void setData (String key, Object value) { + checkDevice (); + if (key == null) error (SWT.ERROR_NULL_ARGUMENT); + + if (key.equals (ADD_WIDGET_KEY)) { + Object [] data = (Object [])value; + NSObject object = (NSObject)data [0]; + Widget widget = (Widget)data [1]; + if (widget == null) { + removeWidget (object); + } else { + addWidget (object, widget); + } + } + + /* Remove the key/value pair */ + if (value == null) { + if (keys == null) return; + int index = 0; + while (index < keys.length && !keys [index].equals (key)) index++; + if (index == keys.length) return; + if (keys.length == 1) { + keys = null; + values = null; + } else { + String [] newKeys = new String [keys.length - 1]; + Object [] newValues = new Object [values.length - 1]; + System.arraycopy (keys, 0, newKeys, 0, index); + System.arraycopy (keys, index + 1, newKeys, index, newKeys.length - index); + System.arraycopy (values, 0, newValues, 0, index); + System.arraycopy (values, index + 1, newValues, index, newValues.length - index); + keys = newKeys; + values = newValues; + } + return; + } + + /* Add the key/value pair */ + if (keys == null) { + keys = new String [] {key}; + values = new Object [] {value}; + return; + } + for (int i=0; i<keys.length; i++) { + if (keys [i].equals (key)) { + values [i] = value; + return; + } + } + String [] newKeys = new String [keys.length + 1]; + Object [] newValues = new Object [values.length + 1]; + System.arraycopy (keys, 0, newKeys, 0, keys.length); + System.arraycopy (values, 0, newValues, 0, values.length); + newKeys [keys.length] = key; + newValues [values.length] = value; + keys = newKeys; + values = newValues; +} + +void setMenuBar (Menu menu) { + if (menu == menuBar) return; + menuBar = menu; + //remove all existing menu items except the application menu + NSMenu menubar = application.mainMenu(); + /* + * For some reason, NSMenu.cancelTracking() does not dismisses + * the menu right away when the menu bar is set in a stacked + * event loop. The fix is to use CancelMenuTracking() instead. + */ +// menubar.cancelTracking(); + OS.CancelMenuTracking (OS.AcquireRootMenu (), true, 0); + int /*long*/ count = menubar.numberOfItems(); + while (count > 1) { + menubar.removeItemAtIndex(count - 1); + count--; + } + //set parent of each item to NULL and add them to menubar + if (menu != null) { + MenuItem[] items = menu.getItems(); + for (int i = 0; i < items.length; i++) { + MenuItem item = items[i]; + NSMenuItem nsItem = item.nsItem; + nsItem.setMenu(null); + menubar.addItem(nsItem); + + /* + * Bug in Cocoa: Calling NSMenuItem.setEnabled() for menu item of a menu bar only + * works when the menu bar is the current menu bar. The underline OS menu does get + * enabled/disable when that menu is set later on. The fix is to toggle the + * item enabled state to force the underline menu to be updated. + */ + boolean enabled = menu.getEnabled () && item.getEnabled (); + nsItem.setEnabled(!enabled); + nsItem.setEnabled(enabled); + } + } +} + +void setModalShell (Shell shell) { + if (modalShells == null) modalShells = new Shell [4]; + int index = 0, length = modalShells.length; + while (index < length) { + if (modalShells [index] == shell) return; + if (modalShells [index] == null) break; + index++; + } + if (index == length) { + Shell [] newModalShells = new Shell [length + 4]; + System.arraycopy (modalShells, 0, newModalShells, 0, length); + modalShells = newModalShells; + } + modalShells [index] = shell; + Shell [] shells = getShells (); + for (int i=0; i<shells.length; i++) shells [i].updateModal (); +} + +/** + * Sets the application defined, display specific data + * associated with the receiver, to the argument. + * The <em>display specific data</em> is a single, + * unnamed field that is stored with every display. + * <p> + * Applications may put arbitrary objects in this field. If + * the object stored in the display specific data needs to + * be notified when the display is disposed of, it is the + * application's responsibility provide a + * <code>disposeExec()</code> handler which does so. + * </p> + * + * @param data the new display specific data + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #getData() + * @see #disposeExec(Runnable) + */ +public void setData (Object data) { + checkDevice (); + this.data = data; +} + +/** + * Sets the synchronizer used by the display to be + * the argument, which can not be null. + * + * @param synchronizer the new synchronizer for the display (must not be null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the synchronizer is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_FAILED_EXEC - if an exception occurred while running an inter-thread message</li> + * </ul> + */ +public void setSynchronizer (Synchronizer synchronizer) { + checkDevice (); + if (synchronizer == null) error (SWT.ERROR_NULL_ARGUMENT); + if (synchronizer == this.synchronizer) return; + Synchronizer oldSynchronizer; + synchronized (Device.class) { + oldSynchronizer = this.synchronizer; + this.synchronizer = synchronizer; + } + if (oldSynchronizer != null) { + oldSynchronizer.runAsyncMessages(true); + } +} + +/** + * Causes the user-interface thread to <em>sleep</em> (that is, + * to be put in a state where it does not consume CPU cycles) + * until an event is received or it is otherwise awakened. + * + * @return <code>true</code> if an event requiring dispatching was placed on the queue. + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #wake + */ +public boolean sleep () { + checkDevice (); + if (getMessageCount () != 0) return true; + try { + addPool(); + allowTimers = runAsyncMessages = false; + NSRunLoop.currentRunLoop().runMode(OS.NSDefaultRunLoopMode, NSDate.distantFuture()); + allowTimers = runAsyncMessages = true; + } finally { + removePool(); + } + return true; +} + +int sourceProc (int info) { + return 0; +} + +/** + * Causes the <code>run()</code> method of the runnable to + * be invoked by the user-interface thread at the next + * reasonable opportunity. The thread which calls this method + * is suspended until the runnable completes. Specifying <code>null</code> + * as the runnable simply wakes the user-interface thread. + * <p> + * Note that at the time the runnable is invoked, widgets + * that have the receiver as their display may have been + * disposed. Therefore, it is necessary to check for this + * case inside the runnable before accessing the widget. + * </p> + * + * @param runnable code to run on the user-interface thread or <code>null</code> + * + * @exception SWTException <ul> + * <li>ERROR_FAILED_EXEC - if an exception occurred when executing the runnable</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #asyncExec + */ +public void syncExec (Runnable runnable) { + Synchronizer synchronizer; + synchronized (Device.class) { + if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED); + synchronizer = this.synchronizer; + } + synchronizer.syncExec (runnable); +} + +/** + * Causes the <code>run()</code> method of the runnable to + * be invoked by the user-interface thread after the specified + * number of milliseconds have elapsed. If milliseconds is less + * than zero, the runnable is not executed. + * <p> + * Note that at the time the runnable is invoked, widgets + * that have the receiver as their display may have been + * disposed. Therefore, it is necessary to check for this + * case inside the runnable before accessing the widget. + * </p> + * + * @param milliseconds the delay before running the runnable + * @param runnable code to run on the user-interface thread + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the runnable is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #asyncExec + */ +public void timerExec (int milliseconds, Runnable runnable) { + checkDevice (); + //TODO - remove a timer, reschedule a timer not tested + if (runnable == null) error (SWT.ERROR_NULL_ARGUMENT); + if (timerList == null) timerList = new Runnable [4]; + if (nsTimers == null) nsTimers = new NSTimer [4]; + int index = 0; + while (index < timerList.length) { + if (timerList [index] == runnable) break; + index++; + } + if (index != timerList.length) { + NSTimer timer = nsTimers [index]; + if (timer == null) { + timerList [index] = null; + } else { + if (milliseconds < 0) { + timer.invalidate(); + timer.release(); + timerList [index] = null; + nsTimers [index] = null; + } else { + timer.setFireDate(NSDate.dateWithTimeIntervalSinceNow (milliseconds / 1000.0)); + } + return; + } + } + if (milliseconds < 0) return; + index = 0; + while (index < timerList.length) { + if (timerList [index] == null) break; + index++; + } + if (index == timerList.length) { + Runnable [] newTimerList = new Runnable [timerList.length + 4]; + System.arraycopy (timerList, 0, newTimerList, 0, timerList.length); + timerList = newTimerList; + NSTimer [] newTimerIds = new NSTimer [nsTimers.length + 4]; + System.arraycopy (nsTimers, 0, newTimerIds, 0, nsTimers.length); + nsTimers = newTimerIds; + } + NSNumber userInfo = NSNumber.numberWithInt(index); + NSTimer timer = NSTimer.scheduledTimerWithTimeInterval(milliseconds / 1000.0, timerDelegate, OS.sel_timerProc_, userInfo, false); + NSRunLoop.currentRunLoop().addTimer(timer, OS.NSEventTrackingRunLoopMode); + timer.retain(); + if (timer != null) { + nsTimers [index] = timer; + timerList [index] = runnable; + } +} + +int /*long*/ timerProc (int /*long*/ id, int /*long*/ sel, int /*long*/ timerID) { + NSTimer timer = new NSTimer (timerID); + NSNumber number = new NSNumber(timer.userInfo()); + int index = number.intValue(); + if (timerList == null) return 0; + if (0 <= index && index < timerList.length) { + if (allowTimers) { + Runnable runnable = timerList [index]; + timerList [index] = null; + nsTimers [index] = null; + if (runnable != null) runnable.run (); + } else { + nsTimers [index] = null; + wakeThread (); + } + } + timer.invalidate(); + timer.release(); + return 0; +} + +/** + * Forces all outstanding paint requests for the display + * to be processed before this method returns. + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see Control#update() + */ +public void update () { + checkDevice (); + Shell [] shells = getShells (); + for (int i=0; i<shells.length; i++) { + Shell shell = shells [i]; + if (!shell.isDisposed ()) shell.update (true); + } +} + +void updateDefaultButton () { + timerExec(hasDefaultButton() ? DEFAULT_BUTTON_INTERVAL : -1, defaultButtonTimer); +} + +void updateQuitMenu () { + boolean enabled = true; + Shell [] shells = getShells (); + int mask = SWT.PRIMARY_MODAL | SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL; + for (int i=0; i<shells.length; i++) { + Shell shell = shells [i]; + if ((shell.style & mask) != 0 && shell.isVisible ()) { + enabled = false; + break; + } + } + + NSMenu mainmenu = application.mainMenu(); + NSMenuItem appitem = mainmenu.itemAtIndex(0); + if (appitem != null) { + NSMenu sm = appitem.submenu(); + + // Normally this would be sel_terminate_ but we changed it so terminate: doesn't kill the app. + int /*long*/ quitIndex = sm.indexOfItemWithTarget(applicationDelegate, OS.sel_quitRequested_); + + if (quitIndex != -1) { + NSMenuItem quitItem = sm.itemAtIndex(quitIndex); + quitItem.setEnabled(enabled); + } + } +} + + +/** + * If the receiver's user-interface thread was <code>sleep</code>ing, + * causes it to be awakened and start running again. Note that this + * method may be called from any thread. + * + * @exception SWTException <ul> + * <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li> + * </ul> + * + * @see #sleep + */ +public void wake () { + synchronized (Device.class) { + if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED); + if (thread == Thread.currentThread ()) return; + wakeThread (); + } +} + +void wakeThread () { + //new pool? + NSObject object = new NSObject().alloc().init(); + object.performSelectorOnMainThread(OS.sel_release, null, false); +} + +Control findControl (boolean checkTrim) { + return findControl(checkTrim, null); +} + +Control findControl (boolean checkTrim, NSView[] hitView) { + NSView view = null; + NSPoint screenLocation = NSEvent.mouseLocation(); + NSArray windows = application.orderedWindows(); + for (int i = 0, count = (int)/*64*/windows.count(); i < count && view == null; i++) { + NSWindow window = new NSWindow(windows.objectAtIndex(i)); + NSView contentView = window.contentView(); + if (contentView != null && OS.NSPointInRect(screenLocation, window.frame())) { + NSPoint location = window.convertScreenToBase(screenLocation); + view = contentView.hitTest (location); + if (view == null && !checkTrim) { + view = contentView; + } + break; + } + } + Control control = null; + if (view != null) { + do { + Widget widget = getWidget (view); + if (widget instanceof Control) { + control = (Control)widget; + break; + } + view = view.superview(); + } while (view != null); + } + if (checkTrim) { + if (control != null && control.isTrim (view)) control = null; + } + if (control != null && hitView != null) hitView[0] = view; + return control; +} + +void finishLaunching (int /*long*/ id, int /*long*/ sel) { + /* + * [NSApplication finishLaunching] cannot run multiple times otherwise + * multiple main menus are added. + */ + if (launched) return; + launched = true; + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + OS.objc_msgSendSuper(super_struct, sel); +} + +void applicationDidBecomeActive (int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { + checkFocus(); + checkEnterExit(findControl(true), null, false); +} + +void applicationDidResignActive (int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { + checkFocus(); + checkEnterExit(null, null, false); +} + +int /*long*/ applicationNextEventMatchingMask (int /*long*/ id, int /*long*/ sel, int /*long*/ mask, int /*long*/ expiration, int /*long*/ mode, int /*long*/ dequeue) { + if (dequeue != 0 && trackingControl != null && !trackingControl.isDisposed()) runDeferredEvents(); + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + int /*long*/ result = OS.objc_msgSendSuper(super_struct, sel, mask, expiration, mode, dequeue != 0); + if (result != 0) { + if (dequeue != 0 && trackingControl != null && !trackingControl.isDisposed()) { + applicationSendTrackingEvent(new NSEvent(result), trackingControl); + } + } + return result; +} + +void applicationSendTrackingEvent (NSEvent nsEvent, Control trackingControl) { + int type = (int)/*64*/nsEvent.type(); + switch (type) { + case OS.NSLeftMouseDown: + case OS.NSRightMouseDown: + case OS.NSOtherMouseDown: + trackingControl.sendMouseEvent (nsEvent, SWT.MouseDown, true); + break; + case OS.NSLeftMouseUp: + case OS.NSRightMouseUp: + case OS.NSOtherMouseUp: + checkEnterExit (findControl (true), nsEvent, true); + if (trackingControl.isDisposed()) return; + trackingControl.sendMouseEvent (nsEvent, SWT.MouseUp, true); + break; + case OS.NSLeftMouseDragged: + case OS.NSRightMouseDragged: + case OS.NSOtherMouseDragged: + checkEnterExit (trackingControl, nsEvent, true); + if (trackingControl.isDisposed()) return; + //FALL THROUGH + case OS.NSMouseMoved: + trackingControl.sendMouseEvent (nsEvent, SWT.MouseMove, true); + break; + } +} + +void applicationSendEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ event) { + NSEvent nsEvent = new NSEvent(event); + NSWindow window = nsEvent.window (); + int type = (int)/*64*/nsEvent.type (); + boolean down = false; + switch (type) { + case OS.NSLeftMouseDown: + case OS.NSRightMouseDown: + case OS.NSOtherMouseDown: + down = true; + case OS.NSLeftMouseUp: + case OS.NSRightMouseUp: + case OS.NSOtherMouseUp: + case OS.NSLeftMouseDragged: + case OS.NSRightMouseDragged: + case OS.NSOtherMouseDragged: + case OS.NSMouseMoved: + case OS.NSMouseEntered: + case OS.NSMouseExited: + case OS.NSKeyDown: + case OS.NSKeyUp: + case OS.NSScrollWheel: + if (window != null) { + Shell shell = (Shell) getWidget (window.id); + if (shell != null) { + Shell modalShell = shell.getModalShell (); + if (modalShell != null) { + if (down) { + if (!application.isActive()) { + application.activateIgnoringOtherApps(true); + } + NSRect rect = window.contentRectForFrameRect(window.frame()); + NSPoint pt = window.convertBaseToScreen(nsEvent.locationInWindow()); + if (OS.NSPointInRect(pt, rect)) beep (); + } + return; + } + } + } + break; + } + sendEvent = true; + + /* + * Feature in Cocoa. The help key triggers context-sensitive help but doesn't get forwarded to the window as a key event. + * If the event is destined for the key window, is the help key, and is an NSKeyDown, send it directly to the window first. + */ + if (window != null && window.isKeyWindow() && nsEvent.type() == OS.NSKeyDown && (nsEvent.modifierFlags() & OS.NSHelpKeyMask) != 0) { + window.sendEvent(nsEvent); + } + + /* + * Feature in Cocoa. NSKeyUp events are not delivered to the window if the command key is down. + * If the event is destined for the key window, and it's a key up and the command key is down, send it directly to the window. + */ + if (window != null && window.isKeyWindow() && nsEvent.type() == OS.NSKeyUp && (nsEvent.modifierFlags() & OS.NSCommandKeyMask) != 0) { + window.sendEvent(nsEvent); + } else { + objc_super super_struct = new objc_super (); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend (id, OS.sel_superclass); + OS.objc_msgSendSuper (super_struct, sel, event); + } + sendEvent = false; +} + +void applicationWillFinishLaunching (int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { + boolean loaded = false; + NSBundle bundle = NSBundle.bundleWithIdentifier(NSString.stringWith("com.apple.JavaVM")); + NSDictionary dict = NSDictionary.dictionaryWithObject(applicationDelegate, NSString.stringWith("NSOwner")); + NSString path = bundle.pathForResource(NSString.stringWith("DefaultApp"), NSString.stringWith("nib")); + if (!loaded) loaded = path != null && NSBundle.loadNibFile(path, dict, 0); + if (!loaded) { + NSString resourcePath = bundle.resourcePath(); + path = resourcePath != null ? resourcePath.stringByAppendingString(NSString.stringWith("/English.lproj/DefaultApp.nib")) : null; + loaded = path != null && NSBundle.loadNibFile(path, dict, 0); + } + if (!loaded) { + path = NSString.stringWith(System.getProperty("java.home") + "/../Resources/English.lproj/DefaultApp.nib"); + loaded = path != null && NSBundle.loadNibFile(path, dict, 0); + } + if (!loaded) { + createMainMenu(); + } + //replace %@ with application name + NSMenu mainmenu = application.mainMenu(); + NSMenuItem appitem = mainmenu.itemAtIndex(0); + if (appitem != null) { + NSString name = getAppName(); + NSString match = NSString.stringWith("%@"); + appitem.setTitle(name); + NSMenu sm = appitem.submenu(); + NSArray ia = sm.itemArray(); + for(int i = 0; i < ia.count(); i++) { + NSMenuItem ni = new NSMenuItem(ia.objectAtIndex(i)); + NSString title = ni.title().stringByReplacingOccurrencesOfString(match, name); + ni.setTitle(title); + } + + int /*long*/ quitIndex = sm.indexOfItemWithTarget(applicationDelegate, OS.sel_terminate_); + + if (quitIndex != -1) { + NSMenuItem quitItem = sm.itemAtIndex(quitIndex); + quitItem.setAction(OS.sel_quitRequested_); + } + } +} + +static int /*long*/ applicationProc(int /*long*/ id, int /*long*/ sel) { + //TODO optimize getting the display + Display display = getCurrent (); + if (display == null) return 0; + if (sel == OS.sel_isRunning) { + // #245724: [NSApplication isRunning] must return true to allow the AWT to load correctly. + return display.isDisposed() ? 0 : 1; + } + if (sel == OS.sel_finishLaunching) { + display.finishLaunching (id, sel); + } + return 0; +} + +static int /*long*/ applicationProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + //TODO optimize getting the display + Display display = getCurrent (); + if (display == null) return 0; + NSApplication application = display.application; + if (sel == OS.sel_sendEvent_) { + display.applicationSendEvent (id, sel, arg0); + } else if (sel == OS.sel_applicationWillFinishLaunching_) { + display.applicationWillFinishLaunching(id, sel, arg0); + } else if (sel == OS.sel_terminate_) { + // Do nothing here -- without a definition of sel_terminate we get a warning dumped to the console. + } else if (sel == OS.sel_orderFrontStandardAboutPanel_) { +// application.orderFrontStandardAboutPanel(application); + } else if (sel == OS.sel_hideOtherApplications_) { + application.hideOtherApplications(application); + } else if (sel == OS.sel_hide_) { + application.hide(application); + } else if (sel == OS.sel_unhideAllApplications_) { + application.unhideAllApplications(application); + } else if (sel == OS.sel_quitRequested_) { + if (!display.disposing) { + Event event = new Event (); + display.sendEvent (SWT.Close, event); + if (event.doit) { + display.dispose(); + } + } + } else if (sel == OS.sel_applicationDidBecomeActive_) { + display.applicationDidBecomeActive(id, sel, arg0); + } else if (sel == OS.sel_applicationDidResignActive_) { + display.applicationDidResignActive(id, sel, arg0); + } + return 0; +} + +static int /*long*/ applicationProc(int /*long*/ id, int /*long*/sel, int /*long*/ arg0, int /*long*/ arg1, int /*long*/ arg2, int /*long*/ arg3) { + //TODO optimize getting the display + Display display = getCurrent (); + if (display == null) return 0; + if (sel == OS.sel_nextEventMatchingMask_untilDate_inMode_dequeue_) { + return display.applicationNextEventMatchingMask(id, sel, arg0, arg1, arg2, arg3); + } + return 0; +} + +static int /*long*/ dialogProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + int /*long*/ [] jniRef = new int /*long*/ [1]; + OS.object_getInstanceVariable(id, SWT_OBJECT, jniRef); + if (jniRef[0] == 0) return 0; + if (sel == OS.sel_changeColor_) { + ColorDialog dialog = (ColorDialog)OS.JNIGetObject(jniRef[0]); + if (dialog == null) return 0; + dialog.changeColor(id, sel, arg0); + } else if (sel == OS.sel_changeFont_) { + FontDialog dialog = (FontDialog)OS.JNIGetObject(jniRef[0]); + if (dialog == null) return 0; + dialog.changeFont(id, sel, arg0); + } else if (sel == OS.sel_sendSelection_) { + FileDialog dialog = (FileDialog)OS.JNIGetObject(jniRef[0]); + if (dialog == null) return 0; + dialog.sendSelection(id, sel, arg0); + } else if (sel == OS.sel_windowWillClose_) { + Object object = OS.JNIGetObject(jniRef[0]); + if (object instanceof FontDialog) { + ((FontDialog)object).windowWillClose(id, sel, arg0); + } else if (object instanceof ColorDialog) { + ((ColorDialog)object).windowWillClose(id, sel, arg0); + } + } + return 0; +} + +static int /*long*/ dialogProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1) { + int /*long*/ [] jniRef = new int /*long*/ [1]; + OS.object_getInstanceVariable(id, SWT_OBJECT, jniRef); + if (jniRef[0] == 0) return 0; + if (sel == OS.sel_panel_shouldShowFilename_) { + FileDialog dialog = (FileDialog)OS.JNIGetObject(jniRef[0]); + if (dialog == null) return 0; + return dialog.panel_shouldShowFilename(id, sel, arg0, arg1); + } + return 0; +} + +static int /*long*/ dialogProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1, int /*long*/ arg2) { + int /*long*/ [] jniRef = new int /*long*/ [1]; + OS.object_getInstanceVariable(id, SWT_OBJECT, jniRef); + if (jniRef[0] == 0) return 0; + if (sel == OS.sel_panelDidEnd_returnCode_contextInfo_) { + MessageBox dialog = (MessageBox)OS.JNIGetObject(jniRef[0]); + if (dialog == null) return 0; + dialog.panelDidEnd_returnCode_contextInfo(id, sel, arg0, arg1, arg2); + } + return 0; +} + +static int /*long*/ fieldEditorProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + Widget widget = null; + NSView view = new NSView (id); + do { + widget = GetWidget (view.id); + if (widget != null) break; + view = view.superview (); + } while (view != null); + if (widget == null) return 0; + if (sel == OS.sel_keyDown_) { + widget.keyDown (id, sel, arg0); + } else if (sel == OS.sel_keyUp_) { + widget.keyUp (id, sel, arg0); + } else if (sel == OS.sel_flagsChanged_) { + widget.flagsChanged(id, sel, arg0); + } else if (sel == OS.sel_insertText_) { + return widget.insertText (id, sel, arg0) ? 1 : 0; + } else if (sel == OS.sel_doCommandBySelector_) { + widget.doCommandBySelector (id, sel, arg0); + } else if (sel == OS.sel_menuForEvent_) { + return widget.menuForEvent (id, sel, arg0); + } else if (sel == OS.sel_mouseDown_) { + widget.mouseDown(id, sel, arg0); + } else if (sel == OS.sel_mouseUp_) { + widget.mouseUp(id, sel, arg0); + } else if (sel == OS.sel_mouseMoved_) { + widget.mouseMoved(id, sel, arg0); + } else if (sel == OS.sel_mouseDragged_) { + widget.mouseDragged(id, sel, arg0); + } else if (sel == OS.sel_mouseEntered_) { + widget.mouseEntered(id, sel, arg0); + } else if (sel == OS.sel_mouseExited_) { + widget.mouseExited(id, sel, arg0); + } else if (sel == OS.sel_cursorUpdate_) { + widget.cursorUpdate(id, sel, arg0); + } else if (sel == OS.sel_rightMouseDown_) { + widget.rightMouseDown(id, sel, arg0); + } else if (sel == OS.sel_rightMouseDragged_) { + widget.rightMouseDragged(id, sel, arg0); + } else if (sel == OS.sel_rightMouseUp_) { + widget.rightMouseUp(id, sel, arg0); + } else if (sel == OS.sel_otherMouseDown_) { + widget.otherMouseDown(id, sel, arg0); + } else if (sel == OS.sel_otherMouseUp_) { + widget.otherMouseUp(id, sel, arg0); + } else if (sel == OS.sel_otherMouseDragged_) { + widget.otherMouseDragged(id, sel, arg0); + } + return 0; +} + +static int /*long*/ fieldEditorProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1) { + Widget widget = null; + NSView view = new NSView (id); + do { + widget = GetWidget (view.id); + if (widget != null) break; + view = view.superview (); + } while (view != null); + if (sel == OS.sel_shouldChangeTextInRange_replacementString_) { + return widget.shouldChangeTextInRange_replacementString(id, sel, arg0, arg1) ? 1 : 0; + } + return 0; +} + +static int /*long*/ windowProc(int /*long*/ id, int /*long*/ sel) { + /* + * Feature in Cocoa. In Cocoa, the default button animation is done + * in a separate thread that calls drawRect() and isOpaque() from + * outside the UI thread. This means that those methods, and application + * code that runs as a result of those methods, must be thread safe. + * In SWT, paint events must happen in the UI thread. The fix is + * to detect a non-UI thread and avoid the drawing. Instead, the + * default button is animated by a timer. + */ + if (!NSThread.isMainThread()) { + if (sel == OS.sel_isOpaque) { + return 1; + } + } + Widget widget = GetWidget(id); + if (widget == null) return 0; + if (sel == OS.sel_sendSelection) { + widget.sendSelection(); + } else if (sel == OS.sel_sendDoubleSelection) { + widget.sendDoubleSelection(); + } else if (sel == OS.sel_sendVerticalSelection) { + widget.sendVerticalSelection(); + } else if (sel == OS.sel_sendHorizontalSelection) { + widget.sendHorizontalSelection(); + } else if (sel == OS.sel_sendSearchSelection) { + widget.sendSearchSelection(); + } else if (sel == OS.sel_sendCancelSelection) { + widget.sendCancelSelection(); + } else if (sel == OS.sel_acceptsFirstResponder) { + return widget.acceptsFirstResponder(id, sel) ? 1 : 0; + } else if (sel == OS.sel_becomeFirstResponder) { + return widget.becomeFirstResponder(id, sel) ? 1 : 0; + } else if (sel == OS.sel_resignFirstResponder) { + return widget.resignFirstResponder(id, sel) ? 1 : 0; + } else if (sel == OS.sel_isOpaque) { + return widget.isOpaque(id, sel) ? 1 : 0; + } else if (sel == OS.sel_isFlipped) { + return widget.isFlipped(id, sel) ? 1 : 0; + } else if (sel == OS.sel_canBecomeKeyView) { + return widget.canBecomeKeyView(id,sel) ? 1 : 0; + } else if (sel == OS.sel_becomeKeyWindow) { + widget.becomeKeyWindow(id, sel); + } else if (sel == OS.sel_unmarkText) { + //TODO not called? + } else if (sel == OS.sel_validAttributesForMarkedText) { + return widget.validAttributesForMarkedText (id, sel); + } else if (sel == OS.sel_markedRange) { + NSRange range = widget.markedRange (id, sel); + /* NOTE that this is freed in C */ + int /*long*/ result = OS.malloc (NSRange.sizeof); + OS.memmove (result, range, NSRange.sizeof); + return result; + } else if (sel == OS.sel_selectedRange) { + NSRange range = widget.selectedRange (id, sel); + /* NOTE that this is freed in C */ + int /*long*/ result = OS.malloc (NSRange.sizeof); + OS.memmove (result, range, NSRange.sizeof); + return result; + } else if (sel == OS.sel_cellSize) { + NSSize size = widget.cellSize (id, sel); + /* NOTE that this is freed in C */ + int /*long*/ result = OS.malloc (NSSize.sizeof); + OS.memmove (result, size, NSSize.sizeof); + return result; + } else if (sel == OS.sel_hasMarkedText) { + return widget.hasMarkedText (id, sel) ? 1 : 0; + } else if (sel == OS.sel_canBecomeKeyWindow) { + return widget.canBecomeKeyWindow (id, sel) ? 1 : 0; + } else if (sel == OS.sel_accessibilityActionNames) { + return widget.accessibilityActionNames(id, sel); + } else if (sel == OS.sel_accessibilityAttributeNames) { + return widget.accessibilityAttributeNames(id, sel); + } else if (sel == OS.sel_accessibilityParameterizedAttributeNames) { + return widget.accessibilityParameterizedAttributeNames(id, sel); + } else if (sel == OS.sel_accessibilityFocusedUIElement) { + return widget.accessibilityFocusedUIElement(id, sel); + } else if (sel == OS.sel_accessibilityIsIgnored) { + return (widget.accessibilityIsIgnored(id, sel) ? 1 : 0); + } else if (sel == OS.sel_nextState) { + return widget.nextState(id, sel); + } else if (sel == OS.sel_resetCursorRects) { + widget.resetCursorRects(id, sel); + } else if (sel == OS.sel_updateTrackingAreas) { + widget.updateTrackingAreas(id, sel); + } else if (sel == OS.sel_viewDidMoveToWindow) { + widget.viewDidMoveToWindow(id, sel); + } else if (sel == OS.sel_image) { + return widget.image(id, sel); + } + return 0; +} + +static int /*long*/ windowProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + /* + * Feature in Cocoa. In Cocoa, the default button animation is done + * in a separate thread that calls drawRect() and isOpaque() from + * outside the UI thread. This means that those methods, and application + * code that runs as a result of those methods, must be thread safe. + * In SWT, paint events must happen in the UI thread. The fix is + * to detect a non-UI thread and avoid the drawing. Instead, the + * default button is animated by a timer. + */ + if (!NSThread.isMainThread()) { + if (sel == OS.sel_drawRect_) { + return 0; + } + } + if (sel == OS.sel_timerProc_) { + //TODO optimize getting the display + Display display = getCurrent (); + if (display == null) return 0; + return display.timerProc (id, sel, arg0); + } + if (sel == OS.sel_systemSettingsChanged_) { + //TODO optimize getting the display + Display display = getCurrent (); + if (display == null) return 0; + display.runSettings = true; + return 0; + } + Widget widget = GetWidget(id); + if (widget == null) return 0; + if (sel == OS.sel_windowWillClose_) { + widget.windowWillClose(id, sel, arg0); + } else if (sel == OS.sel_drawRect_) { + NSRect rect = new NSRect(); + OS.memmove(rect, arg0, NSRect.sizeof); + widget.drawRect(id, sel, rect); + } else if (sel == OS.sel__drawThemeProgressArea_) { + widget._drawThemeProgressArea(id, sel, arg0); + } else if (sel == OS.sel_setFrameOrigin_) { + NSPoint point = new NSPoint(); + OS.memmove(point, arg0, NSPoint.sizeof); + widget.setFrameOrigin(id, sel, point); + } else if (sel == OS.sel_setFrameSize_) { + NSSize size = new NSSize(); + OS.memmove(size, arg0, NSSize.sizeof); + widget.setFrameSize(id, sel, size); + } else if (sel == OS.sel_hitTest_) { + NSPoint point = new NSPoint(); + OS.memmove(point, arg0, NSPoint.sizeof); + return widget.hitTest(id, sel, point); + } else if (sel == OS.sel_windowShouldClose_) { + return widget.windowShouldClose(id, sel, arg0) ? 1 : 0; + } else if (sel == OS.sel_mouseDown_) { + widget.mouseDown(id, sel, arg0); + } else if (sel == OS.sel_keyDown_) { + widget.keyDown(id, sel, arg0); + } else if (sel == OS.sel_keyUp_) { + widget.keyUp(id, sel, arg0); + } else if (sel == OS.sel_flagsChanged_) { + widget.flagsChanged(id, sel, arg0); + } else if (sel == OS.sel_mouseUp_) { + widget.mouseUp(id, sel, arg0); + } else if (sel == OS.sel_rightMouseDown_) { + widget.rightMouseDown(id, sel, arg0); + } else if (sel == OS.sel_rightMouseDragged_) { + widget.rightMouseDragged(id, sel, arg0); + } else if (sel == OS.sel_rightMouseUp_) { + widget.rightMouseUp(id, sel, arg0); + } else if (sel == OS.sel_otherMouseDown_) { + widget.otherMouseDown(id, sel, arg0); + } else if (sel == OS.sel_otherMouseUp_) { + widget.otherMouseUp(id, sel, arg0); + } else if (sel == OS.sel_otherMouseDragged_) { + widget.otherMouseDragged(id, sel, arg0); + } else if (sel == OS.sel_mouseMoved_) { + widget.mouseMoved(id, sel, arg0); + } else if (sel == OS.sel_mouseDragged_) { + widget.mouseDragged(id, sel, arg0); + } else if (sel == OS.sel_mouseEntered_) { + widget.mouseEntered(id, sel, arg0); + } else if (sel == OS.sel_mouseExited_) { + widget.mouseExited(id, sel, arg0); + } else if (sel == OS.sel_cursorUpdate_) { + widget.cursorUpdate(id, sel, arg0); + } else if (sel == OS.sel_menuForEvent_) { + return widget.menuForEvent(id, sel, arg0); + } else if (sel == OS.sel_noResponderFor_) { + widget.noResponderFor(id, sel, arg0); + } else if (sel == OS.sel_shouldDelayWindowOrderingForEvent_) { + return widget.shouldDelayWindowOrderingForEvent(id, sel, arg0) ? 1 : 0; + } else if (sel == OS.sel_acceptsFirstMouse_) { + return widget.acceptsFirstMouse(id, sel, arg0) ? 1 : 0; + } else if (sel == OS.sel_numberOfRowsInTableView_) { + return widget.numberOfRowsInTableView(id, sel, arg0); + } else if (sel == OS.sel_tableViewSelectionDidChange_) { + widget.tableViewSelectionDidChange(id, sel, arg0); + } else if (sel == OS.sel_windowDidResignKey_) { + widget.windowDidResignKey(id, sel, arg0); + } else if (sel == OS.sel_windowDidBecomeKey_) { + widget.windowDidBecomeKey(id, sel, arg0); + } else if (sel == OS.sel_windowDidResize_) { + widget.windowDidResize(id, sel, arg0); + } else if (sel == OS.sel_windowDidMove_) { + widget.windowDidMove(id, sel, arg0); + } else if (sel == OS.sel_menuWillOpen_) { + widget.menuWillOpen(id, sel, arg0); + } else if (sel == OS.sel_menuDidClose_) { + widget.menuDidClose(id, sel, arg0); + } else if (sel == OS.sel_menuNeedsUpdate_) { + widget.menuNeedsUpdate(id, sel, arg0); + } else if (sel == OS.sel_outlineViewSelectionDidChange_) { + widget.outlineViewSelectionDidChange(id, sel, arg0); + } else if (sel == OS.sel_sendEvent_) { + widget.windowSendEvent(id, sel, arg0); + } else if (sel == OS.sel_helpRequested_) { + widget.helpRequested(id, sel, arg0); + } else if (sel == OS.sel_scrollWheel_) { + widget.scrollWheel(id, sel, arg0); + } else if (sel == OS.sel_pageDown_) { + widget.pageDown(id, sel, arg0); + } else if (sel == OS.sel_pageUp_) { + widget.pageUp(id, sel, arg0); + } else if (sel == OS.sel_textViewDidChangeSelection_) { + widget.textViewDidChangeSelection(id, sel, arg0); + } else if (sel == OS.sel_textDidChange_) { + widget.textDidChange(id, sel, arg0); + } else if (sel == OS.sel_textDidEndEditing_) { + widget.textDidEndEditing(id, sel, arg0); + } else if (sel == OS.sel_attributedSubstringFromRange_) { + return widget.attributedSubstringFromRange (id, sel, arg0); + } else if (sel == OS.sel_characterIndexForPoint_) { + return widget.characterIndexForPoint (id, sel, arg0); + } else if (sel == OS.sel_firstRectForCharacterRange_) { + NSRect rect = widget.firstRectForCharacterRange (id, sel, arg0); + /* NOTE that this is freed in C */ + int /*long*/ result = OS.malloc (NSRect.sizeof); + OS.memmove (result, rect, NSRect.sizeof); + return result; + } else if (sel == OS.sel_insertText_) { + return widget.insertText (id, sel, arg0) ? 1 : 0; + } else if (sel == OS.sel_doCommandBySelector_) { + widget.doCommandBySelector (id, sel, arg0); + } else if (sel == OS.sel_highlightSelectionInClipRect_) { + widget.highlightSelectionInClipRect (id, sel, arg0); + } else if (sel == OS.sel_reflectScrolledClipView_) { + widget.reflectScrolledClipView (id, sel, arg0); + } else if (sel == OS.sel_accessibilityHitTest_) { + NSPoint point = new NSPoint(); + OS.memmove(point, arg0, NSPoint.sizeof); + return widget.accessibilityHitTest(id, sel, point); + } else if (sel == OS.sel_accessibilityAttributeValue_) { + return widget.accessibilityAttributeValue(id, sel, arg0); + } else if (sel == OS.sel_accessibilityPerformAction_) { + widget.accessibilityPerformAction(id, sel, arg0); + } else if (sel == OS.sel_accessibilityActionDescription_) { + widget.accessibilityActionDescription(id, sel, arg0); + } else if (sel == OS.sel_makeFirstResponder_) { + return widget.makeFirstResponder(id, sel, arg0) ? 1 : 0; + } else if (sel == OS.sel_tableViewColumnDidMove_) { + widget.tableViewColumnDidMove(id, sel, arg0); + } else if (sel == OS.sel_tableViewColumnDidResize_) { + widget.tableViewColumnDidResize(id, sel, arg0); + } else if (sel == OS.sel_outlineViewColumnDidMove_) { + widget.outlineViewColumnDidMove(id, sel, arg0); + } else if (sel == OS.sel_outlineViewColumnDidResize_) { + widget.outlineViewColumnDidResize(id, sel, arg0); + } else if (sel == OS.sel_setNeedsDisplay_) { + widget.setNeedsDisplay(id, sel, arg0 != 0); + } else if (sel == OS.sel_setNeedsDisplayInRect_) { + widget.setNeedsDisplayInRect(id, sel, arg0); + } else if (sel == OS.sel_setImage_) { + widget.setImage(id, sel, arg0); + } else if (sel == OS.sel_imageRectForBounds_) { + NSRect rect = new NSRect(); + OS.memmove(rect, arg0, NSRect.sizeof); + rect = widget.imageRectForBounds(id, sel, rect); + /* NOTE that this is freed in C */ + int /*long*/ result = OS.malloc (NSRect.sizeof); + OS.memmove (result, rect, NSRect.sizeof); + return result; + } else if (sel == OS.sel_titleRectForBounds_) { + NSRect rect = new NSRect(); + OS.memmove(rect, arg0, NSRect.sizeof); + rect = widget.titleRectForBounds(id, sel, rect); + /* NOTE that this is freed in C */ + int /*long*/ result = OS.malloc (NSRect.sizeof); + OS.memmove (result, rect, NSRect.sizeof); + return result; + } else if (sel == OS.sel_setObjectValue_) { + widget.setObjectValue(id, sel, arg0); + } else if (sel == OS.sel_updateOpenGLContext_) { + widget.updateOpenGLContext(id, sel, arg0); + } + return 0; +} + +static int /*long*/ windowProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1) { + Widget widget = GetWidget(id); + if (widget == null) return 0; + if (sel == OS.sel_tabView_willSelectTabViewItem_) { + widget.tabView_willSelectTabViewItem(id, sel, arg0, arg1); + } else if (sel == OS.sel_tabView_didSelectTabViewItem_) { + widget.tabView_didSelectTabViewItem(id, sel, arg0, arg1); + } else if (sel == OS.sel_outlineView_isItemExpandable_) { + return widget.outlineView_isItemExpandable(id, sel, arg0, arg1) ? 1 : 0; + } else if (sel == OS.sel_outlineView_numberOfChildrenOfItem_) { + return widget.outlineView_numberOfChildrenOfItem(id, sel, arg0, arg1); + } else if (sel == OS.sel_menu_willHighlightItem_) { + widget.menu_willHighlightItem(id, sel, arg0, arg1); + } else if (sel == OS.sel_setMarkedText_selectedRange_) { + widget.setMarkedText_selectedRange (id, sel, arg0, arg1); + } else if (sel == OS.sel_drawInteriorWithFrame_inView_) { + NSRect rect = new NSRect (); + OS.memmove (rect, arg0, NSRect.sizeof); + widget.drawInteriorWithFrame_inView (id, sel, rect, arg1); + } else if (sel == OS.sel_drawWithExpansionFrame_inView_) { + NSRect rect = new NSRect(); + OS.memmove(rect, arg0, NSRect.sizeof); + widget.drawWithExpansionFrame_inView (id, sel, rect, arg1); + } else if (sel == OS.sel_accessibilityAttributeValue_forParameter_) { + return widget.accessibilityAttributeValue_forParameter(id, sel, arg0, arg1); + } else if (sel == OS.sel_tableView_didClickTableColumn_) { + widget.tableView_didClickTableColumn (id, sel, arg0, arg1); + } else if (sel == OS.sel_outlineView_didClickTableColumn_) { + widget.outlineView_didClickTableColumn (id, sel, arg0, arg1); + } else if (sel == OS.sel_shouldChangeTextInRange_replacementString_) { + return widget.shouldChangeTextInRange_replacementString(id, sel, arg0, arg1) ? 1 : 0; + } else if (sel == OS.sel_canDragRowsWithIndexes_atPoint_) { + return widget.canDragRowsWithIndexes_atPoint(id, sel, arg0, arg1) ? 1 : 0; + } else if (sel == OS.sel_expandItem_expandChildren_) { + widget.expandItem_expandChildren(id, sel, arg0, arg1 != 0); + } else if (sel == OS.sel_collapseItem_collapseChildren_) { + widget.collapseItem_collapseChildren(id, sel, arg0, arg1 != 0); + } else if (sel == OS.sel_expansionFrameWithFrame_inView_) { + NSRect rect = new NSRect(); + OS.memmove(rect, arg0, NSRect.sizeof); + rect = widget.expansionFrameWithFrame_inView(id, sel, rect, arg1); + /* NOTE that this is freed in C */ + int /*long*/ result = OS.malloc (NSRect.sizeof); + OS.memmove (result, rect, NSRect.sizeof); + return result; + } + return 0; +} + +static int /*long*/ windowProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1, int /*long*/ arg2) { + Widget widget = GetWidget(id); + if (widget == null) return 0; + if (sel == OS.sel_tableView_objectValueForTableColumn_row_) { + return widget.tableView_objectValueForTableColumn_row(id, sel, arg0, arg1, arg2); + } else if (sel == OS.sel_tableView_shouldEditTableColumn_row_) { + return widget.tableView_shouldEditTableColumn_row(id, sel, arg0, arg1, arg2) ? 1 : 0; + } else if (sel == OS.sel_textView_clickedOnLink_atIndex_) { + return widget.textView_clickOnLink_atIndex(id, sel, arg0, arg1, arg2) ? 1 : 0; + } else if (sel == OS.sel_outlineView_child_ofItem_) { + return widget.outlineView_child_ofItem(id, sel, arg0, arg1, arg2); + } else if (sel == OS.sel_outlineView_objectValueForTableColumn_byItem_) { + return widget.outlineView_objectValueForTableColumn_byItem(id, sel, arg0, arg1, arg2); + } else if (sel == OS.sel_textView_willChangeSelectionFromCharacterRange_toCharacterRange_) { + NSRange range = widget.textView_willChangeSelectionFromCharacterRange_toCharacterRange(id, sel, arg0, arg1, arg2); + /* NOTE that this is freed in C */ + int /*long*/ result = OS.malloc (NSRange.sizeof); + OS.memmove (result, range, NSRange.sizeof); + return result; + } else if (sel == OS.sel_dragSelectionWithEvent_offset_slideBack_) { + NSSize offset = new NSSize(); + OS.memmove(offset, arg0, NSSize.sizeof); + return (widget.dragSelectionWithEvent(id, sel, arg0, arg1, arg2) ? 1 : 0); + } else if (sel == OS.sel_drawImage_withFrame_inView_) { + NSRect rect = new NSRect (); + OS.memmove (rect, arg1, NSRect.sizeof); + widget.drawImageWithFrameInView (id, sel, arg0, rect, arg2); + } else if (sel == OS.sel_hitTestForEvent_inRect_ofView_) { + NSRect rect = new NSRect (); + OS.memmove (rect, arg1, NSRect.sizeof); + return widget.hitTestForEvent (id, sel, arg0, rect, arg2); + } else if (sel == OS.sel_tableView_writeRowsWithIndexes_toPasteboard_) { + return (widget.tableView_writeRowsWithIndexes_toPasteboard(id, sel, arg0, arg1, arg2) ? 1 : 0); + } else if (sel == OS.sel_outlineView_writeItems_toPasteboard_) { + return (widget.outlineView_writeItems_toPasteboard(id, sel, arg0, arg1, arg2) ? 1 : 0); + } + return 0; +} + +static int /*long*/ windowProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1, int /*long*/ arg2, int /*long*/ arg3) { + Widget widget = GetWidget(id); + if (widget == null) return 0; + if (sel == OS.sel_tableView_willDisplayCell_forTableColumn_row_) { + widget.tableView_willDisplayCell_forTableColumn_row(id, sel, arg0, arg1, arg2, arg3); + } else if (sel == OS.sel_outlineView_willDisplayCell_forTableColumn_item_) { + widget.outlineView_willDisplayCell_forTableColumn_item(id, sel, arg0, arg1, arg2, arg3); + } else if (sel == OS.sel_outlineView_setObjectValue_forTableColumn_byItem_) { + widget.outlineView_setObjectValue_forTableColumn_byItem(id, sel, arg0, arg1, arg2, arg3); + } else if (sel == OS.sel_tableView_setObjectValue_forTableColumn_row_) { + widget.tableView_setObjectValue_forTableColumn_row(id, sel, arg0, arg1, arg2, arg3); + } else if (sel == OS.sel_view_stringForToolTip_point_userData_) { + return widget.view_stringForToolTip_point_userData(id, sel, arg0, arg1, arg2, arg3); + } + return 0; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/FileDialog.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/FileDialog.java new file mode 100755 index 0000000000..6192f9414c --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/FileDialog.java @@ -0,0 +1,461 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.*; +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class allow the user to navigate + * the file system and select or enter a file name. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>SAVE, OPEN, MULTI</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * Note: Only one of the styles SAVE and OPEN may be specified. + * </p><p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the SWT implementation. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#filedialog">FileDialog snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample, Dialog tab</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class FileDialog extends Dialog { + NSSavePanel panel; + NSPopUpButton popup; + String [] filterNames = new String [0]; + String [] filterExtensions = new String [0]; + String [] fileNames = new String[0]; + String filterPath = "", fileName = ""; + int filterIndex = -1; + boolean overwrite = false; + static final char EXTENSION_SEPARATOR = ';'; + +/** + * Constructs a new instance of this class given only its parent. + * + * @param parent a shell which will be the parent of the new instance + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + */ +public FileDialog (Shell parent) { + this (parent, SWT.APPLICATION_MODAL); +} + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a shell which will be the parent of the new instance + * @param style the style of dialog to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#SAVE + * @see SWT#OPEN + * @see SWT#MULTI + */ +public FileDialog (Shell parent, int style) { + super (parent, checkStyle (parent, style)); + if (Display.getSheetEnabled ()) { + if (parent != null && (style & SWT.SHEET) != 0) this.style |= SWT.SHEET; + } + checkSubclass (); +} + +/** + * Returns the path of the first file that was + * selected in the dialog relative to the filter path, or an + * empty string if no such file has been selected. + * + * @return the relative path of the file + */ +public String getFileName () { + return fileName; +} + +/** + * Returns a (possibly empty) array with the paths of all files + * that were selected in the dialog relative to the filter path. + * + * @return the relative paths of the files + */ +public String [] getFileNames () { + return fileNames; +} + +/** + * Returns the file extensions which the dialog will + * use to filter the files it shows. + * + * @return the file extensions filter + */ +public String [] getFilterExtensions () { + return filterExtensions; +} + +/** + * Get the 0-based index of the file extension filter + * which was selected by the user, or -1 if no filter + * was selected. + * <p> + * This is an index into the FilterExtensions array and + * the FilterNames array. + * </p> + * + * @return index the file extension filter index + * + * @see #getFilterExtensions + * @see #getFilterNames + * + * @since 3.4 + */ +public int getFilterIndex () { + return filterIndex; +} + +/** + * Returns the names that describe the filter extensions + * which the dialog will use to filter the files it shows. + * + * @return the list of filter names + */ +public String [] getFilterNames () { + return filterNames; +} + +/** + * Returns the directory path that the dialog will use, or an empty + * string if this is not set. File names in this path will appear + * in the dialog, filtered according to the filter extensions. + * + * @return the directory path string + * + * @see #setFilterExtensions + */ +public String getFilterPath () { + return filterPath; +} + +/** + * Returns the flag that the dialog will use to + * determine whether to prompt the user for file + * overwrite if the selected file already exists. + * + * @return true if the dialog will prompt for file overwrite, false otherwise + * + * @since 3.4 + */ +public boolean getOverwrite () { + return overwrite; +} + +/** + * Makes the dialog visible and brings it to the front + * of the display. + * + * @return a string describing the absolute path of the first selected file, + * or null if the dialog was cancelled or an error occurred + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the dialog has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the dialog</li> + * </ul> + */ +public String open () { + String fullPath = null; + fileNames = new String [0]; + int /*long*/ method = 0; + int /*long*/ methodImpl = 0; + Callback callback = null; + if ((style & SWT.SAVE) != 0) { + NSSavePanel savePanel = NSSavePanel.savePanel(); + panel = savePanel; + if (!overwrite) { + callback = new Callback(this, "_overwriteExistingFileCheck", 3); + int /*long*/ proc = callback.getAddress(); + if (proc == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + method = OS.class_getInstanceMethod(OS.class_NSSavePanel, OS.sel_overwriteExistingFileCheck); + if (method != 0) methodImpl = OS.method_setImplementation(method, proc); + } + } else { + NSOpenPanel openPanel = NSOpenPanel.openPanel(); + openPanel.setAllowsMultipleSelection((style & SWT.MULTI) != 0); + panel = openPanel; + } + panel.setCanCreateDirectories(true); + int /*long*/ jniRef = 0; + SWTPanelDelegate delegate = null; + if (filterExtensions != null && filterExtensions.length != 0) { + delegate = (SWTPanelDelegate)new SWTPanelDelegate().alloc().init(); + jniRef = OS.NewGlobalRef(this); + if (jniRef == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.object_setInstanceVariable(delegate.id, Display.SWT_OBJECT, jniRef); + panel.setDelegate(delegate); + NSPopUpButton widget = (NSPopUpButton)new NSPopUpButton().alloc(); + widget.initWithFrame(new NSRect(), false); + widget.setTarget(delegate); + widget.setAction(OS.sel_sendSelection_); + NSMenu menu = widget.menu(); + menu.setAutoenablesItems(false); + for (int i = 0; i < filterExtensions.length; i++) { + String str = filterExtensions [i]; + if (filterNames != null && filterNames.length > i) { + str = filterNames [i]; + } + NSMenuItem nsItem = (NSMenuItem)new NSMenuItem().alloc(); + nsItem.initWithTitle(NSString.stringWith(str), 0, NSString.stringWith("")); + menu.addItem(nsItem); + nsItem.release(); + } + widget.selectItemAtIndex(0 <= filterIndex && filterIndex < filterExtensions.length ? filterIndex : 0); + widget.sizeToFit(); + panel.setAccessoryView(widget); + popup = widget; + } + panel.setTitle(NSString.stringWith(title != null ? title : "")); + NSApplication application = NSApplication.sharedApplication(); + if (parent != null && (style & SWT.SHEET) != 0) { + application.beginSheet(panel, parent.window, null, 0, 0); + } + NSString dir = filterPath != null ? NSString.stringWith(filterPath) : null; + NSString file = fileName != null ? NSString.stringWith(fileName) : null; + int /*long*/ response = panel.runModalForDirectory(dir, file); + if (parent != null && (style & SWT.SHEET) != 0) { + application.endSheet(panel, 0); + } + if (!overwrite) { + if (method != 0) OS.method_setImplementation(method, methodImpl); + if (callback != null) callback.dispose(); + } + if (response == OS.NSFileHandlingPanelOKButton) { + NSString filename = panel.filename(); + fullPath = filename.getString(); + if ((style & SWT.SAVE) == 0) { + NSArray filenames = ((NSOpenPanel)panel).filenames(); + int count = (int)/*64*/filenames.count(); + fileNames = new String[count]; + + for (int i = 0; i < count; i++) { + filename = new NSString(filenames.objectAtIndex(i)); + NSString filenameOnly = filename.lastPathComponent(); + NSString pathOnly = filename.stringByDeletingLastPathComponent(); + + if (i == 0) { + /* Filter path */ + filterPath = pathOnly.getString(); + + /* File name */ + fileName = fileNames [0] = filenameOnly.getString(); + } else { + if (pathOnly.getString().equals (filterPath)) { + fileNames [i] = filenameOnly.getString(); + } else { + fileNames [i] = filename.getString(); + } + } + } + } + filterIndex = -1; + } + if (popup != null) { + filterIndex = (int)/*64*/popup.indexOfSelectedItem(); + panel.setAccessoryView(null); + popup.release(); + popup = null; + } + if (delegate != null) { + panel.setDelegate(null); + delegate.release(); + } + if (jniRef != 0) OS.DeleteGlobalRef(jniRef); + panel = null; + return fullPath; +} + +int /*long*/ _overwriteExistingFileCheck (int /*long*/ id, int /*long*/ sel, int /*long*/ str) { + return 1; +} + +int /*long*/ panel_shouldShowFilename (int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1) { + NSString path = new NSString(arg1); + if (filterExtensions != null && filterExtensions.length != 0) { + NSFileManager manager = NSFileManager.defaultManager(); + int /*long*/ ptr = OS.malloc(1); + boolean found = manager.fileExistsAtPath(path, ptr); + byte[] isDirectory = new byte[1]; + OS.memmove(isDirectory, ptr, 1); + OS.free(ptr); + if (found) { + if (isDirectory[0] != 0) { + return 1; + } else { + NSString ext = path.pathExtension(); + if (ext != null) { + int filterIndex = (int)/*64*/popup.indexOfSelectedItem(); + String extension = ext.getString(); + String extensions = filterExtensions [filterIndex]; + int start = 0, length = extensions.length (); + while (start < length) { + int index = extensions.indexOf (EXTENSION_SEPARATOR, start); + if (index == -1) index = length; + String filter = extensions.substring (start, index).trim (); + if (filter.equals ("*") || filter.equals ("*.*")) return 1; + if (filter.startsWith ("*.")) filter = filter.substring (2); + if (filter.toLowerCase ().equals(extension.toLowerCase ())) return 1; + start = index + 1; + } + } + return 0; + } + } + } + return 1; +} + +void sendSelection (int /*long*/ id, int /*long*/ sel, int /*long*/ arg) { + panel.validateVisibleColumns(); +} + +/** + * Set the initial filename which the dialog will + * select by default when opened to the argument, + * which may be null. The name will be prefixed with + * the filter path when one is supplied. + * + * @param string the file name + */ +public void setFileName (String string) { + fileName = string; +} + +/** + * Set the file extensions which the dialog will + * use to filter the files it shows to the argument, + * which may be null. + * <p> + * The strings are platform specific. For example, on + * some platforms, an extension filter string is typically + * of the form "*.extension", where "*.*" matches all files. + * For filters with multiple extensions, use semicolon as + * a separator, e.g. "*.jpg;*.png". + * </p> + * + * @param extensions the file extension filter + * + * @see #setFilterNames to specify the user-friendly + * names corresponding to the extensions + */ +public void setFilterExtensions (String [] extensions) { + filterExtensions = extensions; +} + +/** + * Set the 0-based index of the file extension filter + * which the dialog will use initially to filter the files + * it shows to the argument. + * <p> + * This is an index into the FilterExtensions array and + * the FilterNames array. + * </p> + * + * @param index the file extension filter index + * + * @see #setFilterExtensions + * @see #setFilterNames + * + * @since 3.4 + */ +public void setFilterIndex (int index) { + filterIndex = index; +} + +/** + * Sets the names that describe the filter extensions + * which the dialog will use to filter the files it shows + * to the argument, which may be null. + * <p> + * Each name is a user-friendly short description shown for + * its corresponding filter. The <code>names</code> array must + * be the same length as the <code>extensions</code> array. + * </p> + * + * @param names the list of filter names, or null for no filter names + * + * @see #setFilterExtensions + */ +public void setFilterNames (String [] names) { + filterNames = names; +} + +/** + * Sets the directory path that the dialog will use + * to the argument, which may be null. File names in this + * path will appear in the dialog, filtered according + * to the filter extensions. If the string is null, + * then the operating system's default filter path + * will be used. + * <p> + * Note that the path string is platform dependent. + * For convenience, either '/' or '\' can be used + * as a path separator. + * </p> + * + * @param string the directory path + * + * @see #setFilterExtensions + */ +public void setFilterPath (String string) { + filterPath = string; +} + +/** + * Sets the flag that the dialog will use to + * determine whether to prompt the user for file + * overwrite if the selected file already exists. + * + * @param overwrite true if the dialog will prompt for file overwrite, false otherwise + * + * @since 3.4 + */ +public void setOverwrite (boolean overwrite) { + this.overwrite = overwrite; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/FontDialog.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/FontDialog.java new file mode 100755 index 0000000000..df410af548 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/FontDialog.java @@ -0,0 +1,223 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class allow the user to select a font + * from all available fonts in the system. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>(none)</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the SWT implementation. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample, Dialog tab</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class FontDialog extends Dialog { + FontData fontData; + RGB rgb; + boolean selected; + int fontID, fontSize; + +/** + * Constructs a new instance of this class given only its parent. + * + * @param parent a shell which will be the parent of the new instance + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + */ +public FontDialog (Shell parent) { + this (parent, SWT.APPLICATION_MODAL); +} + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a shell which will be the parent of the new instance + * @param style the style of dialog to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + */ +public FontDialog (Shell parent, int style) { + super (parent, checkStyle (parent, style)); + checkSubclass (); +} + +void changeFont(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + selected = true; +} + +/** + * Returns a FontData object describing the font that was + * selected in the dialog, or null if none is available. + * + * @return the FontData for the selected font, or null + * @deprecated use #getFontList () + */ +public FontData getFontData () { + return fontData; +} + +/** + * Returns a FontData set describing the font that was + * selected in the dialog, or null if none is available. + * + * @return the FontData for the selected font, or null + * @since 2.1.1 + */ +public FontData [] getFontList () { + if (fontData == null) return null; + FontData [] result = new FontData [1]; + result [0] = fontData; + return result; +} + +/** + * Returns an RGB describing the color that was selected + * in the dialog, or null if none is available. + * + * @return the RGB value for the selected color, or null + * + * @see PaletteData#getRGBs + * + * @since 2.1 + */ +public RGB getRGB () { + return rgb; +} + +/** + * Makes the dialog visible and brings it to the front + * of the display. + * + * @return a FontData object describing the font that was selected, + * or null if the dialog was cancelled or an error occurred + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the dialog has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the dialog</li> + * </ul> + */ +public FontData open () { + Display display = parent != null ? parent.display : Display.getCurrent (); + NSFontPanel panel = NSFontPanel.sharedFontPanel(); + panel.setTitle(NSString.stringWith(title != null ? title : "")); + boolean create = fontData != null; + Font font = create ? new Font(display, fontData) : display.getSystemFont(); + panel.setPanelFont(font.handle, false); + SWTPanelDelegate delegate = (SWTPanelDelegate)new SWTPanelDelegate().alloc().init(); + int /*long*/ jniRef = OS.NewGlobalRef(this); + if (jniRef == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.object_setInstanceVariable(delegate.id, Display.SWT_OBJECT, jniRef); + panel.setDelegate(delegate); + fontData = null; + selected = false; + panel.orderFront(null); + NSApplication.sharedApplication().runModalForWindow(panel); + if (selected) { + NSFont nsFont = panel.panelConvertFont(font.handle); + if (nsFont != null) { + fontData = Font.cocoa_new(display, nsFont).getFontData()[0]; + } + } + panel.setDelegate(null); + delegate.release(); + OS.DeleteGlobalRef(jniRef); + if (create) font.dispose(); + return fontData; +} + +/** + * Sets a FontData object describing the font to be + * selected by default in the dialog, or null to let + * the platform choose one. + * + * @param fontData the FontData to use initially, or null + * @deprecated use #setFontList (FontData []) + */ +public void setFontData (FontData fontData) { + this.fontData = fontData; +} + +/** + * Sets the set of FontData objects describing the font to + * be selected by default in the dialog, or null to let + * the platform choose one. + * + * @param fontData the set of FontData objects to use initially, or null + * to let the platform select a default when open() is called + * + * @see Font#getFontData + * + * @since 2.1.1 + */ +public void setFontList (FontData [] fontData) { + if (fontData != null && fontData.length > 0) { + this.fontData = fontData [0]; + } else { + this.fontData = null; + } +} + +/** + * Sets the RGB describing the color to be selected by default + * in the dialog, or null to let the platform choose one. + * + * @param rgb the RGB value to use initially, or null to let + * the platform select a default when open() is called + * + * @see PaletteData#getRGBs + * + * @since 2.1 + */ +public void setRGB (RGB rgb) { + this.rgb = rgb; +} + +void windowWillClose(int /*long*/ id, int /*long*/ sel, int /*long*/ sender) { + NSApplication.sharedApplication().stop(null); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Group.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Group.java new file mode 100755 index 0000000000..e5b1dc262e --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Group.java @@ -0,0 +1,241 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class provide an etched border + * with an optional title. + * <p> + * Shadow styles are hints and may not be honoured + * by the platform. To create a group with the + * default shadow style for the platform, do not + * specify a shadow style. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>SHADOW_ETCHED_IN, SHADOW_ETCHED_OUT, SHADOW_IN, SHADOW_OUT, SHADOW_NONE</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * Note: Only one of the above styles may be specified. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Group extends Composite { + NSView contentView; + String text = ""; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#SHADOW_ETCHED_IN + * @see SWT#SHADOW_ETCHED_OUT + * @see SWT#SHADOW_IN + * @see SWT#SHADOW_OUT + * @see SWT#SHADOW_NONE + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Group (Composite parent, int style) { + super (parent, checkStyle (style)); +} + +static int checkStyle (int style) { + style |= SWT.NO_FOCUS; + /* + * Even though it is legal to create this widget + * with scroll bars, they serve no useful purpose + * because they do not automatically scroll the + * widget's client area. The fix is to clear + * the SWT style. + */ + return style & ~(SWT.H_SCROLL | SWT.V_SCROLL); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +public Rectangle computeTrim (int x, int y, int width, int height) { + checkWidget (); + NSBox widget = (NSBox)view; + int border = (int)Math.ceil (widget.borderWidth ()); + NSSize margins = widget.contentViewMargins(); + NSRect frame = contentView.frame(); + width += (margins.width + border) * 2; + height += (margins.height + border) * 2 + frame.y; + return super.computeTrim(x, y, width, height); +} + +NSView contentView () { + return contentView; +} + +void createHandle () { + state |= THEME_BACKGROUND; + NSBox widget = (NSBox)new SWTBox().alloc(); + widget.init(); + widget.setTitlePosition(OS.NSNoTitle); + NSView contentWidget = (NSView)new SWTView().alloc(); + contentWidget.init(); +// contentWidget.setDrawsBackground(false); + widget.setContentView(contentWidget); + contentView = contentWidget; + view = widget; +} + +NSFont defaultNSFont () { + return display.boxFont; +} + +void deregister () { + super.deregister (); + display.removeWidget (contentView); + SWTBox box = (SWTBox)view; + display.removeWidget (box.titleCell()); +} + +void drawBackground (int /*long*/ id, NSGraphicsContext context, NSRect rect) { + if (id != view.id) return; + fillBackground (view, context, rect, -1); +} + +NSView eventView () { + return contentView; +} + +public Rectangle getClientArea () { + checkWidget(); + NSRect rect = contentView.bounds(); + return new Rectangle((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height); +} + +String getNameText () { + return getText (); +} + +/** + * Returns the receiver's text, which is the string that the + * is used as the <em>title</em>. If the text has not previously + * been set, returns an empty string. + * + * @return the text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getText () { + checkWidget (); + return text; +} + +float getThemeAlpha () { + return (background != null ? 1 : 0.25f) * parent.getThemeAlpha (); +} + +void register () { + super.register (); + display.addWidget (contentView, this); + SWTBox box = (SWTBox)view; + display.addWidget (box.titleCell(), this); +} + +void releaseHandle () { + super.releaseHandle (); + if (contentView != null) contentView.release(); + contentView = null; +} + +void setFont(NSFont font) { + ((NSBox) view).setTitleFont(font); +} + +void setForeground (float /*double*/ [] color) { + NSColor nsColor; + if (color == null) { + nsColor = NSColor.textColor (); + } else { + nsColor = NSColor.colorWithDeviceRed (color[0], color[1], color[2], 1); + } + NSTextFieldCell cell = new NSTextFieldCell (((NSBox)view).titleCell ().id); + cell.setTextColor (nsColor); +} + +/** + * Sets the receiver's text, which is the string that will + * be displayed as the receiver's <em>title</em>, to the argument, + * which may not be null. The string may include the mnemonic character. + * </p> + * Mnemonics are indicated by an '&' that causes the next + * character to be the mnemonic. When the user presses a + * key sequence that matches the mnemonic, focus is assigned + * to the first child of the group. On most platforms, the + * mnemonic appears underlined but may be emphasised in a + * platform specific manner. The mnemonic indicator character + * '&' can be escaped by doubling it in the string, causing + * a single '&' to be displayed. + * </p> + * @param string the new text + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the text is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setText (String string) { + checkWidget(); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + text = string; + char [] buffer = new char [text.length ()]; + text.getChars (0, buffer.length, buffer, 0); + int length = fixMnemonic (buffer); + NSBox box = (NSBox)view; + box.setTitlePosition(length == 0 ? OS.NSNoTitle : OS.NSAtTop); + box.setTitle(NSString.stringWithCharacters(buffer, length)); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/IME.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/IME.java new file mode 100644 index 0000000000..f6c09b0693 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/IME.java @@ -0,0 +1,510 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class represent input method editors. + * These are typically in-line pre-edit text areas that allow + * the user to compose characters from Far Eastern languages + * such as Japanese, Chinese or Korean. + * + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>(none)</dd> + * <dt><b>Events:</b></dt> + * <dd>ImeComposition</dd> + * </dl> + * <p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.4 + * @noextend This class is not intended to be subclassed by clients. + */ +public class IME extends Widget { + Canvas parent; + int caretOffset; + int startOffset; + int commitCount; + String text; + int [] ranges; + TextStyle [] styles; + + static final int UNDERLINE_THICK = 1 << 16; + +/** + * Prevents uninitialized instances from being created outside the package. + */ +IME () { +} + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a canvas control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public IME (Canvas parent, int style) { + super (parent, style); + this.parent = parent; + createWidget (); +} + +int /*long*/ attributedSubstringFromRange (int /*long*/ id, int /*long*/ sel, int /*long*/ rangePtr) { + Event event = new Event (); + event.detail = SWT.COMPOSITION_SELECTION; + sendEvent (SWT.ImeComposition, event); + NSRange range = new NSRange (); + OS.memmove (range, rangePtr, NSRange.sizeof); + int start = (int)/*64*/range.location; + int end = (int)/*64*/(range.location + range.length); + if (event.start <= start && start <= event.end && event.start <= end && end <= event.end) { + NSString str = NSString.stringWith (event.text.substring(start - event.start, end - event.start)); + NSAttributedString attriStr = ((NSAttributedString)new NSAttributedString().alloc()).initWithString(str, null); + attriStr.autorelease (); + return attriStr.id; + } + return 0; +} + +int /*long*/ characterIndexForPoint (int /*long*/ id, int /*long*/ sel, int /*long*/ point) { + if (!isInlineEnabled ()) return OS.NSNotFound; + NSPoint pt = new NSPoint (); + OS.memmove (pt, point, NSPoint.sizeof); + NSView view = parent.view; + pt = view.window ().convertScreenToBase (pt); + pt = view.convertPoint_fromView_ (pt, null); + Event event = new Event (); + event.detail = SWT.COMPOSITION_OFFSET; + event.x = (int) pt.x; + event.y = (int) pt.y; + sendEvent (SWT.ImeComposition, event); + int offset = event.index + event.count; + return offset != -1 ? offset : OS.NSNotFound; +} + +void createWidget () { + text = ""; + startOffset = -1; + if (parent.getIME () == null) { + parent.setIME (this); + } +} + +NSRect firstRectForCharacterRange(int /*long*/ id, int /*long*/ sel, int /*long*/ range) { + NSRect rect = new NSRect (); + Caret caret = parent.caret; + if (caret != null) { + NSView view = parent.view; + NSPoint pt = new NSPoint (); + pt.x = caret.x; + pt.y = caret.y + caret.height; + pt = view.convertPoint_toView_ (pt, null); + pt = view.window ().convertBaseToScreen (pt); + rect.x = pt.x; + rect.y = pt.y; + rect.width = caret.width; + rect.height = caret.height; + } + return rect; +} + +/** + * Returns the offset of the caret from the start of the document. + * The caret is within the current composition. + * + * @return the caret offset + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getCaretOffset () { + checkWidget (); + return startOffset + caretOffset; +} + +/** + * Returns the commit count of the composition. This is the + * number of characters that have been composed. When the + * commit count is equal to the length of the composition + * text, then the in-line edit operation is complete. + * + * @return the commit count + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see IME#getText + */ +public int getCommitCount () { + checkWidget (); + return commitCount; +} + +/** + * Returns the offset of the composition from the start of the document. + * This is the start offset of the composition within the document and + * in not changed by the input method editor itself during the in-line edit + * session. + * + * @return the offset of the composition + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getCompositionOffset () { + checkWidget (); + return startOffset; +} + +/** + * Returns the ranges for the style that should be applied during the + * in-line edit session. + * <p> + * The ranges array contains start and end pairs. Each pair refers to + * the corresponding style in the styles array. For example, the pair + * that starts at ranges[n] and ends at ranges[n+1] uses the style + * at styles[n/2] returned by <code>getStyles()</code>. + * </p> + * @return the ranges for the styles + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see IME#getStyles + */ +public int [] getRanges () { + checkWidget (); + if (ranges == null) return new int [0]; + int [] result = new int [ranges.length]; + for (int i = 0; i < result.length; i++) { + result [i] = ranges [i] + startOffset; + } + return result; +} + +/** + * Returns the styles for the ranges. + * <p> + * The ranges array contains start and end pairs. Each pair refers to + * the corresponding style in the styles array. For example, the pair + * that starts at ranges[n] and ends at ranges[n+1] uses the style + * at styles[n/2]. + * </p> + * + * @return the ranges for the styles + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see IME#getRanges + */ +public TextStyle [] getStyles () { + checkWidget (); + if (styles == null) return new TextStyle [0]; + TextStyle [] result = new TextStyle [styles.length]; + System.arraycopy (styles, 0, result, 0, styles.length); + return result; +} + +TextStyle getStyle (NSDictionary attribs) { + NSArray keys = attribs.allKeys (); + int /*long*/ count = keys.count (); + TextStyle style = new TextStyle (); + for (int j = 0; j < count; j++) { + NSString key = new NSString (keys.objectAtIndex (j)); + if (key.isEqualTo (OS.NSBackgroundColorAttributeName)) { + NSColor color = new NSColor (attribs.objectForKey (key)).colorUsingColorSpaceName (OS.NSCalibratedRGBColorSpace); + float /*double*/ [] rgbColor = new float /*double*/ []{color.redComponent(), color.greenComponent(), color.blueComponent(), color.alphaComponent()}; + style.background = Color.cocoa_new (display, rgbColor); + } else if (key.isEqualTo (OS.NSForegroundColorAttributeName)) { + NSColor color = new NSColor (attribs.objectForKey (key)).colorUsingColorSpaceName (OS.NSCalibratedRGBColorSpace); + float /*double*/ [] rgbColor = new float /*double*/ []{color.redComponent(), color.greenComponent(), color.blueComponent(), color.alphaComponent()}; + style.foreground = Color.cocoa_new (display, rgbColor); + } else if (key.isEqualTo (OS.NSUnderlineColorAttributeName)) { + NSColor color = new NSColor (attribs.objectForKey (key)).colorUsingColorSpaceName (OS.NSCalibratedRGBColorSpace); + float /*double*/ [] rgbColor = new float /*double*/ []{color.redComponent(), color.greenComponent(), color.blueComponent(), color.alphaComponent()}; + style.underlineColor = Color.cocoa_new (display, rgbColor); + } else if (key.isEqualTo (OS.NSUnderlineStyleAttributeName)) { + NSNumber value = new NSNumber (attribs.objectForKey (key)); + switch (value.intValue ()) { + case OS.NSUnderlineStyleSingle: style.underlineStyle = SWT.UNDERLINE_SINGLE; break; + case OS.NSUnderlineStyleDouble: style.underlineStyle = SWT.UNDERLINE_DOUBLE; break; + case OS.NSUnderlineStyleThick: style.underlineStyle = UNDERLINE_THICK; break; + } + style.underline = value.intValue () != OS.NSUnderlineStyleNone; + } else if (key.isEqualTo (OS.NSStrikethroughColorAttributeName)) { + NSColor color = new NSColor (attribs.objectForKey (key)).colorUsingColorSpaceName (OS.NSCalibratedRGBColorSpace); + float /*double*/ [] rgbColor = new float /*double*/ []{color.redComponent(), color.greenComponent(), color.blueComponent(), color.alphaComponent()}; + style.strikeoutColor = Color.cocoa_new (display, rgbColor); + } else if (key.isEqualTo (OS.NSStrikethroughStyleAttributeName)) { + NSNumber value = new NSNumber (attribs.objectForKey (key)); + style.strikeout = value.intValue () != OS.NSUnderlineStyleNone; + } else if (key.isEqualTo (OS.NSFontAttributeName)) { + NSFont font = new NSFont (attribs.objectForKey (key)); + font.retain(); + style.font = Font.cocoa_new (display, font); + } + } + return style; +} + +/** + * Returns the composition text. + * <p> + * The text for an IME is the characters in the widget that + * are in the current composition. When the commit count is + * equal to the length of the composition text, then the + * in-line edit operation is complete. + * </p> + * + * @return the widget text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getText () { + checkWidget (); + return text; +} + +/** + * Returns <code>true</code> if the caret should be wide, and + * <code>false</code> otherwise. In some languages, for example + * Korean, the caret is typically widened to the width of the + * current character in the in-line edit session. + * + * @return the wide caret state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getWideCaret() { + return false; +} + +boolean hasMarkedText (int /*long*/ id, int /*long*/ sel) { + return text.length () != 0; +} + +boolean insertText (int /*long*/ id, int /*long*/ sel, int /*long*/ string) { + if (startOffset == -1) return true; + NSString str = new NSString (string); + if (str.isKindOfClass (OS.objc_getClass ("NSAttributedString"))) { + str = new NSAttributedString (string).string (); + } + int length = (int)/*64*/str.length (); + int end = startOffset + text.length (); + resetStyles (); + caretOffset = commitCount = length; + Event event = new Event (); + event.detail = SWT.COMPOSITION_CHANGED; + event.start = startOffset; + event.end = end; + event.text = text = str.getString(); + sendEvent (SWT.ImeComposition, event); + text = ""; + caretOffset = commitCount = 0; + startOffset = -1; + return event.doit; +} + +boolean isInlineEnabled () { + return hooks (SWT.ImeComposition); +} + +NSRange markedRange (int /*long*/ id, int /*long*/ sel) { + NSRange range = new NSRange (); + if (startOffset != -1) { + range.location = startOffset; + range.length = text.length (); + } else { + range.location = OS.NSNotFound; + } + return range; +} + +void resetStyles () { + if (styles != null) { + for (int i = 0; i < styles.length; i++) { + TextStyle style = styles [i]; + Font font = style.font; + if (font != null) font.handle.release (); + } + } + styles = null; + ranges = null; +} + +void releaseParent () { + super.releaseParent (); + if (this == parent.getIME ()) parent.setIME (null); +} + +void releaseWidget () { + super.releaseWidget (); + parent = null; + text = null; + resetStyles (); +} + +NSRange selectedRange (int /*long*/ id, int /*long*/ sel) { + Event event = new Event (); + event.detail = SWT.COMPOSITION_SELECTION; + sendEvent (SWT.ImeComposition, event); + NSRange range = new NSRange (); + range.location = event.start; + range.length = event.text.length (); + return range; +} + +/** + * Sets the offset of the composition from the start of the document. + * This is the start offset of the composition within the document and + * in not changed by the input method editor itself during the in-line edit + * session but may need to be changed by clients of the IME. For example, + * if during an in-line edit operation, a text editor inserts characters + * above the IME, then the IME must be informed that the composition + * offset has changed. + * + * @param offset the offset of the composition + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setCompositionOffset (int offset) { + checkWidget (); + if (offset < 0) return; + if (startOffset != -1) { + startOffset = offset; + } +} + +boolean setMarkedText_selectedRange (int /*long*/ id, int /*long*/ sel, int /*long*/ string, int /*long*/ selRange) { + if (!isInlineEnabled ()) return true; + resetStyles (); + caretOffset = commitCount = 0; + int end = startOffset + text.length (); + if (startOffset == -1) { + Event event = new Event (); + event.detail = SWT.COMPOSITION_SELECTION; + sendEvent (SWT.ImeComposition, event); + startOffset = event.start; + end = event.end; + } + NSString str = new NSString (string); + if (str.isKindOfClass (OS.objc_getClass ("NSAttributedString"))) { + NSAttributedString attribStr = new NSAttributedString (string); + str = attribStr.string (); + int length = (int)/*64*/str.length (); + styles = new TextStyle [length]; + ranges = new int [length * 2]; + NSRange rangeLimit = new NSRange (), effectiveRange = new NSRange (); + rangeLimit.length = length; + int rangeCount = 0; + int /*long*/ ptr = OS.malloc (NSRange.sizeof); + for (int i = 0; i < length;) { + NSDictionary attribs = attribStr.attributesAtIndex(i, ptr, rangeLimit); + OS.memmove (effectiveRange, ptr, NSRange.sizeof); + i = (int)/*64*/(effectiveRange.location + effectiveRange.length); + ranges [rangeCount * 2] = (int)/*64*/effectiveRange.location; + ranges [rangeCount * 2 + 1] = (int)/*64*/(effectiveRange.location + effectiveRange.length - 1); + styles [rangeCount++] = getStyle (attribs); + } + OS.free (ptr); + if (rangeCount != styles.length) { + TextStyle [] newStyles = new TextStyle [rangeCount]; + System.arraycopy (styles, 0, newStyles, 0, newStyles.length); + styles = newStyles; + int [] newRanges = new int [rangeCount * 2]; + System.arraycopy (ranges, 0, newRanges, 0, newRanges.length); + ranges = newRanges; + } + } + int length = (int)/*64*/str.length (); + if (ranges == null && length > 0) { + styles = new TextStyle []{getStyle (display.markedAttributes)}; + ranges = new int[]{0, length - 1}; + } + NSRange range = new NSRange (); + OS.memmove (range, selRange, NSRange.sizeof); + caretOffset = (int)/*64*/range.location; + Event event = new Event (); + event.detail = SWT.COMPOSITION_CHANGED; + event.start = startOffset; + event.end = end; + event.text = text = str.getString(); + sendEvent (SWT.ImeComposition, event); + if (isDisposed ()) return false; + if (text.length () == 0) { + Shell s = parent.getShell (); + s.keyInputHappened = true; + startOffset = -1; + resetStyles (); + } + return true; +} + +int /*long*/ validAttributesForMarkedText (int /*long*/ id, int /*long*/ sel) { + NSMutableArray attribs = NSMutableArray.arrayWithCapacity (6); + attribs.addObject (new NSString (OS.NSForegroundColorAttributeName ())); + attribs.addObject (new NSString (OS.NSBackgroundColorAttributeName ())); + attribs.addObject (new NSString (OS.NSUnderlineStyleAttributeName ())); + attribs.addObject (new NSString (OS.NSUnderlineColorAttributeName ())); + attribs.addObject (new NSString (OS.NSStrikethroughStyleAttributeName ())); + attribs.addObject (new NSString (OS.NSStrikethroughColorAttributeName ())); + return attribs.id; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Label.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Label.java new file mode 100755 index 0000000000..2afd1625e8 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Label.java @@ -0,0 +1,521 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import org.eclipse.swt.*; +import org.eclipse.swt.accessibility.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class represent a non-selectable + * user interface object that displays a string or image. + * When SEPARATOR is specified, displays a single + * vertical or horizontal line. + * <p> + * Shadow styles are hints and may not be honored + * by the platform. To create a separator label + * with the default shadow style for the platform, + * do not specify a shadow style. + * </p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>SEPARATOR, HORIZONTAL, VERTICAL</dd> + * <dd>SHADOW_IN, SHADOW_OUT, SHADOW_NONE</dd> + * <dd>CENTER, LEFT, RIGHT, WRAP</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * Note: Only one of SHADOW_IN, SHADOW_OUT and SHADOW_NONE may be specified. + * SHADOW_NONE is a HINT. Only one of HORIZONTAL and VERTICAL may be specified. + * Only one of CENTER, LEFT and RIGHT may be specified. + * </p><p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the SWT implementation. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#label">Label snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Label extends Control { + String text; + Image image; + boolean isImage; + NSTextField textView; + NSImageView imageView; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#SEPARATOR + * @see SWT#HORIZONTAL + * @see SWT#VERTICAL + * @see SWT#SHADOW_IN + * @see SWT#SHADOW_OUT + * @see SWT#SHADOW_NONE + * @see SWT#CENTER + * @see SWT#LEFT + * @see SWT#RIGHT + * @see SWT#WRAP + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Label (Composite parent, int style) { + super (parent, checkStyle (style)); +} + +int /*long*/ accessibilityAttributeNames(int /*long*/ id, int /*long*/ sel) { + if (accessible != null) { + if ((textView != null && (id == textView.id || id == textView.cell().id)) || (imageView != null && (id == imageView.id || id == imageView.cell().id))) { + // See if the accessible will override or augment the standard list. + // Help, title, and description can be overridden. + NSMutableArray extraAttributes = NSMutableArray.arrayWithCapacity(3); + extraAttributes.addObject(OS.NSAccessibilityHelpAttribute); + extraAttributes.addObject(OS.NSAccessibilityDescriptionAttribute); + extraAttributes.addObject(OS.NSAccessibilityTitleAttribute); + + for (int i = (int)/*64*/extraAttributes.count() - 1; i >= 0; i--) { + NSString attribute = new NSString(extraAttributes.objectAtIndex(i).id); + if (accessible.internal_accessibilityAttributeValue(attribute, ACC.CHILDID_SELF) == null) { + extraAttributes.removeObjectAtIndex(i); + } + } + + if (extraAttributes.count() > 0) { + int /*long*/ superResult = super.accessibilityAttributeNames(id, sel); + NSArray baseAttributes = new NSArray(superResult); + NSMutableArray mutableAttributes = NSMutableArray.arrayWithCapacity(baseAttributes.count() + 1); + mutableAttributes.addObjectsFromArray(baseAttributes); + + for (int i = 0; i < extraAttributes.count(); i++) { + id currAttribute = extraAttributes.objectAtIndex(i); + if (!mutableAttributes.containsObject(currAttribute)) { + mutableAttributes.addObject(currAttribute); + } + } + + return mutableAttributes.id; + } + } + } + + return super.accessibilityAttributeNames(id, sel); +} + +boolean accessibilityIsIgnored(int /*long*/ id, int /*long*/ sel) { + if (id == view.id) return true; + return super.accessibilityIsIgnored(id, sel); +} + +void addRelation (Control control) { + if (!control.isDescribedByLabel ()) return; + + if (textView != null) { + NSObject accessibleElement = control.focusView(); + + if (accessibleElement instanceof NSControl) { + NSControl viewAsControl = (NSControl)accessibleElement; + if (viewAsControl.cell() != null) accessibleElement = viewAsControl.cell(); + } + + accessibleElement.accessibilitySetOverrideValue(textView.cell(), OS.NSAccessibilityTitleUIElementAttribute); + NSArray controlArray = NSArray.arrayWithObject(accessibleElement); + textView.cell().accessibilitySetOverrideValue(controlArray, OS.NSAccessibilityServesAsTitleForUIElementsAttribute); + } +} + +static int checkStyle (int style) { + style |= SWT.NO_FOCUS; + if ((style & SWT.SEPARATOR) != 0) { + style = checkBits (style, SWT.VERTICAL, SWT.HORIZONTAL, 0, 0, 0, 0); + return checkBits (style, SWT.SHADOW_OUT, SWT.SHADOW_IN, SWT.SHADOW_NONE, 0, 0, 0); + } + return checkBits (style, SWT.LEFT, SWT.CENTER, SWT.RIGHT, 0, 0, 0); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget(); + int width = DEFAULT_WIDTH; + int height = DEFAULT_HEIGHT; + if ((style & SWT.SEPARATOR) != 0) { + float /*double*/ lineWidth = ((NSBox)view).borderWidth (); + if ((style & SWT.HORIZONTAL) != 0) { + height = (int)Math.ceil (lineWidth * 2); + } else { + width = (int)Math.ceil (lineWidth * 2); + } + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + int border = getBorderWidth (); + width += border * 2; height += border * 2; + return new Point (width, height); + } + if (isImage) { + if (image != null) { + NSImage nsimage = image.handle; + NSSize size = nsimage.size (); + width = (int)size.width; + height = (int)size.height; + } else { + width = height = 0; + } + } else { + NSSize size = null; + if ((style & SWT.WRAP) != 0 && wHint != SWT.DEFAULT) { + NSRect rect = new NSRect (); + rect.width = wHint; + rect.height = hHint != SWT.DEFAULT ? hHint : Float.MAX_VALUE; + size = textView.cell ().cellSizeForBounds (rect); + } else { + size = textView.cell ().cellSize (); + } + width = (int)Math.ceil (size.width); + height = (int)Math.ceil (size.height); + } + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + return new Point (width, height); +} + +void createHandle () { + state |= THEME_BACKGROUND; + NSBox widget = (NSBox)new SWTBox().alloc(); + widget.init(); + widget.setTitle(NSString.stringWith("")); + if ((style & SWT.SEPARATOR) != 0) { + widget.setBoxType(OS.NSBoxSeparator); + NSView child = (NSView) new SWTView().alloc().init(); + widget.setContentView(child); + child.release(); + } else { + widget.setBorderType(OS.NSNoBorder); + widget.setBorderWidth (0); + widget.setBoxType (OS.NSBoxCustom); + NSSize offsetSize = new NSSize (); + widget.setContentViewMargins (offsetSize); + + NSImageView imageWidget = (NSImageView) new SWTImageView ().alloc (); + imageWidget.init(); + imageWidget.setImageScaling (OS.NSScaleNone); + + NSTextField textWidget = (NSTextField)new SWTTextField().alloc(); + textWidget.init(); + textWidget.setBordered(false); + textWidget.setEditable(false); + textWidget.setDrawsBackground(false); + NSTextFieldCell cell = new NSTextFieldCell(textWidget.cell()); + cell.setWraps ((style & SWT.WRAP) != 0); + + widget.addSubview(imageWidget); + widget.addSubview(textWidget); + widget.setContentView(textWidget); + + imageView = imageWidget; + textView = textWidget; + _setAlignment(); + } + view = widget; +} + +void createWidget() { + text = ""; + super.createWidget (); +} + +NSAttributedString createString() { + NSAttributedString attribStr = createString(text, null, foreground, (style & SWT.WRAP) == 0 ? style : 0, true, true); + attribStr.autorelease(); + return attribStr; +} + +NSFont defaultNSFont () { + return display.textFieldFont; +} + +void deregister () { + super.deregister (); + if (textView != null) { + display.removeWidget(textView); + display.removeWidget(textView.cell()); + } + if (imageView != null) { + display.removeWidget (imageView); + display.removeWidget (imageView.cell()); + } +} + +NSView eventView () { + return ((NSBox)view).contentView(); +} + +/** + * Returns a value which describes the position of the + * text or image in the receiver. The value will be one of + * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code> + * unless the receiver is a <code>SEPARATOR</code> label, in + * which case, <code>NONE</code> is returned. + * + * @return the alignment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getAlignment () { + checkWidget(); + if ((style & SWT.SEPARATOR) != 0) return SWT.LEFT; + if ((style & SWT.CENTER) != 0) return SWT.CENTER; + if ((style & SWT.RIGHT) != 0) return SWT.RIGHT; + return SWT.LEFT; +} + +/** + * Returns the receiver's image if it has one, or null + * if it does not. + * + * @return the receiver's image + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Image getImage () { + checkWidget(); + return image; +} + +String getNameText () { + return getText (); +} + +/** + * Returns the receiver's text, which will be an empty + * string if it has never been set or if the receiver is + * a <code>SEPARATOR</code> label. + * + * @return the receiver's text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getText () { + checkWidget(); + if ((style & SWT.SEPARATOR) != 0) return ""; + return text; +} + +boolean isDescribedByLabel () { + return false; +} + +void register () { + super.register (); + if (textView != null) { + display.addWidget (textView, this); + display.addWidget (textView.cell(), this); + } + if (imageView != null) { + display.addWidget (imageView, this); + display.addWidget (imageView.cell(), this); + } +} + +void releaseHandle () { + super.releaseHandle (); + if (textView != null) textView.release(); + if (imageView != null) imageView.release(); + textView = null; + imageView = null; +} + +/* + * Remove "Labeled by" relations from the receiver. + */ +void removeRelation () { + if (textView != null) { + textView.cell().accessibilitySetOverrideValue(null, OS.NSAccessibilityServesAsTitleForUIElementsAttribute); + } +} + +/** + * Controls how text and images will be displayed in the receiver. + * The argument should be one of <code>LEFT</code>, <code>RIGHT</code> + * or <code>CENTER</code>. If the receiver is a <code>SEPARATOR</code> + * label, the argument is ignored and the alignment is not changed. + * + * @param alignment the new alignment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setAlignment (int alignment) { + checkWidget(); + if ((style & SWT.SEPARATOR) != 0) return; + if ((alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER)) == 0) return; + style &= ~(SWT.LEFT | SWT.RIGHT | SWT.CENTER); + style |= alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER); + _setAlignment(); +} + +void updateBackground () { + if ((style & SWT.SEPARATOR) != 0) return; + NSColor nsColor = null; + if (backgroundImage != null) { + nsColor = NSColor.colorWithPatternImage(backgroundImage.handle); + } else if (background != null) { + nsColor = NSColor.colorWithDeviceRed(background[0], background[1], background[2], background[3]); + } else { + nsColor = NSColor.clearColor(); + } + ((NSBox)view).setFillColor(nsColor); +} + +void _setAlignment() { + if (image != null) { + if ((style & SWT.RIGHT) != 0) imageView.setImageAlignment(OS.NSImageAlignRight); + if ((style & SWT.LEFT) != 0) imageView.setImageAlignment(OS.NSImageAlignLeft); + if ((style & SWT.CENTER) != 0) imageView.setImageAlignment(OS.NSImageAlignCenter); + } + if (text != null) { + NSCell cell = new NSCell(textView.cell()); + cell.setAttributedStringValue(createString()); + } +} + +void setFont(NSFont font) { + if (textView != null) { + NSCell cell = new NSCell(textView.cell()); + cell.setAttributedStringValue(createString()); + textView.setFont (font); + } +} + +void setForeground (float /*double*/ [] color) { + if ((style & SWT.SEPARATOR) != 0) return; + NSCell cell = new NSCell(textView.cell()); + cell.setAttributedStringValue(createString()); +} + +boolean setTabItemFocus () { + return false; +} + +/** + * Sets the receiver's image to the argument, which may be + * null indicating that no image should be displayed. + * + * @param image the image to display on the receiver (may be null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setImage (Image image) { + checkWidget(); + if ((style & SWT.SEPARATOR) != 0) return; + if (image != null && image.isDisposed ()) { + error (SWT.ERROR_INVALID_ARGUMENT); + } + this.image = image; + isImage = true; + + /* + * Feature in Cocoa. If the NSImage object being set into the view is + * the same NSImage object that is already there then the new image is + * not taken. This results in the view's image not changing even if the + * NSImage object's content has changed since it was last set into the + * view. The workaround is to temporarily set the view's image to null + * so that the new image will then be taken. + */ + if (image != null) { + NSImage current = imageView.image (); + if (current != null && current.id == image.handle.id) { + imageView.setImage (null); + } + } + imageView.setImage(image != null ? image.handle : null); + ((NSBox)view).setContentView(imageView); +} + +/** + * Sets the receiver's text. + * <p> + * This method sets the widget label. The label may include + * the mnemonic character and line delimiters. + * </p> + * <p> + * Mnemonics are indicated by an '&' that causes the next + * character to be the mnemonic. When the user presses a + * key sequence that matches the mnemonic, focus is assigned + * to the control that follows the label. On most platforms, + * the mnemonic appears underlined but may be emphasised in a + * platform specific manner. The mnemonic indicator character + * '&' can be escaped by doubling it in the string, causing + * a single '&' to be displayed. + * </p> + * + * @param string the new text + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the text is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setText (String string) { + checkWidget(); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if ((style & SWT.SEPARATOR) != 0) return; + isImage = false; + text = string; + NSCell cell = new NSCell(textView.cell()); + cell.setAttributedStringValue(createString()); + ((NSBox)view).setContentView(textView); +} + + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Link.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Link.java new file mode 100755 index 0000000000..18210ff412 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Link.java @@ -0,0 +1,532 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; + +/** + * Instances of this class represent a selectable + * user interface object that displays a text with + * links. + * <p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>(none)</dd> + * <dt><b>Events:</b></dt> + * <dd>Selection</dd> + * </dl> + * <p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#link">Link snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.1 + * @noextend This class is not intended to be subclassed by clients. + */ +public class Link extends Control { + NSScrollView scrollView; + String text; + Point [] offsets; + Point selection; + String [] ids; + int [] mnemonics; + NSColor linkColor; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Link (Composite parent, int style) { + super (parent, style); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the control is selected by the user, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * <code>widgetSelected</code> is called when the control is selected by the user. + * <code>widgetDefaultSelected</code> is not called. + * </p> + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener (SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Selection, typedListener); + addListener (SWT.DefaultSelection, typedListener); +} + +boolean textView_clickOnLink_atIndex(int /*long*/ id, int /*long*/ sel, int /*long*/ textView, int /*long*/ link, int /*long*/ charIndex) { + NSString str = new NSString (link); + Event event = new Event (); + event.text = str.getString(); + sendEvent (SWT.Selection, event); + return true; +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + if (wHint != SWT.DEFAULT && wHint < 0) wHint = 0; + if (hHint != SWT.DEFAULT && hHint < 0) hHint = 0; + int width, height; + //TODO wrapping, wHint + int borderStyle = hasBorder() ? OS.NSBezelBorder : OS.NSNoBorder; + NSSize borderSize = NSScrollView.frameSizeForContentSize(new NSSize(), false, false, borderStyle); + NSTextView widget = (NSTextView)view; + NSSize size = widget.textStorage().size(); + width = (int)(size.width + borderSize.width); + height = (int)(size.height + borderSize.height); + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + int border = getBorderWidth (); + width += border * 2; + height += border * 2; + + // TODO is this true? if so, can this rounding be turned off? + /* + * Bug in Cocoa. NSTextStorage.size() seems to return a width + * value that is rounded down, because its result is never + * fractional. The workaround is to increment width by 1 + * to ensure that it is wide enough to show the full text. + */ + width += 1; + return new Point (width, height); +} + +void createHandle () { + state |= THEME_BACKGROUND; + NSScrollView scrollWidget = (NSScrollView)new SWTScrollView().alloc(); + scrollWidget.init(); + scrollWidget.setDrawsBackground(false); + scrollWidget.setBorderType(hasBorder() ? OS.NSBezelBorder : OS.NSNoBorder); + + NSTextView widget = (NSTextView)new SWTTextView().alloc(); + widget.init(); + widget.setEditable(false); + widget.setDrawsBackground(false); + widget.setDelegate(widget); + widget.setAutoresizingMask (OS.NSViewWidthSizable | OS.NSViewHeightSizable); + widget.textContainer().setLineFragmentPadding(0); + + scrollView = scrollWidget; + view = widget; +} + +void createWidget () { + super.createWidget (); + text = ""; + NSDictionary dict = ((NSTextView)view).linkTextAttributes(); + linkColor = new NSColor(dict.valueForKey(OS.NSForegroundColorAttributeName)); +} + +NSFont defaultNSFont () { + return display.textViewFont; +} + +void deregister () { + super.deregister (); + if (scrollView != null) display.removeWidget (scrollView); +} + +void enableWidget (boolean enabled) { + super.enableWidget (enabled); + NSColor nsColor = null; + if (enabled) { + if (foreground == null) { + nsColor = NSColor.textColor (); + } else { + nsColor = NSColor.colorWithDeviceRed (foreground [0], foreground [1], foreground [2], foreground[3]); + } + } else { + nsColor = NSColor.disabledControlTextColor(); + } + NSTextView widget = (NSTextView)view; + widget.setTextColor(nsColor); + NSDictionary linkTextAttributes = widget.linkTextAttributes(); + int count = (int)/*64*/linkTextAttributes.count(); + NSMutableDictionary dict = NSMutableDictionary.dictionaryWithCapacity(count); + dict.setDictionary(linkTextAttributes); + dict.setValue(enabled ? linkColor : nsColor, OS.NSForegroundColorAttributeName); + widget.setLinkTextAttributes(dict); +} + +String getNameText () { + return getText (); +} + + +/** + * Returns the receiver's text, which will be an empty + * string if it has never been set. + * + * @return the receiver's text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getText () { + checkWidget (); + return text; +} + +void register () { + super.register (); + if (scrollView != null) display.addWidget (scrollView, this); +} + +void releaseWidget () { + super.releaseWidget (); + offsets = null; + ids = null; + mnemonics = null; + text = null; + linkColor = null; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is selected by the user. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener (SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Selection, listener); + eventTable.unhook (SWT.DefaultSelection, listener); +} + +String parse (String string) { + int length = string.length (); + offsets = new Point [length / 4]; + ids = new String [length / 4]; + mnemonics = new int [length / 4 + 1]; + StringBuffer result = new StringBuffer (); + char [] buffer = new char [length]; + string.getChars (0, string.length (), buffer, 0); + int index = 0, state = 0, linkIndex = 0; + int start = 0, tagStart = 0, linkStart = 0, endtagStart = 0, refStart = 0; + while (index < length) { + char c = Character.toLowerCase (buffer [index]); + switch (state) { + case 0: + if (c == '<') { + tagStart = index; + state++; + } + break; + case 1: + if (c == 'a') state++; + break; + case 2: + switch (c) { + case 'h': + state = 7; + break; + case '>': + linkStart = index + 1; + state++; + break; + default: + if (Character.isWhitespace(c)) break; + else state = 13; + } + break; + case 3: + if (c == '<') { + endtagStart = index; + state++; + } + break; + case 4: + state = c == '/' ? state + 1 : 3; + break; + case 5: + state = c == 'a' ? state + 1 : 3; + break; + case 6: + if (c == '>') { + mnemonics [linkIndex] = parseMnemonics (buffer, start, tagStart, result); + int offset = result.length (); + parseMnemonics (buffer, linkStart, endtagStart, result); + offsets [linkIndex] = new Point (offset, result.length () - 1); + if (ids [linkIndex] == null) { + ids [linkIndex] = new String (buffer, linkStart, endtagStart - linkStart); + } + linkIndex++; + start = tagStart = linkStart = endtagStart = refStart = index + 1; + state = 0; + } else { + state = 3; + } + break; + case 7: + state = c == 'r' ? state + 1 : 0; + break; + case 8: + state = c == 'e' ? state + 1 : 0; + break; + case 9: + state = c == 'f' ? state + 1 : 0; + break; + case 10: + state = c == '=' ? state + 1 : 0; + break; + case 11: + if (c == '"') { + state++; + refStart = index + 1; + } else { + state = 0; + } + break; + case 12: + if (c == '"') { + ids[linkIndex] = new String (buffer, refStart, index - refStart); + state = 2; + } + break; + case 13: + if (Character.isWhitespace (c)) { + state = 0; + } else if (c == '='){ + state++; + } + break; + case 14: + state = c == '"' ? state + 1 : 0; + break; + case 15: + if (c == '"') state = 2; + break; + default: + state = 0; + break; + } + index++; + } + if (start < length) { + int tmp = parseMnemonics (buffer, start, tagStart, result); + int mnemonic = parseMnemonics (buffer, Math.max (tagStart, linkStart), length, result); + if (mnemonic == -1) mnemonic = tmp; + mnemonics [linkIndex] = mnemonic; + } else { + mnemonics [linkIndex] = -1; + } + if (offsets.length != linkIndex) { + Point [] newOffsets = new Point [linkIndex]; + System.arraycopy (offsets, 0, newOffsets, 0, linkIndex); + offsets = newOffsets; + String [] newIDs = new String [linkIndex]; + System.arraycopy (ids, 0, newIDs, 0, linkIndex); + ids = newIDs; + int [] newMnemonics = new int [linkIndex + 1]; + System.arraycopy (mnemonics, 0, newMnemonics, 0, linkIndex + 1); + mnemonics = newMnemonics; + } + return result.toString (); +} + +int parseMnemonics (char[] buffer, int start, int end, StringBuffer result) { + int mnemonic = -1, index = start; + while (index < end) { + if (buffer [index] == '&') { + if (index + 1 < end && buffer [index + 1] == '&') { + result.append (buffer [index]); + index++; + } else { + mnemonic = result.length(); + } + } else { + result.append (buffer [index]); + } + index++; + } + return mnemonic; +} + +void updateBackground () { + NSColor nsColor = null; + if (backgroundImage != null) { + nsColor = NSColor.colorWithPatternImage(backgroundImage.handle); + } else if (background != null) { + nsColor = NSColor.colorWithDeviceRed(background[0], background[1], background[2], background[3]); + } + setBackground(nsColor); +} + +void setBackground(NSColor nsColor) { + NSTextView widget = (NSTextView)view; + if (nsColor == null) { + widget.setDrawsBackground(false); + } else { + widget.setDrawsBackground(true); + widget.setBackgroundColor (nsColor); + } +} + +void setFont(NSFont font) { + ((NSTextView) view).setFont(font); +} + +void setForeground (float /*double*/ [] color) { + if (!getEnabled ()) return; + NSColor nsColor; + if (color == null) { + nsColor = NSColor.textColor (); + } else { + nsColor = NSColor.colorWithDeviceRed (color [0], color [1], color [2], 1); + } + ((NSTextView) view).setTextColor (nsColor); +} + +/** + * Sets the receiver's text. + * <p> + * The string can contain both regular text and hyperlinks. A hyperlink + * is delimited by an anchor tag, <A> and </A>. Within an + * anchor, a single HREF attribute is supported. When a hyperlink is + * selected, the text field of the selection event contains either the + * text of the hyperlink or the value of its HREF, if one was specified. + * In the rare case of identical hyperlinks within the same string, the + * HREF attribute can be used to distinguish between them. The string may + * include the mnemonic character and line delimiters. The only delimiter + * the HREF attribute supports is the quotation mark ("). + * </p> + * + * @param string the new text + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the text is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setText (String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if (string.equals (text)) return; + text = string; + NSTextView widget = (NSTextView)view; + widget.setString(NSString.stringWith(parse(string))); + NSTextStorage textStorage = widget.textStorage(); + NSRange range = new NSRange(); + for (int i = 0; i < offsets.length; i++) { + range.location = offsets[i].x; + range.length = offsets[i].y - offsets[i].x + 1; + textStorage.addAttribute(OS.NSLinkAttributeName, NSString.stringWith(ids[i]), range); + } +} + +public void setToolTipText(String string) { + ((NSTextView)view).setDisplaysLinkToolTips(string == null); + super.setToolTipText(string); +} + +void setZOrder () { + super.setZOrder (); + if (scrollView != null) scrollView.setDocumentView (view); +} + +NSView topView () { + return scrollView; +} + +void updateCursorRects (boolean enabled) { + super.updateCursorRects (enabled); + if (scrollView == null) return; + updateCursorRects (enabled, scrollView); + NSClipView contentView = scrollView.contentView (); + updateCursorRects (enabled, contentView); + contentView.setDocumentCursor (enabled ? NSCursor.IBeamCursor () : null); +} + +//int traversalCode (int key, int theEvent) { +// if (offsets.length == 0) return 0; +// int bits = super.traversalCode (key, theEvent); +// if (key == 48 /* Tab */ && theEvent != 0) { +// int [] modifiers = new int [1]; +// OS.GetEventParameter (theEvent, OS.kEventParamKeyModifiers, OS.typeUInt32, null, 4, null, modifiers); +// boolean next = (modifiers [0] & OS.shiftKey) == 0; +// if (next && focusIndex < offsets.length - 1) { +// return bits & ~ SWT.TRAVERSE_TAB_NEXT; +// } +// if (!next && focusIndex > 0) { +// return bits & ~ SWT.TRAVERSE_TAB_PREVIOUS; +// } +// } +// return bits; +//} + +} + diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/List.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/List.java new file mode 100755 index 0000000000..efc9e9af66 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/List.java @@ -0,0 +1,1429 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.*; +import org.eclipse.swt.accessibility.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class represent a selectable user interface + * object that displays a list of strings and issues notification + * when a string is selected. A list may be single or multi select. + * <p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>SINGLE, MULTI</dd> + * <dt><b>Events:</b></dt> + * <dd>Selection, DefaultSelection</dd> + * </dl> + * <p> + * Note: Only one of SINGLE and MULTI may be specified. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#list">List snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class List extends Scrollable { + NSTableColumn column; + String [] items; + int itemCount; + boolean ignoreSelect; + + static int NEXT_ID; + + static final int CELL_GAP = 1; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#SINGLE + * @see SWT#MULTI + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public List (Composite parent, int style) { + super (parent, checkStyle (style)); +} + +int /*long*/ accessibilityAttributeValue (int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + + if (accessible != null) { + NSString attribute = new NSString(arg0); + id returnValue = accessible.internal_accessibilityAttributeValue(attribute, ACC.CHILDID_SELF); + if (returnValue != null) return returnValue.id; + } + + NSString attributeName = new NSString(arg0); + + // Accessibility Verifier queries for a title or description. NSOutlineView doesn't + // seem to return either, so we return a default description value here. + if (attributeName.isEqualToString (OS.NSAccessibilityDescriptionAttribute)) { + return NSString.stringWith("").id; + } + +// if (attributeName.isEqualToString(OS.NSAccessibilityHeaderAttribute)) { +// /* +// * Bug in the Macintosh. Even when the header is not visible, +// * VoiceOver still reports each column header's role for every row. +// * This is confusing and overly verbose. The fix is to return +// * "no header" when the screen reader asks for the header, by +// * returning noErr without setting the event parameter. +// */ +// return 0; +// } + + return super.accessibilityAttributeValue(id, sel, arg0); +} + +/** + * Adds the argument to the end of the receiver's list. + * + * @param string the new item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #add(String,int) + */ +public void add (String string) { + checkWidget(); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if (itemCount == items.length) { + String [] newItems = new String [itemCount + 4]; + System.arraycopy (items, 0, newItems, 0, items.length); + items = newItems; + } + items [itemCount++] = string; + ((NSTableView)view).noteNumberOfRowsChanged (); + setScrollWidth(string); +} + +/** + * Adds the argument to the receiver's list at the given + * zero-relative index. + * <p> + * Note: To add an item at the end of the list, use the + * result of calling <code>getItemCount()</code> as the + * index or use <code>add(String)</code>. + * </p> + * + * @param string the new item + * @param index the index for the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #add(String) + */ +public void add (String string, int index) { + checkWidget(); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if (!(0 <= index && index <= itemCount)) error (SWT.ERROR_INVALID_RANGE); + if (itemCount == items.length) { + String [] newItems = new String [itemCount + 4]; + System.arraycopy (items, 0, newItems, 0, items.length); + items = newItems; + } + System.arraycopy (items, index, items, index + 1, itemCount++ - index); + items [index] = string; + ((NSTableView)view).noteNumberOfRowsChanged (); + if (index != itemCount) fixSelection (index, true); + setScrollWidth(string); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the user changes the receiver's selection, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * <code>widgetSelected</code> is called when the selection changes. + * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked. + * </p> + * + * @param listener the listener which should be notified when the user changes the receiver's selection + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener(listener); + addListener(SWT.Selection,typedListener); + addListener(SWT.DefaultSelection,typedListener); +} + +static int checkStyle (int style) { + return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget(); + int width = 0; + if (wHint == SWT.DEFAULT) { + NSCell cell = column.dataCell (); + Font font = this.font != null ? this.font : defaultFont (); + cell.setFont (font.handle); + for (int i = 0; i < items.length; i++) { + if (items[i] != null) { + cell.setTitle (NSString.stringWith (items[i])); + NSSize size = cell.cellSize (); + width = Math.max (width, (int)Math.ceil (size.width)); + } + } + width += CELL_GAP; + } else { + width = wHint; + } + if (width <= 0) width = DEFAULT_WIDTH; + int height = 0; + if (hHint == SWT.DEFAULT) { + int itemHeight = getItemHeight () + CELL_GAP; + height = itemCount * itemHeight; + } else { + height = hHint; + } + if (height <= 0) height = DEFAULT_HEIGHT; + Rectangle rect = computeTrim (0, 0, width, height); + return new Point (rect.width, rect.height); +} + +void createHandle () { + NSScrollView scrollWidget = (NSScrollView)new SWTScrollView().alloc(); + scrollWidget.init(); + if ((style & SWT.H_SCROLL) != 0) scrollWidget.setHasHorizontalScroller(true); + if ((style & SWT.V_SCROLL) != 0) scrollWidget.setHasVerticalScroller(true); + scrollWidget.setAutohidesScrollers(true); + scrollWidget.setBorderType((style & SWT.BORDER) != 0 ? OS.NSBezelBorder : OS.NSNoBorder); + + NSTableView widget = (NSTableView)new SWTTableView().alloc(); + widget.init(); + widget.setAllowsMultipleSelection((style & SWT.MULTI) != 0); + widget.setDataSource(widget); + widget.setHeaderView(null); + widget.setDelegate(widget); + if ((style & SWT.H_SCROLL) != 0) { + widget.setColumnAutoresizingStyle (OS.NSTableViewNoColumnAutoresizing); + } + NSSize spacing = new NSSize(); + spacing.width = spacing.height = CELL_GAP; + widget.setIntercellSpacing(spacing); + widget.setDoubleAction(OS.sel_sendDoubleSelection); + if (!hasBorder()) widget.setFocusRingType(OS.NSFocusRingTypeNone); + + column = (NSTableColumn)new NSTableColumn().alloc(); + column = column.initWithIdentifier(NSString.stringWith(String.valueOf(++NEXT_ID))); + column.setWidth(0); + widget.addTableColumn (column); + + scrollView = scrollWidget; + view = widget; +} + +void createWidget () { + super.createWidget (); + items = new String [4]; +} + +Color defaultBackground () { + return display.getWidgetColor (SWT.COLOR_LIST_BACKGROUND); +} + +NSFont defaultNSFont () { + return display.tableViewFont; +} + +Color defaultForeground () { + return display.getWidgetColor (SWT.COLOR_LIST_FOREGROUND); +} + +/** + * Deselects the item at the given zero-relative index in the receiver. + * If the item at the index was already deselected, it remains + * deselected. Indices that are out of range are ignored. + * + * @param index the index of the item to deselect + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void deselect (int index) { + checkWidget(); + if (0 <= index && index < itemCount) { + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.deselectRow (index); + ignoreSelect = false; + } +} + +/** + * Deselects the items at the given zero-relative indices in the receiver. + * If the item at the given zero-relative index in the receiver + * is selected, it is deselected. If the item at the index + * was not selected, it remains deselected. The range of the + * indices is inclusive. Indices that are out of range are ignored. + * + * @param start the start index of the items to deselect + * @param end the end index of the items to deselect + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void deselect (int start, int end) { + checkWidget(); + if (start > end) return; + if (end < 0 || start >= itemCount) return; + start = Math.max (0, start); + end = Math.min (itemCount - 1, end); + if (start == 0 && end == itemCount - 1) { + deselectAll (); + } else { + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + for (int i=start; i<=end; i++) { + widget.deselectRow (i); + } + ignoreSelect = false; + } +} + +/** + * Deselects the items at the given zero-relative indices in the receiver. + * If the item at the given zero-relative index in the receiver + * is selected, it is deselected. If the item at the index + * was not selected, it remains deselected. Indices that are out + * of range and duplicate indices are ignored. + * + * @param indices the array of indices for the items to deselect + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the set of indices is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void deselect (int [] indices) { + checkWidget(); + if (indices == null) error (SWT.ERROR_NULL_ARGUMENT); + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + for (int i=0; i<indices.length; i++) { + widget.deselectRow (indices [i]); + } + ignoreSelect = false; +} + +/** + * Deselects all selected items in the receiver. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void deselectAll () { + checkWidget (); + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.deselectAll(null); + ignoreSelect = false; +} + +boolean dragDetect(int x, int y, boolean filter, boolean[] consume) { + NSTableView widget = (NSTableView)view; + NSPoint pt = new NSPoint(); + pt.x = x; + pt.y = y; + int /*long*/ row = widget.rowAtPoint(pt); + if (row == -1) return false; + boolean dragging = super.dragDetect(x, y, filter, consume); + if (dragging) { + if (!widget.isRowSelected(row)) { + //TODO expand current selection when Shift, Command key pressed?? + NSIndexSet set = (NSIndexSet)new NSIndexSet().alloc(); + set = set.initWithIndex(row); + widget.selectRowIndexes (set, false); + set.release(); + } + } + consume[0] = dragging; + return dragging; +} + +void fixSelection (int index, boolean add) { + int [] selection = getSelectionIndices (); + if (selection.length == 0) return; + int newCount = 0; + boolean fix = false; + for (int i = 0; i < selection.length; i++) { + if (!add && selection [i] == index) { + fix = true; + } else { + int newIndex = newCount++; + selection [newIndex] = selection [i]; + if (selection [newIndex] >= index) { + selection [newIndex] += add ? 1 : -1; + fix = true; + } + } + } + if (fix) select (selection, newCount, true); +} + +/** + * Returns the zero-relative index of the item which currently + * has the focus in the receiver, or -1 if no item has focus. + * + * @return the index of the selected item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getFocusIndex () { + checkWidget(); + return (int)/*64*/((NSTableView)view).selectedRow(); +} + +/** + * Returns the item at the given, zero-relative index in the + * receiver. Throws an exception if the index is out of range. + * + * @param index the index of the item to return + * @return the item at the given index + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getItem (int index) { + checkWidget(); + if (!(0 <= index && index < itemCount)) error (SWT.ERROR_INVALID_RANGE); + return items [index]; +} + +/** + * Returns the number of items contained in the receiver. + * + * @return the number of items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getItemCount () { + checkWidget(); + return itemCount; +} + +/** + * Returns the height of the area which would be used to + * display <em>one</em> of the items in the list. + * + * @return the height of one item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getItemHeight () { + checkWidget (); + return (int)((NSTableView)view).rowHeight(); +} + +/** + * Returns a (possibly empty) array of <code>String</code>s which + * are the items in the receiver. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its list of items, so modifying the array will + * not affect the receiver. + * </p> + * + * @return the items in the receiver's list + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String [] getItems () { + checkWidget(); + String [] result = new String [itemCount]; + System.arraycopy (items, 0, result, 0, itemCount); + return result; +} + +/** + * Returns an array of <code>String</code>s that are currently + * selected in the receiver. The order of the items is unspecified. + * An empty array indicates that no items are selected. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its selection, so modifying the array will + * not affect the receiver. + * </p> + * @return an array representing the selection + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String [] getSelection () { + checkWidget (); + NSTableView widget = (NSTableView)view; + if (widget.numberOfSelectedRows() == 0) { + return new String [0]; + } + NSIndexSet selection = widget.selectedRowIndexes(); + int count = (int)/*64*/selection.count(); + int /*long*/ [] indexBuffer = new int /*long*/ [count]; + selection.getIndexes(indexBuffer, count, 0); + String [] result = new String [count]; + for (int i=0; i<count; i++) { + result [i] = items [(int)/*64*/indexBuffer [i]]; + } + return result; +} + +/** + * Returns the number of selected items contained in the receiver. + * + * @return the number of selected items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getSelectionCount () { + checkWidget (); + return (int)/*64*/((NSTableView)view).numberOfSelectedRows(); +} + +/** + * Returns the zero-relative index of the item which is currently + * selected in the receiver, or -1 if no item is selected. + * + * @return the index of the selected item or -1 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getSelectionIndex () { + checkWidget(); + NSTableView widget = (NSTableView)view; + if (widget.numberOfSelectedRows() == 0) { + return -1; + } + NSIndexSet selection = widget.selectedRowIndexes(); + int count = (int)/*64*/selection.count(); + int /*long*/ [] result = new int /*long*/ [count]; + selection.getIndexes(result, count, 0); + return (int)/*64*/result [0]; +} + +/** + * Returns the zero-relative indices of the items which are currently + * selected in the receiver. The order of the indices is unspecified. + * The array is empty if no items are selected. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its selection, so modifying the array will + * not affect the receiver. + * </p> + * @return the array of indices of the selected items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int [] getSelectionIndices () { + checkWidget (); + NSTableView widget = (NSTableView)view; + if (widget.numberOfSelectedRows() == 0) { + return new int [0]; + } + NSIndexSet selection = widget.selectedRowIndexes(); + int count = (int)/*64*/selection.count(); + int /*long*/ [] indices = new int /*long*/ [count]; + selection.getIndexes(indices, count, 0); + int [] result = new int [count]; + for (int i = 0; i < result.length; i++) { + result [i] = (int)/*64*/indices [i]; + } + return result; +} + +/** + * Returns the zero-relative index of the item which is currently + * at the top of the receiver. This index can change when items are + * scrolled or new items are added or removed. + * + * @return the index of the top item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getTopIndex () { + checkWidget(); + //TODO - partial item at the top + NSRect rect = scrollView.documentVisibleRect(); + NSPoint point = new NSPoint(); + point.x = rect.x; + point.y = rect.y; + int result = (int)/*64*/((NSTableView)view).rowAtPoint(point); + if (result == -1) result = 0; + return result; +} + +/** + * Gets the index of an item. + * <p> + * The list is searched starting at 0 until an + * item is found that is equal to the search item. + * If no item is found, -1 is returned. Indexing + * is zero based. + * + * @param string the search item + * @return the index of the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int indexOf (String item) { + checkWidget(); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + for (int i=0; i<itemCount; i++) { + if (items [i].equals (item)) return i; + } + return -1; +} + +/** + * Searches the receiver's list starting at the given, + * zero-relative index until an item is found that is equal + * to the argument, and returns the index of that item. If + * no item is found or the starting index is out of range, + * returns -1. + * + * @param string the search item + * @param start the zero-relative index at which to start the search + * @return the index of the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int indexOf (String string, int start) { + checkWidget(); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + for (int i=start; i<itemCount; i++) { + if (items [i].equals (string)) return i; + } + return -1; +} + +/** + * Returns <code>true</code> if the item is selected, + * and <code>false</code> otherwise. Indices out of + * range are ignored. + * + * @param index the index of the item + * @return the selection state of the item at the index + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean isSelected (int index) { + checkWidget(); + if (!(0 <= index && index < itemCount)) return false; + return ((NSTableView)view).isRowSelected(index); +} + +/* + * Feature in Cocoa: Table views do not change the selection when the user + * right-clicks or control-clicks on an NSTableView or its subclasses. Fix is to select the + * clicked-on row ourselves. + */ +int /*long*/ menuForEvent(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + NSEvent event = new NSEvent(theEvent); + NSTableView table = (NSTableView)view; + + // get the current selections for the outline view. + NSIndexSet selectedRowIndexes = table.selectedRowIndexes(); + + // select the row that was clicked before showing the menu for the event + NSPoint mousePoint = view.convertPoint_fromView_(event.locationInWindow(), null); + int /*long*/ row = table.rowAtPoint(mousePoint); + + // figure out if the row that was just clicked on is currently selected + if (selectedRowIndexes.containsIndex(row) == false) { + NSIndexSet set = (NSIndexSet)new NSIndexSet().alloc(); + set = set.initWithIndex(row); + table.selectRowIndexes (set, false); + set.release(); + } + // else that row is currently selected, so don't change anything. + + return super.menuForEvent(id, sel, theEvent); +} + +int /*long*/ numberOfRowsInTableView(int /*long*/ id, int /*long*/ sel, int /*long*/ aTableView) { + return itemCount; +} + +void releaseHandle () { + super.releaseHandle (); + if (column != null) column.release(); + column = null; +} + +void releaseWidget () { + super.releaseWidget (); + items = null; +} + +/** + * Removes the item from the receiver at the given + * zero-relative index. + * + * @param index the index for the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void remove (int index) { + checkWidget(); + if (!(0 <= index && index < itemCount)) error (SWT.ERROR_INVALID_RANGE); + remove(index, true); +} + +void remove (int index, boolean fixScroll) { + if (index != itemCount - 1) fixSelection (index, false); + System.arraycopy (items, index + 1, items, index, --itemCount - index); + items [itemCount] = null; + ((NSTableView)view).noteNumberOfRowsChanged(); + if (fixScroll) setScrollWidth(); +} + +/** + * Removes the items from the receiver which are + * between the given zero-relative start and end + * indices (inclusive). + * + * @param start the start of the range + * @param end the end of the range + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void remove (int start, int end) { + checkWidget(); + if (start > end) return; + if (!(0 <= start && start <= end && end < itemCount)) { + error (SWT.ERROR_INVALID_RANGE); + } + int length = end - start + 1; + for (int i=0; i<length; i++) remove (start, false); + setScrollWidth(); +} + +/** + * Searches the receiver's list starting at the first item + * until an item is found that is equal to the argument, + * and removes that item from the list. + * + * @param string the item to remove + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the string is not found in the list</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void remove (String string) { + checkWidget(); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + int index = indexOf (string, 0); + if (index == -1) error (SWT.ERROR_INVALID_ARGUMENT); + remove (index); +} + +/** + * Removes the items from the receiver at the given + * zero-relative indices. + * + * @param indices the array of indices of the items + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void remove (int [] indices) { + checkWidget (); + if (indices == null) error (SWT.ERROR_NULL_ARGUMENT); + if (indices.length == 0) return; + int [] newIndices = new int [indices.length]; + System.arraycopy (indices, 0, newIndices, 0, indices.length); + sort (newIndices); + int start = newIndices [newIndices.length - 1], end = newIndices [0]; + int count = getItemCount (); + if (!(0 <= start && start <= end && end < count)) { + error (SWT.ERROR_INVALID_RANGE); + } + int last = -1; + for (int i=0; i<newIndices.length; i++) { + int index = newIndices [i]; + if (index != last) { + remove (index, false); + last = index; + } + } + setScrollWidth(); +} + +/** + * Removes all of the items from the receiver. + * <p> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void removeAll () { + checkWidget(); + items = new String [4]; + itemCount = 0; + ((NSTableView)view).noteNumberOfRowsChanged(); + setScrollWidth(); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the user changes the receiver's selection. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook(SWT.Selection, listener); + eventTable.unhook(SWT.DefaultSelection,listener); +} + +/** + * Selects the item at the given zero-relative index in the receiver's + * list. If the item at the index was already selected, it remains + * selected. Indices that are out of range are ignored. + * + * @param index the index of the item to select + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void select (int index) { + checkWidget(); + if (0 <= index && index < itemCount) { + NSIndexSet set = (NSIndexSet)new NSIndexSet().alloc(); + set = set.initWithIndex(index); + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.selectRowIndexes(set, (style & SWT.MULTI) != 0); + ignoreSelect = false; + set.release(); + } +} + +/** + * Selects the items in the range specified by the given zero-relative + * indices in the receiver. The range of indices is inclusive. + * The current selection is not cleared before the new items are selected. + * <p> + * If an item in the given range is not selected, it is selected. + * If an item in the given range was already selected, it remains selected. + * Indices that are out of range are ignored and no items will be selected + * if start is greater than end. + * If the receiver is single-select and there is more than one item in the + * given range, then all indices are ignored. + * + * @param start the start of the range + * @param end the end of the range + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see List#setSelection(int,int) + */ +public void select (int start, int end) { + checkWidget (); + if (end < 0 || start > end || ((style & SWT.SINGLE) != 0 && start != end)) return; + if (itemCount == 0 || start >= itemCount) return; + if (start == 0 && end == itemCount - 1) { + selectAll (); + } else { + start = Math.max (0, start); + end = Math.min (end, itemCount - 1); + NSRange range = new NSRange(); + range.location = start; + range.length = end - start + 1; + NSIndexSet set = (NSIndexSet)new NSIndexSet().alloc(); + set = set.initWithIndexesInRange(range); + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.selectRowIndexes(set, (style & SWT.MULTI) != 0); + ignoreSelect = false; + set.release(); + } +} + +/** + * Selects the items at the given zero-relative indices in the receiver. + * The current selection is not cleared before the new items are selected. + * <p> + * If the item at a given index is not selected, it is selected. + * If the item at a given index was already selected, it remains selected. + * Indices that are out of range and duplicate indices are ignored. + * If the receiver is single-select and multiple indices are specified, + * then all indices are ignored. + * + * @param indices the array of indices for the items to select + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see List#setSelection(int[]) + */ +public void select (int [] indices) { + checkWidget (); + if (indices == null) error (SWT.ERROR_NULL_ARGUMENT); + int length = indices.length; + if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return; + int count = 0; + NSMutableIndexSet set = (NSMutableIndexSet)new NSMutableIndexSet().alloc().init(); + for (int i=0; i<length; i++) { + int index = indices [i]; + if (index >= 0 && index < itemCount) { + set.addIndex (indices [i]); + count++; + } + } + if (count > 0) { + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.selectRowIndexes(set, (style & SWT.MULTI) != 0); + ignoreSelect = false; + } + set.release(); +} + +void select (int [] indices, int count, boolean clear) { + NSMutableIndexSet set = (NSMutableIndexSet)new NSMutableIndexSet().alloc().init(); + for (int i=0; i<count; i++) set.addIndex (indices [i]); + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.selectRowIndexes(set, !clear); + ignoreSelect = false; + set.release(); +} + +/** + * Selects all of the items in the receiver. + * <p> + * If the receiver is single-select, do nothing. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void selectAll () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) return; + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.selectAll(null); + ignoreSelect = false; +} + +void sendDoubleSelection() { + if (((NSTableView)view).clickedRow () != -1) { + postEvent (SWT.DefaultSelection); + } +} + +boolean sendKeyEvent (NSEvent nsEvent, int type) { + boolean result = super.sendKeyEvent (nsEvent, type); + if (!result) return result; + if (type != SWT.KeyDown) return result; + short keyCode = nsEvent.keyCode (); + switch (keyCode) { + case 76: /* KP Enter */ + case 36: { /* Return */ + postEvent (SWT.DefaultSelection); + break; + } + } + return result; +} + +void updateBackground () { + NSColor nsColor = null; + if (backgroundImage != null) { + nsColor = NSColor.colorWithPatternImage(backgroundImage.handle); + } else if (background != null) { + nsColor = NSColor.colorWithDeviceRed(background[0], background[1], background[2], background[3]); + } + ((NSTableView) view).setBackgroundColor (nsColor); +} + +void setFont (NSFont font) { + super.setFont (font); + float /*double*/ ascent = font.ascender (); + float /*double*/ descent = -font.descender () + font.leading (); + ((NSTableView)view).setRowHeight ((int)Math.ceil (ascent + descent) + 1); + setScrollWidth(); +} + +/** + * Sets the text of the item in the receiver's list at the given + * zero-relative index to the string argument. + * + * @param index the index for the item + * @param string the new text for the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setItem (int index, String string) { + checkWidget(); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if (!(0 <= index && index < itemCount)) error (SWT.ERROR_INVALID_RANGE); + items [index] = string; + NSTableView tableView = (NSTableView)view; + NSRect rect = tableView.rectOfRow (index); + tableView.setNeedsDisplayInRect (rect); + setScrollWidth(string); +} + +/** + * Sets the receiver's items to be the given array of items. + * + * @param items the array of items + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the items array is null</li> + * <li>ERROR_INVALID_ARGUMENT - if an item in the items array is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setItems (String [] items) { + checkWidget(); + if (items == null) error (SWT.ERROR_NULL_ARGUMENT); + for (int i=0; i<items.length; i++) { + if (items [i] == null) error (SWT.ERROR_INVALID_ARGUMENT); + } + this.items = new String [items.length]; + System.arraycopy (items, 0, this.items, 0, items.length); + itemCount = items.length; + ((NSTableView)view).reloadData(); + setScrollWidth(); +} + +boolean setScrollWidth (String item) { + if ((style & SWT.H_SCROLL) == 0) return false; + NSCell cell = column.dataCell (); + Font font = this.font != null ? this.font : defaultFont (); + cell.setFont (font.handle); + cell.setTitle (NSString.stringWith (item)); + NSSize size = cell.cellSize (); + float /*double*/ oldWidth = column.width (); + if (oldWidth < size.width) { + column.setWidth (size.width); + return true; + } + return false; +} + +boolean setScrollWidth () { + if ((style & SWT.H_SCROLL) == 0) return false; + if (items == null) return false; + NSCell cell = column.dataCell (); + Font font = this.font != null ? this.font : defaultFont (); + cell.setFont (font.handle); + float /*double*/ width = 0; + for (int i = 0; i < itemCount; i++) { + cell.setTitle (NSString.stringWith (items[i])); + NSSize size = cell.cellSize (); + width = Math.max (width, size.width); + } + column.setWidth (width); + return true; +} + +/** + * Selects the item at the given zero-relative index in the receiver. + * If the item at the index was already selected, it remains selected. + * The current selection is first cleared, then the new item is selected. + * Indices that are out of range are ignored. + * + * @param index the index of the item to select + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * @see List#deselectAll() + * @see List#select(int) + */ +public void setSelection (int index) { + checkWidget(); + deselectAll (); + if (0 <= index && index < itemCount) { + NSIndexSet set = (NSIndexSet)new NSIndexSet().alloc(); + set = set.initWithIndex(index); + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.selectRowIndexes(set, false); + ignoreSelect = false; + set.release(); + showIndex (index); + } +} + +/** + * Selects the items in the range specified by the given zero-relative + * indices in the receiver. The range of indices is inclusive. + * The current selection is cleared before the new items are selected. + * <p> + * Indices that are out of range are ignored and no items will be selected + * if start is greater than end. + * If the receiver is single-select and there is more than one item in the + * given range, then all indices are ignored. + * + * @param start the start index of the items to select + * @param end the end index of the items to select + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see List#deselectAll() + * @see List#select(int,int) + */ +public void setSelection (int start, int end) { + checkWidget (); + deselectAll (); + if (end < 0 || start > end || ((style & SWT.SINGLE) != 0 && start != end)) return; + if (itemCount == 0 || start >= itemCount) return; + start = Math.max (0, start); + end = Math.min (end, itemCount - 1); + NSRange range = new NSRange(); + range.location = start; + range.length = end - start + 1; + NSIndexSet set = (NSIndexSet)new NSIndexSet().alloc(); + set = set.initWithIndexesInRange(range); + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.selectRowIndexes(set, false); + ignoreSelect = false; + set.release(); + showIndex(end); +} + +/** + * Selects the items at the given zero-relative indices in the receiver. + * The current selection is cleared before the new items are selected. + * <p> + * Indices that are out of range and duplicate indices are ignored. + * If the receiver is single-select and multiple indices are specified, + * then all indices are ignored. + * + * @param indices the indices of the items to select + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see List#deselectAll() + * @see List#select(int[]) + */ +public void setSelection (int [] indices) { + checkWidget (); + if (indices == null) error (SWT.ERROR_NULL_ARGUMENT); + deselectAll (); + int length = indices.length; + if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return; + int [] newIndices = new int [length]; + int count = 0; + for (int i=0; i<length; i++) { + int index = indices [length - i - 1]; + if (index >= 0 && index < itemCount) { + newIndices [count++] = index; + } + } + if (count > 0) { + select (newIndices, count, true); + showIndex (newIndices [0]); + } +} + +/** + * Sets the receiver's selection to be the given array of items. + * The current selection is cleared before the new items are selected. + * <p> + * Items that are not in the receiver are ignored. + * If the receiver is single-select and multiple items are specified, + * then all items are ignored. + * + * @param items the array of items + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the array of items is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see List#deselectAll() + * @see List#select(int[]) + * @see List#setSelection(int[]) + */ +public void setSelection (String [] items) { + checkWidget (); + if (items == null) error (SWT.ERROR_NULL_ARGUMENT); + deselectAll (); + int length = items.length; + if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return; + int count = 0; + int [] indices = new int [length]; + for (int i=0; i<length; i++) { + String string = items [length - i - 1]; + if ((style & SWT.SINGLE) != 0) { + int index = indexOf (string, 0); + if (index != -1) { + count = 1; + indices = new int [] {index}; + } + } else { + int index = 0; + while ((index = indexOf (string, index)) != -1) { + if (count == indices.length) { + int [] newIds = new int [indices.length + 4]; + System.arraycopy (indices, 0, newIds, 0, indices.length); + indices = newIds; + } + indices [count++] = index; + index++; + } + } + } + if (count > 0) { + select (indices, count, true); + showIndex (indices [0]); + } +} + +/** + * Sets the zero-relative index of the item which is currently + * at the top of the receiver. This index can change when items + * are scrolled or new items are added and removed. + * + * @param index the index of the top item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setTopIndex (int index) { + checkWidget(); + NSTableView widget = (NSTableView) view; + int row = Math.max(0, Math.min(index, itemCount)); + NSPoint pt = new NSPoint(); + pt.x = scrollView.contentView().bounds().x; + pt.y = widget.frameOfCellAtColumn(0, row).y; + view.scrollPoint(pt); +} + +void showIndex (int index) { + if (0 <= index && index < itemCount) { + ((NSTableView)view).scrollRowToVisible(index); + } +} + +/** + * Shows the selection. If the selection is already showing in the receiver, + * this method simply returns. Otherwise, the items are scrolled until + * the selection is visible. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void showSelection () { + checkWidget(); + int index = getSelectionIndex (); + if (index >= 0) showIndex (index); +} + +void tableViewSelectionDidChange (int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { + if (ignoreSelect) return; + postEvent (SWT.Selection); +} + +boolean tableView_shouldEditTableColumn_row(int /*long*/ id, int /*long*/ sel, int /*long*/ aTableView, int /*long*/ aTableColumn, int /*long*/ rowIndex) { + return false; +} + +int /*long*/ tableView_objectValueForTableColumn_row(int /*long*/ id, int /*long*/ sel, int /*long*/ aTableView, int /*long*/ aTableColumn, int /*long*/ rowIndex) { + NSAttributedString attribStr = createString(items[(int)/*64*/rowIndex], null, foreground, 0, true, false); + attribStr.autorelease(); + return attribStr.id; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Menu.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Menu.java new file mode 100755 index 0000000000..62934535e4 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Menu.java @@ -0,0 +1,926 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; + +/** + * Instances of this class are user interface objects that contain + * menu items. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>BAR, DROP_DOWN, POP_UP, NO_RADIO_GROUP</dd> + * <dd>LEFT_TO_RIGHT, RIGHT_TO_LEFT</dd> + * <dt><b>Events:</b></dt> + * <dd>Help, Hide, Show </dd> + * </dl> + * <p> + * Note: Only one of BAR, DROP_DOWN and POP_UP may be specified. + * Only one of LEFT_TO_RIGHT or RIGHT_TO_LEFT may be specified. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#menu">Menu snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Menu extends Widget { + /** + * the handle to the OS resource + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + NSMenu nsMenu; + int x, y, itemCount; + boolean hasLocation, visible; + MenuItem [] items; + MenuItem cascade, defaultItem; + Decorations parent; + +/** + * Constructs a new instance of this class given its parent, + * and sets the style for the instance so that the instance + * will be a popup menu on the given parent's shell. + * <p> + * After constructing a menu, it can be set into its parent + * using <code>parent.setMenu(menu)</code>. In this case, the parent may + * be any control in the same widget tree as the parent. + * </p> + * + * @param parent a control which will be the parent of the new instance (cannot be null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#POP_UP + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Menu (Control parent) { + this (checkNull (parent).menuShell (), SWT.POP_UP); +} + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>Decorations</code>) and a style value + * describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p><p> + * After constructing a menu or menuBar, it can be set into its parent + * using <code>parent.setMenu(menu)</code> or <code>parent.setMenuBar(menuBar)</code>. + * </p> + * + * @param parent a decorations control which will be the parent of the new instance (cannot be null) + * @param style the style of menu to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#BAR + * @see SWT#DROP_DOWN + * @see SWT#POP_UP + * @see SWT#NO_RADIO_GROUP + * @see SWT#LEFT_TO_RIGHT + * @see SWT#RIGHT_TO_LEFT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Menu (Decorations parent, int style) { + super (parent, checkStyle (style)); + this.parent = parent; + createWidget (); +} + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>Menu</code>) and sets the style + * for the instance so that the instance will be a drop-down + * menu on the given parent's parent. + * <p> + * After constructing a drop-down menu, it can be set into its parentMenu + * using <code>parentMenu.setMenu(menu)</code>. + * </p> + * + * @param parentMenu a menu which will be the parent of the new instance (cannot be null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#DROP_DOWN + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Menu (Menu parentMenu) { + this (checkNull (parentMenu).parent, SWT.DROP_DOWN); +} + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>MenuItem</code>) and sets the style + * for the instance so that the instance will be a drop-down + * menu on the given parent's parent menu. + * <p> + * After constructing a drop-down menu, it can be set into its parentItem + * using <code>parentItem.setMenu(menu)</code>. + * </p> + * + * @param parentItem a menu item which will be the parent of the new instance (cannot be null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#DROP_DOWN + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Menu (MenuItem parentItem) { + this (checkNull (parentItem).parent); +} + +static Control checkNull (Control control) { + if (control == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return control; +} + +static Menu checkNull (Menu menu) { + if (menu == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return menu; +} + +static MenuItem checkNull (MenuItem item) { + if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return item; +} + +static int checkStyle (int style) { + return checkBits (style, SWT.POP_UP, SWT.BAR, SWT.DROP_DOWN, 0, 0, 0); +} + +void _setVisible (boolean visible) { + if ((style & (SWT.BAR | SWT.DROP_DOWN)) != 0) return; + TrayItem trayItem = display.currentTrayItem; + if (trayItem != null && visible) { + trayItem.showMenu (this); + return; + } + if (visible) { + Shell shell = getShell (); + NSWindow window = shell.window; + NSPoint location = null; + if (hasLocation) { + NSView topView = window.contentView(); + Point shellCoord = display.map(null, shell, new Point(x,y)); + location = new NSPoint (); + location.x = shellCoord.x; + location.y = topView.frame().height - shellCoord.y; + } else { + location = window.mouseLocationOutsideOfEventStream(); + } + + // Hold on to window in case it is disposed while the popup is open. + window.retain(); + NSEvent nsEvent = NSEvent.otherEventWithType(OS.NSApplicationDefined, location, 0, 0.0, window.windowNumber(), window.graphicsContext(), (short)0, 0, 0); + NSMenu.popUpContextMenu(nsMenu, nsEvent, shell.view); + window.release(); + } else { + nsMenu.cancelTracking (); + } +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when help events are generated for the control, + * by sending it one of the messages defined in the + * <code>HelpListener</code> interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see HelpListener + * @see #removeHelpListener + */ +public void addHelpListener (HelpListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Help, typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when menus are hidden or shown, by sending it + * one of the messages defined in the <code>MenuListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see MenuListener + * @see #removeMenuListener + */ +public void addMenuListener (MenuListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Hide,typedListener); + addListener (SWT.Show,typedListener); +} + +void createHandle () { + display.addMenu (this); + NSMenu widget = (NSMenu)new SWTMenu().alloc(); + widget = widget.initWithTitle(NSString.stringWith("")); + widget.setAutoenablesItems(false); + widget.setDelegate(widget); + nsMenu = widget; +} + +void createItem (MenuItem item, int index) { + if (!(0 <= index && index <= itemCount)) error (SWT.ERROR_INVALID_RANGE); + NSMenuItem nsItem = null; + if ((item.style & SWT.SEPARATOR) != 0) { + nsItem = NSMenuItem.separatorItem(); + nsItem.retain(); + } else { + nsItem = (NSMenuItem)new SWTMenuItem().alloc(); + nsItem.initWithTitle(NSString.stringWith(""), 0, NSString.stringWith("")); + nsItem.setTarget(nsItem); + nsItem.setAction(OS.sel_sendSelection); + } + item.nsItem = nsItem; + item.createJNIRef(); + item.register(); + nsMenu.insertItem(nsItem, index); + if (itemCount == items.length) { + MenuItem [] newItems = new MenuItem [items.length + 4]; + System.arraycopy (items, 0, newItems, 0, items.length); + items = newItems; + } + System.arraycopy (items, index, items, index + 1, itemCount++ - index); + items [index] = item; + NSMenu emptyMenu = item.createEmptyMenu (); + if (emptyMenu != null) { + nsItem.setSubmenu (emptyMenu); + emptyMenu.release(); + } + if (display.menuBar == this) { + NSApplication application = display.application; + NSMenu menubar = application.mainMenu(); + if (menubar != null) { + nsItem.setMenu(null); + menubar.insertItem(nsItem, index + 1); + } + } + //TODO - find a way to disable the menu instead of each item + if (!getEnabled ()) nsItem.setEnabled (false); +} + +void createWidget () { + checkOrientation (parent); + super.createWidget (); + items = new MenuItem [4]; +} + +void deregister () { + super.deregister (); + display.removeWidget (nsMenu); +} + +void destroyItem (MenuItem item) { + int index = 0; + while (index < itemCount) { + if (items [index] == item) break; + index++; + } + if (index == itemCount) return; + System.arraycopy (items, index + 1, items, index, --itemCount - index); + items [itemCount] = null; + if (itemCount == 0) items = new MenuItem [4]; + nsMenu.removeItem (item.nsItem); + if (display.menuBar == this) { + NSApplication application = display.application; + NSMenu menubar = application.mainMenu(); + if (menubar != null) { + NSMenuItem nsItem = item.nsItem; + menubar.removeItem(nsItem); + } + } +} + +void fixMenus (Decorations newParent) { + this.parent = newParent; +} + +/** + * Returns the default menu item or null if none has + * been previously set. + * + * @return the default menu item. + * + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public MenuItem getDefaultItem () { + checkWidget(); + return defaultItem; +} + +/** + * Returns <code>true</code> if the receiver is enabled, and + * <code>false</code> otherwise. A disabled menu is typically + * not selectable from the user interface and draws with an + * inactive or "grayed" look. + * + * @return the receiver's enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #isEnabled + */ +public boolean getEnabled () { + checkWidget(); + return (state & DISABLED) == 0; +} + +/** + * Returns the item at the given, zero-relative index in the + * receiver. Throws an exception if the index is out of range. + * + * @param index the index of the item to return + * @return the item at the given index + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public MenuItem getItem (int index) { + checkWidget (); + if (!(0 <= index && index < itemCount)) error (SWT.ERROR_INVALID_RANGE); + return items [index]; +} + +/** + * Returns the number of items contained in the receiver. + * + * @return the number of items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getItemCount () { + checkWidget (); + return itemCount; +} + +/** + * Returns a (possibly empty) array of <code>MenuItem</code>s which + * are the items in the receiver. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its list of items, so modifying the array will + * not affect the receiver. + * </p> + * + * @return the items in the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public MenuItem [] getItems () { + checkWidget (); + MenuItem [] result = new MenuItem [itemCount]; + int index = 0; + if (items != null) { + for (int i = 0; i < itemCount; i++) { + MenuItem item = items [i]; + if (item != null && !item.isDisposed ()) { + result [index++] = item; + } + } + } + if (index != result.length) { + MenuItem [] newItems = new MenuItem[index]; + System.arraycopy(result, 0, newItems, 0, index); + result = newItems; + } + return result; +} + +String getNameText () { + String result = ""; + MenuItem [] items = getItems (); + int length = items.length; + if (length > 0) { + for (int i=0; i<length-1; i++) { + result = result + items [i].getNameText() + ", "; + } + result = result + items [length-1].getNameText (); + } + return result; +} + +/** + * Returns the receiver's parent, which must be a <code>Decorations</code>. + * + * @return the receiver's parent + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Decorations getParent () { + checkWidget (); + return parent; +} + +/** + * Returns the receiver's parent item, which must be a + * <code>MenuItem</code> or null when the receiver is a + * root. + * + * @return the receiver's parent item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public MenuItem getParentItem () { + checkWidget (); + return cascade; +} + +/** + * Returns the receiver's parent item, which must be a + * <code>Menu</code> or null when the receiver is a + * root. + * + * @return the receiver's parent item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Menu getParentMenu () { + checkWidget (); + if (cascade != null) return cascade.parent; + return null; +} + +/** + * Returns the receiver's shell. For all controls other than + * shells, this simply returns the control's nearest ancestor + * shell. Shells return themselves, even if they are children + * of other shells. + * + * @return the receiver's shell + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #getParent + */ +public Shell getShell () { + checkWidget (); + return parent.getShell (); +} + +/** + * Returns <code>true</code> if the receiver is visible, and + * <code>false</code> otherwise. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, this method + * may still indicate that it is considered visible even though + * it may not actually be showing. + * </p> + * + * @return the receiver's visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getVisible () { + checkWidget (); + if ((style & SWT.BAR) != 0) { + return this == parent.menuShell ().menuBar; + } + if ((style & SWT.POP_UP) != 0) { + Menu [] popups = display.popups; + if (popups == null) return false; + for (int i=0; i<popups.length; i++) { + if (popups [i] == this) return true; + } + } + return visible; +} + +/** + * Searches the receiver's list starting at the first item + * (index 0) until an item is found that is equal to the + * argument, and returns the index of that item. If no item + * is found, returns -1. + * + * @param item the search item + * @return the index of the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int indexOf (MenuItem item) { + checkWidget (); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + for (int i=0; i<itemCount; i++) { + if (items [i] == item) return i; + } + return -1; +} + +/** + * Returns <code>true</code> if the receiver is enabled and all + * of the receiver's ancestors are enabled, and <code>false</code> + * otherwise. A disabled menu is typically not selectable from the + * user interface and draws with an inactive or "grayed" look. + * + * @return the receiver's enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #getEnabled + */ +public boolean isEnabled () { + checkWidget (); + Menu parentMenu = getParentMenu (); + if (parentMenu == null) { + return getEnabled () && parent.isEnabled (); + } + return getEnabled () && parentMenu.isEnabled (); +} + +/** + * Returns <code>true</code> if the receiver is visible and all + * of the receiver's ancestors are visible and <code>false</code> + * otherwise. + * + * @return the receiver's visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #getVisible + */ +public boolean isVisible () { + checkWidget (); + return getVisible (); +} + +void menu_willHighlightItem(int /*long*/ id, int /*long*/ sel, int /*long*/ menu, int /*long*/ itemID) { + Widget widget = display.getWidget(itemID); + if (widget instanceof MenuItem) { + MenuItem item = (MenuItem)widget; + item.sendEvent (SWT.Arm); + } +} + +void menuNeedsUpdate(int /*long*/ id, int /*long*/ sel, int /*long*/ menu) { + //This code is intentionally commented + //sendEvent (SWT.Show); +} + +void menuWillOpen(int /*long*/ id, int /*long*/ sel, int /*long*/ menu) { + visible = true; + sendEvent (SWT.Show); + for (int i=0; i<items.length; i++) { + MenuItem item = items [i]; + if (item != null) item.updateAccelerator (true); + } +} + +void menuDidClose(int /*long*/ id, int /*long*/ sel, int /*long*/ menu) { + sendEvent (SWT.Hide); + visible = false; + for (int i=0; i<items.length; i++) { + MenuItem item = items [i]; + if (item != null) item.updateAccelerator (false); + } +} + +void register () { + super.register (); + display.addWidget (nsMenu, this); +} + +void releaseChildren (boolean destroy) { + if (items != null) { + for (int i=0; i<items.length; i++) { + MenuItem item = items [i]; + if (item != null && !item.isDisposed ()) { + item.release (false); + } + } + items = null; + } + super.releaseChildren (destroy); +} + +void releaseHandle () { + super.releaseHandle (); + if (nsMenu != null) nsMenu.release(); + nsMenu = null; +} + +void releaseParent () { + super.releaseParent (); + if (cascade != null) cascade.setMenu (null); + if ((style & SWT.BAR) != 0 && this == parent.menuBar) { + parent.setMenuBar (null); + } +} + +void releaseWidget () { + super.releaseWidget (); + display.removeMenu (this); + parent = null; + cascade = defaultItem = null; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the help events are generated for the control. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see HelpListener + * @see #addHelpListener + */ +public void removeHelpListener (HelpListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Help, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the menu events are generated for the control. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see MenuListener + * @see #addMenuListener + */ +public void removeMenuListener (MenuListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Hide, listener); + eventTable.unhook (SWT.Show, listener); +} + +/** + * Sets the default menu item to the argument or removes + * the default emphasis when the argument is <code>null</code>. + * + * @param item the default menu item or null + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the menu item has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setDefaultItem (MenuItem item) { + checkWidget(); + if (item != null && item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + defaultItem = item; +} + +/** + * Enables the receiver if the argument is <code>true</code>, + * and disables it otherwise. A disabled menu is typically + * not selectable from the user interface and draws with an + * inactive or "grayed" look. + * + * @param enabled the new enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setEnabled (boolean enabled) { + checkWidget(); + if (enabled) { + state &= ~DISABLED; + } else { + state |= DISABLED; + } + //TODO - find a way to disable the menu instead of each item + for (int i=0; i<items.length; i++) { + MenuItem item = items [i]; + if (item != null) { + /* + * Feature in the Macintosh. When a cascade menu + * item is disabled, rather than disabling the item, + * the submenu is disabled. + * + * There is no fix for this at this time. + */ + item.nsItem.setEnabled (enabled && item.getEnabled ()); + } + } +} + +/** + * Sets the location of the receiver, which must be a popup, + * to the point specified by the arguments which are relative + * to the display. + * <p> + * Note that this is different from most widgets where the + * location of the widget is relative to the parent. + * </p><p> + * Note that the platform window manager ultimately has control + * over the location of popup menus. + * </p> + * + * @param x the new x coordinate for the receiver + * @param y the new y coordinate for the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setLocation (int x, int y) { + checkWidget (); + this.x = x; + this.y = y; + hasLocation = true; +} + +/** + * Sets the location of the receiver, which must be a popup, + * to the point specified by the argument which is relative + * to the display. + * <p> + * Note that this is different from most widgets where the + * location of the widget is relative to the parent. + * </p><p> + * Note that the platform window manager ultimately has control + * over the location of popup menus. + * </p> + * + * @param location the new location for the receiver + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the point is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.1 + */ +public void setLocation (Point location) { + checkWidget (); + if (location == null) error (SWT.ERROR_NULL_ARGUMENT); + setLocation (location.x, location.y); +} + +/** + * Marks the receiver as visible if the argument is <code>true</code>, + * and marks it invisible otherwise. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, marking + * it visible may not actually cause it to be displayed. + * </p> + * + * @param visible the new visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setVisible (boolean visible) { + checkWidget (); + if ((style & (SWT.BAR | SWT.DROP_DOWN)) != 0) return; + if (visible) { + display.addPopup (this); + } else { + display.removePopup (this); + } +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/MenuItem.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/MenuItem.java new file mode 100755 index 0000000000..d27761b4f8 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/MenuItem.java @@ -0,0 +1,850 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * Instances of this class represent a selectable user interface object + * that issues notification when pressed and released. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>CHECK, CASCADE, PUSH, RADIO, SEPARATOR</dd> + * <dt><b>Events:</b></dt> + * <dd>Arm, Help, Selection</dd> + * </dl> + * <p> + * Note: Only one of the styles CHECK, CASCADE, PUSH, RADIO and SEPARATOR + * may be specified. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class MenuItem extends Item { + NSMenuItem nsItem; + Menu parent, menu; + int accelerator; + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>Menu</code>) and a style value + * describing its behavior and appearance. The item is added + * to the end of the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a menu control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#CHECK + * @see SWT#CASCADE + * @see SWT#PUSH + * @see SWT#RADIO + * @see SWT#SEPARATOR + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public MenuItem (Menu parent, int style) { + super (parent, checkStyle (style)); + this.parent = parent; + parent.createItem (this, parent.getItemCount ()); +} + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>Menu</code>), a style value + * describing its behavior and appearance, and the index + * at which to place it in the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a menu control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * @param index the zero-relative index to store the receiver in its parent + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#CHECK + * @see SWT#CASCADE + * @see SWT#PUSH + * @see SWT#RADIO + * @see SWT#SEPARATOR + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public MenuItem (Menu parent, int style, int index) { + super (parent, checkStyle (style)); + this.parent = parent; + parent.createItem (this, index); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the arm events are generated for the control, by sending + * it one of the messages defined in the <code>ArmListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ArmListener + * @see #removeArmListener + */ +public void addArmListener (ArmListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Arm, typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the help events are generated for the control, by sending + * it one of the messages defined in the <code>HelpListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see HelpListener + * @see #removeHelpListener + */ +public void addHelpListener (HelpListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Help, typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the menu item is selected by the user, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * When <code>widgetSelected</code> is called, the stateMask field of the event object is valid. + * <code>widgetDefaultSelected</code> is not called. + * </p> + * + * @param listener the listener which should be notified when the menu item is selected by the user + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener (SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener(listener); + addListener (SWT.Selection,typedListener); + addListener (SWT.DefaultSelection,typedListener); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +static int checkStyle (int style) { + return checkBits (style, SWT.PUSH, SWT.CHECK, SWT.RADIO, SWT.SEPARATOR, SWT.CASCADE, 0); +} + +NSMenu createEmptyMenu () { + if ((parent.style & SWT.BAR) != 0) { + return (NSMenu) new SWTMenu ().alloc ().init (); + } + return null; +} + +void deregister () { + super.deregister (); + display.removeWidget (nsItem); +} + +void destroyWidget () { + parent.destroyItem (this); + releaseHandle (); +} + +/** + * Returns the widget accelerator. An accelerator is the bit-wise + * OR of zero or more modifier masks and a key. Examples: + * <code>SWT.CONTROL | SWT.SHIFT | 'T', SWT.ALT | SWT.F2</code>. + * The default value is zero, indicating that the menu item does + * not have an accelerator. + * + * @return the accelerator or 0 + * + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getAccelerator () { + checkWidget (); + return accelerator; +} + +/** + * Returns <code>true</code> if the receiver is enabled, and + * <code>false</code> otherwise. A disabled menu item is typically + * not selectable from the user interface and draws with an + * inactive or "grayed" look. + * + * @return the receiver's enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #isEnabled + */ +public boolean getEnabled () { + checkWidget(); + return (state & DISABLED) == 0; +} + +/** + * Returns the receiver's cascade menu if it has one or null + * if it does not. Only <code>CASCADE</code> menu items can have + * a pull down menu. The sequence of key strokes, button presses + * and/or button releases that are used to request a pull down + * menu is platform specific. + * + * @return the receiver's menu + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Menu getMenu () { + checkWidget (); + return menu; +} + +String getNameText () { + if ((style & SWT.SEPARATOR) != 0) return "|"; + return super.getNameText (); +} + +/** + * Returns the receiver's parent, which must be a <code>Menu</code>. + * + * @return the receiver's parent + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Menu getParent () { + checkWidget (); + return parent; +} + +/** + * Returns <code>true</code> if the receiver is selected, + * and false otherwise. + * <p> + * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>, + * it is selected when it is checked. + * + * @return the selection state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getSelection () { + checkWidget (); + if ((style & (SWT.CHECK | SWT.RADIO)) == 0) return false; + return nsItem.state() == OS.NSOnState; +} + +/** + * Returns <code>true</code> if the receiver is enabled and all + * of the receiver's ancestors are enabled, and <code>false</code> + * otherwise. A disabled menu item is typically not selectable from the + * user interface and draws with an inactive or "grayed" look. + * + * @return the receiver's enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #getEnabled + */ +public boolean isEnabled () { + return getEnabled () && parent.isEnabled (); +} + +int keyChar (int key) { + //TODO - use the NS key constants + switch (key) { + case SWT.BS: return OS.NSBackspaceCharacter; + case SWT.CR: return OS.NSCarriageReturnCharacter; + case SWT.DEL: return OS.NSDeleteCharacter; + case SWT.ESC: return SWT.ESC; + case SWT.LF: return OS.NSNewlineCharacter; + case SWT.TAB: return OS.NSTabCharacter; +// case ' ': return OS.kMenuBlankGlyph; +// case ' ': return OS.kMenuSpaceGlyph; + case SWT.ALT: return 0x2325; + case SWT.SHIFT: return 0x21E7; + case SWT.CONTROL: return 0xF2303; + case SWT.COMMAND: return 0x2318; + case SWT.ARROW_UP: return 0x2191; + case SWT.ARROW_DOWN: return 0x2193; + case SWT.ARROW_LEFT: return 0x2190; + case SWT.ARROW_RIGHT: return 0x2192; + case SWT.PAGE_UP: return 0x21DE; + case SWT.PAGE_DOWN: return 0x21DF; + case SWT.KEYPAD_CR: return OS.NSEnterCharacter; + case SWT.HELP: return OS.NSHelpFunctionKey; + case SWT.HOME: return 0xF729; + case SWT.END: return 0xF72B; +// case SWT.CAPS_LOCK: return ??; + case SWT.F1: return 0xF704; + case SWT.F2: return 0xF705; + case SWT.F3: return 0xF706; + case SWT.F4: return 0xF707; + case SWT.F5: return 0xF708; + case SWT.F6: return 0xF709; + case SWT.F7: return 0xF70A; + case SWT.F8: return 0xF70B; + case SWT.F9: return 0xF70C; + case SWT.F10: return 0xF70D; + case SWT.F11: return 0xF70E; + case SWT.F12: return 0xF70F; + case SWT.F13: return 0xF710; + case SWT.F14: return 0xF711; + case SWT.F15: return 0xF712; + /* + * The following lines are intentionally commented. + */ +// case SWT.INSERT: return ??; + } + return 0; +} + + +void register () { + super.register (); + display.addWidget (nsItem, this); +} + +void releaseHandle () { + super.releaseHandle (); + if (nsItem != null) nsItem.release(); + nsItem = null; + parent = null; +} + +void releaseChildren (boolean destroy) { + if (menu != null) { + menu.release (false); + menu = null; + } + super.releaseChildren (destroy); +} + +void releaseWidget () { + super.releaseWidget (); + accelerator = 0; + if (this == parent.defaultItem) parent.defaultItem = null; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the arm events are generated for the control. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ArmListener + * @see #addArmListener + */ +public void removeArmListener (ArmListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Arm, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the help events are generated for the control. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see HelpListener + * @see #addHelpListener + */ +public void removeHelpListener (HelpListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Help, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is selected by the user. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener (SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Selection, listener); + eventTable.unhook (SWT.DefaultSelection,listener); +} + +void selectRadio () { + int index = 0; + MenuItem [] items = parent.getItems (); + while (index < items.length && items [index] != this) index++; + int i = index - 1; + while (i >= 0 && items [i].setRadioSelection (false)) --i; + int j = index + 1; + while (j < items.length && items [j].setRadioSelection (false)) j++; + setSelection (true); +} + +void sendSelection () { + if ((style & SWT.CHECK) != 0) { + setSelection (!getSelection ()); + } else { + if ((style & SWT.RADIO) != 0) { + if ((parent.getStyle () & SWT.NO_RADIO_GROUP) != 0) { + setSelection (!getSelection ()); + } else { + selectRadio (); + } + } + } + Event event = new Event (); + NSEvent nsEvent = NSApplication.sharedApplication ().currentEvent (); + if (nsEvent != null) setInputState (event, nsEvent, 0); + postEvent (SWT.Selection, event); +} + +/** + * Sets the widget accelerator. An accelerator is the bit-wise + * OR of zero or more modifier masks and a key. Examples: + * <code>SWT.MOD1 | SWT.MOD2 | 'T', SWT.MOD3 | SWT.F2</code>. + * <code>SWT.CONTROL | SWT.SHIFT | 'T', SWT.ALT | SWT.F2</code>. + * The default value is zero, indicating that the menu item does + * not have an accelerator. + * + * @param accelerator an integer that is the bit-wise OR of masks and a key + * + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setAccelerator (int accelerator) { + checkWidget (); + if (this.accelerator == accelerator) return; + this.accelerator = accelerator; + int key = accelerator & SWT.KEY_MASK; + int virtualKey = keyChar (key); + NSString string = null; + if (virtualKey != 0) { + string = NSString.stringWith ((char)virtualKey + ""); + } else { + string = NSString.stringWith ((char)key + ""); + } + nsItem.setKeyEquivalent (string.lowercaseString()); + int mask = 0; + if ((accelerator & SWT.SHIFT) != 0) mask |= OS.NSShiftKeyMask; + if ((accelerator & SWT.CONTROL) != 0) mask |= OS.NSControlKeyMask; + if ((accelerator & SWT.COMMAND) != 0) mask |= OS.NSCommandKeyMask; + if ((accelerator & SWT.ALT) != 0) mask |= OS.NSAlternateKeyMask; + nsItem.setKeyEquivalentModifierMask (mask); +} + +/** + * Enables the receiver if the argument is <code>true</code>, + * and disables it otherwise. A disabled menu item is typically + * not selectable from the user interface and draws with an + * inactive or "grayed" look. + * + * @param enabled the new enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setEnabled (boolean enabled) { + checkWidget (); + if (enabled) { + state &= ~DISABLED; + } else { + state |= DISABLED; + } + nsItem.setEnabled(enabled); +} + +/** + * Sets the image the receiver will display to the argument. + * <p> + * Note: This operation is a hint and is not supported on + * platforms that do not have this concept (for example, Windows NT). + * Furthermore, some platforms (such as GTK), cannot display both + * a check box and an image at the same time. Instead, they hide + * the image and display the check box. + * </p> + * + * @param image the image to display + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setImage (Image image) { + checkWidget (); + if ((style & SWT.SEPARATOR) != 0) return; + super.setImage (image); + nsItem.setImage(image != null? image.handle : null); +} + +/** + * Sets the receiver's pull down menu to the argument. + * Only <code>CASCADE</code> menu items can have a + * pull down menu. The sequence of key strokes, button presses + * and/or button releases that are used to request a pull down + * menu is platform specific. + * <p> + * Note: Disposing of a menu item that has a pull down menu + * will dispose of the menu. To avoid this behavior, set the + * menu to null before the menu item is disposed. + * </p> + * + * @param menu the new pull down menu + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_MENU_NOT_DROP_DOWN - if the menu is not a drop down menu</li> + * <li>ERROR_MENUITEM_NOT_CASCADE - if the menu item is not a <code>CASCADE</code></li> + * <li>ERROR_INVALID_ARGUMENT - if the menu has been disposed</li> + * <li>ERROR_INVALID_PARENT - if the menu is not in the same widget tree</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setMenu (Menu menu) { + checkWidget (); + + /* Check to make sure the new menu is valid */ + if ((style & SWT.CASCADE) == 0) { + error (SWT.ERROR_MENUITEM_NOT_CASCADE); + } + if (menu != null) { + if (menu.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + if ((menu.style & SWT.DROP_DOWN) == 0) { + error (SWT.ERROR_MENU_NOT_DROP_DOWN); + } + if (menu.parent != parent.parent) { + error (SWT.ERROR_INVALID_PARENT); + } + } + /* Assign the new menu */ + Menu oldMenu = this.menu; + if (oldMenu == menu) return; + if (oldMenu != null) oldMenu.cascade = null; + this.menu = menu; + + /* Update the menu in the OS */ + if (menu == null) { + NSMenu emptyMenu = createEmptyMenu (); + if (emptyMenu != null) { + nsItem.setSubmenu (emptyMenu); + emptyMenu.release(); + } + } else { + menu.cascade = this; + nsItem.setSubmenu (menu.nsMenu); + } + + if (menu != null) { + nsItem.setTarget(null); + nsItem.setAction(0); + } else { + nsItem.setTarget(nsItem); + nsItem.setAction(OS.sel_sendSelection); + } + + /* Update menu title with parent item title */ + updateText (); +} + +boolean setRadioSelection (boolean value) { + if ((style & SWT.RADIO) == 0) return false; + if (getSelection () != value) { + setSelection (value); + postEvent (SWT.Selection); + } + return true; +} + +/** + * Sets the selection state of the receiver. + * <p> + * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>, + * it is selected when it is checked. + * + * @param selected the new selection state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSelection (boolean selected) { + checkWidget (); + if ((style & (SWT.CHECK | SWT.RADIO)) == 0) return; + nsItem.setState(selected ? OS.NSOnState : OS.NSOffState); +} + +/** + * Sets the receiver's text. The string may include + * the mnemonic character and accelerator text. + * <p> + * Mnemonics are indicated by an '&' that causes the next + * character to be the mnemonic. When the user presses a + * key sequence that matches the mnemonic, a selection + * event occurs. On most platforms, the mnemonic appears + * underlined but may be emphasised in a platform specific + * manner. The mnemonic indicator character '&' can be + * escaped by doubling it in the string, causing a single + * '&' to be displayed. + * </p> + * <p> + * Accelerator text is indicated by the '\t' character. + * On platforms that support accelerator text, the text + * that follows the '\t' character is displayed to the user, + * typically indicating the key stroke that will cause + * the item to become selected. On most platforms, the + * accelerator text appears right aligned in the menu. + * Setting the accelerator text does not install the + * accelerator key sequence. The accelerator key sequence + * is installed using #setAccelerator. + * </p> + * + * @param string the new text + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the text is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setAccelerator + */ +public void setText (String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if ((style & SWT.SEPARATOR) != 0) return; + if (text.equals (string)) return; + super.setText (string); + updateText (); +} + +void updateText () { + char [] buffer = new char [text.length ()]; + text.getChars (0, buffer.length, buffer, 0); + int i=0, j=0; + while (i < buffer.length) { + if (buffer [i] == '\t') break; + if ((buffer [j++] = buffer [i++]) == '&') { + if (i == buffer.length) {continue;} + if (buffer [i] == '&') {i++; continue;} + j--; + } + } + String text = new String (buffer, 0, j); + NSMenu submenu = nsItem.submenu (); + NSString label = NSString.stringWith (text); + if(submenu != null && (parent.getStyle () & SWT.BAR) != 0) { + submenu.setTitle (label); + } else { + nsItem.setTitle (label); + } +} + +void updateAccelerator (boolean show) { + if (accelerator != 0) return; + int mask = 0, key = 0; + if (show) { + char [] buffer = new char [text.length ()]; + text.getChars (0, buffer.length, buffer, 0); + int i=0, j=0; + while (i < buffer.length) { + if (buffer [i] == '\t') break; + if ((buffer [j++] = buffer [i++]) == '&') { + if (i == buffer.length) {continue;} + if (buffer [i] == '&') {i++; continue;} + j--; + } + } + if (i < buffer.length && buffer [i] == '\t') { + for (j = i + 1; j < buffer.length; j++) { + switch (buffer [j]) { + case '\u2303': mask |= OS.NSControlKeyMask; i++; break; + case '\u2325': mask |= OS.NSAlternateKeyMask; i++; break; + case '\u21E7': mask |= OS.NSShiftKeyMask; i++; break; + case '\u2318': mask |= OS.NSCommandKeyMask; i++; break; + default: + j = buffer.length; + break; + } + } + switch (buffer.length - i - 1) { + case 1: + key = buffer [i + 1]; + if (key == 0x2423) key = ' '; + break; + case 2: + if (buffer [i + 1] == 'F') { + switch (buffer [i + 2]) { + case '1': key = 0xF704; break; + case '2': key = 0xF705; break; + case '3': key = 0xF706; break; + case '4': key = 0xF707; break; + case '5': key = 0xF708; break; + case '6': key = 0xF709; break; + case '7': key = 0xF70A; break; + case '8': key = 0xF70B; break; + case '9': key = 0xF70C; break; + } + } + break; + case 3: + if (buffer [i + 1] == 'F' && buffer [i + 2] == '1') { + switch (buffer [i + 3]) { + case '0': key = 0xF70D; break; + case '1': key = 0xF70E; break; + case '2': key = 0xF70F; break; + case '3': key = 0xF710; break; + case '4': key = 0xF711; break; + case '5': key = 0xF712; break; + } + } + break; + } + } + } + NSString string = NSString.stringWith (key == 0 ? "" : String.valueOf ((char)key)); + nsItem.setKeyEquivalentModifierMask (mask); + nsItem.setKeyEquivalent (string.lowercaseString ()); +} + +} + diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/MessageBox.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/MessageBox.java new file mode 100755 index 0000000000..9d0ff92045 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/MessageBox.java @@ -0,0 +1,331 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class are used to inform or warn the user. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>ICON_ERROR, ICON_INFORMATION, ICON_QUESTION, ICON_WARNING, ICON_WORKING</dd> + * <dd>OK, OK | CANCEL</dd> + * <dd>YES | NO, YES | NO | CANCEL</dd> + * <dd>RETRY | CANCEL</dd> + * <dd>ABORT | RETRY | IGNORE</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * Note: Only one of the styles ICON_ERROR, ICON_INFORMATION, ICON_QUESTION, + * ICON_WARNING and ICON_WORKING may be specified. + * </p><p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the SWT implementation. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample, Dialog tab</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class MessageBox extends Dialog { + String message = ""; + int returnCode; + +/** + * Constructs a new instance of this class given only its parent. + * + * @param parent a shell which will be the parent of the new instance + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + */ +public MessageBox (Shell parent) { + this (parent, SWT.OK | SWT.ICON_INFORMATION | SWT.APPLICATION_MODAL); +} + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * + * @param parent a shell which will be the parent of the new instance + * @param style the style of dialog to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#ICON_ERROR + * @see SWT#ICON_INFORMATION + * @see SWT#ICON_QUESTION + * @see SWT#ICON_WARNING + * @see SWT#ICON_WORKING + * @see SWT#OK + * @see SWT#CANCEL + * @see SWT#YES + * @see SWT#NO + * @see SWT#ABORT + * @see SWT#RETRY + * @see SWT#IGNORE + */ +public MessageBox (Shell parent, int style) { + super (parent, checkStyle (parent, checkStyle (style))); + if (Display.getSheetEnabled ()) { + if (parent != null && (style & SWT.SHEET) != 0) this.style |= SWT.SHEET; + } + checkSubclass (); +} + +static int checkStyle (int style) { + int mask = (SWT.YES | SWT.NO | SWT.OK | SWT.CANCEL | SWT.ABORT | SWT.RETRY | SWT.IGNORE); + int bits = style & mask; + if (bits == SWT.OK || bits == SWT.CANCEL || bits == (SWT.OK | SWT.CANCEL)) return style; + if (bits == SWT.YES || bits == SWT.NO || bits == (SWT.YES | SWT.NO) || bits == (SWT.YES | SWT.NO | SWT.CANCEL)) return style; + if (bits == (SWT.RETRY | SWT.CANCEL) || bits == (SWT.ABORT | SWT.RETRY | SWT.IGNORE)) return style; + style = (style & ~mask) | SWT.OK; + return style; +} + +/** + * Returns the dialog's message, or an empty string if it does not have one. + * The message is a description of the purpose for which the dialog was opened. + * This message will be visible in the dialog while it is open. + * + * @return the message + */ +public String getMessage () { + return message; +} + +/** + * Makes the dialog visible and brings it to the front + * of the display. + * + * @return the ID of the button that was selected to dismiss the + * message box (e.g. SWT.OK, SWT.CANCEL, etc.) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the dialog has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the dialog</li> + * </ul> + */ +public int open () { + NSAlert alert = (NSAlert) new NSAlert().alloc().init(); + int alertType = OS.NSInformationalAlertStyle; + if ((style & SWT.ICON_ERROR) != 0) alertType = OS.NSCriticalAlertStyle; + if ((style & SWT.ICON_INFORMATION) != 0) alertType = OS.NSInformationalAlertStyle; + if ((style & SWT.ICON_QUESTION) != 0) alertType = OS.NSInformationalAlertStyle; + if ((style & SWT.ICON_WARNING) != 0) alertType = OS.NSWarningAlertStyle; + if ((style & SWT.ICON_WORKING) != 0) alertType = OS.NSInformationalAlertStyle; + alert.setAlertStyle(alertType); + + int mask = (SWT.YES | SWT.NO | SWT.OK | SWT.CANCEL | SWT.ABORT | SWT.RETRY | SWT.IGNORE); + int bits = style & mask; + NSString title; + switch (bits) { + case SWT.OK: + title = NSString.stringWith(SWT.getMessage("SWT_OK")); + alert.addButtonWithTitle(title); + break; + case SWT.CANCEL: + title = NSString.stringWith(SWT.getMessage("SWT_Cancel")); + alert.addButtonWithTitle(title); + break; + case SWT.OK | SWT.CANCEL: + title = NSString.stringWith(SWT.getMessage("SWT_OK")); + alert.addButtonWithTitle(title); + title = NSString.stringWith(SWT.getMessage("SWT_Cancel")); + alert.addButtonWithTitle(title); + break; + case SWT.YES: + title = NSString.stringWith(SWT.getMessage("SWT_Yes")); + alert.addButtonWithTitle(title); + break; + case SWT.NO: + title = NSString.stringWith(SWT.getMessage("SWT_No")); + alert.addButtonWithTitle(title); + break; + case SWT.YES | SWT.NO: + title = NSString.stringWith(SWT.getMessage("SWT_Yes")); + alert.addButtonWithTitle(title); + title = NSString.stringWith(SWT.getMessage("SWT_No")); + alert.addButtonWithTitle(title); +// no.setKeyEquivalent(NSString.stringWith("\033")); + break; + case SWT.YES | SWT.NO | SWT.CANCEL: + title = NSString.stringWith(SWT.getMessage("SWT_Yes")); + alert.addButtonWithTitle(title); + title = NSString.stringWith(SWT.getMessage("SWT_Cancel")); + alert.addButtonWithTitle(title); + title = NSString.stringWith(SWT.getMessage("SWT_No")); + alert.addButtonWithTitle(title); + break; + case SWT.RETRY | SWT.CANCEL: + title = NSString.stringWith(SWT.getMessage("SWT_Retry")); + alert.addButtonWithTitle(title); + title = NSString.stringWith(SWT.getMessage("SWT_Cancel")); + alert.addButtonWithTitle(title); + break; + case SWT.ABORT | SWT.RETRY | SWT.IGNORE: + title = NSString.stringWith(SWT.getMessage("SWT_Abort")); + alert.addButtonWithTitle(title); + title = NSString.stringWith(SWT.getMessage("SWT_Ignore")); + alert.addButtonWithTitle(title); + title = NSString.stringWith(SWT.getMessage("SWT_Retry")); + alert.addButtonWithTitle(title); + break; + } + title = NSString.stringWith(this.title != null ? this.title : ""); + alert.window().setTitle(title); + NSString message = NSString.stringWith(this.message != null ? this.message : ""); + alert.setMessageText(message); + int response = 0; + int /*long*/ jniRef = 0; + SWTPanelDelegate delegate = null; + if ((style & SWT.SHEET) != 0) { + delegate = (SWTPanelDelegate)new SWTPanelDelegate().alloc().init(); + jniRef = OS.NewGlobalRef(this); + if (jniRef == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.object_setInstanceVariable(delegate.id, Display.SWT_OBJECT, jniRef); + alert.beginSheetModalForWindow(parent.window, delegate, OS.sel_panelDidEnd_returnCode_contextInfo_, 0); + if ((style & SWT.APPLICATION_MODAL) != 0) { + response = (int)/*64*/alert.runModal(); + } else { + this.returnCode = 0; + NSWindow window = alert.window(); + NSApplication application = NSApplication.sharedApplication(); + while (window.isVisible()) application.run(); + response = this.returnCode; + } + } else { + response = (int)/*64*/alert.runModal(); + } + if (delegate != null) delegate.release(); + if (jniRef != 0) OS.DeleteGlobalRef(jniRef); + alert.release(); + switch (bits) { + case SWT.OK: + switch (response) { + case OS.NSAlertFirstButtonReturn: + return SWT.OK; + } + break; + case SWT.CANCEL: + switch (response) { + case OS.NSAlertFirstButtonReturn: + return SWT.CANCEL; + } + break; + case SWT.OK | SWT.CANCEL: + switch (response) { + case OS.NSAlertFirstButtonReturn: + return SWT.OK; + case OS.NSAlertSecondButtonReturn: + return SWT.CANCEL; + } + break; + case SWT.YES: + switch (response) { + case OS.NSAlertFirstButtonReturn: + return SWT.YES; + } + break; + case SWT.NO: + switch (response) { + case OS.NSAlertFirstButtonReturn: + return SWT.NO; + } + break; + case SWT.YES | SWT.NO: + switch (response) { + case OS.NSAlertFirstButtonReturn: + return SWT.YES; + case OS.NSAlertSecondButtonReturn: + return SWT.NO; + } + break; + case SWT.YES | SWT.NO | SWT.CANCEL: + switch (response) { + case OS.NSAlertFirstButtonReturn: + return SWT.YES; + case OS.NSAlertSecondButtonReturn: + return SWT.CANCEL; + case OS.NSAlertThirdButtonReturn: + return SWT.NO; + } + break; + case SWT.RETRY | SWT.CANCEL: + switch (response) { + case OS.NSAlertFirstButtonReturn: + return SWT.RETRY; + case OS.NSAlertSecondButtonReturn: + return SWT.CANCEL; + } + break; + case SWT.ABORT | SWT.RETRY | SWT.IGNORE: + switch (response) { + case OS.NSAlertFirstButtonReturn: + return SWT.ABORT; + case OS.NSAlertSecondButtonReturn: + return SWT.IGNORE; + case OS.NSAlertThirdButtonReturn: + return SWT.RETRY; + } + break; + } + return SWT.CANCEL; +} + +void panelDidEnd_returnCode_contextInfo(int /*long*/ id, int /*long*/ sel, int /*long*/ alert, int /*long*/ returnCode, int /*long*/ contextInfo) { + this.returnCode = (int)/*64*/returnCode; + NSApplication application = NSApplication.sharedApplication(); + application.endSheet(new NSAlert(alert).window(), returnCode); + if ((style & SWT.PRIMARY_MODAL) != 0) { + application.stop(null); + } +} + +/** + * Sets the dialog's message, which is a description of + * the purpose for which it was opened. This message will be + * visible on the dialog while it is open. + * + * @param string the message + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + */ +public void setMessage (String string) { + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + message = string; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ProgressBar.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ProgressBar.java new file mode 100755 index 0000000000..55cd72486f --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ProgressBar.java @@ -0,0 +1,330 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; + +/** + * Instances of the receiver represent an unselectable + * user interface object that is used to display progress, + * typically in the form of a bar. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>SMOOTH, HORIZONTAL, VERTICAL, INDETERMINATE</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified. + * </p><p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the SWT implementation. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#progressbar">ProgressBar snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class ProgressBar extends Control { + + NSBezierPath visiblePath; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#SMOOTH + * @see SWT#HORIZONTAL + * @see SWT#VERTICAL + * @see SWT#INDETERMINATE + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public ProgressBar (Composite parent, int style) { + super (parent, checkStyle (style)); +} + +static int checkStyle (int style) { + style |= SWT.NO_FOCUS; + return checkBits (style, SWT.HORIZONTAL, SWT.VERTICAL, 0, 0, 0, 0); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget(); + int size = OS.NSProgressIndicatorPreferredThickness; + int width = 0, height = 0; + if ((style & SWT.HORIZONTAL) != 0) { + height = size; + width = height * 10; + } else { + width = size; + height = width * 10; + } + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + return new Point (width, height); +} + +void createHandle () { + NSProgressIndicator widget = (NSProgressIndicator)new SWTProgressIndicator().alloc(); + widget.init(); + widget.setUsesThreadedAnimation(false); + widget.setIndeterminate((style & SWT.INDETERMINATE) != 0); + view = widget; +} + +NSFont defaultNSFont () { + return display.progressIndicatorFont; +} + +void _drawThemeProgressArea (int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + /* + * Bug in Cocoa. When the threaded animation is turned off by calling + * setUsesThreadedAnimation(), _drawThemeProgressArea() attempts to + * access a deallocated NSBitmapGraphicsContext when drawing a zero sized + * progress bar. The fix is to avoid calling super when the progress bar + * is zero sized. + */ + NSRect frame = view.frame(); + if (frame.width == 0 || frame.height == 0) return; + + /* + * Bug in Cocoa. When the progress bar is animating it calls + * _drawThemeProgressArea() directly without taking into account + * obscured areas. The fix is to clip the drawing to the visible + * region of the progress bar before calling super. + */ + if (visiblePath == null) { + int /*long*/ visibleRegion = getVisibleRegion(); + visiblePath = getPath(visibleRegion); + OS.DisposeRgn(visibleRegion); + } + NSGraphicsContext context = NSGraphicsContext.currentContext(); + context.saveGraphicsState(); + visiblePath.setClip(); + super._drawThemeProgressArea (id, sel, arg0); + context.restoreGraphicsState(); +} + +/** + * Returns the maximum value which the receiver will allow. + * + * @return the maximum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getMaximum () { + checkWidget(); + return (int)((NSProgressIndicator)view).maxValue(); +} + +/** + * Returns the minimum value which the receiver will allow. + * + * @return the minimum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getMinimum () { + checkWidget(); + return (int)((NSProgressIndicator)view).minValue(); +} + +/** + * Returns the single 'selection' that is the receiver's position. + * + * @return the selection + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getSelection () { + checkWidget(); + return (int)((NSProgressIndicator)view).doubleValue(); +} + +/** + * Returns the state of the receiver. The value will be one of: + * <ul> + * <li>{@link SWT#NORMAL}</li> + * <li>{@link SWT#ERROR}</li> + * <li>{@link SWT#PAUSED}</li> + * </ul> + * + * @return the state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public int getState () { + checkWidget (); + return SWT.NORMAL; +} + +/** + * Sets the maximum value that the receiver will allow. This new + * value will be ignored if it is not greater than the receiver's current + * minimum value. If the new maximum is applied then the receiver's + * selection value will be adjusted if necessary to fall within its new range. + * + * @param value the new maximum, which must be greater than the current minimum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setMaximum (int value) { + checkWidget(); + int minimum = (int)((NSProgressIndicator)view).minValue(); + if (value <= minimum) return; + ((NSProgressIndicator)view).setMaxValue(value); + int selection = (int)((NSProgressIndicator)view).doubleValue(); + int newSelection = Math.min (selection, value); + if (selection != newSelection) { + ((NSProgressIndicator)view).setDoubleValue(newSelection); + } +} + +/** + * Sets the minimum value that the receiver will allow. This new + * value will be ignored if it is negative or is not less than the receiver's + * current maximum value. If the new minimum is applied then the receiver's + * selection value will be adjusted if necessary to fall within its new range. + * + * @param value the new minimum, which must be nonnegative and less than the current maximum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setMinimum (int value) { + checkWidget(); + int maximum = (int)((NSProgressIndicator)view).maxValue(); + if (!(0 <= value && value < maximum)) return; + ((NSProgressIndicator)view).setMinValue(value); + int selection = (int)((NSProgressIndicator)view).doubleValue(); + int newSelection = Math.max (selection, value); + if (selection != newSelection) { + ((NSProgressIndicator)view).setDoubleValue(newSelection); + } +} + +/** + * Sets the single 'selection' that is the receiver's + * position to the argument which must be greater than or equal + * to zero. + * + * @param value the new selection (must be zero or greater) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSelection (int value) { + checkWidget(); + ((NSProgressIndicator)view).setDoubleValue(value); + /* + * Feature in Cocoa. The progress bar does + * not redraw right away when a value is + * changed. This is not strictly incorrect + * but unexpected. The fix is to force all + * outstanding redraws to be delivered. + */ + update(false); +} + +/** + * Sets the state of the receiver. The state must be one of these values: + * <ul> + * <li>{@link SWT#NORMAL}</li> + * <li>{@link SWT#ERROR}</li> + * <li>{@link SWT#PAUSED}</li> + * </ul> + * + * @param state the new state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public void setState (int state) { + checkWidget (); + //NOT IMPLEMENTED +} + +void releaseWidget () { + super.releaseWidget(); + if (visiblePath != null) visiblePath.release(); + visiblePath = null; +} + +void resetVisibleRegion () { + super.resetVisibleRegion (); + if (visiblePath != null) visiblePath.release(); + visiblePath = null; +} + +void viewDidMoveToWindow(int /*long*/ id, int /*long*/ sel) { + /* + * Bug in Cocoa. An indeterminate progress indicator doesn't start animating until it is in + * a visible window. Workaround is to catch when the bar has been added to a window and start + * the animation there. + */ + if (view.window() != null) { + if ((style & SWT.INDETERMINATE) != 0) { + ((NSProgressIndicator)view).startAnimation(null); + } + } +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Sash.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Sash.java new file mode 100755 index 0000000000..dd1454aab4 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Sash.java @@ -0,0 +1,478 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import org.eclipse.swt.*; +import org.eclipse.swt.accessibility.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of the receiver represent a selectable user interface object + * that allows the user to drag a rubber banded outline of the sash within + * the parent control. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>HORIZONTAL, VERTICAL, SMOOTH</dd> + * <dt><b>Events:</b></dt> + * <dd>Selection</dd> + * </dl> + * <p> + * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified. + * </p><p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the SWT implementation. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#sash">Sash snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Sash extends Control { + Cursor sizeCursor; + boolean dragging; + int lastX, lastY, startX, startY; + private final static int INCREMENT = 1; + private final static int PAGE_INCREMENT = 9; + NSArray accessibilityAttributes = null; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#HORIZONTAL + * @see SWT#VERTICAL + * @see SWT#SMOOTH + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Sash (Composite parent, int style) { + super (parent, checkStyle (style)); + int cursorStyle = (style & SWT.VERTICAL) != 0 ? SWT.CURSOR_SIZEWE : SWT.CURSOR_SIZENS; + sizeCursor = new Cursor (display, cursorStyle); +} + +int /*long*/ accessibilityAttributeNames(int /*long*/ id, int /*long*/ sel) { + if (accessibilityAttributes == null) { + NSMutableArray ourAttributes = NSMutableArray.arrayWithCapacity(10); + ourAttributes.addObject(OS.NSAccessibilityRoleAttribute); + ourAttributes.addObject(OS.NSAccessibilityRoleDescriptionAttribute); + ourAttributes.addObject(OS.NSAccessibilityParentAttribute); + ourAttributes.addObject(OS.NSAccessibilityPositionAttribute); + ourAttributes.addObject(OS.NSAccessibilitySizeAttribute); + ourAttributes.addObject(OS.NSAccessibilityWindowAttribute); + ourAttributes.addObject(OS.NSAccessibilityTopLevelUIElementAttribute); + ourAttributes.addObject(OS.NSAccessibilityFocusedAttribute); + ourAttributes.addObject(OS.NSAccessibilityValueAttribute); + ourAttributes.addObject(OS.NSAccessibilityMaxValueAttribute); + ourAttributes.addObject(OS.NSAccessibilityMinValueAttribute); + // The accessibility documentation says that these next two are optional, but the + // Accessibility Verifier says they are required. + ourAttributes.addObject(OS.NSAccessibilityNextContentsAttribute); + ourAttributes.addObject(OS.NSAccessibilityPreviousContentsAttribute); + ourAttributes.addObject(OS.NSAccessibilityOrientationAttribute); + + if (accessible != null) { + // See if the accessible will override or augment the standard list. + // Help, title, and description can be overridden. + NSMutableArray extraAttributes = NSMutableArray.arrayWithCapacity(3); + extraAttributes.addObject(OS.NSAccessibilityHelpAttribute); + extraAttributes.addObject(OS.NSAccessibilityDescriptionAttribute); + extraAttributes.addObject(OS.NSAccessibilityTitleAttribute); + + for (int i = (int)/*64*/extraAttributes.count() - 1; i >= 0; i--) { + NSString attribute = new NSString(extraAttributes.objectAtIndex(i).id); + if (accessible.internal_accessibilityAttributeValue(attribute, ACC.CHILDID_SELF) != null) { + ourAttributes.addObject(extraAttributes.objectAtIndex(i)); + } + } + } + + accessibilityAttributes = ourAttributes; + accessibilityAttributes.retain(); + } + + return accessibilityAttributes.id; +} + +int /*long*/ accessibilityAttributeValue(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + int /*long*/ returnValue = 0; + NSString attributeName = new NSString(arg0); + + if (accessible != null) { + id returnObject = accessible.internal_accessibilityAttributeValue(attributeName, ACC.CHILDID_SELF); + + if (returnObject != null) returnValue = returnObject.id; + } + + if (returnValue != 0) return returnValue; + + if (attributeName.isEqualToString (OS.NSAccessibilityRoleAttribute) || attributeName.isEqualToString (OS.NSAccessibilityRoleDescriptionAttribute)) { + NSString roleText = OS.NSAccessibilitySplitterRole; + + if (attributeName.isEqualToString (OS.NSAccessibilityRoleAttribute)) { + return roleText.id; + } else { // NSAccessibilityRoleDescriptionAttribute + return OS.NSAccessibilityRoleDescription (roleText.id, 0); + } + } else if (attributeName.isEqualToString (OS.NSAccessibilityEnabledAttribute)) { + return NSNumber.numberWithBool(isEnabled()).id; + } else if (attributeName.isEqualToString (OS.NSAccessibilityOrientationAttribute)) { + NSString orientation = (style & SWT.VERTICAL) != 0 ? OS.NSAccessibilityVerticalOrientationValue : OS.NSAccessibilityHorizontalOrientationValue; + return orientation.id; + } else if (attributeName.isEqualToString (OS.NSAccessibilityValueAttribute)) { + Point location = getLocation(); + int value = (style & SWT.VERTICAL) != 0 ? location.x : location.y; + return NSNumber.numberWithInt(value).id; + } else if (attributeName.isEqualToString (OS.NSAccessibilityMaxValueAttribute)) { + NSRect parentBounds = view.bounds(); + float /*double*/ maxValue = (style & SWT.VERTICAL) != 0 ? + parentBounds.width : + parentBounds.height; + return NSNumber.numberWithInt((int)maxValue).id; + } else if (attributeName.isEqualToString (OS.NSAccessibilityMinValueAttribute)) { + return NSNumber.numberWithInt(0).id; + } else if (attributeName.isEqualToString (OS.NSAccessibilityNextContentsAttribute)) { + Control[] children = parent._getChildren(); + Control nextView = null; + for (int i = 0; i < children.length; i++) { + if (children[i] == this) { + if (i < children.length - 1) { + nextView = children[i + 1]; + break; + } + } + } + + if (nextView != null) + return NSArray.arrayWithObject(nextView.view).id; + else + return NSArray.array().id; + } else if (attributeName.isEqualToString (OS.NSAccessibilityPreviousContentsAttribute)) { + Control[] children = parent._getChildren(); + Control nextView = null; + for (int i = 0; i < children.length; i++) { + if (children[i] == this) { + if (i > 0) { + nextView = children[i - 1]; + break; + } + } + } + + if (nextView != null) + return NSArray.arrayWithObject(nextView.view).id; + else + return NSArray.array().id; + } + + return super.accessibilityAttributeValue(id, sel, arg0); +} + +boolean accessibilityIsIgnored(int /*long*/ id, int /*long*/ sel) { + return false; +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the control is selected by the user, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * When <code>widgetSelected</code> is called, the x, y, width, and height fields of the event object are valid. + * If the receiver is being dragged, the event object detail field contains the value <code>SWT.DRAG</code>. + * <code>widgetDefaultSelected</code> is not called. + * </p> + * + * @param listener the listener which should be notified when the control is selected by the user + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener(listener); + addListener(SWT.Selection,typedListener); + addListener(SWT.DefaultSelection,typedListener); +} + +static int checkStyle (int style) { + /* + * Macintosh only supports smooth dragging. + */ + style |= SWT.SMOOTH; + return checkBits (style, SWT.HORIZONTAL, SWT.VERTICAL, 0, 0, 0, 0); +} + +boolean becomeFirstResponder (int /*long*/ id, int /*long*/ sel) { + boolean result = super.becomeFirstResponder(id, sel); + NSRect frame = view.frame(); + lastX = (int)frame.x; + lastY = (int)frame.y; + return result; +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget(); + int width = 0, height = 0; + if ((style & SWT.HORIZONTAL) != 0) { + width += DEFAULT_WIDTH; height += 5; + } else { + width += 5; height += DEFAULT_HEIGHT; + } + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + return new Point (width, height); +} + +void createHandle () { + state |= THEME_BACKGROUND; + NSView widget = (NSView)new SWTView().alloc(); + widget.init (); + view = widget; +} + +void drawBackground (int /*long*/ id, NSGraphicsContext context, NSRect rect) { + if (id != view.id) return; + fillBackground (view, context, rect, -1); +} + +Cursor findCursor () { + Cursor cursor = super.findCursor (); + if (cursor == null) { + int cursorType = (style & SWT.HORIZONTAL) != 0 ? SWT.CURSOR_SIZENS : SWT.CURSOR_SIZEWE; + cursor = display.getSystemCursor (cursorType); + } + return cursor; +} + +boolean sendKeyEvent(NSEvent nsEvent, int type) { + super.sendKeyEvent (nsEvent, type); + if (type == SWT.KeyDown) { + int keyCode = nsEvent.keyCode(); + switch (keyCode) { + case 126: /* Up arrow */ + case 123: /* Left arrow */ + case 125: /* Down arrow */ + case 124: /* Right arrow */ { + int xChange = 0, yChange = 0; + int stepSize = PAGE_INCREMENT; + int /*long*/ modifiers = nsEvent.modifierFlags(); + if ((modifiers & OS.NSControlKeyMask) != 0) stepSize = INCREMENT; + if ((style & SWT.VERTICAL) != 0) { + if (keyCode == 126 || keyCode == 125) break; + xChange = keyCode == 123 ? -stepSize : stepSize; + } else { + if (keyCode == 123 || keyCode == 124) break; + yChange = keyCode == 126 ? -stepSize : stepSize; + } + + Rectangle bounds = getBounds (); + int width = bounds.width, height = bounds.height; + Rectangle parentBounds = parent.getBounds (); + int parentWidth = parentBounds.width; + int parentHeight = parentBounds.height; + int newX = lastX, newY = lastY; + if ((style & SWT.VERTICAL) != 0) { + newX = Math.min (Math.max (0, lastX + xChange), parentWidth - width); + } else { + newY = Math.min (Math.max (0, lastY + yChange), parentHeight - height); + } + if (newX == lastX && newY == lastY) return true; + Event event = new Event (); + event.x = newX; + event.y = newY; + event.width = width; + event.height = height; + sendEvent (SWT.Selection, event); + if (isDisposed ()) break; + if (event.doit) { + setBounds (event.x, event.y, width, height); + if (isDisposed ()) break; + lastX = event.x; + lastY = event.y; + if (isDisposed ()) return false; + int cursorX = event.x, cursorY = event.y; + if ((style & SWT.VERTICAL) != 0) { + cursorY += height / 2; + } else { + cursorX += width / 2; + } + display.setCursorLocation (parent.toDisplay (cursorX, cursorY)); + } + break; + } + } + } + return true; +} + +void mouseDown(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + //TODO use sendMouseEvent + super.mouseDown(id, sel, theEvent); + if (isDisposed()) return; + NSEvent nsEvent = new NSEvent(theEvent); + if (nsEvent.clickCount() != 1) return; + NSPoint location = nsEvent.locationInWindow(); + NSPoint point = view.convertPoint_fromView_(location, null); + startX = (int)point.x; + startY = (int)point.y; + NSRect frame = view.frame(); + Event event = new Event (); + event.x = (int)frame.x; + event.y = (int)frame.y; + event.width = (int)frame.width; + event.height = (int)frame.height; + sendEvent (SWT.Selection, event); + if (isDisposed ()) return; + if (event.doit) { + lastX = event.x; + lastY = event.y; + dragging = true; + setLocation(event.x, event.y); + } +} + +boolean mouseEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent, int type) { + super.mouseEvent (id, sel, theEvent, type); + return new NSEvent (theEvent).type () != OS.NSLeftMouseDown; +} + +void mouseDragged(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + //TODO use sendMouseEvent + super.mouseDragged(id, sel, theEvent); + if (isDisposed()) return; + if (!dragging) return; + NSEvent nsEvent = new NSEvent(theEvent); + NSPoint location = nsEvent.locationInWindow(); + NSPoint point = view.convertPoint_fromView_(location, null); + NSRect frame = view.frame(); + NSRect parentFrame = parent.topView().frame(); + int newX = lastX, newY = lastY; + if ((style & SWT.VERTICAL) != 0) { + newX = Math.min (Math.max (0, (int)(point.x + frame.x - startX)), (int)(parentFrame.width - frame.width)); + } else { + newY = Math.min (Math.max (0, (int)(point.y + frame.y - startY)), (int)(parentFrame.height - frame.height)); + } + if (newX == lastX && newY == lastY) return; + Event event = new Event (); + event.x = newX; + event.y = newY; + event.width = (int)frame.width; + event.height = (int)frame.height; + sendEvent (SWT.Selection, event); + if (isDisposed ()) return; + if (event.doit) { + lastX = event.x; + lastY = event.y; + setBounds (event.x, event.y, (int)frame.width, (int)frame.height); + } +} + +void mouseUp(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + //TODO use sendMouseEvent + super.mouseUp(id, sel, theEvent); + if (isDisposed()) return; + if (!dragging) return; + dragging = false; + NSRect frame = view.frame(); + Event event = new Event (); + event.x = lastX; + event.y = lastY; + event.width = (int)frame.width; + event.height = (int)frame.height; + sendEvent (SWT.Selection, event); + if (isDisposed ()) return; + if (event.doit) { + setBounds (event.x, event.y, (int)frame.width, (int)frame.height); + } +} + +void releaseHandle () { + super.releaseHandle (); + if (accessibilityAttributes != null) accessibilityAttributes.release(); + accessibilityAttributes = null; +} + +void releaseWidget () { + super.releaseWidget (); + if (sizeCursor != null) sizeCursor.dispose (); + sizeCursor = null; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is selected by the user. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook(SWT.Selection, listener); + eventTable.unhook(SWT.DefaultSelection,listener); +} + +void superKeyDown (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { +} + +void superKeyUp (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { +} + +int traversalCode (int key, NSEvent theEvent) { + return 0; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Scale.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Scale.java new file mode 100755 index 0000000000..2d120fe32d --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Scale.java @@ -0,0 +1,362 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; + +/** + * Instances of the receiver represent a selectable user + * interface object that present a range of continuous + * numeric values. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>HORIZONTAL, VERTICAL</dd> + * <dt><b>Events:</b></dt> + * <dd>Selection</dd> + * </dl> + * <p> + * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified. + * </p><p> + * <p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the SWT implementation. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#scale">Scale snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Scale extends Control { + int increment = 1; + int pageIncrement = 10; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#HORIZONTAL + * @see SWT#VERTICAL + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Scale (Composite parent, int style) { + super (parent, checkStyle (style)); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the user changes the receiver's value, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * <code>widgetSelected</code> is called when the user changes the receiver's value. + * <code>widgetDefaultSelected</code> is not called. + * </p> + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + */ +public void addSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener(listener); + addListener(SWT.Selection,typedListener); + addListener(SWT.DefaultSelection,typedListener); +} + +static int checkStyle (int style) { + return checkBits (style, SWT.HORIZONTAL, SWT.VERTICAL, 0, 0, 0, 0); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget(); + NSSlider widget = (NSSlider)view; + float /*double*/ thickness = widget.knobThickness(); + int width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT; + if ((style & SWT.HORIZONTAL) != 0) { + height = (int)Math.ceil(thickness); + width = height * 10; + } else { + width = (int)Math.ceil(thickness); + height = width * 10; + } + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + return new Point (width, height); +} + +void createHandle () { + state |= THEME_BACKGROUND; + NSSlider widget = (NSSlider)new SWTSlider().alloc(); + widget.init(); + widget.setMaxValue(100); + widget.setTarget(widget); + widget.setAction(OS.sel_sendSelection); + view = widget; +} + +NSFont defaultNSFont () { + return display.sliderFont; +} + +void deregister() { + super.deregister(); + display.removeWidget(((NSControl)view).cell()); +} + + +/** + * Returns the amount that the receiver's value will be + * modified by when the up/down (or right/left) arrows + * are pressed. + * + * @return the increment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getIncrement () { + checkWidget(); + return increment; +} + +/** + * Returns the maximum value which the receiver will allow. + * + * @return the maximum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getMaximum () { + checkWidget(); + return (int)((NSSlider)view).maxValue(); +} + +/** + * Returns the minimum value which the receiver will allow. + * + * @return the minimum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getMinimum () { + checkWidget(); + return (int)((NSSlider)view).minValue(); +} + +/** + * Returns the amount that the receiver's value will be + * modified by when the page increment/decrement areas + * are selected. + * + * @return the page increment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getPageIncrement () { + checkWidget(); + return pageIncrement; +} + +/** + * Returns the 'selection', which is the receiver's position. + * + * @return the selection + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getSelection () { + checkWidget(); + return (int)((NSSlider)view).doubleValue(); +} + +void register() { + super.register(); + display.addWidget(((NSControl)view).cell(), this); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the user changes the receiver's value. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook(SWT.Selection, listener); + eventTable.unhook(SWT.DefaultSelection,listener); +} + +void sendSelection () { + NSEvent currEvent = NSApplication.sharedApplication().currentEvent(); + + if (currEvent.type() != OS.NSLeftMouseUp) + postEvent (SWT.Selection); +} + +/** + * Sets the amount that the receiver's value will be + * modified by when the up/down (or right/left) arrows + * are pressed to the argument, which must be at least + * one. + * + * @param increment the new increment (must be greater than zero) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setIncrement (int value) { + checkWidget(); + if (value < 1) return; + increment = value; +} + +/** + * Sets the maximum value that the receiver will allow. This new + * value will be ignored if it is not greater than the receiver's current + * minimum value. If the new maximum is applied then the receiver's + * selection value will be adjusted if necessary to fall within its new range. + * + * @param value the new maximum, which must be greater than the current minimum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setMaximum (int value) { + checkWidget(); + int minimum = (int)((NSSlider)view).minValue(); + if (value <= minimum) return; + ((NSSlider)view).setMaxValue(value); +} + +/** + * Sets the minimum value that the receiver will allow. This new + * value will be ignored if it is negative or is not less than the receiver's + * current maximum value. If the new minimum is applied then the receiver's + * selection value will be adjusted if necessary to fall within its new range. + * + * @param value the new minimum, which must be nonnegative and less than the current maximum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setMinimum (int value) { + checkWidget(); + int maximum = (int)((NSSlider)view).maxValue(); + if (!(0 <= value && value < maximum)) return; + ((NSSlider)view).setMinValue(value); +} + +/** + * Sets the amount that the receiver's value will be + * modified by when the page increment/decrement areas + * are selected to the argument, which must be at least + * one. + * + * @param pageIncrement the page increment (must be greater than zero) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setPageIncrement (int value) { + checkWidget(); + if (value < 1) return; + pageIncrement = value; +} + +/** + * Sets the 'selection', which is the receiver's value, + * to the argument which must be greater than or equal to zero. + * + * @param value the new selection (must be zero or greater) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSelection (int value) { + checkWidget(); + ((NSSlider)view).setDoubleValue(value); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ScrollBar.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ScrollBar.java new file mode 100755 index 0000000000..2a9fee54bd --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ScrollBar.java @@ -0,0 +1,685 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; + +/** + * Instances of this class are selectable user interface + * objects that represent a range of positive, numeric values. + * <p> + * At any given moment, a given scroll bar will have a + * single 'selection' that is considered to be its + * value, which is constrained to be within the range of + * values the scroll bar represents (that is, between its + * <em>minimum</em> and <em>maximum</em> values). + * </p><p> + * Typically, scroll bars will be made up of five areas: + * <ol> + * <li>an arrow button for decrementing the value</li> + * <li>a page decrement area for decrementing the value by a larger amount</li> + * <li>a <em>thumb</em> for modifying the value by mouse dragging</li> + * <li>a page increment area for incrementing the value by a larger amount</li> + * <li>an arrow button for incrementing the value</li> + * </ol> + * Based on their style, scroll bars are either <code>HORIZONTAL</code> + * (which have a left facing button for decrementing the value and a + * right facing button for incrementing it) or <code>VERTICAL</code> + * (which have an upward facing button for decrementing the value + * and a downward facing buttons for incrementing it). + * </p><p> + * On some platforms, the size of the scroll bar's thumb can be + * varied relative to the magnitude of the range of values it + * represents (that is, relative to the difference between its + * maximum and minimum values). Typically, this is used to + * indicate some proportional value such as the ratio of the + * visible area of a document to the total amount of space that + * it would take to display it. SWT supports setting the thumb + * size even if the underlying platform does not, but in this + * case the appearance of the scroll bar will not change. + * </p><p> + * Scroll bars are created by specifying either <code>H_SCROLL</code>, + * <code>V_SCROLL</code> or both when creating a <code>Scrollable</code>. + * They are accessed from the <code>Scrollable</code> using + * <code>getHorizontalBar</code> and <code>getVerticalBar</code>. + * </p><p> + * Note: Scroll bars are not Controls. On some platforms, scroll bars + * that appear as part of some standard controls such as a text or list + * have no operating system resources and are not children of the control. + * For this reason, scroll bars are treated specially. To create a control + * that looks like a scroll bar but has operating system resources, use + * <code>Slider</code>. + * </p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>HORIZONTAL, VERTICAL</dd> + * <dt><b>Events:</b></dt> + * <dd>Selection</dd> + * </dl> + * <p> + * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see Slider + * @see Scrollable + * @see Scrollable#getHorizontalBar + * @see Scrollable#getVerticalBar + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class ScrollBar extends Widget { + NSScroller view; + Scrollable parent; + int minimum, maximum = 100, thumb = 10; + int increment = 1; + int pageIncrement = 10; + id target; + int /*long*/ actionSelector;; + +ScrollBar () { + /* Do nothing */ +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the user changes the receiver's value, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * When <code>widgetSelected</code> is called, the event object detail field contains one of the following values: + * <code>SWT.NONE</code> - for the end of a drag. + * <code>SWT.DRAG</code>. + * <code>SWT.HOME</code>. + * <code>SWT.END</code>. + * <code>SWT.ARROW_DOWN</code>. + * <code>SWT.ARROW_UP</code>. + * <code>SWT.PAGE_DOWN</code>. + * <code>SWT.PAGE_UP</code>. + * <code>widgetDefaultSelected</code> is not called. + * </p> + * + * @param listener the listener which should be notified when the user changes the receiver's value + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener(listener); + addListener(SWT.Selection,typedListener); + addListener(SWT.DefaultSelection,typedListener); +} + +static int checkStyle (int style) { + return checkBits (style, SWT.HORIZONTAL, SWT.VERTICAL, 0, 0, 0, 0); +} + +void deregister () { + super.deregister (); + display.removeWidget (view); +} + +boolean getDrawing () { + return parent.getDrawing (); +} + +/** + * Returns <code>true</code> if the receiver is enabled, and + * <code>false</code> otherwise. A disabled control is typically + * not selectable from the user interface and draws with an + * inactive or "grayed" look. + * + * @return the receiver's enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #isEnabled + */ +public boolean getEnabled () { + checkWidget(); + return (state & DISABLED) == 0; +} + +/** + * Returns the amount that the receiver's value will be + * modified by when the up/down (or right/left) arrows + * are pressed. + * + * @return the increment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getIncrement () { + checkWidget(); + return increment; +} + +/** + * Returns the maximum value which the receiver will allow. + * + * @return the maximum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getMaximum () { + checkWidget(); + return maximum; +} + +/** + * Returns the minimum value which the receiver will allow. + * + * @return the minimum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getMinimum () { + checkWidget(); + return minimum; +} + +/** + * Returns the amount that the receiver's value will be + * modified by when the page increment/decrement areas + * are selected. + * + * @return the page increment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getPageIncrement () { + checkWidget(); + return pageIncrement; +} + +/** + * Returns the receiver's parent, which must be a Scrollable. + * + * @return the receiver's parent + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Scrollable getParent () { + checkWidget (); + return parent; +} + +/** + * Returns the single 'selection' that is the receiver's value. + * + * @return the selection + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getSelection () { + checkWidget(); + NSScroller widget = (NSScroller)view; + double value = widget.doubleValue(); + return (int)(0.5f + ((maximum - thumb - minimum) * value + minimum)); +} + +/** + * Returns a point describing the receiver's size. The + * x coordinate of the result is the width of the receiver. + * The y coordinate of the result is the height of the + * receiver. + * + * @return the receiver's size + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Point getSize () { + checkWidget(); + NSRect rect = ((NSScroller)view).frame(); + return new Point((int)rect.width, (int)rect.height); +} + +/** + * Returns the size of the receiver's thumb relative to the + * difference between its maximum and minimum values. + * + * @return the thumb value + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ScrollBar + */ +public int getThumb () { + checkWidget(); + return thumb; +} + +/** + * Returns <code>true</code> if the receiver is visible, and + * <code>false</code> otherwise. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, this method + * may still indicate that it is considered visible even though + * it may not actually be showing. + * </p> + * + * @return the receiver's visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getVisible () { + checkWidget(); + return (state & HIDDEN) == 0; +} + +/** + * Returns <code>true</code> if the receiver is enabled and all + * of the receiver's ancestors are enabled, and <code>false</code> + * otherwise. A disabled control is typically not selectable from the + * user interface and draws with an inactive or "grayed" look. + * + * @return the receiver's enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #getEnabled + */ +public boolean isEnabled () { + checkWidget(); + return getEnabled () && parent.isEnabled (); +} + +boolean isDrawing () { + return getDrawing() && parent.isDrawing (); +} + +/** + * Returns <code>true</code> if the receiver is visible and all + * of the receiver's ancestors are visible and <code>false</code> + * otherwise. + * + * @return the receiver's visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #getVisible + */ +public boolean isVisible () { + checkWidget(); + return getVisible () && parent.isVisible (); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the user changes the receiver's value. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook(SWT.Selection, listener); + eventTable.unhook(SWT.DefaultSelection,listener); +} + +void register () { + super.register (); + display.addWidget (view, this); +} + +void releaseHandle () { + super.releaseHandle (); + if (view != null) view.release(); + view = null; +} + +void releaseParent () { + super.releaseParent (); + if (parent.horizontalBar == this) parent.horizontalBar = null; + if (parent.verticalBar == this) parent.verticalBar = null; +} + +void releaseWidget () { + super.releaseWidget (); + parent = null; +} + +void sendSelection () { + int value = 0; + if (target != null) { + view.sendAction(actionSelector, target); + } else { + value = getSelection (); + } + Event event = new Event(); + int hitPart = (int)/*64*/((NSScroller)view).hitPart(); + switch (hitPart) { + case OS.NSScrollerDecrementLine: + value -= increment; + event.detail = SWT.ARROW_UP; + break; + case OS.NSScrollerDecrementPage: + value -= pageIncrement; + event.detail = SWT.PAGE_UP; + break; + case OS.NSScrollerIncrementLine: + value += increment; + event.detail = SWT.ARROW_DOWN; + break; + case OS.NSScrollerIncrementPage: + value += pageIncrement; + event.detail = SWT.PAGE_DOWN; + break; + case OS.NSScrollerKnob: + event.detail = SWT.DRAG; + break; + } + if (target == null) { + if (event.detail != SWT.DRAG) { + setSelection(value); + } + } + sendEvent(SWT.Selection, event); +} + +/** + * Sets the amount that the receiver's value will be + * modified by when the up/down (or right/left) arrows + * are pressed to the argument, which must be at least + * one. + * + * @param value the new increment (must be greater than zero) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setIncrement (int value) { + checkWidget(); + if (value < 1) return; + increment = value; +} + +void setClipRegion (float /*double*/ x, float /*double*/ y) { + NSRect frame = view.frame(); + parent.setClipRegion(frame.x + x, frame.y + y); +} + +/** + * Enables the receiver if the argument is <code>true</code>, + * and disables it otherwise. A disabled control is typically + * not selectable from the user interface and draws with an + * inactive or "grayed" look. + * + * @param enabled the new enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setEnabled (boolean enabled) { + checkWidget(); + if (enabled) { + if ((state & DISABLED) == 0) return; + state &= ~DISABLED; + } else { + if ((state & DISABLED) != 0) return; + state |= DISABLED; + } + enableWidget (enabled); +} + +void enableWidget (boolean enabled) { + if (!enabled || (state & DISABLED) == 0) { + view.setEnabled (enabled); + } +} + +/** + * Sets the maximum. If this value is negative or less than or + * equal to the minimum, the value is ignored. If necessary, first + * the thumb and then the selection are adjusted to fit within the + * new range. + * + * @param value the new maximum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setMaximum (int value) { + checkWidget(); + if (value < 0) return; + if (value <= minimum) return; + if (value - minimum < thumb) { + thumb = value - minimum; + } + int selection = Math.max(minimum, Math.min (getSelection (), value - thumb)); + this.maximum = value; + updateBar(selection, minimum, value, thumb); +} + +/** + * Sets the minimum value. If this value is negative or greater + * than or equal to the maximum, the value is ignored. If necessary, + * first the thumb and then the selection are adjusted to fit within + * the new range. + * + * @param value the new minimum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setMinimum (int value) { + checkWidget(); + if (value < 0) return; + if (value >= maximum) return; + if (maximum - value < thumb) { + thumb = maximum - value; + } + int selection = Math.min(maximum - thumb, Math.max (getSelection (), value)); + this.minimum = value; + updateBar(selection, value, maximum, thumb); +} + +/** + * Sets the amount that the receiver's value will be + * modified by when the page increment/decrement areas + * are selected to the argument, which must be at least + * one. + * + * @param value the page increment (must be greater than zero) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setPageIncrement (int value) { + checkWidget(); + if (value < 1) return; + pageIncrement = value; +} + +/** + * Sets the single <em>selection</em> that is the receiver's + * value to the argument which must be greater than or equal + * to zero. + * + * @param selection the new selection (must be zero or greater) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSelection (int value) { + checkWidget(); + updateBar(value, minimum, maximum, thumb); +} + +/** + * Sets the size of the receiver's thumb relative to the + * difference between its maximum and minimum values. This new + * value will be ignored if it is less than one, and will be + * clamped if it exceeds the receiver's current range. + * + * @param value the new thumb value, which must be at least one and not + * larger than the size of the current range + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setThumb (int value) { + checkWidget(); + if (value < 1) return; + value = Math.min (value, maximum - minimum); + updateBar(getSelection(), minimum, maximum, value); + this.thumb = value; +} + +/** + * Sets the receiver's selection, minimum value, maximum + * value, thumb, increment and page increment all at once. + * <p> + * Note: This is similar to setting the values individually + * using the appropriate methods, but may be implemented in a + * more efficient fashion on some platforms. + * </p> + * + * @param selection the new selection value + * @param minimum the new minimum value + * @param maximum the new maximum value + * @param thumb the new thumb value + * @param increment the new increment value + * @param pageIncrement the new pageIncrement value + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setValues (int selection, int minimum, int maximum, int thumb, int increment, int pageIncrement) { + checkWidget(); + if (minimum < 0) return; + if (maximum < 0) return; + if (thumb < 1) return; + if (increment < 1) return; + if (pageIncrement < 1) return; + this.thumb = thumb = Math.min (thumb, maximum - minimum); + this.maximum = maximum; + this.minimum = minimum; + this.increment = increment; + this.pageIncrement = pageIncrement; + updateBar (selection, minimum, maximum, thumb); +} + +/** + * Marks the receiver as visible if the argument is <code>true</code>, + * and marks it invisible otherwise. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, marking + * it visible may not actually cause it to be displayed. + * </p> + * + * @param visible the new visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setVisible (boolean visible) { + checkWidget(); + parent.setScrollBarVisible (this, visible); +} + +void updateBar (int selection, int minimum, int maximum, int thumb) { + NSScroller widget = (NSScroller) view; + selection = Math.max (minimum, Math.min (maximum - thumb, selection)); + int range = maximum - thumb - minimum; + float fraction = range <= 0 ? 1 : (float) (selection - minimum) / range; + float knob = range <= 0 ? 1 : (float) thumb / (maximum - minimum); + widget.setFloatValue (fraction, knob); + widget.setEnabled (range > 0); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Scrollable.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Scrollable.java new file mode 100755 index 0000000000..50e08c80ba --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Scrollable.java @@ -0,0 +1,335 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * This class is the abstract superclass of all classes which + * represent controls that have standard scroll bars. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>H_SCROLL, V_SCROLL</dd> + * <dt><b>Events:</b> + * <dd>(none)</dd> + * </dl> + * <p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the SWT implementation. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public abstract class Scrollable extends Control { + NSScrollView scrollView; + ScrollBar horizontalBar, verticalBar; + +Scrollable () { + /* Do nothing */ +} + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#H_SCROLL + * @see SWT#V_SCROLL + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Scrollable (Composite parent, int style) { + super (parent, style); +} + +boolean accessibilityIsIgnored(int /*long*/ id, int /*long*/ sel) { + // Always ignore scrollers. + if (scrollView != null && id == scrollView.id) return true; + return super.accessibilityIsIgnored(id, sel); +} + +/** + * Given a desired <em>client area</em> for the receiver + * (as described by the arguments), returns the bounding + * rectangle which would be required to produce that client + * area. + * <p> + * In other words, it returns a rectangle such that, if the + * receiver's bounds were set to that rectangle, the area + * of the receiver which is capable of displaying data + * (that is, not covered by the "trimmings") would be the + * rectangle described by the arguments (relative to the + * receiver's parent). + * </p> + * + * @param x the desired x coordinate of the client area + * @param y the desired y coordinate of the client area + * @param width the desired width of the client area + * @param height the desired height of the client area + * @return the required bounds to produce the given client area + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #getClientArea + */ +public Rectangle computeTrim (int x, int y, int width, int height) { + checkWidget(); + if (scrollView != null) { + NSSize size = new NSSize(); + size.width = width; + size.height = height; + int border = hasBorder() ? OS.NSBezelBorder : OS.NSNoBorder; + size = NSScrollView.frameSizeForContentSize(size, (style & SWT.H_SCROLL) != 0, (style & SWT.V_SCROLL) != 0, border); + width = (int)size.width; + height = (int)size.height; + NSRect frame = scrollView.contentView().frame(); + x -= frame.x; + y -= frame.y; + } + return new Rectangle (x, y, width, height); +} + +ScrollBar createScrollBar (int style) { + if (scrollView == null) return null; + ScrollBar bar = new ScrollBar (); + bar.parent = this; + bar.style = style; + bar.display = display; + NSScroller scroller; + int /*long*/ actionSelector; + NSRect rect = new NSRect(); + if ((style & SWT.H_SCROLL) != 0) { + rect.width = 1; + } else { + rect.height = 1; + } + scroller = (NSScroller)new SWTScroller().alloc(); + scroller.initWithFrame(rect); + if ((style & SWT.H_SCROLL) != 0) { + scrollView.setHorizontalScroller(scroller); + actionSelector = OS.sel_sendHorizontalSelection; + } else { + scrollView.setVerticalScroller(scroller); + actionSelector = OS.sel_sendVerticalSelection; + } + bar.view = scroller; + bar.createJNIRef(); + bar.register(); + if ((state & CANVAS) == 0) { + bar.target = scroller.target(); + bar.actionSelector = scroller.action(); + } + scroller.setTarget(scrollView); + scroller.setAction(actionSelector); + if ((state & CANVAS) != 0) { + bar.updateBar(0, 0, 100, 10); + } + return bar; +} + +void createWidget () { + super.createWidget (); + if ((style & SWT.H_SCROLL) != 0) horizontalBar = createScrollBar (SWT.H_SCROLL); + if ((style & SWT.V_SCROLL) != 0) verticalBar = createScrollBar (SWT.V_SCROLL); +} + +void deregister () { + super.deregister (); + if (scrollView != null) display.removeWidget (scrollView); +} + +/** + * Returns a rectangle which describes the area of the + * receiver which is capable of displaying data (that is, + * not covered by the "trimmings"). + * + * @return the client area + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #computeTrim + */ +public Rectangle getClientArea () { + checkWidget(); + if (scrollView != null) { + NSSize size = scrollView.contentSize(); + NSClipView contentView = scrollView.contentView(); + NSRect bounds = contentView.bounds(); + return new Rectangle((int)bounds.x, (int)bounds.y, (int)size.width, (int)size.height); + } else { + NSRect rect = view.bounds(); + return new Rectangle(0, 0, (int)rect.width, (int)rect.height); + } +} + +/** + * Returns the receiver's horizontal scroll bar if it has + * one, and null if it does not. + * + * @return the horizontal scroll bar (or null) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public ScrollBar getHorizontalBar () { + checkWidget(); + return horizontalBar; +} + +/** + * Returns the receiver's vertical scroll bar if it has + * one, and null if it does not. + * + * @return the vertical scroll bar (or null) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public ScrollBar getVerticalBar () { + checkWidget(); + return verticalBar; +} + +boolean hooksKeys () { + return hooks (SWT.KeyDown) || hooks (SWT.KeyUp) || hooks (SWT.Traverse); +} + +boolean isEventView (int /*long*/ id) { + return id == eventView ().id; +} + +boolean isTrim (NSView view) { + if (scrollView != null) { + if (scrollView.id == view.id) return true; + if (horizontalBar != null && horizontalBar.view.id == view.id) return true; + if (verticalBar != null && verticalBar.view.id == view.id) return true; + } + return super.isTrim (view); +} + +void register () { + super.register (); + if (scrollView != null) display.addWidget (scrollView, this); +} + +void releaseHandle () { + super.releaseHandle (); + if (scrollView != null) scrollView.release(); + scrollView = null; +} + +void releaseChildren (boolean destroy) { + if (horizontalBar != null) { + horizontalBar.release (false); + horizontalBar = null; + } + if (verticalBar != null) { + verticalBar.release (false); + verticalBar = null; + } + super.releaseChildren (destroy); +} + +void sendHorizontalSelection () { + if (horizontalBar.view.isHiddenOrHasHiddenAncestor()) return; + if ((state & CANVAS) == 0 && scrollView != null && visibleRgn == 0) { + scrollView.contentView().setCopiesOnScroll(!isObscured()); + } + horizontalBar.sendSelection (); +} + +void sendVerticalSelection () { + if (verticalBar.view.isHiddenOrHasHiddenAncestor()) return; + if ((state & CANVAS) == 0 && scrollView != null && visibleRgn == 0) { + scrollView.contentView().setCopiesOnScroll(!isObscured()); + } + verticalBar.sendSelection (); +} + +void enableWidget (boolean enabled) { + super.enableWidget (enabled); + if (horizontalBar != null) horizontalBar.enableWidget (enabled); + if (verticalBar != null) verticalBar.enableWidget (enabled); +} + +boolean setScrollBarVisible (ScrollBar bar, boolean visible) { + if (scrollView == null) return false; + if ((state & CANVAS) == 0) return false; + if (visible) { + if ((bar.state & HIDDEN) == 0) return false; + bar.state &= ~HIDDEN; + } else { + if ((bar.state & HIDDEN) != 0) return false; + bar.state |= HIDDEN; + } + if ((bar.style & SWT.HORIZONTAL) != 0) { + scrollView.setHasHorizontalScroller (visible); + } else { + scrollView.setHasVerticalScroller (visible); + } + bar.sendEvent (visible ? SWT.Show : SWT.Hide); + sendEvent (SWT.Resize); + return true; +} + +void setZOrder () { + super.setZOrder (); + if (scrollView != null) scrollView.setDocumentView (view); +} + +NSView topView () { + if (scrollView != null) return scrollView; + return super.topView (); +} + +void updateCursorRects (boolean enabled) { + super.updateCursorRects (enabled); + if (scrollView == null) return; + updateCursorRects (enabled, scrollView); + NSClipView contentView = scrollView.contentView (); + updateCursorRects (enabled, contentView); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Shell.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Shell.java new file mode 100755 index 0000000000..3c2f2e66b3 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Shell.java @@ -0,0 +1,1829 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class represent the "windows" + * which the desktop or "window manager" is managing. + * Instances that do not have a parent (that is, they + * are built using the constructor, which takes a + * <code>Display</code> as the argument) are described + * as <em>top level</em> shells. Instances that do have + * a parent are described as <em>secondary</em> or + * <em>dialog</em> shells. + * <p> + * Instances are always displayed in one of the maximized, + * minimized or normal states: + * <ul> + * <li> + * When an instance is marked as <em>maximized</em>, the + * window manager will typically resize it to fill the + * entire visible area of the display, and the instance + * is usually put in a state where it can not be resized + * (even if it has style <code>RESIZE</code>) until it is + * no longer maximized. + * </li><li> + * When an instance is in the <em>normal</em> state (neither + * maximized or minimized), its appearance is controlled by + * the style constants which were specified when it was created + * and the restrictions of the window manager (see below). + * </li><li> + * When an instance has been marked as <em>minimized</em>, + * its contents (client area) will usually not be visible, + * and depending on the window manager, it may be + * "iconified" (that is, replaced on the desktop by a small + * simplified representation of itself), relocated to a + * distinguished area of the screen, or hidden. Combinations + * of these changes are also possible. + * </li> + * </ul> + * </p><p> + * The <em>modality</em> of an instance may be specified using + * style bits. The modality style bits are used to determine + * whether input is blocked for other shells on the display. + * The <code>PRIMARY_MODAL</code> style allows an instance to block + * input to its parent. The <code>APPLICATION_MODAL</code> style + * allows an instance to block input to every other shell in the + * display. The <code>SYSTEM_MODAL</code> style allows an instance + * to block input to all shells, including shells belonging to + * different applications. + * </p><p> + * Note: The styles supported by this class are treated + * as <em>HINT</em>s, since the window manager for the + * desktop on which the instance is visible has ultimate + * control over the appearance and behavior of decorations + * and modality. For example, some window managers only + * support resizable windows and will always assume the + * RESIZE style, even if it is not set. In addition, if a + * modality style is not supported, it is "upgraded" to a + * more restrictive modality style that is supported. For + * example, if <code>PRIMARY_MODAL</code> is not supported, + * it would be upgraded to <code>APPLICATION_MODAL</code>. + * A modality style may also be "downgraded" to a less + * restrictive style. For example, most operating systems + * no longer support <code>SYSTEM_MODAL</code> because + * it can freeze up the desktop, so this is typically + * downgraded to <code>APPLICATION_MODAL</code>. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>BORDER, CLOSE, MIN, MAX, NO_TRIM, RESIZE, TITLE, ON_TOP, TOOL, SHEET</dd> + * <dd>APPLICATION_MODAL, MODELESS, PRIMARY_MODAL, SYSTEM_MODAL</dd> + * <dt><b>Events:</b></dt> + * <dd>Activate, Close, Deactivate, Deiconify, Iconify</dd> + * </dl> + * Class <code>SWT</code> provides two "convenience constants" + * for the most commonly required style combinations: + * <dl> + * <dt><code>SHELL_TRIM</code></dt> + * <dd> + * the result of combining the constants which are required + * to produce a typical application top level shell: (that + * is, <code>CLOSE | TITLE | MIN | MAX | RESIZE</code>) + * </dd> + * <dt><code>DIALOG_TRIM</code></dt> + * <dd> + * the result of combining the constants which are required + * to produce a typical application dialog shell: (that + * is, <code>TITLE | CLOSE | BORDER</code>) + * </dd> + * </dl> + * </p> + * <p> + * Note: Only one of the styles APPLICATION_MODAL, MODELESS, + * PRIMARY_MODAL and SYSTEM_MODAL may be specified. + * </p><p> + * IMPORTANT: This class is not intended to be subclassed. + * </p> + * + * @see Decorations + * @see SWT + * @see <a href="http://www.eclipse.org/swt/snippets/#shell">Shell snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + */ +public class Shell extends Decorations { + NSWindow window; + SWTWindowDelegate windowDelegate; + int /*long*/ tooltipOwner, tooltipTag, tooltipUserData; + boolean opened, moved, resized, fullScreen, center; + Control lastActive; + Rectangle normalBounds; + boolean keyInputHappened; + NSRect currentFrame; + NSRect fullScreenFrame; + + static int DEFAULT_CLIENT_WIDTH = -1; + static int DEFAULT_CLIENT_HEIGHT = -1; + +/** + * Constructs a new instance of this class. This is equivalent + * to calling <code>Shell((Display) null)</code>. + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + */ +public Shell () { + this ((Display) null); +} + +/** + * Constructs a new instance of this class given only the style + * value describing its behavior and appearance. This is equivalent + * to calling <code>Shell((Display) null, style)</code>. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param style the style of control to construct + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#BORDER + * @see SWT#CLOSE + * @see SWT#MIN + * @see SWT#MAX + * @see SWT#RESIZE + * @see SWT#TITLE + * @see SWT#TOOL + * @see SWT#NO_TRIM + * @see SWT#SHELL_TRIM + * @see SWT#DIALOG_TRIM + * @see SWT#ON_TOP + * @see SWT#MODELESS + * @see SWT#PRIMARY_MODAL + * @see SWT#APPLICATION_MODAL + * @see SWT#SYSTEM_MODAL + * @see SWT#SHEET + */ +public Shell (int style) { + this ((Display) null, style); +} + +/** + * Constructs a new instance of this class given only the display + * to create it on. It is created with style <code>SWT.SHELL_TRIM</code>. + * <p> + * Note: Currently, null can be passed in for the display argument. + * This has the effect of creating the shell on the currently active + * display if there is one. If there is no current display, the + * shell is created on a "default" display. <b>Passing in null as + * the display argument is not considered to be good coding style, + * and may not be supported in a future release of SWT.</b> + * </p> + * + * @param display the display to create the shell on + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + */ +public Shell (Display display) { + this (display, SWT.SHELL_TRIM); +} + +/** + * Constructs a new instance of this class given the display + * to create it on and a style value describing its behavior + * and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p><p> + * Note: Currently, null can be passed in for the display argument. + * This has the effect of creating the shell on the currently active + * display if there is one. If there is no current display, the + * shell is created on a "default" display. <b>Passing in null as + * the display argument is not considered to be good coding style, + * and may not be supported in a future release of SWT.</b> + * </p> + * + * @param display the display to create the shell on + * @param style the style of control to construct + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#BORDER + * @see SWT#CLOSE + * @see SWT#MIN + * @see SWT#MAX + * @see SWT#RESIZE + * @see SWT#TITLE + * @see SWT#TOOL + * @see SWT#NO_TRIM + * @see SWT#SHELL_TRIM + * @see SWT#DIALOG_TRIM + * @see SWT#ON_TOP + * @see SWT#MODELESS + * @see SWT#PRIMARY_MODAL + * @see SWT#APPLICATION_MODAL + * @see SWT#SYSTEM_MODAL + * @see SWT#SHEET + */ +public Shell (Display display, int style) { + this (display, null, style, 0, false); +} + +Shell (Display display, Shell parent, int style, int /*long*/handle, boolean embedded) { + super (); + checkSubclass (); + if (display == null) display = Display.getCurrent (); + if (display == null) display = Display.getDefault (); + if (!display.isValidThread ()) { + error (SWT.ERROR_THREAD_INVALID_ACCESS); + } + if (parent != null && parent.isDisposed ()) { + error (SWT.ERROR_INVALID_ARGUMENT); + } + if (!Display.getSheetEnabled ()) { + this.center = parent != null && (style & SWT.SHEET) != 0; + } + this.style = checkStyle (parent, style); + this.parent = parent; + this.display = display; + if (handle != 0) { + if (embedded) { + view = new NSView(handle); + } else { + window = new NSWindow(handle); + state |= FOREIGN_HANDLE; + } + } + createWidget (); +} + +/** + * Constructs a new instance of this class given only its + * parent. It is created with style <code>SWT.DIALOG_TRIM</code>. + * <p> + * Note: Currently, null can be passed in for the parent. + * This has the effect of creating the shell on the currently active + * display if there is one. If there is no current display, the + * shell is created on a "default" display. <b>Passing in null as + * the parent is not considered to be good coding style, + * and may not be supported in a future release of SWT.</b> + * </p> + * + * @param parent a shell which will be the parent of the new instance + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the parent is disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + */ +public Shell (Shell parent) { + this (parent, SWT.DIALOG_TRIM); +} + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p><p> + * Note: Currently, null can be passed in for the parent. + * This has the effect of creating the shell on the currently active + * display if there is one. If there is no current display, the + * shell is created on a "default" display. <b>Passing in null as + * the parent is not considered to be good coding style, + * and may not be supported in a future release of SWT.</b> + * </p> + * + * @param parent a shell which will be the parent of the new instance + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the parent is disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#BORDER + * @see SWT#CLOSE + * @see SWT#MIN + * @see SWT#MAX + * @see SWT#RESIZE + * @see SWT#TITLE + * @see SWT#NO_TRIM + * @see SWT#SHELL_TRIM + * @see SWT#DIALOG_TRIM + * @see SWT#ON_TOP + * @see SWT#TOOL + * @see SWT#MODELESS + * @see SWT#PRIMARY_MODAL + * @see SWT#APPLICATION_MODAL + * @see SWT#SYSTEM_MODAL + * @see SWT#SHEET + */ +public Shell (Shell parent, int style) { + this (parent != null ? parent.display : null, parent, style, 0, false); +} + +/** + * Invokes platform specific functionality to allocate a new shell + * that is not embedded. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>Shell</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @param display the display for the shell + * @param handle the handle for the shell + * @return a new shell object containing the specified display and handle + * + * @since 3.3 + */ +public static Shell internal_new (Display display, int /*long*/ handle) { + return new Shell (display, null, SWT.NO_TRIM, handle, false); +} + +/** + * Invokes platform specific functionality to allocate a new shell + * that is 'embedded'. In this case, the handle represents an NSView + * that acts as an embedded SWT Shell in an AWT Canvas. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>Shell</code>. It is marked public only so that it + * can be shared within the packages provided by SWT. It is not + * available on all platforms, and should never be called from + * application code. + * </p> + * + * @param display the display for the shell + * @param handle the handle for the shell + * @return a new shell object containing the specified display and handle + * + * @since 3.5 + */ +public static Shell cocoa_new (Display display, int /*long*/ handle) { + return new Shell (display, null, SWT.NO_TRIM, handle, true); +} + +static int checkStyle (Shell parent, int style) { + style = Decorations.checkStyle (style); + style &= ~SWT.TRANSPARENT; + int mask = SWT.SYSTEM_MODAL | SWT.APPLICATION_MODAL | SWT.PRIMARY_MODAL; + if ((style & SWT.SHEET) != 0) { + if (Display.getSheetEnabled ()) { + style &= ~(SWT.CLOSE | SWT.TITLE | SWT.MIN | SWT.MAX); + if (parent == null) { + style &= ~SWT.SHEET; + style |= SWT.SHELL_TRIM; + } + } else { + style &= ~SWT.SHEET; + style |= parent == null ? SWT.SHELL_TRIM : SWT.DIALOG_TRIM; + } + if ((style & mask) == 0) { + style |= parent == null ? SWT.APPLICATION_MODAL : SWT.PRIMARY_MODAL; + } + } + int bits = style & ~mask; + if ((style & SWT.SYSTEM_MODAL) != 0) return bits | SWT.SYSTEM_MODAL; + if ((style & SWT.APPLICATION_MODAL) != 0) return bits | SWT.APPLICATION_MODAL; + if ((style & SWT.PRIMARY_MODAL) != 0) return bits | SWT.PRIMARY_MODAL; + return bits; +} + +boolean accessibilityIsIgnored(int /*long*/ id, int /*long*/ sel) { + // The content view of a shell is always ignored. + if (id == view.id) return true; + return super.accessibilityIsIgnored(id, sel); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when operations are performed on the receiver, + * by sending the listener one of the messages defined in the + * <code>ShellListener</code> interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ShellListener + * @see #removeShellListener + */ +public void addShellListener(ShellListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener(SWT.Activate,typedListener); + addListener(SWT.Close,typedListener); + addListener(SWT.Deactivate,typedListener); + addListener(SWT.Iconify,typedListener); + addListener(SWT.Deiconify,typedListener); +} + +void becomeKeyWindow (int /*long*/ id, int /*long*/ sel) { + Display display = this.display; + display.keyWindow = window; + super.becomeKeyWindow(id, sel); + display.checkFocus(); + display.keyWindow = null; +} + +void bringToTop (boolean force) { + if (getMinimized ()) return; + if (force) { + forceActive (); + } else { + setActive (); + } +} + +boolean canBecomeKeyWindow (int /*long*/ id, int /*long*/ sel) { + if (window.styleMask () == OS.NSBorderlessWindowMask) return true; + return super.canBecomeKeyWindow (id, sel); +} + +void checkOpen () { + if (!opened) resized = false; +} + +void center () { + if (parent == null) return; + Rectangle rect = getBounds (); + Rectangle parentRect = display.map (parent, null, parent.getClientArea()); + int x = Math.max (parentRect.x, parentRect.x + (parentRect.width - rect.width) / 2); + int y = Math.max (parentRect.y, parentRect.y + (parentRect.height - rect.height) / 2); + Rectangle monitorRect = parent.getMonitor ().getClientArea(); + if (x + rect.width > monitorRect.x + monitorRect.width) { + x = Math.max (monitorRect.x, monitorRect.x + monitorRect.width - rect.width); + } else { + x = Math.max (x, monitorRect.x); + } + if (y + rect.height > monitorRect.y + monitorRect.height) { + y = Math.max (monitorRect.y, monitorRect.y + monitorRect.height - rect.height); + } else { + y = Math.max (y, monitorRect.y); + } + setLocation (x, y); +} + +/** + * Requests that the window manager close the receiver in + * the same way it would be closed when the user clicks on + * the "close box" or performs some other platform specific + * key or mouse combination that indicates the window + * should be removed. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SWT#Close + * @see #dispose + */ +public void close () { + checkWidget(); + closeWidget (); +} + +void closeWidget () { + Event event = new Event (); + sendEvent (SWT.Close, event); + if (event.doit && !isDisposed ()) dispose (); +} + +public Rectangle computeTrim (int x, int y, int width, int height) { + checkWidget(); + Rectangle trim = super.computeTrim(x, y, width, height); + NSRect rect = new NSRect (); + rect.x = trim.x; + rect.y = trim.y; + rect.width = trim.width; + rect.height = trim.height; + if (window != null) { + if (!fixResize()) { + rect = window.frameRectForContentRect(rect); + } + } + return new Rectangle ((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height); +} + +void createHandle () { + state |= HIDDEN; + if (window == null) { + window = (NSWindow) new SWTWindow ().alloc (); + int styleMask = OS.NSBorderlessWindowMask; + if ((style & SWT.NO_TRIM) == 0) { + if ((style & SWT.TITLE) != 0) styleMask |= OS.NSTitledWindowMask; + if ((style & SWT.CLOSE) != 0) styleMask |= OS.NSClosableWindowMask; + if ((style & SWT.MIN) != 0) styleMask |= OS.NSMiniaturizableWindowMask; + if ((style & SWT.MAX) != 0) styleMask |= OS.NSResizableWindowMask; + if ((style & SWT.RESIZE) != 0) styleMask |= OS.NSResizableWindowMask; + } + NSScreen screen = null; + NSScreen primaryScreen = new NSScreen(NSScreen.screens().objectAtIndex(0)); + if (parent != null) screen = parent.getShell().window.screen(); + if (screen == null) screen = primaryScreen; + window = window.initWithContentRect(new NSRect(), styleMask, OS.NSBackingStoreBuffered, false, screen); + if ((style & (SWT.NO_TRIM | SWT.BORDER | SWT.SHELL_TRIM)) == 0 || (style & (SWT.TOOL | SWT.SHEET)) != 0) { + window.setHasShadow (true); + } + if ((style & SWT.NO_TRIM) == 0) { + NSSize size = window.minSize(); + size.width = NSWindow.minFrameWidthWithTitle(NSString.stringWith(""), styleMask); + window.setMinSize(size); + } + if (fixResize ()) { + if (window.respondsToSelector(OS.sel_setMovable_)) { + OS.objc_msgSend(window.id, OS.sel_setMovable_, 0); + } + } + display.cascadeWindow(window, screen); + NSRect screenFrame = screen.frame(); + float /*double*/ width = screenFrame.width * 5 / 8, height = screenFrame.height * 5 / 8;; + NSRect frame = window.frame(); + NSRect primaryFrame = primaryScreen.frame(); + frame.y = primaryFrame.height - ((primaryFrame.height - (frame.y + frame.height)) + height); + frame.width = width; + frame.height = height; + window.setFrame(frame, false); + if ((style & SWT.ON_TOP) != 0) { + window.setLevel(OS.NSStatusWindowLevel); + } + super.createHandle (); + topView ().setHidden (true); + } else { +// int /*long*/ cls = OS.objc_lookUpClass ("SWTWindow"); +// OS.object_setClass(window.id, cls); + state &= ~HIDDEN; + //TODO - get the content of the foreign window instead of creating it + super.createHandle (); + style |= SWT.NO_BACKGROUND; + } + window.setAcceptsMouseMovedEvents(true); + windowDelegate = (SWTWindowDelegate)new SWTWindowDelegate().alloc().init(); + window.setDelegate(windowDelegate); + id id = window.fieldEditor (true, null); + if (id != null) { + OS.object_setClass (id.id, OS.objc_getClass ("SWTEditorView")); + } +} + +void deregister () { + super.deregister (); + if (window != null) display.removeWidget (window); + if (windowDelegate != null) display.removeWidget (windowDelegate); +} + +void destroyWidget () { + NSWindow window = this.window; + Display display = this.display; + boolean sheet = (style & (SWT.SHEET)) != 0; + releaseHandle (); + if (window != null) { + if (sheet) { + NSApplication application = NSApplication.sharedApplication(); + application.endSheet(window, 0); + } + window.close(); + } + //If another shell is not going to become active, clear the menu bar. + if (!display.isDisposed () && display.getShells ().length == 0) { + display.setMenuBar (null); + } +} + +void drawBackground (int /*long*/ id, NSGraphicsContext context, NSRect rect) { + if (id != view.id) return; + if (regionPath != null && background == null) { + context.saveGraphicsState(); + NSColor.windowBackgroundColor().setFill(); + NSBezierPath.fillRect(rect); + context.restoreGraphicsState(); + return; + } + super.drawBackground (id, context, rect); +} + +Control findBackgroundControl () { + return background != null || backgroundImage != null ? this : null; +} + +Composite findDeferredControl () { + return layoutCount > 0 ? this : null; +} + +Cursor findCursor () { + return cursor; +} + +boolean fixResize () { + /* + * Feature in Cocoa. It is not possible to have a resizable window + * without the title bar. The fix is to resize the content view on + * top of the title bar. + */ + if ((style & SWT.NO_TRIM) == 0) { + if ((style & SWT.RESIZE) != 0 && (style & (SWT.TITLE | SWT.CLOSE | SWT.MIN | SWT.MAX)) == 0) { + return true; + } + } + return false; +} + +void fixShell (Shell newShell, Control control) { + if (this == newShell) return; + if (control == lastActive) setActiveControl (null); +} + +/** + * If the receiver is visible, moves it to the top of the + * drawing order for the display on which it was created + * (so that all other shells on that display, which are not + * the receiver's children will be drawn behind it) and forces + * the window manager to make the shell active. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.0 + * @see Control#moveAbove + * @see Control#setFocus + * @see Control#setVisible + * @see Display#getActiveShell + * @see Decorations#setDefaultButton(Button) + * @see Shell#open + * @see Shell#setActive + */ +public void forceActive () { + checkWidget (); + if (!isVisible()) return; + if (window == null) return; + makeKeyAndOrderFront (); + NSApplication application = NSApplication.sharedApplication (); + application.activateIgnoringOtherApps (true); +} + +/** + * Returns the receiver's alpha value. The alpha value + * is between 0 (transparent) and 255 (opaque). + * + * @return the alpha value + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public int getAlpha () { + checkWidget (); + // TODO: Should we support embedded frame alpha? + if (window == null) return 255; + return (int)(window.alphaValue() * 255); +} + +public Rectangle getBounds () { + checkWidget(); + NSRect frame = (window == null ? view.frame() : window.frame()); + float /*double*/ y = display.getPrimaryFrame().height - (int)(frame.y + frame.height); + return new Rectangle ((int)frame.x, (int)y, (int)frame.width, (int)frame.height); +} + +public Rectangle getClientArea () { + checkWidget(); + NSRect rect; + if (window != null) { + rect = window.frame(); + if (!fixResize ()) { + rect = window.contentRectForFrameRect(rect); + } + } else { + rect = scrollView != null ? scrollView.frame() : view.frame(); + } + int width = (int)rect.width, height = (int)rect.height; + if (scrollView != null) { + NSSize size = new NSSize(); + size.width = width; + size.height = height; + size = NSScrollView.contentSizeForFrameSize(size, (style & SWT.H_SCROLL) != 0, (style & SWT.V_SCROLL) != 0, OS.NSNoBorder); + width = (int)size.width; + height = (int)size.height; + } + return new Rectangle (0, 0, width, height); +} + +/** + * Returns <code>true</code> if the receiver is currently + * in fullscreen state, and false otherwise. + * <p> + * + * @return the fullscreen state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public boolean getFullScreen () { + checkWidget(); + return fullScreen; +} + +/** + * Returns the receiver's input method editor mode. This + * will be the result of bitwise OR'ing together one or + * more of the following constants defined in class + * <code>SWT</code>: + * <code>NONE</code>, <code>ROMAN</code>, <code>DBCS</code>, + * <code>PHONETIC</code>, <code>NATIVE</code>, <code>ALPHA</code>. + * + * @return the IME mode + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SWT + */ +public int getImeInputMode () { + checkWidget(); + return SWT.NONE; +} + +public Point getLocation () { + checkWidget(); + // TODO: frame is relative to superview. What does getLocation mean in the embedded case? + NSRect frame = (window != null ? window.frame() : view.frame()); + float /*double*/ y = display.getPrimaryFrame().height - (int)(frame.y + frame.height); + return new Point ((int)frame.x, (int)y); +} + +public boolean getMaximized () { + checkWidget(); + if (window == null) return false; + return !fullScreen && window.isZoomed(); +} + +Shell getModalShell () { + Shell shell = null; + Shell [] modalShells = display.modalShells; + if (modalShells != null) { + int bits = SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL; + int index = modalShells.length; + while (--index >= 0) { + Shell modal = modalShells [index]; + if (modal != null) { + if ((modal.style & bits) != 0) { + Control control = this; + while (control != null) { + if (control == modal) break; + control = control.parent; + } + if (control != modal) return modal; + break; + } + if ((modal.style & SWT.PRIMARY_MODAL) != 0) { + if (shell == null) shell = getShell (); + if (modal.parent == shell) return modal; + } + } + } + } + return null; +} + +/** + * Gets the receiver's modified state. + * + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.5 + */ +public boolean getModified () { + checkWidget (); + return window.isDocumentEdited (); +} + +public boolean getMinimized () { + checkWidget(); + if (!getVisible ()) return super.getMinimized (); + if (window == null) return false; + return window.isMiniaturized(); +} + +/** + * Returns a point describing the minimum receiver's size. The + * x coordinate of the result is the minimum width of the receiver. + * The y coordinate of the result is the minimum height of the + * receiver. + * + * @return the receiver's size + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public Point getMinimumSize () { + checkWidget(); + if (window == null) return new Point(0, 0); + NSSize size = window.minSize(); + return new Point((int)size.width, (int)size.height); +} + +/** + * Returns the region that defines the shape of the shell, + * or null if the shell has the default shape. + * + * @return the region that defines the shape of the shell (or null) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + * + */ +public Region getRegion () { + /* This method is needed for the @since 3.0 Javadoc */ + checkWidget (); + return region; +} + +public Shell getShell () { + checkWidget(); + return this; +} + +/** + * Returns an array containing all shells which are + * descendants of the receiver. + * <p> + * @return the dialog shells + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Shell [] getShells () { + checkWidget(); + int count = 0; + Shell [] shells = display.getShells (); + for (int i=0; i<shells.length; i++) { + Control shell = shells [i]; + do { + shell = shell.parent; + } while (shell != null && shell != this); + if (shell == this) count++; + } + int index = 0; + Shell [] result = new Shell [count]; + for (int i=0; i<shells.length; i++) { + Control shell = shells [i]; + do { + shell = shell.parent; + } while (shell != null && shell != this); + if (shell == this) { + result [index++] = shells [i]; + } + } + return result; +} + +public Point getSize () { + checkWidget(); + NSRect frame = (window != null ? window.frame() : view.frame()); + return new Point ((int) frame.width, (int) frame.height); +} + +float getThemeAlpha () { + return 1; +} + +boolean hasBorder () { + return false; +} + +void helpRequested(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + Control control = display.getFocusControl(); + while (control != null) { + if (control.hooks (SWT.Help)) { + control.postEvent (SWT.Help); + break; + } + control = control.parent; + } +} + +void invalidateVisibleRegion () { + resetVisibleRegion (); + invalidateChildrenVisibleRegion (); +} + +boolean isDrawing () { + return getDrawing (); +} + +public boolean isEnabled () { + checkWidget(); + return getEnabled (); +} + +boolean isEnabledCursor () { + return true; +} + +public boolean isVisible () { + checkWidget(); + return getVisible (); +} + +boolean makeFirstResponder (int /*long*/ id, int /*long*/ sel, int /*long*/ responder) { + Display display = this.display; + boolean result = super.makeFirstResponder(id, sel, responder); + display.checkFocus(); + return result; +} + +void makeKeyAndOrderFront() { + /* + * Bug in Cocoa. If a child window becomes the key window when its + * parent window is miniaturized, the parent window appears as if + * restored to its full size without actually being restored. In this + * case the parent window does become active when its child is closed + * and the user is forced to restore the window from the dock. + * The fix is to be sure that the parent window is deminiaturized before + * making the child a key window. + */ + if (parent != null) { + Shell shell = (Shell) parent; + if (shell.window.isMiniaturized()) shell.window.deminiaturize(null); + } + window.makeKeyAndOrderFront (null); +} + +void noResponderFor(int /*long*/ id, int /*long*/ sel, int /*long*/ selector) { + /** + * Feature in Cocoa. If the selector is keyDown and nothing has handled the event + * a system beep is generated. There's no need to beep, as many keystrokes in the SWT + * are listened for and acted upon but not explicitly handled in a keyDown handler. Fix is to + * not call the default implementation when a keyDown: is being handled. + */ + if (selector != OS.sel_keyDown_) super.noResponderFor(id, sel, selector); +} + +/** + * Moves the receiver to the top of the drawing order for + * the display on which it was created (so that all other + * shells on that display, which are not the receiver's + * children will be drawn behind it), marks it visible, + * sets the focus and asks the window manager to make the + * shell active. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Control#moveAbove + * @see Control#setFocus + * @see Control#setVisible + * @see Display#getActiveShell + * @see Decorations#setDefaultButton(Button) + * @see Shell#setActive + * @see Shell#forceActive + */ +public void open () { + checkWidget(); + int mask = SWT.PRIMARY_MODAL | SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL; + if ((style & mask) != 0) { + display.setModalShell (this); + } else { + updateModal (); + } + bringToTop (false); + setWindowVisible (true, true); + if (isDisposed ()) return; + if (!restoreFocus () && !traverseGroup (true)) { + // if the parent shell is minimized, setting focus will cause it + // to become unminimized. + if (parent == null || !((Shell)parent).window.isMiniaturized()) { + setFocus (); + } + } +} + +public boolean print (GC gc) { + checkWidget (); + if (gc == null) error (SWT.ERROR_NULL_ARGUMENT); + if (gc.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + return false; +} + +void register () { + super.register (); + if (window != null) display.addWidget (window, this); + if (windowDelegate != null) display.addWidget (windowDelegate, this); +} + +void releaseChildren (boolean destroy) { + Shell [] shells = getShells (); + for (int i=0; i<shells.length; i++) { + Shell shell = shells [i]; + if (shell != null && !shell.isDisposed ()) { + shell.dispose (); + } + } + super.releaseChildren (destroy); +} + +void releaseHandle () { + if (window != null) window.setDelegate(null); + if (windowDelegate != null) windowDelegate.release(); + windowDelegate = null; + super.releaseHandle (); + window = null; +} + +void releaseParent () { + /* Do nothing */ +} + +void releaseWidget () { + super.releaseWidget (); + display.clearModal (this); + updateParent (false); + display.updateQuitMenu(); + lastActive = null; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when operations are performed on the receiver. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ShellListener + * @see #addShellListener + */ +public void removeShellListener(ShellListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook(SWT.Activate, listener); + eventTable.unhook(SWT.Close, listener); + eventTable.unhook(SWT.Deactivate, listener); + eventTable.unhook(SWT.Iconify,listener); + eventTable.unhook(SWT.Deiconify,listener); +} + +void sendToolTipEvent (boolean enter) { + if (!isVisible()) return; + if (tooltipTag == 0) { + NSView view = window.contentView(); + tooltipTag = view.addToolTipRect(new NSRect(), window, 0); + if (tooltipTag != 0) { + NSTrackingArea trackingArea = new NSTrackingArea(tooltipTag); + id owner = trackingArea.owner(); + if (owner != null) tooltipOwner = owner.id; + id userInfo = trackingArea.userInfo(); + if (userInfo != null) { + tooltipUserData = userInfo.id; + } else { + int /*long*/ [] value = new int /*long*/ [1]; + OS.object_getInstanceVariable(tooltipTag, new byte[]{'_','u', 's', 'e', 'r', 'I', 'n', 'f', 'o'}, value); + tooltipUserData = value[0]; + } + } + } + if (tooltipTag == 0 || tooltipOwner == 0 || tooltipUserData == 0) return; + NSPoint pt = window.convertScreenToBase(NSEvent.mouseLocation()); + NSEvent event = NSEvent.enterExitEventWithType(enter ? OS.NSMouseEntered : OS.NSMouseExited, pt, 0, 0, window.windowNumber(), null, 0, tooltipTag, tooltipUserData); + OS.objc_msgSend(tooltipOwner, enter ? OS.sel_mouseEntered_ : OS.sel_mouseExited_, event.id); +} + +/** + * If the receiver is visible, moves it to the top of the + * drawing order for the display on which it was created + * (so that all other shells on that display, which are not + * the receiver's children will be drawn behind it) and asks + * the window manager to make the shell active + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.0 + * @see Control#moveAbove + * @see Control#setFocus + * @see Control#setVisible + * @see Display#getActiveShell + * @see Decorations#setDefaultButton(Button) + * @see Shell#open + * @see Shell#setActive + */ +public void setActive () { + if (window == null) return; + checkWidget (); + if (!isVisible()) return; + makeKeyAndOrderFront (); +} + +void setActiveControl (Control control) { + if (control != null && control.isDisposed ()) control = null; + if (lastActive != null && lastActive.isDisposed ()) lastActive = null; + if (lastActive == control) return; + + /* + * Compute the list of controls to be activated and + * deactivated by finding the first common parent + * control. + */ + Control [] activate = (control == null) ? new Control[0] : control.getPath (); + Control [] deactivate = (lastActive == null) ? new Control[0] : lastActive.getPath (); + lastActive = control; + int index = 0, length = Math.min (activate.length, deactivate.length); + while (index < length) { + if (activate [index] != deactivate [index]) break; + index++; + } + + /* + * It is possible (but unlikely), that application + * code could have destroyed some of the widgets. If + * this happens, keep processing those widgets that + * are not disposed. + */ + for (int i=deactivate.length-1; i>=index; --i) { + if (!deactivate [i].isDisposed ()) { + deactivate [i].sendEvent (SWT.Deactivate); + } + } + for (int i=activate.length-1; i>=index; --i) { + if (!activate [i].isDisposed ()) { + activate [i].sendEvent (SWT.Activate); + } + } +} + +/** + * Sets the receiver's alpha value which must be + * between 0 (transparent) and 255 (opaque). + * <p> + * This operation requires the operating system's advanced + * widgets subsystem which may not be available on some + * platforms. + * </p> + * @param alpha the alpha value + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public void setAlpha (int alpha) { + if (window == null) return; + checkWidget (); + alpha &= 0xFF; + window.setAlphaValue (alpha / 255f); +} + +void setBounds (int x, int y, int width, int height, boolean move, boolean resize) { + // Embedded Shells are not resizable. + if (window == null) return; + if (fullScreen) setFullScreen (false); + boolean sheet = window.isSheet(); + if (sheet && move && !resize) return; + int screenHeight = (int) display.getPrimaryFrame().height; + NSRect frame = window.frame(); + if (!move) { + x = (int)frame.x; + y = screenHeight - (int)(frame.y + frame.height); + } + if (resize) { + NSSize minSize = window.minSize(); + width = Math.max(width, (int)minSize.width); + height = Math.max(height, (int)minSize.height); + } else { + width = (int)frame.width; + height = (int)frame.height; + } + if (sheet) { + y = screenHeight - (int)(frame.y + frame.height); + NSRect parentRect = parent.getShell().window.frame(); + frame.width = width; + frame.height = height; + frame.x = parentRect.x + (parentRect.width - frame.width) / 2; + frame.y = screenHeight - (int)(y + frame.height); + window.setFrame(frame, isVisible(), true); + } else { + frame.x = x; + frame.y = screenHeight - (int)(y + height); + frame.width = width; + frame.height = height; + window.setFrame(frame, isVisible()); + } +} + +void setClipRegion (float /*double*/ x, float /*double*/ y) { + if (regionPath != null) { + NSAffineTransform transform = NSAffineTransform.transform(); + transform.translateXBy(-x, -y); + regionPath.transformUsingAffineTransform(transform); + regionPath.addClip(); + transform.translateXBy(2*x, 2*y); + regionPath.transformUsingAffineTransform(transform); + } +} + +public void setEnabled (boolean enabled) { + checkWidget(); + if (((state & DISABLED) == 0) == enabled) return; + super.setEnabled (enabled); +// if (enabled && OS.IsWindowActive (shellHandle)) { +// if (!restoreFocus ()) traverseGroup (false); +// } +} + +/** + * Sets the full screen state of the receiver. + * If the argument is <code>true</code> causes the receiver + * to switch to the full screen state, and if the argument is + * <code>false</code> and the receiver was previously switched + * into full screen state, causes the receiver to switch back + * to either the maximized or normal states. + * <p> + * Note: The result of intermixing calls to <code>setFullScreen(true)</code>, + * <code>setMaximized(true)</code> and <code>setMinimized(true)</code> will + * vary by platform. Typically, the behavior will match the platform user's + * expectations, but not always. This should be avoided if possible. + * </p> + * + * @param fullScreen the new fullscreen state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public void setFullScreen (boolean fullScreen) { + checkWidget (); + if (this.fullScreen == fullScreen) return; + this.fullScreen = fullScreen; + + if (fullScreen) { + currentFrame = window.frame(); + window.setShowsResizeIndicator(false); //only hides resize indicator + if (window.respondsToSelector(OS.sel_setMovable_)) { + OS.objc_msgSend(window.id, OS.sel_setMovable_, 0); + } + + fullScreenFrame = NSScreen.mainScreen().frame(); + if (getMonitor().equals(display.getPrimaryMonitor ())) { + if (menuBar != null) { + float /*double*/ menuBarHt = currentFrame.height - contentView().frame().height; + fullScreenFrame.height -= menuBarHt; + OS.SetSystemUIMode(OS.kUIModeContentHidden, 0); + } + else { + OS.SetSystemUIMode(OS.kUIModeAllHidden, 0); + } + } + window.setFrame(fullScreenFrame, true); + window.contentView().setFrame(fullScreenFrame); + } else { + window.setShowsResizeIndicator(true); + if (window.respondsToSelector(OS.sel_setMovable_)) { + OS.objc_msgSend(window.id, OS.sel_setMovable_, 1); + } + OS.SetSystemUIMode(OS.kUIModeNormal, 0); + window.setFrame(currentFrame, true); + } +} + +public void setMenuBar (Menu menu) { + checkWidget(); + super.setMenuBar (menu); + if (display.getActiveShell () == this) { + display.setMenuBar (menuBar); + } +} + +/** + * Sets the input method editor mode to the argument which + * should be the result of bitwise OR'ing together one or more + * of the following constants defined in class <code>SWT</code>: + * <code>NONE</code>, <code>ROMAN</code>, <code>DBCS</code>, + * <code>PHONETIC</code>, <code>NATIVE</code>, <code>ALPHA</code>. + * + * @param mode the new IME mode + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SWT + */ +public void setImeInputMode (int mode) { + checkWidget(); +} + +public void setMaximized (boolean maximized) { + checkWidget(); + super.setMaximized (maximized); + if (window == null) return; + if (window.isZoomed () == maximized) return; + window.zoom (null); +} + +public void setMinimized (boolean minimized) { + checkWidget(); + super.setMinimized (minimized); + if (window == null) return; + if (minimized) { + window.miniaturize (null); + } else { + window.deminiaturize (null); + } +} + +/** + * Sets the receiver's minimum size to the size specified by the arguments. + * If the new minimum size is larger than the current size of the receiver, + * the receiver is resized to the new minimum size. + * + * @param width the new minimum width for the receiver + * @param height the new minimum height for the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public void setMinimumSize (int width, int height) { + checkWidget(); + if (window == null) return; + NSSize size = new NSSize(); + size.width = width; + size.height = height; + window.setMinSize(size); + NSRect frame = window.frame(); + if (width > frame.width || height > frame.height) { + width = (int)(width > frame.width ? width : frame.width); + height = (int)(height > frame.height ? height : frame.height); + setBounds(0, 0, width, height, false, true); + } +} + +/** + * Sets the receiver's minimum size to the size specified by the argument. + * If the new minimum size is larger than the current size of the receiver, + * the receiver is resized to the new minimum size. + * + * @param size the new minimum size for the receiver + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the point is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public void setMinimumSize (Point size) { + checkWidget(); + if (size == null) error (SWT.ERROR_NULL_ARGUMENT); + setMinimumSize (size.x, size.y); +} + +/** + * Sets the receiver's modified state as specified by the argument. + * + * @param modified the new modified state for the receiver + * + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.5 + */ +public void setModified (boolean modified) { + checkWidget (); + window.setDocumentEdited (modified); +} + +/** + * Sets the shape of the shell to the region specified + * by the argument. When the argument is null, the + * default shape of the shell is restored. The shell + * must be created with the style SWT.NO_TRIM in order + * to specify a region. + * + * @param region the region that defines the shape of the shell (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the region has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + * + */ +public void setRegion (Region region) { + checkWidget (); + if ((style & SWT.NO_TRIM) == 0) return; + if (window == null) return; + if (region != null && region.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT); + this.region = region; + if (regionPath != null) regionPath.release(); + regionPath = getPath(region); + if (region != null) { + window.setBackgroundColor(NSColor.clearColor()); + window.setOpaque(false); + } else { + window.setBackgroundColor(NSColor.windowBackgroundColor()); + window.setOpaque(true); + } + window.contentView().setNeedsDisplay(true); + if (isVisible() && window.hasShadow()) { + window.display(); + window.invalidateShadow(); + } +} + +public void setText (String string) { + checkWidget(); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if (window == null) return; + super.setText (string); + NSString str = NSString.stringWith(string); + window.setTitle(str); +} + +public void setVisible (boolean visible) { + checkWidget(); + int mask = SWT.PRIMARY_MODAL | SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL; + if ((style & mask) != 0) { + if (visible) { + display.setModalShell (this); + } else { + display.clearModal (this); + } + } else { + updateModal (); + } + if (window == null) { + super.setVisible(visible); + } else { + setWindowVisible (visible, false); + } +} + +void setWindowVisible (boolean visible, boolean key) { + if (visible) { + if ((state & HIDDEN) == 0) return; + state &= ~HIDDEN; + } else { + if ((state & HIDDEN) != 0) return; + state |= HIDDEN; + } + if (window != null && (window.isVisible() == visible)) return; + if (visible) { + display.clearPool (); + if (center && !moved) { + if (isDisposed ()) return; + center (); + } + sendEvent (SWT.Show); + if (isDisposed ()) return; + topView ().setHidden (false); + invalidateVisibleRegion(); + if ((style & (SWT.SHEET)) != 0) { + NSApplication application = NSApplication.sharedApplication(); + application.beginSheet(window, ((Shell)parent).window, null, 0, 0); + if (OS.VERSION <= 0x1060 && window.respondsToSelector(OS.sel__setNeedsToUseHeartBeatWindow_)) { + OS.objc_msgSend(window.id, OS.sel__setNeedsToUseHeartBeatWindow_, 0); + } + } else { + // If the parent window is miniaturized, the window will be shown + // when its parent is shown. + boolean parentMinimized = parent != null && ((Shell)parent).window.isMiniaturized(); + if (!parentMinimized) { + if (key) { + makeKeyAndOrderFront (); + } else { + window.orderFront (null); + } + } + } + updateParent (visible); + opened = true; + if (!moved) { + moved = true; + sendEvent (SWT.Move); + if (isDisposed ()) return; + } + if (!resized) { + resized = true; + sendEvent (SWT.Resize); + if (isDisposed ()) return; + if (layout != null) { + markLayout (false, false); + updateLayout (false); + } + } + } else { + updateParent (visible); + if ((style & (SWT.SHEET)) != 0) { + NSApplication application = NSApplication.sharedApplication(); + application.endSheet(window, 0); + } + window.orderOut (null); + topView ().setHidden (true); + invalidateVisibleRegion(); + sendEvent (SWT.Hide); + } + + display.updateQuitMenu(); +} + +void setZOrder () { + if (scrollView != null) scrollView.setDocumentView (view); + if (window == null) return; + window.setContentView (scrollView != null ? scrollView : view); + if (fixResize ()) { + NSRect rect = window.frame(); + rect.x = rect.y = 0; + window.contentView().setFrame(rect); + } +} + +void setZOrder (Control control, boolean above) { + if (window == null) return; + if (control == null) { + if (above) { + window.orderFront(null); + } else { + window.orderBack(null); + } + } else { + NSWindow otherWindow = control.getShell().window; + window.orderWindow(above ? OS.NSWindowAbove : OS.NSWindowBelow, otherWindow.windowNumber()); + } +} + +boolean traverseEscape () { + if (parent == null) return false; + if (!isVisible () || !isEnabled ()) return false; + close (); + return true; +} + +void updateModal () { + // do nothing +} + +void updateParent (boolean visible) { + if (visible) { + if (parent != null && parent.getVisible ()) { + ((Shell)parent).window.addChildWindow (window, OS.NSWindowAbove); + } + } else { + NSWindow parentWindow = window.parentWindow (); + if (parentWindow != null) parentWindow.removeChildWindow (window); + } + Shell [] shells = getShells (); + for (int i = 0; i < shells.length; i++) { + Shell shell = shells [i]; + if (shell.parent == this && shell.getVisible ()) { + shell.updateParent (visible); + } + } +} + +void updateSystemUIMode () { + if (!getMonitor ().equals (display.getPrimaryMonitor ())) return; + if (fullScreen) { + int mode = OS.kUIModeAllHidden; + if (menuBar != null) { + mode = OS.kUIModeContentHidden; + } + OS.SetSystemUIMode (mode, 0); + window.setFrame(fullScreenFrame, true); + } else { + OS.SetSystemUIMode (OS.kUIModeNormal, 0); + } +} + +int /*long*/ view_stringForToolTip_point_userData (int /*long*/ id, int /*long*/ sel, int /*long*/ view, int /*long*/ tag, int /*long*/ point, int /*long*/ userData) { + NSPoint pt = new NSPoint(); + OS.memmove (pt, point, NSPoint.sizeof); + Control control = display.findControl (false); + if (control == null) return 0; + Widget target = control.findTooltip (new NSView (view).convertPoint_toView_ (pt, null)); + String string = target.tooltipText (); + if (string == null) return 0; + char[] chars = new char [string.length ()]; + string.getChars (0, chars.length, chars, 0); + int length = fixMnemonic (chars); + return NSString.stringWithCharacters (chars, length).id; +} + +void windowDidBecomeKey(int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { + super.windowDidBecomeKey(id, sel, notification); + Display display = this.display; + display.setMenuBar (menuBar); + sendEvent (SWT.Activate); + if (isDisposed ()) return; + Shell parentShell = this; + while (parentShell.parent != null) { + parentShell = (Shell) parentShell.parent; + if (parentShell.fullScreen) { + break; + } + } + if (!parentShell.fullScreen || menuBar != null) { + updateSystemUIMode (); + } else { + parentShell.updateSystemUIMode (); + } +} + +void windowDidMove(int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { + moved = true; + sendEvent(SWT.Move); +} + +void windowDidResize(int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { + if (fullScreen) { + window.setFrame(fullScreenFrame, true); + window.contentView().setFrame(fullScreenFrame); + } + if (fixResize ()) { + NSRect rect = window.frame (); + rect.x = rect.y = 0; + window.contentView ().setFrame (rect); + } + resized = true; + sendEvent (SWT.Resize); + if (isDisposed ()) return; + if (layout != null) { + markLayout (false, false); + updateLayout (false); + } +} + +void windowDidResignKey(int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { + super.windowDidResignKey(id, sel, notification); + sendEvent (SWT.Deactivate); +} + +void windowSendEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ event) { + NSEvent nsEvent = new NSEvent (event); + int type = (int)/*64*/nsEvent.type (); + switch (type) { + case OS.NSLeftMouseUp: + case OS.NSRightMouseUp: + case OS.NSOtherMouseUp: + case OS.NSMouseMoved: + NSView[] hitView = new NSView[1]; + Control control = display.findControl (false, hitView); + if (control != null && (!control.isActive() || !control.isEnabled())) control = null; + if (type == OS.NSMouseMoved) { + Control trimControl = control; + if (trimControl != null && trimControl.isTrim (hitView[0])) trimControl = null; + display.checkEnterExit (trimControl, nsEvent, false); + if (trimControl != null) trimControl.sendMouseEvent (nsEvent, type, false); + } + Widget target = null; + if (control != null) target = control.findTooltip (nsEvent.locationInWindow()); + if (display.tooltipControl != control || display.tooltipTarget != target) { + Control oldControl = display.tooltipControl; + Shell oldShell = oldControl != null && !oldControl.isDisposed() ? oldControl.getShell() : null; + Shell shell = control != null && !control.isDisposed() ? control.getShell() : null; + if (oldShell != null) oldShell.sendToolTipEvent (false); + if (shell != null) shell.sendToolTipEvent (true); + } + display.tooltipControl = control; + display.tooltipTarget = target; + break; + + case OS.NSKeyDown: + /** + * Feature in cocoa. Control+Tab, Ctrl+Shift+Tab, Ctrl+PageDown and Ctrl+PageUp are + * swallowed to handle native traversal. If we find that, force the key event to + * the first responder. + */ + if ((nsEvent.modifierFlags() & OS.NSControlKeyMask) != 0) { + NSString chars = nsEvent.characters(); + + if (chars != null && chars.length() == 1) { + int firstChar = (int)/*64*/chars.characterAtIndex(0); + + // Shift-tab appears as control-Y. + switch (firstChar) { + case '\t': + case 25: + case OS.NSPageDownFunctionKey: + case OS.NSPageUpFunctionKey: + window.firstResponder().keyDown(nsEvent); + return; + } + } + } + break; + } + super.windowSendEvent (id, sel, event); +} + +boolean windowShouldClose(int /*long*/ id, int /*long*/ sel, int /*long*/ window) { + closeWidget (); + return false; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Slider.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Slider.java new file mode 100755 index 0000000000..305d47d879 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Slider.java @@ -0,0 +1,527 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; + +/** + * Instances of this class are selectable user interface + * objects that represent a range of positive, numeric values. + * <p> + * At any given moment, a given slider will have a + * single 'selection' that is considered to be its + * value, which is constrained to be within the range of + * values the slider represents (that is, between its + * <em>minimum</em> and <em>maximum</em> values). + * </p><p> + * Typically, sliders will be made up of five areas: + * <ol> + * <li>an arrow button for decrementing the value</li> + * <li>a page decrement area for decrementing the value by a larger amount</li> + * <li>a <em>thumb</em> for modifying the value by mouse dragging</li> + * <li>a page increment area for incrementing the value by a larger amount</li> + * <li>an arrow button for incrementing the value</li> + * </ol> + * Based on their style, sliders are either <code>HORIZONTAL</code> + * (which have a left facing button for decrementing the value and a + * right facing button for incrementing it) or <code>VERTICAL</code> + * (which have an upward facing button for decrementing the value + * and a downward facing buttons for incrementing it). + * </p><p> + * On some platforms, the size of the slider's thumb can be + * varied relative to the magnitude of the range of values it + * represents (that is, relative to the difference between its + * maximum and minimum values). Typically, this is used to + * indicate some proportional value such as the ratio of the + * visible area of a document to the total amount of space that + * it would take to display it. SWT supports setting the thumb + * size even if the underlying platform does not, but in this + * case the appearance of the slider will not change. + * </p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>HORIZONTAL, VERTICAL</dd> + * <dt><b>Events:</b></dt> + * <dd>Selection</dd> + * </dl> + * <p> + * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see ScrollBar + * @see <a href="http://www.eclipse.org/swt/snippets/#slider">Slider snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Slider extends Control { + boolean dragging; + int minimum, maximum, thumb; + int increment = 1; + int pageIncrement = 10; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#HORIZONTAL + * @see SWT#VERTICAL + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Slider (Composite parent, int style) { + super (parent, checkStyle (style)); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the user changes the receiver's value, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * When <code>widgetSelected</code> is called, the event object detail field contains one of the following values: + * <code>SWT.NONE</code> - for the end of a drag. + * <code>SWT.DRAG</code>. + * <code>SWT.HOME</code>. + * <code>SWT.END</code>. + * <code>SWT.ARROW_DOWN</code>. + * <code>SWT.ARROW_UP</code>. + * <code>SWT.PAGE_DOWN</code>. + * <code>SWT.PAGE_UP</code>. + * <code>widgetDefaultSelected</code> is not called. + * </p> + * + * @param listener the listener which should be notified when the user changes the receiver's value + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener(listener); + addListener(SWT.Selection,typedListener); + addListener(SWT.DefaultSelection,typedListener); +} + +static int checkStyle (int style) { + return checkBits (style, SWT.HORIZONTAL, SWT.VERTICAL, 0, 0, 0, 0); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget(); + int width = 0, height = 0; + if ((style & SWT.HORIZONTAL) != 0) { + height = (int)NSScroller.scrollerWidthForControlSize(((NSScroller)view).controlSize()); + width = height * 10; + } else { + width = (int)NSScroller.scrollerWidthForControlSize(((NSScroller)view).controlSize()); + height = width * 10; + } + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + return new Point (width, height); +} + +void createHandle () { + NSScroller widget = (NSScroller)new SWTScroller().alloc(); + NSRect rect = new NSRect(); + if ((style & SWT.HORIZONTAL) != 0) { + rect.width = 1; + } else { + rect.height = 1; + } + widget.initWithFrame(rect); + widget.setEnabled(true); + widget.setTarget(widget); + widget.setAction(OS.sel_sendSelection); + view = widget; + updateBar(0, minimum, maximum, thumb); +} + +void createWidget () { + maximum = 100; + thumb = 10; + super.createWidget(); +} + +NSFont defaultNSFont () { + return display.scrollerFont; +} + +/** + * Returns the amount that the receiver's value will be + * modified by when the up/down (or right/left) arrows + * are pressed. + * + * @return the increment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getIncrement () { + checkWidget(); + return increment; +} + +/** + * Returns the maximum value which the receiver will allow. + * + * @return the maximum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getMaximum () { + checkWidget(); + return maximum; +} + +/** + * Returns the minimum value which the receiver will allow. + * + * @return the minimum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getMinimum () { + checkWidget(); + return minimum; +} + +/** + * Returns the amount that the receiver's value will be + * modified by when the page increment/decrement areas + * are selected. + * + * @return the page increment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getPageIncrement () { + checkWidget(); + return pageIncrement; +} + +/** + * Returns the 'selection', which is the receiver's value. + * + * @return the selection + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getSelection () { + checkWidget(); + NSScroller widget = (NSScroller)view; + double value = widget.doubleValue(); + return (int)(0.5f + ((maximum - thumb - minimum) * value + minimum)); +} + +/** + * Returns the size of the receiver's thumb relative to the + * difference between its maximum and minimum values. + * + * @return the thumb value + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getThumb () { + checkWidget(); + return thumb; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the user changes the receiver's value. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook(SWT.Selection, listener); + eventTable.unhook(SWT.DefaultSelection,listener); +} + +void sendSelection () { + Event event = new Event(); + int hitPart = (int)/*64*/((NSScroller)view).hitPart(); + int value = getSelection (); + switch (hitPart) { + case OS.NSScrollerDecrementLine: + event.detail = SWT.ARROW_UP; + value -= increment; + break; + case OS.NSScrollerDecrementPage: + value -= pageIncrement; + event.detail = SWT.PAGE_UP; + break; + case OS.NSScrollerIncrementLine: + value += increment; + event.detail = SWT.ARROW_DOWN; + break; + case OS.NSScrollerIncrementPage: + value += pageIncrement; + event.detail = SWT.PAGE_DOWN; + break; + case OS.NSScrollerKnob: + event.detail = SWT.DRAG; + break; + } + if (event.detail != SWT.DRAG) { + setSelection(value); + } + sendEvent(SWT.Selection, event); +} + +/** + * Sets the amount that the receiver's value will be + * modified by when the up/down (or right/left) arrows + * are pressed to the argument, which must be at least + * one. + * + * @param value the new increment (must be greater than zero) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setIncrement (int value) { + checkWidget(); + if (value < 1) return; + increment = value; +} + +/** + * Sets the maximum. If this value is negative or less than or + * equal to the minimum, the value is ignored. If necessary, first + * the thumb and then the selection are adjusted to fit within the + * new range. + * + * @param value the new maximum, which must be greater than the current minimum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setMaximum (int value) { + checkWidget(); + if (value < 0) return; + if (value <= minimum) return; + if (value - minimum < thumb) { + thumb = value - minimum; + } + int selection = Math.max(minimum, Math.min (getSelection (), value - thumb)); + this.maximum = value; + updateBar(selection, minimum, value, thumb); +} + +/** + * Sets the minimum value. If this value is negative or greater + * than or equal to the maximum, the value is ignored. If necessary, + * first the thumb and then the selection are adjusted to fit within + * the new range. + * + * @param value the new minimum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setMinimum (int value) { + checkWidget(); + if (value < 0) return; + if (value >= maximum) return; + if (maximum - value < thumb) { + thumb = maximum - value; + } + int selection = Math.min(maximum - thumb, Math.max (getSelection (), value)); + this.minimum = value; + updateBar(selection, value, maximum, thumb); +} + +/** + * Sets the amount that the receiver's value will be + * modified by when the page increment/decrement areas + * are selected to the argument, which must be at least + * one. + * + * @param value the page increment (must be greater than zero) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setPageIncrement (int value) { + checkWidget(); + if (value < 1) return; + pageIncrement = value; +} + +/** + * Sets the 'selection', which is the receiver's + * value, to the argument which must be greater than or equal + * to zero. + * + * @param value the new selection (must be zero or greater) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSelection (int value) { + checkWidget(); + updateBar(value, minimum, maximum, thumb); +} + +void setSmallSize () { + /* This code is intentionally comment */ +// ((NSScroller)view).setControlSize (OS.NSSmallControlSize); +} + +void updateBar (int selection, int minimum, int maximum, int thumb) { + NSScroller widget = (NSScroller)view; + selection = Math.max (minimum, Math.min (maximum - thumb, selection)); + int range = maximum - thumb - minimum; + float fraction = range <= 0 ? 1 : (float)(selection - minimum) / range; + float knob = range <= 0 ? 1 : (float)thumb / (maximum - minimum); + widget.setFloatValue (fraction, knob); +} + +/** + * Sets the size of the receiver's thumb relative to the + * difference between its maximum and minimum values. This new + * value will be ignored if it is less than one, and will be + * clamped if it exceeds the receiver's current range. + * + * @param value the new thumb value, which must be at least one and not + * larger than the size of the current range + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setThumb (int value) { + checkWidget(); + if (value < 1) return; + value = Math.min (value, maximum - minimum); + updateBar(getSelection(), minimum, maximum, value); + this.thumb = value; +} + +/** + * Sets the receiver's selection, minimum value, maximum + * value, thumb, increment and page increment all at once. + * <p> + * Note: This is similar to setting the values individually + * using the appropriate methods, but may be implemented in a + * more efficient fashion on some platforms. + * </p> + * + * @param selection the new selection value + * @param minimum the new minimum value + * @param maximum the new maximum value + * @param thumb the new thumb value + * @param increment the new increment value + * @param pageIncrement the new pageIncrement value + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setValues (int selection, int minimum, int maximum, int thumb, int increment, int pageIncrement) { + checkWidget(); + if (minimum < 0) return; + if (maximum < 0) return; + if (thumb < 1) return; + if (increment < 1) return; + if (pageIncrement < 1) return; + thumb = Math.min (thumb, maximum - minimum); + this.thumb = thumb; + this.maximum = maximum; + this.minimum = minimum; + this.increment = increment; + this.pageIncrement = pageIncrement; + updateBar(selection, minimum, maximum, thumb); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Spinner.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Spinner.java new file mode 100755 index 0000000000..6c53ac470b --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Spinner.java @@ -0,0 +1,1064 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class are selectable user interface + * objects that allow the user to enter and modify numeric + * values. + * <p> + * Note that although this class is a subclass of <code>Composite</code>, + * it does not make sense to add children to it, or set a layout on it. + * </p><p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>READ_ONLY, WRAP</dd> + * <dt><b>Events:</b></dt> + * <dd>Selection, Modify, Verify</dd> + * </dl> + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#spinner">Spinner snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.1 + * @noextend This class is not intended to be subclassed by clients. + */ +public class Spinner extends Composite { + NSTextField textView; + NSNumberFormatter textFormatter; + NSStepper buttonView; + int pageIncrement = 10; + int digits = 0; + int textLimit = LIMIT; + static int GAP = 0; + + /** + * the operating system limit for the number of characters + * that the text field in an instance of this class can hold + * + * @since 3.4 + */ + public static final int LIMIT; + + /* + * These values can be different on different platforms. + * Therefore they are not initialized in the declaration + * to stop the compiler from inlining. + */ + static { + LIMIT = 0x7FFFFFFF; + } + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#READ_ONLY + * @see SWT#WRAP + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Spinner (Composite parent, int style) { + super (parent, checkStyle (style)); +} + +boolean acceptsFirstResponder(int /*long*/ id, int /*long*/ sel) { + if (id == view.id) return false; + return super.acceptsFirstResponder (id, sel); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the receiver's text is modified, by sending + * it one of the messages defined in the <code>ModifyListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ModifyListener + * @see #removeModifyListener + */ +public void addModifyListener (ModifyListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Modify, typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the control is selected by the user, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * <code>widgetSelected</code> is not called for texts. + * <code>widgetDefaultSelected</code> is typically called when ENTER is pressed in a single-line text. + * </p> + * + * @param listener the listener which should be notified when the control is selected by the user + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener(SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener(listener); + addListener (SWT.Selection,typedListener); + addListener (SWT.DefaultSelection,typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the receiver's text is verified, by sending + * it one of the messages defined in the <code>VerifyListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see VerifyListener + * @see #removeVerifyListener + */ +void addVerifyListener (VerifyListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Verify, typedListener); +} + +static int checkStyle (int style) { + /* + * Even though it is legal to create this widget + * with scroll bars, they serve no useful purpose + * because they do not automatically scroll the + * widget's client area. The fix is to clear + * the SWT style. + */ + return style & ~(SWT.H_SCROLL | SWT.V_SCROLL); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + float /*double*/ width = 0, height = 0; + String string = Double.toString (buttonView.maxValue ()); + Font font = Font.cocoa_new(display, textView.font ()); + NSAttributedString str = parent.createString(string, font, null, 0, true, false); + NSSize size = str.size (); + str.release (); + width = (float)/*64*/size.width; + height = (float)/*64*/size.height; + NSRect frameRect = textView.frame(); + NSCell cell = new NSCell (textView.cell ()); + NSRect cellRect = cell.drawingRectForBounds(frameRect); + width += frameRect.width - cellRect.width; + height += frameRect.height - cellRect.height; + width += GAP; + size = buttonView.cell ().cellSize (); + width += (int)/*64*/size.width; + height = Math.max (height, size.height); + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + Rectangle trim = computeTrim (0, 0, (int)Math.ceil (width), (int)Math.ceil (height)); + return new Point (trim.width, trim.height); +} + +/** + * Copies the selected text. + * <p> + * The current selection is copied to the clipboard. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void copy () { + checkWidget (); + NSText fieldEditor = textView.currentEditor(); + if (fieldEditor != null) { + fieldEditor.copy(null); + } else { + //TODO + } +} + +void createHandle () { + NSView widget = (NSView)new SWTView().alloc(); + widget.init(); +// widget.setDrawsBackground(false); + NSStepper buttonWidget = (NSStepper)new SWTStepper().alloc(); + buttonWidget.init(); + buttonWidget.setValueWraps((style & SWT.WRAP) != 0); + buttonWidget.setTarget(buttonWidget); + buttonWidget.setAction(OS.sel_sendSelection); + buttonWidget.setMaxValue(100); + NSTextField textWidget = (NSTextField)new SWTTextField().alloc(); + textWidget.init(); +// textWidget.setTarget(widget); + textWidget.setEditable((style & SWT.READ_ONLY) == 0); + textFormatter = (NSNumberFormatter)new NSNumberFormatter().alloc(); + textFormatter.init(); + widget.addSubview(textWidget); + widget.addSubview(buttonWidget); + buttonView = buttonWidget; + textView = textWidget; + view = widget; + setSelection (0, false, true, false); +} + +/** + * Cuts the selected text. + * <p> + * The current selection is first copied to the + * clipboard and then deleted from the widget. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void cut () { + checkWidget (); + if ((style & SWT.READ_ONLY) != 0) return; + NSText fieldEditor = textView.currentEditor(); + if (fieldEditor != null) { + fieldEditor.cut(null); + } else { + //TODO + } +} + +void enableWidget (boolean enabled) { + super.enableWidget(enabled); + buttonView.setEnabled(enabled); + textView.setEnabled(enabled); +} + +NSFont defaultNSFont () { + return display.textFieldFont; +} + +void deregister () { + super.deregister (); + if (textView != null) { + display.removeWidget (textView); + display.removeWidget (textView.cell()); + } + + if (buttonView != null) { + display.removeWidget (buttonView); + display.removeWidget (buttonView.cell()); + } +} + +NSView focusView () { + return textView; +} + +/** + * Returns the number of decimal places used by the receiver. + * + * @return the digits + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getDigits () { + checkWidget (); + return digits; +} + +/** + * Returns the amount that the receiver's value will be + * modified by when the up/down arrows are pressed. + * + * @return the increment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getIncrement () { + checkWidget (); + return (int)buttonView.increment(); +} + +/** + * Returns the maximum value which the receiver will allow. + * + * @return the maximum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getMaximum () { + checkWidget (); + return (int)buttonView.maxValue(); +} + +/** + * Returns the minimum value which the receiver will allow. + * + * @return the minimum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getMinimum () { + checkWidget (); + return (int)buttonView.minValue(); +} + +/** + * Returns the amount that the receiver's position will be + * modified by when the page up/down keys are pressed. + * + * @return the page increment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getPageIncrement () { + checkWidget (); + return pageIncrement; +} + +/** + * Returns the <em>selection</em>, which is the receiver's position. + * + * @return the selection + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getSelection () { + checkWidget (); + return (int)((NSStepper)buttonView).doubleValue(); +} + +int getSelectionText (boolean[] parseFail) { + String string = textView.stringValue().getString(); + try { + int value; + if (digits > 0) { + String decimalSeparator = textFormatter.decimalSeparator().getString(); + int index = string.indexOf (decimalSeparator); + if (index != -1) { + int startIndex = string.startsWith ("+") || string.startsWith ("-") ? 1 : 0; + String wholePart = startIndex != index ? string.substring (startIndex, index) : "0"; + String decimalPart = string.substring (index + 1); + if (decimalPart.length () > digits) { + decimalPart = decimalPart.substring (0, digits); + } else { + int i = digits - decimalPart.length (); + for (int j = 0; j < i; j++) { + decimalPart = decimalPart + "0"; + } + } + int wholeValue = Integer.parseInt (wholePart); + int decimalValue = Integer.parseInt (decimalPart); + for (int i = 0; i < digits; i++) wholeValue *= 10; + value = wholeValue + decimalValue; + if (string.startsWith ("-")) value = -value; + } else { + value = Integer.parseInt (string); + for (int i = 0; i < digits; i++) value *= 10; + } + } else { + value = Integer.parseInt (string); + } + int max = getMaximum(); + int min = getMinimum(); + if (min <= value && value <= max) return value; + } catch (NumberFormatException e) { + } + parseFail [0] = true; + return -1; +} + +/** + * Returns a string containing a copy of the contents of the + * receiver's text field, or an empty string if there are no + * contents. + * + * @return the receiver's text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public String getText () { + checkWidget (); + NSString str = new NSTextFieldCell (textView.cell ()).title (); + return str.getString (); +} + +/** + * Returns the maximum number of characters that the receiver's + * text field is capable of holding. If this has not been changed + * by <code>setTextLimit()</code>, it will be the constant + * <code>Spinner.LIMIT</code>. + * + * @return the text limit + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #LIMIT + * + * @since 3.4 + */ +public int getTextLimit () { + checkWidget(); + return textLimit; +} + +boolean isEventView (int /*long*/ id) { + return true; +} + +/** + * Pastes text from clipboard. + * <p> + * The selected text is deleted from the widget + * and new text inserted from the clipboard. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void paste () { + checkWidget (); + if ((style & SWT.READ_ONLY) != 0) return; + NSText fieldEditor = textView.currentEditor(); + if (fieldEditor != null) { + fieldEditor.paste(null); + } else { + //TODO + } +} + +void register () { + super.register (); + if (textView != null) { + display.addWidget (textView, this); + display.addWidget (textView.cell(), this); + } + + if (buttonView != null) { + display.addWidget (buttonView, this); + display.addWidget (buttonView.cell(), this); + } +} + +void releaseHandle () { + super.releaseHandle(); + if (textFormatter != null) textFormatter.release(); + if (buttonView != null) buttonView.release(); + if (textView != null) textView.release(); + textFormatter = null; + buttonView = null; + textView = null; +} + +void releaseWidget () { + super.releaseWidget (); + if (textView != null) textView.abortEditing(); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the receiver's text is modified. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ModifyListener + * @see #addModifyListener + */ +public void removeModifyListener (ModifyListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Modify, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is selected by the user. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener(SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Selection, listener); + eventTable.unhook (SWT.DefaultSelection,listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is verified. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see VerifyListener + * @see #addVerifyListener + */ +void removeVerifyListener (VerifyListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Verify, listener); +} + +void resized () { + super.resized (); + buttonView.sizeToFit(); + NSSize textSize = textView.cell ().cellSize (); + NSRect buttonFrame = buttonView.bounds(); + NSRect frame = view.frame(); + buttonFrame.x = frame.width - buttonFrame.width; + buttonFrame.y = (frame.height - buttonFrame.height) / 2; + int textHeight = (int)Math.min(textSize.height, frame.height); + frame.x = 0; + frame.y = (frame.height - textHeight) / 2; + frame.width -= buttonFrame.width + GAP; + frame.height = textHeight; + textView.setFrame(frame); + buttonView.setFrame(buttonFrame); +} + +boolean sendKeyEvent (NSEvent nsEvent, int type) { + boolean result = super.sendKeyEvent (nsEvent, type); + if (!result) return result; + if (type != SWT.KeyDown) return result; + int delta = 0; + short keyCode = nsEvent.keyCode (); + switch (keyCode) { + case 76: /* KP Enter */ + case 36: { /* Return */ + postEvent (SWT.DefaultSelection); + return true; + } + + case 116: delta = pageIncrement; break; /* Page Up */ + case 121: delta = -pageIncrement; break; /* Page Down */ + case 125: delta = -getIncrement(); break; /* Down arrow */ + case 126: delta = getIncrement(); break; /* Up arrow */ + } + + if (delta != 0) { + boolean [] parseFail = new boolean [1]; + int value = getSelectionText (parseFail); + if (parseFail [0]) { + value = (int)buttonView.doubleValue(); + } + int newValue = value + delta; + int max = (int)buttonView.maxValue(); + int min = (int)buttonView.minValue(); + if ((style & SWT.WRAP) != 0) { + if (newValue > max) newValue = min; + if (newValue < min) newValue = max; + } + newValue = Math.min (Math.max (min, newValue), max); + if (value != newValue) setSelection (newValue, true, true, true); + // Prevent the arrow or page up/down from being handled by the text field. + result = false; + } else { + boolean [] parseFail = new boolean [1]; + int value = getSelectionText (parseFail); + if (!parseFail [0]) { + int pos = (int)buttonView.doubleValue(); + if (pos != value) setSelection (value, true, false, true); + } + } + + return result; +} + +void sendSelection () { + setSelection (getSelection(), false, true, true); +} + +void updateBackground () { + NSColor nsColor = null; + if (backgroundImage != null) { + nsColor = NSColor.colorWithPatternImage(backgroundImage.handle); + } else if (background != null) { + nsColor = NSColor.colorWithDeviceRed(background[0], background[1], background[2], background[3]); + } else { + nsColor = NSColor.textBackgroundColor (); + } + ((NSTextField) textView).setBackgroundColor (nsColor); +} + +/** + * Sets the number of decimal places used by the receiver. + * <p> + * The digit setting is used to allow for floating point values in the receiver. + * For example, to set the selection to a floating point value of 1.37 call setDigits() with + * a value of 2 and setSelection() with a value of 137. Similarly, if getDigits() has a value + * of 2 and getSelection() returns 137 this should be interpreted as 1.37. This applies to all + * numeric APIs. + * </p> + * + * @param value the new digits (must be greater than or equal to zero) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the value is less than zero</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setDigits (int value) { + checkWidget (); + if (value < 0) error (SWT.ERROR_INVALID_ARGUMENT); + if (value == digits) return; + digits = value; + int pos = (int)buttonView.doubleValue(); + setSelection (pos, false, true, false); +} + +void setFont(NSFont font) { + textView.setFont(font); +} + +void setForeground (float /*double*/ [] color) { + NSColor nsColor; + if (color == null) { + nsColor = NSColor.textColor (); + } else { + nsColor = NSColor.colorWithDeviceRed (color [0], color [1], color [2], 1); + } + ((NSTextField) textView).setTextColor (nsColor); +} + +/** + * Sets the amount that the receiver's value will be + * modified by when the up/down arrows are pressed to + * the argument, which must be at least one. + * + * @param value the new increment (must be greater than zero) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setIncrement (int value) { + checkWidget (); + if (value < 1) return; + buttonView.setIncrement(value); +} + +/** + * Sets the maximum value that the receiver will allow. This new + * value will be ignored if it is not greater than the receiver's current + * minimum value. If the new maximum is applied then the receiver's + * selection value will be adjusted if necessary to fall within its new range. + * + * @param value the new maximum, which must be greater than the current minimum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setMaximum (int value) { + checkWidget (); + int min = getMinimum (); + if (value <= min) return; + int pos = getSelection(); + buttonView.setMaxValue(value); + if (pos > value) setSelection (value, true, true, false); +} + +/** + * Sets the minimum value that the receiver will allow. This new + * value will be ignored if it is not less than the receiver's + * current maximum value. If the new minimum is applied then the receiver's + * selection value will be adjusted if necessary to fall within its new range. + * + * @param value the new minimum, which must be less than the current maximum + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setMinimum (int value) { + checkWidget (); + int max = getMaximum(); + if (value >= max) return; + int pos = getSelection(); + buttonView.setMinValue(value); + if (pos < value) setSelection (value, true, true, false); +} + +/** + * Sets the amount that the receiver's position will be + * modified by when the page up/down keys are pressed + * to the argument, which must be at least one. + * + * @param value the page increment (must be greater than zero) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setPageIncrement (int value) { + checkWidget (); + if (value < 1) return; + pageIncrement = value; +} + +/** + * Sets the <em>selection</em>, which is the receiver's + * position, to the argument. If the argument is not within + * the range specified by minimum and maximum, it will be + * adjusted to fall within this range. + * + * @param value the new selection (must be zero or greater) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSelection (int value) { + checkWidget (); + int min = getMinimum(); + int max = getMaximum(); + value = Math.min (Math.max (min, value), max); + setSelection (value, true, true, false); +} + +void setSelection (int value, boolean setPos, boolean setText, boolean notify) { + if (setPos) { + ((NSStepper)buttonView).setDoubleValue(value); + } + if (setText) { + String string = String.valueOf (value); + if (digits > 0) { + String decimalSeparator = textFormatter.decimalSeparator().getString(); + int index = string.length () - digits; + StringBuffer buffer = new StringBuffer (); + if (index > 0) { + buffer.append (string.substring (0, index)); + buffer.append (decimalSeparator); + buffer.append (string.substring (index)); + } else { + buffer.append ("0"); + buffer.append (decimalSeparator); + while (index++ < 0) buffer.append ("0"); + buffer.append (string); + } + string = buffer.toString (); + } + NSCell cell = new NSCell(textView.cell()); + if (hooks (SWT.Verify) || filters (SWT.Verify)) { + int length = (int)/*64*/cell.title().length(); + string = verifyText (string, 0, length, null); + if (string == null) return; + } + textView.setStringValue(NSString.stringWith(string)); + NSRange selection = new NSRange(); + selection.location = 0; + selection.length = string.length(); + NSText fieldEditor = textView.currentEditor(); + if (fieldEditor != null) fieldEditor.setSelectedRange(selection); + sendEvent (SWT.Modify); + } + if (notify) postEvent (SWT.Selection); +} + +void setSmallSize () { + textView.cell ().setControlSize (OS.NSSmallControlSize); + buttonView.cell ().setControlSize (OS.NSSmallControlSize); +} + +/** + * Sets the maximum number of characters that the receiver's + * text field is capable of holding to be the argument. + * <p> + * To reset this value to the default, use <code>setTextLimit(Spinner.LIMIT)</code>. + * Specifying a limit value larger than <code>Spinner.LIMIT</code> sets the + * receiver's limit to <code>Spinner.LIMIT</code>. + * </p> + * @param limit new text limit + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_CANNOT_BE_ZERO - if the limit is zero</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #LIMIT + * + * @since 3.4 + */ +public void setTextLimit (int limit) { + checkWidget(); + if (limit == 0) error (SWT.ERROR_CANNOT_BE_ZERO); + textLimit = limit; +} + +/** + * Sets the receiver's selection, minimum value, maximum + * value, digits, increment and page increment all at once. + * <p> + * Note: This is similar to setting the values individually + * using the appropriate methods, but may be implemented in a + * more efficient fashion on some platforms. + * </p> + * + * @param selection the new selection value + * @param minimum the new minimum value + * @param maximum the new maximum value + * @param digits the new digits value + * @param increment the new increment value + * @param pageIncrement the new pageIncrement value + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public void setValues (int selection, int minimum, int maximum, int digits, int increment, int pageIncrement) { + checkWidget (); + if (maximum <= minimum) return; + if (digits < 0) return; + if (increment < 1) return; + if (pageIncrement < 1) return; + selection = Math.min (Math.max (minimum, selection), maximum); + this.pageIncrement = pageIncrement; + this.digits = digits; + buttonView.setIncrement(increment); + buttonView.setMaxValue(maximum); + buttonView.setMinValue(minimum); + setSelection (selection, true, true, false); +} + +boolean shouldChangeTextInRange_replacementString(int /*long*/ id, int /*long*/ sel, int /*long*/ affectedCharRange, int /*long*/ replacementString) { + NSRange range = new NSRange(); + OS.memmove(range, affectedCharRange, NSRange.sizeof); + boolean result = callSuperBoolean(id, sel, range, replacementString); + if (hooks (SWT.Verify)) { + String text = new NSString(replacementString).getString(); + NSEvent currentEvent = display.application.currentEvent(); + int /*long*/ type = currentEvent.type(); + if (type != OS.NSKeyDown && type != OS.NSKeyUp) currentEvent = null; + String newText = verifyText(text, (int)/*64*/range.location, (int)/*64*/(range.location+range.length), currentEvent); + if (newText == null) return false; + if (text != newText) { + int length = newText.length(); + NSText fieldEditor = textView.currentEditor (); + if (fieldEditor != null) { + NSRange selectedRange = fieldEditor.selectedRange(); + if (textLimit != LIMIT) { + int /*long*/ charCount = fieldEditor.string().length(); + if (charCount - selectedRange.length + length > textLimit) { + length = (int)/*64*/(textLimit - charCount + selectedRange.length); + } + } + char [] buffer = new char [length]; + newText.getChars (0, buffer.length, buffer, 0); + NSString nsstring = NSString.stringWithCharacters (buffer, buffer.length); + fieldEditor.replaceCharactersInRange (fieldEditor.selectedRange (), nsstring); + result = false; + } + } + if (!result) sendEvent (SWT.Modify); + } + return result; +} + +void textDidChange (int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { + super.textDidChange (id, sel, aNotification); + boolean [] parseFail = new boolean [1]; + int value = getSelectionText (parseFail); + if (!parseFail [0]) { + int pos = (int)buttonView.doubleValue(); + if (value != pos) { + setSelection (value, true, false, true); + } + } + postEvent (SWT.Modify); +} + +NSRange textView_willChangeSelectionFromCharacterRange_toCharacterRange (int /*long*/ id, int /*long*/ sel, int /*long*/ aTextView, int /*long*/ oldSelectedCharRange, int /*long*/ newSelectedCharRange) { + /* allow the selection change to proceed */ + NSRange result = new NSRange (); + OS.memmove(result, newSelectedCharRange, NSRange.sizeof); + return result; +} + +void textDidEndEditing(int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { + boolean [] parseFail = new boolean [1]; + int value = getSelectionText (parseFail); + if (parseFail [0]) { + value = (int)buttonView.doubleValue(); + setSelection (value, false, true, false); + } + super.textDidEndEditing(id, sel, aNotification); +} + +void updateCursorRects (boolean enabled) { + super.updateCursorRects (enabled); + updateCursorRects (enabled, textView); + updateCursorRects (enabled, buttonView); +} + +String verifyText (String string, int start, int end, NSEvent keyEvent) { + Event event = new Event (); + if (keyEvent != null) setKeyState(event, SWT.MouseDown, keyEvent); + event.text = string; + event.start = start; + event.end = end; + int index = 0; + if (digits > 0) { + String decimalSeparator = ".";//getDecimalSeparator (); + index = string.indexOf (decimalSeparator); + if (index != -1) { + string = string.substring (0, index) + string.substring (index + 1); + } + index = 0; + } + while (index < string.length ()) { + if (!Character.isDigit (string.charAt (index))) break; + index++; + } + event.doit = index == string.length (); + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the verify + * event. If this happens, answer null to cancel + * the operation. + */ + sendEvent (SWT.Verify, event); + if (!event.doit || isDisposed ()) return null; + return event.text; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TabFolder.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TabFolder.java new file mode 100755 index 0000000000..095bb05eda --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TabFolder.java @@ -0,0 +1,652 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; + +/** + * Instances of this class implement the notebook user interface + * metaphor. It allows the user to select a notebook page from + * set of pages. + * <p> + * The item children that may be added to instances of this class + * must be of type <code>TabItem</code>. + * <code>Control</code> children are created and then set into a + * tab item using <code>TabItem#setControl</code>. + * </p><p> + * Note that although this class is a subclass of <code>Composite</code>, + * it does not make sense to set a layout on it. + * </p><p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>TOP, BOTTOM</dd> + * <dt><b>Events:</b></dt> + * <dd>Selection</dd> + * </dl> + * <p> + * Note: Only one of the styles TOP and BOTTOM may be specified. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#tabfolder">TabFolder, TabItem snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class TabFolder extends Composite { + TabItem [] items; + int itemCount; + boolean ignoreSelect; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT + * @see SWT#TOP + * @see SWT#BOTTOM + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public TabFolder (Composite parent, int style) { + super (parent, checkStyle (style)); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the user changes the receiver's selection, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * When <code>widgetSelected</code> is called, the item field of the event object is valid. + * <code>widgetDefaultSelected</code> is not called. + * </p> + * + * @param listener the listener which should be notified when the user changes the receiver's selection + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener(SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Selection,typedListener); + addListener (SWT.DefaultSelection,typedListener); +} + +static int checkStyle (int style) { + style = checkBits (style, SWT.TOP, SWT.BOTTOM, 0, 0, 0, 0); + /* + * Even though it is legal to create this widget + * with scroll bars, they serve no useful purpose + * because they do not automatically scroll the + * widget's client area. The fix is to clear + * the SWT style. + */ + return style & ~(SWT.H_SCROLL | SWT.V_SCROLL); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + Point size = super.computeSize (wHint, hHint, changed); + if (wHint == SWT.DEFAULT && items.length > 0) { + NSSize minSize = ((NSTabView)view).minimumSize(); + Rectangle trim = computeTrim (0, 0, (int)Math.ceil (minSize.width), 0); + size.x = Math.max (trim.width, size.x); + } + return size; +} + +public Rectangle computeTrim (int x, int y, int width, int height) { + checkWidget (); + NSTabView widget = (NSTabView)view; + NSRect rect = widget.contentRect (); + x -= rect.x; + y -= rect.y; + NSRect frame = widget.frame(); + width += Math.ceil (frame.width - rect.width); + height += Math.ceil (frame.height - rect.height); + return super.computeTrim (x, y, width, height); +} + +void createHandle () { + NSTabView widget = (NSTabView)new SWTTabView().alloc(); + widget.init (); + widget.setDelegate(widget); + if ((style & SWT.BOTTOM) != 0) { + widget.setTabViewType(OS.NSBottomTabsBezelBorder); + } + view = widget; +} + +void createItem (TabItem item, int index) { + int count = itemCount; + if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE); + if (count == items.length) { + TabItem [] newItems = new TabItem [items.length + 4]; + System.arraycopy (items, 0, newItems, 0, items.length); + items = newItems; + } + System.arraycopy (items, index, items, index + 1, count - index); + items [index] = item; + itemCount++; + NSTabViewItem nsItem = (NSTabViewItem)new NSTabViewItem().alloc().init(); + item.nsItem = nsItem; + ((NSTabView)view).insertTabViewItem(nsItem, index); +} + +void createWidget () { + super.createWidget (); + items = new TabItem [4]; +} + +NSFont defaultNSFont () { + return display.tabViewFont; +} + +void destroyItem (TabItem item) { + int count = itemCount; + int index = 0; + while (index < count) { + if (items [index] == item) break; + index++; + } + if (index == count) return; + --count; + System.arraycopy (items, index + 1, items, index, count - index); + items [count] = null; + if (count == 0) { + items = new TabItem [4]; + } + itemCount = count; + ((NSTabView)view).removeTabViewItem(item.nsItem); +} + +Widget findTooltip (NSPoint pt) { + pt = view.convertPoint_fromView_ (pt, null); + NSTabViewItem nsItem = ((NSTabView)view).tabViewItemAtPoint (pt); + if (nsItem != null) { + for (int i = 0; i < itemCount; i++) { + TabItem item = items [i]; + if (item.nsItem.id == nsItem.id) return item; + } + } + return super.findTooltip (pt); +} + +public Rectangle getClientArea () { + checkWidget (); + NSRect rect = ((NSTabView)view).contentRect(); + int x = Math.max (0, (int)rect.x); + int y = Math.max (0, (int)rect.y); + int width = Math.max (0, (int)Math.ceil (rect.width)); + int height = Math.max (0, (int)Math.ceil (rect.height)); + return new Rectangle (x, y, width, height); +} + +/** + * Returns the item at the given, zero-relative index in the + * receiver. Throws an exception if the index is out of range. + * + * @param index the index of the item to return + * @return the item at the given index + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public TabItem getItem (int index) { + checkWidget (); + int count = itemCount; + if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE); + return items [index]; +} + +/** + * Returns the tab item at the given point in the receiver + * or null if no such item exists. The point is in the + * coordinate system of the receiver. + * + * @param point the point used to locate the item + * @return the tab item at the given point, or null if the point is not in a tab item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the point is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public TabItem getItem (Point point) { + checkWidget (); + if (point == null) error (SWT.ERROR_NULL_ARGUMENT); + NSPoint nsPoint = new NSPoint (); + nsPoint.x = point.x; + nsPoint.y = point.y; + NSTabView tabView = (NSTabView) view; + NSTabViewItem tabViewItem = tabView.tabViewItemAtPoint (nsPoint); + for (int i = 0; i < itemCount; i++) { + NSTabViewItem item = items[i].nsItem; + if (item.isEqual (tabViewItem)) { + return items [i]; + } + } + return null; +} + +/** + * Returns the number of items contained in the receiver. + * + * @return the number of items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getItemCount () { + checkWidget (); + return itemCount; +} + +/** + * Returns an array of <code>TabItem</code>s which are the items + * in the receiver. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its list of items, so modifying the array will + * not affect the receiver. + * </p> + * + * @return the items in the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public TabItem [] getItems () { + checkWidget (); + int count = itemCount; + TabItem [] result = new TabItem [count]; + System.arraycopy (items, 0, result, 0, count); + return result; +} + +/** + * Returns an array of <code>TabItem</code>s that are currently + * selected in the receiver. An empty array indicates that no + * items are selected. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its selection, so modifying the array will + * not affect the receiver. + * </p> + * @return an array representing the selection + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public TabItem [] getSelection () { + checkWidget (); + int index = getSelectionIndex (); + if (index == -1) return new TabItem [0]; + return new TabItem [] {items [index]}; +} + +/** + * Returns the zero-relative index of the item which is currently + * selected in the receiver, or -1 if no item is selected. + * + * @return the index of the selected item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getSelectionIndex () { + checkWidget (); + NSTabViewItem selected = ((NSTabView)view).selectedTabViewItem(); + if (selected == null) return -1; + for (int i = 0; i < itemCount; i++) { + if (items[i].nsItem.id == selected.id) return i; + } + return -1; +} + +float getThemeAlpha () { + return (background != null ? 1 : 0.25f) * parent.getThemeAlpha (); +} + +/** + * Searches the receiver's list starting at the first item + * (index 0) until an item is found that is equal to the + * argument, and returns the index of that item. If no item + * is found, returns -1. + * + * @param item the search item + * @return the index of the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int indexOf (TabItem item) { + checkWidget (); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + int count = itemCount; + for (int i=0; i<count; i++) { + if (items [i] == item) return i; + } + return -1; +} + +Point minimumSize (int wHint, int hHint, boolean flushCache) { + Control [] children = _getChildren (); + int width = 0, height = 0; + for (int i=0; i<children.length; i++) { + Control child = children [i]; + int index = 0; + int count = itemCount; + while (index < count) { + if (items [index].control == child) break; + index++; + } + if (index == count) { + Rectangle rect = child.getBounds (); + width = Math.max (width, rect.x + rect.width); + height = Math.max (height, rect.y + rect.height); + } else { + Point size = child.computeSize (wHint, hHint, flushCache); + width = Math.max (width, size.x); + height = Math.max (height, size.y); + } + } + return new Point (width, height); +} + +void releaseChildren (boolean destroy) { + if (items != null) { + for (int i=0; i<items.length; i++) { + TabItem item = items [i]; + if (item != null && !item.isDisposed ()) { + item.release (false); + } + } + items = null; + } + super.releaseChildren (destroy); +} + +void removeControl (Control control) { + super.removeControl (control); + int count = itemCount; + for (int i=0; i<count; i++) { + TabItem item = items [i]; + if (item.control == control) item.setControl (null); + } +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the user changes the receiver's selection. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener (SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Selection, listener); + eventTable.unhook (SWT.DefaultSelection,listener); +} + +void setFont (NSFont font) { + ((NSTabView)view).setFont(font); +} + +/** + * Sets the receiver's selection to the given item. + * The current selected is first cleared, then the new item is + * selected. + * + * @param item the item to select + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public void setSelection (TabItem item) { + checkWidget (); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + setSelection (new TabItem [] {item}); +} + +/** + * Sets the receiver's selection to be the given array of items. + * The current selected is first cleared, then the new items are + * selected. + * + * @param items the array of items + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the items array is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSelection (TabItem [] items) { + checkWidget (); + if (items == null) error (SWT.ERROR_NULL_ARGUMENT); + if (items.length == 0) { + setSelection (-1, false, false); + } else { + for (int i=items.length - 1; i>=0; --i) { + int index = indexOf (items [i]); + if (index != -1) setSelection (index, false, false); + } + } +} + +/** + * Selects the item at the given zero-relative index in the receiver. + * If the item at the index was already selected, it remains selected. + * The current selection is first cleared, then the new items are + * selected. Indices that are out of range are ignored. + * + * @param index the index of the item to select + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSelection (int index) { + checkWidget (); + int count = itemCount; + if (!(0 <= index && index < count)) return; + setSelection (index, false, false); +} + +void setSelection (int index, boolean notify, boolean force) { + if (!(0 <= index && index < itemCount)) return; + int currentIndex = getSelectionIndex (); + if (!force && currentIndex == index) return; + if (currentIndex != -1) { + TabItem item = items [currentIndex]; + if (item != null) { + Control control = item.control; + if (control != null && !control.isDisposed ()) { + control.setVisible (false); + } + } + } + ignoreSelect = true; + ((NSTabView)view).selectTabViewItemAtIndex(index); + ignoreSelect = false; + index = getSelectionIndex(); + if (index != -1) { + TabItem item = items [index]; + if (item != null) { + Control control = item.control; + if (control != null && !control.isDisposed ()) { + control.setVisible (true); + } + if (notify) { + Event event = new Event (); + event.item = item; + sendEvent (SWT.Selection, event); + } + } + } +} + +void setSmallSize () { + ((NSTabView)view).setControlSize (OS.NSSmallControlSize); +} + +boolean traversePage (boolean next) { + int count = getItemCount (); + if (count == 0) return false; + int index = getSelectionIndex (); + if (index == -1) { + index = 0; + } else { + int offset = (next) ? 1 : -1; + index = (index + offset + count) % count; + } + setSelection (index, true, false); + return index == getSelectionIndex (); +} + +void tabView_willSelectTabViewItem(int /*long*/ id, int /*long*/ sel, int /*long*/ tabView, int /*long*/ tabViewItem) { + if (tabViewItem == 0) return; + for (int i = 0; i < itemCount; i++) { + TabItem item = items [i]; + if (item.nsItem.id == tabViewItem) { + int currentIndex = getSelectionIndex (); + if (currentIndex != -1) { + TabItem selected = items [currentIndex]; + if (selected != null) { + Control control = selected.control; + if (control != null && !control.isDisposed ()) { + control.setVisible (false); + } + } + } + Control control = item.control; + if (control != null && !control.isDisposed ()) { + control.setVisible (true); + } + break; + } + } +} + +void tabView_didSelectTabViewItem(int /*long*/ id, int /*long*/ sel, int /*long*/ tabView, int /*long*/ tabViewItem) { + if (tabViewItem == 0) return; + for (int i = 0; i < itemCount; i++) { + TabItem item = items [i]; + /* + * Feature in Cocoa. For some reason the control on a tab being + * deselected has its parent removed natively. The fix is to + * re-set the control's parent. + */ + Control control = item.control; + if (control != null) { + NSView topView = control.topView (); + if (topView.superview () == null) { + contentView ().addSubview (topView, OS.NSWindowBelow, null); + } + } + if (item.nsItem.id == tabViewItem) { + if (!ignoreSelect) { + Event event = new Event (); + event.item = item; + postEvent (SWT.Selection, event); + } + } + } +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TabItem.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TabItem.java new file mode 100755 index 0000000000..dcbca3e71f --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TabItem.java @@ -0,0 +1,373 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class represent a selectable user interface object + * corresponding to a tab for a page in a tab folder. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>(none)</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#tabfolder">TabFolder, TabItem snippets</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class TabItem extends Item { + TabFolder parent; + Control control; + String toolTipText; + NSTabViewItem nsItem; + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>TabFolder</code>) and a style value + * describing its behavior and appearance. The item is added + * to the end of the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public TabItem (TabFolder parent, int style) { + super (parent, style); + this.parent = parent; + parent.createItem (this, parent.getItemCount ()); +} + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>TabFolder</code>), a style value + * describing its behavior and appearance, and the index + * at which to place it in the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * @param index the zero-relative index to store the receiver in its parent + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public TabItem (TabFolder parent, int style, int index) { + super (parent, style); + this.parent = parent; + parent.createItem (this, index); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +void destroyWidget () { + parent.destroyItem (this); + releaseHandle (); +} + +/** + * Returns a rectangle describing the receiver's size and location + * relative to its parent. + * + * @return the receiver's bounding rectangle + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public Rectangle getBounds() { + checkWidget(); + Rectangle result = new Rectangle (0, 0, 0, 0); + if (nsItem.respondsToSelector (OS.sel_accessibilityAttributeValue_)) { + int /*long*/ posValue = OS.objc_msgSend (nsItem.id, OS.sel_accessibilityAttributeValue_, OS.NSAccessibilityPositionAttribute ()); + int /*long*/ sizeValue = OS.objc_msgSend (nsItem.id, OS.sel_accessibilityAttributeValue_, OS.NSAccessibilitySizeAttribute ()); + NSValue val = new NSValue (posValue); + NSPoint pt = val.pointValue (); + NSWindow window = parent.view.window (); + pt.y = display.getPrimaryFrame().height - pt.y; + pt = parent.view.convertPoint_fromView_ (pt, null); + pt = window.convertScreenToBase (pt); + result.x = (int) pt.x; + result.y = (int) pt.y; + val = new NSValue (sizeValue); + NSSize size = val.sizeValue (); + result.width = (int) Math.ceil (size.width); + result.height = (int) Math.ceil (size.height); + } + return result; +} + +/** + * Returns the control that is used to fill the client area of + * the tab folder when the user selects the tab item. If no + * control has been set, return <code>null</code>. + * <p> + * @return the control + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Control getControl () { + checkWidget (); + return control; +} + +/** + * Returns the receiver's parent, which must be a <code>TabFolder</code>. + * + * @return the receiver's parent + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public TabFolder getParent () { + checkWidget (); + return parent; +} + +/** + * Returns the receiver's tool tip text, or null if it has + * not been set. + * + * @return the receiver's tool tip text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getToolTipText () { + checkWidget (); + return toolTipText; +} + +void releaseHandle () { + super.releaseHandle (); + if (nsItem != null) nsItem.release(); + nsItem = null; + parent = null; +} + +void releaseParent () { + super.releaseParent (); + int index = parent.indexOf (this); + if (index == parent.getSelectionIndex ()) { + if (control != null) control.setVisible (false); + } +} + +void releaseWidget () { + super.releaseWidget (); + control = null; +} + +/** + * Sets the control that is used to fill the client area of + * the tab folder when the user selects the tab item. + * <p> + * @param control the new control (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li> + * <li>ERROR_INVALID_PARENT - if the control is not in the same widget tree</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setControl (Control control) { + checkWidget (); + if (control != null) { + if (control.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT); + if (control.parent != parent) error (SWT.ERROR_INVALID_PARENT); + } + if (this.control != null && this.control.isDisposed ()) { + this.control = null; + } + Control oldControl = this.control, newControl = control; + this.control = control; + int index = parent.indexOf (this), selectionIndex = parent.getSelectionIndex();; + if (index != selectionIndex) { + if (newControl != null) { + boolean hideControl = true; + if (selectionIndex != -1) { + Control selectedControl = parent.getItem(selectionIndex).getControl(); + if (selectedControl == newControl) hideControl=false; + } + if (hideControl) newControl.setVisible(false); + } + } else { + if (newControl != null) { + newControl.setVisible (true); + } + if (oldControl != null) oldControl.setVisible (false); + } + NSView view; + if (newControl != null) { + view = newControl.topView(); + } else { + view = (NSView)new NSView().alloc(); + view.init (); + view.autorelease(); + } + nsItem.setView (view); + /* + * Feature in Cocoa. The method setView() removes the old view from + * its parent. The fix is to detected it has been removed and add + * it back. + */ + if (oldControl != null) { + NSView topView = oldControl.topView (); + if (topView.superview () == null) { + parent.contentView ().addSubview (topView, OS.NSWindowBelow, null); + } + } +} + +public void setImage (Image image) { + checkWidget (); + int index = parent.indexOf (this); + if (index == -1) return; + super.setImage (image); +} + +/** + * Sets the receiver's text. The string may include + * the mnemonic character. + * </p> + * <p> + * Mnemonics are indicated by an '&' that causes the next + * character to be the mnemonic. When the user presses a + * key sequence that matches the mnemonic, a selection + * event occurs. On most platforms, the mnemonic appears + * underlined but may be emphasised in a platform specific + * manner. The mnemonic indicator character '&' can be + * escaped by doubling it in the string, causing a single + * '&' to be displayed. + * </p> + * + * @param string the new text + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the text is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + */ +public void setText (String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + int index = parent.indexOf (this); + if (index == -1) return; + super.setText (string); + char [] chars = new char [string.length ()]; + string.getChars (0, chars.length, chars, 0); + int length = fixMnemonic (chars); + NSString str = NSString.stringWithCharacters (chars, length); + nsItem.setLabel (str); +} + +/** + * Sets the receiver's tool tip text to the argument, which + * may be null indicating that the default tool tip for the + * control will be shown. For a control that has a default + * tool tip, such as the Tree control on Windows, setting + * the tool tip text to an empty string replaces the default, + * causing no tool tip text to be shown. + * <p> + * The mnemonic indicator (character '&') is not displayed in a tool tip. + * To display a single '&' in the tool tip, the character '&' can be + * escaped by doubling it in the string. + * </p> + * + * @param string the new tool tip text (or null) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setToolTipText (String string) { + checkWidget(); + toolTipText = string; + parent.checkToolTip (this); +} + +String tooltipText () { + return toolTipText; +} + +void update () { + setText (text); + setImage (image); + setToolTipText (toolTipText); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Table.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Table.java new file mode 100755 index 0000000000..6eb9b700ac --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Table.java @@ -0,0 +1,3087 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import org.eclipse.swt.*; +import org.eclipse.swt.accessibility.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class implement a selectable user interface + * object that displays a list of images and strings and issues + * notification when selected. + * <p> + * The item children that may be added to instances of this class + * must be of type <code>TableItem</code>. + * </p><p> + * Style <code>VIRTUAL</code> is used to create a <code>Table</code> whose + * <code>TableItem</code>s are to be populated by the client on an on-demand basis + * instead of up-front. This can provide significant performance improvements for + * tables that are very large or for which <code>TableItem</code> population is + * expensive (for example, retrieving values from an external source). + * </p><p> + * Here is an example of using a <code>Table</code> with style <code>VIRTUAL</code>: + * <code><pre> + * final Table table = new Table (parent, SWT.VIRTUAL | SWT.BORDER); + * table.setItemCount (1000000); + * table.addListener (SWT.SetData, new Listener () { + * public void handleEvent (Event event) { + * TableItem item = (TableItem) event.item; + * int index = table.indexOf (item); + * item.setText ("Item " + index); + * System.out.println (item.getText ()); + * } + * }); + * </pre></code> + * </p><p> + * Note that although this class is a subclass of <code>Composite</code>, + * it does not normally make sense to add <code>Control</code> children to + * it, or set a layout on it, unless implementing something like a cell + * editor. + * </p><p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, VIRTUAL, NO_SCROLL</dd> + * <dt><b>Events:</b></dt> + * <dd>Selection, DefaultSelection, SetData, MeasureItem, EraseItem, PaintItem</dd> + * </dl> + * </p><p> + * Note: Only one of the styles SINGLE, and MULTI may be specified. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#table">Table, TableItem, TableColumn snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Table extends Composite { + TableItem [] items; + TableColumn [] columns; + TableColumn sortColumn; + TableItem currentItem; + NSTableHeaderView headerView; + NSTableColumn firstColumn, checkColumn; + NSTextFieldCell dataCell; + NSButtonCell buttonCell; + int columnCount, itemCount, lastIndexOf, sortDirection; + boolean ignoreSelect, fixScrollWidth, drawExpansion; + Rectangle imageBounds; + + static int NEXT_ID; + + static final int FIRST_COLUMN_MINIMUM_WIDTH = 5; + static final int IMAGE_GAP = 3; + static final int TEXT_GAP = 2; + static final int CELL_GAP = 1; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#SINGLE + * @see SWT#MULTI + * @see SWT#CHECK + * @see SWT#FULL_SELECTION + * @see SWT#HIDE_SELECTION + * @see SWT#VIRTUAL + * @see SWT#NO_SCROLL + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Table (Composite parent, int style) { + super (parent, checkStyle (style)); +} + +int /*long*/ accessibilityAttributeValue (int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + + if (accessible != null) { + NSString attribute = new NSString(arg0); + id returnValue = accessible.internal_accessibilityAttributeValue(attribute, ACC.CHILDID_SELF); + if (returnValue != null) return returnValue.id; + } + + NSString attributeName = new NSString(arg0); + + // Accessibility Verifier queries for a title or description. NSTableView doesn't + // seem to return either, so we return a default description value here. + if (attributeName.isEqualToString (OS.NSAccessibilityDescriptionAttribute)) { + return NSString.stringWith("").id; + } + + return super.accessibilityAttributeValue(id, sel, arg0); +} + +void _addListener (int eventType, Listener listener) { + super._addListener (eventType, listener); + clearCachedWidth(items); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the user changes the receiver's selection, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * When <code>widgetSelected</code> is called, the item field of the event object is valid. + * If the receiver has the <code>SWT.CHECK</code> style and the check selection changes, + * the event object detail field contains the value <code>SWT.CHECK</code>. + * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked. + * The item field of the event object is valid for default selection, but the detail field is not used. + * </p> + * + * @param listener the listener which should be notified when the user changes the receiver's selection + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener (SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Selection, typedListener); + addListener (SWT.DefaultSelection, typedListener); +} + +TableItem _getItem (int index) { + if ((style & SWT.VIRTUAL) == 0) return items [index]; + if (items [index] != null) return items [index]; + return items [index] = new TableItem (this, SWT.NULL, -1, false); +} + +int calculateWidth (TableItem[] items, int index, GC gc) { + int width = 0; + for (int i=0; i < itemCount; i++) { + TableItem item = items [i]; + if (item != null && item.cached) { + width = Math.max (width, item.calculateWidth (index, gc)); + } + } + return width; +} + +NSSize cellSize (int /*long*/ id, int /*long*/ sel) { + NSSize size = super.cellSize(id, sel); + NSImage image = new NSCell(id).image(); + if (image != null) size.width += imageBounds.width + IMAGE_GAP; + if (hooks(SWT.MeasureItem)) { + int /*long*/ [] outValue = new int /*long*/ [1]; + OS.object_getInstanceVariable(id, Display.SWT_ROW, outValue); + int /*long*/ rowIndex = outValue [0]; + TableItem item = _getItem((int)/*64*/rowIndex); + OS.object_getInstanceVariable(id, Display.SWT_COLUMN, outValue); + int /*long*/ tableColumn = outValue[0]; + int columnIndex = 0; + for (int i=0; i<columnCount; i++) { + if (columns [i].nsColumn.id == tableColumn) { + columnIndex = i; + break; + } + } + sendMeasureItem (item, columnIndex, size); + } + return size; +} + +boolean canDragRowsWithIndexes_atPoint(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1) { + NSPoint clickPoint = new NSPoint(); + OS.memmove(clickPoint, arg1, NSPoint.sizeof); + NSTableView table = (NSTableView)view; + + // If the current row is not selected and the user is not attempting to modify the selection, select the row first. + int /*long*/ row = table.rowAtPoint(clickPoint); + int /*long*/ modifiers = NSApplication.sharedApplication().currentEvent().modifierFlags(); + + boolean drag = (state & DRAG_DETECT) != 0 && hooks (SWT.DragDetect); + if (drag) { + if (!table.isRowSelected(row) && (modifiers & (OS.NSCommandKeyMask | OS.NSShiftKeyMask | OS.NSAlternateKeyMask)) == 0) { + NSIndexSet set = (NSIndexSet)new NSIndexSet().alloc(); + set = set.initWithIndex(row); + table.selectRowIndexes (set, false); + set.release(); + } + } + + // The clicked row must be selected to initiate a drag. + return (table.isRowSelected(row) && drag); +} + +boolean checkData (TableItem item) { + return checkData (item, indexOf (item)); +} + +boolean checkData (TableItem item, int index) { + if (item.cached) return true; + if ((style & SWT.VIRTUAL) != 0) { + item.cached = true; + Event event = new Event (); + event.item = item; + event.index = indexOf (item); + currentItem = item; + sendEvent (SWT.SetData, event); + //widget could be disposed at this point + currentItem = null; + if (isDisposed () || item.isDisposed ()) return false; + if (!setScrollWidth (item)) item.redraw (-1); + } + return true; +} + +static int checkStyle (int style) { + /* + * Feature in Windows. Even when WS_HSCROLL or + * WS_VSCROLL is not specified, Windows creates + * trees and tables with scroll bars. The fix + * is to set H_SCROLL and V_SCROLL. + * + * NOTE: This code appears on all platforms so that + * applications have consistent scroll bar behavior. + */ + if ((style & SWT.NO_SCROLL) == 0) { + style |= SWT.H_SCROLL | SWT.V_SCROLL; + } + /* This platform is always FULL_SELECTION */ + style |= SWT.FULL_SELECTION; + return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +/** + * Clears the item at the given zero-relative index in the receiver. + * The text, icon and other attributes of the item are set to the default + * value. If the table was created with the <code>SWT.VIRTUAL</code> style, + * these attributes are requested again as needed. + * + * @param index the index of the item to clear + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SWT#VIRTUAL + * @see SWT#SetData + * + * @since 3.0 + */ +public void clear (int index) { + checkWidget (); + if (!(0 <= index && index < itemCount)) error (SWT.ERROR_INVALID_RANGE); + TableItem item = items [index]; + if (item != null) { + if (currentItem != item) item.clear (); + if (currentItem == null) item.redraw (-1); + setScrollWidth (item); + } +} +/** + * Removes the items from the receiver which are between the given + * zero-relative start and end indices (inclusive). The text, icon + * and other attributes of the items are set to their default values. + * If the table was created with the <code>SWT.VIRTUAL</code> style, + * these attributes are requested again as needed. + * + * @param start the start index of the item to clear + * @param end the end index of the item to clear + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SWT#VIRTUAL + * @see SWT#SetData + * + * @since 3.0 + */ +public void clear (int start, int end) { + checkWidget (); + if (start > end) return; + if (!(0 <= start && start <= end && end < itemCount)) { + error (SWT.ERROR_INVALID_RANGE); + } + if (start == 0 && end == itemCount - 1) { + clearAll (); + } else { + for (int i=start; i<=end; i++) { + clear (i); + } + } +} + +/** + * Clears the items at the given zero-relative indices in the receiver. + * The text, icon and other attributes of the items are set to their default + * values. If the table was created with the <code>SWT.VIRTUAL</code> style, + * these attributes are requested again as needed. + * + * @param indices the array of indices of the items + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SWT#VIRTUAL + * @see SWT#SetData + * + * @since 3.0 + */ +public void clear (int [] indices) { + checkWidget (); + if (indices == null) error (SWT.ERROR_NULL_ARGUMENT); + if (indices.length == 0) return; + for (int i=0; i<indices.length; i++) { + if (!(0 <= indices [i] && indices [i] < itemCount)) { + error (SWT.ERROR_INVALID_RANGE); + } + } + for (int i=0; i<indices.length; i++) { + clear (indices [i]); + } +} + +/** + * Clears all the items in the receiver. The text, icon and other + * attributes of the items are set to their default values. If the + * table was created with the <code>SWT.VIRTUAL</code> style, these + * attributes are requested again as needed. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SWT#VIRTUAL + * @see SWT#SetData + * + * @since 3.0 + */ +public void clearAll () { + checkWidget (); + for (int i=0; i<itemCount; i++) { + TableItem item = items [i]; + if (item != null) { + item.clear (); + } + } + if (currentItem == null && isDrawing ()) view.setNeedsDisplay(true); + setScrollWidth (items, true); +} + +void clearCachedWidth (TableItem[] items) { + if (items == null) return; + for (int i = 0; i < items.length; i++) { + if (items [i] != null) items [i].width = -1; + } +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + int width = 0; + if (wHint == SWT.DEFAULT) { + if (columnCount != 0) { + for (int i=0; i<columnCount; i++) { + width += columns [i].getWidth (); + } + } else { + GC gc = new GC (this); + width += calculateWidth (items, 0, gc) + CELL_GAP; + gc.dispose (); + } + if ((style & SWT.CHECK) != 0) width += getCheckColumnWidth (); + } else { + width = wHint; + } + if (width <= 0) width = DEFAULT_WIDTH; + int height = 0; + if (hHint == SWT.DEFAULT) { + height = itemCount * getItemHeight () + getHeaderHeight(); + } else { + height = hHint; + } + if (height <= 0) height = DEFAULT_HEIGHT; + Rectangle rect = computeTrim (0, 0, width, height); + return new Point (rect.width, rect.height); +} + +void createColumn (TableItem item, int index) { + String [] strings = item.strings; + if (strings != null) { + String [] temp = new String [columnCount]; + System.arraycopy (strings, 0, temp, 0, index); + System.arraycopy (strings, index, temp, index+1, columnCount-index-1); + temp [index] = ""; + item.strings = temp; + } + if (index == 0) item.text = ""; + Image [] images = item.images; + if (images != null) { + Image [] temp = new Image [columnCount]; + System.arraycopy (images, 0, temp, 0, index); + System.arraycopy (images, index, temp, index+1, columnCount-index-1); + item.images = temp; + } + if (index == 0) item.image = null; + Color [] cellBackground = item.cellBackground; + if (cellBackground != null) { + Color [] temp = new Color [columnCount]; + System.arraycopy (cellBackground, 0, temp, 0, index); + System.arraycopy (cellBackground, index, temp, index+1, columnCount-index-1); + item.cellBackground = temp; + } + Color [] cellForeground = item.cellForeground; + if (cellForeground != null) { + Color [] temp = new Color [columnCount]; + System.arraycopy (cellForeground, 0, temp, 0, index); + System.arraycopy (cellForeground, index, temp, index+1, columnCount-index-1); + item.cellForeground = temp; + } + Font [] cellFont = item.cellFont; + if (cellFont != null) { + Font [] temp = new Font [columnCount]; + System.arraycopy (cellFont, 0, temp, 0, index); + System.arraycopy (cellFont, index, temp, index+1, columnCount-index-1); + item.cellFont = temp; + } +} + +void createHandle () { + NSScrollView scrollWidget = (NSScrollView)new SWTScrollView().alloc(); + scrollWidget.init(); + scrollWidget.setHasHorizontalScroller ((style & SWT.H_SCROLL) != 0); + scrollWidget.setHasVerticalScroller ((style & SWT.V_SCROLL) != 0); + scrollWidget.setAutohidesScrollers(true); + scrollWidget.setBorderType(hasBorder() ? OS.NSBezelBorder : OS.NSNoBorder); + + NSTableView widget = (NSTableView)new SWTTableView().alloc(); + widget.init(); + widget.setAllowsMultipleSelection((style & SWT.MULTI) != 0); + widget.setAllowsColumnReordering (false); + widget.setDataSource(widget); + widget.setDelegate(widget); + widget.setColumnAutoresizingStyle (OS.NSTableViewNoColumnAutoresizing); + NSSize spacing = new NSSize(); + spacing.width = spacing.height = CELL_GAP; + widget.setIntercellSpacing(spacing); + widget.setDoubleAction(OS.sel_sendDoubleSelection); + if (!hasBorder()) widget.setFocusRingType(OS.NSFocusRingTypeNone); + + headerView = (NSTableHeaderView)new SWTTableHeaderView ().alloc ().init (); + widget.setHeaderView (null); + + NSString str = NSString.stringWith(""); //$NON-NLS-1$ + if ((style & SWT.CHECK) != 0) { + checkColumn = (NSTableColumn)new NSTableColumn().alloc(); + checkColumn = checkColumn.initWithIdentifier(NSString.stringWith(String.valueOf(++NEXT_ID))); + checkColumn.headerCell().setTitle(str); + widget.addTableColumn (checkColumn); + checkColumn.setResizingMask(OS.NSTableColumnNoResizing); + checkColumn.setEditable(false); + int /*long*/ cls = NSButton.cellClass (); /* use our custom cell class */ + buttonCell = new NSButtonCell (OS.class_createInstance (cls, 0)); + buttonCell.init (); + checkColumn.setDataCell (buttonCell); + buttonCell.setButtonType (OS.NSSwitchButton); + buttonCell.setImagePosition (OS.NSImageOnly); + buttonCell.setAllowsMixedState (true); + checkColumn.setWidth(getCheckColumnWidth()); + } + + firstColumn = (NSTableColumn)new NSTableColumn().alloc(); + firstColumn = firstColumn.initWithIdentifier(NSString.stringWith(String.valueOf(++NEXT_ID))); + /* + * Feature in Cocoa. If a column's width is too small to show any content + * then tableView_objectValueForTableColumn_row is never invoked to + * query for item values, which is a problem for VIRTUAL Tables. The + * workaround is to ensure that, for 0-column Tables, the internal first + * column always has a minimal width that makes this call come in. + */ + firstColumn.setMinWidth (FIRST_COLUMN_MINIMUM_WIDTH); + firstColumn.setWidth(0); + firstColumn.setResizingMask (OS.NSTableColumnNoResizing); + firstColumn.headerCell ().setTitle (str); + widget.addTableColumn (firstColumn); + dataCell = (NSTextFieldCell)new SWTImageTextCell ().alloc ().init (); + dataCell.setLineBreakMode(OS.NSLineBreakByClipping); + firstColumn.setDataCell (dataCell); + + scrollView = scrollWidget; + view = widget; +} + +void createItem (TableColumn column, int index) { + if (!(0 <= index && index <= columnCount)) error (SWT.ERROR_INVALID_RANGE); + if (columnCount == columns.length) { + TableColumn [] newColumns = new TableColumn [columnCount + 4]; + System.arraycopy (columns, 0, newColumns, 0, columns.length); + columns = newColumns; + } + NSTableColumn nsColumn; + if (columnCount == 0) { + //TODO - clear attributes, alignment etc. + nsColumn = firstColumn; + nsColumn.setMinWidth (0); + nsColumn.setResizingMask (OS.NSTableColumnUserResizingMask); + firstColumn = null; + } else { + //TODO - set attributes, alignment etc. + nsColumn = (NSTableColumn)new NSTableColumn().alloc(); + nsColumn = nsColumn.initWithIdentifier(NSString.stringWith(String.valueOf(++NEXT_ID))); + nsColumn.setMinWidth(0); + ((NSTableView)view).addTableColumn (nsColumn); + int checkColumn = (style & SWT.CHECK) != 0 ? 1 : 0; + ((NSTableView)view).moveColumn (columnCount + checkColumn, index + checkColumn); + nsColumn.setDataCell (dataCell); + } + column.createJNIRef (); + NSTableHeaderCell headerCell = (NSTableHeaderCell)new SWTTableHeaderCell ().alloc ().init (); + nsColumn.setHeaderCell (headerCell); + display.addWidget (headerCell, column); + column.nsColumn = nsColumn; + nsColumn.setWidth(0); + System.arraycopy (columns, index, columns, index + 1, columnCount++ - index); + columns [index] = column; + for (int i = 0; i < itemCount; i++) { + TableItem item = items [i]; + if (item != null) { + if (columnCount > 1) { + createColumn (item, index); + } + } + } +} + +void createItem (TableItem item, int index) { + if (!(0 <= index && index <= itemCount)) error (SWT.ERROR_INVALID_RANGE); + if (itemCount == items.length) { + /* Grow the array faster when redraw is off */ + int length = getDrawing () ? items.length + 4 : Math.max (4, items.length * 3 / 2); + TableItem [] newItems = new TableItem [length]; + System.arraycopy (items, 0, newItems, 0, items.length); + items = newItems; + } + System.arraycopy (items, index, items, index + 1, itemCount++ - index); + items [index] = item; + ((NSTableView)view).noteNumberOfRowsChanged (); + if (index != itemCount) fixSelection (index, true); +} + +void createWidget () { + super.createWidget (); + items = new TableItem [4]; + columns = new TableColumn [4]; +} + +Color defaultBackground () { + return display.getWidgetColor (SWT.COLOR_LIST_BACKGROUND); +} + +NSFont defaultNSFont () { + return display.tableViewFont; +} + +Color defaultForeground () { + return display.getWidgetColor (SWT.COLOR_LIST_FOREGROUND); +} + +void deregister () { + super.deregister (); + display.removeWidget (headerView); + display.removeWidget (dataCell); + if (buttonCell != null) display.removeWidget (buttonCell); +} + +/** + * Deselects the item at the given zero-relative index in the receiver. + * If the item at the index was already deselected, it remains + * deselected. Indices that are out of range are ignored. + * + * @param index the index of the item to deselect + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void deselect (int index) { + checkWidget (); + if (0 <= index && index < itemCount) { + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.deselectRow (index); + ignoreSelect = false; + } +} + +/** + * Deselects the items at the given zero-relative indices in the receiver. + * If the item at the given zero-relative index in the receiver + * is selected, it is deselected. If the item at the index + * was not selected, it remains deselected. The range of the + * indices is inclusive. Indices that are out of range are ignored. + * + * @param start the start index of the items to deselect + * @param end the end index of the items to deselect + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void deselect (int start, int end) { + checkWidget(); + if (start > end) return; + if (end < 0 || start >= itemCount) return; + start = Math.max (0, start); + end = Math.min (itemCount - 1, end); + if (start == 0 && end == itemCount - 1) { + deselectAll (); + } else { + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + for (int i=start; i<=end; i++) { + widget.deselectRow (i); + } + ignoreSelect = false; + } +} + +/** + * Deselects the items at the given zero-relative indices in the receiver. + * If the item at the given zero-relative index in the receiver + * is selected, it is deselected. If the item at the index + * was not selected, it remains deselected. Indices that are out + * of range and duplicate indices are ignored. + * + * @param indices the array of indices for the items to deselect + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the set of indices is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void deselect (int [] indices) { + checkWidget (); + if (indices == null) error (SWT.ERROR_NULL_ARGUMENT); + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + for (int i=0; i<indices.length; i++) { + widget.deselectRow (indices [i]); + } + ignoreSelect = false; +} + +/** + * Deselects all selected items in the receiver. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void deselectAll () { + checkWidget (); + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.deselectAll(null); + ignoreSelect = false; +} + +void destroyItem (TableColumn column) { + int index = 0; + while (index < columnCount) { + if (columns [index] == column) break; + index++; + } + for (int i=0; i<itemCount; i++) { + TableItem item = items [i]; + if (item != null) { + if (columnCount <= 1) { + item.strings = null; + item.images = null; + item.cellBackground = null; + item.cellForeground = null; + item.cellFont = null; + } else { + if (item.strings != null) { + String [] strings = item.strings; + if (index == 0) { + item.text = strings [1] != null ? strings [1] : ""; + } + String [] temp = new String [columnCount - 1]; + System.arraycopy (strings, 0, temp, 0, index); + System.arraycopy (strings, index + 1, temp, index, columnCount - 1 - index); + item.strings = temp; + } else { + if (index == 0) item.text = ""; + } + if (item.images != null) { + Image [] images = item.images; + if (index == 0) item.image = images [1]; + Image [] temp = new Image [columnCount - 1]; + System.arraycopy (images, 0, temp, 0, index); + System.arraycopy (images, index + 1, temp, index, columnCount - 1 - index); + item.images = temp; + } else { + if (index == 0) item.image = null; + } + if (item.cellBackground != null) { + Color [] cellBackground = item.cellBackground; + Color [] temp = new Color [columnCount - 1]; + System.arraycopy (cellBackground, 0, temp, 0, index); + System.arraycopy (cellBackground, index + 1, temp, index, columnCount - 1 - index); + item.cellBackground = temp; + } + if (item.cellForeground != null) { + Color [] cellForeground = item.cellForeground; + Color [] temp = new Color [columnCount - 1]; + System.arraycopy (cellForeground, 0, temp, 0, index); + System.arraycopy (cellForeground, index + 1, temp, index, columnCount - 1 - index); + item.cellForeground = temp; + } + if (item.cellFont != null) { + Font [] cellFont = item.cellFont; + Font [] temp = new Font [columnCount - 1]; + System.arraycopy (cellFont, 0, temp, 0, index); + System.arraycopy (cellFont, index + 1, temp, index, columnCount - 1 - index); + item.cellFont = temp; + } + } + } + } + + int oldIndex = indexOf (column.nsColumn); + + System.arraycopy (columns, index + 1, columns, index, --columnCount - index); + columns [columnCount] = null; + if (columnCount == 0) { + //TODO - reset attributes + firstColumn = column.nsColumn; + firstColumn.retain (); + /* + * Feature in Cocoa. If a column's width is too small to show any content + * then tableView_objectValueForTableColumn_row is never invoked to + * query for item values, which is a problem for VIRTUAL Tables. The + * workaround is to ensure that, for 0-column Tables, the internal first + * column always has a minimal width that makes this call come in. + */ + firstColumn.setMinWidth (FIRST_COLUMN_MINIMUM_WIDTH); + firstColumn.setResizingMask (OS.NSTableColumnNoResizing); + setScrollWidth (); + } else { + ((NSTableView)view).removeTableColumn(column.nsColumn); + } + + NSArray array = ((NSTableView)view).tableColumns (); + int arraySize = (int)/*64*/array.count (); + for (int i = oldIndex; i < arraySize; i++) { + int /*long*/ columnId = array.objectAtIndex (i).id; + for (int j = 0; j < columnCount; j++) { + if (columns[j].nsColumn.id == columnId) { + columns [j].sendEvent (SWT.Move); + break; + } + } + } +} + +void destroyItem (TableItem item) { + int index = 0; + while (index < itemCount) { + if (items [index] == item) break; + index++; + } + if (index != itemCount - 1) fixSelection (index, false); + System.arraycopy (items, index + 1, items, index, --itemCount - index); + items [itemCount] = null; + ((NSTableView)view).noteNumberOfRowsChanged(); + if (itemCount == 0) setTableEmpty (); +} + +boolean dragDetect(int x, int y, boolean filter, boolean[] consume) { + // Let Cocoa determine if a drag is starting and fire the notification when we get the callback. + return false; +} + +void drawInteriorWithFrame_inView (int /*long*/ id, int /*long*/ sel, NSRect rect, int /*long*/ view) { + boolean hooksErase = hooks (SWT.EraseItem); + boolean hooksPaint = hooks (SWT.PaintItem); + boolean hooksMeasure = hooks (SWT.MeasureItem); + + NSTextFieldCell cell = new NSTextFieldCell (id); + + NSTableView widget = (NSTableView)this.view; + int /*long*/ [] outValue = new int /*long*/ [1]; + OS.object_getInstanceVariable(id, Display.SWT_ROW, outValue); + int /*long*/ rowIndex = outValue [0]; + TableItem item = _getItem((int)/*64*/rowIndex); + OS.object_getInstanceVariable(id, Display.SWT_COLUMN, outValue); + int /*long*/ tableColumn = outValue[0]; + int /*long*/ nsColumnIndex = widget.tableColumns().indexOfObjectIdenticalTo(new id(tableColumn)); + int columnIndex = 0; + for (int i=0; i<columnCount; i++) { + if (columns [i].nsColumn.id == tableColumn) { + columnIndex = i; + break; + } + } + + Color background = item.cellBackground != null ? item.cellBackground [columnIndex] : null; + if (background == null) background = item.background; + boolean drawBackground = background != null; + boolean drawForeground = true; + boolean isSelected = cell.isHighlighted(); + boolean drawSelection = isSelected; + boolean hasFocus = hooksErase && hasFocus (); + + Color selectionBackground = null, selectionForeground = null; + if (isSelected && (hooksErase || hooksPaint)) { + selectionForeground = Color.cocoa_new(display, hasFocus ? display.alternateSelectedControlTextColor : display.selectedControlTextColor); + selectionBackground = Color.cocoa_new(display, hasFocus ? display.alternateSelectedControlColor : display.secondarySelectedControlColor); + } + + NSSize contentSize = super.cellSize(id, OS.sel_cellSize); + NSImage image = cell.image(); + if (image != null) contentSize.width += imageBounds.width + IMAGE_GAP; + int contentWidth = (int)Math.ceil (contentSize.width); + NSSize spacing = widget.intercellSpacing(); + int itemHeight = (int)Math.ceil (widget.rowHeight() + spacing.height); + + NSRect cellRect = widget.rectOfColumn (nsColumnIndex); + cellRect.y = rect.y; + cellRect.height = rect.height + spacing.height; + if (columnCount == 0) { + NSRect rowRect = widget.rectOfRow (rowIndex); + cellRect.width = rowRect.width; + } + float /*double*/ offsetX = 0, offsetY = 0; + if (hooksPaint || hooksErase) { + NSRect frameCell = widget.frameOfCellAtColumn(nsColumnIndex, rowIndex); + offsetX = rect.x - frameCell.x; + offsetY = rect.y - frameCell.y; + if (drawExpansion) { + offsetX -= 0.5f; + offsetY -= 0.5f; + } + } + int itemX = (int)(rect.x - offsetX), itemY = (int)(rect.y - offsetY); + NSGraphicsContext context = NSGraphicsContext.currentContext (); + + if (hooksMeasure) { + sendMeasureItem(item, columnIndex, contentSize); + } + + Color userForeground = null; + if (hooksErase) { + context.saveGraphicsState(); + NSAffineTransform transform = NSAffineTransform.transform(); + transform.translateXBy(offsetX, offsetY); + transform.concat(); + + GCData data = new GCData (); + data.paintRect = cellRect; + GC gc = GC.cocoa_new (this, data); + gc.setFont (item.getFont (columnIndex)); + if (isSelected) { + gc.setForeground (selectionForeground); + gc.setBackground (selectionBackground); + } else { + gc.setForeground (item.getForeground (columnIndex)); + gc.setBackground (item.getBackground (columnIndex)); + } + if (!drawExpansion) { + gc.setClipping ((int)(cellRect.x - offsetX), (int)(cellRect.y - offsetY), (int)cellRect.width, (int)cellRect.height); + } + Event event = new Event (); + event.item = item; + event.gc = gc; + event.index = columnIndex; + event.detail = SWT.FOREGROUND; + if (drawBackground) event.detail |= SWT.BACKGROUND; + if (isSelected) event.detail |= SWT.SELECTED; + event.x = (int)cellRect.x; + event.y = (int)cellRect.y; + event.width = (int)cellRect.width; + event.height = (int)cellRect.height; + sendEvent (SWT.EraseItem, event); + if (!event.doit) { + drawForeground = drawBackground = drawSelection = false; + } else { + drawBackground = drawBackground && (event.detail & SWT.BACKGROUND) != 0; + drawForeground = (event.detail & SWT.FOREGROUND) != 0; + drawSelection = drawSelection && (event.detail & SWT.SELECTED) != 0; + } + if (!drawSelection && isSelected) { + userForeground = Color.cocoa_new(display, gc.getForeground().handle); + } + gc.dispose (); + + context.restoreGraphicsState(); + + if (isDisposed ()) return; + if (item.isDisposed ()) return; + + if (drawSelection && ((style & SWT.HIDE_SELECTION) == 0 || hasFocus)) { + cellRect.height -= spacing.height; + callSuper (widget.id, OS.sel_highlightSelectionInClipRect_, cellRect); + cellRect.height += spacing.height; + } + } + + if (drawBackground && !drawSelection) { + context.saveGraphicsState (); + float /*double*/ [] colorRGB = background.handle; + NSColor color = NSColor.colorWithDeviceRed (colorRGB[0], colorRGB[1], colorRGB[2], 1f); + color.setFill (); + NSBezierPath.fillRect (cellRect); + context.restoreGraphicsState (); + } + + if (drawForeground) { + if ((!drawExpansion || hooksMeasure) && image != null) { + NSRect destRect = new NSRect(); + destRect.x = rect.x + IMAGE_GAP; + destRect.y = rect.y + (float)Math.ceil((rect.height - imageBounds.height) / 2); + destRect.width = imageBounds.width; + destRect.height = imageBounds.height; + NSRect srcRect = new NSRect(); + NSSize size = image.size(); + srcRect.width = size.width; + srcRect.height = size.height; + context.saveGraphicsState(); + NSBezierPath.bezierPathWithRect(rect).addClip(); + NSAffineTransform transform = NSAffineTransform.transform(); + transform.scaleXBy(1, -1); + transform.translateXBy(0, -(destRect.height + 2 * destRect.y)); + transform.concat(); + image.drawInRect(destRect, srcRect, OS.NSCompositeSourceOver, 1); + context.restoreGraphicsState(); + int imageWidth = imageBounds.width + IMAGE_GAP; + rect.x += imageWidth; + rect.width -= imageWidth; + } + cell.setHighlighted (false); + boolean callSuper = false; + if (userForeground != null) { + /* + * Bug in Cocoa. For some reason, it is not possible to change the + * foreground color to black when the cell is highlighted. The text + * still draws white. The fix is to draw the text and not call super. + */ + float /*double*/ [] color = userForeground.handle; + if (color[0] == 0 && color[1] == 0 && color[2] == 0 && color[3] == 1) { + NSMutableAttributedString newStr = new NSMutableAttributedString(cell.attributedStringValue().mutableCopy()); + NSRange range = new NSRange(); + range.length = newStr.length(); + newStr.removeAttribute(OS.NSForegroundColorAttributeName, range); + NSRect newRect = new NSRect(); + newRect.x = rect.x + TEXT_GAP; + newRect.y = rect.y; + newRect.width = rect.width - TEXT_GAP; + newRect.height = rect.height; + NSSize size = newStr.size(); + if (newRect.height > size.height) { + newRect.y += (newRect.height - size.height) / 2; + newRect.height = size.height; + } + newStr.drawInRect(newRect); + newStr.release(); + } else { + NSColor nsColor = NSColor.colorWithDeviceRed(color[0], color[1], color[2], color[3]); + cell.setTextColor(nsColor); + callSuper = true; + } + } else { + callSuper = true; + } + if (callSuper) { + NSAttributedString attrStr = cell.attributedStringValue(); + NSSize size = attrStr.size(); + if (rect.height > size.height) { + rect.y += (rect.height - size.height) / 2; + rect.height = size.height; + } + super.drawInteriorWithFrame_inView(id, sel, rect, view); + } + } + + if (hooksPaint) { + context.saveGraphicsState(); + NSAffineTransform transform = NSAffineTransform.transform(); + transform.translateXBy(offsetX, offsetY); + transform.concat(); + + GCData data = new GCData (); + data.paintRect = cellRect; + GC gc = GC.cocoa_new (this, data); + gc.setFont (item.getFont (columnIndex)); + if (drawSelection) { + gc.setForeground (selectionForeground); + gc.setBackground (selectionBackground); + } else { + gc.setForeground (userForeground != null ? userForeground : item.getForeground (columnIndex)); + gc.setBackground (item.getBackground (columnIndex)); + } + if (!drawExpansion) { + gc.setClipping ((int)(cellRect.x - offsetX), (int)(cellRect.y - offsetY), (int)cellRect.width, (int)cellRect.height); + } + Event event = new Event (); + event.item = item; + event.gc = gc; + event.index = columnIndex; + if (drawForeground) event.detail |= SWT.FOREGROUND; + if (drawBackground) event.detail |= SWT.BACKGROUND; + if (drawSelection) event.detail |= SWT.SELECTED; + event.x = itemX; + event.y = itemY; + event.width = contentWidth; + event.height = itemHeight; + sendEvent (SWT.PaintItem, event); + gc.dispose (); + + context.restoreGraphicsState(); + } +} + +void drawWithExpansionFrame_inView (int /*long*/ id, int /*long*/ sel, NSRect cellFrame, int /*long*/ view) { + drawExpansion = true; + super.drawWithExpansionFrame_inView(id, sel, cellFrame, view); + drawExpansion = false; +} + +void drawRect(int id, int sel, NSRect rect) { + fixScrollWidth = false; + super.drawRect(id, sel, rect); + if (isDisposed ()) return; + if (fixScrollWidth) { + fixScrollWidth = false; + if (setScrollWidth (items, true)) view.setNeedsDisplay(true); + } +} + +NSRect expansionFrameWithFrame_inView(int /*long*/ id, int /*long*/ sel, NSRect cellRect, int /*long*/ view) { + if (toolTipText == null) { + NSRect rect = super.expansionFrameWithFrame_inView(id, sel, cellRect, view); + NSCell cell = new NSCell(id); + if (rect.width != 0 && rect.height != 0) { + if (hooks(SWT.MeasureItem)) { + NSSize cellSize = cell.cellSize(); + cellRect.width = cellSize.width; + return cellRect; + } + } else { + NSRect expansionRect; + if (hooks(SWT.MeasureItem)) { + expansionRect = cellRect; + NSSize cellSize = cell.cellSize(); + expansionRect.width = cellSize.width; + } else { + expansionRect = cell.titleRectForBounds(cellRect); + NSSize cellSize = super.cellSize(id, OS.sel_cellSize); + expansionRect.width = cellSize.width; + } + NSRect contentRect = scrollView.contentView().bounds(); + OS.NSIntersectionRect(contentRect, expansionRect, contentRect); + if (!OS.NSEqualRects(expansionRect, contentRect)) { + return expansionRect; + } + } + return rect; + } + return new NSRect(); +} + +Widget findTooltip (NSPoint pt) { + NSTableView widget = (NSTableView)view; + NSTableHeaderView headerView = widget.headerView(); + if (headerView != null) { + pt = headerView.convertPoint_fromView_ (pt, null); + int /*long*/ index = headerView.columnAtPoint (pt); + if (index != -1) { + NSArray nsColumns = widget.tableColumns (); + id nsColumn = nsColumns.objectAtIndex (index); + for (int i = 0; i < columnCount; i++) { + TableColumn column = columns [i]; + if (column.nsColumn.id == nsColumn.id) { + return column; + } + } + } + } + return super.findTooltip (pt); +} + +void fixSelection (int index, boolean add) { + int [] selection = getSelectionIndices (); + if (selection.length == 0) return; + int newCount = 0; + boolean fix = false; + for (int i = 0; i < selection.length; i++) { + if (!add && selection [i] == index) { + fix = true; + } else { + int newIndex = newCount++; + selection [newIndex] = selection [i]; + if (selection [newIndex] >= index) { + selection [newIndex] += add ? 1 : -1; + fix = true; + } + } + } + if (fix) select (selection, newCount, true); +} + +int getCheckColumnWidth () { + return (int)checkColumn.dataCell().cellSize().width; +} + +public Rectangle getClientArea () { + checkWidget (); + Rectangle rect = super.getClientArea (); + NSTableHeaderView headerView = ((NSTableView) view).headerView (); + if (headerView != null) { + int height = (int) headerView.bounds ().height; + rect.y -= height; + rect.height += height; + } + return rect; +} + +TableColumn getColumn (id id) { + for (int i = 0; i < columnCount; i++) { + if (columns[i].nsColumn.id == id.id) { + return columns[i]; + } + } + return null; +} + +/** + * Returns the column at the given, zero-relative index in the + * receiver. Throws an exception if the index is out of range. + * Columns are returned in the order that they were created. + * If no <code>TableColumn</code>s were created by the programmer, + * this method will throw <code>ERROR_INVALID_RANGE</code> despite + * the fact that a single column of data may be visible in the table. + * This occurs when the programmer uses the table like a list, adding + * items but never creating a column. + * + * @param index the index of the column to return + * @return the column at the given index + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Table#getColumnOrder() + * @see Table#setColumnOrder(int[]) + * @see TableColumn#getMoveable() + * @see TableColumn#setMoveable(boolean) + * @see SWT#Move + */ +public TableColumn getColumn (int index) { + checkWidget (); + if (!(0 <=index && index < columnCount)) error (SWT.ERROR_INVALID_RANGE); + return columns [index]; +} + +/** + * Returns the number of columns contained in the receiver. + * If no <code>TableColumn</code>s were created by the programmer, + * this value is zero, despite the fact that visually, one column + * of items may be visible. This occurs when the programmer uses + * the table like a list, adding items but never creating a column. + * + * @return the number of columns + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getColumnCount () { + checkWidget (); + return columnCount; +} + +/** + * Returns an array of zero-relative integers that map + * the creation order of the receiver's items to the + * order in which they are currently being displayed. + * <p> + * Specifically, the indices of the returned array represent + * the current visual order of the items, and the contents + * of the array represent the creation order of the items. + * </p><p> + * Note: This is not the actual structure used by the receiver + * to maintain its list of items, so modifying the array will + * not affect the receiver. + * </p> + * + * @return the current visual order of the receiver's items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Table#setColumnOrder(int[]) + * @see TableColumn#getMoveable() + * @see TableColumn#setMoveable(boolean) + * @see SWT#Move + * + * @since 3.1 + */ +public int [] getColumnOrder () { + checkWidget (); + int [] order = new int [columnCount]; + for (int i = 0; i < columnCount; i++) { + TableColumn column = columns [i]; + int index = indexOf (column.nsColumn); + if ((style & SWT.CHECK) != 0) index -= 1; + order [index] = i; + } + return order; +} + +/** + * Returns an array of <code>TableColumn</code>s which are the + * columns in the receiver. Columns are returned in the order + * that they were created. If no <code>TableColumn</code>s were + * created by the programmer, the array is empty, despite the fact + * that visually, one column of items may be visible. This occurs + * when the programmer uses the table like a list, adding items but + * never creating a column. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its list of items, so modifying the array will + * not affect the receiver. + * </p> + * + * @return the items in the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Table#getColumnOrder() + * @see Table#setColumnOrder(int[]) + * @see TableColumn#getMoveable() + * @see TableColumn#setMoveable(boolean) + * @see SWT#Move + */ +public TableColumn [] getColumns () { + checkWidget (); + TableColumn [] result = new TableColumn [columnCount]; + System.arraycopy (columns, 0, result, 0, columnCount); + return result; +} + +/** + * Returns the width in pixels of a grid line. + * + * @return the width of a grid line in pixels + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getGridLineWidth () { + checkWidget (); + return 0; +} + +/** + * Returns the height of the receiver's header + * + * @return the height of the header or zero if the header is not visible + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.0 + */ +public int getHeaderHeight () { + checkWidget (); + NSTableHeaderView headerView = ((NSTableView)view).headerView(); + if (headerView == null) return 0; + return (int)headerView.bounds().height; +} + +/** + * Returns <code>true</code> if the receiver's header is visible, + * and <code>false</code> otherwise. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, this method + * may still indicate that it is considered visible even though + * it may not actually be showing. + * </p> + * + * @return the receiver's header's visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getHeaderVisible () { + checkWidget (); + return ((NSTableView)view).headerView() != null; +} + +/** + * Returns the item at the given, zero-relative index in the + * receiver. Throws an exception if the index is out of range. + * + * @param index the index of the item to return + * @return the item at the given index + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public TableItem getItem (int index) { + checkWidget (); + if (!(0 <= index && index < itemCount)) error (SWT.ERROR_INVALID_RANGE); + return _getItem (index); +} + +/** + * Returns the item at the given point in the receiver + * or null if no such item exists. The point is in the + * coordinate system of the receiver. + * <p> + * The item that is returned represents an item that could be selected by the user. + * For example, if selection only occurs in items in the first column, then null is + * returned if the point is outside of the item. + * Note that the SWT.FULL_SELECTION style hint, which specifies the selection policy, + * determines the extent of the selection. + * </p> + * + * @param point the point used to locate the item + * @return the item at the given point, or null if the point is not in a selectable item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the point is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public TableItem getItem (Point point) { + checkWidget (); + NSTableView widget = (NSTableView)view; + NSPoint pt = new NSPoint(); + pt.x = point.x; + pt.y = point.y; + int row = (int)/*64*/widget.rowAtPoint(pt); + if (row == -1) return null; + return items[row]; +} + +/** + * Returns the number of items contained in the receiver. + * + * @return the number of items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getItemCount () { + checkWidget (); + return itemCount; +} + +/** + * Returns the height of the area which would be used to + * display <em>one</em> of the items in the receiver. + * + * @return the height of one item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getItemHeight () { + checkWidget (); + return (int)((NSTableView)view).rowHeight() + CELL_GAP; +} + +/** + * Returns a (possibly empty) array of <code>TableItem</code>s which + * are the items in the receiver. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its list of items, so modifying the array will + * not affect the receiver. + * </p> + * + * @return the items in the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public TableItem [] getItems () { + checkWidget (); + TableItem [] result = new TableItem [itemCount]; + if ((style & SWT.VIRTUAL) != 0) { + for (int i=0; i<itemCount; i++) { + result [i] = _getItem (i); + } + } else { + System.arraycopy (items, 0, result, 0, itemCount); + } + return result; +} + +/** + * Returns <code>true</code> if the receiver's lines are visible, + * and <code>false</code> otherwise. Note that some platforms draw + * grid lines while others may draw alternating row colors. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, this method + * may still indicate that it is considered visible even though + * it may not actually be showing. + * </p> + * + * @return the visibility state of the lines + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getLinesVisible () { + checkWidget (); + return ((NSTableView)view).usesAlternatingRowBackgroundColors(); +} + +/** + * Returns an array of <code>TableItem</code>s that are currently + * selected in the receiver. The order of the items is unspecified. + * An empty array indicates that no items are selected. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its selection, so modifying the array will + * not affect the receiver. + * </p> + * @return an array representing the selection + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public TableItem [] getSelection () { + checkWidget (); + NSTableView widget = (NSTableView)view; + if (widget.numberOfSelectedRows() == 0) { + return new TableItem [0]; + } + NSIndexSet selection = widget.selectedRowIndexes(); + int count = (int)/*64*/selection.count(); + int /*long*/ [] indexBuffer = new int /*long*/ [count]; + selection.getIndexes(indexBuffer, count, 0); + TableItem [] result = new TableItem [count]; + for (int i=0; i<count; i++) { + result [i] = _getItem ((int)/*64*/indexBuffer [i]); + } + return result; +} + +/** + * Returns the number of selected items contained in the receiver. + * + * @return the number of selected items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getSelectionCount () { + checkWidget (); + return (int)/*64*/((NSTableView)view).numberOfSelectedRows(); +} + +/** + * Returns the zero-relative index of the item which is currently + * selected in the receiver, or -1 if no item is selected. + * + * @return the index of the selected item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getSelectionIndex () { + checkWidget (); + NSTableView widget = (NSTableView)view; + if (widget.numberOfSelectedRows() == 0) { + return -1; + } + NSIndexSet selection = widget.selectedRowIndexes(); + int count = (int)/*64*/selection.count(); + int /*long*/ [] result = new int /*long*/ [count]; + selection.getIndexes(result, count, 0); + return (int)/*64*/result [0]; +} + +/** + * Returns the zero-relative indices of the items which are currently + * selected in the receiver. The order of the indices is unspecified. + * The array is empty if no items are selected. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its selection, so modifying the array will + * not affect the receiver. + * </p> + * @return the array of indices of the selected items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int [] getSelectionIndices () { + checkWidget (); + NSTableView widget = (NSTableView)view; + if (widget.numberOfSelectedRows() == 0) { + return new int [0]; + } + NSIndexSet selection = widget.selectedRowIndexes(); + int count = (int)/*64*/selection.count(); + int /*long*/ [] indices = new int /*long*/ [count]; + selection.getIndexes(indices, count, 0); + int [] result = new int [count]; + for (int i = 0; i < indices.length; i++) { + result [i] = (int)/*64*/indices [i]; + } + return result; +} + +/** + * Returns the column which shows the sort indicator for + * the receiver. The value may be null if no column shows + * the sort indicator. + * + * @return the sort indicator + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setSortColumn(TableColumn) + * + * @since 3.2 + */ +public TableColumn getSortColumn () { + checkWidget (); + return sortColumn; +} + +/** + * Returns the direction of the sort indicator for the receiver. + * The value will be one of <code>UP</code>, <code>DOWN</code> + * or <code>NONE</code>. + * + * @return the sort direction + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setSortDirection(int) + * + * @since 3.2 + */ +public int getSortDirection () { + checkWidget (); + return sortDirection; +} + +/** + * Returns the zero-relative index of the item which is currently + * at the top of the receiver. This index can change when items are + * scrolled or new items are added or removed. + * + * @return the index of the top item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getTopIndex () { + checkWidget (); + //TODO - partial item at the top + NSRect rect = scrollView.documentVisibleRect(); + NSPoint point = new NSPoint(); + point.x = rect.x; + point.y = rect.y; + return (int)/*64*/((NSTableView)view).rowAtPoint(point); +} + +void highlightSelectionInClipRect(int /*long*/ id, int /*long*/ sel, int /*long*/ rect) { + if (hooks (SWT.EraseItem)) return; + if ((style & SWT.HIDE_SELECTION) != 0 && !hasFocus()) return; + NSRect clipRect = new NSRect (); + OS.memmove (clipRect, rect, NSRect.sizeof); + callSuper (id, sel, clipRect); +} + +int /*long*/ hitTestForEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ event, NSRect rect, int /*long*/ controlView) { + /* + * For some reason, the cell class needs to implement hitTestForEvent:inRect:ofView:, + * otherwise the double action selector is not called properly. + */ + return callSuper(id, sel, event, rect, controlView); +} + +int /*long*/ image (int /*long*/ id, int /*long*/ sel) { + int /*long*/ [] image = new int /*long*/ [1]; + OS.object_getInstanceVariable(id, Display.SWT_IMAGE, image); + return image[0]; +} + +NSRect imageRectForBounds (int /*long*/ id, int /*long*/ sel, NSRect cellFrame) { + NSImage image = new NSCell(id).image(); + if (image != null) { + cellFrame.x += IMAGE_GAP; + cellFrame.width = imageBounds.width; + cellFrame.height = imageBounds.height; + } + return cellFrame; +} + +int indexOf (NSTableColumn column) { + return (int)/*64*/((NSTableView)view).tableColumns().indexOfObjectIdenticalTo(column); +} + +/** + * Searches the receiver's list starting at the first column + * (index 0) until a column is found that is equal to the + * argument, and returns the index of that column. If no column + * is found, returns -1. + * + * @param column the search column + * @return the index of the column + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the column is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int indexOf (TableColumn column) { + checkWidget (); + if (column == null) error (SWT.ERROR_NULL_ARGUMENT); + for (int i=0; i<columnCount; i++) { + if (columns [i] == column) return i; + } + return -1; +} + +/** + * Searches the receiver's list starting at the first item + * (index 0) until an item is found that is equal to the + * argument, and returns the index of that item. If no item + * is found, returns -1. + * + * @param item the search item + * @return the index of the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int indexOf (TableItem item) { + checkWidget (); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + if (1 <= lastIndexOf && lastIndexOf < itemCount - 1) { + if (items [lastIndexOf] == item) return lastIndexOf; + if (items [lastIndexOf + 1] == item) return ++lastIndexOf; + if (items [lastIndexOf - 1] == item) return --lastIndexOf; + } + if (lastIndexOf < itemCount / 2) { + for (int i=0; i<itemCount; i++) { + if (items [i] == item) return lastIndexOf = i; + } + } else { + for (int i=itemCount - 1; i>=0; --i) { + if (items [i] == item) return lastIndexOf = i; + } + } + return -1; +} + +/** + * Returns <code>true</code> if the item is selected, + * and <code>false</code> otherwise. Indices out of + * range are ignored. + * + * @param index the index of the item + * @return the selection state of the item at the index + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean isSelected (int index) { + checkWidget (); + if (!(0 <= index && index < itemCount)) return false; + return ((NSTableView)view).isRowSelected(index); +} + +boolean isTrim (NSView view) { + if (super.isTrim (view)) return true; + return view.id == headerView.id; +} + +int /*long*/ menuForEvent(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (id != headerView.id) { + /* + * Feature in Cocoa: Table views do not change the selection when the user + * right-clicks or control-clicks on an NSTableView or its subclasses. Fix is to select the + * clicked-on row ourselves. + */ + NSEvent event = new NSEvent(theEvent); + NSTableView table = (NSTableView)view; + + // get the current selections for the table view. + NSIndexSet selectedRowIndexes = table.selectedRowIndexes(); + + // select the row that was clicked before showing the menu for the event + NSPoint mousePoint = view.convertPoint_fromView_(event.locationInWindow(), null); + int /*long*/ row = table.rowAtPoint(mousePoint); + + // figure out if the row that was just clicked on is currently selected + if (selectedRowIndexes.containsIndex(row) == false) { + NSIndexSet set = (NSIndexSet)new NSIndexSet().alloc(); + set = set.initWithIndex(row); + table.selectRowIndexes (set, false); + set.release(); + } + // else that row is currently selected, so don't change anything. + } + return super.menuForEvent(id, sel, theEvent); +} + +void mouseDown (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (headerView != null && id == headerView.id) { + NSTableView widget = (NSTableView)view; + widget.setAllowsColumnReordering(false); + NSPoint pt = headerView.convertPoint_fromView_(new NSEvent(theEvent).locationInWindow(), null); + int /*long*/ nsIndex = headerView.columnAtPoint(pt); + if (nsIndex != -1) { + id nsColumn = widget.tableColumns().objectAtIndex(nsIndex); + for (int i = 0; i < columnCount; i++) { + if (columns[i].nsColumn.id == nsColumn.id) { + widget.setAllowsColumnReordering(columns[i].movable); + break; + } + } + } + } + else if (id == view.id) { + // Bug/feature in Cocoa: If the table has a context menu we just set it visible instead of returning + // it from menuForEvent:. This has the side effect, however, of sending control-click to the NSTableView, + // which is interpreted as a single click that clears the selection. Fix is to ignore control-click if the + // view has a context menu. + NSEvent event = new NSEvent(theEvent); + if ((event.modifierFlags() & OS.NSControlKeyMask) != 0) return; + } + super.mouseDown(id, sel, theEvent); +} + +/* + * Feature in Cocoa. If a checkbox is in multi-state mode, nextState cycles + * from off to mixed to on and back to off again. This will cause the on state + * to momentarily appear while clicking on the checkbox. To avoid this, + * override [NSCell nextState] to go directly to the desired state. + */ +int /*long*/ nextState (int /*long*/ id, int /*long*/ sel) { + NSTableView tableView = (NSTableView)view; + int index = (int)/*64*/tableView.selectedRow (); + TableItem item = items[index]; + if (item.grayed) { + return item.checked ? OS.NSOffState : OS.NSMixedState; + } + return item.checked ? OS.NSOffState : OS.NSOnState; +} + +int /*long*/ numberOfRowsInTableView(int /*long*/ id, int /*long*/ sel, int /*long*/ aTableView) { + return itemCount; +} + +void register () { + super.register (); + display.addWidget (headerView, this); + display.addWidget (dataCell, this); + if (buttonCell != null) display.addWidget (buttonCell, this); +} + +void releaseChildren (boolean destroy) { + if (items != null) { + for (int i=0; i<itemCount; i++) { + TableItem item = items [i]; + if (item != null && !item.isDisposed ()) { + item.release (false); + } + } + items = null; + } + if (columns != null) { + for (int i=0; i<columnCount; i++) { + TableColumn column = columns [i]; + if (column != null && !column.isDisposed ()) { + column.release (false); + } + } + columns = null; + } + super.releaseChildren (destroy); +} + +void releaseHandle () { + super.releaseHandle (); + if (headerView != null) headerView.release(); + headerView = null; + if (firstColumn != null) firstColumn.release(); + firstColumn = null; + if (checkColumn != null) checkColumn.release(); + checkColumn = null; + if (dataCell != null) dataCell.release(); + dataCell = null; + if (buttonCell != null) buttonCell.release(); + buttonCell = null; +} + +void releaseWidget () { + super.releaseWidget (); + currentItem = null; + sortColumn = null; +} + +/** + * Removes the item from the receiver at the given + * zero-relative index. + * + * @param index the index for the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void remove (int index) { + checkWidget (); + if (!(0 <= index && index < itemCount)) error (SWT.ERROR_INVALID_RANGE); + TableItem item = items [index]; + if (item != null) item.release (false); + if (index != itemCount - 1) fixSelection (index, false); + System.arraycopy (items, index + 1, items, index, --itemCount - index); + items [itemCount] = null; + ((NSTableView)view).noteNumberOfRowsChanged(); + if (itemCount == 0) { + setTableEmpty (); + } +} + +/** + * Removes the items from the receiver which are + * between the given zero-relative start and end + * indices (inclusive). + * + * @param start the start of the range + * @param end the end of the range + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void remove (int start, int end) { + checkWidget (); + if (start > end) return; + if (!(0 <= start && start <= end && end < itemCount)) { + error (SWT.ERROR_INVALID_RANGE); + } + if (start == 0 && end == itemCount - 1) { + removeAll (); + } else { + int length = end - start + 1; + for (int i=0; i<length; i++) remove (start); + } +} + +/** + * Removes the items from the receiver's list at the given + * zero-relative indices. + * + * @param indices the array of indices of the items + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void remove (int [] indices) { + checkWidget (); + if (indices == null) error (SWT.ERROR_NULL_ARGUMENT); + if (indices.length == 0) return; + int [] newIndices = new int [indices.length]; + System.arraycopy (indices, 0, newIndices, 0, indices.length); + sort (newIndices); + int start = newIndices [newIndices.length - 1], end = newIndices [0]; + if (!(0 <= start && start <= end && end < itemCount)) { + error (SWT.ERROR_INVALID_RANGE); + } + int last = -1; + for (int i=0; i<newIndices.length; i++) { + int index = newIndices [i]; + if (index != last) { + remove (index); + last = index; + } + } +} + +/** + * Removes all of the items from the receiver. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void removeAll () { + checkWidget (); + for (int i=0; i<itemCount; i++) { + TableItem item = items [i]; + if (item != null && !item.isDisposed ()) item.release (false); + } + setTableEmpty (); + ((NSTableView)view).noteNumberOfRowsChanged(); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the user changes the receiver's selection. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener(SelectionListener) + */ +public void removeSelectionListener(SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Selection, listener); + eventTable.unhook (SWT.DefaultSelection,listener); +} + +/** + * Selects the item at the given zero-relative index in the receiver. + * If the item at the index was already selected, it remains + * selected. Indices that are out of range are ignored. + * + * @param index the index of the item to select + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void select (int index) { + checkWidget (); + if (0 <= index && index < itemCount) { + NSIndexSet set = (NSIndexSet)new NSIndexSet().alloc(); + set = set.initWithIndex(index); + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.selectRowIndexes(set, (style & SWT.MULTI) != 0); + ignoreSelect = false; + set.release(); + } +} + +/** + * Selects the items in the range specified by the given zero-relative + * indices in the receiver. The range of indices is inclusive. + * The current selection is not cleared before the new items are selected. + * <p> + * If an item in the given range is not selected, it is selected. + * If an item in the given range was already selected, it remains selected. + * Indices that are out of range are ignored and no items will be selected + * if start is greater than end. + * If the receiver is single-select and there is more than one item in the + * given range, then all indices are ignored. + * </p> + * + * @param start the start of the range + * @param end the end of the range + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Table#setSelection(int,int) + */ +public void select (int start, int end) { + checkWidget (); + if (end < 0 || start > end || ((style & SWT.SINGLE) != 0 && start != end)) return; + if (itemCount == 0 || start >= itemCount) return; + if (start == 0 && end == itemCount - 1) { + selectAll (); + } else { + start = Math.max (0, start); + end = Math.min (end, itemCount - 1); + NSRange range = new NSRange(); + range.location = start; + range.length = end - start + 1; + NSIndexSet set = (NSIndexSet)new NSIndexSet().alloc(); + set = set.initWithIndexesInRange(range); + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.selectRowIndexes(set, (style & SWT.MULTI) != 0); + ignoreSelect = false; + set.release(); + } +} + +/** + * Selects the items at the given zero-relative indices in the receiver. + * The current selection is not cleared before the new items are selected. + * <p> + * If the item at a given index is not selected, it is selected. + * If the item at a given index was already selected, it remains selected. + * Indices that are out of range and duplicate indices are ignored. + * If the receiver is single-select and multiple indices are specified, + * then all indices are ignored. + * </p> + * + * @param indices the array of indices for the items to select + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Table#setSelection(int[]) + */ +public void select (int [] indices) { + checkWidget (); + if (indices == null) error (SWT.ERROR_NULL_ARGUMENT); + int length = indices.length; + if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return; + int count = 0; + NSMutableIndexSet set = (NSMutableIndexSet)new NSMutableIndexSet().alloc().init(); + for (int i=0; i<length; i++) { + int index = indices [i]; + if (index >= 0 && index < itemCount) { + set.addIndex (indices [i]); + count++; + } + } + if (count > 0) { + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.selectRowIndexes(set, (style & SWT.MULTI) != 0); + ignoreSelect = false; + } + set.release(); +} + +void select (int [] indices, int count, boolean clear) { + NSMutableIndexSet set = (NSMutableIndexSet)new NSMutableIndexSet().alloc().init(); + for (int i=0; i<count; i++) set.addIndex (indices [i]); + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.selectRowIndexes(set, !clear); + ignoreSelect = false; + set.release(); +} + +/** + * Selects all of the items in the receiver. + * <p> + * If the receiver is single-select, do nothing. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void selectAll () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) return; + NSTableView widget = (NSTableView)view; + ignoreSelect = true; + widget.selectAll(null); + ignoreSelect = false; +} + +void updateBackground () { + NSColor nsColor = null; + if (backgroundImage != null) { + nsColor = NSColor.colorWithPatternImage(backgroundImage.handle); + } else if (background != null) { + nsColor = NSColor.colorWithDeviceRed(background[0], background[1], background[2], background[3]); + } + ((NSTableView) view).setBackgroundColor (nsColor); +} + +/** + * Sets the order that the items in the receiver should + * be displayed in to the given argument which is described + * in terms of the zero-relative ordering of when the items + * were added. + * + * @param order the new order to display the items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item order is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li> + * </ul> + * + * @see Table#getColumnOrder() + * @see TableColumn#getMoveable() + * @see TableColumn#setMoveable(boolean) + * @see SWT#Move + * + * @since 3.1 + */ +public void setColumnOrder (int [] order) { + checkWidget (); + if (order == null) error (SWT.ERROR_NULL_ARGUMENT); + if (columnCount == 0) { + if (order.length != 0) error (SWT.ERROR_INVALID_ARGUMENT); + return; + } + if (order.length != columnCount) error (SWT.ERROR_INVALID_ARGUMENT); + int [] oldOrder = getColumnOrder (); + boolean reorder = false; + boolean [] seen = new boolean [columnCount]; + for (int i=0; i<order.length; i++) { + int index = order [i]; + if (index < 0 || index >= columnCount) error (SWT.ERROR_INVALID_ARGUMENT); + if (seen [index]) error (SWT.ERROR_INVALID_ARGUMENT); + seen [index] = true; + if (order [i] != oldOrder [i]) reorder = true; + } + if (reorder) { + NSTableView tableView = (NSTableView)view; + int [] oldX = new int [oldOrder.length]; + int check = (style & SWT.CHECK) != 0 ? 1 : 0; + for (int i=0; i<oldOrder.length; i++) { + int index = oldOrder[i]; + oldX [index] = (int)tableView.rectOfColumn (i + check).x; + } + int [] newX = new int [order.length]; + for (int i=0; i<order.length; i++) { + int index = order [i]; + TableColumn column = columns[index]; + int oldIndex = indexOf (column.nsColumn); + int newIndex = i + check; + tableView.moveColumn (oldIndex, newIndex); + newX [index] = (int)tableView.rectOfColumn (newIndex).x; + } + TableColumn[] newColumns = new TableColumn [columnCount]; + System.arraycopy (columns, 0, newColumns, 0, columnCount); + for (int i=0; i<columnCount; i++) { + TableColumn column = newColumns [i]; + if (!column.isDisposed ()) { + if (newX [i] != oldX [i]) { + column.sendEvent (SWT.Move); + } + } + } + } +} + +void setFont (NSFont font) { + super.setFont (font); + setItemHeight (null, font, !hooks (SWT.MeasureItem)); + view.setNeedsDisplay (true); + clearCachedWidth (items); + setScrollWidth (items, true); +} + +/** + * Marks the receiver's header as visible if the argument is <code>true</code>, + * and marks it invisible otherwise. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, marking + * it visible may not actually cause it to be displayed. + * </p> + * + * @param show the new visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setHeaderVisible (boolean show) { + checkWidget (); + ((NSTableView)view).setHeaderView (show ? headerView : null); +} + +void setImage (int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + OS.object_setInstanceVariable(id, Display.SWT_IMAGE, arg0); +} + +/** + * Sets the number of items contained in the receiver. + * + * @param count the number of items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public void setItemCount (int count) { + checkWidget (); + count = Math.max (0, count); + if (count == itemCount) return; + if (count == itemCount) return; + TableItem [] children = items; + if (count < itemCount) { + for (int index = count; index < itemCount; index ++) { + TableItem item = children [index]; + if (item != null && !item.isDisposed()) item.release (false); + } + } + if (count > itemCount) { + if ((getStyle() & SWT.VIRTUAL) == 0) { + for (int i=itemCount; i<count; i++) { + new TableItem (this, SWT.NONE, i, true); + } + return; + } + } + int length = Math.max (4, (count + 3) / 4 * 4); + TableItem [] newItems = new TableItem [length]; + if (children != null) { + System.arraycopy (items, 0, newItems, 0, Math.min (count, itemCount)); + } + children = newItems; + this.items = newItems; + this.itemCount = count; + ((NSTableView) view).noteNumberOfRowsChanged (); +} + +/*public*/ void setItemHeight (int itemHeight) { + checkWidget (); + if (itemHeight < -1) error (SWT.ERROR_INVALID_ARGUMENT); + if (itemHeight == -1) { + //TODO - reset item height, ensure other API's such as setFont don't do this + } else { + ((NSTableView)view).setRowHeight (itemHeight); + } +} + +void setItemHeight (Image image, NSFont font, boolean set) { + if (font == null) font = getFont ().handle; + float /*double*/ ascent = font.ascender (); + float /*double*/ descent = -font.descender () + font.leading (); + int height = (int)Math.ceil (ascent + descent) + 1; + Rectangle bounds = image != null ? image.getBounds () : imageBounds; + if (bounds != null) { + imageBounds = bounds; + height = Math.max (height, bounds.height); + } + NSTableView widget = (NSTableView)view; + if (set || widget.rowHeight () < height) { + widget.setRowHeight (height); + } +} + +public void setRedraw (boolean redraw) { + checkWidget (); + super.setRedraw (redraw); + if (redraw && drawCount == 0) { + /* Resize the item array to match the item count */ + if (items.length > 4 && items.length - itemCount > 3) { + int length = Math.max (4, (itemCount + 3) / 4 * 4); + TableItem [] newItems = new TableItem [length]; + System.arraycopy (items, 0, newItems, 0, itemCount); + items = newItems; + } + setScrollWidth (); + } +} + +/** + * Marks the receiver's lines as visible if the argument is <code>true</code>, + * and marks it invisible otherwise. Note that some platforms draw grid lines + * while others may draw alternating row colors. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, marking + * it visible may not actually cause it to be displayed. + * </p> + * + * @param show the new visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setLinesVisible (boolean show) { + checkWidget (); + ((NSTableView)view).setUsesAlternatingRowBackgroundColors(show); +} + +boolean setScrollWidth () { + return setScrollWidth (items, true); +} + +boolean setScrollWidth (TableItem item) { + if (columnCount != 0) return false; + if (!getDrawing()) return false; + if (currentItem != null) { + if (currentItem != item) fixScrollWidth = true; + return false; + } + GC gc = new GC (this); + int newWidth = item.calculateWidth (0, gc); + gc.dispose (); + int oldWidth = (int)firstColumn.width (); + if (oldWidth < newWidth) { + firstColumn.setWidth (newWidth); + if (horizontalBar != null && horizontalBar.view != null) redrawWidget (horizontalBar.view, false); + return true; + } + return false; +} + +boolean setScrollWidth (TableItem [] items, boolean set) { + if (items == null) return false; + if (columnCount != 0) return false; + if (!getDrawing()) return false; + if (currentItem != null) { + fixScrollWidth = true; + return false; + } + GC gc = new GC (this); + int newWidth = 0; + for (int i = 0; i < items.length; i++) { + TableItem item = items [i]; + if (item != null) { + newWidth = Math.max (newWidth, item.calculateWidth (0, gc)); + } + } + gc.dispose (); + if (!set) { + int oldWidth = (int)firstColumn.width (); + if (oldWidth >= newWidth) return false; + } + firstColumn.setWidth (newWidth); + if (horizontalBar != null && horizontalBar.view != null) redrawWidget (horizontalBar.view, false); + return true; +} + +/** + * Selects the item at the given zero-relative index in the receiver. + * The current selection is first cleared, then the new item is selected. + * + * @param index the index of the item to select + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Table#deselectAll() + * @see Table#select(int) + */ +public void setSelection (int index) { + checkWidget (); + //TODO - optimize to use expand flag + deselectAll (); + if (0 <= index && index < itemCount) { + select (index); + showIndex (index); + } +} + +/** + * Selects the items in the range specified by the given zero-relative + * indices in the receiver. The range of indices is inclusive. + * The current selection is cleared before the new items are selected. + * <p> + * Indices that are out of range are ignored and no items will be selected + * if start is greater than end. + * If the receiver is single-select and there is more than one item in the + * given range, then all indices are ignored. + * </p> + * + * @param start the start index of the items to select + * @param end the end index of the items to select + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Table#deselectAll() + * @see Table#select(int,int) + */ +public void setSelection (int start, int end) { + checkWidget (); + //TODO - optimize to use expand flag + deselectAll (); + if (end < 0 || start > end || ((style & SWT.SINGLE) != 0 && start != end)) return; + if (itemCount == 0 || start >= itemCount) return; + start = Math.max (0, start); + end = Math.min (end, itemCount - 1); + select (start, end); + showIndex (start); +} + +/** + * Selects the items at the given zero-relative indices in the receiver. + * The current selection is cleared before the new items are selected. + * <p> + * Indices that are out of range and duplicate indices are ignored. + * If the receiver is single-select and multiple indices are specified, + * then all indices are ignored. + * </p> + * + * @param indices the indices of the items to select + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Table#deselectAll() + * @see Table#select(int[]) + */ +public void setSelection (int [] indices) { + checkWidget (); + if (indices == null) error (SWT.ERROR_NULL_ARGUMENT); + //TODO - optimize to use expand flag + deselectAll (); + int length = indices.length; + if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return; + select (indices); + showIndex (indices [0]); +} + +/** + * Sets the receiver's selection to the given item. + * The current selection is cleared before the new item is selected. + * <p> + * If the item is not in the receiver, then it is ignored. + * </p> + * + * @param item the item to select + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public void setSelection (TableItem item) { + checkWidget (); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + setSelection (new TableItem [] {item}); +} + +/** + * Sets the receiver's selection to be the given array of items. + * The current selection is cleared before the new items are selected. + * <p> + * Items that are not in the receiver are ignored. + * If the receiver is single-select and multiple items are specified, + * then all items are ignored. + * </p> + * + * @param items the array of items + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the array of items is null</li> + * <li>ERROR_INVALID_ARGUMENT - if one of the items has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Table#deselectAll() + * @see Table#select(int[]) + * @see Table#setSelection(int[]) + */ +public void setSelection (TableItem [] items) { + checkWidget (); + if (items == null) error (SWT.ERROR_NULL_ARGUMENT); + //TODO - optimize to use expand flag + deselectAll (); + int length = items.length; + if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return; + int [] indices = new int [length]; + int count = 0; + for (int i=0; i<length; i++) { + int index = indexOf (items [length - i - 1]); + if (index != -1) { + indices [count++] = index; + } + } + if (count > 0) { + select (indices); + showIndex (indices [0]); + } +} + +void setSmallSize () { + if (checkColumn == null) return; + checkColumn.dataCell ().setControlSize (OS.NSSmallControlSize); + checkColumn.setWidth (getCheckColumnWidth ()); +} + +/** + * Sets the column used by the sort indicator for the receiver. A null + * value will clear the sort indicator. The current sort column is cleared + * before the new column is set. + * + * @param column the column used by the sort indicator or <code>null</code> + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the column is disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public void setSortColumn (TableColumn column) { + checkWidget (); + if (column != null && column.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + if (column == sortColumn) return; + sortColumn = column; + ((NSTableView)view).setHighlightedTableColumn (column == null ? null : column.nsColumn); +} + +/** + * Sets the direction of the sort indicator for the receiver. The value + * can be one of <code>UP</code>, <code>DOWN</code> or <code>NONE</code>. + * + * @param direction the direction of the sort indicator + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public void setSortDirection (int direction) { + checkWidget (); + if (direction != SWT.UP && direction != SWT.DOWN && direction != SWT.NONE) return; + if (direction == sortDirection) return; + sortDirection = direction; + if (sortColumn == null) return; + NSTableHeaderView headerView = ((NSTableView)view).headerView (); + if (headerView == null) return; + int index = indexOf (sortColumn.nsColumn); + NSRect rect = headerView.headerRectOfColumn (index); + headerView.setNeedsDisplayInRect (rect); +} + +void setTableEmpty () { + itemCount = 0; + items = new TableItem [4]; + imageBounds = null; +} + +/** + * Sets the zero-relative index of the item which is currently + * at the top of the receiver. This index can change when items + * are scrolled or new items are added and removed. + * + * @param index the index of the top item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setTopIndex (int index) { + checkWidget (); + NSTableView widget = (NSTableView) view; + int row = Math.max(0, Math.min(index, itemCount)); + NSPoint pt = new NSPoint(); + pt.x = scrollView.contentView().bounds().x; + pt.y = widget.frameOfCellAtColumn(0, row).y; + view.scrollPoint(pt); +} + +/** + * Shows the column. If the column is already showing in the receiver, + * this method simply returns. Otherwise, the columns are scrolled until + * the column is visible. + * + * @param column the column to be shown + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the column is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the column has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public void showColumn (TableColumn column) { + checkWidget (); + if (column == null) error (SWT.ERROR_NULL_ARGUMENT); + if (column.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + if (column.parent != this) return; + if (columnCount <= 1) return; + int index = indexOf (column.nsColumn); + if (!(0 <= index && index < columnCount + ((style & SWT.CHECK) != 0 ? 1 : 0))) return; + ((NSTableView)view).scrollColumnToVisible (index); +} + +void showIndex (int index) { + if (0 <= index && index < itemCount) { + ((NSTableView)view).scrollRowToVisible(index); + } +} + +/** + * Shows the item. If the item is already showing in the receiver, + * this method simply returns. Otherwise, the items are scrolled until + * the item is visible. + * + * @param item the item to be shown + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Table#showSelection() + */ +public void showItem (TableItem item) { + checkWidget (); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + if (item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + int index = indexOf (item); + if (index != -1) showIndex (index); +} + +/** + * Shows the selection. If the selection is already showing in the receiver, + * this method simply returns. Otherwise, the items are scrolled until + * the selection is visible. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Table#showItem(TableItem) + */ +public void showSelection () { + checkWidget (); + int index = getSelectionIndex (); + if (index >= 0) { + checkData(_getItem(index)); + showIndex (index); + } +} + +void sendDoubleSelection() { + NSTableView tableView = (NSTableView)view; + int rowIndex = (int)/*64*/tableView.clickedRow (); + if (rowIndex != -1) { + if ((style & SWT.CHECK) != 0) { + NSArray columns = tableView.tableColumns (); + int columnIndex = (int)/*64*/tableView.clickedColumn (); + id column = columns.objectAtIndex (columnIndex); + if (column.id == checkColumn.id) return; + } + Event event = new Event (); + event.item = _getItem (rowIndex); + postEvent (SWT.DefaultSelection, event); + } +} + +boolean sendKeyEvent (NSEvent nsEvent, int type) { + boolean result = super.sendKeyEvent (nsEvent, type); + if (!result) return result; + if (type != SWT.KeyDown) return result; + short keyCode = nsEvent.keyCode (); + switch (keyCode) { + case 76: /* KP Enter */ + case 36: { /* Return */ + postEvent (SWT.DefaultSelection); + break; + } + } + return result; +} + +void sendMeasureItem (TableItem item, int columnIndex, NSSize size) { + NSTableView widget = (NSTableView)this.view; + int contentWidth = (int)Math.ceil (size.width); + NSSize spacing = widget.intercellSpacing(); + int itemHeight = (int)Math.ceil (widget.rowHeight() + spacing.height); + GCData data = new GCData (); + data.paintRect = widget.frame (); + GC gc = GC.cocoa_new (this, data); + gc.setFont (item.getFont (columnIndex)); + Event event = new Event (); + event.item = item; + event.gc = gc; + event.index = columnIndex; + event.width = contentWidth; + event.height = itemHeight; + sendEvent (SWT.MeasureItem, event); + gc.dispose (); + if (!isDisposed () && !item.isDisposed ()) { + size.width = event.width; + size.height = event.height; + if (itemHeight < event.height) { + widget.setRowHeight (event.height); + } + if (contentWidth != event.width) { + if (columnCount == 0 && columnIndex == 0) { + item.width = event.width; + if (setScrollWidth (item)) { + widget.setNeedsDisplay(true); + } + } + } + } +} + +void tableViewColumnDidMove (int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { + NSNotification notification = new NSNotification (aNotification); + NSDictionary userInfo = notification.userInfo (); + id nsOldIndex = userInfo.valueForKey (NSString.stringWith ("NSOldColumn")); //$NON-NLS-1$ + id nsNewIndex = userInfo.valueForKey (NSString.stringWith ("NSNewColumn")); //$NON-NLS-1$ + int oldIndex = new NSNumber (nsOldIndex).intValue (); + int newIndex = new NSNumber (nsNewIndex).intValue (); + int startIndex = Math.min (oldIndex, newIndex); + int endIndex = Math.max (oldIndex, newIndex); + NSTableView tableView = (NSTableView)view; + NSArray nsColumns = tableView.tableColumns (); + for (int i = startIndex; i <= endIndex; i++) { + id columnId = nsColumns.objectAtIndex (i); + TableColumn column = getColumn (columnId); + if (column != null) { + column.sendEvent (SWT.Move); + if (isDisposed ()) return; + } + } +} + +void tableViewColumnDidResize (int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { + NSNotification notification = new NSNotification (aNotification); + NSDictionary userInfo = notification.userInfo (); + id columnId = userInfo.valueForKey (NSString.stringWith ("NSTableColumn")); //$NON-NLS-1$ + TableColumn column = getColumn (columnId); + if (column == null) return; /* either CHECK column or firstColumn in 0-column Table */ + + column.sendEvent (SWT.Resize); + if (isDisposed ()) return; + + NSTableView tableView = (NSTableView)view; + int index = indexOf (column.nsColumn); + if (index == -1) return; /* column was disposed in Resize callback */ + + NSArray nsColumns = tableView.tableColumns (); + int columnCount = (int)/*64*/tableView.numberOfColumns (); + for (int i = index + 1; i < columnCount; i++) { + columnId = nsColumns.objectAtIndex (i); + column = getColumn (columnId); + if (column != null) { + column.sendEvent (SWT.Move); + if (isDisposed ()) return; + } + } +} + +void tableViewSelectionDidChange (int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { + if (ignoreSelect) return; + NSTableView widget = (NSTableView) view; + int row = (int)/*64*/widget.selectedRow (); + if(row == -1) + postEvent (SWT.Selection); + else { + TableItem item = _getItem (row); + Event event = new Event (); + event.item = item; + event.index = row; + postEvent (SWT.Selection, event); + } +} + +void tableView_didClickTableColumn (int /*long*/ id, int /*long*/ sel, int /*long*/ tableView, int /*long*/ tableColumn) { + TableColumn column = getColumn (new id (tableColumn)); + if (column == null) return; /* either CHECK column or firstColumn in 0-column Table */ + column.postEvent (SWT.Selection); +} + +int /*long*/ tableView_objectValueForTableColumn_row (int /*long*/ id, int /*long*/ sel, int /*long*/ aTableView, int /*long*/ aTableColumn, int /*long*/ rowIndex) { + int index = (int)/*64*/rowIndex; + TableItem item = _getItem (index); + checkData (item, index); + if (checkColumn != null && aTableColumn == checkColumn.id) { + NSNumber value; + if (item.checked && item.grayed) { + value = NSNumber.numberWithInt (OS.NSMixedState); + } else { + value = NSNumber.numberWithInt (item.checked ? OS.NSOnState : OS.NSOffState); + } + return value.id; + } + for (int i=0; i<columnCount; i++) { + if (columns [i].nsColumn.id == aTableColumn) { + return item.createString (i).id; + } + } + return item.createString (0).id; +} + +void tableView_setObjectValue_forTableColumn_row (int /*long*/ id, int /*long*/ sel, int /*long*/ aTableView, int /*long*/ anObject, int /*long*/ aTableColumn, int /*long*/ rowIndex) { + if (checkColumn != null && aTableColumn == checkColumn.id) { + TableItem item = items [(int)/*64*/rowIndex]; + item.checked = !item.checked; + Event event = new Event (); + event.detail = SWT.CHECK; + event.item = item; + event.index = (int)/*64*/rowIndex; + postEvent (SWT.Selection, event); + item.redraw (-1); + } +} + +boolean tableView_shouldEditTableColumn_row (int /*long*/ id, int /*long*/ sel, int /*long*/ aTableView, int /*long*/ aTableColumn, int /*long*/ rowIndex) { + return false; +} + +void tableView_willDisplayCell_forTableColumn_row (int /*long*/ id, int /*long*/ sel, int /*long*/ aTableView, int /*long*/ cell, int /*long*/ tableColumn, int /*long*/ rowIndex) { + if (checkColumn != null && tableColumn == checkColumn.id) return; + TableItem item = items [(int)/*64*/rowIndex]; + int index = 0; + for (int i=0; i<columnCount; i++) { + if (columns [i].nsColumn.id == tableColumn) { + index = i; + break; + } + } + NSTextFieldCell textCell = new NSTextFieldCell (cell); + OS.object_setInstanceVariable(cell, Display.SWT_ROW, rowIndex); + OS.object_setInstanceVariable(cell, Display.SWT_COLUMN, tableColumn); + Image image = index == 0 ? item.image : (item.images == null ? null : item.images [index]); + textCell.setImage (image != null ? image.handle : null); + NSColor color; + if (textCell.isEnabled()) { + if (textCell.isHighlighted()) { + color = NSColor.selectedControlTextColor(); + } else { + Color foreground = item.cellForeground != null ? item.cellForeground [index] : null; + if (foreground == null) foreground = item.foreground; + if (foreground == null) foreground = getForegroundColor(); + color = NSColor.colorWithDeviceRed (foreground.handle [0], foreground.handle [1], foreground.handle [2], 1); + } + } else { + color = NSColor.disabledControlTextColor(); + } + int alignment = OS.NSLeftTextAlignment; + if (columnCount > 0) { + int style = columns [index].style; + if ((style & SWT.CENTER) != 0) { + alignment = OS.NSCenterTextAlignment; + } else if ((style & SWT.RIGHT) != 0) { + alignment = OS.NSRightTextAlignment; + } + } + Font font = item.cellFont != null ? item.cellFont [index] : null; + if (font == null) font = item.font; + if (font == null) font = this.font; + if (font == null) font = defaultFont (); + if (font.extraTraits != 0) { + NSMutableDictionary dict = ((NSMutableDictionary)new NSMutableDictionary().alloc()).initWithCapacity(5); + dict.setObject (color, OS.NSForegroundColorAttributeName); + dict.setObject (font.handle, OS.NSFontAttributeName); + addTraits(dict, font); + NSMutableParagraphStyle paragraphStyle = (NSMutableParagraphStyle)new NSMutableParagraphStyle ().alloc ().init (); + paragraphStyle.setLineBreakMode (OS.NSLineBreakByClipping); + paragraphStyle.setAlignment (alignment); + dict.setObject (paragraphStyle, OS.NSParagraphStyleAttributeName); + paragraphStyle.release (); + NSAttributedString attribStr = ((NSAttributedString) new NSAttributedString ().alloc ()).initWithString (textCell.title(), dict); + textCell.setAttributedStringValue(attribStr); + attribStr.release(); + dict.release(); + } else { + textCell.setFont(font.handle); + textCell.setTextColor(color); + textCell.setAlignment (alignment); + } +} + +boolean tableView_writeRowsWithIndexes_toPasteboard(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1, int /*long*/ arg2) { + return sendMouseEvent(NSApplication.sharedApplication().currentEvent(), SWT.DragDetect, true); +} + +NSRect titleRectForBounds (int /*long*/ id, int /*long*/ sel, NSRect cellFrame) { + NSImage image = new NSCell(id).image(); + if (image != null) { + int imageWidth = imageBounds.width + IMAGE_GAP; + cellFrame.x += imageWidth; + cellFrame.width -= imageWidth; + } + return cellFrame; +} + +void updateCursorRects (boolean enabled) { + super.updateCursorRects (enabled); + if (headerView == null) return; + updateCursorRects (enabled, headerView); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TableColumn.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TableColumn.java new file mode 100755 index 0000000000..8bfd6587e4 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TableColumn.java @@ -0,0 +1,677 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * Instances of this class represent a column in a table widget. + * <p><dl> + * <dt><b>Styles:</b></dt> + * <dd>LEFT, RIGHT, CENTER</dd> + * <dt><b>Events:</b></dt> + * <dd> Move, Resize, Selection</dd> + * </dl> + * </p><p> + * Note: Only one of the styles LEFT, RIGHT and CENTER may be specified. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#table">Table, TableItem, TableColumn snippets</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class TableColumn extends Item { + Table parent; + NSTableColumn nsColumn; + String toolTipText, displayText; + boolean movable; + + static final int MARGIN = 2; + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>Table</code>) and a style value + * describing its behavior and appearance. The item is added + * to the end of the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#LEFT + * @see SWT#RIGHT + * @see SWT#CENTER + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public TableColumn (Table parent, int style) { + super (parent, checkStyle (style)); + this.parent = parent; + parent.createItem (this, parent.columnCount); +} + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>Table</code>), a style value + * describing its behavior and appearance, and the index + * at which to place it in the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * <p> + * Note that due to a restriction on some platforms, the first column + * is always left aligned. + * </p> + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * @param index the zero-relative index to store the receiver in its parent + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#LEFT + * @see SWT#RIGHT + * @see SWT#CENTER + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public TableColumn (Table parent, int style, int index) { + super (parent, checkStyle (style)); + this.parent = parent; + parent.createItem (this, index); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the control is moved or resized, by sending + * it one of the messages defined in the <code>ControlListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ControlListener + * @see #removeControlListener + */ +public void addControlListener(ControlListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Resize,typedListener); + addListener (SWT.Move,typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the control is selected by the user, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * <code>widgetSelected</code> is called when the column header is selected. + * <code>widgetDefaultSelected</code> is not called. + * </p> + * + * @param listener the listener which should be notified when the control is selected by the user + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener (SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Selection,typedListener); + addListener (SWT.DefaultSelection,typedListener); +} + +static int checkStyle (int style) { + return checkBits (style, SWT.LEFT, SWT.CENTER, SWT.RIGHT, 0, 0, 0); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +void deregister () { + super.deregister (); + display.removeWidget (nsColumn.headerCell()); +} + +void destroyWidget () { + parent.destroyItem (this); + releaseHandle (); +} + +void drawInteriorWithFrame_inView (int /*long*/ id, int /*long*/ sel, NSRect cellRect, int /*long*/ view) { + /* + * Feature in Cocoa. When the last column in a table does not reach the + * rightmost edge of the table view, the cell that draws the rightmost- + * column's header is also invoked to draw the header space between its + * right edge and the table's right edge. If this case is detected then + * nothing should be drawn. + */ + int columnIndex = parent.indexOf (nsColumn); + NSRect headerRect = parent.headerView.headerRectOfColumn (columnIndex); + if (headerRect.x != cellRect.x || headerRect.width != cellRect.width) return; + + NSGraphicsContext context = NSGraphicsContext.currentContext (); + context.saveGraphicsState (); + + int contentWidth = 0; + NSSize stringSize = null, imageSize = null; + NSAttributedString attrString = null; + NSTableHeaderCell headerCell = nsColumn.headerCell (); + if (displayText != null) { + Font font = Font.cocoa_new(display, headerCell.font ()); + attrString = parent.createString(displayText, font, null, SWT.LEFT, (parent.state & DISABLED) == 0, false); + stringSize = attrString.size (); + contentWidth += Math.ceil (stringSize.width); + if (image != null) contentWidth += MARGIN; /* space between image and text */ + } + if (image != null) { + imageSize = image.handle.size (); + contentWidth += Math.ceil (imageSize.width); + } + + if (parent.sortColumn == this && parent.sortDirection != SWT.NONE) { + boolean ascending = parent.sortDirection == SWT.UP; + headerCell.drawSortIndicatorWithFrame (cellRect, new NSView(view), ascending, 0); + /* remove the arrow's space from the available drawing width */ + NSRect sortRect = headerCell.sortIndicatorRectForBounds (cellRect); + cellRect.width = Math.max (0, sortRect.x - cellRect.x); + } + + int drawX = 0; + if ((style & SWT.CENTER) != 0) { + drawX = (int)(cellRect.x + Math.max (MARGIN, ((cellRect.width - contentWidth) / 2))); + } else if ((style & SWT.RIGHT) != 0) { + drawX = (int)(cellRect.x + Math.max (MARGIN, cellRect.width - contentWidth - MARGIN)); + } else { + drawX = (int)cellRect.x + MARGIN; + } + + if (image != null) { + NSRect destRect = new NSRect (); + destRect.x = drawX; + destRect.y = cellRect.y; + destRect.width = Math.min (imageSize.width, cellRect.width - 2 * MARGIN); + destRect.height = Math.min (imageSize.height, cellRect.height); + boolean isFlipped = new NSView (view).isFlipped(); + if (isFlipped) { + context.saveGraphicsState (); + NSAffineTransform transform = NSAffineTransform.transform (); + transform.scaleXBy (1, -1); + transform.translateXBy (0, -(destRect.height + 2 * destRect.y)); + transform.concat (); + } + NSRect sourceRect = new NSRect (); + sourceRect.width = destRect.width; + sourceRect.height = destRect.height; + image.handle.drawInRect (destRect, sourceRect, OS.NSCompositeSourceOver, 1f); + if (isFlipped) context.restoreGraphicsState (); + drawX += destRect.width; + } + + if (displayText != null && displayText.length () > 0) { + if (image != null) drawX += MARGIN; /* space between image and text */ + NSRect destRect = new NSRect (); + destRect.x = drawX; + destRect.y = cellRect.y; + destRect.width = Math.min (stringSize.width, cellRect.x + cellRect.width - MARGIN - drawX); + destRect.height = Math.min (stringSize.height, cellRect.height); + attrString.drawInRect (destRect); + } + if (attrString != null) attrString.release (); + + context.restoreGraphicsState (); +} + +/** + * Returns a value which describes the position of the + * text or image in the receiver. The value will be one of + * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>. + * + * @return the alignment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getAlignment () { + checkWidget (); + if ((style & SWT.LEFT) != 0) return SWT.LEFT; + if ((style & SWT.CENTER) != 0) return SWT.CENTER; + if ((style & SWT.RIGHT) != 0) return SWT.RIGHT; + return SWT.LEFT; +} + +String getNameText () { + return getText (); +} + +/** + * Returns the receiver's parent, which must be a <code>Table</code>. + * + * @return the receiver's parent + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Table getParent () { + checkWidget (); + return parent; +} + +/** + * Gets the moveable attribute. A column that is + * not moveable cannot be reordered by the user + * by dragging the header but may be reordered + * by the programmer. + * + * @return the moveable attribute + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Table#getColumnOrder() + * @see Table#setColumnOrder(int[]) + * @see TableColumn#setMoveable(boolean) + * @see SWT#Move + * + * @since 3.1 + */ +public boolean getMoveable () { + checkWidget (); + return movable; +} + +/** + * Gets the resizable attribute. A column that is + * not resizable cannot be dragged by the user but + * may be resized by the programmer. + * + * @return the resizable attribute + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getResizable () { + checkWidget (); + return nsColumn.resizingMask() != OS.NSTableColumnNoResizing; +} + +/** + * Returns the receiver's tool tip text, or null if it has + * not been set. + * + * @return the receiver's tool tip text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public String getToolTipText () { + checkWidget (); + return toolTipText; +} + +/** + * Gets the width of the receiver. + * + * @return the width + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getWidth () { + checkWidget (); + int width = (int)nsColumn.width(); + // TODO how to differentiate 0 and 1 cases? + if (width > 0) width += Table.CELL_GAP; + return width; +} + +/** + * Causes the receiver to be resized to its preferred size. + * For a composite, this involves computing the preferred size + * from its layout, if there is one. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + */ +public void pack () { + checkWidget (); + + int width = 0; + + /* compute header width */ + if (displayText != null) { + NSTableHeaderCell headerCell = nsColumn.headerCell (); + Font font = Font.cocoa_new(display, headerCell.font ()); + NSAttributedString attrString = parent.createString(displayText, font, null, 0, true, false); + NSSize stringSize = attrString.size (); + attrString.release (); + width += Math.ceil (stringSize.width); + if (image != null) width += MARGIN; /* space between image and text */ + } + if (image != null) { + NSSize imageSize = image.handle.size (); + width += Math.ceil (imageSize.width); + } + if (parent.sortColumn == this && parent.sortDirection != SWT.NONE) { + NSTableHeaderCell headerCell = nsColumn.headerCell (); + NSRect rect = new NSRect (); + rect.width = rect.height = Float.MAX_VALUE; + NSSize cellSize = headerCell.cellSizeForBounds (rect); + rect.height = cellSize.height; + NSRect sortRect = headerCell.sortIndicatorRectForBounds (rect); + width += Math.ceil (sortRect.width); + } + + /* compute item widths down column */ + GC gc = new GC (parent); + int index = parent.indexOf (this); + width = Math.max (width, parent.calculateWidth (parent.items, index, gc)); + gc.dispose (); + setWidth (width); +} + +void releaseHandle () { + super.releaseHandle (); + if (nsColumn != null) { + nsColumn.headerCell ().release (); + nsColumn.release (); + } + nsColumn = null; + parent = null; +} + +void releaseWidget () { + super.releaseWidget (); + if (parent.sortColumn == this) { + parent.sortColumn = null; + } +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is moved or resized. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ControlListener + * @see #addControlListener + */ +public void removeControlListener (ControlListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Move, listener); + eventTable.unhook (SWT.Resize, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is selected by the user. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener(SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Selection, listener); + eventTable.unhook (SWT.DefaultSelection,listener); +} + +/** + * Controls how text and images will be displayed in the receiver. + * The argument should be one of <code>LEFT</code>, <code>RIGHT</code> + * or <code>CENTER</code>. + * <p> + * Note that due to a restriction on some platforms, the first column + * is always left aligned. + * </p> + * @param alignment the new alignment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setAlignment (int alignment) { + checkWidget (); + if ((alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER)) == 0) return; + int index = parent.indexOf (this); + if (index == -1 || index == 0) return; + style &= ~(SWT.LEFT | SWT.RIGHT | SWT.CENTER); + style |= alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER); + NSTableView tableView = ((NSTableView) parent.view); + NSTableHeaderView headerView = tableView.headerView (); + if (headerView == null) return; + index = parent.indexOf (nsColumn); + NSRect rect = headerView.headerRectOfColumn (index); + headerView.setNeedsDisplayInRect (rect); + rect = tableView.rectOfColumn (index); + parent.view.setNeedsDisplayInRect (rect); +} + +public void setImage (Image image) { + checkWidget(); + if (image != null && image.isDisposed ()) { + error (SWT.ERROR_INVALID_ARGUMENT); + } + super.setImage (image); + NSTableHeaderView headerView = ((NSTableView) parent.view).headerView (); + if (headerView == null) return; + int index = parent.indexOf (nsColumn); + NSRect rect = headerView.headerRectOfColumn (index); + headerView.setNeedsDisplayInRect (rect); +} + +/** + * Sets the moveable attribute. A column that is + * moveable can be reordered by the user by dragging + * the header. A column that is not moveable cannot be + * dragged by the user but may be reordered + * by the programmer. + * + * @param moveable the moveable attribute + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Table#setColumnOrder(int[]) + * @see Table#getColumnOrder() + * @see TableColumn#getMoveable() + * @see SWT#Move + * + * @since 3.1 + */ +public void setMoveable (boolean moveable) { + checkWidget (); + this.movable = moveable; +} + +/** + * Sets the resizable attribute. A column that is + * resizable can be resized by the user dragging the + * edge of the header. A column that is not resizable + * cannot be dragged by the user but may be resized + * by the programmer. + * + * @param resizable the resize attribute + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setResizable (boolean resizable) { + checkWidget (); + nsColumn.setResizingMask (resizable ? OS.NSTableColumnUserResizingMask : OS.NSTableColumnNoResizing); +} + +public void setText (String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + super.setText (string); + char [] buffer = new char [text.length ()]; + text.getChars (0, buffer.length, buffer, 0); + int length = fixMnemonic (buffer); + displayText = new String (buffer, 0, length); + NSString title = NSString.stringWith (displayText); + nsColumn.headerCell ().setTitle (title); + NSTableHeaderView headerView = ((NSTableView) parent.view).headerView (); + if (headerView == null) return; + int index = parent.indexOf (nsColumn); + NSRect rect = headerView.headerRectOfColumn (index); + headerView.setNeedsDisplayInRect (rect); +} + +/** + * Sets the receiver's tool tip text to the argument, which + * may be null indicating that the default tool tip for the + * control will be shown. For a control that has a default + * tool tip, such as the Tree control on Windows, setting + * the tool tip text to an empty string replaces the default, + * causing no tool tip text to be shown. + * <p> + * The mnemonic indicator (character '&') is not displayed in a tool tip. + * To display a single '&' in the tool tip, the character '&' can be + * escaped by doubling it in the string. + * </p> + * + * @param string the new tool tip text (or null) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public void setToolTipText (String string) { + checkWidget(); + toolTipText = string; + parent.checkToolTip (this); +} + +/** + * Sets the width of the receiver. + * + * @param width the new width + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setWidth (int width) { + checkWidget (); + if (width < 0) return; + // TODO how to differentiate 0 and 1 cases? + width = Math.max (0, width - Table.CELL_GAP); + nsColumn.setWidth (width); +} + +String tooltipText () { + return toolTipText; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TableItem.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TableItem.java new file mode 100755 index 0000000000..3711d3dfb1 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TableItem.java @@ -0,0 +1,1031 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * Instances of this class represent a selectable user interface object + * that represents an item in a table. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>(none)</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#table">Table, TableItem, TableColumn snippets</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class TableItem extends Item { + Table parent; + String [] strings; + Image [] images; + boolean checked, grayed, cached; + Color foreground, background; + Color[] cellForeground, cellBackground; + Font font; + Font[] cellFont; + int width = -1; + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>Table</code>) and a style value + * describing its behavior and appearance. The item is added + * to the end of the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public TableItem (Table parent, int style) { + this (parent, style, checkNull (parent).getItemCount (), true); +} + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>Table</code>), a style value + * describing its behavior and appearance, and the index + * at which to place it in the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * @param index the zero-relative index to store the receiver in its parent + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public TableItem (Table parent, int style, int index) { + this (parent, style, index, true); +} + +TableItem (Table parent, int style, int index, boolean create) { + super (parent, style); + this.parent = parent; + if (create) parent.createItem (this, index); +} + +static Table checkNull (Table control) { + if (control == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return control; +} + +int calculateWidth (int index, GC gc) { + if (index == 0 && width != -1) return width; + Font font = null; + if (cellFont != null) font = cellFont[index]; + if (font == null) font = this.font; + if (font == null) font = parent.font; + if (font == null) font = parent.defaultFont(); + String text = index == 0 ? this.text : (strings == null ? "" : strings [index]); + Image image = index == 0 ? this.image : (images == null ? null : images [index]); + NSCell cell = parent.dataCell; + if (font.extraTraits != 0) { + NSAttributedString attribStr = parent.createString(text, font, null, 0, true, false); + cell.setAttributedStringValue(attribStr); + attribStr.release(); + } else { + cell.setFont (font.handle); + cell.setTitle (NSString.stringWith(text != null ? text : "")); + } + + /* This code is inlined for performance */ + objc_super super_struct = new objc_super(); + super_struct.receiver = cell.id; + super_struct.super_class = OS.objc_msgSend(cell.id, OS.sel_superclass); + NSSize size = new NSSize(); + OS.objc_msgSendSuper_stret(size, super_struct, OS.sel_cellSize); + if (image != null) size.width += parent.imageBounds.width + Table.IMAGE_GAP; +// cell.setImage (image != null ? image.handle : null); +// NSSize size = cell.cellSize (); + + int width = (int)Math.ceil (size.width); + boolean sendMeasure = true; + if ((parent.style & SWT.VIRTUAL) != 0) { + sendMeasure = cached; + } + if (sendMeasure && parent.hooks (SWT.MeasureItem)) { + gc.setFont (font); + Event event = new Event (); + event.item = this; + event.index = index; + event.gc = gc; + NSTableView widget = (NSTableView)parent.view; + int height = (int)widget.rowHeight (); + event.width = width; + event.height = height; + parent.sendEvent (SWT.MeasureItem, event); + if (height < event.height) { + widget.setRowHeight (event.height); + widget.setNeedsDisplay (true); + } + width = event.width; + } + if (index == 0) this.width = width; + return width; +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +void clear () { + text = ""; + image = null; + strings = null; + images = null; + checked = grayed = cached = false; + foreground = background = null; + cellForeground = cellBackground = null; + font = null; + cellFont = null; + width = -1; +} + +NSObject createString (int index) { + String text = index == 0 ? this.text : (strings == null ? "" : strings [index]); + return NSString.stringWith(text != null ? text : ""); +} + +void destroyWidget () { + parent.destroyItem (this); + releaseHandle (); +} + +/** + * Returns the receiver's background color. + * + * @return the background color + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.0 + */ +public Color getBackground () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + return background != null ? background : parent.getBackground (); +} + +/** + * Returns the background color at the given column index in the receiver. + * + * @param index the column index + * @return the background color + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public Color getBackground (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + int count = Math.max (1, parent.columnCount); + if (0 > index || index > count -1) return getBackground (); + if (cellBackground == null || cellBackground [index] == null) return getBackground (); + return cellBackground [index]; +} + +/** + * Returns a rectangle describing the receiver's size and location + * relative to its parent. + * + * @return the receiver's bounding rectangle + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public Rectangle getBounds () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + NSTableView tableView = (NSTableView) parent.view; + NSRect rect = tableView.rectOfRow (parent.indexOf (this)); + return new Rectangle((int) rect.x, (int) rect.y, (int) rect.width, (int) rect.height); +} + +/** + * Returns a rectangle describing the receiver's size and location + * relative to its parent at a column in the table. + * + * @param index the index that specifies the column + * @return the receiver's bounding column rectangle + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Rectangle getBounds (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + if (!(0 <= index && index < Math.max (1, parent.columnCount))) return new Rectangle (0, 0, 0, 0); + + NSTableView tableView = (NSTableView) parent.view; + if (parent.columnCount == 0) { + index = (parent.style & SWT.CHECK) != 0 ? 1 : 0; + } else { + TableColumn column = parent.getColumn (index); + index = parent.indexOf (column.nsColumn); + } + NSRect rect = tableView.frameOfCellAtColumn (index, parent.indexOf (this)); + return new Rectangle ((int) rect.x, (int) rect.y, (int) rect.width, (int) rect.height); +} + +/** + * Returns <code>true</code> if the receiver is checked, + * and false otherwise. When the parent does not have + * the <code>CHECK</code> style, return false. + * + * @return the checked state of the checkbox + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getChecked () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + if ((parent.style & SWT.CHECK) == 0) return false; + return checked; +} + +/** + * Returns the font that the receiver will use to paint textual information for this item. + * + * @return the receiver's font + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public Font getFont () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + return font != null ? font : parent.getFont (); +} + +/** + * Returns the font that the receiver will use to paint textual information + * for the specified cell in this item. + * + * @param index the column index + * @return the receiver's font + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public Font getFont (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + int count = Math.max (1, parent.columnCount); + if (0 > index || index > count -1) return getFont (); + if (cellFont == null || cellFont [index] == null) return getFont (); + return cellFont [index]; +} + +/** + * Returns the foreground color that the receiver will use to draw. + * + * @return the receiver's foreground color + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.0 + */ +public Color getForeground () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + return foreground != null ? foreground : parent.getForeground (); +} + +/** + * + * Returns the foreground color at the given column index in the receiver. + * + * @param index the column index + * @return the foreground color + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public Color getForeground (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + int count = Math.max (1, parent.columnCount); + if (0 > index || index > count -1) return getForeground (); + if (cellForeground == null || cellForeground [index] == null) return getForeground (); + return cellForeground [index]; +} + +/** + * Returns <code>true</code> if the receiver is grayed, + * and false otherwise. When the parent does not have + * the <code>CHECK</code> style, return false. + * + * @return the grayed state of the checkbox + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getGrayed () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + if ((parent.style & SWT.CHECK) == 0) return false; + return grayed; +} + +public Image getImage () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + return super.getImage (); +} + +/** + * Returns the image stored at the given column index in the receiver, + * or null if the image has not been set or if the column does not exist. + * + * @param index the column index + * @return the image stored at the given column index in the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Image getImage (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + if (index == 0) return getImage (); + if (images != null) { + if (0 <= index && index < images.length) return images [index]; + } + return null; +} + +/** + * Returns a rectangle describing the size and location + * relative to its parent of an image at a column in the + * table. An empty rectangle is returned if index exceeds + * the index of the table's last column. + * + * @param index the index that specifies the column + * @return the receiver's bounding image rectangle + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Rectangle getImageBounds (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + if (!(0 <= index && index < Math.max (1, parent.columnCount))) return new Rectangle (0, 0, 0, 0); + + NSTableView tableView = (NSTableView) parent.view; + Image image = index == 0 ? this.image : (images != null) ? images [index] : null; + if (parent.columnCount == 0) { + index = (parent.style & SWT.CHECK) != 0 ? 1 : 0; + } else { + TableColumn column = parent.getColumn (index); + index = parent.indexOf (column.nsColumn); + } + NSRect rect = tableView.frameOfCellAtColumn (index, parent.indexOf (this)); + rect.x += Table.IMAGE_GAP; + if (image != null) { + rect.width = parent.imageBounds.width; + } else { + rect.width = 0; + } + return new Rectangle((int) rect.x, (int) rect.y, (int) rect.width, (int) rect.height); +} + +/** + * Gets the image indent. + * + * @return the indent + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getImageIndent () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + return 0; +} + +String getNameText () { + if ((parent.style & SWT.VIRTUAL) != 0) { + if (!cached) return "*virtual*"; //$NON-NLS-1$ + } + return super.getNameText (); +} + +/** + * Returns the receiver's parent, which must be a <code>Table</code>. + * + * @return the receiver's parent + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Table getParent () { + checkWidget (); + return parent; +} + +public String getText () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + return super.getText (); +} + +/** + * Returns the text stored at the given column index in the receiver, + * or empty string if the text has not been set. + * + * @param index the column index + * @return the text stored at the given column index in the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getText (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + if (index == 0) return getText (); + if (strings != null) { + if (0 <= index && index < strings.length) { + String string = strings [index]; + return string != null ? string : ""; + } + } + return ""; +} + +/** + * Returns a rectangle describing the size and location + * relative to its parent of the text at a column in the + * table. An empty rectangle is returned if index exceeds + * the index of the table's last column. + * + * @param index the index that specifies the column + * @return the receiver's bounding text rectangle + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.3 + */ +public Rectangle getTextBounds (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + if (!(0 <= index && index < Math.max (1, parent.columnCount))) return new Rectangle (0, 0, 0, 0); + + NSTableView tableView = (NSTableView) parent.view; + Image image = index == 0 ? this.image : (images != null) ? images [index] : null; + if (parent.columnCount == 0) { + index = (parent.style & SWT.CHECK) != 0 ? 1 : 0; + } else { + TableColumn column = parent.getColumn (index); + index = parent.indexOf (column.nsColumn); + } + NSRect rect = tableView.frameOfCellAtColumn (index, parent.indexOf (this)); + rect.x += Table.TEXT_GAP; + rect.width -= Table.TEXT_GAP; + if (image != null) { + int offset = parent.imageBounds.width + Table.IMAGE_GAP; + rect.x += offset; + rect.width -= offset; + } + return new Rectangle((int) rect.x, (int) rect.y, (int) rect.width, (int) rect.height); +} + +void redraw (int columnIndex) { + if (parent.currentItem == this || !isDrawing()) return; + /* redraw the full item if columnIndex == -1 */ + NSTableView tableView = (NSTableView) parent.view; + NSRect rect = null; + if (columnIndex == -1 || parent.hooks (SWT.MeasureItem) || parent.hooks (SWT.EraseItem) || parent.hooks (SWT.PaintItem)) { + rect = tableView.rectOfRow (parent.indexOf (this)); + } else { + int index; + if (parent.columnCount == 0) { + index = (parent.style & SWT.CHECK) != 0 ? 1 : 0; + } else { + if (0 <= columnIndex && columnIndex < parent.columnCount) { + index = parent.indexOf (parent.columns[columnIndex].nsColumn); + } else { + return; + } + } + rect = tableView.frameOfCellAtColumn (index, parent.indexOf (this)); + } + tableView.setNeedsDisplayInRect (rect); +} + +void releaseHandle () { + super.releaseHandle (); + parent = null; +} + +void releaseParent () { + super.releaseParent (); +// parent.checkItems (true); +} + +void releaseWidget () { + super.releaseWidget (); + strings = null; + images = null; + background = foreground = null; + font = null; + cellBackground = cellForeground = null; + cellFont = null; +} + +/** + * Sets the receiver's background color to the color specified + * by the argument, or to the default system color for the item + * if the argument is null. + * + * @param color the new color (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.0 + */ +public void setBackground (Color color) { + checkWidget (); + if (color != null && color.isDisposed ()) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + Color oldColor = background; + if (oldColor == color) return; + background = color; + if (oldColor != null && oldColor.equals (color)) return; + cached = true; + redraw (-1); +} + +/** + * Sets the background color at the given column index in the receiver + * to the color specified by the argument, or to the default system color for the item + * if the argument is null. + * + * @param index the column index + * @param color the new color (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public void setBackground (int index, Color color) { + checkWidget (); + if (color != null && color.isDisposed ()) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + int count = Math.max (1, parent.columnCount); + if (0 > index || index > count - 1) return; + if (cellBackground == null) { + if (color == null) return; + cellBackground = new Color [count]; + } + Color oldColor = cellBackground [index]; + if (oldColor == color) return; + cellBackground [index] = color; + if (oldColor != null && oldColor.equals (color)) return; + cached = true; + redraw (index); +} + +/** + * Sets the checked state of the checkbox for this item. This state change + * only applies if the Table was created with the SWT.CHECK style. + * + * @param checked the new checked state of the checkbox + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setChecked (boolean checked) { + checkWidget (); + if ((parent.style & SWT.CHECK) == 0) return; + if (this.checked == checked) return; + this.checked = checked; + cached = true; + redraw (-1); +} + +/** + * Sets the font that the receiver will use to paint textual information + * for this item to the font specified by the argument, or to the default font + * for that kind of control if the argument is null. + * + * @param font the new font (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public void setFont (Font font) { + checkWidget (); + if (font != null && font.isDisposed ()) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + Font oldFont = this.font; + if (oldFont == font) return; + this.font = font; + if (oldFont != null && oldFont.equals (font)) return; + width = -1; + cached = true; + redraw (-1); +} + +/** + * Sets the font that the receiver will use to paint textual information + * for the specified cell in this item to the font specified by the + * argument, or to the default font for that kind of control if the + * argument is null. + * + * @param index the column index + * @param font the new font (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public void setFont (int index, Font font) { + checkWidget (); + if (font != null && font.isDisposed ()) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + int count = Math.max (1, parent.columnCount); + if (0 > index || index > count - 1) return; + if (cellFont == null) { + if (font == null) return; + cellFont = new Font [count]; + } + Font oldFont = cellFont [index]; + if (oldFont == font) return; + cellFont [index] = font; + if (oldFont != null && oldFont.equals (font)) return; + width = -1; + cached = true; + redraw (index); +} + +/** + * Sets the receiver's foreground color to the color specified + * by the argument, or to the default system color for the item + * if the argument is null. + * + * @param color the new color (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.0 + */ +public void setForeground (Color color) { + checkWidget (); + if (color != null && color.isDisposed ()) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + Color oldColor = foreground; + if (oldColor == color) return; + foreground = color; + if (oldColor != null && oldColor.equals (color)) return; + cached = true; + redraw (-1); +} + +/** + * Sets the foreground color at the given column index in the receiver + * to the color specified by the argument, or to the default system color for the item + * if the argument is null. + * + * @param index the column index + * @param color the new color (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public void setForeground (int index, Color color) { + checkWidget (); + if (color != null && color.isDisposed ()) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + int count = Math.max (1, parent.columnCount); + if (0 > index || index > count - 1) return; + if (cellForeground == null) { + if (color == null) return; + cellForeground = new Color [count]; + } + Color oldColor = cellForeground [index]; + if (oldColor == color) return; + cellForeground [index] = color; + if (oldColor != null && oldColor.equals (color)) return; + cached = true; + redraw (index); +} + +/** + * Sets the grayed state of the checkbox for this item. This state change + * only applies if the Table was created with the SWT.CHECK style. + * + * @param grayed the new grayed state of the checkbox; + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setGrayed (boolean grayed) { + checkWidget (); + if ((parent.style & SWT.CHECK) == 0) return; + if (this.grayed == grayed) return; + this.grayed = grayed; + cached = true; + redraw (-1); +} + +/** + * Sets the image for multiple columns in the table. + * + * @param images the array of new images + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the array of images is null</li> + * <li>ERROR_INVALID_ARGUMENT - if one of the images has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setImage (Image [] images) { + checkWidget (); + if (images == null) error (SWT.ERROR_NULL_ARGUMENT); + for (int i=0; i<images.length; i++) { + setImage (i, images [i]); + } +} + +/** + * Sets the receiver's image at a column. + * + * @param index the column index + * @param image the new image + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setImage (int index, Image image) { + checkWidget (); + if (image != null && image.isDisposed ()) { + error(SWT.ERROR_INVALID_ARGUMENT); + } + int itemIndex = parent.indexOf (this); + if (itemIndex == -1) return; + if (parent.imageBounds == null && image != null) { + parent.setItemHeight (image, null, false); + } + if (index == 0) { + if (image != null && image.type == SWT.ICON) { + if (image.equals (this.image)) return; + } + width = -1; + super.setImage (image); + } + int count = Math.max (1, parent.columnCount); + if (0 <= index && index < count) { + if (images == null) images = new Image [count]; + if (image != null && image.type == SWT.ICON) { + if (image.equals (images [index])) return; + } + images [index] = image; + } + cached = true; + if (index == 0) parent.setScrollWidth (this); + redraw (index); +} + +public void setImage (Image image) { + checkWidget (); + setImage (0, image); +} + +/** + * Sets the indent of the first column's image, expressed in terms of the image's width. + * + * @param indent the new indent + * + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @deprecated this functionality is not supported on most platforms + */ +public void setImageIndent (int indent) { + checkWidget (); + if (indent < 0) return; + cached = true; + /* Image indent is not supported on the Macintosh */ +} + +/** + * Sets the text for multiple columns in the table. + * + * @param strings the array of new strings + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the text is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setText (String [] strings) { + checkWidget (); + if (strings == null) error (SWT.ERROR_NULL_ARGUMENT); + for (int i=0; i<strings.length; i++) { + String string = strings [i]; + if (string != null) setText (i, string); + } +} + +/** + * Sets the receiver's text at a column + * + * @param index the column index + * @param string the new text + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the text is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setText (int index, String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if (index == 0) { + if (string.equals (text)) return; + width = -1; + super.setText (string); + } + int count = Math.max (1, parent.columnCount); + if (0 <= index && index < count) { + if (strings == null) strings = new String [count]; + if (string.equals (strings [index])) return; + strings [index] = string; + } + cached = true; + if (index == 0) parent.setScrollWidth (this); + redraw (index); +} + +public void setText (String string) { + checkWidget (); + setText (0, string); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Text.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Text.java new file mode 100755 index 0000000000..b7876ed9bf --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Text.java @@ -0,0 +1,1983 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class are selectable user interface + * objects that allow the user to enter and modify text. + * Text controls can be either single or multi-line. + * When a text control is created with a border, the + * operating system includes a platform specific inset + * around the contents of the control. When created + * without a border, an effort is made to remove the + * inset such that the preferred size of the control + * is the same size as the contents. + * <p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>CENTER, ICON_CANCEL, ICON_SEARCH, LEFT, MULTI, PASSWORD, SEARCH, SINGLE, RIGHT, READ_ONLY, WRAP</dd> + * <dt><b>Events:</b></dt> + * <dd>DefaultSelection, Modify, Verify</dd> + * </dl> + * <p> + * Note: Only one of the styles MULTI and SINGLE may be specified, + * and only one of the styles LEFT, CENTER, and RIGHT may be specified. + * </p> + * <p> + * Note: The styles ICON_CANCEL and ICON_SEARCH are hints used in combination with SEARCH. + * When the platform supports the hint, the text control shows these icons. When an icon + * is selected, a default selection event is sent with the detail field set to one of + * ICON_CANCEL or ICON_SEARCH. Normally, application code does not need to check the + * detail. In the case of ICON_CANCEL, the text is cleared before the default selection + * event is sent causing the application to search for an empty string. + * </p> + * <p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#text">Text snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Text extends Scrollable { + int textLimit = LIMIT, tabs = 8; + char echoCharacter; + boolean doubleClick, receivingFocus; + String hiddenText, message; + NSRange selectionRange; + id targetSearch, targetCancel; + int /*long*/ actionSearch, actionCancel; + + /** + * The maximum number of characters that can be entered + * into a text widget. + * <p> + * Note that this value is platform dependent, based upon + * the native widget implementation. + * </p> + */ + public static final int LIMIT; + + /** + * The delimiter used by multi-line text widgets. When text + * is queried and from the widget, it will be delimited using + * this delimiter. + */ + public static final String DELIMITER; + static final char PASSWORD = '\u2022'; + + /* + * These values can be different on different platforms. + * Therefore they are not initialized in the declaration + * to stop the compiler from inlining. + */ + static { + LIMIT = 0x7FFFFFFF; + DELIMITER = "\r"; + } + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#SINGLE + * @see SWT#MULTI + * @see SWT#READ_ONLY + * @see SWT#WRAP + * @see SWT#LEFT + * @see SWT#RIGHT + * @see SWT#CENTER + * @see SWT#PASSWORD + * @see SWT#SEARCH + * @see SWT#ICON_SEARCH + * @see SWT#ICON_CANCEL + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Text (Composite parent, int style) { + super (parent, checkStyle (style)); + if ((style & SWT.SEARCH) != 0) { + /* + * Ensure that SWT.ICON_CANCEL and ICON_SEARCH are set. + * NOTE: ICON_CANCEL has the same value as H_SCROLL and + * ICON_SEARCH has the same value as V_SCROLL so it is + * necessary to first clear these bits to avoid a scroll + * bar and then reset the bit using the original style + * supplied by the programmer. + */ + NSSearchFieldCell cell = new NSSearchFieldCell (((NSSearchField) view).cell ()); + if ((style & SWT.ICON_CANCEL) != 0) { + this.style |= SWT.ICON_CANCEL; + NSButtonCell cancelCell = cell.cancelButtonCell(); + targetCancel = cancelCell.target(); + actionCancel = cancelCell.action(); + cancelCell.setTarget (view); + cancelCell.setAction (OS.sel_sendCancelSelection); + } else { + cell.setCancelButtonCell (null); + } + if ((style & SWT.ICON_SEARCH) != 0) { + this.style |= SWT.ICON_SEARCH; + NSButtonCell searchCell = cell.searchButtonCell(); + targetSearch = searchCell.target(); + actionSearch = searchCell.action(); + searchCell.setTarget (view); + searchCell.setAction (OS.sel_sendSearchSelection); + } else { + cell.setSearchButtonCell (null); + } + } +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the receiver's text is modified, by sending + * it one of the messages defined in the <code>ModifyListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ModifyListener + * @see #removeModifyListener + */ +public void addModifyListener (ModifyListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Modify, typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the control is selected by the user, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * <code>widgetSelected</code> is not called for texts. + * <code>widgetDefaultSelected</code> is typically called when ENTER is pressed in a single-line text, + * or when ENTER is pressed in a search text. If the receiver has the <code>SWT.SEARCH | SWT.CANCEL</code> style + * and the user cancels the search, the event object detail field contains the value <code>SWT.CANCEL</code>. + * </p> + * + * @param listener the listener which should be notified when the control is selected by the user + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener(SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Selection,typedListener); + addListener (SWT.DefaultSelection,typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the receiver's text is verified, by sending + * it one of the messages defined in the <code>VerifyListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see VerifyListener + * @see #removeVerifyListener + */ +public void addVerifyListener (VerifyListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Verify, typedListener); +} + +/** + * Appends a string. + * <p> + * The new text is appended to the text at + * the end of the widget. + * </p> + * + * @param string the string to be appended + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void append (String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if (hooks (SWT.Verify) || filters (SWT.Verify)) { + int charCount = getCharCount (); + string = verifyText (string, charCount, charCount, null); + if (string == null) return; + } + NSString str = NSString.stringWith (string); + if ((style & SWT.SINGLE) != 0) { + setSelection (getCharCount ()); + insertEditText (string); + } else { + NSTextView widget = (NSTextView) view; + NSTextStorage storage = widget.textStorage (); + NSRange range = new NSRange(); + range.location = storage.length(); + storage.replaceCharactersInRange (range, str); + range.location = storage.length(); + widget.scrollRangeToVisible (range); + widget.setSelectedRange(range); + } + if (string.length () != 0) sendEvent (SWT.Modify); +} + +boolean becomeFirstResponder (int /*long*/ id, int /*long*/ sel) { + receivingFocus = true; + boolean result = super.becomeFirstResponder (id, sel); + receivingFocus = false; + return result; +} + +static int checkStyle (int style) { + if ((style & SWT.SEARCH) != 0) { + style |= SWT.SINGLE | SWT.BORDER; + style &= ~SWT.PASSWORD; + /* + * NOTE: ICON_CANCEL has the same value as H_SCROLL and + * ICON_SEARCH has the same value as V_SCROLL so they are + * cleared because SWT.SINGLE is set. + */ + } + if ((style & SWT.SINGLE) != 0 && (style & SWT.MULTI) != 0) { + style &= ~SWT.MULTI; + } + style = checkBits (style, SWT.LEFT, SWT.CENTER, SWT.RIGHT, 0, 0, 0); + if ((style & SWT.SINGLE) != 0) style &= ~(SWT.H_SCROLL | SWT.V_SCROLL | SWT.WRAP); + if ((style & SWT.WRAP) != 0) { + style |= SWT.MULTI; + style &= ~SWT.H_SCROLL; + } + if ((style & SWT.MULTI) != 0) style &= ~SWT.PASSWORD; + if ((style & (SWT.SINGLE | SWT.MULTI)) != 0) return style; + if ((style & (SWT.H_SCROLL | SWT.V_SCROLL)) != 0) return style | SWT.MULTI; + return style | SWT.SINGLE; +} + +/** + * Clears the selection. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void clearSelection () { + checkWidget (); + Point selection = getSelection (); + setSelection (selection.x); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + int width = 0, height = 0; + if ((style & SWT.SINGLE) != 0) { + NSTextField widget = (NSTextField) view; + NSSize size = widget.cell ().cellSize (); + width = (int)Math.ceil (size.width); + height = (int)Math.ceil (size.height); + + Point border = null; + if ((style & SWT.BORDER) != 0 && (wHint != SWT.DEFAULT || hHint != SWT.DEFAULT)) { + /* determine the size of the cell without its border */ + NSRect insets = widget.cell ().titleRectForBounds (new NSRect ()); + border = new Point (-(int)Math.ceil (insets.width), -(int)Math.ceil (insets.height)); + width -= border.x; + height -= border.y; + } + if (width <= 0) width = DEFAULT_WIDTH; + if (height <= 0) height = DEFAULT_HEIGHT; + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + if (border != null) { + /* re-add the border size (if any) now that wHint/hHint is taken */ + width += border.x; + height += border.y; + } + } else { + NSLayoutManager layoutManager = (NSLayoutManager)new NSLayoutManager ().alloc ().init (); + NSTextContainer textContainer = (NSTextContainer)new NSTextContainer ().alloc (); + NSSize size = new NSSize (); + size.width = size.height = Float.MAX_VALUE; + if ((style & SWT.WRAP) != 0) { + if (wHint != SWT.DEFAULT) size.width = wHint; + if (hHint != SWT.DEFAULT) size.height = hHint; + } + textContainer.initWithContainerSize (size); + layoutManager.addTextContainer (textContainer); + + NSTextStorage textStorage = (NSTextStorage)new NSTextStorage ().alloc ().init (); + textStorage.setAttributedString (((NSTextView)view).textStorage ()); + layoutManager.setTextStorage (textStorage); + layoutManager.glyphRangeForTextContainer (textContainer); + + NSRect rect = layoutManager.usedRectForTextContainer (textContainer); + width = layoutManager.numberOfGlyphs () == 0 ? DEFAULT_WIDTH : (int)Math.ceil (rect.width); + height = (int)Math.ceil (rect.height); + textStorage.release (); + textContainer.release (); + layoutManager.release (); + + if (width <= 0) width = DEFAULT_WIDTH; + if (height <= 0) height = DEFAULT_HEIGHT; + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + Rectangle trim = computeTrim (0, 0, width, height); + width = trim.width; + height = trim.height; + } + return new Point (width, height); +} + +public Rectangle computeTrim (int x, int y, int width, int height) { + Rectangle result = super.computeTrim (x, y, width, height); + if ((style & SWT.SINGLE) != 0) { + NSTextField widget = (NSTextField) view; + if ((style & SWT.SEARCH) != 0) { + NSSearchFieldCell cell = new NSSearchFieldCell (widget.cell ()); + int testWidth = 100; + NSRect rect = new NSRect (); + rect.width = testWidth; + rect = cell.searchTextRectForBounds (rect); + int leftIndent = (int)rect.x; + int rightIndent = testWidth - leftIndent - (int)Math.ceil (rect.width); + result.x -= leftIndent; + result.width += leftIndent + rightIndent; + } + NSRect inset = widget.cell ().titleRectForBounds (new NSRect ()); + result.x -= inset.x; + result.y -= inset.y; + result.width -= inset.width; + result.height -= inset.height; + } + return result; +} + +/** + * Copies the selected text. + * <p> + * The current selection is copied to the clipboard. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void copy () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) { + Point selection = getSelection (); + if (selection.x == selection.y) return; + copyToClipboard (getEditText (selection.x, selection.y - 1)); + } else { + NSText text = (NSText) view; + if (text.selectedRange ().length == 0) return; + text.copy (null); + } +} + +void createHandle () { + if ((style & SWT.READ_ONLY) != 0) { + if ((style & (SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL)) == 0) { + state |= THEME_BACKGROUND; + } + } + if ((style & SWT.SINGLE) != 0) { + NSTextField widget; + if ((style & SWT.PASSWORD) != 0) { + widget = (NSTextField) new SWTSecureTextField ().alloc (); + } else if ((style & SWT.SEARCH) != 0) { + widget = (NSTextField) new SWTSearchField ().alloc (); + } else { + widget = (NSTextField) new SWTTextField ().alloc (); + } + widget.init (); + widget.setSelectable (true); + widget.setEditable((style & SWT.READ_ONLY) == 0); + if ((style & SWT.BORDER) == 0) { + widget.setFocusRingType (OS.NSFocusRingTypeNone); + widget.setBordered (false); + } + int align = OS.NSLeftTextAlignment; + if ((style & SWT.CENTER) != 0) align = OS.NSCenterTextAlignment; + if ((style & SWT.RIGHT) != 0) align = OS.NSRightTextAlignment; + widget.setAlignment (align); + NSCell cell = widget.cell(); + cell.setWraps(false); + cell.setScrollable(true); +// widget.setTarget(widget); +// widget.setAction(OS.sel_sendSelection); + view = widget; + } else { + NSScrollView scrollWidget = (NSScrollView) new SWTScrollView ().alloc (); + scrollWidget.init (); + scrollWidget.setHasVerticalScroller ((style & SWT.VERTICAL) != 0); + scrollWidget.setHasHorizontalScroller ((style & SWT.HORIZONTAL) != 0); + scrollWidget.setAutoresizesSubviews (true); + if ((style & SWT.BORDER) != 0) scrollWidget.setBorderType (OS.NSBezelBorder); + + NSTextView widget = (NSTextView) new SWTTextView ().alloc (); + widget.init (); + widget.setEditable ((style & SWT.READ_ONLY) == 0); + + NSSize size = new NSSize (); + size.width = size.height = Float.MAX_VALUE; + widget.setMaxSize (size); + widget.setAutoresizingMask (OS.NSViewWidthSizable | OS.NSViewHeightSizable); + + if ((style & SWT.WRAP) == 0) { + NSTextContainer textContainer = widget.textContainer (); + widget.setHorizontallyResizable (true); + textContainer.setWidthTracksTextView (false); + NSSize csize = new NSSize (); + csize.width = csize.height = Float.MAX_VALUE; + textContainer.setContainerSize (csize); + } + + int align = OS.NSLeftTextAlignment; + if ((style & SWT.CENTER) != 0) align = OS.NSCenterTextAlignment; + if ((style & SWT.RIGHT) != 0) align = OS.NSRightTextAlignment; + widget.setAlignment (align); +// widget.setTarget(widget); +// widget.setAction(OS.sel_sendSelection); + widget.setRichText (false); + widget.setDelegate(widget); + widget.setFont (display.getSystemFont ().handle); + + view = widget; + scrollView = scrollWidget; + } +} + +void createWidget () { + super.createWidget (); + doubleClick = true; + message = ""; +} + +/** + * Cuts the selected text. + * <p> + * The current selection is first copied to the + * clipboard and then deleted from the widget. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void cut () { + checkWidget (); + if ((style & SWT.READ_ONLY) != 0) return; + boolean cut = true; + char [] oldText = null; + Point oldSelection = getSelection (); + if (hooks (SWT.Verify) || filters (SWT.Verify)) { + if (oldSelection.x != oldSelection.y) { + oldText = getEditText (oldSelection.x, oldSelection.y - 1); + String newText = verifyText ("", oldSelection.x, oldSelection.y, null); + if (newText == null) return; + if (newText.length () != 0) { + copyToClipboard (oldText); + if ((style & SWT.SINGLE) != 0) { + insertEditText (newText); + } else { + NSTextView widget = (NSTextView) view; + widget.replaceCharactersInRange (widget.selectedRange (), NSString.stringWith (newText)); + } + cut = false; + } + } + } + if (cut) { + if ((style & SWT.SINGLE) != 0) { + if (oldText == null) oldText = getEditText (oldSelection.x, oldSelection.y - 1); + copyToClipboard (oldText); + insertEditText (""); + } else { + ((NSTextView) view).cut (null); + } + } + Point newSelection = getSelection (); + if (!cut || !oldSelection.equals (newSelection)) sendEvent (SWT.Modify); +} + +Color defaultBackground () { + return display.getWidgetColor (SWT.COLOR_LIST_BACKGROUND); +} + +NSFont defaultNSFont () { + if ((style & SWT.MULTI) != 0) return display.textViewFont; + if ((style & SWT.SEARCH) != 0) return display.searchFieldFont; + if ((style & SWT.PASSWORD) != 0) return display.secureTextFieldFont; + return display.textFieldFont; +} + +Color defaultForeground () { + return display.getWidgetColor (SWT.COLOR_LIST_FOREGROUND); +} + +void deregister() { + super.deregister(); + + if ((style & SWT.SINGLE) != 0) { + display.removeWidget(((NSControl)view).cell()); + } +} + +boolean dragDetect (int x, int y, boolean filter, boolean [] consume) { + Point selection = getSelection (); + if (selection.x != selection.y) { + int /*long*/ position = getPosition (x, y); + if (selection.x <= position && position < selection.y) { + if (super.dragDetect (x, y, filter, consume)) { + if (consume != null) consume [0] = true; + return true; + } + } + } + return false; +} + +/** + * Returns the line number of the caret. + * <p> + * The line number of the caret is returned. + * </p> + * + * @return the line number + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getCaretLineNumber () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) return 0; + return (getTopPixel () + getCaretLocation ().y) / getLineHeight (); +} + +boolean acceptsFirstResponder(int /*long*/ id, int /*long*/ sel) { + if ((style & SWT.READ_ONLY) != 0) return true; + return super.acceptsFirstResponder(id, sel); +} + +/** + * Returns a point describing the receiver's location relative + * to its parent (or its display if its parent is null). + * <p> + * The location of the caret is returned. + * </p> + * + * @return a point, the location of the caret + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Point getCaretLocation () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) { + //TODO - caret location for single text + return new Point (0, 0); + } + NSTextView widget = (NSTextView)view; + NSLayoutManager layoutManager = widget.layoutManager(); + NSTextContainer container = widget.textContainer(); + NSRange range = widget.selectedRange(); + int /*long*/ pRectCount = OS.malloc(C.PTR_SIZEOF); + int /*long*/ pArray = layoutManager.rectArrayForCharacterRange(range, range, container, pRectCount); + int /*long*/ [] rectCount = new int /*long*/ [1]; + OS.memmove(rectCount, pRectCount, C.PTR_SIZEOF); + OS.free(pRectCount); + NSRect rect = new NSRect(); + if (rectCount[0] > 0) OS.memmove(rect, pArray, NSRect.sizeof); + return new Point((int)rect.x, (int)rect.y); +} + +/** + * Returns the character position of the caret. + * <p> + * Indexing is zero based. + * </p> + * + * @return the position of the caret + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getCaretPosition () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) { + return selectionRange != null ? (int)/*64*/selectionRange.location : 0; + } else { + NSRange range = ((NSTextView)view).selectedRange(); + return (int)/*64*/range.location; + } +} + +/** + * Returns the number of characters. + * + * @return number of characters in the widget + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getCharCount () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) { + return (int)/*64*/new NSCell (((NSControl) view).cell ()).title ().length (); + } else { + return (int)/*64*/((NSTextView) view).textStorage ().length (); + } +} + +/** + * Returns the double click enabled flag. + * <p> + * The double click flag enables or disables the + * default action of the text widget when the user + * double clicks. + * </p> + * + * @return whether or not double click is enabled + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getDoubleClickEnabled () { + checkWidget (); + return doubleClick; +} + +/** + * Returns the echo character. + * <p> + * The echo character is the character that is + * displayed when the user enters text or the + * text is changed by the programmer. + * </p> + * + * @return the echo character + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setEchoChar + */ +public char getEchoChar () { + checkWidget (); + return echoCharacter; +} + +/** + * Returns the editable state. + * + * @return whether or not the receiver is editable + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getEditable () { + checkWidget (); + return (style & SWT.READ_ONLY) == 0; +} + +char [] getEditText () { + NSString str = null; + if ((style & SWT.SINGLE) != 0) { + str = new NSTextFieldCell (((NSTextField) view).cell ()).title (); + } else { + str = ((NSTextView)view).textStorage().string(); + } + + int length = (int)/*64*/str.length (); + char [] buffer = new char [length]; + if (hiddenText != null) { + hiddenText.getChars (0, length, buffer, 0); + } else { + NSRange range = new NSRange (); + range.length = length; + str.getCharacters (buffer, range); + } + return buffer; +} + +char [] getEditText (int start, int end) { + NSString str = null; + if ((style & SWT.SINGLE) != 0) { + str = new NSTextFieldCell (((NSTextField) view).cell ()).title (); + } else { + str = ((NSTextView)view).textStorage().string(); + } + + int length = (int)/*64*/str.length (); + end = Math.min (end, length - 1); + if (start > end) return new char [0]; + start = Math.max (0, start); + NSRange range = new NSRange (); + range.location = start; + range.length = Math.max (0, end - start + 1); + char [] buffer = new char [(int)/*64*/range.length]; + if (hiddenText != null) { + hiddenText.getChars ((int)/*64*/range.location, (int)/*64*/(range.location + range.length), buffer, 0); + } else { + str.getCharacters (buffer, range); + } + return buffer; +} + +/** + * Returns the number of lines. + * + * @return the number of lines in the widget + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getLineCount () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) return 1; + NSTextStorage storage = ((NSTextView) view).textStorage (); + int count = (int)/*64*/storage.paragraphs ().count (); + NSString string = storage.string(); + int /*long*/ length = string.length(), c; + if (length == 0 || (c = string.characterAtIndex(length - 1)) == '\n' || c == '\r') { + count++; + } + return count; +} + +/** + * Returns the line delimiter. + * + * @return a string that is the line delimiter + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #DELIMITER + */ +public String getLineDelimiter () { + checkWidget (); + return DELIMITER; +} + +/** + * Returns the height of a line. + * + * @return the height of a row of text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getLineHeight () { + checkWidget (); + Font font = this.font != null ? this.font : defaultFont(); + if ((style & SWT.SINGLE) != 0) { + NSDictionary dict = NSDictionary.dictionaryWithObject(font.handle, OS.NSFontAttributeName); + NSString str = NSString.stringWith(" "); + NSAttributedString attribStr = ((NSAttributedString)new NSAttributedString().alloc()).initWithString(str, dict); + NSSize size = attribStr.size(); + attribStr.release(); + return (int) size.height; + } else { + NSTextView widget = (NSTextView)view; + return (int)Math.ceil(widget.layoutManager().defaultLineHeightForFont(font.handle)); + } +} + +/** + * Returns the orientation of the receiver, which will be one of the + * constants <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>. + * + * @return the orientation style + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.1.2 + */ +public int getOrientation () { + checkWidget (); + return style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT); +} + +/** + * Returns the widget message. The message text is displayed + * as a hint for the user, indicating the purpose of the field. + * <p> + * Typically this is used in conjunction with <code>SWT.SEARCH</code>. + * </p> + * + * @return the widget message + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.3 + */ +public String getMessage () { + checkWidget (); + return message; +} + +int /*long*/ getPosition (int /*long*/ x, int /*long*/ y) { +// checkWidget (); + if ((style & SWT.MULTI) != 0) { + NSTextView widget = (NSTextView) view; + NSPoint viewLocation = new NSPoint(); + viewLocation.x = x; + viewLocation.y = y; + return widget.characterIndexForInsertionAtPoint(viewLocation); + } else { + //TODO + return 0; + } +} + +/** + * Returns a <code>Point</code> whose x coordinate is the + * character position representing the start of the selected + * text, and whose y coordinate is the character position + * representing the end of the selection. An "empty" selection + * is indicated by the x and y coordinates having the same value. + * <p> + * Indexing is zero based. The range of a selection is from + * 0..N where N is the number of characters in the widget. + * </p> + * + * @return a point representing the selection start and end + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Point getSelection () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) { + if (selectionRange == null) { + NSString str = new NSTextFieldCell (((NSTextField) view).cell ()).title (); + return new Point((int)/*64*/str.length (), (int)/*64*/str.length ()); + } + return new Point ((int)/*64*/selectionRange.location, (int)/*64*/(selectionRange.location + selectionRange.length)); + } else { + NSTextView widget = (NSTextView) view; + NSRange range = widget.selectedRange (); + return new Point ((int)/*64*/range.location, (int)/*64*/(range.location + range.length)); + } +} + +/** + * Returns the number of selected characters. + * + * @return the number of selected characters. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getSelectionCount () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) { + return selectionRange != null ? (int)/*64*/selectionRange.length : 0; + } else { + NSTextView widget = (NSTextView) view; + NSRange range = widget.selectedRange (); + return (int)/*64*/range.length; + } +} + +/** + * Gets the selected text, or an empty string if there is no current selection. + * + * @return the selected text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getSelectionText () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) { + Point selection = getSelection (); + if (selection.x == selection.y) return ""; + return new String (getEditText (selection.x, selection.y - 1)); + } else { + NSTextView widget = (NSTextView) view; + NSRange range = widget.selectedRange (); + NSString str = widget.textStorage ().string (); + char[] buffer = new char [(int)/*64*/range.length]; + str.getCharacters (buffer, range); + return new String (buffer); + } +} + +/** + * Returns the number of tabs. + * <p> + * Tab stop spacing is specified in terms of the + * space (' ') character. The width of a single + * tab stop is the pixel width of the spaces. + * </p> + * + * @return the number of tab characters + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getTabs () { + checkWidget (); + return tabs; +} + +/** + * Returns the widget text. + * <p> + * The text for a text widget is the characters in the widget, or + * an empty string if this has never been set. + * </p> + * + * @return the widget text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getText () { + checkWidget (); + NSString str; + if ((style & SWT.SINGLE) != 0) { + return new String (getEditText ()); + } else { + str = ((NSTextView)view).textStorage ().string (); + } + return str.getString(); +} + +/** + * Returns a range of text. Returns an empty string if the + * start of the range is greater than the end. + * <p> + * Indexing is zero based. The range of + * a selection is from 0..N-1 where N is + * the number of characters in the widget. + * </p> + * + * @param start the start of the range + * @param end the end of the range + * @return the range of text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getText (int start, int end) { + checkWidget (); + if (!(start <= end && 0 <= end)) return ""; //$NON-NLS-1$ + if ((style & SWT.SINGLE) != 0) { + return new String (getEditText (start, end)); + } + NSTextStorage storage = ((NSTextView) view).textStorage (); + end = Math.min (end, (int)/*64*/storage.length () - 1); + if (start > end) return ""; //$NON-NLS-1$ + start = Math.max (0, start); + NSRange range = new NSRange (); + range.location = start; + range.length = end - start + 1; + NSAttributedString substring = storage.attributedSubstringFromRange (range); + NSString string = substring.string (); + return string.getString(); +} + +/** + * Returns the maximum number of characters that the receiver is capable of holding. + * <p> + * If this has not been changed by <code>setTextLimit()</code>, + * it will be the constant <code>Text.LIMIT</code>. + * </p> + * + * @return the text limit + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #LIMIT + */ +public int getTextLimit () { + checkWidget (); + return textLimit; +} + +/** + * Returns the zero-relative index of the line which is currently + * at the top of the receiver. + * <p> + * This index can change when lines are scrolled or new lines are added or removed. + * </p> + * + * @return the index of the top line + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getTopIndex () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) return 0; + return getTopPixel () / getLineHeight (); +} + +/** + * Returns the top pixel. + * <p> + * The top pixel is the pixel position of the line + * that is currently at the top of the widget. On + * some platforms, a text widget can be scrolled by + * pixels instead of lines so that a partial line + * is displayed at the top of the widget. + * </p><p> + * The top pixel changes when the widget is scrolled. + * The top pixel does not include the widget trimming. + * </p> + * + * @return the pixel position of the top line + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getTopPixel () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) return 0; + return (int)scrollView.contentView().bounds().y; +} + +/** + * Inserts a string. + * <p> + * The old selection is replaced with the new text. + * </p> + * + * @param string the string + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is <code>null</code></li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void insert (String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if (hooks (SWT.Verify) || filters (SWT.Verify)) { + Point selection = getSelection (); + string = verifyText (string, selection.x, selection.y, null); + if (string == null) return; + } + if ((style & SWT.SINGLE) != 0) { + insertEditText (string); + } else { + NSString str = NSString.stringWith (string); + NSTextView widget = (NSTextView) view; + NSRange range = widget.selectedRange (); + widget.textStorage ().replaceCharactersInRange (range, str); + } + if (string.length () != 0) sendEvent (SWT.Modify); +} + +void insertEditText (String string) { + int length = string.length (); + Point selection = getSelection (); + if (hasFocus () && hiddenText == null) { + if (textLimit != LIMIT) { + int charCount = getCharCount(); + if (charCount - (selection.y - selection.x) + length > textLimit) { + length = textLimit - charCount + (selection.y - selection.x); + } + } + char [] buffer = new char [length]; + string.getChars (0, buffer.length, buffer, 0); + NSString nsstring = NSString.stringWithCharacters (buffer, buffer.length); + NSText fieldEditor = ((NSTextField) view).currentEditor (); + if (fieldEditor != null) fieldEditor.replaceCharactersInRange (fieldEditor.selectedRange (), nsstring); + selectionRange = null; + } else { + String oldText = getText (); + if (textLimit != LIMIT) { + int charCount = oldText.length (); + if (charCount - (selection.y - selection.x) + length > textLimit) { + string = string.substring(0, textLimit - charCount + (selection.y - selection.x)); + } + } + String newText = oldText.substring (0, selection.x) + string + oldText.substring (selection.y); + setEditText (newText); + setSelection (selection.x + string.length ()); + } +} + +boolean isEventView (int /*long*/ id) { + if ((style & SWT.MULTI) != 0) return super.isEventView (id); + return true; +} + +/** + * Pastes text from clipboard. + * <p> + * The selected text is deleted from the widget + * and new text inserted from the clipboard. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void paste () { + checkWidget (); + if ((style & SWT.READ_ONLY) != 0) return; + boolean paste = true; + String oldText = null; + if (hooks (SWT.Verify) || filters (SWT.Verify)) { + oldText = getClipboardText (); + if (oldText != null) { + Point selection = getSelection (); + String newText = verifyText (oldText, selection.x, selection.y, null); + if (newText == null) return; + if (!newText.equals (oldText)) { + if ((style & SWT.SINGLE) != 0) { + insertEditText (newText); + } else { + NSTextView textView = (NSTextView) view; + textView.replaceCharactersInRange (textView.selectedRange (), NSString.stringWith (newText)); + } + paste = false; + } + } + } + if (paste) { + if ((style & SWT.SINGLE) != 0) { + if (oldText == null) oldText = getClipboardText (); + if (oldText == null) return; + insertEditText (oldText); + } else { + //TODO check text limit + ((NSTextView) view).paste (null); + } + } + sendEvent (SWT.Modify); +} + +void register() { + super.register(); + + if ((style & SWT.SINGLE) != 0) { + display.addWidget(((NSControl)view).cell(), this); + } +} + +void releaseWidget () { + super.releaseWidget (); + if ((style & SWT.SINGLE) != 0) ((NSControl)view).abortEditing(); + hiddenText = message = null; + selectionRange = null; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the receiver's text is modified. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ModifyListener + * @see #addModifyListener + */ +public void removeModifyListener (ModifyListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Modify, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is selected by the user. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener(SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Selection, listener); + eventTable.unhook (SWT.DefaultSelection,listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is verified. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see VerifyListener + * @see #addVerifyListener + */ +public void removeVerifyListener (VerifyListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Verify, listener); +} + +/** + * Selects all the text in the receiver. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void selectAll () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) { + setSelection (0, getCharCount ()); + } else { + ((NSTextView) view).selectAll (null); + } +} + +boolean sendKeyEvent (NSEvent nsEvent, int type) { + boolean result = super.sendKeyEvent (nsEvent, type); + if (!result) return result; + if (type != SWT.KeyDown) return result; + int stateMask = 0; + int /*long*/ modifierFlags = nsEvent.modifierFlags(); + if ((modifierFlags & OS.NSAlternateKeyMask) != 0) stateMask |= SWT.ALT; + if ((modifierFlags & OS.NSShiftKeyMask) != 0) stateMask |= SWT.SHIFT; + if ((modifierFlags & OS.NSControlKeyMask) != 0) stateMask |= SWT.CONTROL; + if ((modifierFlags & OS.NSCommandKeyMask) != 0) stateMask |= SWT.COMMAND; + if (stateMask == SWT.COMMAND) { + short keyCode = nsEvent.keyCode (); + switch (keyCode) { + case 7: /* X */ + cut (); + return false; + case 8: /* C */ + copy (); + return false; + case 9: /* V */ + paste (); + return false; + } + } + if ((style & SWT.SINGLE) != 0) { + short keyCode = nsEvent.keyCode (); + switch (keyCode) { + case 76: /* KP Enter */ + case 36: /* Return */ + postEvent (SWT.DefaultSelection); + } + } + return result; +} + +void sendSearchSelection () { + if (targetSearch != null) { + ((NSSearchField)view).sendAction(actionSearch, targetSearch); + } + Event event = new Event (); + event.detail = SWT.ICON_SEARCH; + postEvent (SWT.DefaultSelection, event); +} + +void sendCancelSelection () { + if (targetCancel != null) { + ((NSSearchField)view).sendAction(actionCancel, targetCancel); + } + Event event = new Event (); + event.detail = SWT.ICON_CANCEL; + postEvent (SWT.DefaultSelection, event); +} + +void updateBackground () { + NSColor nsColor = null; + if (backgroundImage != null) { + nsColor = NSColor.colorWithPatternImage(backgroundImage.handle); + } else if (background != null) { + nsColor = NSColor.colorWithDeviceRed(background[0], background[1], background[2], background[3]); + } else { + nsColor = NSColor.textBackgroundColor (); + } + if ((style & SWT.SINGLE) != 0) { + ((NSTextField) view).setBackgroundColor (nsColor); + } else { + ((NSTextView) view).setBackgroundColor (nsColor); + } +} + +/** + * Sets the double click enabled flag. + * <p> + * The double click flag enables or disables the + * default action of the text widget when the user + * double clicks. + * </p><p> + * Note: This operation is a hint and is not supported on + * platforms that do not have this concept. + * </p> + * + * @param doubleClick the new double click flag + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setDoubleClickEnabled (boolean doubleClick) { + checkWidget (); + this.doubleClick = doubleClick; +} + +/** + * Sets the echo character. + * <p> + * The echo character is the character that is + * displayed when the user enters text or the + * text is changed by the programmer. Setting + * the echo character to '\0' clears the echo + * character and redraws the original text. + * If for any reason the echo character is invalid, + * or if the platform does not allow modification + * of the echo character, the default echo character + * for the platform is used. + * </p> + * + * @param echo the new echo character + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setEchoChar (char echo) { + checkWidget (); + if ((style & SWT.MULTI) != 0) return; + if ((style & SWT.PASSWORD) == 0) { + Point selection = getSelection (); + String text = getText (); + echoCharacter = echo; + setEditText (text); + setSelection (selection); + } + echoCharacter = echo; +} + +/** + * Sets the editable state. + * + * @param editable the new editable state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setEditable (boolean editable) { + checkWidget (); + if (editable) { + style &= ~SWT.READ_ONLY; + } else { + style |= SWT.READ_ONLY; + } + if ((style & SWT.SINGLE) != 0) { + ((NSTextField) view).setEditable (editable); + } else { + ((NSTextView) view).setEditable (editable); + } +} + +void setEditText (String string) { + char [] buffer; + if ((style & SWT.PASSWORD) == 0 && echoCharacter != '\0') { + hiddenText = string; + buffer = new char [Math.min(hiddenText.length (), textLimit)]; + for (int i = 0; i < buffer.length; i++) buffer [i] = echoCharacter; + } else { + hiddenText = null; + buffer = new char [Math.min(string.length (), textLimit)]; + string.getChars (0, buffer.length, buffer, 0); + } + NSString nsstring = NSString.stringWithCharacters (buffer, buffer.length); + new NSCell (((NSTextField) view).cell ()).setTitle (nsstring); + selectionRange = null; +} + +void setFont(NSFont font) { + if ((style & SWT.MULTI) != 0) { + ((NSTextView) view).setFont (font); + return; + } + super.setFont (font); +} + +void setForeground (float /*double*/ [] color) { + NSColor nsColor; + if (color == null) { + nsColor = NSColor.textColor (); + } else { + nsColor = NSColor.colorWithDeviceRed (color [0], color [1], color [2], 1); + } + if ((style & SWT.SINGLE) != 0) { + ((NSTextField) view).setTextColor (nsColor); + } else { + ((NSTextView) view).setTextColor (nsColor); + } +} + +/** + * Sets the orientation of the receiver, which must be one + * of the constants <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>. + * <p> + * Note: This operation is a hint and is not supported on + * platforms that do not have this concept. + * </p> + * + * @param orientation new orientation style + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.1.2 + */ +public void setOrientation (int orientation) { + checkWidget (); +} + +/** + * Sets the widget message. The message text is displayed + * as a hint for the user, indicating the purpose of the field. + * <p> + * Typically this is used in conjunction with <code>SWT.SEARCH</code>. + * </p> + * + * @param message the new message + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the message is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.3 + */ +public void setMessage (String message) { + checkWidget (); + if (message == null) error (SWT.ERROR_NULL_ARGUMENT); + this.message = message; + if ((style & SWT.SINGLE) != 0) { + NSString str = NSString.stringWith (message); + NSTextFieldCell cell = new NSTextFieldCell (((NSTextField) view).cell ()); + cell.setPlaceholderString (str); + } +} + +/** + * Sets the selection. + * <p> + * Indexing is zero based. The range of + * a selection is from 0..N where N is + * the number of characters in the widget. + * </p><p> + * Text selections are specified in terms of + * caret positions. In a text widget that + * contains N characters, there are N+1 caret + * positions, ranging from 0..N. This differs + * from other functions that address character + * position such as getText () that use the + * regular array indexing rules. + * </p> + * + * @param start new caret position + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSelection (int start) { + checkWidget (); + setSelection (start, start); +} + +/** + * Sets the selection to the range specified + * by the given start and end indices. + * <p> + * Indexing is zero based. The range of + * a selection is from 0..N where N is + * the number of characters in the widget. + * </p><p> + * Text selections are specified in terms of + * caret positions. In a text widget that + * contains N characters, there are N+1 caret + * positions, ranging from 0..N. This differs + * from other functions that address character + * position such as getText () that use the + * usual array indexing rules. + * </p> + * + * @param start the start of the range + * @param end the end of the range + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSelection (int start, int end) { + checkWidget (); + if ((style & SWT.SINGLE) != 0) { + NSString str = new NSCell (((NSTextField) view).cell ()).title (); + int length = (int)/*64*/str.length (); + int selStart = Math.min (Math.max (Math.min (start, end), 0), length); + int selEnd = Math.min (Math.max (Math.max (start, end), 0), length); + selectionRange = new NSRange (); + selectionRange.location = selStart; + selectionRange.length = selEnd - selStart; + NSText fieldEditor = ((NSControl)view).currentEditor(); + if (fieldEditor != null) { + fieldEditor.setSelectedRange (selectionRange); + } + } else { + int length = (int)/*64*/((NSTextView) view).textStorage ().length (); + int selStart = Math.min (Math.max (Math.min (start, end), 0), length); + int selEnd = Math.min (Math.max (Math.max (start, end), 0), length); + NSRange range = new NSRange (); + range.location = selStart; + range.length = selEnd - selStart; + ((NSTextView) view).setSelectedRange (range); + } +} + +/** + * Sets the selection to the range specified + * by the given point, where the x coordinate + * represents the start index and the y coordinate + * represents the end index. + * <p> + * Indexing is zero based. The range of + * a selection is from 0..N where N is + * the number of characters in the widget. + * </p><p> + * Text selections are specified in terms of + * caret positions. In a text widget that + * contains N characters, there are N+1 caret + * positions, ranging from 0..N. This differs + * from other functions that address character + * position such as getText () that use the + * usual array indexing rules. + * </p> + * + * @param selection the point + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the point is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSelection (Point selection) { + checkWidget (); + if (selection == null) error (SWT.ERROR_NULL_ARGUMENT); + setSelection (selection.x, selection.y); +} + +/** + * Sets the number of tabs. + * <p> + * Tab stop spacing is specified in terms of the + * space (' ') character. The width of a single + * tab stop is the pixel width of the spaces. + * </p> + * + * @param tabs the number of tabs + * + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setTabs (int tabs) { + checkWidget (); + if (this.tabs == tabs) return; + this.tabs = tabs; + if ((style & SWT.SINGLE) != 0) return; + float /*double*/ size = textExtent("s").width * tabs; + NSTextView widget = (NSTextView)view; + NSParagraphStyle defaultStyle = widget.defaultParagraphStyle(); + NSMutableParagraphStyle paragraphStyle = new NSMutableParagraphStyle(defaultStyle.mutableCopy()); + paragraphStyle.setTabStops(NSArray.array()); + NSTextTab tab = (NSTextTab)new NSTextTab().alloc(); + tab = tab.initWithType(OS.NSLeftTabStopType, size); + paragraphStyle.addTabStop(tab); + tab.release(); + paragraphStyle.setDefaultTabInterval(size); + widget.setDefaultParagraphStyle(paragraphStyle); + paragraphStyle.release(); +} + +/** + * Sets the contents of the receiver to the given string. If the receiver has style + * SINGLE and the argument contains multiple lines of text, the result of this + * operation is undefined and may vary from platform to platform. + * + * @param string the new text + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the string is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setText (String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if (hooks (SWT.Verify) || filters (SWT.Verify)) { + string = verifyText (string, 0, getCharCount (), null); + if (string == null) return; + } + if ((style & SWT.SINGLE) != 0) { + setEditText (string); + } else { + NSTextView widget = (NSTextView)view; + NSString str = NSString.stringWith (string); + widget.setString (str); + widget.setSelectedRange(new NSRange()); + } + sendEvent (SWT.Modify); +} + +/** + * Sets the maximum number of characters that the receiver + * is capable of holding to be the argument. + * <p> + * Instead of trying to set the text limit to zero, consider + * creating a read-only text widget. + * </p><p> + * To reset this value to the default, use <code>setTextLimit(Text.LIMIT)</code>. + * Specifying a limit value larger than <code>Text.LIMIT</code> sets the + * receiver's limit to <code>Text.LIMIT</code>. + * </p> + * + * @param limit new text limit + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_CANNOT_BE_ZERO - if the limit is zero</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #LIMIT + */ +public void setTextLimit (int limit) { + checkWidget (); + if (limit == 0) error (SWT.ERROR_CANNOT_BE_ZERO); + textLimit = limit; +} + +/** + * Sets the zero-relative index of the line which is currently + * at the top of the receiver. This index can change when lines + * are scrolled or new lines are added and removed. + * + * @param index the index of the top item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setTopIndex (int index) { + checkWidget (); + if ((style & SWT.SINGLE) != 0) return; + int row = Math.max(0, Math.min(index, getLineCount() - 1)); + NSPoint pt = new NSPoint(); + pt.x = scrollView.contentView().bounds().x; + pt.y = getLineHeight() * row; + view.scrollPoint(pt); +} + +boolean shouldChangeTextInRange_replacementString(int /*long*/ id, int /*long*/ sel, int /*long*/ affectedCharRange, int /*long*/ replacementString) { + NSRange range = new NSRange(); + OS.memmove(range, affectedCharRange, NSRange.sizeof); + boolean result = callSuperBoolean(id, sel, range, replacementString); + if (!hooks(SWT.Verify) && echoCharacter =='\0') return result; + String text = new NSString(replacementString).getString(); + String newText = text; + if (hooks (SWT.Verify)) { + NSEvent currentEvent = display.application.currentEvent(); + int /*long*/ type = currentEvent.type(); + if (type != OS.NSKeyDown && type != OS.NSKeyUp) currentEvent = null; + newText = verifyText(text, (int)/*64*/range.location, (int)/*64*/(range.location+range.length), currentEvent); + } + if (newText == null) return false; + if ((style & SWT.SINGLE) != 0) { + if (text != newText || echoCharacter != '\0') { + //handle backspace and delete + if (range.length == 1) { + NSText editor = new NSText(id); + editor.setSelectedRange (range); + } + insertEditText(newText); + result = false; + } + } else { + if (text != newText) { + NSTextView widget = (NSTextView) view; + Point selection = getSelection(); + NSRange selRange = new NSRange(); + selRange.location = selection.x; + selRange.length = selection.x + selection.y; + widget.textStorage ().replaceCharactersInRange (selRange, NSString.stringWith(newText)); + result = false; + } + } + if (!result) sendEvent (SWT.Modify); + return result; +} + +/** + * Shows the selection. + * <p> + * If the selection is already showing + * in the receiver, this method simply returns. Otherwise, + * lines are scrolled until the selection is visible. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void showSelection () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) { + setSelection (getSelection ()); + } else { + NSTextView widget = (NSTextView) view; + widget.scrollRangeToVisible (widget.selectedRange ()); + } +} + +void textViewDidChangeSelection(int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { + NSNotification notification = new NSNotification (aNotification); + NSText editor = new NSText (notification.object ().id); + selectionRange = editor.selectedRange (); +} + +void textDidChange (int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { + if ((style & SWT.SINGLE) != 0) super.textDidChange (id, sel, aNotification); + postEvent (SWT.Modify); +} + +NSRange textView_willChangeSelectionFromCharacterRange_toCharacterRange (int /*long*/ id, int /*long*/ sel, int /*long*/ aTextView, int /*long*/ oldSelectedCharRange, int /*long*/ newSelectedCharRange) { + /* + * If the selection is changing as a result of the receiver getting focus + * then return the receiver's last selection range, otherwise the full + * text will be automatically selected. + */ + if (receivingFocus && selectionRange != null) return selectionRange; + + /* allow the selection change to proceed */ + NSRange result = new NSRange (); + OS.memmove(result, newSelectedCharRange, NSRange.sizeof); + return result; +} + +int traversalCode (int key, NSEvent theEvent) { + int bits = super.traversalCode (key, theEvent); + if ((style & SWT.READ_ONLY) != 0) return bits; + if ((style & SWT.MULTI) != 0) { + bits &= ~SWT.TRAVERSE_RETURN; + if (key == 48 /* Tab */ && theEvent != null) { + int /*long*/ modifiers = theEvent.modifierFlags (); + boolean next = (modifiers & OS.NSShiftKeyMask) == 0; + if (next && (modifiers & OS.NSControlKeyMask) == 0) { + bits &= ~(SWT.TRAVERSE_TAB_NEXT | SWT.TRAVERSE_TAB_PREVIOUS); + } + } + } + return bits; +} + +void updateCursorRects (boolean enabled) { + super.updateCursorRects (enabled); + if (scrollView == null) return; + NSClipView contentView = scrollView.contentView (); + contentView.setDocumentCursor (enabled ? NSCursor.IBeamCursor () : null); +} + +String verifyText (String string, int start, int end, NSEvent keyEvent) { + Event event = new Event (); + if (keyEvent != null) setKeyState(event, SWT.MouseDown, keyEvent); + event.text = string; + event.start = start; + event.end = end; + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the verify + * event. If this happens, answer null to cancel + * the operation. + */ + sendEvent (SWT.Verify, event); + if (!event.doit || isDisposed ()) return null; + return event.text; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ToolBar.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ToolBar.java new file mode 100755 index 0000000000..77d70b71de --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ToolBar.java @@ -0,0 +1,536 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.accessibility.*; +import org.eclipse.swt.graphics.*; + +/** + * Instances of this class support the layout of selectable + * tool bar items. + * <p> + * The item children that may be added to instances of this class + * must be of type <code>ToolItem</code>. + * </p><p> + * Note that although this class is a subclass of <code>Composite</code>, + * it does not make sense to add <code>Control</code> children to it, + * or set a layout on it. + * </p><p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>FLAT, WRAP, RIGHT, HORIZONTAL, VERTICAL, SHADOW_OUT</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#toolbar">ToolBar, ToolItem snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class ToolBar extends Composite { + int itemCount; + ToolItem [] items; + NSArray accessibilityAttributes = null; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#FLAT + * @see SWT#WRAP + * @see SWT#RIGHT + * @see SWT#HORIZONTAL + * @see SWT#SHADOW_OUT + * @see SWT#VERTICAL + * @see Widget#checkSubclass() + * @see Widget#getStyle() + */ +public ToolBar (Composite parent, int style) { + super (parent, checkStyle (style)); + + /* + * Ensure that either of HORIZONTAL or VERTICAL is set. + * NOTE: HORIZONTAL and VERTICAL have the same values + * as H_SCROLL and V_SCROLL so it is necessary to first + * clear these bits to avoid scroll bars and then reset + * the bits using the original style supplied by the + * programmer. + */ + if ((style & SWT.VERTICAL) != 0) { + this.style |= SWT.VERTICAL; + } else { + this.style |= SWT.HORIZONTAL; + } +} + +int /*long*/ accessibilityAttributeNames(int /*long*/ id, int /*long*/ sel) { + + if (accessibilityAttributes == null) { + NSMutableArray ourAttributes = NSMutableArray.arrayWithCapacity(10); + ourAttributes.addObject(OS.NSAccessibilityRoleAttribute); + ourAttributes.addObject(OS.NSAccessibilityRoleDescriptionAttribute); + ourAttributes.addObject(OS.NSAccessibilityParentAttribute); + ourAttributes.addObject(OS.NSAccessibilityPositionAttribute); + ourAttributes.addObject(OS.NSAccessibilitySizeAttribute); + ourAttributes.addObject(OS.NSAccessibilityWindowAttribute); + ourAttributes.addObject(OS.NSAccessibilityTopLevelUIElementAttribute); + ourAttributes.addObject(OS.NSAccessibilityHelpAttribute); + ourAttributes.addObject(OS.NSAccessibilityEnabledAttribute); + ourAttributes.addObject(OS.NSAccessibilityFocusedAttribute); + ourAttributes.addObject(OS.NSAccessibilityChildrenAttribute); + + if (accessible != null) { + // See if the accessible will override or augment the standard list. + // Help, title, and description can be overridden. + NSMutableArray extraAttributes = NSMutableArray.arrayWithCapacity(3); + extraAttributes.addObject(OS.NSAccessibilityHelpAttribute); + extraAttributes.addObject(OS.NSAccessibilityDescriptionAttribute); + extraAttributes.addObject(OS.NSAccessibilityTitleAttribute); + + for (int i = (int)/*64*/extraAttributes.count() - 1; i >= 0; i--) { + NSString attribute = new NSString(extraAttributes.objectAtIndex(i).id); + if (accessible.internal_accessibilityAttributeValue(attribute, ACC.CHILDID_SELF) != null) { + ourAttributes.addObject(extraAttributes.objectAtIndex(i)); + } + } + } + + accessibilityAttributes = ourAttributes; + accessibilityAttributes.retain(); + } + + return accessibilityAttributes.id; +} + +int /*long*/ accessibilityAttributeValue (int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + NSString nsAttributeName = new NSString(arg0); + + if (accessible != null) { + id returnObject = accessible.internal_accessibilityAttributeValue(nsAttributeName, ACC.CHILDID_SELF); + if (returnObject != null) return returnObject.id; + } + + if (nsAttributeName.isEqualToString (OS.NSAccessibilityRoleAttribute) || nsAttributeName.isEqualToString (OS.NSAccessibilityRoleDescriptionAttribute)) { + NSString role = OS.NSAccessibilityToolbarRole; + + if (nsAttributeName.isEqualToString (OS.NSAccessibilityRoleAttribute)) + return role.id; + else { + int /*long*/ roleDescription = OS.NSAccessibilityRoleDescription(role.id, 0); + return roleDescription; + } + } else if (nsAttributeName.isEqualToString(OS.NSAccessibilityEnabledAttribute)) { + return NSNumber.numberWithBool(isEnabled()).id; + } else if (nsAttributeName.isEqualToString(OS.NSAccessibilityFocusedAttribute)) { + boolean focused = (view.id == view.window().firstResponder().id); + return NSNumber.numberWithBool(focused).id; + } + + return super.accessibilityAttributeValue(id, sel, arg0); +} + +boolean accessibilityIsIgnored(int /*long*/ id, int /*long*/ sel) { + // Toolbars aren't ignored. + return false; +} + +static int checkStyle (int style) { + /* + * Even though it is legal to create this widget + * with scroll bars, they serve no useful purpose + * because they do not automatically scroll the + * widget's client area. The fix is to clear + * the SWT style. + */ + return style & ~(SWT.H_SCROLL | SWT.V_SCROLL); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget(); + int width = wHint, height = hHint; + if (wHint == SWT.DEFAULT) width = 0x7FFFFFFF; + if (hHint == SWT.DEFAULT) height = 0x7FFFFFFF; + int [] result = layout (width, height, false); + Point extent = new Point (result [1], result [2]); + if (wHint != SWT.DEFAULT) extent.x = wHint; + if (hHint != SWT.DEFAULT) extent.y = hHint; + return extent; +} + +void createHandle () { + state |= THEME_BACKGROUND; + NSView widget = (NSView)new SWTView().alloc(); + widget.init(); +// widget.setDrawsBackground(false); + view = widget; +} + +void createItem (ToolItem item, int index) { + if (!(0 <= index && index <= itemCount)) error (SWT.ERROR_INVALID_RANGE); + if (itemCount == items.length) { + ToolItem [] newItems = new ToolItem [itemCount + 4]; + System.arraycopy (items, 0, newItems, 0, items.length); + items = newItems; + } + item.createWidget(); + view.addSubview(item.view); + System.arraycopy (items, index, items, index + 1, itemCount++ - index); + items [index] = item; + relayout (); +} + +void createWidget () { + super.createWidget (); + items = new ToolItem [4]; + itemCount = 0; +} + +void destroyItem (ToolItem item) { + int index = 0; + while (index < itemCount) { + if (items [index] == item) break; + index++; + } + if (index == itemCount) return; + System.arraycopy (items, index + 1, items, index, --itemCount - index); + items [itemCount] = null; + item.view.removeFromSuperview(); + relayout (); +} + +void drawBackground (int /*long*/ id, NSGraphicsContext context, NSRect rect) { + if (id != view.id) return; + if (background != null) { + fillBackground (view, context, rect, -1); + } +} + +void enableWidget(boolean enabled) { + super.enableWidget(enabled); + for (int i = 0; i < itemCount; i++) { + ToolItem item = items[i]; + if (item != null) { + item.enableWidget(enabled); + } + } +} + +Widget findTooltip (NSPoint pt) { + pt = view.convertPoint_fromView_ (pt, null); + for (int i = 0; i < itemCount; i++) { + ToolItem item = items [i]; + if (OS.NSPointInRect(pt, item.view.frame())) return item; + } + return super.findTooltip (pt); +} + +/** + * Returns the item at the given, zero-relative index in the + * receiver. Throws an exception if the index is out of range. + * + * @param index the index of the item to return + * @return the item at the given index + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public ToolItem getItem (int index) { + checkWidget(); + if (0 <= index && index < itemCount) return items [index]; + error (SWT.ERROR_INVALID_RANGE); + return null; +} + +/** + * Returns the item at the given point in the receiver + * or null if no such item exists. The point is in the + * coordinate system of the receiver. + * + * @param point the point used to locate the item + * @return the item at the given point + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the point is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public ToolItem getItem (Point pt) { + checkWidget(); + if (pt == null) error (SWT.ERROR_NULL_ARGUMENT); + for (int i=0; i<itemCount; i++) { + Rectangle rect = items [i].getBounds (); + if (rect.contains (pt)) return items [i]; + } + return null; +} + +/** + * Returns the number of items contained in the receiver. + * + * @return the number of items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getItemCount () { + checkWidget(); + return itemCount; +} + +/** + * Returns an array of <code>ToolItem</code>s which are the items + * in the receiver. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its list of items, so modifying the array will + * not affect the receiver. + * </p> + * + * @return the items in the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public ToolItem [] getItems () { + checkWidget(); + ToolItem [] result = new ToolItem [itemCount]; + System.arraycopy (items, 0, result, 0, itemCount); + return result; +} + +/** + * Returns the number of rows in the receiver. When + * the receiver has the <code>WRAP</code> style, the + * number of rows can be greater than one. Otherwise, + * the number of rows is always one. + * + * @return the number of items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getRowCount () { + checkWidget(); + Rectangle rect = getClientArea (); + return layout (rect.width, rect.height, false) [0]; +} + +/** + * Searches the receiver's list starting at the first item + * (index 0) until an item is found that is equal to the + * argument, and returns the index of that item. If no item + * is found, returns -1. + * + * @param item the search item + * @return the index of the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the tool item is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the tool item has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int indexOf (ToolItem item) { + checkWidget(); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + if (item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + for (int i=0; i<itemCount; i++) { + if (items [i] == item) return i; + } + return -1; +} + +int [] layoutHorizontal (int width, int height, boolean resize) { + int xSpacing = 0, ySpacing = 2; + int marginWidth = 0, marginHeight = 0; + int x = marginWidth, y = marginHeight; + int maxX = 0, rows = 1; + boolean wrap = (style & SWT.WRAP) != 0; + int itemHeight = 0; + Point [] sizes = new Point [itemCount]; + for (int i=0; i<itemCount; i++) { + Point size = sizes [i] = items [i].computeSize (); + itemHeight = Math.max (itemHeight, size.y); + } + for (int i=0; i<itemCount; i++) { + ToolItem item = items [i]; + Point size = sizes [i]; + if (wrap && i != 0 && x + size.x > width) { + rows++; + x = marginWidth; + y += ySpacing + itemHeight; + } + if (resize) { + item.setBounds (x, y, size.x, itemHeight); + boolean visible = x + size.x <= width && y + itemHeight <= height; + item.setVisible (visible); + Control control = item.control; + if (control != null) { + int controlY = y + (itemHeight - size.y) / 2; + control.setBounds (x, controlY, size.x, itemHeight - (controlY - y)); + } + } + x += xSpacing + size.x; + maxX = Math.max (maxX, x); + } + + return new int [] {rows, maxX, y + itemHeight}; +} + +int [] layoutVertical (int width, int height, boolean resize) { + int xSpacing = 2, ySpacing = 0; + int marginWidth = 0, marginHeight = 0; + int x = marginWidth, y = marginHeight; + int maxY = 0, cols = 1; + boolean wrap = (style & SWT.WRAP) != 0; + int itemWidth = 0; + Point [] sizes = new Point [itemCount]; + for (int i=0; i<itemCount; i++) { + Point size = sizes [i] = items [i].computeSize (); + itemWidth = Math.max (itemWidth, size.x); + } + for (int i=0; i<itemCount; i++) { + ToolItem item = items [i]; + Point size = sizes [i]; + if (wrap && i != 0 && y + size.y > height) { + cols++; + x += xSpacing + itemWidth; + y = marginHeight; + } + if (resize) { + item.setBounds (x, y, itemWidth, size.y); + boolean visible = x + itemWidth <= width && y + size.y <= height; + item.setVisible (visible); + Control control = item.control; + if (control != null) { + int controlX = x + (itemWidth - size.x) / 2; + control.setBounds (controlX, y, itemWidth - (controlX - x), size.y); + } + } + y += ySpacing + size.y; + maxY = Math.max (maxY, y); + } + + return new int [] {cols, x + itemWidth, maxY}; +} + +int [] layout (int nWidth, int nHeight, boolean resize) { + if ((style & SWT.VERTICAL) != 0) { + return layoutVertical (nWidth, nHeight, resize); + } else { + return layoutHorizontal (nWidth, nHeight, resize); + } +} + +void relayout () { + if (!getDrawing()) return; + Rectangle rect = getClientArea (); + layout (rect.width, rect.height, true); +} + +void releaseChildren (boolean destroy) { + if (items != null) { + for (int i=0; i<itemCount; i++) { + ToolItem item = items [i]; + if (item != null && !item.isDisposed ()) { + item.release (false); + } + } + itemCount = 0; + items = null; + } + super.releaseChildren (destroy); +} + +void releaseHandle () { + super.releaseHandle (); + if (accessibilityAttributes != null) accessibilityAttributes.release(); + accessibilityAttributes = null; +} + +void removeControl (Control control) { + super.removeControl (control); + for (int i=0; i<itemCount; i++) { + ToolItem item = items [i]; + if (item.control == control) item.setControl (null); + } +} + +void resized () { + super.resized (); + relayout (); +} + +void setFont(NSFont font) { + for (int i = 0; i < itemCount; i++) { + ToolItem item = items[i]; + if (item.button != null) ((NSButton)item.button).setAttributedTitle(item.createString()); + } +} + +public void setRedraw (boolean redraw) { + checkWidget(); + super.setRedraw (redraw); + if (redraw && drawCount == 0) relayout(); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ToolItem.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ToolItem.java new file mode 100755 index 0000000000..21853d587f --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ToolItem.java @@ -0,0 +1,992 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class represent a selectable user interface object + * that represents a button in a tool bar. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>PUSH, CHECK, RADIO, SEPARATOR, DROP_DOWN</dd> + * <dt><b>Events:</b></dt> + * <dd>Selection</dd> + * </dl> + * <p> + * Note: Only one of the styles CHECK, PUSH, RADIO, SEPARATOR and DROP_DOWN + * may be specified. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#toolbar">ToolBar, ToolItem snippets</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class ToolItem extends Item { + NSView view; + NSButton button; + int width = DEFAULT_SEPARATOR_WIDTH; + ToolBar parent; + Image hotImage, disabledImage; + String toolTipText; + Control control; + boolean selection; + + static final int DEFAULT_WIDTH = 24; + static final int DEFAULT_HEIGHT = 22; + static final int DEFAULT_SEPARATOR_WIDTH = 6; + static final int INSET = 3; + static final int ARROW_WIDTH = 5; + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>ToolBar</code>) and a style value + * describing its behavior and appearance. The item is added + * to the end of the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#PUSH + * @see SWT#CHECK + * @see SWT#RADIO + * @see SWT#SEPARATOR + * @see SWT#DROP_DOWN + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public ToolItem (ToolBar parent, int style) { + super (parent, checkStyle (style)); + this.parent = parent; + parent.createItem (this, parent.getItemCount ()); +} + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>ToolBar</code>), a style value + * describing its behavior and appearance, and the index + * at which to place it in the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * @param index the zero-relative index to store the receiver in its parent + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#PUSH + * @see SWT#CHECK + * @see SWT#RADIO + * @see SWT#SEPARATOR + * @see SWT#DROP_DOWN + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public ToolItem (ToolBar parent, int style, int index) { + super (parent, checkStyle (style)); + this.parent = parent; + parent.createItem (this, index); +} + +int /*long*/ accessibilityAttributeValue(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + NSString nsAttributeName = new NSString(arg0); + + if (nsAttributeName.isEqualToString (OS.NSAccessibilityRoleAttribute) || nsAttributeName.isEqualToString (OS.NSAccessibilityRoleDescriptionAttribute)) { + NSString roleText = ((style & SWT.PUSH) != 0) ? OS.NSAccessibilityButtonRole + : ((style & SWT.RADIO) != 0) ? OS.NSAccessibilityRadioButtonRole + : ((style & SWT.CHECK) != 0) ? OS.NSAccessibilityCheckBoxRole + : ((style & SWT.DROP_DOWN) != 0) ? OS.NSAccessibilityMenuButtonRole + : null; // SEPARATOR + if (roleText != null) { + if (nsAttributeName.isEqualToString (OS.NSAccessibilityRoleAttribute)) { + return roleText.id; + } else { // NSAccessibilityRoleDescriptionAttribute + int /*long*/ description = OS.NSAccessibilityRoleDescription (roleText.id, 0); + return description; + } + } + } else if (nsAttributeName.isEqualToString (OS.NSAccessibilityTitleAttribute) || nsAttributeName.isEqualToString (OS.NSAccessibilityDescriptionAttribute)) { + String accessibleText = toolTipText; + if (accessibleText == null || accessibleText.equals("")) accessibleText = text; + if (!(accessibleText == null || accessibleText.equals(""))) { + return NSString.stringWith(accessibleText).id; + } else { + return NSString.stringWith("").id; + } + } else if (nsAttributeName.isEqualToString (OS.NSAccessibilityValueAttribute) && (style & (SWT.CHECK | SWT.RADIO)) != 0) { + NSNumber value = NSNumber.numberWithInt(selection ? 1 : 0); + return value.id; + } else if (nsAttributeName.isEqualToString(OS.NSAccessibilityEnabledAttribute)) { + NSNumber value = NSNumber.numberWithInt(getEnabled() ? 1 : 0); + return value.id; + } + + return super.accessibilityAttributeValue(id, sel, arg0); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the control is selected by the user, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * When <code>widgetSelected</code> is called when the mouse is over the arrow portion of a drop-down tool, + * the event object detail field contains the value <code>SWT.ARROW</code>. + * <code>widgetDefaultSelected</code> is not called. + * </p> + * + * @param listener the listener which should be notified when the control is selected by the user, + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener(listener); + addListener(SWT.Selection,typedListener); + addListener(SWT.DefaultSelection,typedListener); +} + +static int checkStyle (int style) { + return checkBits (style, SWT.PUSH, SWT.CHECK, SWT.RADIO, SWT.SEPARATOR, SWT.DROP_DOWN, 0); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +Point computeSize () { + checkWidget(); + int width = 0, height = 0; + if ((style & SWT.SEPARATOR) != 0) { + if ((parent.style & SWT.HORIZONTAL) != 0) { + width = getWidth (); + height = DEFAULT_HEIGHT; + } else { + width = DEFAULT_WIDTH; + height = getWidth (); + } + if (control != null) { + height = Math.max (height, control.getMininumHeight ()); + } + } else { + if (text.length () != 0 || image != null) { + NSButton widget = (NSButton)button; + NSSize size = widget.cell().cellSize(); + width = (int)Math.ceil(size.width); + height = (int)Math.ceil(size.height); + } else { + width = DEFAULT_WIDTH; + height = DEFAULT_HEIGHT; + } + if ((style & SWT.DROP_DOWN) != 0) { + width += ARROW_WIDTH + INSET; + } + width += INSET * 2; + height += INSET * 2; + } + return new Point (width, height); +} + +void createHandle () { + if ((style & SWT.SEPARATOR) != 0) { + NSBox widget = (NSBox)new SWTBox().alloc(); + widget.init(); + widget.setBoxType(OS.NSBoxSeparator); + widget.setBorderWidth(0); + view = widget; + } else { + NSView widget = (NSView)new SWTView().alloc(); + widget.init(); + button = (NSButton)new SWTButton().alloc(); + button.init(); + /* + * Feature in Cocoa. NSButtons without borders do not leave any margin + * between their edge and their image. The workaround is to provide a + * custom cell that displays the image in a better position. + */ + NSButtonCell cell = (NSButtonCell)new SWTButtonCell ().alloc ().init (); + button.setCell (cell); + cell.release(); + button.setBordered(false); + button.setAction(OS.sel_sendSelection); + button.setTarget(button); + Font font = parent.font != null ? parent.font : parent.defaultFont (); + button.setFont(font.handle); + button.setImagePosition(OS.NSImageOverlaps); + NSString emptyStr = NSString.stringWith(""); + button.setTitle(emptyStr); + button.setEnabled(parent.getEnabled()); + widget.addSubview(button); + view = widget; + } +} + +NSAttributedString createString() { + NSAttributedString attribStr = parent.createString(text, null, parent.foreground, SWT.CENTER, true, true); + attribStr.autorelease(); + return attribStr; +} + +void deregister () { + super.deregister (); + display.removeWidget(view); + + if (button != null) { + display.removeWidget (button); + display.removeWidget (button.cell()); + } +} + +void destroyWidget() { + parent.destroyItem(this); + super.destroyWidget(); +} + +void drawImageWithFrameInView (int /*long*/ id, int /*long*/ sel, int /*long*/ image, NSRect rect, int /*long*/ view) { + if (text.length () > 0) { + if ((parent.style & SWT.RIGHT) != 0) { + rect.x += 3; + } else { + rect.y += 3; + } + } + callSuper (id, sel, image, rect, view); +} + +void drawWidget (int /*long*/ id, NSGraphicsContext context, NSRect rect) { + if (id == view.id) { + if (getSelection ()) { + NSRect bounds = view.bounds(); + context.saveGraphicsState(); + NSColor.colorWithDeviceRed(0.1f, 0.1f, 0.1f, 0.1f).setFill(); + NSColor.colorWithDeviceRed(0.2f, 0.2f, 0.2f, 0.2f).setStroke(); + NSBezierPath.fillRect(bounds); + bounds.x += 0.5f; + bounds.y += 0.5f; + bounds.width -= 1; + bounds.height -= 1; + NSBezierPath.strokeRect(bounds); + context.restoreGraphicsState(); + } + if ((style & SWT.DROP_DOWN) != 0) { + NSRect bounds = view.bounds(); + context.saveGraphicsState(); + NSBezierPath path = NSBezierPath.bezierPath(); + NSPoint pt = new NSPoint(); + path.moveToPoint(pt); + pt.x += ARROW_WIDTH; + path.lineToPoint(pt); + pt.y += ARROW_WIDTH - 1; + pt.x -= ARROW_WIDTH / 2f; + path.lineToPoint(pt); + path.closePath(); + NSAffineTransform transform = NSAffineTransform.transform(); + transform.translateXBy((int)bounds.width - ARROW_WIDTH - INSET, (int)(bounds.height - ARROW_WIDTH / 2) / 2); + transform.concat(); + NSColor color = isEnabled() ? NSColor.blackColor() : NSColor.disabledControlTextColor(); + color.set(); + path.fill(); + context.restoreGraphicsState(); + } + } +} + +void enableWidget(boolean enabled) { + if ((style & SWT.SEPARATOR) == 0) { + ((NSButton)button).setEnabled(enabled); + } +} + +/** + * Returns a rectangle describing the receiver's size and location + * relative to its parent. + * + * @return the receiver's bounding rectangle + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Rectangle getBounds () { + checkWidget(); + NSRect rect = view.frame(); + return new Rectangle((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height); +} + +void setClipRegion (float /*double*/ x, float /*double*/ y) { + NSRect frame = view.frame(); + parent.setClipRegion(frame.x + x, frame.y + y); +} + +/** + * Returns the control that is used to fill the bounds of + * the item when the item is a <code>SEPARATOR</code>. + * + * @return the control + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Control getControl () { + checkWidget(); + return control; +} + +/** + * Returns the receiver's disabled image if it has one, or null + * if it does not. + * <p> + * The disabled image is displayed when the receiver is disabled. + * </p> + * + * @return the receiver's disabled image + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Image getDisabledImage () { + checkWidget(); + return disabledImage; +} + +boolean getDrawing () { + return parent.getDrawing (); +} + +/** + * Returns <code>true</code> if the receiver is enabled, and + * <code>false</code> otherwise. A disabled control is typically + * not selectable from the user interface and draws with an + * inactive or "grayed" look. + * + * @return the receiver's enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #isEnabled + */ +public boolean getEnabled () { + checkWidget(); + return (state & DISABLED) == 0; +} + +/** + * Returns the receiver's hot image if it has one, or null + * if it does not. + * <p> + * The hot image is displayed when the mouse enters the receiver. + * </p> + * + * @return the receiver's hot image + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Image getHotImage () { + checkWidget(); + return hotImage; +} + +/** + * Returns the receiver's parent, which must be a <code>ToolBar</code>. + * + * @return the receiver's parent + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public ToolBar getParent () { + checkWidget(); + return parent; +} + +/** + * Returns <code>true</code> if the receiver is selected, + * and false otherwise. + * <p> + * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>, + * it is selected when it is checked (which some platforms draw as a + * pushed in button). If the receiver is of any other type, this method + * returns false. + * </p> + * + * @return the selection state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getSelection () { + checkWidget(); + if ((style & (SWT.CHECK | SWT.RADIO)) == 0) return false; + return selection; +} + +/** + * Returns the receiver's tool tip text, or null if it has not been set. + * + * @return the receiver's tool tip text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getToolTipText () { + checkWidget(); + return toolTipText; +} + +/** + * Gets the width of the receiver. + * + * @return the width + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getWidth () { + checkWidget(); + return width; +} + +/** + * Returns <code>true</code> if the receiver is enabled and all + * of the receiver's ancestors are enabled, and <code>false</code> + * otherwise. A disabled control is typically not selectable from the + * user interface and draws with an inactive or "grayed" look. + * + * @return the receiver's enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #getEnabled + */ +public boolean isEnabled () { + checkWidget(); + return getEnabled () && parent.isEnabled (); +} + +boolean isDrawing () { + return getDrawing() && parent.isDrawing (); +} + +int /*long*/ menuForEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + return parent.menuForEvent (id, sel, theEvent); +} + +void mouseDown(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!parent.mouseEvent(parent.view.id, sel, theEvent, SWT.MouseDown)) return; + Display display = this.display; + display.trackingControl = parent; + super.mouseDown(id, sel, theEvent); + display.trackingControl = null; + if ((style & SWT.DROP_DOWN) != 0 && id == view.id) { + NSRect frame = view.frame(); + Event event = new Event (); + event.detail = SWT.ARROW; + event.x = (int)frame.x; + event.y = (int)(frame.y + frame.height); + postEvent (SWT.Selection, event); + } +} + +void mouseUp(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!parent.mouseEvent(parent.view.id, sel, theEvent, SWT.MouseUp)) return; + super.mouseUp(id, sel, theEvent); +} + +void mouseDragged(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!parent.mouseEvent(parent.view.id, sel, theEvent, SWT.MouseMove)) return; + super.mouseDragged(id, sel, theEvent); +} + +void rightMouseDown(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!parent.mouseEvent(parent.view.id, sel, theEvent, SWT.MouseDown)) return; + super.rightMouseDown(id, sel, theEvent); +} + +void rightMouseUp(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!parent.mouseEvent(parent.view.id, sel, theEvent, SWT.MouseUp)) return; + super.rightMouseUp(id, sel, theEvent); +} + +void rightMouseDragged(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!parent.mouseEvent(parent.view.id, sel, theEvent, SWT.MouseMove)) return; + super.rightMouseDragged(id, sel, theEvent); +} + +void otherMouseDown(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!parent.mouseEvent(parent.view.id, sel, theEvent, SWT.MouseDown)) return; + super.otherMouseDown(id, sel, theEvent); +} + +void otherMouseUp(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!parent.mouseEvent(parent.view.id, sel, theEvent, SWT.MouseUp)) return; + super.otherMouseUp(id, sel, theEvent); +} + +void otherMouseDragged(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (!parent.mouseEvent(parent.view.id, sel, theEvent, SWT.MouseMove)) return; + super.otherMouseDragged(id, sel, theEvent); +} + +void register () { + super.register (); + display.addWidget (view, this); + + if (button != null) { + display.addWidget (button, this); + display.addWidget (button.cell(), this); + } +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is selected by the user. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook(SWT.Selection, listener); + eventTable.unhook(SWT.DefaultSelection,listener); +} + +void releaseParent () { + super.releaseParent (); + setVisible (false); +} + +void releaseHandle () { + super.releaseHandle (); + if (view != null) view.release (); + if (button != null) button.release (); + view = button = null; + parent = null; +} + +void releaseWidget () { + super.releaseWidget (); + control = null; + toolTipText = null; + image = disabledImage = hotImage = null; +} + +void selectRadio () { + int index = 0; + ToolItem [] items = parent.getItems (); + while (index < items.length && items [index] != this) index++; + int i = index - 1; + while (i >= 0 && items [i].setRadioSelection (false)) --i; + int j = index + 1; + while (j < items.length && items [j].setRadioSelection (false)) j++; + setSelection (true); +} + +void sendSelection () { + if ((style & SWT.RADIO) != 0) { + if ((parent.getStyle () & SWT.NO_RADIO_GROUP) == 0) { + selectRadio (); + } + } + if ((style & SWT.CHECK) != 0) setSelection (!getSelection ()); + postEvent (SWT.Selection); +} + +void setBounds (int x, int y, int width, int height) { + NSRect rect = new NSRect(); + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + view.setFrame(rect); + if (button != null) { + rect.x = 0; + rect.y = 0; + rect.width = width; + rect.height = height; + if ((style & SWT.DROP_DOWN) != 0) rect.width -= ARROW_WIDTH + INSET; + button.setFrame(rect); + } +} + +/** + * Sets the control that is used to fill the bounds of + * the item when the item is a <code>SEPARATOR</code>. + * + * @param control the new control + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li> + * <li>ERROR_INVALID_PARENT - if the control is not in the same widget tree</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setControl (Control control) { + checkWidget(); + if (control != null) { + if (control.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT); + if (control.parent != parent) error (SWT.ERROR_INVALID_PARENT); + } + if ((style & SWT.SEPARATOR) == 0) return; + if (this.control == control) return; + NSBox widget = (NSBox)view; + if (control == null) { + widget.setBoxType(OS.NSBoxSeparator); + } else { + widget.setBoxType(OS.NSBoxCustom); + } + this.control = control; + view.setHidden(control != null); + if (control != null && !control.isDisposed ()) { + control.moveAbove (null); + } + parent.relayout (); +} + +/** + * Enables the receiver if the argument is <code>true</code>, + * and disables it otherwise. + * <p> + * A disabled control is typically + * not selectable from the user interface and draws with an + * inactive or "grayed" look. + * </p> + * + * @param enabled the new enabled state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setEnabled (boolean enabled) { + checkWidget(); + if ((state & DISABLED) == 0 && enabled) return; + if (enabled) { + state &= ~DISABLED; + } else { + state |= DISABLED; + } + enableWidget(enabled); +} + +/** + * Sets the receiver's disabled image to the argument, which may be + * null indicating that no disabled image should be displayed. + * <p> + * The disabled image is displayed when the receiver is disabled. + * </p> + * + * @param image the disabled image to display on the receiver (may be null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setDisabledImage (Image image) { + checkWidget(); + if (image != null && image.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + if ((style & SWT.SEPARATOR) != 0) return; + disabledImage = image; + updateImage (true); +} + +/** + * Sets the receiver's hot image to the argument, which may be + * null indicating that no hot image should be displayed. + * <p> + * The hot image is displayed when the mouse enters the receiver. + * </p> + * + * @param image the hot image to display on the receiver (may be null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setHotImage (Image image) { + checkWidget(); + if (image != null && image.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + if ((style & SWT.SEPARATOR) != 0) return; + hotImage = image; + updateImage (true); +} + +public void setImage (Image image) { + checkWidget(); + if (image != null && image.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + if ((style & SWT.SEPARATOR) != 0) return; + super.setImage (image); + updateImage (true); +} + +boolean setRadioSelection (boolean value) { + if ((style & SWT.RADIO) == 0) return false; + if (getSelection () != value) { + setSelection (value); + postEvent (SWT.Selection); + } + return true; +} + +/** + * Sets the selection state of the receiver. + * <p> + * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>, + * it is selected when it is checked (which some platforms draw as a + * pushed in button). + * </p> + * + * @param selected the new selection state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setSelection (boolean selected) { + checkWidget(); + if ((style & (SWT.CHECK | SWT.RADIO)) == 0) return; + this.selection = selected; + view.setNeedsDisplay(true); +} + +/** + * Sets the receiver's text. The string may include + * the mnemonic character. + * </p> + * <p> + * Mnemonics are indicated by an '&' that causes the next + * character to be the mnemonic. When the user presses a + * key sequence that matches the mnemonic, a selection + * event occurs. On most platforms, the mnemonic appears + * underlined but may be emphasised in a platform specific + * manner. The mnemonic indicator character '&' can be + * escaped by doubling it in the string, causing a single + * '&' to be displayed. + * </p> + * + * @param string the new text + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the text is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setText (String string) { + checkWidget(); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if ((style & SWT.SEPARATOR) != 0) return; + super.setText (string); + NSButton widget = (NSButton)button; + widget.setAttributedTitle(createString()); + if (text.length() != 0 && image != null) { + if ((parent.style & SWT.RIGHT) != 0) { + widget.setImagePosition(OS.NSImageLeft); + } else { + widget.setImagePosition(OS.NSImageAbove); + } + } else { + widget.setImagePosition(text.length() != 0 ? OS.NSNoImage : OS.NSImageOnly); + } + parent.relayout (); +} + +/** + * Sets the receiver's tool tip text to the argument, which + * may be null indicating that the default tool tip for the + * control will be shown. For a control that has a default + * tool tip, such as the Tree control on Windows, setting + * the tool tip text to an empty string replaces the default, + * causing no tool tip text to be shown. + * <p> + * The mnemonic indicator (character '&') is not displayed in a tool tip. + * To display a single '&' in the tool tip, the character '&' can be + * escaped by doubling it in the string. + * </p> + * + * @param string the new tool tip text (or null) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setToolTipText (String string) { + checkWidget(); + toolTipText = string; + parent.checkToolTip (this); +} + +void setVisible (boolean visible) { + if (visible) { + if ((state & HIDDEN) == 0) return; + state &= ~HIDDEN; + } else { + if ((state & HIDDEN) != 0) return; + state |= HIDDEN; + } + view.setHidden(!visible); +} + +/** + * Sets the width of the receiver, for <code>SEPARATOR</code> ToolItems. + * + * @param width the new width + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setWidth (int width) { + checkWidget(); + if ((style & SWT.SEPARATOR) == 0) return; + if (width < 0 || this.width == width) return; + this.width = width; + parent.relayout(); +} + +String tooltipText () { + return toolTipText; +} + +void updateImage (boolean layout) { + if ((style & SWT.SEPARATOR) != 0) return; + Image image = null; + if (hotImage != null) { + image = hotImage; + } else { + if (this.image != null) { + image = this.image; + } else { + image = disabledImage; + } + } + NSButton widget = (NSButton)button; + /* + * Feature in Cocoa. If the NSImage object being set into the button is + * the same NSImage object that is already there then the button does not + * redraw itself. This results in the button's image not visually updating + * if the NSImage object's content has changed since it was last set + * into the button. The workaround is to explicitly redraw the button. + */ + widget.setImage(image != null ? image.handle : null); + widget.setNeedsDisplay(true); + if (text.length() != 0 && image != null) { + if ((parent.style & SWT.RIGHT) != 0) { + widget.setImagePosition(OS.NSImageLeft); + } else { + ((NSButton)button).setImagePosition(OS.NSImageAbove); + } + } else { + widget.setImagePosition(text.length() != 0 ? OS.NSNoImage : OS.NSImageOnly); + } + parent.relayout(); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tracker.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tracker.java new file mode 100755 index 0000000000..c7a9e95ef4 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tracker.java @@ -0,0 +1,1113 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class implement rubber banding rectangles that are + * drawn onto a parent <code>Composite</code> or <code>Display</code>. + * These rectangles can be specified to respond to mouse and key events + * by either moving or resizing themselves accordingly. Trackers are + * typically used to represent window geometries in a lightweight manner. + * + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>LEFT, RIGHT, UP, DOWN, RESIZE</dd> + * <dt><b>Events:</b></dt> + * <dd>Move, Resize</dd> + * </dl> + * <p> + * Note: Rectangle move behavior is assumed unless RESIZE is specified. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#tracker">Tracker snippets</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Tracker extends Widget { + Control parent; + boolean tracking, cancelled, stippled; + Cursor clientCursor, resizeCursor; + Rectangle [] rectangles = new Rectangle [0], proportions = rectangles; + Rectangle bounds; + int cursorOrientation = SWT.NONE; + boolean inEvent = false; + NSWindow window; + int oldX, oldY; + + /* + * The following values mirror step sizes on Windows + */ + final static int STEPSIZE_SMALL = 1; + final static int STEPSIZE_LARGE = 9; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a widget which will be the parent of the new instance (cannot be null) + * @param style the style of widget to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#LEFT + * @see SWT#RIGHT + * @see SWT#UP + * @see SWT#DOWN + * @see SWT#RESIZE + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Tracker (Composite parent, int style) { + super (parent, checkStyle (style)); + this.parent = parent; +} + +/** + * Constructs a new instance of this class given the display + * to create it on and a style value describing its behavior + * and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p><p> + * Note: Currently, null can be passed in for the display argument. + * This has the effect of creating the tracker on the currently active + * display if there is one. If there is no current display, the + * tracker is created on a "default" display. <b>Passing in null as + * the display argument is not considered to be good coding style, + * and may not be supported in a future release of SWT.</b> + * </p> + * + * @param display the display to create the tracker on + * @param style the style of control to construct + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#LEFT + * @see SWT#RIGHT + * @see SWT#UP + * @see SWT#DOWN + * @see SWT#RESIZE + */ +public Tracker (Display display, int style) { + if (display == null) display = Display.getCurrent (); + if (display == null) display = Display.getDefault (); + if (!display.isValidThread ()) { + error (SWT.ERROR_THREAD_INVALID_ACCESS); + } + this.style = checkStyle (style); + this.display = display; +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the control is moved or resized, by sending + * it one of the messages defined in the <code>ControlListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ControlListener + * @see #removeControlListener + */ +public void addControlListener (ControlListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Resize, typedListener); + addListener (SWT.Move, typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when keys are pressed and released on the system keyboard, by sending + * it one of the messages defined in the <code>KeyListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see KeyListener + * @see #removeKeyListener + */ +public void addKeyListener(KeyListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener(SWT.KeyUp,typedListener); + addListener(SWT.KeyDown,typedListener); +} + +Point adjustMoveCursor () { + if (bounds == null) return null; + int newX = bounds.x + bounds.width / 2; + int newY = bounds.y; + /* + * Convert to screen coordinates if needed + */ + if (parent != null) { + Point pt = parent.toDisplay (newX, newY); + newX = pt.x; + newY = pt.y; + } + display.setCursorLocation(newX, newY); + return new Point (newX, newY); +} + +Point adjustResizeCursor (boolean movePointer) { + if (bounds == null) return null; + int newX, newY; + + if ((cursorOrientation & SWT.LEFT) != 0) { + newX = bounds.x; + } else if ((cursorOrientation & SWT.RIGHT) != 0) { + newX = bounds.x + bounds.width; + } else { + newX = bounds.x + bounds.width / 2; + } + + if ((cursorOrientation & SWT.UP) != 0) { + newY = bounds.y; + } else if ((cursorOrientation & SWT.DOWN) != 0) { + newY = bounds.y + bounds.height; + } else { + newY = bounds.y + bounds.height / 2; + } + + /* + * Convert to screen coordinates if needed + */ + if (parent != null) { + Point pt = parent.toDisplay (newX, newY); + newX = pt.x; + newY = pt.y; + } + if (movePointer) { + display.setCursorLocation(newX, newY); + } + + /* + * If the client has not provided a custom cursor then determine + * the appropriate resize cursor. + */ + if (clientCursor == null) { + Cursor newCursor = null; + switch (cursorOrientation) { + case SWT.UP: + newCursor = new Cursor(display, SWT.CURSOR_SIZENS); + break; + case SWT.DOWN: + newCursor = new Cursor(display, SWT.CURSOR_SIZENS); + break; + case SWT.LEFT: + newCursor = new Cursor(display, SWT.CURSOR_SIZEWE); + break; + case SWT.RIGHT: + newCursor = new Cursor(display, SWT.CURSOR_SIZEWE); + break; + case SWT.LEFT | SWT.UP: + newCursor = new Cursor(display, SWT.CURSOR_SIZENWSE); + break; + case SWT.RIGHT | SWT.DOWN: + newCursor = new Cursor(display, SWT.CURSOR_SIZENWSE); + break; + case SWT.LEFT | SWT.DOWN: + newCursor = new Cursor(display, SWT.CURSOR_SIZENESW); + break; + case SWT.RIGHT | SWT.UP: + newCursor = new Cursor(display, SWT.CURSOR_SIZENESW); + break; + default: + newCursor = new Cursor(display, SWT.CURSOR_SIZEALL); + break; + } + display.lockCursor = false; + newCursor.handle.set(); + display.lockCursor = true; + if (resizeCursor != null) { + resizeCursor.dispose (); + } + resizeCursor = newCursor; + } + + return new Point (newX, newY); +} + +static int checkStyle (int style) { + if ((style & (SWT.LEFT | SWT.RIGHT | SWT.UP | SWT.DOWN)) == 0) { + style |= SWT.LEFT | SWT.RIGHT | SWT.UP | SWT.DOWN; + } + return style; +} + +/** + * Stops displaying the tracker rectangles. Note that this is not considered + * to be a cancelation by the user. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void close () { + checkWidget (); + tracking = false; +} +Rectangle computeBounds () { + if (rectangles.length == 0) return null; + int xMin = rectangles [0].x; + int yMin = rectangles [0].y; + int xMax = rectangles [0].x + rectangles [0].width; + int yMax = rectangles [0].y + rectangles [0].height; + + for (int i = 1; i < rectangles.length; i++) { + if (rectangles [i].x < xMin) xMin = rectangles [i].x; + if (rectangles [i].y < yMin) yMin = rectangles [i].y; + int rectRight = rectangles [i].x + rectangles [i].width; + if (rectRight > xMax) xMax = rectRight; + int rectBottom = rectangles [i].y + rectangles [i].height; + if (rectBottom > yMax) yMax = rectBottom; + } + + return new Rectangle (xMin, yMin, xMax - xMin, yMax - yMin); +} + +Rectangle [] computeProportions (Rectangle [] rects) { + Rectangle [] result = new Rectangle [rects.length]; + bounds = computeBounds (); + if (bounds != null) { + for (int i = 0; i < rects.length; i++) { + int x = 0, y = 0, width = 0, height = 0; + if (bounds.width != 0) { + x = (rects [i].x - bounds.x) * 100 / bounds.width; + width = rects [i].width * 100 / bounds.width; + } else { + width = 100; + } + if (bounds.height != 0) { + y = (rects [i].y - bounds.y) * 100 / bounds.height; + height = rects [i].height * 100 / bounds.height; + } else { + height = 100; + } + result [i] = new Rectangle (x, y, width, height); + } + } + return result; +} + +void drawRectangles (NSWindow window, Rectangle [] rects, boolean erase) { + NSGraphicsContext context = window.graphicsContext(); + NSGraphicsContext.static_saveGraphicsState(); + NSGraphicsContext.setCurrentContext(context); + context.saveGraphicsState(); + Point parentOrigin; + if (parent != null) { + parentOrigin = display.map (parent, null, 0, 0); + } else { + parentOrigin = new Point (0, 0); + } + context.setCompositingOperation(erase ? OS.NSCompositeClear : OS.NSCompositeSourceOver); + NSRect rectFrame = new NSRect(); + NSPoint globalPoint = new NSPoint(); + float /*double*/ screenHeight = display.getPrimaryFrame().height; + for (int i=0; i<rects.length; i++) { + Rectangle rect = rects [i]; + rectFrame.x = rect.x + parentOrigin.x; + rectFrame.y = screenHeight - (int)((rect.y + parentOrigin.y) + rect.height); + rectFrame.width = rect.width; + rectFrame.height = rect.height; + globalPoint.x = rectFrame.x; + globalPoint.y = rectFrame.y; + globalPoint = window.convertScreenToBase(globalPoint); + rectFrame.x = globalPoint.x; + rectFrame.y = globalPoint.y; + + if (erase) { + rectFrame.width++; + rectFrame.height++; + NSBezierPath.fillRect(rectFrame); + } else { + rectFrame.x += 0.5f; + rectFrame.y += 0.5f; + NSBezierPath.strokeRect(rectFrame); + } + } + if (!erase) context.flushGraphics(); + context.restoreGraphicsState(); + NSGraphicsContext.static_restoreGraphicsState(); +} + +/** + * Returns the bounds that are being drawn, expressed relative to the parent + * widget. If the parent is a <code>Display</code> then these are screen + * coordinates. + * + * @return the bounds of the Rectangles being drawn + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Rectangle [] getRectangles () { + checkWidget(); + Rectangle [] result = new Rectangle [rectangles.length]; + for (int i = 0; i < rectangles.length; i++) { + Rectangle current = rectangles [i]; + result [i] = new Rectangle (current.x, current.y, current.width, current.height); + } + return result; +} + +/** + * Returns <code>true</code> if the rectangles are drawn with a stippled line, <code>false</code> otherwise. + * + * @return the stippled effect of the rectangles + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getStippled () { + checkWidget (); + return stippled; +} + +void mouse (NSEvent nsEvent) { + NSPoint location; + if (nsEvent == null || nsEvent.type() == OS.NSMouseMoved) { + location = NSEvent.mouseLocation(); + } else { + location = nsEvent.locationInWindow(); + location = nsEvent.window().convertBaseToScreen(location); + } + location.y = display.getPrimaryFrame().height - location.y; + int newX = (int)location.x, newY = (int)location.y; + if (newX != oldX || newY != oldY) { + Rectangle [] oldRectangles = rectangles; + Rectangle [] rectsToErase = new Rectangle [rectangles.length]; + for (int i = 0; i < rectangles.length; i++) { + Rectangle current = rectangles [i]; + rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); + } + Event event = new Event (); + event.x = newX; + event.y = newY; + if ((style & SWT.RESIZE) != 0) { + boolean orientationInit = resizeRectangles (newX - oldX, newY - oldY); + inEvent = true; + sendEvent (SWT.Resize, event); + inEvent = false; + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the move + * event. If this happens, return false to indicate + * that the tracking has failed. + */ + if (isDisposed ()) { + cancelled = true; + return; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the resize event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } + else { + draw = true; + } + if (draw) { + drawRectangles (window, rectsToErase, true); + drawRectangles (window, rectangles, false); + } + Point cursorPos = adjustResizeCursor (orientationInit); + if (cursorPos != null) { + newX = cursorPos.x; + newY = cursorPos.y; + } + } else { + moveRectangles (newX - oldX, newY - oldY); + inEvent = true; + sendEvent (SWT.Move, event); + inEvent = false; + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the move + * event. If this happens, return false to indicate + * that the tracking has failed. + */ + if (isDisposed ()) { + cancelled = true; + return; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the move event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } else { + draw = true; + } + if (draw) { + drawRectangles (window, rectsToErase, true); + drawRectangles (window, rectangles, false); + } + } + oldX = newX; oldY = newY; + } + switch ((int)/*64*/nsEvent.type()) { + case OS.NSLeftMouseUp: + case OS.NSRightMouseUp: + case OS.NSOtherMouseUp: + tracking = false; + } +} + +void key (NSEvent nsEvent) { + //TODO send event +// if (!sendKeyEvent (SWT.KeyDown, theEvent)) return OS.noErr; + int /*long*/ modifierFlags = nsEvent.modifierFlags(); + int stepSize = (modifierFlags & OS.NSControlKeyMask) != 0 ? STEPSIZE_SMALL : STEPSIZE_LARGE; + int xChange = 0, yChange = 0; + switch (nsEvent.keyCode()) { + case 53: /* Esc */ + cancelled = true; + tracking = false; + break; + case 76: /* KP Enter */ + case 36: /* Return */ + tracking = false; + break; + case 123: /* Left arrow */ + xChange = -stepSize; + break; + case 124: /* Right arrow */ + xChange = stepSize; + break; + case 126: /* Up arrow */ + yChange = -stepSize; + break; + case 125: /* Down arrow */ + yChange = stepSize; + break; + } + if (xChange != 0 || yChange != 0) { + Rectangle [] oldRectangles = rectangles; + Rectangle [] rectsToErase = new Rectangle [rectangles.length]; + for (int i = 0; i < rectangles.length; i++) { + Rectangle current = rectangles [i]; + rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); + } + Event event = new Event (); + int newX = oldX + xChange; + int newY = oldY + yChange; + event.x = newX; + event.y = newY; + Point cursorPos; + if ((style & SWT.RESIZE) != 0) { + resizeRectangles (xChange, yChange); + inEvent = true; + sendEvent (SWT.Resize, event); + inEvent = false; + /* + * It is possible (but unlikely) that application + * code could have disposed the widget in the move + * event. If this happens return false to indicate + * that the tracking has failed. + */ + if (isDisposed ()) { + cancelled = true; + return; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the resize event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } else { + draw = true; + } + if (draw) { + drawRectangles (window, rectsToErase, true); + drawRectangles (window, rectangles, false); + } + cursorPos = adjustResizeCursor (true); + } else { + moveRectangles (xChange, yChange); + inEvent = true; + sendEvent (SWT.Move, event); + inEvent = false; + /* + * It is possible (but unlikely) that application + * code could have disposed the widget in the move + * event. If this happens return false to indicate + * that the tracking has failed. + */ + if (isDisposed ()) { + cancelled = true; + return; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the move event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } else { + draw = true; + } + if (draw) { + drawRectangles (window, rectsToErase, true); + drawRectangles (window, rectangles, false); + } + cursorPos = adjustMoveCursor (); + } + if (cursorPos != null) { + oldX = cursorPos.x; + oldY = cursorPos.y; + } + } +} + +void moveRectangles (int xChange, int yChange) { + if (bounds == null) return; + if (xChange < 0 && ((style & SWT.LEFT) == 0)) xChange = 0; + if (xChange > 0 && ((style & SWT.RIGHT) == 0)) xChange = 0; + if (yChange < 0 && ((style & SWT.UP) == 0)) yChange = 0; + if (yChange > 0 && ((style & SWT.DOWN) == 0)) yChange = 0; + if (xChange == 0 && yChange == 0) return; + bounds.x += xChange; bounds.y += yChange; + for (int i = 0; i < rectangles.length; i++) { + rectangles [i].x += xChange; + rectangles [i].y += yChange; + } +} + +/** + * Displays the Tracker rectangles for manipulation by the user. Returns when + * the user has either finished manipulating the rectangles or has cancelled the + * Tracker. + * + * @return <code>true</code> if the user did not cancel the Tracker, <code>false</code> otherwise + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean open () { + checkWidget (); + Display display = this.display; + cancelled = false; + tracking = true; + window = (NSWindow)new NSWindow().alloc(); + NSArray screens = NSScreen.screens(); + float /*double*/ minX = Float.MAX_VALUE, maxX = Float.MIN_VALUE; + float /*double*/ minY = Float.MAX_VALUE, maxY = Float.MIN_VALUE; + int count = (int)/*64*/screens.count(); + for (int i = 0; i < count; i++) { + NSScreen screen = new NSScreen(screens.objectAtIndex(i)); + NSRect frame = screen.frame(); + float /*double*/ x1 = frame.x, x2 = frame.x + frame.width; + float /*double*/ y1 = frame.y, y2 = frame.y + frame.height; + if (x1 < minX) minX = x1; + if (x2 < minX) minX = x2; + if (x1 > maxX) maxX = x1; + if (x2 > maxX) maxX = x2; + if (y1 < minY) minY = y1; + if (y2 < minY) minY = y2; + if (y1 > maxY) maxY = y1; + if (y2 > maxY) maxY = y2; + } + NSRect frame = new NSRect(); + frame.x = minX; + frame.y = minY; + frame.width = maxX - minX; + frame.height = maxY - minY; + window = window.initWithContentRect(frame, OS.NSBorderlessWindowMask, OS.NSBackingStoreBuffered, false); + window.setOpaque(false); + window.setContentView(null); + window.setBackgroundColor(NSColor.clearColor()); + NSGraphicsContext context = window.graphicsContext(); + NSGraphicsContext.static_saveGraphicsState(); + NSGraphicsContext.setCurrentContext(context); + context.setCompositingOperation(OS.NSCompositeClear); + frame.x = frame.y = 0; + NSBezierPath.fillRect(frame); + NSGraphicsContext.static_restoreGraphicsState(); + window.orderFrontRegardless(); + + drawRectangles (window, rectangles, false); + + /* + * If exactly one of UP/DOWN is specified as a style then set the cursor + * orientation accordingly (the same is done for LEFT/RIGHT styles below). + */ + int vStyle = style & (SWT.UP | SWT.DOWN); + if (vStyle == SWT.UP || vStyle == SWT.DOWN) { + cursorOrientation |= vStyle; + } + int hStyle = style & (SWT.LEFT | SWT.RIGHT); + if (hStyle == SWT.LEFT || hStyle == SWT.RIGHT) { + cursorOrientation |= hStyle; + } + + Point cursorPos; + boolean down = false; + NSApplication application = NSApplication.sharedApplication(); + NSEvent currentEvent = application.currentEvent(); + if (currentEvent != null) { + switch ((int)/*64*/currentEvent.type()) { + case OS.NSLeftMouseDown: + case OS.NSLeftMouseDragged: + case OS.NSRightMouseDown: + case OS.NSRightMouseDragged: + case OS.NSOtherMouseDown: + case OS.NSOtherMouseDragged: + down = true; + } + } + if (down) { + cursorPos = display.getCursorLocation(); + } else { + if ((style & SWT.RESIZE) != 0) { + cursorPos = adjustResizeCursor (true); + } else { + cursorPos = adjustMoveCursor (); + } + } + if (cursorPos != null) { + oldX = cursorPos.x; + oldY = cursorPos.y; + } + + Control oldTrackingControl = display.trackingControl; + display.trackingControl = null; + /* Tracker behaves like a Dialog with its own OS event loop. */ + while (tracking && !cancelled) { + display.addPool(); + try { + NSEvent event = application.nextEventMatchingMask(0, NSDate.distantFuture(), OS.NSDefaultRunLoopMode, true); + if (event == null) continue; + int type = (int)/*64*/event.type(); + switch (type) { + case OS.NSLeftMouseUp: + case OS.NSRightMouseUp: + case OS.NSOtherMouseUp: + case OS.NSMouseMoved: + case OS.NSLeftMouseDragged: + case OS.NSRightMouseDragged: + case OS.NSOtherMouseDragged: + mouse(event); + break; + case OS.NSKeyDown: +// case OS.NSKeyUp: + case OS.NSFlagsChanged: + key(event); + break; + } + boolean dispatch = true; + switch (type) { + case OS.NSLeftMouseDown: + case OS.NSLeftMouseUp: + case OS.NSRightMouseDown: + case OS.NSRightMouseUp: + case OS.NSOtherMouseDown: + case OS.NSOtherMouseUp: + case OS.NSMouseMoved: + case OS.NSLeftMouseDragged: + case OS.NSRightMouseDragged: + case OS.NSOtherMouseDragged: + case OS.NSMouseEntered: + case OS.NSMouseExited: + case OS.NSKeyDown: + case OS.NSKeyUp: + case OS.NSFlagsChanged: + dispatch = false; + } + if (dispatch) application.sendEvent(event); + if (clientCursor != null && resizeCursor == null) { + display.lockCursor = false; + clientCursor.handle.set(); + display.lockCursor = true; + } + } finally { + display.removePool(); + } + } + if (oldTrackingControl != null && !oldTrackingControl.isDisposed()) { + display.trackingControl = oldTrackingControl; + } + display.setCursor(display.findControl(true)); + if (!isDisposed()) { + drawRectangles (window, rectangles, true); + } + if (window != null) window.close(); + tracking = false; + window = null; + return !cancelled; +} + +void releaseWidget () { + super.releaseWidget (); + parent = null; + rectangles = proportions = null; + bounds = null; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is moved or resized. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ControlListener + * @see #addControlListener + */ +public void removeControlListener (ControlListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Resize, listener); + eventTable.unhook (SWT.Move, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when keys are pressed and released on the system keyboard. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see KeyListener + * @see #addKeyListener + */ +public void removeKeyListener(KeyListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook(SWT.KeyUp, listener); + eventTable.unhook(SWT.KeyDown, listener); +} + +/* + * Returns true if the pointer's orientation was initialized in some dimension, + * and false otherwise. + */ +boolean resizeRectangles (int xChange, int yChange) { + if (bounds == null) return false; + boolean orientationInit = false; + /* + * If the cursor orientation has not been set in the orientation of + * this change then try to set it here. + */ + if (xChange < 0 && ((style & SWT.LEFT) != 0) && ((cursorOrientation & SWT.RIGHT) == 0)) { + if ((cursorOrientation & SWT.LEFT) == 0) { + cursorOrientation |= SWT.LEFT; + orientationInit = true; + } + } + if (xChange > 0 && ((style & SWT.RIGHT) != 0) && ((cursorOrientation & SWT.LEFT) == 0)) { + if ((cursorOrientation & SWT.RIGHT) == 0) { + cursorOrientation |= SWT.RIGHT; + orientationInit = true; + } + } + if (yChange < 0 && ((style & SWT.UP) != 0) && ((cursorOrientation & SWT.DOWN) == 0)) { + if ((cursorOrientation & SWT.UP) == 0) { + cursorOrientation |= SWT.UP; + orientationInit = true; + } + } + if (yChange > 0 && ((style & SWT.DOWN) != 0) && ((cursorOrientation & SWT.UP) == 0)) { + if ((cursorOrientation & SWT.DOWN) == 0) { + cursorOrientation |= SWT.DOWN; + orientationInit = true; + } + } + + /* + * If the bounds will flip about the x or y axis then apply the adjustment + * up to the axis (ie.- where bounds width/height becomes 0), change the + * cursor's orientation accordingly, and flip each Rectangle's origin (only + * necessary for > 1 Rectangles) + */ + if ((cursorOrientation & SWT.LEFT) != 0) { + if (xChange > bounds.width) { + if ((style & SWT.RIGHT) == 0) return orientationInit; + cursorOrientation |= SWT.RIGHT; + cursorOrientation &= ~SWT.LEFT; + bounds.x += bounds.width; + xChange -= bounds.width; + bounds.width = 0; + if (proportions.length > 1) { + for (int i = 0; i < proportions.length; i++) { + Rectangle proportion = proportions [i]; + proportion.x = 100 - proportion.x - proportion.width; + } + } + } + } else if ((cursorOrientation & SWT.RIGHT) != 0) { + if (bounds.width < -xChange) { + if ((style & SWT.LEFT) == 0) return orientationInit; + cursorOrientation |= SWT.LEFT; + cursorOrientation &= ~SWT.RIGHT; + xChange += bounds.width; + bounds.width = 0; + if (proportions.length > 1) { + for (int i = 0; i < proportions.length; i++) { + Rectangle proportion = proportions [i]; + proportion.x = 100 - proportion.x - proportion.width; + } + } + } + } + if ((cursorOrientation & SWT.UP) != 0) { + if (yChange > bounds.height) { + if ((style & SWT.DOWN) == 0) return orientationInit; + cursorOrientation |= SWT.DOWN; + cursorOrientation &= ~SWT.UP; + bounds.y += bounds.height; + yChange -= bounds.height; + bounds.height = 0; + if (proportions.length > 1) { + for (int i = 0; i < proportions.length; i++) { + Rectangle proportion = proportions [i]; + proportion.y = 100 - proportion.y - proportion.height; + } + } + } + } else if ((cursorOrientation & SWT.DOWN) != 0) { + if (bounds.height < -yChange) { + if ((style & SWT.UP) == 0) return orientationInit; + cursorOrientation |= SWT.UP; + cursorOrientation &= ~SWT.DOWN; + yChange += bounds.height; + bounds.height = 0; + if (proportions.length > 1) { + for (int i = 0; i < proportions.length; i++) { + Rectangle proportion = proportions [i]; + proportion.y = 100 - proportion.y - proportion.height; + } + } + } + } + + // apply the bounds adjustment + if ((cursorOrientation & SWT.LEFT) != 0) { + bounds.x += xChange; + bounds.width -= xChange; + } else if ((cursorOrientation & SWT.RIGHT) != 0) { + bounds.width += xChange; + } + if ((cursorOrientation & SWT.UP) != 0) { + bounds.y += yChange; + bounds.height -= yChange; + } else if ((cursorOrientation & SWT.DOWN) != 0) { + bounds.height += yChange; + } + + Rectangle [] newRects = new Rectangle [rectangles.length]; + for (int i = 0; i < rectangles.length; i++) { + Rectangle proportion = proportions[i]; + newRects[i] = new Rectangle ( + proportion.x * bounds.width / 100 + bounds.x, + proportion.y * bounds.height / 100 + bounds.y, + proportion.width * bounds.width / 100, + proportion.height * bounds.height / 100); + } + rectangles = newRects; + return orientationInit; +} + +/** + * Sets the <code>Cursor</code> of the Tracker. If this cursor is <code>null</code> + * then the cursor reverts to the default. + * + * @param newCursor the new <code>Cursor</code> to display + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setCursor (Cursor newCursor) { + checkWidget (); + clientCursor = newCursor; + if (newCursor != null) { + display.lockCursor = false; + if (inEvent) newCursor.handle.set(); + display.lockCursor = true; + } +} + +/** + * Specifies the rectangles that should be drawn, expressed relative to the parent + * widget. If the parent is a Display then these are screen coordinates. + * + * @param rectangles the bounds of the rectangles to be drawn + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the set of rectangles is null or contains a null rectangle</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setRectangles (Rectangle [] rectangles) { + checkWidget (); + if (rectangles == null) error (SWT.ERROR_NULL_ARGUMENT); + int length = rectangles.length; + this.rectangles = new Rectangle [length]; + for (int i = 0; i < length; i++) { + Rectangle current = rectangles [i]; + if (current == null) error (SWT.ERROR_NULL_ARGUMENT); + this.rectangles [i] = new Rectangle (current.x, current.y, current.width, current.height); + } + proportions = computeProportions (rectangles); +} + +/** + * Changes the appearance of the line used to draw the rectangles. + * + * @param stippled <code>true</code> if rectangle should appear stippled + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setStippled (boolean stippled) { + checkWidget (); + this.stippled = stippled; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TrayItem.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TrayItem.java new file mode 100755 index 0000000000..ec0a782aa6 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TrayItem.java @@ -0,0 +1,537 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class represent icons that can be placed on the + * system tray or task bar status area. + * <p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>(none)</dd> + * <dt><b>Events:</b></dt> + * <dd>DefaultSelection, MenuDetect, Selection</dd> + * </dl> + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#tray">Tray, TrayItem snippets</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.0 + * @noextend This class is not intended to be subclassed by clients. + */ +public class TrayItem extends Item { + Tray parent; + ToolTip toolTip; + String toolTipText; + boolean visible = true, highlight; + NSStatusItem item; + NSImageView view; + + static final float BORDER = 8f; + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>Tray</code>) and a style value + * describing its behavior and appearance. The item is added + * to the end of the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public TrayItem (Tray parent, int style) { + super (parent, style); + this.parent = parent; + parent.createItem (this, parent.getItemCount ()); + createWidget (); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the platform-specific context menu trigger + * has occurred, by sending it one of the messages defined in + * the <code>MenuDetectListener</code> interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see MenuDetectListener + * @see #removeMenuDetectListener + * + * @since 3.3 + */ +public void addMenuDetectListener (MenuDetectListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.MenuDetect, typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the receiver is selected by the user, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * <code>widgetSelected</code> is called when the receiver is selected + * <code>widgetDefaultSelected</code> is called when the receiver is double-clicked + * </p> + * + * @param listener the listener which should be notified when the receiver is selected by the user + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener(SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Selection,typedListener); + addListener (SWT.DefaultSelection,typedListener); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +void createHandle () { + NSStatusBar statusBar = NSStatusBar.systemStatusBar(); + item = statusBar.statusItemWithLength(0); + if (item == null) error (SWT.ERROR_NO_HANDLES); + item.retain(); + item.setHighlightMode(true); + view = (NSImageView)new SWTImageView().alloc(); + if (view == null) error (SWT.ERROR_NO_HANDLES); + view.init (); + item.setView(view); +} + +void deregister () { + super.deregister (); + display.removeWidget (view); + display.removeWidget(view.cell()); +} + +void destroyWidget () { + parent.destroyItem (this); + releaseHandle (); +} + +Point getLocation () { + NSRect rect = view.frame(); + NSRect windowRect = view.window().frame(); + NSPoint pt = new NSPoint(); + pt.x = rect.width / 2; + pt = view.convertPoint_fromView_(pt, null); + pt.x += windowRect.x; + return new Point ((int)pt.x, (int)pt.y); +} + +/** + * Returns the receiver's parent, which must be a <code>Tray</code>. + * + * @return the receiver's parent + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public Tray getParent () { + checkWidget (); + return parent; +} + +/** + * Returns the receiver's tool tip, or null if it has + * not been set. + * + * @return the receiver's tool tip text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public ToolTip getToolTip () { + checkWidget (); + return toolTip; +} + +/** + * Returns the receiver's tool tip text, or null if it has + * not been set. + * + * @return the receiver's tool tip text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String getToolTipText () { + checkWidget (); + return toolTipText; +} + +/** + * Returns <code>true</code> if the receiver is visible and + * <code>false</code> otherwise. + * + * @return the receiver's visibility + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getVisible () { + checkWidget (); + return visible; +} + +void register () { + super.register (); + display.addWidget (view, this); + display.addWidget (((NSControl)view).cell(), this); +} + +void releaseHandle () { + super.releaseHandle (); + parent = null; + if (item != null) item.release(); + if (view != null) view.release(); + item = null; + view = null; +} + +void releaseWidget () { + super.releaseWidget (); + NSStatusBar statusBar = NSStatusBar.systemStatusBar(); + statusBar.removeStatusItem(item); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the platform-specific context menu trigger has + * occurred. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see MenuDetectListener + * @see #addMenuDetectListener + * + * @since 3.3 + */ +public void removeMenuDetectListener (MenuDetectListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.MenuDetect, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the receiver is selected by the user. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener (SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Selection, listener); + eventTable.unhook (SWT.DefaultSelection,listener); +} + +/** + * Sets the receiver's image. + * + * @param image the new image + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setImage (Image image) { + checkWidget (); + if (image != null && image.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + super.setImage (image); + float /*double*/ width = 0; + if (image == null) { + view.setImage (null); + } else { + /* + * Feature in Cocoa. If the NSImage object being set into the view is + * the same NSImage object that is already there then the new image is + * not taken. This results in the view's image not changing even if the + * NSImage object's content has changed since it was last set into the + * view. The workaround is to temporarily set the view's image to null + * so that the new image will then be taken. + */ + NSImage current = view.image (); + if (current != null && current.id == image.handle.id) { + view.setImage (null); + } + view.setImage (image.handle); + if (visible) { + width = image.handle.size ().width + BORDER; + } + } + item.setLength (width); +} + +/** + * Sets the receiver's tool tip to the argument, which + * may be null indicating that no tool tip should be shown. + * + * @param toolTip the new tool tip (or null) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public void setToolTip (ToolTip toolTip) { + checkWidget (); + ToolTip oldTip = this.toolTip, newTip = toolTip; + if (oldTip != null) oldTip.item = null; + this.toolTip = newTip; + if (newTip != null) newTip.item = this; +} + +/** + * Sets the receiver's tool tip text to the argument, which + * may be null indicating that the default tool tip for the + * control will be shown. For a control that has a default + * tool tip, such as the Tree control on Windows, setting + * the tool tip text to an empty string replaces the default, + * causing no tool tip text to be shown. + * <p> + * The mnemonic indicator (character '&') is not displayed in a tool tip. + * To display a single '&' in the tool tip, the character '&' can be + * escaped by doubling it in the string. + * </p> + * + * @param string the new tool tip text (or null) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setToolTipText (String string) { + checkWidget (); + toolTipText = string; + _setToolTipText (string); +} + +void _setToolTipText (String string) { + if (string != null) { + char[] chars = new char [string.length ()]; + string.getChars (0, chars.length, chars, 0); + int length = fixMnemonic (chars); + NSString str = NSString.stringWithCharacters (chars, length); + view.setToolTip (str); + } else { + view.setToolTip (null); + } +} + +/** + * Makes the receiver visible if the argument is <code>true</code>, + * and makes it invisible otherwise. + * + * @param visible the new visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setVisible (boolean visible) { + checkWidget (); + if (this.visible == visible) return; + if (visible) { + sendEvent (SWT.Show); + if (isDisposed ()) return; + } + this.visible = visible; + float /*double*/ width = image != null && visible ? image.handle.size().width + BORDER : 0; + item.setLength(width); + if (!visible) sendEvent (SWT.Hide); +} + +void showMenu (Menu menu) { + display.trayItemMenu = menu; + item.popUpStatusItemMenu(menu.nsMenu); +} + +void showMenu () { + _setToolTipText (null); + Display display = this.display; + display.currentTrayItem = this; + sendEvent (SWT.MenuDetect); + if (!isDisposed ()) display.runPopups(); + display.currentTrayItem = null; + if (isDisposed ()) return; + _setToolTipText (toolTipText); +} + +void displayMenu () { + if (highlight) { + view.display(); + display.trayItemMenu = null; + showMenu(); + if (display.trayItemMenu != null) { + display.trayItemMenu = null; + highlight = false; + view.setNeedsDisplay(true); + } + } +} + +boolean shouldShowMenu (NSEvent event) { + if (!hooks(SWT.MenuDetect)) return false; + switch ((int)/*64*/event.type()) { + case OS.NSRightMouseDown: return true; + case OS.NSLeftMouseDown: + if (!(hooks(SWT.Selection) || hooks(SWT.DefaultSelection))) { + return true; + } + if ((event.modifierFlags() & OS.NSDeviceIndependentModifierFlagsMask) == OS.NSControlKeyMask) { + return true; + } + return false; + case OS.NSLeftMouseDragged: + case OS.NSRightMouseDragged: + return true; + } + return false; +} + +void mouseDown(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + NSEvent nsEvent = new NSEvent(theEvent); + highlight = true; + view.setNeedsDisplay(true); + if (shouldShowMenu(nsEvent)) displayMenu(); +} + +void mouseDragged(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + NSEvent nsEvent = new NSEvent(theEvent); + NSRect frame = view.frame(); + highlight = OS.NSPointInRect(nsEvent.locationInWindow(), frame); + view.setNeedsDisplay(true); + if (shouldShowMenu(nsEvent)) displayMenu(); +} + +void mouseUp(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (highlight) { + NSEvent nsEvent = new NSEvent(theEvent); + if (nsEvent.type() == OS.NSLeftMouseUp) { + postEvent(nsEvent.clickCount() == 2 ? SWT.DefaultSelection : SWT.Selection); + } + } + highlight = false; + view.setNeedsDisplay(true); +} + +void rightMouseDown(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + mouseDown(id, sel, theEvent); +} + +void rightMouseUp(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + mouseUp(id, sel, theEvent); +} + +void rightMouseDragged(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + mouseDragged(id, sel, theEvent); +} + +void drawRect(int /*long*/ id, int /*long*/ sel, NSRect rect) { + item.drawStatusBarBackgroundInRect(rect, highlight); + super.drawRect(id, sel, rect); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tree.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tree.java new file mode 100755 index 0000000000..efac0d6428 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tree.java @@ -0,0 +1,2974 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import org.eclipse.swt.*; +import org.eclipse.swt.accessibility.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.cocoa.*; + +/** + * Instances of this class provide a selectable user interface object + * that displays a hierarchy of items and issues notification when an + * item in the hierarchy is selected. + * <p> + * The item children that may be added to instances of this class + * must be of type <code>TreeItem</code>. + * </p><p> + * Style <code>VIRTUAL</code> is used to create a <code>Tree</code> whose + * <code>TreeItem</code>s are to be populated by the client on an on-demand basis + * instead of up-front. This can provide significant performance improvements for + * trees that are very large or for which <code>TreeItem</code> population is + * expensive (for example, retrieving values from an external source). + * </p><p> + * Here is an example of using a <code>Tree</code> with style <code>VIRTUAL</code>: + * <code><pre> + * final Tree tree = new Tree(parent, SWT.VIRTUAL | SWT.BORDER); + * tree.setItemCount(20); + * tree.addListener(SWT.SetData, new Listener() { + * public void handleEvent(Event event) { + * TreeItem item = (TreeItem)event.item; + * TreeItem parentItem = item.getParentItem(); + * String text = null; + * if (parentItem == null) { + * text = "node " + tree.indexOf(item); + * } else { + * text = parentItem.getText() + " - " + parentItem.indexOf(item); + * } + * item.setText(text); + * System.out.println(text); + * item.setItemCount(10); + * } + * }); + * </pre></code> + * </p><p> + * Note that although this class is a subclass of <code>Composite</code>, + * it does not normally make sense to add <code>Control</code> children to + * it, or set a layout on it, unless implementing something like a cell + * editor. + * </p><p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, VIRTUAL, NO_SCROLL</dd> + * <dt><b>Events:</b></dt> + * <dd>Selection, DefaultSelection, Collapse, Expand, SetData, MeasureItem, EraseItem, PaintItem</dd> + * </dl> + * </p><p> + * Note: Only one of the styles SINGLE and MULTI may be specified. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#tree">Tree, TreeItem, TreeColumn snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Tree extends Composite { + NSTableColumn firstColumn, checkColumn; + NSTextFieldCell dataCell; + NSButtonCell buttonCell; + NSTableHeaderView headerView; + TreeItem [] items; + int itemCount; + TreeColumn [] columns; + TreeColumn sortColumn; + int columnCount; + int sortDirection; + boolean ignoreExpand, ignoreSelect, ignoreRedraw, reloadPending, drawExpansion; + Rectangle imageBounds; + TreeItem insertItem; + boolean insertBefore; + + static int NEXT_ID; + + static final int FIRST_COLUMN_MINIMUM_WIDTH = 5; + static final int IMAGE_GAP = 3; + static final int TEXT_GAP = 2; + static final int CELL_GAP = 1; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#SINGLE + * @see SWT#MULTI + * @see SWT#CHECK + * @see SWT#FULL_SELECTION + * @see SWT#VIRTUAL + * @see SWT#NO_SCROLL + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public Tree (Composite parent, int style) { + super (parent, checkStyle (style)); +} + +void _addListener (int eventType, Listener listener) { + super._addListener (eventType, listener); + clearCachedWidth (items); +} + +TreeItem _getItem (TreeItem parentItem, int index, boolean create) { + int count; + TreeItem[] items; + if (parentItem != null) { + count = parentItem.itemCount; + items = parentItem.items; + } else { + count = this.itemCount; + items = this.items; + } + if (index < 0 || index >= count) return null; + TreeItem item = items [index]; + if (item != null || (style & SWT.VIRTUAL) == 0 || !create) return item; + item = new TreeItem (this, parentItem, SWT.NONE, index, false); + items [index] = item; + return item; +} + +int /*long*/ accessibilityAttributeValue (int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + + if (accessible != null) { + NSString attribute = new NSString(arg0); + id returnValue = accessible.internal_accessibilityAttributeValue(attribute, ACC.CHILDID_SELF); + if (returnValue != null) return returnValue.id; + } + + NSString attributeName = new NSString(arg0); + + // Accessibility Verifier queries for a title or description. NSOutlineView doesn't + // seem to return either, so we return a default description value here. + if (attributeName.isEqualToString (OS.NSAccessibilityDescriptionAttribute)) { + return NSString.stringWith("").id; + } + + return super.accessibilityAttributeValue(id, sel, arg0); +} + + +/** + * Adds the listener to the collection of listeners who will + * be notified when the user changes the receiver's selection, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * When <code>widgetSelected</code> is called, the item field of the event object is valid. + * If the receiver has the <code>SWT.CHECK</code> style and the check selection changes, + * the event object detail field contains the value <code>SWT.CHECK</code>. + * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked. + * The item field of the event object is valid for default selection, but the detail field is not used. + * </p> + * + * @param listener the listener which should be notified when the user changes the receiver's selection + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener(SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Selection, typedListener); + addListener (SWT.DefaultSelection, typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when an item in the receiver is expanded or collapsed + * by sending it one of the messages defined in the <code>TreeListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see TreeListener + * @see #removeTreeListener + */ +public void addTreeListener(TreeListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Expand, typedListener); + addListener (SWT.Collapse, typedListener); +} + +int calculateWidth (TreeItem[] items, int index, GC gc, boolean recurse) { + if (items == null) return 0; + int width = 0; + for (int i=0; i<items.length; i++) { + TreeItem item = items [i]; + if (item != null) { + int itemWidth = item.calculateWidth (index, gc); + width = Math.max (width, itemWidth); + if (recurse && item.getExpanded ()) { + width = Math.max (width, calculateWidth (item.items, index, gc, recurse)); + } + } + } + return width; +} + +NSSize cellSize (int /*long*/ id, int /*long*/ sel) { + NSSize size = super.cellSize(id, sel); + NSImage image = new NSCell(id).image(); + if (image != null) size.width += imageBounds.width + IMAGE_GAP; + if (hooks(SWT.MeasureItem)) { + int /*long*/ [] outValue = new int /*long*/ [1]; + OS.object_getInstanceVariable(id, Display.SWT_ROW, outValue); + TreeItem item = (TreeItem) display.getWidget (outValue [0]); + OS.object_getInstanceVariable(id, Display.SWT_COLUMN, outValue); + int /*long*/ tableColumn = outValue[0]; + int columnIndex = 0; + for (int i=0; i<columnCount; i++) { + if (columns [i].nsColumn.id == tableColumn) { + columnIndex = i; + break; + } + } + sendMeasureItem (item, columnIndex, size); + } + return size; +} + +boolean canDragRowsWithIndexes_atPoint(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1) { + NSPoint clickPoint = new NSPoint(); + OS.memmove(clickPoint, arg1, NSPoint.sizeof); + NSOutlineView tree = (NSOutlineView)view; + + // If the current row is not selected and the user is not attempting to modify the selection, select the row first. + int /*long*/ row = tree.rowAtPoint(clickPoint); + int /*long*/ modifiers = NSApplication.sharedApplication().currentEvent().modifierFlags(); + + boolean drag = (state & DRAG_DETECT) != 0 && hooks (SWT.DragDetect); + if (drag) { + if (!tree.isRowSelected(row) && (modifiers & (OS.NSCommandKeyMask | OS.NSShiftKeyMask | OS.NSAlternateKeyMask | OS.NSControlKeyMask)) == 0) { + NSIndexSet set = (NSIndexSet)new NSIndexSet().alloc(); + set = set.initWithIndex(row); + tree.selectRowIndexes (set, false); + set.release(); + } + } + + // The clicked row must be selected to initiate a drag. + return (tree.isRowSelected(row) && drag); +} + +boolean checkData (TreeItem item) { + if (item.cached) return true; + if ((style & SWT.VIRTUAL) != 0) { + item.cached = true; + Event event = new Event (); + TreeItem parentItem = item.getParentItem (); + event.item = item; + event.index = parentItem == null ? indexOf (item) : parentItem.indexOf (item); + ignoreRedraw = true; + sendEvent (SWT.SetData, event); + //widget could be disposed at this point + ignoreRedraw = false; + if (isDisposed () || item.isDisposed ()) return false; + if (!setScrollWidth (item)) item.redraw (-1); + } + return true; +} + +static int checkStyle (int style) { + /* + * Feature in Windows. Even when WS_HSCROLL or + * WS_VSCROLL is not specified, Windows creates + * trees and tables with scroll bars. The fix + * is to set H_SCROLL and V_SCROLL. + * + * NOTE: This code appears on all platforms so that + * applications have consistent scroll bar behavior. + */ + if ((style & SWT.NO_SCROLL) == 0) { + style |= SWT.H_SCROLL | SWT.V_SCROLL; + } + /* This platform is always FULL_SELECTION */ + style |= SWT.FULL_SELECTION; + return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +void checkItems () { + if (!reloadPending) return; + reloadPending = false; + TreeItem[] selectedItems = getSelection (); + ((NSOutlineView)view).reloadData (); + selectItems (selectedItems, true); + ignoreExpand = true; + for (int i = 0; i < itemCount; i++) { + if (items[i] != null) items[i].updateExpanded (); + } + ignoreExpand = false; +} + +void clear (TreeItem parentItem, int index, boolean all) { + TreeItem item = _getItem (parentItem, index, false); + if (item != null) { + item.clear(); + item.redraw (-1); + if (all) { + clearAll (item, true); + } + } +} + +void clearAll (TreeItem parentItem, boolean all) { + int count = getItemCount (parentItem); + if (count == 0) return; + TreeItem [] children = parentItem == null ? items : parentItem.items; + for (int i=0; i<count; i++) { + TreeItem item = children [i]; + if (item != null) { + item.clear (); + item.redraw (-1); + if (all) clearAll (item, true); + } + } +} + +/** + * Clears the item at the given zero-relative index in the receiver. + * The text, icon and other attributes of the item are set to the default + * value. If the tree was created with the <code>SWT.VIRTUAL</code> style, + * these attributes are requested again as needed. + * + * @param index the index of the item to clear + * @param all <code>true</code> if all child items of the indexed item should be + * cleared recursively, and <code>false</code> otherwise + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SWT#VIRTUAL + * @see SWT#SetData + * + * @since 3.2 + */ +public void clear (int index, boolean all) { + checkWidget (); + int count = getItemCount (); + if (index < 0 || index >= count) error (SWT.ERROR_INVALID_RANGE); + clear (null, index, all); +} + +/** + * Clears all the items in the receiver. The text, icon and other + * attributes of the items are set to their default values. If the + * tree was created with the <code>SWT.VIRTUAL</code> style, these + * attributes are requested again as needed. + * + * @param all <code>true</code> if all child items should be cleared + * recursively, and <code>false</code> otherwise + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SWT#VIRTUAL + * @see SWT#SetData + * + * @since 3.2 + */ +public void clearAll (boolean all) { + checkWidget (); + clearAll (null, all); +} + +void clearCachedWidth (TreeItem[] items) { + if (items == null) return; + for (int i = 0; i < items.length; i++) { + TreeItem item = items [i]; + if (item == null) break; + item.width = -1; + clearCachedWidth (item.items); + } +} + +void collapseItem_collapseChildren (int /*long*/ id, int /*long*/ sel, int /*long*/ itemID, boolean children) { + TreeItem item = (TreeItem)display.getWidget(itemID); + if (!ignoreExpand) item.sendExpand (false, children); + ignoreExpand = true; + super.collapseItem_collapseChildren (id, sel, itemID, children); + ignoreExpand = false; + if (isDisposed() || item.isDisposed()) return; + setScrollWidth (); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + int width = 0, height = 0; + if (wHint == SWT.DEFAULT) { + if (columnCount != 0) { + for (int i=0; i<columnCount; i++) { + width += columns [i].getWidth (); + } + } else { + GC gc = new GC (this); + width = calculateWidth (items, 0, gc, true) + CELL_GAP; + gc.dispose (); + } + if ((style & SWT.CHECK) != 0) width += getCheckColumnWidth (); + } else { + width = wHint; + } + if (hHint == SWT.DEFAULT) { + height = (int)/*64*/((NSOutlineView) view).numberOfRows () * getItemHeight () + getHeaderHeight (); + } else { + height = hHint; + } + if (width <= 0) width = DEFAULT_WIDTH; + if (height <= 0) height = DEFAULT_HEIGHT; + Rectangle rect = computeTrim (0, 0, width, height); + return new Point (rect.width, rect.height); +} + +void createColumn (TreeItem item, int index) { + if (item.items != null) { + for (int i = 0; i < item.items.length; i++) { + if (item.items[i] != null) createColumn (item.items[i], index); + } + } + String [] strings = item.strings; + if (strings != null) { + String [] temp = new String [columnCount]; + System.arraycopy (strings, 0, temp, 0, index); + System.arraycopy (strings, index, temp, index+1, columnCount-index-1); + temp [index] = ""; + item.strings = temp; + } + if (index == 0) item.text = ""; + Image [] images = item.images; + if (images != null) { + Image [] temp = new Image [columnCount]; + System.arraycopy (images, 0, temp, 0, index); + System.arraycopy (images, index, temp, index+1, columnCount-index-1); + item.images = temp; + } + if (index == 0) item.image = null; + Color [] cellBackground = item.cellBackground; + if (cellBackground != null) { + Color [] temp = new Color [columnCount]; + System.arraycopy (cellBackground, 0, temp, 0, index); + System.arraycopy (cellBackground, index, temp, index+1, columnCount-index-1); + item.cellBackground = temp; + } + Color [] cellForeground = item.cellForeground; + if (cellForeground != null) { + Color [] temp = new Color [columnCount]; + System.arraycopy (cellForeground, 0, temp, 0, index); + System.arraycopy (cellForeground, index, temp, index+1, columnCount-index-1); + item.cellForeground = temp; + } + Font [] cellFont = item.cellFont; + if (cellFont != null) { + Font [] temp = new Font [columnCount]; + System.arraycopy (cellFont, 0, temp, 0, index); + System.arraycopy (cellFont, index, temp, index+1, columnCount-index-1); + item.cellFont = temp; + } +} + +void createHandle () { + NSScrollView scrollWidget = (NSScrollView) new SWTScrollView ().alloc (); + scrollWidget.init (); + scrollWidget.setHasHorizontalScroller ((style & SWT.H_SCROLL) != 0); + scrollWidget.setHasVerticalScroller ((style & SWT.V_SCROLL) != 0); + scrollWidget.setAutohidesScrollers (true); + scrollWidget.setBorderType(hasBorder () ? OS.NSBezelBorder : OS.NSNoBorder); + + NSOutlineView widget = (NSOutlineView) new SWTOutlineView ().alloc (); + /* + * Bug in Cocoa. Calling init, instead of initWithFrame on an NSOutlineView + * cause the NSOutlineView to leak some memory. The work around is to call + * initWithFrame and pass an empty NSRect instead of calling init. + */ + widget.initWithFrame(new NSRect()); + widget.setAllowsMultipleSelection ((style & SWT.MULTI) != 0); + widget.setAllowsColumnReordering (false); + widget.setAutoresizesOutlineColumn (false); + widget.setAutosaveExpandedItems (true); + widget.setDataSource (widget); + widget.setDelegate (widget); + widget.setColumnAutoresizingStyle (OS.NSTableViewNoColumnAutoresizing); + NSSize spacing = new NSSize(); + spacing.width = spacing.height = CELL_GAP; + widget.setIntercellSpacing(spacing); + widget.setDoubleAction (OS.sel_sendDoubleSelection); + if (!hasBorder ()) widget.setFocusRingType (OS.NSFocusRingTypeNone); + + headerView = (NSTableHeaderView)new SWTTableHeaderView ().alloc ().init (); + widget.setHeaderView (null); + + NSString str = NSString.stringWith (""); //$NON-NLS-1$ + if ((style & SWT.CHECK) != 0) { + checkColumn = (NSTableColumn) new NSTableColumn ().alloc (); + checkColumn = checkColumn.initWithIdentifier(NSString.stringWith(String.valueOf(++NEXT_ID))); + checkColumn.headerCell ().setTitle (str); + widget.addTableColumn (checkColumn); + widget.setOutlineTableColumn (checkColumn); + checkColumn.setResizingMask (OS.NSTableColumnNoResizing); + checkColumn.setEditable (false); + int /*long*/ cls = NSButton.cellClass (); /* use our custom cell class */ + buttonCell = new NSButtonCell (OS.class_createInstance (cls, 0)); + buttonCell.init (); + checkColumn.setDataCell (buttonCell); + buttonCell.setButtonType (OS.NSSwitchButton); + buttonCell.setImagePosition (OS.NSImageOnly); + buttonCell.setAllowsMixedState (true); + checkColumn.setWidth (getCheckColumnWidth ()); + } + + firstColumn = (NSTableColumn) new NSTableColumn ().alloc (); + firstColumn = firstColumn.initWithIdentifier(NSString.stringWith(String.valueOf(++NEXT_ID))); + /* + * Feature in Cocoa. If a column's width is too small to show any content + * then outlineView_objectValueForTableColumn_byItem is never invoked to + * query for item values, which is a problem for VIRTUAL Trees. The + * workaround is to ensure that, for 0-column Trees, the internal first + * column always has a minimal width that makes this call come in. + */ + firstColumn.setMinWidth (FIRST_COLUMN_MINIMUM_WIDTH); + firstColumn.setWidth(0); + firstColumn.setResizingMask (OS.NSTableColumnNoResizing); + firstColumn.headerCell ().setTitle (str); + widget.addTableColumn (firstColumn); + widget.setOutlineTableColumn (firstColumn); + dataCell = (NSTextFieldCell)new SWTImageTextCell ().alloc ().init (); + dataCell.setLineBreakMode(OS.NSLineBreakByClipping); + firstColumn.setDataCell (dataCell); + + scrollView = scrollWidget; + view = widget; +} + +void createItem (TreeColumn column, int index) { + if (!(0 <= index && index <= columnCount)) error (SWT.ERROR_INVALID_RANGE); + if (index == 0) { + // first column must be left aligned + column.style &= ~(SWT.LEFT | SWT.RIGHT | SWT.CENTER); + column.style |= SWT.LEFT; + } + if (columnCount == columns.length) { + TreeColumn [] newColumns = new TreeColumn [columnCount + 4]; + System.arraycopy (columns, 0, newColumns, 0, columns.length); + columns = newColumns; + } + NSTableColumn nsColumn; + if (columnCount == 0) { + //TODO - clear attributes, alignment etc. + nsColumn = firstColumn; + nsColumn.setMinWidth (0); + nsColumn.setResizingMask (OS.NSTableColumnUserResizingMask); + firstColumn = null; + } else { + //TODO - set attributes, alignment etc. + NSOutlineView outlineView = (NSOutlineView)view; + NSString str = NSString.stringWith (""); + nsColumn = (NSTableColumn) new NSTableColumn ().alloc (); + nsColumn = nsColumn.initWithIdentifier(NSString.stringWith(String.valueOf(++NEXT_ID))); + nsColumn.setMinWidth(0); + nsColumn.headerCell ().setTitle (str); + outlineView.addTableColumn (nsColumn); + int checkColumn = (style & SWT.CHECK) != 0 ? 1 : 0; + outlineView.moveColumn (columnCount + checkColumn, index + checkColumn); + nsColumn.setDataCell (dataCell); + if (index == 0) { + outlineView.setOutlineTableColumn (nsColumn); + } + } + column.createJNIRef (); + NSTableHeaderCell headerCell = (NSTableHeaderCell)new SWTTableHeaderCell ().alloc ().init (); + nsColumn.setHeaderCell (headerCell); + display.addWidget (headerCell, column); + column.nsColumn = nsColumn; + nsColumn.setWidth (0); + System.arraycopy (columns, index, columns, index + 1, columnCount++ - index); + columns [index] = column; + for (int i = 0; i < itemCount; i++) { + TreeItem item = items [i]; + if (item != null) { + if (columnCount > 1) { + createColumn (item, index); + } + } + } +} + +void createItem (TreeItem item, TreeItem parentItem, int index) { + int count; + TreeItem [] items; + if (parentItem != null) { + count = parentItem.itemCount; + items = parentItem.items; + } else { + count = this.itemCount; + items = this.items; + } + if (index == -1) index = count; + if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE); + if (count == items.length) { + TreeItem [] newItems = new TreeItem [items.length + 4]; + System.arraycopy (items, 0, newItems, 0, items.length); + items = newItems; + if (parentItem != null) { + parentItem.items = items; + } else { + this.items = items; + } + } + System.arraycopy (items, index, items, index + 1, count++ - index); + items [index] = item; + item.items = new TreeItem [4]; + SWTTreeItem handle = (SWTTreeItem) new SWTTreeItem ().alloc ().init (); + item.handle = handle; + item.createJNIRef (); + item.register (); + if (parentItem != null) { + parentItem.itemCount = count; + } else { + this.itemCount = count; + } + ignoreExpand = true; + reloadItem (parentItem, true); + if (parentItem != null && parentItem.itemCount == 1 && parentItem.expanded) { + ((NSOutlineView)view).expandItem (parentItem.handle); + } + ignoreExpand = false; +} + +void createWidget () { + super.createWidget (); + items = new TreeItem [4]; + columns = new TreeColumn [4]; +} + +Color defaultBackground () { + return display.getWidgetColor (SWT.COLOR_LIST_BACKGROUND); +} + +NSFont defaultNSFont () { + return display.outlineViewFont; +} + +Color defaultForeground () { + return display.getWidgetColor (SWT.COLOR_LIST_FOREGROUND); +} + +/** + * Deselects all selected items in the receiver. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void deselectAll () { + checkWidget (); + NSTableView widget = (NSOutlineView) view; + ignoreSelect = true; + widget.deselectAll (null); + ignoreSelect = false; +} + +void deregister () { + super.deregister (); + display.removeWidget (headerView); + display.removeWidget (dataCell); + if (buttonCell != null) display.removeWidget (buttonCell); +} + +/** + * Deselects an item in the receiver. If the item was already + * deselected, it remains deselected. + * + * @param item the item to be deselected + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public void deselect (TreeItem item) { + checkWidget (); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + NSOutlineView widget = (NSOutlineView)view; + int /*long*/ row = widget.rowForItem(item.handle); + ignoreSelect = true; + widget.deselectRow (row); + ignoreSelect = false; +} + +void destroyItem (TreeColumn column) { + int index = 0; + while (index < columnCount) { + if (columns [index] == column) break; + index++; + } + for (int i=0; i<items.length; i++) { + TreeItem item = items [i]; + if (item != null) { + if (columnCount <= 1) { + item.strings = null; + item.images = null; + item.cellBackground = null; + item.cellForeground = null; + item.cellFont = null; + } else { + if (item.strings != null) { + String [] strings = item.strings; + if (index == 0) { + item.text = strings [1] != null ? strings [1] : ""; + } + String [] temp = new String [columnCount - 1]; + System.arraycopy (strings, 0, temp, 0, index); + System.arraycopy (strings, index + 1, temp, index, columnCount - 1 - index); + item.strings = temp; + } else { + if (index == 0) item.text = ""; + } + if (item.images != null) { + Image [] images = item.images; + if (index == 0) item.image = images [1]; + Image [] temp = new Image [columnCount - 1]; + System.arraycopy (images, 0, temp, 0, index); + System.arraycopy (images, index + 1, temp, index, columnCount - 1 - index); + item.images = temp; + } else { + if (index == 0) item.image = null; + } + if (item.cellBackground != null) { + Color [] cellBackground = item.cellBackground; + Color [] temp = new Color [columnCount - 1]; + System.arraycopy (cellBackground, 0, temp, 0, index); + System.arraycopy (cellBackground, index + 1, temp, index, columnCount - 1 - index); + item.cellBackground = temp; + } + if (item.cellForeground != null) { + Color [] cellForeground = item.cellForeground; + Color [] temp = new Color [columnCount - 1]; + System.arraycopy (cellForeground, 0, temp, 0, index); + System.arraycopy (cellForeground, index + 1, temp, index, columnCount - 1 - index); + item.cellForeground = temp; + } + if (item.cellFont != null) { + Font [] cellFont = item.cellFont; + Font [] temp = new Font [columnCount - 1]; + System.arraycopy (cellFont, 0, temp, 0, index); + System.arraycopy (cellFont, index + 1, temp, index, columnCount - 1 - index); + item.cellFont = temp; + } + } + } + } + + int oldIndex = indexOf (column.nsColumn); + + System.arraycopy (columns, index + 1, columns, index, --columnCount - index); + columns [columnCount] = null; + if (columnCount == 0) { + //TODO - reset attributes + firstColumn = column.nsColumn; + firstColumn.retain (); + /* + * Feature in Cocoa. If a column's width is too small to show any content + * then outlineView_objectValueForTableColumn_byItem is never invoked to + * query for item values, which is a problem for VIRTUAL Trees. The + * workaround is to ensure that, for 0-column Trees, the internal first + * column always has a minimal width that makes this call come in. + */ + firstColumn.setMinWidth (FIRST_COLUMN_MINIMUM_WIDTH); + firstColumn.setResizingMask (OS.NSTableColumnNoResizing); + setScrollWidth (); + } else { + if (index == 0) { + ((NSOutlineView)view).setOutlineTableColumn(columns[0].nsColumn); + } + ((NSOutlineView)view).removeTableColumn(column.nsColumn); + } + + NSArray array = ((NSOutlineView)view).tableColumns (); + int arraySize = (int)/*64*/array.count (); + for (int i = oldIndex; i < arraySize; i++) { + int /*long*/ columnId = array.objectAtIndex (i).id; + for (int j = 0; j < columnCount; j++) { + if (columns[j].nsColumn.id == columnId) { + columns [j].sendEvent (SWT.Move); + break; + } + } + } +} + +void destroyItem (TreeItem item) { + int count; + TreeItem[] items; + TreeItem parentItem = item.parentItem; + if (parentItem != null) { + count = parentItem.itemCount; + items = parentItem.items; + } else { + count = this.itemCount; + items = this.items; + } + int index = 0; + while (index < count) { + if (items [index] == item) break; + index++; + } + System.arraycopy (items, index + 1, items, index, --count - index); + items [count] = null; + if (parentItem != null) { + parentItem.itemCount = count; + } else { + this.itemCount = count; + } + reloadItem (parentItem, true); + setScrollWidth (); + if (this.itemCount == 0) imageBounds = null; +} + +boolean dragDetect(int x, int y, boolean filter, boolean[] consume) { + // Let Cocoa determine if a drag is starting and fire the notification when we get the callback. + return false; +} + +void drawInteriorWithFrame_inView (int /*long*/ id, int /*long*/ sel, NSRect rect, int /*long*/ view) { + boolean hooksErase = hooks (SWT.EraseItem); + boolean hooksPaint = hooks (SWT.PaintItem); + boolean hooksMeasure = hooks (SWT.MeasureItem); + + NSTextFieldCell cell = new NSTextFieldCell (id); + + NSOutlineView widget = (NSOutlineView)this.view; + int /*long*/ [] outValue = new int /*long*/ [1]; + OS.object_getInstanceVariable(id, Display.SWT_ROW, outValue); + TreeItem item = (TreeItem) display.getWidget (outValue [0]); + int /*long*/ rowIndex = widget.rowForItem(item.handle); + OS.object_getInstanceVariable(id, Display.SWT_COLUMN, outValue); + int /*long*/ tableColumn = outValue[0]; + int /*long*/ nsColumnIndex = widget.tableColumns().indexOfObjectIdenticalTo(new id(tableColumn)); + int columnIndex = 0; + for (int i=0; i<columnCount; i++) { + if (columns [i].nsColumn.id == tableColumn) { + columnIndex = i; + break; + } + } + + Color background = item.cellBackground != null ? item.cellBackground [columnIndex] : null; + if (background == null) background = item.background; + boolean drawBackground = background != null; + boolean drawForeground = true; + boolean isSelected = cell.isHighlighted(); + boolean drawSelection = isSelected; + boolean hasFocus = hooksErase && hasFocus (); + + Color selectionBackground = null, selectionForeground = null; + if (isSelected && (hooksErase || hooksPaint)) { + selectionForeground = Color.cocoa_new(display, hasFocus ? display.alternateSelectedControlTextColor : display.selectedControlTextColor); + selectionBackground = Color.cocoa_new(display, hasFocus ? display.alternateSelectedControlColor : display.secondarySelectedControlColor); + } + + NSSize contentSize = super.cellSize(id, OS.sel_cellSize); + NSImage image = cell.image(); + if (image != null) contentSize.width += imageBounds.width + IMAGE_GAP; + int contentWidth = (int)Math.ceil (contentSize.width); + NSSize spacing = widget.intercellSpacing(); + int itemHeight = (int)Math.ceil (widget.rowHeight() + spacing.height); + + NSRect cellRect = widget.rectOfColumn (nsColumnIndex); + cellRect.y = rect.y; + cellRect.height = rect.height + spacing.height; + if (columnCount == 0) { + NSRect rowRect = widget.rectOfRow (rowIndex); + cellRect.width = rowRect.width; + } + float /*double*/ offsetX = 0, offsetY = 0; + if (hooksPaint || hooksErase) { + NSRect frameCell = widget.frameOfCellAtColumn(nsColumnIndex, rowIndex); + offsetX = rect.x - frameCell.x; + offsetY = rect.y - frameCell.y; + if (drawExpansion) { + offsetX -= 0.5f; + offsetY -= 0.5f; + } + } + int itemX = (int)(rect.x - offsetX), itemY = (int)(rect.y - offsetY); + NSGraphicsContext context = NSGraphicsContext.currentContext (); + + if (hooksMeasure) { + sendMeasureItem(item, columnIndex, contentSize); + } + + Color userForeground = null; + if (hooksErase) { + context.saveGraphicsState(); + NSAffineTransform transform = NSAffineTransform.transform(); + transform.translateXBy(offsetX, offsetY); + transform.concat(); + + GCData data = new GCData (); + data.paintRect = cellRect; + GC gc = GC.cocoa_new (this, data); + gc.setFont (item.getFont (columnIndex)); + if (isSelected) { + gc.setForeground (selectionForeground); + gc.setBackground (selectionBackground); + } else { + gc.setForeground (item.getForeground (columnIndex)); + gc.setBackground (item.getBackground (columnIndex)); + } + if (!drawExpansion) { + gc.setClipping ((int)(cellRect.x - offsetX), (int)(cellRect.y - offsetY), (int)cellRect.width, (int)cellRect.height); + } + Event event = new Event (); + event.item = item; + event.gc = gc; + event.index = columnIndex; + event.detail = SWT.FOREGROUND; + if (drawBackground) event.detail |= SWT.BACKGROUND; + if (isSelected) event.detail |= SWT.SELECTED; + event.x = (int)cellRect.x; + event.y = (int)cellRect.y; + event.width = (int)cellRect.width; + event.height = (int)cellRect.height; + sendEvent (SWT.EraseItem, event); + if (!event.doit) { + drawForeground = drawBackground = drawSelection = false; + } else { + drawBackground = drawBackground && (event.detail & SWT.BACKGROUND) != 0; + drawForeground = (event.detail & SWT.FOREGROUND) != 0; + drawSelection = drawSelection && (event.detail & SWT.SELECTED) != 0; + } + if (!drawSelection && isSelected) { + userForeground = Color.cocoa_new(display, gc.getForeground().handle); + } + gc.dispose (); + + context.restoreGraphicsState(); + + if (isDisposed ()) return; + if (item.isDisposed ()) return; + + if (drawSelection && ((style & SWT.HIDE_SELECTION) == 0 || hasFocus)) { + cellRect.height -= spacing.height; + callSuper (widget.id, OS.sel_highlightSelectionInClipRect_, cellRect); + cellRect.height += spacing.height; + } + } + + if (drawBackground && !drawSelection) { + context.saveGraphicsState (); + float /*double*/ [] colorRGB = background.handle; + NSColor color = NSColor.colorWithDeviceRed (colorRGB[0], colorRGB[1], colorRGB[2], 1f); + color.setFill (); + NSBezierPath.fillRect (cellRect); + context.restoreGraphicsState (); + } + + if (insertItem != null && !insertItem.isDisposed()) { + context.saveGraphicsState (); + NSRect contentRect = cell.titleRectForBounds (rect); + GCData data = new GCData (); + data.paintRect = contentRect; + GC gc = GC.cocoa_new (this, data); + gc.setClipping ((int)(contentRect.x - offsetX), (int)(contentRect.y - offsetY), (int)contentRect.width, (int)contentRect.height); + Rectangle itemRect = insertItem.getImageBounds(0).union(insertItem.getBounds()); + Rectangle clientRect = getClientArea(); + int x = clientRect.x + clientRect.width; + int posY = insertBefore ? itemRect.y : itemRect.y + itemRect.height - 1; + gc.drawLine(itemRect.x, posY, x, posY); + gc.dispose (); + context.restoreGraphicsState (); + } + + if (drawForeground) { + if ((!drawExpansion || hooksMeasure) && image != null) { + NSRect destRect = new NSRect(); + destRect.x = rect.x + IMAGE_GAP; + destRect.y = rect.y + (float)Math.ceil((rect.height - imageBounds.height) / 2); + destRect.width = imageBounds.width; + destRect.height = imageBounds.height; + NSRect srcRect = new NSRect(); + NSSize size = image.size(); + srcRect.width = size.width; + srcRect.height = size.height; + context.saveGraphicsState(); + NSBezierPath.bezierPathWithRect(rect).addClip(); + NSAffineTransform transform = NSAffineTransform.transform(); + transform.scaleXBy(1, -1); + transform.translateXBy(0, -(destRect.height + 2 * destRect.y)); + transform.concat(); + image.drawInRect(destRect, srcRect, OS.NSCompositeSourceOver, 1); + context.restoreGraphicsState(); + int imageWidth = imageBounds.width + IMAGE_GAP; + rect.x += imageWidth; + rect.width -= imageWidth; + } + cell.setHighlighted (false); + boolean callSuper = false; + if (userForeground != null) { + /* + * Bug in Cocoa. For some reason, it is not possible to change the + * foreground color to black when the cell is highlighted. The text + * still draws white. The fix is to draw the text and not call super. + */ + float /*double*/ [] color = userForeground.handle; + if (color[0] == 0 && color[1] == 0 && color[2] == 0 && color[3] == 1) { + NSMutableAttributedString newStr = new NSMutableAttributedString(cell.attributedStringValue().mutableCopy()); + NSRange range = new NSRange(); + range.length = newStr.length(); + newStr.removeAttribute(OS.NSForegroundColorAttributeName, range); + NSRect newRect = new NSRect(); + newRect.x = rect.x + TEXT_GAP; + newRect.y = rect.y; + newRect.width = rect.width - TEXT_GAP; + newRect.height = rect.height; + NSSize size = newStr.size(); + if (newRect.height > size.height) { + newRect.y += (newRect.height - size.height) / 2; + newRect.height = size.height; + } + newStr.drawInRect(newRect); + newStr.release(); + } else { + NSColor nsColor = NSColor.colorWithDeviceRed(color[0], color[1], color[2], color[3]); + cell.setTextColor(nsColor); + callSuper = true; + } + } else { + callSuper = true; + } + if (callSuper) { + NSAttributedString attrStr = cell.attributedStringValue(); + NSSize size = attrStr.size(); + if (rect.height > size.height) { + rect.y += (rect.height - size.height) / 2; + rect.height = size.height; + } + super.drawInteriorWithFrame_inView(id, sel, rect, view); + } + } + + if (hooksPaint) { + context.saveGraphicsState(); + NSAffineTransform transform = NSAffineTransform.transform(); + transform.translateXBy(offsetX, offsetY); + transform.concat(); + + GCData data = new GCData (); + data.paintRect = cellRect; + GC gc = GC.cocoa_new (this, data); + gc.setFont (item.getFont (columnIndex)); + if (drawSelection) { + gc.setForeground (selectionForeground); + gc.setBackground (selectionBackground); + } else { + gc.setForeground (userForeground != null ? userForeground : item.getForeground (columnIndex)); + gc.setBackground (item.getBackground (columnIndex)); + } + if (!drawExpansion) { + gc.setClipping ((int)(cellRect.x - offsetX), (int)(cellRect.y - offsetY), (int)cellRect.width, (int)cellRect.height); + } + Event event = new Event (); + event.item = item; + event.gc = gc; + event.index = columnIndex; + if (drawForeground) event.detail |= SWT.FOREGROUND; + if (drawBackground) event.detail |= SWT.BACKGROUND; + if (drawSelection) event.detail |= SWT.SELECTED; + event.x = itemX; + event.y = itemY; + event.width = contentWidth; + event.height = itemHeight; + sendEvent (SWT.PaintItem, event); + gc.dispose (); + + context.restoreGraphicsState(); + } +} + +void drawWithExpansionFrame_inView (int /*long*/ id, int /*long*/ sel, NSRect cellFrame, int /*long*/ view) { + drawExpansion = true; + super.drawWithExpansionFrame_inView(id, sel, cellFrame, view); + drawExpansion = false; +} + +void expandItem_expandChildren (int /*long*/ id, int /*long*/ sel, int /*long*/ itemID, boolean children) { + TreeItem item = (TreeItem)display.getWidget(itemID); + if (!ignoreExpand) item.sendExpand (true, children); + ignoreExpand = true; + super.expandItem_expandChildren (id, sel, itemID, children); + ignoreExpand = false; + if (isDisposed() || item.isDisposed()) return; + if (!children) { + ignoreExpand = true; + TreeItem[] items = item.items; + for (int i = 0; i < item.itemCount; i++) { + if (items[i] != null) items[i].updateExpanded (); + } + ignoreExpand = false; + } + setScrollWidth (false, item.items, true); +} + +NSRect expansionFrameWithFrame_inView(int /*long*/ id, int /*long*/ sel, NSRect cellRect, int /*long*/ view) { + if (toolTipText == null) { + NSRect rect = super.expansionFrameWithFrame_inView(id, sel, cellRect, view); + NSCell cell = new NSCell(id); + if (rect.width != 0 && rect.height != 0) { + if (hooks(SWT.MeasureItem)) { + NSSize cellSize = cell.cellSize(); + cellRect.width = cellSize.width; + return cellRect; + } + } else { + NSRect expansionRect; + if (hooks(SWT.MeasureItem)) { + expansionRect = cellRect; + NSSize cellSize = cell.cellSize(); + expansionRect.width = cellSize.width; + } else { + expansionRect = cell.titleRectForBounds(cellRect); + NSSize cellSize = super.cellSize(id, OS.sel_cellSize); + expansionRect.width = cellSize.width; + } + NSRect contentRect = scrollView.contentView().bounds(); + OS.NSIntersectionRect(contentRect, expansionRect, contentRect); + if (!OS.NSEqualRects(expansionRect, contentRect)) { + return expansionRect; + } + } + return rect; + } + return new NSRect(); +} + +Widget findTooltip (NSPoint pt) { + NSTableView widget = (NSTableView)view; + NSTableHeaderView headerView = widget.headerView(); + if (headerView != null) { + pt = headerView.convertPoint_fromView_ (pt, null); + int /*long*/ index = headerView.columnAtPoint (pt); + if (index != -1) { + NSArray nsColumns = widget.tableColumns (); + id nsColumn = nsColumns.objectAtIndex (index); + for (int i = 0; i < columnCount; i++) { + TreeColumn column = columns [i]; + if (column.nsColumn.id == nsColumn.id) { + return column; + } + } + } + } + return super.findTooltip (pt); +} + +int getCheckColumnWidth () { + return (int)checkColumn.dataCell().cellSize().width; +} + +public Rectangle getClientArea () { + checkWidget (); + Rectangle rect = super.getClientArea (); + NSTableHeaderView headerView = ((NSTableView) view).headerView (); + if (headerView != null) { + int height = (int) headerView.bounds ().height; + rect.y -= height; + rect.height += height; + } + return rect; +} + +TreeColumn getColumn (id id) { + for (int i = 0; i < columnCount; i++) { + if (columns[i].nsColumn.id == id.id) { + return columns[i]; + } + } + return null; +} + +/** + * Returns the column at the given, zero-relative index in the + * receiver. Throws an exception if the index is out of range. + * Columns are returned in the order that they were created. + * If no <code>TreeColumn</code>s were created by the programmer, + * this method will throw <code>ERROR_INVALID_RANGE</code> despite + * the fact that a single column of data may be visible in the tree. + * This occurs when the programmer uses the tree like a list, adding + * items but never creating a column. + * + * @param index the index of the column to return + * @return the column at the given index + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Tree#getColumnOrder() + * @see Tree#setColumnOrder(int[]) + * @see TreeColumn#getMoveable() + * @see TreeColumn#setMoveable(boolean) + * @see SWT#Move + * + * @since 3.1 + */ +public TreeColumn getColumn (int index) { + checkWidget (); + if (!(0 <=index && index < columnCount)) error (SWT.ERROR_INVALID_RANGE); + return columns [index]; +} + +/** + * Returns the number of columns contained in the receiver. + * If no <code>TreeColumn</code>s were created by the programmer, + * this value is zero, despite the fact that visually, one column + * of items may be visible. This occurs when the programmer uses + * the tree like a list, adding items but never creating a column. + * + * @return the number of columns + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public int getColumnCount () { + checkWidget (); + return columnCount; +} + +/** + * Returns an array of zero-relative integers that map + * the creation order of the receiver's items to the + * order in which they are currently being displayed. + * <p> + * Specifically, the indices of the returned array represent + * the current visual order of the items, and the contents + * of the array represent the creation order of the items. + * </p><p> + * Note: This is not the actual structure used by the receiver + * to maintain its list of items, so modifying the array will + * not affect the receiver. + * </p> + * + * @return the current visual order of the receiver's items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Tree#setColumnOrder(int[]) + * @see TreeColumn#getMoveable() + * @see TreeColumn#setMoveable(boolean) + * @see SWT#Move + * + * @since 3.2 + */ +public int [] getColumnOrder () { + checkWidget (); + int [] order = new int [columnCount]; + for (int i = 0; i < columnCount; i++) { + TreeColumn column = columns [i]; + int index = indexOf (column.nsColumn); + if ((style & SWT.CHECK) != 0) index -= 1; + order [index] = i; + } + return order; +} + +/** + * Returns an array of <code>TreeColumn</code>s which are the + * columns in the receiver. Columns are returned in the order + * that they were created. If no <code>TreeColumn</code>s were + * created by the programmer, the array is empty, despite the fact + * that visually, one column of items may be visible. This occurs + * when the programmer uses the tree like a list, adding items but + * never creating a column. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its list of items, so modifying the array will + * not affect the receiver. + * </p> + * + * @return the items in the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Tree#getColumnOrder() + * @see Tree#setColumnOrder(int[]) + * @see TreeColumn#getMoveable() + * @see TreeColumn#setMoveable(boolean) + * @see SWT#Move + * + * @since 3.1 + */ +public TreeColumn [] getColumns () { + checkWidget (); + TreeColumn [] result = new TreeColumn [columnCount]; + System.arraycopy (columns, 0, result, 0, columnCount); + return result; +} + +/** + * Returns the width in pixels of a grid line. + * + * @return the width of a grid line in pixels + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public int getGridLineWidth () { + checkWidget (); + return 0; +} + +/** + * Returns the height of the receiver's header + * + * @return the height of the header or zero if the header is not visible + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public int getHeaderHeight () { + checkWidget (); + NSTableHeaderView headerView = ((NSOutlineView) view).headerView (); + if (headerView == null) return 0; + return (int) headerView.bounds ().height; +} + +/** + * Returns <code>true</code> if the receiver's header is visible, + * and <code>false</code> otherwise. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, this method + * may still indicate that it is considered visible even though + * it may not actually be showing. + * </p> + * + * @return the receiver's header's visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public boolean getHeaderVisible () { + checkWidget (); + return ((NSOutlineView) view).headerView () != null; +} + +/** + * Returns the item at the given, zero-relative index in the + * receiver. Throws an exception if the index is out of range. + * + * @param index the index of the item to return + * @return the item at the given index + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public TreeItem getItem (int index) { + checkWidget (); + int count = getItemCount (); + if (index < 0 || index >= count) error (SWT.ERROR_INVALID_RANGE); + return _getItem (null, index, true); +} + +/** + * Returns the item at the given point in the receiver + * or null if no such item exists. The point is in the + * coordinate system of the receiver. + * <p> + * The item that is returned represents an item that could be selected by the user. + * For example, if selection only occurs in items in the first column, then null is + * returned if the point is outside of the item. + * Note that the SWT.FULL_SELECTION style hint, which specifies the selection policy, + * determines the extent of the selection. + * </p> + * + * @param point the point used to locate the item + * @return the item at the given point, or null if the point is not in a selectable item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the point is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public TreeItem getItem (Point point) { + checkWidget (); + if (point == null) error (SWT.ERROR_NULL_ARGUMENT); + checkItems (); + NSOutlineView widget = (NSOutlineView)view; + NSPoint pt = new NSPoint(); + pt.x = point.x; + pt.y = point.y; + int row = (int)/*64*/widget.rowAtPoint(pt); + if (row == -1) return null; + NSRect rect = widget.frameOfOutlineCellAtRow(row); + if (OS.NSPointInRect(pt, rect)) return null; + id id = widget.itemAtRow(row); + Widget item = display.getWidget (id.id); + if (item != null && item instanceof TreeItem) { + return (TreeItem)item; + } + return null; +} + +/** + * Returns the number of items contained in the receiver + * that are direct item children of the receiver. The + * number that is returned is the number of roots in the + * tree. + * + * @return the number of items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getItemCount () { + checkWidget (); + return itemCount; +} + +int getItemCount (TreeItem item) { + return item == null ? itemCount : item.itemCount; +} + +/** + * Returns the height of the area which would be used to + * display <em>one</em> of the items in the tree. + * + * @return the height of one item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getItemHeight () { + checkWidget (); + return (int)((NSOutlineView) view).rowHeight () + CELL_GAP; +} + +/** + * Returns a (possibly empty) array of items contained in the + * receiver that are direct item children of the receiver. These + * are the roots of the tree. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its list of items, so modifying the array will + * not affect the receiver. + * </p> + * + * @return the items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public TreeItem [] getItems () { + checkWidget (); + TreeItem [] result = new TreeItem [itemCount]; + for (int i=0; i<itemCount; i++) { + result [i] = _getItem (null, i, true); + } + return result; +} + +/** + * Returns <code>true</code> if the receiver's lines are visible, + * and <code>false</code> otherwise. Note that some platforms draw + * grid lines while others may draw alternating row colors. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, this method + * may still indicate that it is considered visible even though + * it may not actually be showing. + * </p> + * + * @return the visibility state of the lines + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public boolean getLinesVisible () { + checkWidget (); + return ((NSOutlineView) view).usesAlternatingRowBackgroundColors (); +} + +/** + * Returns the receiver's parent item, which must be a + * <code>TreeItem</code> or null when the receiver is a + * root. + * + * @return the receiver's parent item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public TreeItem getParentItem () { + checkWidget (); + return null; +} + +/** + * Returns an array of <code>TreeItem</code>s that are currently + * selected in the receiver. The order of the items is unspecified. + * An empty array indicates that no items are selected. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its selection, so modifying the array will + * not affect the receiver. + * </p> + * @return an array representing the selection + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public TreeItem [] getSelection () { + checkWidget (); + NSOutlineView widget = (NSOutlineView) view; + if (widget.numberOfSelectedRows () == 0) { + return new TreeItem [0]; + } + NSIndexSet selection = widget.selectedRowIndexes (); + int count = (int)/*64*/selection.count (); + int /*long*/ [] indexBuffer = new int /*long*/ [count]; + selection.getIndexes (indexBuffer, count, 0); + TreeItem [] result = new TreeItem [count]; + for (int i=0; i<count; i++) { + id id = widget.itemAtRow (indexBuffer [i]); + Widget item = display.getWidget (id.id); + if (item != null && item instanceof TreeItem) { + result[i] = (TreeItem) item; + } + } + return result; +} + +/** + * Returns the number of selected items contained in the receiver. + * + * @return the number of selected items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getSelectionCount () { + checkWidget (); + return (int)/*64*/((NSOutlineView) view).numberOfSelectedRows (); +} + +/** + * Returns the column which shows the sort indicator for + * the receiver. The value may be null if no column shows + * the sort indicator. + * + * @return the sort indicator + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setSortColumn(TreeColumn) + * + * @since 3.2 + */ +public TreeColumn getSortColumn () { + checkWidget (); + return sortColumn; +} + +/** + * Returns the direction of the sort indicator for the receiver. + * The value will be one of <code>UP</code>, <code>DOWN</code> + * or <code>NONE</code>. + * + * @return the sort direction + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setSortDirection(int) + * + * @since 3.2 + */ +public int getSortDirection () { + checkWidget (); + return sortDirection; +} + +/** + * Returns the item which is currently at the top of the receiver. + * This item can change when items are expanded, collapsed, scrolled + * or new items are added or removed. + * + * @return the item at the top of the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.1 + */ +public TreeItem getTopItem () { + checkWidget (); + //TODO - partial item at the top + NSRect rect = scrollView.documentVisibleRect (); + NSPoint point = new NSPoint (); + point.x = rect.x; + point.y = rect.y; + NSOutlineView outlineView = (NSOutlineView)view; + int /*long*/ index = outlineView.rowAtPoint (point); + if (index == -1) return null; /* empty */ + id item = outlineView.itemAtRow (index); + return (TreeItem)display.getWidget (item.id); +} + +void highlightSelectionInClipRect(int /*long*/ id, int /*long*/ sel, int /*long*/ rect) { + if (hooks (SWT.EraseItem)) return; + if ((style & SWT.HIDE_SELECTION) != 0 && !hasFocus()) return; + NSRect clipRect = new NSRect (); + OS.memmove (clipRect, rect, NSRect.sizeof); + callSuper (id, sel, clipRect); +} + +int /*long*/ hitTestForEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ event, NSRect rect, int /*long*/ controlView) { + /* + * For some reason, the cell class needs to implement hitTestForEvent:inRect:ofView:, + * otherwise the double action selector is not called properly. + */ + return callSuper(id, sel, event, rect, controlView); +} + +int /*long*/ image (int /*long*/ id, int /*long*/ sel) { + int /*long*/ [] image = new int /*long*/ [1]; + OS.object_getInstanceVariable(id, Display.SWT_IMAGE, image); + return image[0]; +} + +NSRect imageRectForBounds (int /*long*/ id, int /*long*/ sel, NSRect cellFrame) { + NSImage image = new NSCell(id).image(); + if (image != null) { + cellFrame.x += IMAGE_GAP; + cellFrame.width = imageBounds.width; + cellFrame.height = imageBounds.height; + } + return cellFrame; +} + +int indexOf (NSTableColumn column) { + return (int)/*64*/((NSTableView)view).tableColumns().indexOfObjectIdenticalTo(column); +} + +/** + * Searches the receiver's list starting at the first column + * (index 0) until a column is found that is equal to the + * argument, and returns the index of that column. If no column + * is found, returns -1. + * + * @param column the search column + * @return the index of the column + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the column is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public int indexOf (TreeColumn column) { + checkWidget (); + if (column == null) error (SWT.ERROR_NULL_ARGUMENT); + if (column.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + for (int i=0; i<columnCount; i++) { + if (columns [i] == column) return i; + } + return -1; +} + +/** + * Searches the receiver's list starting at the first item + * (index 0) until an item is found that is equal to the + * argument, and returns the index of that item. If no item + * is found, returns -1. + * + * @param item the search item + * @return the index of the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public int indexOf (TreeItem item) { + checkWidget (); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + if (item.parentItem != null) return -1; + for (int i = 0; i < itemCount; i++) { + if (item == items[i]) return i; + } + return -1; +} + +boolean isTrim (NSView view) { + if (super.isTrim (view)) return true; + return view.id == headerView.id; +} + +int /*long*/ menuForEvent(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (id != headerView.id) { + /* + * Feature in Cocoa: Table views do not change the selection when the user + * right-clicks or control-clicks on an NSTableView or its subclasses. Fix is to select the + * clicked-on row ourselves. + */ + NSEvent event = new NSEvent(theEvent); + NSOutlineView tree = (NSOutlineView)view; + + // get the current selections for the outline view. + NSIndexSet selectedRowIndexes = tree.selectedRowIndexes(); + + // select the row that was clicked before showing the menu for the event + NSPoint mousePoint = view.convertPoint_fromView_(event.locationInWindow(), null); + int /*long*/ row = tree.rowAtPoint(mousePoint); + + // figure out if the row that was just clicked on is currently selected + if (selectedRowIndexes.containsIndex(row) == false) { + NSIndexSet set = (NSIndexSet)new NSIndexSet().alloc(); + set = set.initWithIndex(row); + tree.selectRowIndexes (set, false); + set.release(); + } + // else that row is currently selected, so don't change anything. + } + + return super.menuForEvent(id, sel, theEvent); +} + +void mouseDown (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + if (headerView != null && id == headerView.id) { + NSTableView widget = (NSTableView)view; + widget.setAllowsColumnReordering(false); + NSPoint pt = headerView.convertPoint_fromView_(new NSEvent(theEvent).locationInWindow(), null); + int /*long*/ nsIndex = headerView.columnAtPoint(pt); + if (nsIndex != -1) { + id nsColumn = widget.tableColumns().objectAtIndex(nsIndex); + for (int i = 0; i < columnCount; i++) { + if (columns[i].nsColumn.id == nsColumn.id) { + widget.setAllowsColumnReordering(columns[i].movable); + break; + } + } + } + } + else if (id == view.id) { + // Bug/feature in Cocoa: If the tree has a context menu we just set it visible instead of returning + // it from menuForEvent:. This has the side effect, however, of sending control-click to the NSTableView, + // which is interpreted as a single click that clears the selection. Fix is to ignore control-click, + NSEvent event = new NSEvent(theEvent); + if ((event.modifierFlags() & OS.NSControlKeyMask) != 0) return; + } + super.mouseDown(id, sel, theEvent); +} + +/* + * Feature in Cocoa. If a checkbox is in multi-state mode, nextState cycles + * from off to mixed to on and back to off again. This will cause the on state + * to momentarily appear while clicking on the checkbox. To avoid this, + * override [NSCell nextState] to go directly to the desired state. + */ +int /*long*/ nextState (int /*long*/ id, int /*long*/ sel) { + NSOutlineView outlineView = (NSOutlineView)view; + int index = (int)/*64*/outlineView.selectedRow (); + TreeItem item = (TreeItem)display.getWidget (outlineView.itemAtRow (index).id); + if (item.grayed) { + return item.checked ? OS.NSOffState : OS.NSMixedState; + } + return item.checked ? OS.NSOffState : OS.NSOnState; +} + +int /*long*/ outlineView_child_ofItem (int /*long*/ id, int /*long*/ sel, int /*long*/ outlineView, int /*long*/ index, int /*long*/ itemID) { + TreeItem parent = (TreeItem) display.getWidget (itemID); + TreeItem item = _getItem (parent, (int)/*64*/index, true); + return item.handle.id; +} + +void outlineView_didClickTableColumn (int /*long*/ id, int /*long*/ sel, int /*long*/ outlineView, int /*long*/ tableColumn) { + TreeColumn column = getColumn (new id (tableColumn)); + if (column == null) return; /* either CHECK column or firstColumn in 0-column Tree */ + column.postEvent (SWT.Selection); +} + +int /*long*/ outlineView_objectValueForTableColumn_byItem (int /*long*/ id, int /*long*/ sel, int /*long*/ outlineView, int /*long*/ tableColumn, int /*long*/ itemID) { + TreeItem item = (TreeItem) display.getWidget (itemID); + checkData (item); + if (checkColumn != null && tableColumn == checkColumn.id) { + NSNumber value; + if (item.checked && item.grayed) { + value = NSNumber.numberWithInt (OS.NSMixedState); + } else { + value = NSNumber.numberWithInt (item.checked ? OS.NSOnState : OS.NSOffState); + } + return value.id; + } + for (int i=0; i<columnCount; i++) { + if (columns [i].nsColumn.id == tableColumn) { + return item.createString (i).id; + } + } + return item.createString (0).id; +} + +boolean outlineView_isItemExpandable (int /*long*/ id, int /*long*/ sel, int /*long*/ outlineView, int /*long*/ item) { + if (item == 0) return true; + return ((TreeItem) display.getWidget (item)).itemCount != 0; +} + +int /*long*/ outlineView_numberOfChildrenOfItem (int /*long*/ id, int /*long*/ sel, int /*long*/ outlineView, int /*long*/ item) { + if (item == 0) return itemCount; + return ((TreeItem) display.getWidget (item)).itemCount; +} + +void outlineView_willDisplayCell_forTableColumn_item (int /*long*/ id, int /*long*/ sel, int /*long*/ outlineView, int /*long*/ cell, int /*long*/ tableColumn, int /*long*/ itemID) { + if (checkColumn != null && tableColumn == checkColumn.id) return; + TreeItem item = (TreeItem) display.getWidget(itemID); + int index = 0; + for (int i=0; i<columnCount; i++) { + if (columns [i].nsColumn.id == tableColumn) { + index = i; + break; + } + } + NSTextFieldCell textCell = new NSTextFieldCell (cell); + OS.object_setInstanceVariable(cell, Display.SWT_ROW, itemID); + OS.object_setInstanceVariable(cell, Display.SWT_COLUMN, tableColumn); + Image image = index == 0 ? item.image : (item.images == null ? null : item.images [index]); + textCell.setImage (image != null ? image.handle : null); + NSColor color; + if (textCell.isEnabled()) { + if (textCell.isHighlighted ()) { + color = NSColor.selectedControlTextColor(); + } else { + Color foreground = item.cellForeground != null ? item.cellForeground [index] : null; + if (foreground == null) foreground = item.foreground; + if (foreground == null) foreground = getForegroundColor (); + color = NSColor.colorWithDeviceRed (foreground.handle [0], foreground.handle [1], foreground.handle [2], 1); + } + } else { + color = NSColor.disabledControlTextColor(); + } + int alignment = OS.NSLeftTextAlignment; + if (columnCount > 0) { + int style = columns [index].style; + if ((style & SWT.CENTER) != 0) { + alignment = OS.NSCenterTextAlignment; + } else if ((style & SWT.RIGHT) != 0) { + alignment = OS.NSRightTextAlignment; + } + } + Font font = item.cellFont != null ? item.cellFont [index] : null; + if (font == null) font = item.font; + if (font == null) font = this.font; + if (font == null) font = defaultFont (); + if (font.extraTraits != 0) { + NSMutableDictionary dict = ((NSMutableDictionary)new NSMutableDictionary().alloc()).initWithCapacity(5); + dict.setObject (color, OS.NSForegroundColorAttributeName); + dict.setObject (font.handle, OS.NSFontAttributeName); + addTraits(dict, font); + NSMutableParagraphStyle paragraphStyle = (NSMutableParagraphStyle)new NSMutableParagraphStyle ().alloc ().init (); + paragraphStyle.setLineBreakMode (OS.NSLineBreakByClipping); + paragraphStyle.setAlignment (alignment); + dict.setObject (paragraphStyle, OS.NSParagraphStyleAttributeName); + paragraphStyle.release (); + NSAttributedString attribStr = ((NSAttributedString) new NSAttributedString ().alloc ()).initWithString (textCell.title(), dict); + textCell.setAttributedStringValue(attribStr); + attribStr.release(); + dict.release(); + } else { + textCell.setFont(font.handle); + textCell.setTextColor(color); + textCell.setAlignment (alignment); + } +} + +void outlineViewColumnDidMove (int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { + NSNotification notification = new NSNotification (aNotification); + NSDictionary userInfo = notification.userInfo (); + id nsOldIndex = userInfo.valueForKey (NSString.stringWith ("NSOldColumn")); //$NON-NLS-1$ + id nsNewIndex = userInfo.valueForKey (NSString.stringWith ("NSNewColumn")); //$NON-NLS-1$ + int oldIndex = new NSNumber (nsOldIndex).intValue (); + int newIndex = new NSNumber (nsNewIndex).intValue (); + int startIndex = Math.min (oldIndex, newIndex); + int endIndex = Math.max (oldIndex, newIndex); + NSOutlineView outlineView = (NSOutlineView)view; + NSArray nsColumns = outlineView.tableColumns (); + for (int i = startIndex; i <= endIndex; i++) { + id columnId = nsColumns.objectAtIndex (i); + TreeColumn column = getColumn (columnId); + if (column != null) { + column.sendEvent (SWT.Move); + if (isDisposed ()) return; + } + } +} + +void outlineViewColumnDidResize (int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { + NSNotification notification = new NSNotification (aNotification); + NSDictionary userInfo = notification.userInfo (); + id columnId = userInfo.valueForKey (NSString.stringWith ("NSTableColumn")); //$NON-NLS-1$ + TreeColumn column = getColumn (columnId); + if (column == null) return; /* either CHECK column or firstColumn in 0-column Tree */ + + column.sendEvent (SWT.Resize); + if (isDisposed ()) return; + + NSOutlineView outlineView = (NSOutlineView)view; + int index = indexOf (column.nsColumn); + if (index == -1) return; /* column was disposed in Resize callback */ + + NSArray nsColumns = outlineView.tableColumns (); + int columnCount = (int)/*64*/outlineView.numberOfColumns (); + for (int i = index + 1; i < columnCount; i++) { + columnId = nsColumns.objectAtIndex (i); + column = getColumn (columnId); + if (column != null) { + column.sendEvent (SWT.Move); + if (isDisposed ()) return; + } + } +} + +void outlineViewSelectionDidChange (int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { + if (ignoreSelect) return; + NSOutlineView widget = (NSOutlineView) view; + int row = (int)/*64*/widget.selectedRow (); + if (row == -1) + postEvent (SWT.Selection); + else { + id _id = widget.itemAtRow (row); + TreeItem item = (TreeItem) display.getWidget (_id.id); + Event event = new Event (); + event.item = item; + event.index = row; + postEvent (SWT.Selection, event); + } +} + +void outlineView_setObjectValue_forTableColumn_byItem (int /*long*/ id, int /*long*/ sel, int /*long*/ outlineView, int /*long*/ object, int /*long*/ tableColumn, int /*long*/ itemID) { + if (checkColumn != null && tableColumn == checkColumn.id) { + TreeItem item = (TreeItem) display.getWidget (itemID); + item.checked = !item.checked; + Event event = new Event (); + event.detail = SWT.CHECK; + event.item = item; + postEvent (SWT.Selection, event); + item.redraw (-1); + } +} + +boolean outlineView_writeItems_toPasteboard(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1, int /*long*/ arg2) { + return sendMouseEvent(NSApplication.sharedApplication().currentEvent(), SWT.DragDetect, true); +} + +void register () { + super.register (); + display.addWidget (headerView, this); + display.addWidget (dataCell, this); + if (buttonCell != null) display.addWidget (buttonCell, this); +} + +void releaseChildren (boolean destroy) { + for (int i=0; i<items.length; i++) { + TreeItem item = items [i]; + if (item != null && !item.isDisposed ()) { + item.release (false); + } + } + items = null; + if (columns != null) { + for (int i=0; i<columnCount; i++) { + TreeColumn column = columns [i]; + if (column != null && !column.isDisposed ()) { + column.release (false); + } + } + columns = null; + } + super.releaseChildren (destroy); +} + +void releaseHandle () { + super.releaseHandle (); + if (headerView != null) headerView.release (); + headerView = null; + if (firstColumn != null) firstColumn.release (); + firstColumn = null; + if (checkColumn != null) checkColumn.release (); + checkColumn = null; + if (dataCell != null) dataCell.release (); + dataCell = null; + if (buttonCell != null) buttonCell.release(); + buttonCell = null; +} + +void releaseWidget () { + super.releaseWidget (); + sortColumn = null; +} + +void reloadItem (TreeItem item, boolean recurse) { + if (getDrawing()) { + NSOutlineView widget = (NSOutlineView)view; + TreeItem[] selectedItems = getSelection (); + if (item != null) { + widget.reloadItem (item.handle, recurse); + } else { + widget.reloadData (); + } + selectItems (selectedItems, true); + } else { + reloadPending = true; + } +} + +/** + * Removes all of the items from the receiver. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void removeAll () { + checkWidget (); + for (int i=0; i<items.length; i++) { + TreeItem item = items [i]; + if (item != null && !item.isDisposed ()) item.release (false); + } + items = new TreeItem [4]; + itemCount = 0; + imageBounds = null; + ((NSOutlineView) view).reloadData (); + setScrollWidth (); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the user changes the receiver's selection. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener (SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + eventTable.unhook (SWT.Selection, listener); + eventTable.unhook (SWT.DefaultSelection, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when items in the receiver are expanded or collapsed. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see TreeListener + * @see #addTreeListener + */ +public void removeTreeListener (TreeListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Expand, listener); + eventTable.unhook (SWT.Collapse, listener); +} + +void setImage (int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + OS.object_setInstanceVariable(id, Display.SWT_IMAGE, arg0); +} + +/** + * Display a mark indicating the point at which an item will be inserted. + * The drop insert item has a visual hint to show where a dragged item + * will be inserted when dropped on the tree. + * + * @param item the insert item. Null will clear the insertion mark. + * @param before true places the insert mark above 'item'. false places + * the insert mark below 'item'. + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setInsertMark (TreeItem item, boolean before) { + checkWidget (); + if (item != null && item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + TreeItem oldMark = insertItem; + insertItem = item; + insertBefore = before; + if (oldMark != null && !oldMark.isDisposed()) oldMark.redraw (-1); + if (item != null) item.redraw (-1); +} + +/** + * Selects all of the items in the receiver. + * <p> + * If the receiver is single-select, do nothing. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void selectAll () { + checkWidget (); + if ((style & SWT.SINGLE) != 0) return; + checkItems (); + NSOutlineView widget = (NSOutlineView) view; + ignoreSelect = true; + widget.selectAll (null); + ignoreSelect = false; +} + +/** + * Selects an item in the receiver. If the item was already + * selected, it remains selected. + * + * @param item the item to be selected + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.4 + */ +public void select (TreeItem item) { + checkWidget (); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + checkItems (); + showItem (item); + NSOutlineView outlineView = (NSOutlineView) view; + int /*long*/ row = outlineView.rowForItem (item.handle); + NSIndexSet set = (NSIndexSet)new NSIndexSet().alloc(); + set = set.initWithIndex(row); + ignoreSelect = true; + outlineView.selectRowIndexes (set, false); + ignoreSelect = false; + set.release(); +} + +void sendDoubleSelection() { + NSOutlineView outlineView = (NSOutlineView)view; + int rowIndex = (int)/*64*/outlineView.clickedRow (); + if (rowIndex != -1) { + if ((style & SWT.CHECK) != 0) { + NSArray columns = outlineView.tableColumns (); + int columnIndex = (int)/*64*/outlineView.clickedColumn (); + id column = columns.objectAtIndex (columnIndex); + if (column.id == checkColumn.id) return; + } + TreeItem item = (TreeItem) display.getWidget (outlineView.itemAtRow (rowIndex).id); + Event event = new Event (); + event.item = item; + postEvent (SWT.DefaultSelection, event); + } +} + +boolean sendKeyEvent (NSEvent nsEvent, int type) { + boolean result = super.sendKeyEvent (nsEvent, type); + if (!result) return result; + if (type != SWT.KeyDown) return result; + short keyCode = nsEvent.keyCode (); + switch (keyCode) { + case 76: /* KP Enter */ + case 36: { /* Return */ + postEvent (SWT.DefaultSelection); + break; + } + } + return result; +} + +void sendMeasureItem (TreeItem item, int columnIndex, NSSize size) { + NSOutlineView widget = (NSOutlineView)this.view; + int contentWidth = (int)Math.ceil (size.width); + NSSize spacing = widget.intercellSpacing(); + int itemHeight = (int)Math.ceil (widget.rowHeight() + spacing.height); + GCData data = new GCData (); + data.paintRect = widget.frame (); + GC gc = GC.cocoa_new (this, data); + gc.setFont (item.getFont (columnIndex)); + Event event = new Event (); + event.item = item; + event.gc = gc; + event.index = columnIndex; + event.width = contentWidth; + event.height = itemHeight; + sendEvent (SWT.MeasureItem, event); + gc.dispose (); + if (!isDisposed () && !item.isDisposed ()) { + size.width = event.width; + size.height = event.height; + if (itemHeight < event.height) { + widget.setRowHeight (event.height); + } + if (contentWidth != event.width) { + if (columnCount == 0 && columnIndex == 0) { + item.width = event.width; + item.width += widget.indentationPerLevel () * (1 + widget.levelForItem (item.handle)); + if (setScrollWidth (item)) { + widget.setNeedsDisplay(true); + } + } + } + } +} + +void selectItems (TreeItem[] items, boolean ignoreDisposed) { + NSOutlineView outlineView = (NSOutlineView) view; + NSMutableIndexSet set = (NSMutableIndexSet) new NSMutableIndexSet ().alloc ().init (); + int length = items.length; + for (int i=0; i<length; i++) { + if (items [i] != null) { + if (items [i].isDisposed ()) { + if (ignoreDisposed) continue; + error (SWT.ERROR_INVALID_ARGUMENT); + } + TreeItem item = items [i]; + if (!ignoreDisposed) showItem (items [i], false); + set.addIndex (outlineView.rowForItem (item.handle)); + } + } + ignoreSelect = true; + outlineView.selectRowIndexes (set, false); + ignoreSelect = false; + set.release(); +} + +NSRect titleRectForBounds (int /*long*/ id, int /*long*/ sel, NSRect cellFrame) { + NSImage image = new NSCell(id).image(); + if (image != null) { + int imageWidth = imageBounds.width + IMAGE_GAP; + cellFrame.x += imageWidth; + cellFrame.width -= imageWidth; + } + return cellFrame; +} + +void updateBackground () { + NSColor nsColor = null; + if (backgroundImage != null) { + nsColor = NSColor.colorWithPatternImage(backgroundImage.handle); + } else if (background != null) { + nsColor = NSColor.colorWithDeviceRed(background[0], background[1], background[2], background[3]); + } + ((NSOutlineView) view).setBackgroundColor (nsColor); +} + +/** + * Sets the order that the items in the receiver should + * be displayed in to the given argument which is described + * in terms of the zero-relative ordering of when the items + * were added. + * + * @param order the new order to display the items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item order is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li> + * </ul> + * + * @see Tree#getColumnOrder() + * @see TreeColumn#getMoveable() + * @see TreeColumn#setMoveable(boolean) + * @see SWT#Move + * + * @since 3.2 + */ +public void setColumnOrder (int [] order) { + checkWidget (); + if (order == null) error (SWT.ERROR_NULL_ARGUMENT); + if (columnCount == 0) { + if (order.length != 0) error (SWT.ERROR_INVALID_ARGUMENT); + return; + } + if (order.length != columnCount) error (SWT.ERROR_INVALID_ARGUMENT); + int [] oldOrder = getColumnOrder (); + boolean reorder = false; + boolean [] seen = new boolean [columnCount]; + for (int i=0; i<order.length; i++) { + int index = order [i]; + if (index < 0 || index >= columnCount) error (SWT.ERROR_INVALID_ARGUMENT); + if (seen [index]) error (SWT.ERROR_INVALID_ARGUMENT); + seen [index] = true; + if (order [i] != oldOrder [i]) reorder = true; + } + if (reorder) { + NSOutlineView outlineView = (NSOutlineView)view; + int [] oldX = new int [oldOrder.length]; + int check = (style & SWT.CHECK) != 0 ? 1 : 0; + for (int i=0; i<oldOrder.length; i++) { + int index = oldOrder[i]; + oldX [index] = (int)outlineView.rectOfColumn (i + check).x; + } + int [] newX = new int [order.length]; + for (int i=0; i<order.length; i++) { + int index = order [i]; + TreeColumn column = columns[index]; + int oldIndex = indexOf (column.nsColumn); + int newIndex = i + check; + outlineView.moveColumn (oldIndex, newIndex); + newX [index] = (int)outlineView.rectOfColumn (newIndex).x; + } + + TreeColumn[] newColumns = new TreeColumn [columnCount]; + System.arraycopy (columns, 0, newColumns, 0, columnCount); + for (int i=0; i<columnCount; i++) { + TreeColumn column = newColumns [i]; + if (!column.isDisposed ()) { + if (newX [i] != oldX [i]) { + column.sendEvent (SWT.Move); + } + } + } + } +} + +void setFont (NSFont font) { + super.setFont (font); + setItemHeight (null, font, !hooks (SWT.MeasureItem)); + view.setNeedsDisplay (true); + clearCachedWidth (items); + setScrollWidth (); +} + +/** + * Marks the receiver's header as visible if the argument is <code>true</code>, + * and marks it invisible otherwise. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, marking + * it visible may not actually cause it to be displayed. + * </p> + * + * @param show the new visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public void setHeaderVisible (boolean show) { + checkWidget (); + ((NSOutlineView) view).setHeaderView (show ? headerView : null); +} + +/** + * Sets the number of root-level items contained in the receiver. + * + * @param count the number of items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public void setItemCount (int count) { + checkWidget (); + checkItems (); + count = Math.max (0, count); + setItemCount (null, count); +} + +void setItemCount (TreeItem parentItem, int count) { + int itemCount = getItemCount (parentItem); + if (count == itemCount) return; + NSOutlineView widget = (NSOutlineView) view; + int length = Math.max (4, (count + 3) / 4 * 4); + TreeItem [] children = parentItem == null ? items : parentItem.items; + boolean expanded = parentItem == null || parentItem.getExpanded(); + if (count < itemCount) { + /* + * Note that the item count has to be updated before the call to reloadItem(), but + * the items have to be released after. + */ + if (parentItem == null) { + this.itemCount = count; + } else { + parentItem.itemCount = count; + } + /* + * Bug in Cocoa. When removing selected items from an NSOutlineView, the selection + * is not properly updated. The fix is to ensure that the item and its subitems + * are deselected before the item is removed by the reloadItem call. + */ + if (expanded) { + for (int index = count; index < itemCount; index ++) { + TreeItem item = children [index]; + if (item != null && !item.isDisposed ()) item.clearSelection (); + } + } + TreeItem[] selectedItems = getSelection (); + widget.reloadItem (parentItem != null ? parentItem.handle : null, expanded); + selectItems (selectedItems, true); + for (int index = count; index < itemCount; index ++) { + TreeItem item = children [index]; + if (item != null && !item.isDisposed()) item.release (false); + } + TreeItem [] newItems = new TreeItem [length]; + if (children != null) { + System.arraycopy (children, 0, newItems, 0, count); + } + children = newItems; + if (parentItem == null) { + this.items = newItems; + } else { + parentItem.items = newItems; + } + } else { + if ((style & SWT.VIRTUAL) == 0) { + for (int i=itemCount; i<count; i++) { + new TreeItem (this, parentItem, SWT.NONE, i, true); + } + } else { + TreeItem [] newItems = new TreeItem [length]; + if (children != null) { + System.arraycopy (children, 0, newItems, 0, itemCount); + } + children = newItems; + if (parentItem == null) { + this.items = newItems; + this.itemCount = count; + } else { + parentItem.items = newItems; + parentItem.itemCount = count; + } + TreeItem[] selectedItems = getSelection (); + widget.reloadItem (parentItem != null ? parentItem.handle : null, expanded); + selectItems (selectedItems, true); + + if (parentItem != null && itemCount == 0 && parentItem.expanded) { + ignoreExpand = true; + widget.expandItem (parentItem.handle); + ignoreExpand = false; + } + } + } +} + +/*public*/ void setItemHeight (int itemHeight) { + checkWidget (); + if (itemHeight < -1) error (SWT.ERROR_INVALID_ARGUMENT); + if (itemHeight == -1) { + setItemHeight (null, null, true); + } else { + ((NSOutlineView)view).setRowHeight (itemHeight); + } +} + +void setItemHeight (Image image, NSFont font, boolean set) { + if (font == null) font = getFont ().handle; + float /*double*/ ascent = font.ascender (); + float /*double*/ descent = -font.descender () + font.leading (); + int height = (int)Math.ceil (ascent + descent) + 1; + Rectangle bounds = image != null ? image.getBounds () : imageBounds; + if (bounds != null) { + imageBounds = bounds; + height = Math.max (height, bounds.height); + } + NSTableView widget = (NSTableView)view; + if (set || widget.rowHeight () < height) { + widget.setRowHeight (height); + } +} + +/** + * Marks the receiver's lines as visible if the argument is <code>true</code>, + * and marks it invisible otherwise. Note that some platforms draw + * grid lines while others may draw alternating row colors. + * <p> + * If one of the receiver's ancestors is not visible or some + * other condition makes the receiver not visible, marking + * it visible may not actually cause it to be displayed. + * </p> + * + * @param show the new visibility state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public void setLinesVisible (boolean show) { + checkWidget (); + ((NSOutlineView) view).setUsesAlternatingRowBackgroundColors (show); +} + +public void setRedraw (boolean redraw) { + checkWidget (); + super.setRedraw (redraw); + if (redraw && drawCount == 0) { + checkItems (); + setScrollWidth (); + } +} + +boolean setScrollWidth () { + return setScrollWidth (true, items, true); +} + +boolean setScrollWidth (boolean set, TreeItem[] items, boolean recurse) { + if (items == null) return false; + if (ignoreRedraw || !getDrawing()) return false; + if (columnCount != 0) return false; + GC gc = new GC (this); + int newWidth = calculateWidth (items, 0, gc, recurse); + gc.dispose (); + if (!set) { + int oldWidth = (int)firstColumn.width (); + if (oldWidth >= newWidth) return false; + } + firstColumn.setWidth (newWidth); + if (horizontalBar != null && horizontalBar.view != null) redrawWidget (horizontalBar.view, false); + return true; +} + +boolean setScrollWidth (TreeItem item) { + if (ignoreRedraw || !getDrawing()) return false; + if (columnCount != 0) return false; + TreeItem parentItem = item.parentItem; + if (parentItem != null && !parentItem.getExpanded ()) return false; + GC gc = new GC (this); + int newWidth = item.calculateWidth (0, gc); + gc.dispose (); + int oldWidth = (int)firstColumn.width (); + if (oldWidth < newWidth) { + firstColumn.setWidth (newWidth); + if (horizontalBar != null && horizontalBar.view != null) redrawWidget (horizontalBar.view, false); + return true; + } + return false; +} + +/** + * Sets the receiver's selection to the given item. + * The current selection is cleared before the new item is selected. + * <p> + * If the item is not in the receiver, then it is ignored. + * </p> + * + * @param item the item to select + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public void setSelection (TreeItem item) { + checkWidget (); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + setSelection (new TreeItem [] {item}); +} + +/** + * Sets the receiver's selection to be the given array of items. + * The current selection is cleared before the new items are selected. + * <p> + * Items that are not in the receiver are ignored. + * If the receiver is single-select and multiple items are specified, + * then all items are ignored. + * </p> + * + * @param items the array of items + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the array of items is null</li> + * <li>ERROR_INVALID_ARGUMENT - if one of the items has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Tree#deselectAll() + */ +public void setSelection (TreeItem [] items) { + checkWidget (); + if (items == null) error (SWT.ERROR_NULL_ARGUMENT); + checkItems (); + deselectAll (); + int length = items.length; + if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return; + selectItems (items, false); + if (items.length > 0) { + for (int i = 0; i < items.length; i++) { + TreeItem item = items[i]; + if (item != null) { + showItem(item, true); + break; + } + } + } +} + +void setSmallSize () { + if (checkColumn == null) return; + checkColumn.dataCell ().setControlSize (OS.NSSmallControlSize); + checkColumn.setWidth (getCheckColumnWidth ()); +} + +/** + * Sets the column used by the sort indicator for the receiver. A null + * value will clear the sort indicator. The current sort column is cleared + * before the new column is set. + * + * @param column the column used by the sort indicator or <code>null</code> + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the column is disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public void setSortColumn (TreeColumn column) { + checkWidget (); + if (column != null && column.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + if (column == sortColumn) return; + sortColumn = column; + ((NSOutlineView)view).setHighlightedTableColumn (column == null ? null : column.nsColumn); +} + +/** + * Sets the direction of the sort indicator for the receiver. The value + * can be one of <code>UP</code>, <code>DOWN</code> or <code>NONE</code>. + * + * @param direction the direction of the sort indicator + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public void setSortDirection (int direction) { + checkWidget (); + if (direction != SWT.UP && direction != SWT.DOWN && direction != SWT.NONE) return; + if (direction == sortDirection) return; + sortDirection = direction; + if (sortColumn == null) return; + NSTableHeaderView headerView = ((NSOutlineView)view).headerView (); + if (headerView == null) return; + int index = indexOf (sortColumn.nsColumn); + NSRect rect = headerView.headerRectOfColumn (index); + headerView.setNeedsDisplayInRect (rect); +} + +/** + * Sets the item which is currently at the top of the receiver. + * This item can change when items are expanded, collapsed, scrolled + * or new items are added or removed. + * + * @param item the item to be shown + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Tree#getTopItem() + * + * @since 2.1 + */ +public void setTopItem (TreeItem item) { + checkWidget(); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + checkItems (); + showItem (item, false); + NSOutlineView widget = (NSOutlineView) view; + int /*long*/ row = widget.rowForItem (item.handle); + if (row == -1) return; + NSPoint pt = new NSPoint(); + pt.x = scrollView.contentView().bounds().x; + pt.y = widget.frameOfCellAtColumn(0, row).y; + view.scrollPoint(pt); +} + +/** + * Shows the column. If the column is already showing in the receiver, + * this method simply returns. Otherwise, the columns are scrolled until + * the column is visible. + * + * @param column the column to be shown + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public void showColumn (TreeColumn column) { + checkWidget (); + if (column == null) error (SWT.ERROR_NULL_ARGUMENT); + if (column.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT); + if (column.parent != this) return; + if (columnCount <= 1) return; + int index = indexOf (column.nsColumn); + if (!(0 <= index && index < columnCount + ((style & SWT.CHECK) != 0 ? 1 : 0))) return; + ((NSOutlineView)view).scrollColumnToVisible (index); +} + +/** + * Shows the item. If the item is already showing in the receiver, + * this method simply returns. Otherwise, the items are scrolled + * and expanded until the item is visible. + * + * @param item the item to be shown + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Tree#showSelection() + */ +public void showItem (TreeItem item) { + checkWidget (); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + checkItems (); + showItem (item, true); +} + +void showItem (TreeItem item, boolean scroll) { + TreeItem parentItem = item.parentItem; + if (parentItem != null) { + showItem (parentItem, false); + parentItem.setExpanded (true); + } + if (scroll) { + NSOutlineView outlineView = (NSOutlineView) view; + outlineView.scrollRowToVisible (outlineView.rowForItem (item.handle)); + } +} + +/** + * Shows the selection. If the selection is already showing in the receiver, + * this method simply returns. Otherwise, the items are scrolled until + * the selection is visible. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Tree#showItem(TreeItem) + */ +public void showSelection () { + checkWidget (); + checkItems (); + //TODO - optimize + TreeItem [] selection = getSelection (); + if (selection.length > 0) { + checkData(selection [0]); + showItem (selection [0], true); + } +} + +void updateCursorRects (boolean enabled) { + super.updateCursorRects (enabled); + if (headerView == null) return; + updateCursorRects (enabled, headerView); +} + +} + diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TreeColumn.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TreeColumn.java new file mode 100755 index 0000000000..ed0180f373 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TreeColumn.java @@ -0,0 +1,675 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * Instances of this class represent a column in a tree widget. + * <p><dl> + * <dt><b>Styles:</b></dt> + * <dd>LEFT, RIGHT, CENTER</dd> + * <dt><b>Events:</b></dt> + * <dd> Move, Resize, Selection</dd> + * </dl> + * </p><p> + * Note: Only one of the styles LEFT, RIGHT and CENTER may be specified. + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#tree">Tree, TreeItem, TreeColumn snippets</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.1 + * @noextend This class is not intended to be subclassed by clients. + */ +public class TreeColumn extends Item { + NSTableColumn nsColumn; + Tree parent; + String toolTipText, displayText; + boolean movable; + + static final int MARGIN = 2; + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>Tree</code>) and a style value + * describing its behavior and appearance. The item is added + * to the end of the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#LEFT + * @see SWT#RIGHT + * @see SWT#CENTER + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public TreeColumn (Tree parent, int style) { + super (parent, checkStyle (style)); + this.parent = parent; + parent.createItem (this, parent.columnCount); +} + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>Tree</code>), a style value + * describing its behavior and appearance, and the index + * at which to place it in the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * <p> + * Note that due to a restriction on some platforms, the first column + * is always left aligned. + * </p> + * @param parent a composite control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * @param index the zero-relative index to store the receiver in its parent + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT#LEFT + * @see SWT#RIGHT + * @see SWT#CENTER + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public TreeColumn (Tree parent, int style, int index) { + super (parent, checkStyle (style)); + this.parent = parent; + parent.createItem (this, index); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the control is moved or resized, by sending + * it one of the messages defined in the <code>ControlListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ControlListener + * @see #removeControlListener + */ +public void addControlListener(ControlListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Resize,typedListener); + addListener (SWT.Move,typedListener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the control is selected by the user, by sending + * it one of the messages defined in the <code>SelectionListener</code> + * interface. + * <p> + * <code>widgetSelected</code> is called when the column header is selected. + * <code>widgetDefaultSelected</code> is not called. + * </p> + * + * @param listener the listener which should be notified when the control is selected by the user + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener (SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Selection,typedListener); + addListener (SWT.DefaultSelection,typedListener); +} + +static int checkStyle (int style) { + return checkBits (style, SWT.LEFT, SWT.CENTER, SWT.RIGHT, 0, 0, 0); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +void deregister () { + super.deregister (); + display.removeWidget (nsColumn.headerCell()); +} + +void destroyWidget () { + parent.destroyItem (this); + releaseHandle (); +} + +void drawInteriorWithFrame_inView (int /*long*/ id, int /*long*/ sel, NSRect cellRect, int /*long*/ view) { + /* + * Feature in Cocoa. When the last column in a tree does not reach the + * rightmost edge of the tree view, the cell that draws the rightmost- + * column's header is also invoked to draw the header space between its + * right edge and the tree's right edge. If this case is detected then + * nothing should be drawn. + */ + int columnIndex = parent.indexOf (nsColumn); + NSRect headerRect = parent.headerView.headerRectOfColumn (columnIndex); + if (headerRect.x != cellRect.x || headerRect.width != cellRect.width) return; + + NSGraphicsContext context = NSGraphicsContext.currentContext (); + context.saveGraphicsState (); + + int contentWidth = 0; + NSSize stringSize = null, imageSize = null; + NSAttributedString attrString = null; + NSTableHeaderCell headerCell = nsColumn.headerCell (); + if (displayText != null) { + Font font = Font.cocoa_new(display, headerCell.font ()); + attrString = parent.createString(displayText, font, null, SWT.LEFT, (parent.state & DISABLED) == 0, false); + stringSize = attrString.size (); + contentWidth += Math.ceil (stringSize.width); + if (image != null) contentWidth += MARGIN; /* space between image and text */ + } + if (image != null) { + imageSize = image.handle.size (); + contentWidth += Math.ceil (imageSize.width); + } + + if (parent.sortColumn == this && parent.sortDirection != SWT.NONE) { + boolean ascending = parent.sortDirection == SWT.UP; + headerCell.drawSortIndicatorWithFrame (cellRect, new NSView(view), ascending, 0); + /* remove the arrow's space from the available drawing width */ + NSRect sortRect = headerCell.sortIndicatorRectForBounds (cellRect); + cellRect.width = Math.max (0, sortRect.x - cellRect.x); + } + + int drawX = 0; + if ((style & SWT.CENTER) != 0) { + drawX = (int)(cellRect.x + Math.max (MARGIN, ((cellRect.width - contentWidth) / 2))); + } else if ((style & SWT.RIGHT) != 0) { + drawX = (int)(cellRect.x + Math.max (MARGIN, cellRect.width - contentWidth - MARGIN)); + } else { + drawX = (int)cellRect.x + MARGIN; + } + + if (image != null) { + NSRect destRect = new NSRect (); + destRect.x = drawX; + destRect.y = cellRect.y; + destRect.width = Math.min (imageSize.width, cellRect.width - 2 * MARGIN); + destRect.height = Math.min (imageSize.height, cellRect.height); + boolean isFlipped = new NSView (view).isFlipped(); + if (isFlipped) { + context.saveGraphicsState (); + NSAffineTransform transform = NSAffineTransform.transform (); + transform.scaleXBy (1, -1); + transform.translateXBy (0, -(destRect.height + 2 * destRect.y)); + transform.concat (); + } + NSRect sourceRect = new NSRect (); + sourceRect.width = destRect.width; + sourceRect.height = destRect.height; + image.handle.drawInRect (destRect, sourceRect, OS.NSCompositeSourceOver, 1f); + if (isFlipped) context.restoreGraphicsState (); + drawX += destRect.width; + } + + if (displayText != null && displayText.length () > 0) { + if (image != null) drawX += MARGIN; /* space between image and text */ + NSRect destRect = new NSRect (); + destRect.x = drawX; + destRect.y = cellRect.y; + destRect.width = Math.min (stringSize.width, cellRect.x + cellRect.width - MARGIN - drawX); + destRect.height = Math.min (stringSize.height, cellRect.height); + attrString.drawInRect (destRect); + } + if (attrString != null) attrString.release (); + + context.restoreGraphicsState (); +} + +/** + * Returns a value which describes the position of the + * text or image in the receiver. The value will be one of + * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>. + * + * @return the alignment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getAlignment () { + checkWidget (); + if ((style & SWT.LEFT) != 0) return SWT.LEFT; + if ((style & SWT.CENTER) != 0) return SWT.CENTER; + if ((style & SWT.RIGHT) != 0) return SWT.RIGHT; + return SWT.LEFT; +} + +String getNameText () { + return getText (); +} + +/** + * Returns the receiver's parent, which must be a <code>Tree</code>. + * + * @return the receiver's parent + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Tree getParent () { + checkWidget (); + return parent; +} + +/** + * Gets the moveable attribute. A column that is + * not moveable cannot be reordered by the user + * by dragging the header but may be reordered + * by the programmer. + * + * @return the moveable attribute + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Tree#getColumnOrder() + * @see Tree#setColumnOrder(int[]) + * @see TreeColumn#setMoveable(boolean) + * @see SWT#Move + * + * @since 3.2 + */ +public boolean getMoveable () { + checkWidget (); + return movable; +} + +/** + * Gets the resizable attribute. A column that is + * not resizable cannot be dragged by the user but + * may be resized by the programmer. + * + * @return the resizable attribute + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getResizable () { + checkWidget (); + return nsColumn.resizingMask() != OS.NSTableColumnNoResizing; +} + +/** + * Returns the receiver's tool tip text, or null if it has + * not been set. + * + * @return the receiver's tool tip text + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public String getToolTipText () { + checkWidget (); + return toolTipText; +} + +/** + * Gets the width of the receiver. + * + * @return the width + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getWidth () { + checkWidget (); + int width = (int)nsColumn.width(); + // TODO how to differentiate 0 and 1 cases? + if (width > 0) width += Tree.CELL_GAP; + return width; +} + +/** + * Causes the receiver to be resized to its preferred size. + * For a composite, this involves computing the preferred size + * from its layout, if there is one. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + */ +public void pack () { + checkWidget (); + + int width = 0; + + /* compute header width */ + if (displayText != null) { + NSTableHeaderCell headerCell = nsColumn.headerCell (); + Font font = Font.cocoa_new(display, headerCell.font ()); + NSAttributedString attrString = parent.createString(displayText, font, null, 0, true, false); + NSSize stringSize = attrString.size (); + attrString.release (); + width += Math.ceil (stringSize.width); + if (image != null) width += MARGIN; /* space between image and text */ + } + if (image != null) { + NSSize imageSize = image.handle.size (); + width += Math.ceil (imageSize.width); + } + if (parent.sortColumn == this && parent.sortDirection != SWT.NONE) { + NSTableHeaderCell headerCell = nsColumn.headerCell (); + NSRect rect = new NSRect (); + rect.width = rect.height = Float.MAX_VALUE; + NSSize cellSize = headerCell.cellSizeForBounds (rect); + rect.height = cellSize.height; + NSRect sortRect = headerCell.sortIndicatorRectForBounds (rect); + width += Math.ceil (sortRect.width); + } + + /* compute item widths down column */ + GC gc = new GC (parent); + width = Math.max(width, parent.calculateWidth(parent.items, parent.indexOf (this), gc, true)); + gc.dispose (); + setWidth (width); +} + +void releaseHandle () { + super.releaseHandle (); + if (nsColumn != null) { + nsColumn.headerCell ().release (); + nsColumn.release (); + } + nsColumn = null; + parent = null; +} + +void releaseWidget () { + super.releaseWidget (); + if (parent.sortColumn == this) { + parent.sortColumn = null; + } +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is moved or resized. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see ControlListener + * @see #addControlListener + */ +public void removeControlListener (ControlListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Move, listener); + eventTable.unhook (SWT.Resize, listener); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the control is selected by the user. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener(SelectionListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Selection, listener); + eventTable.unhook (SWT.DefaultSelection,listener); +} + +/** + * Controls how text and images will be displayed in the receiver. + * The argument should be one of <code>LEFT</code>, <code>RIGHT</code> + * or <code>CENTER</code>. + * <p> + * Note that due to a restriction on some platforms, the first column + * is always left aligned. + * </p> + * @param alignment the new alignment + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setAlignment (int alignment) { + checkWidget (); + if ((alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER)) == 0) return; + int index = parent.indexOf (this); + if (index == -1 || index == 0) return; + style &= ~(SWT.LEFT | SWT.RIGHT | SWT.CENTER); + style |= alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER); + NSOutlineView outlineView = ((NSOutlineView) parent.view); + NSTableHeaderView headerView = outlineView.headerView (); + if (headerView == null) return; + index = parent.indexOf (nsColumn); + NSRect rect = headerView.headerRectOfColumn (index); + headerView.setNeedsDisplayInRect (rect); + rect = outlineView.rectOfColumn (index); + parent.view.setNeedsDisplayInRect (rect); +} + +public void setImage (Image image) { + checkWidget(); + if (image != null && image.isDisposed ()) { + error (SWT.ERROR_INVALID_ARGUMENT); + } + super.setImage (image); + NSTableHeaderView headerView = ((NSOutlineView) parent.view).headerView (); + if (headerView == null) return; + int index = parent.indexOf (nsColumn); + NSRect rect = headerView.headerRectOfColumn (index); + headerView.setNeedsDisplayInRect (rect); +} + +/** + * Sets the moveable attribute. A column that is + * moveable can be reordered by the user by dragging + * the header. A column that is not moveable cannot be + * dragged by the user but may be reordered + * by the programmer. + * + * @param moveable the moveable attribute + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Tree#setColumnOrder(int[]) + * @see Tree#getColumnOrder() + * @see TreeColumn#getMoveable() + * @see SWT#Move + * + * @since 3.2 + */ +public void setMoveable (boolean moveable) { + checkWidget (); + this.movable = moveable; +} + +/** + * Sets the resizable attribute. A column that is + * not resizable cannot be dragged by the user but + * may be resized by the programmer. + * + * @param resizable the resize attribute + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setResizable (boolean resizable) { + checkWidget (); + nsColumn.setResizingMask(resizable ? OS.NSTableColumnUserResizingMask : OS.NSTableColumnNoResizing); +} + +public void setText (String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + super.setText (string); + char [] buffer = new char [text.length ()]; + text.getChars (0, buffer.length, buffer, 0); + int length = fixMnemonic (buffer); + displayText = new String (buffer, 0, length); + NSString title = NSString.stringWith (displayText); + nsColumn.headerCell ().setTitle (title); + NSTableHeaderView headerView = ((NSOutlineView) parent.view).headerView (); + if (headerView == null) return; + int index = parent.indexOf (nsColumn); + NSRect rect = headerView.headerRectOfColumn (index); + headerView.setNeedsDisplayInRect (rect); +} + +/** + * Sets the receiver's tool tip text to the argument, which + * may be null indicating that the default tool tip for the + * control will be shown. For a control that has a default + * tool tip, such as the Tree control on Windows, setting + * the tool tip text to an empty string replaces the default, + * causing no tool tip text to be shown. + * <p> + * The mnemonic indicator (character '&') is not displayed in a tool tip. + * To display a single '&' in the tool tip, the character '&' can be + * escaped by doubling it in the string. + * </p> + * + * @param string the new tool tip text (or null) + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public void setToolTipText (String string) { + checkWidget(); + toolTipText = string; + parent.checkToolTip (this); +} + +/** + * Sets the width of the receiver. + * + * @param width the new width + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setWidth (int width) { + checkWidget (); + if (width < 0) return; + // TODO how to differentiate 0 and 1 cases? + width = Math.max (0, width - Tree.CELL_GAP); + nsColumn.setWidth (width); +} + +String tooltipText () { + return toolTipText; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TreeItem.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TreeItem.java new file mode 100755 index 0000000000..7a02097ce1 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TreeItem.java @@ -0,0 +1,1456 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + + +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * Instances of this class represent a selectable user interface object + * that represents a hierarchy of tree items in a tree widget. + * + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>(none)</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#tree">Tree, TreeItem, TreeColumn snippets</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class TreeItem extends Item { + Tree parent; + TreeItem parentItem; + TreeItem[] items; + int itemCount; + String [] strings; + Image [] images; + boolean checked, grayed, cached, expanded; + Color foreground, background; + Color [] cellForeground, cellBackground; + Font font; + Font [] cellFont; + int width = -1; + /** + * the handle to the OS resource + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public SWTTreeItem handle; + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>Tree</code> or a <code>TreeItem</code>) + * and a style value describing its behavior and appearance. + * The item is added to the end of the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a tree control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public TreeItem (Tree parent, int style) { + this (checkNull (parent), null, style, -1, true); +} + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>Tree</code> or a <code>TreeItem</code>), + * a style value describing its behavior and appearance, and the index + * at which to place it in the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a tree control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * @param index the zero-relative index to store the receiver in its parent + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public TreeItem (Tree parent, int style, int index) { + this (checkNull (parent), null, style, checkIndex (index), true); +} + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>Tree</code> or a <code>TreeItem</code>) + * and a style value describing its behavior and appearance. + * The item is added to the end of the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parentItem a tree control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public TreeItem (TreeItem parentItem, int style) { + this (checkNull (parentItem).parent, parentItem, style, -1, true); +} + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>Tree</code> or a <code>TreeItem</code>), + * a style value describing its behavior and appearance, and the index + * at which to place it in the items maintained by its parent. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parentItem a tree control which will be the parent of the new instance (cannot be null) + * @param style the style of control to construct + * @param index the zero-relative index to store the receiver in its parent + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public TreeItem (TreeItem parentItem, int style, int index) { + this (checkNull (parentItem).parent, parentItem, style, checkIndex (index), true); +} + +TreeItem (Tree parent, TreeItem parentItem, int style, int index, boolean create) { + super (parent, style); + this.parent = parent; + this.parentItem = parentItem; + if (create) { + parent.createItem (this, parentItem, index); + } else { + handle = (SWTTreeItem) new SWTTreeItem ().alloc ().init (); + createJNIRef (); + register (); + items = new TreeItem[4]; + } +} + +static TreeItem checkNull (TreeItem item) { + if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return item; +} + +static Tree checkNull (Tree parent) { + if (parent == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return parent; +} + +static int checkIndex (int index) { + if (index < 0) SWT.error (SWT.ERROR_INVALID_RANGE); + return index; +} + +int calculateWidth (int index, GC gc) { + if (index == 0 && width != -1) return width; + Font font = null; + if (cellFont != null) font = cellFont[index]; + if (font == null) font = this.font; + if (font == null) font = parent.font; + if (font == null) font = parent.defaultFont(); + String text = index == 0 ? this.text : (strings == null ? "" : strings [index]); + Image image = index == 0 ? this.image : (images == null ? null : images [index]); + NSCell cell = parent.dataCell; + if (font.extraTraits != 0) { + NSAttributedString attribStr = parent.createString(text, font, null, 0, true, false); + cell.setAttributedStringValue(attribStr); + attribStr.release(); + } else { + cell.setFont (font.handle); + cell.setTitle (NSString.stringWith(text != null ? text : "")); + } + + /* This code is inlined for performance */ + objc_super super_struct = new objc_super(); + super_struct.receiver = cell.id; + super_struct.super_class = OS.objc_msgSend(cell.id, OS.sel_superclass); + NSSize size = new NSSize(); + OS.objc_msgSendSuper_stret(size, super_struct, OS.sel_cellSize); + if (image != null) size.width += parent.imageBounds.width + Tree.IMAGE_GAP; +// cell.setImage (image != null ? image.handle : null); +// NSSize size = cell.cellSize (); + + int width = (int)Math.ceil (size.width); + boolean sendMeasure = true; + if ((parent.style & SWT.VIRTUAL) != 0) { + sendMeasure = cached; + } + if (sendMeasure && parent.hooks (SWT.MeasureItem)) { + gc.setFont (font); + Event event = new Event (); + event.item = this; + event.index = index; + event.gc = gc; + NSTableView widget = (NSTableView)parent.view; + int height = (int)widget.rowHeight (); + event.width = width; + event.height = height; + parent.sendEvent (SWT.MeasureItem, event); + if (height < event.height) { + widget.setRowHeight (event.height); + widget.setNeedsDisplay (true); + } + width = event.width; + } + if (index == 0) { + NSOutlineView outlineView = (NSOutlineView)parent.view; + width += outlineView.indentationPerLevel () * (1 + outlineView.levelForItem (handle)); + this.width = width; + } + return width; +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +void clear () { + cached = false; + text = ""; + image = null; + strings = null; + images = null; + checked = grayed = false; + foreground = background = null; + cellForeground = cellBackground = null; + font = null; + cellFont = null; + width = -1; +} + +/** + * Clears the item at the given zero-relative index in the receiver. + * The text, icon and other attributes of the item are set to the default + * value. If the tree was created with the <code>SWT.VIRTUAL</code> style, + * these attributes are requested again as needed. + * + * @param index the index of the item to clear + * @param all <code>true</code> if all child items of the indexed item should be + * cleared recursively, and <code>false</code> otherwise + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SWT#VIRTUAL + * @see SWT#SetData + * + * @since 3.2 + */ +public void clear (int index, boolean all) { + checkWidget (); + int count = getItemCount (); + if (index < 0 || index >= count) + SWT.error (SWT.ERROR_INVALID_RANGE); + parent.clear (this, index, all); +} + + +/** + * Clears all the items in the receiver. The text, icon and other + * attributes of the items are set to their default values. If the + * tree was created with the <code>SWT.VIRTUAL</code> style, these + * attributes are requested again as needed. + * + * @param all <code>true</code> if all child items should be cleared + * recursively, and <code>false</code> otherwise + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SWT#VIRTUAL + * @see SWT#SetData + * + * @since 3.2 + */ +public void clearAll (boolean all) { + checkWidget (); + parent.clearAll (this, all); +} + +void clearSelection () { + NSOutlineView widget = (NSOutlineView) parent.view; + int /*long*/ row = widget.rowForItem (handle); + if (widget.isRowSelected(row)) widget.deselectRow (row); + if (items != null && getExpanded ()) { + for (int i = 0; i < items.length; i++) { + TreeItem item = items [i]; + if (item != null && !item.isDisposed ()) item.clearSelection (); + } + } +} + +NSObject createString(int index) { + String text = index == 0 ? this.text : (strings == null ? "" : strings [index]); + return NSString.stringWith(text != null ? text : ""); +} + +void deregister () { + super.deregister (); + display.removeWidget (handle); +} + +void destroyWidget () { + parent.destroyItem (this); + releaseHandle (); +} + +/** + * Returns the receiver's background color. + * + * @return the background color + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.0 + * + */ +public Color getBackground () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + return background != null ? background : parent.getBackground (); +} + +/** + * Returns the background color at the given column index in the receiver. + * + * @param index the column index + * @return the background color + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public Color getBackground (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + int count = Math.max (1, parent.columnCount); + if (0 > index || index > count -1) return getBackground (); + if (cellBackground == null || cellBackground [index] == null) return getBackground (); + return cellBackground [index]; +} + +/** + * Returns a rectangle describing the receiver's size and location + * relative to its parent. + * + * @return the receiver's bounding rectangle + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Rectangle getBounds () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + parent.checkItems (); + NSOutlineView outlineView = (NSOutlineView) parent.view; + NSRect rect = outlineView.rectOfRow (outlineView.rowForItem (handle)); + return new Rectangle((int) rect.x, (int) rect.y, (int) rect.width, (int) rect.height); +} + +/** + * Returns a rectangle describing the receiver's size and location + * relative to its parent at a column in the tree. + * + * @param index the index that specifies the column + * @return the receiver's bounding column rectangle + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public Rectangle getBounds (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + if (!(0 <= index && index < Math.max (1, parent.columnCount))) return new Rectangle (0, 0, 0, 0); + + parent.checkItems (); + NSOutlineView outlineView = (NSOutlineView) parent.view; + if (parent.columnCount == 0) { + index = (parent.style & SWT.CHECK) != 0 ? 1 : 0; + } else { + TreeColumn column = parent.getColumn (index); + index = parent.indexOf (column.nsColumn); + } + NSRect rect = outlineView.frameOfCellAtColumn (index, outlineView.rowForItem (handle)); + return new Rectangle ((int) rect.x, (int) rect.y, (int) rect.width, (int) rect.height); +} + +/** + * Returns <code>true</code> if the receiver is checked, + * and false otherwise. When the parent does not have + * the <code>CHECK style, return false. + * <p> + * + * @return the checked state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getChecked () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + if ((parent.style & SWT.CHECK) == 0) return false; + return checked; +} + +/** + * Returns <code>true</code> if the receiver is expanded, + * and false otherwise. + * <p> + * + * @return the expanded state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getExpanded () { + checkWidget (); + return expanded; +} + +/** + * Returns the font that the receiver will use to paint textual information for this item. + * + * @return the receiver's font + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public Font getFont () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + return font != null ? font : parent.getFont (); +} + +/** + * Returns the font that the receiver will use to paint textual information + * for the specified cell in this item. + * + * @param index the column index + * @return the receiver's font + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public Font getFont (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + int count = Math.max (1, parent.columnCount); + if (0 > index || index > count -1) return getFont (); + if (cellFont == null || cellFont [index] == null) return getFont (); + return cellFont [index]; +} + +/** + * Returns the foreground color that the receiver will use to draw. + * + * @return the receiver's foreground color + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.0 + * + */ +public Color getForeground () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + return foreground != null ? foreground : parent.getForeground (); +} + +/** + * + * Returns the foreground color at the given column index in the receiver. + * + * @param index the column index + * @return the foreground color + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public Color getForeground (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + int count = Math.max (1, parent.columnCount); + if (0 > index || index > count -1) return getForeground (); + if (cellForeground == null || cellForeground [index] == null) return getForeground (); + return cellForeground [index]; +} + +/** + * Returns <code>true</code> if the receiver is grayed, + * and false otherwise. When the parent does not have + * the <code>CHECK style, return false. + * <p> + * + * @return the grayed state of the checkbox + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public boolean getGrayed () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + if ((parent.style & SWT.CHECK) == 0) return false; + return grayed; +} + +public Image getImage () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + return super.getImage (); +} + +/** + * Returns the image stored at the given column index in the receiver, + * or null if the image has not been set or if the column does not exist. + * + * @param index the column index + * @return the image stored at the given column index in the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public Image getImage (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + if (index == 0) return getImage (); + if (images != null) { + if (0 <= index && index < images.length) return images [index]; + } + return null; +} + +/** + * Returns a rectangle describing the size and location + * relative to its parent of an image at a column in the + * tree. + * + * @param index the index that specifies the column + * @return the receiver's bounding image rectangle + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public Rectangle getImageBounds (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + if (!(0 <= index && index < Math.max (1, parent.columnCount))) return new Rectangle (0, 0, 0, 0); + + parent.checkItems (); + NSOutlineView outlineView = (NSOutlineView) parent.view; + Image image = index == 0 ? this.image : (images != null) ? images [index] : null; + if (parent.columnCount == 0) { + index = (parent.style & SWT.CHECK) != 0 ? 1 : 0; + } else { + TreeColumn column = parent.getColumn (index); + index = parent.indexOf (column.nsColumn); + } + NSRect rect = outlineView.frameOfCellAtColumn (index, outlineView.rowForItem (handle)); + rect.x += Tree.IMAGE_GAP; + if (image != null) { + rect.width = parent.imageBounds.width; + } else { + rect.width = 0; + } + return new Rectangle((int) rect.x, (int) rect.y, (int) rect.width, (int) rect.height); +} + +/** + * Returns the item at the given, zero-relative index in the + * receiver. Throws an exception if the index is out of range. + * + * @param index the index of the item to return + * @return the item at the given index + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public TreeItem getItem (int index) { + checkWidget (); + if (index < 0) error (SWT.ERROR_INVALID_RANGE); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + if (index >= itemCount) error (SWT.ERROR_INVALID_RANGE); + return parent._getItem (this, index, true); +} + +/** + * Returns the number of items contained in the receiver + * that are direct item children of the receiver. + * + * @return the number of items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getItemCount () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + return itemCount; +} + +/** + * Returns a (possibly empty) array of <code>TreeItem</code>s which + * are the direct item children of the receiver. + * <p> + * Note: This is not the actual structure used by the receiver + * to maintain its list of items, so modifying the array will + * not affect the receiver. + * </p> + * + * @return the receiver's items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public TreeItem [] getItems () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + TreeItem [] result = new TreeItem [itemCount]; + for (int i=0; i<itemCount; i++) { + result [i] = parent._getItem (this, i, true); + } + return result; +} + +String getNameText () { + if ((parent.style & SWT.VIRTUAL) != 0) { + if (!cached) return "*virtual*"; //$NON-NLS-1$ + } + return super.getNameText (); +} + +/** + * Returns the receiver's parent, which must be a <code>Tree</code>. + * + * @return the receiver's parent + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public Tree getParent () { + checkWidget (); + return parent; +} + +/** + * Returns the receiver's parent item, which must be a + * <code>TreeItem</code> or null when the receiver is a + * root. + * + * @return the receiver's parent item + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public TreeItem getParentItem () { + checkWidget (); + return parentItem; +} + +public String getText () { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + return super.getText (); +} + +/** + * Returns the text stored at the given column index in the receiver, + * or empty string if the text has not been set. + * + * @param index the column index + * @return the text stored at the given column index in the receiver + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public String getText (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + if (index == 0) return getText (); + if (strings != null) { + if (0 <= index && index < strings.length) { + String string = strings [index]; + return string != null ? string : ""; + } + } + return ""; +} + +/** + * Returns a rectangle describing the size and location + * relative to its parent of the text at a column in the + * tree. + * + * @param index the index that specifies the column + * @return the receiver's bounding text rectangle + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.3 + */ +public Rectangle getTextBounds (int index) { + checkWidget (); + if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); + if (!(0 <= index && index < Math.max (1, parent.columnCount))) return new Rectangle (0, 0, 0, 0); + + parent.checkItems (); + NSOutlineView outlineView = (NSOutlineView) parent.view; + Image image = index == 0 ? this.image : (images != null) ? images [index] : null; + if (parent.columnCount == 0) { + index = (parent.style & SWT.CHECK) != 0 ? 1 : 0; + } else { + TreeColumn column = parent.getColumn (index); + index = parent.indexOf (column.nsColumn); + } + NSRect rect = outlineView.frameOfCellAtColumn (index, outlineView.rowForItem (handle)); + rect.x += Tree.TEXT_GAP; + rect.width -= Tree.TEXT_GAP; + if (image != null) { + int offset = parent.imageBounds.width + Tree.IMAGE_GAP; + rect.x += offset; + rect.width -= offset; + } + return new Rectangle((int) rect.x, (int) rect.y, (int) rect.width, (int) rect.height); +} + +/** + * Searches the receiver's list starting at the first item + * (index 0) until an item is found that is equal to the + * argument, and returns the index of that item. If no item + * is found, returns -1. + * + * @param item the search item + * @return the index of the item + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public int indexOf (TreeItem item) { + checkWidget (); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + if (item.parentItem != this) return -1; + for (int i = 0; i < itemCount; i++) { + if (item == items [i]) return i; + } + return -1; +} + +void redraw (int columnIndex) { + if (parent.ignoreRedraw || !isDrawing()) return; + /* redraw the full item if columnIndex == -1 */ + NSOutlineView outlineView = (NSOutlineView) parent.view; + NSRect rect; + if (columnIndex == -1 || parent.hooks (SWT.MeasureItem) || parent.hooks (SWT.EraseItem) || parent.hooks (SWT.PaintItem)) { + rect = outlineView.rectOfRow (outlineView.rowForItem (handle)); + } else { + int index; + if (parent.columnCount == 0) { + index = (parent.style & SWT.CHECK) != 0 ? 1 : 0; + } else { + if (0 <= columnIndex && columnIndex < parent.columnCount) { + index = parent.indexOf (parent.columns[columnIndex].nsColumn); + } else { + return; + } + } + rect = outlineView.frameOfCellAtColumn (index, outlineView.rowForItem (handle)); + } + outlineView.setNeedsDisplayInRect (rect); +} + +void register () { + super.register (); + display.addWidget (handle, this); +} + +void release(boolean destroy) { + /* + * Bug in Cocoa. When removing selected items from an NSOutlineView, the selection + * is not properly updated. The fix is to ensure that the item and its subitems + * are deselected before the item is removed by the reloadItem call. + * + * This has to be done in release to avoid traversing the tree twice when items are + * removed from the tree by setItemCount. + */ + if (destroy) clearSelection (); + super.release(destroy); +} + +void releaseChildren (boolean destroy) { + for (int i=0; i<items.length; i++) { + TreeItem item = items [i]; + if (item != null && !item.isDisposed ()) { + item.release (false); + } + } + items = null; + itemCount = 0; + super.releaseChildren (destroy); +} + +void releaseHandle () { + super.releaseHandle (); + if (handle != null) handle.release (); + handle = null; + parentItem = null; + parent = null; +} + +void releaseWidget () { + super.releaseWidget (); + strings = null; + images = null; + background = foreground = null; + font = null; + cellBackground = cellForeground = null; + cellFont = null; +} + +/** + * Removes all of the items from the receiver. + * <p> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public void removeAll () { + checkWidget (); + parent.setItemCount (this, 0); +} + +void sendExpand (boolean expand, boolean recurse) { + if (itemCount == 0) return; + if (expanded != expand) { + Event event = new Event (); + event.item = this; + parent.sendEvent (expand ? SWT.Expand : SWT.Collapse, event); + if (isDisposed ()) return; + expanded = expand; + } + if (recurse) { + for (int i = 0; i < itemCount; i++) { + if (items[i] != null) items[i].sendExpand (expand, recurse); + } + } +} + +/** + * Sets the receiver's background color to the color specified + * by the argument, or to the default system color for the item + * if the argument is null. + * + * @param color the new color (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.0 + * + */ +public void setBackground (Color color) { + checkWidget (); + if (color != null && color.isDisposed ()) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + Color oldColor = background; + if (oldColor == color) return; + background = color; + if (oldColor != null && oldColor.equals (color)) return; + cached = true; + redraw (-1); +} + +/** + * Sets the background color at the given column index in the receiver + * to the color specified by the argument, or to the default system color for the item + * if the argument is null. + * + * @param index the column index + * @param color the new color (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + * + */ +public void setBackground (int index, Color color) { + checkWidget (); + if (color != null && color.isDisposed ()) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + int count = Math.max (1, parent.columnCount); + if (0 > index || index > count - 1) return; + if (cellBackground == null) { + if (color == null) return; + cellBackground = new Color [count]; + } + Color oldColor = cellBackground [index]; + if (oldColor == color) return; + cellBackground [index] = color; + if (oldColor != null && oldColor.equals (color)) return; + cached = true; + redraw (index); +} + +/** + * Sets the checked state of the receiver. + * <p> + * + * @param checked the new checked state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setChecked (boolean checked) { + checkWidget (); + if ((parent.style & SWT.CHECK) == 0) return; + if (this.checked == checked) return; + this.checked = checked; + cached = true; + redraw (-1); +} + +/** + * Sets the expanded state of the receiver. + * <p> + * + * @param expanded the new expanded state + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setExpanded (boolean expanded) { + checkWidget (); + + /* Do nothing when the item is a leaf or already expanded */ + if (itemCount == 0 || expanded == getExpanded ()) return; + + parent.checkItems (); + parent.ignoreExpand = true; + this.expanded = expanded; + if (expanded) { + ((NSOutlineView) parent.view).expandItem (handle); + } else { + ((NSOutlineView) parent.view).collapseItem (handle); + } + parent.ignoreExpand = false; + cached = true; + if (!expanded) { + parent.setScrollWidth (); + } +} + +/** + * Sets the font that the receiver will use to paint textual information + * for this item to the font specified by the argument, or to the default font + * for that kind of control if the argument is null. + * + * @param font the new font (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.0 + */ +public void setFont (Font font) { + checkWidget (); + if (font != null && font.isDisposed ()) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + Font oldFont = this.font; + if (oldFont == font) return; + this.font = font; + if (oldFont != null && oldFont.equals (font)) return; + width = -1; + cached = true; + redraw (-1); +} + +/** + * Sets the font that the receiver will use to paint textual information + * for the specified cell in this item to the font specified by the + * argument, or to the default font for that kind of control if the + * argument is null. + * + * @param index the column index + * @param font the new font (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public void setFont (int index, Font font) { + checkWidget (); + if (font != null && font.isDisposed ()) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + int count = Math.max (1, parent.columnCount); + if (0 > index || index > count - 1) return; + if (cellFont == null) { + if (font == null) return; + cellFont = new Font [count]; + } + Font oldFont = cellFont [index]; + if (oldFont == font) return; + cellFont [index] = font; + if (oldFont != null && oldFont.equals (font)) return; + width = -1; + cached = true; + redraw (index); +} + +/** + * Sets the receiver's foreground color to the color specified + * by the argument, or to the default system color for the item + * if the argument is null. + * + * @param color the new color (or null) + * + * @since 2.0 + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 2.0 + * + */ +public void setForeground (Color color) { + checkWidget (); + if (color != null && color.isDisposed ()) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + Color oldColor = foreground; + if (oldColor == color) return; + foreground = color; + if (oldColor != null && oldColor.equals (color)) return; + cached = true; + redraw (-1); +} + +/** + * Sets the foreground color at the given column index in the receiver + * to the color specified by the argument, or to the default system color for the item + * if the argument is null. + * + * @param index the column index + * @param color the new color (or null) + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + * + */ +public void setForeground (int index, Color color){ + checkWidget (); + if (color != null && color.isDisposed ()) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + int count = Math.max (1, parent.columnCount); + if (0 > index || index > count - 1) return; + if (cellForeground == null) { + if (color == null) return; + cellForeground = new Color [count]; + } + Color oldColor = cellForeground [index]; + if (oldColor == color) return; + cellForeground [index] = color; + if (oldColor != null && oldColor.equals (color)) return; + cached = true; + redraw (index); +} + +/** + * Sets the grayed state of the checkbox for this item. This state change + * only applies if the Tree was created with the SWT.CHECK style. + * + * @param grayed the new grayed state of the checkbox + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public void setGrayed (boolean grayed) { + checkWidget (); + if ((parent.style & SWT.CHECK) == 0) return; + if (this.grayed == grayed) return; + this.grayed = grayed; + cached = true; + redraw (-1); +} + +/** + * Sets the image for multiple columns in the tree. + * + * @param images the array of new images + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the array of images is null</li> + * <li>ERROR_INVALID_ARGUMENT - if one of the images has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public void setImage (Image [] images) { + checkWidget (); + if (images == null) error (SWT.ERROR_NULL_ARGUMENT); + for (int i=0; i<images.length; i++) { + setImage (i, images [i]); + } +} + +/** + * Sets the receiver's image at a column. + * + * @param index the column index + * @param image the new image + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public void setImage (int index, Image image) { + checkWidget (); + if (image != null && image.isDisposed ()) { + error (SWT.ERROR_INVALID_ARGUMENT); + } + if (parent.imageBounds == null && image != null) { + parent.setItemHeight (image, null, false); + } + if (index == 0) { + if (image != null && image.type == SWT.ICON) { + if (image.equals (this.image)) return; + } + width = -1; + super.setImage (image); + } + int count = Math.max (1, parent.columnCount); + if (0 <= index && index < count) { + if (images == null) images = new Image [count]; + if (image != null && image.type == SWT.ICON) { + if (image.equals (images [index])) return; + } + images [index] = image; + } + cached = true; + if (index == 0) parent.setScrollWidth (this); + if (0 <= index && index < count) redraw (index); +} + +public void setImage (Image image) { + checkWidget (); + setImage (0, image); +} + +/** + * Sets the number of child items contained in the receiver. + * + * @param count the number of items + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.2 + */ +public void setItemCount (int count) { + checkWidget (); + count = Math.max (0, count); + parent.setItemCount (this, count); +} + +/** + * Sets the text for multiple columns in the tree. + * + * @param strings the array of new strings + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the text is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public void setText (String [] strings) { + checkWidget (); + if (strings == null) error (SWT.ERROR_NULL_ARGUMENT); + for (int i=0; i<strings.length; i++) { + String string = strings [i]; + if (string != null) setText (i, string); + } +} + +/** + * Sets the receiver's text at a column + * + * @param index the column index + * @param string the new text + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the text is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public void setText (int index, String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if (index == 0) { + if (string.equals (text)) return; + width = -1; + super.setText (string); + } + int count = Math.max (1, parent.columnCount); + if (0 <= index && index < count) { + if (strings == null) strings = new String [count]; + if (string.equals (strings [index])) return; + strings [index] = string; + } + cached = true; + if (index == 0) parent.setScrollWidth (this); + if (0 <= index && index < count) redraw (index); +} + +public void setText (String string) { + checkWidget (); + setText (0, string); +} + +void updateExpanded () { + if (itemCount == 0) return; + NSOutlineView outlineView = (NSOutlineView)parent.view; + if (expanded != outlineView.isItemExpanded (handle)) { + if (expanded) { + outlineView.expandItem (handle); + } else { + outlineView.collapseItem (handle); + } + } + for (int i = 0; i < itemCount; i++) { + if (items[i] != null) items[i].updateExpanded (); + } +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Widget.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Widget.java new file mode 100755 index 0000000000..3ec4eadbcb --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Widget.java @@ -0,0 +1,1767 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.cocoa.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; + +/** + * This class is the abstract superclass of all user interface objects. + * Widgets are created, disposed and issue notification to listeners + * when events occur which affect them. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>(none)</dd> + * <dt><b>Events:</b></dt> + * <dd>Dispose</dd> + * </dl> + * <p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the SWT implementation. However, it has not been marked + * final to allow those outside of the SWT development team to implement + * patched versions of the class in order to get around specific + * limitations in advance of when those limitations can be addressed + * by the team. Any class built using subclassing to access the internals + * of this class will likely fail to compile or run between releases and + * may be strongly platform specific. Subclassing should not be attempted + * without an intimate and detailed understanding of the workings of the + * hierarchy. No support is provided for user-written classes which are + * implemented as subclasses of this class. + * </p> + * + * @see #checkSubclass + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + */ +public abstract class Widget { + int style, state; + Display display; + EventTable eventTable; + Object data; + + int /*long*/ jniRef; + + /* Global state flags */ + static final int DISPOSED = 1 << 0; + static final int CANVAS = 1 << 1; + static final int KEYED_DATA = 1 << 2; + static final int DISABLED = 1 << 3; + static final int HIDDEN = 1 << 4; + static final int GRAB = 1 << 5; + static final int MOVED = 1 << 6; + static final int RESIZED = 1 << 7; + static final int EXPANDING = 1 << 8; + static final int IGNORE_WHEEL = 1 << 9; + static final int PARENT_BACKGROUND = 1 << 10; + static final int THEME_BACKGROUND = 1 << 11; + + /* A layout was requested on this widget */ + static final int LAYOUT_NEEDED = 1<<12; + + /* The preferred size of a child has changed */ + static final int LAYOUT_CHANGED = 1<<13; + + /* A layout was requested in this widget hierachy */ + static final int LAYOUT_CHILD = 1<<14; + + /* More global state flags */ + static final int RELEASED = 1<<15; + static final int DISPOSE_SENT = 1<<16; + static final int FOREIGN_HANDLE = 1<<17; + static final int DRAG_DETECT = 1<<18; + + /* Safari fixes */ + static final int SAFARI_EVENTS_FIX = 1<<19; + static final String SAFARI_EVENTS_FIX_KEY = "org.eclipse.swt.internal.safariEventsFix"; //$NON-NLS-1$ + static final String GLCONTEXT_KEY = "org.eclipse.swt.internal.cocoa.glcontext"; //$NON-NLS-1$ + + /* Default size for widgets */ + static final int DEFAULT_WIDTH = 64; + static final int DEFAULT_HEIGHT = 64; + +Widget () { + /* Do nothing */ +} + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>SWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>SWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a widget which will be the parent of the new instance (cannot be null) + * @param style the style of widget to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the parent is disposed</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see SWT + * @see #checkSubclass + * @see #getStyle + */ +public Widget (Widget parent, int style) { + checkSubclass (); + checkParent (parent); + this.style = style; + display = parent.display; +} + +int /*long*/ accessibilityActionDescription(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + return callSuperObject(id, sel, arg0); +} + +int /*long*/ accessibilityActionNames(int /*long*/ id, int /*long*/ sel) { + return callSuperObject(id, sel); +} + +int /*long*/ accessibilityAttributeNames(int /*long*/ id, int /*long*/ sel) { + return callSuperObject(id, sel); +} + +int /*long*/ accessibilityAttributeValue(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + return callSuperObject(id, sel, arg0); +} + +int /*long*/ accessibilityAttributeValue_forParameter(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + return OS.objc_msgSendSuper(super_struct, sel, arg0, arg1); +} + +int /*long*/ accessibilityFocusedUIElement(int /*long*/ id, int /*long*/ sel) { + return callSuperObject(id, sel); +} + +int /*long*/ accessibilityHitTest(int /*long*/ id, int /*long*/ sel, NSPoint point) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + return OS.objc_msgSendSuper(super_struct, sel, point); +} + +boolean accessibilityIsIgnored(int /*long*/ id, int /*long*/ sel) { + return callSuperBoolean(id, sel); +} + +int /*long*/ accessibilityParameterizedAttributeNames(int /*long*/ id, int /*long*/ sel) { + return callSuperObject(id, sel); +} + +void accessibilityPerformAction(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + callSuper(id, sel, arg0); +} + +String getClipboardText () { + NSPasteboard pasteboard = NSPasteboard.generalPasteboard (); + NSString string = pasteboard.stringForType (OS.NSStringPboardType); + return string != null ? string.getString () : null; +} + +void setClipRegion (float /*double*/ x, float /*double*/ y) { +} + +int /*long*/ attributedSubstringFromRange (int /*long*/ id, int /*long*/ sel, int /*long*/ range) { + return 0; +} + +void callSuper(int /*long*/ id, int /*long*/ sel) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + OS.objc_msgSendSuper(super_struct, sel); +} + +void callSuper(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + OS.objc_msgSendSuper(super_struct, sel, arg0); +} + +void callSuper(int /*long*/ id, int /*long*/ sel, NSRect arg0) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + OS.objc_msgSendSuper(super_struct, sel, arg0); +} + +void callSuper(int /*long*/ id, int /*long*/ sel, NSRect arg0, int /*long*/ arg1) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + OS.objc_msgSendSuper(super_struct, sel, arg0, arg1); +} + +int /*long*/ callSuper(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, NSRect arg1, int /*long*/ arg2) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + return OS.objc_msgSendSuper(super_struct, sel, arg0, arg1, arg2); +} + +boolean callSuperBoolean(int /*long*/ id, int /*long*/ sel) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + return OS.objc_msgSendSuper(super_struct, sel) != 0; +} + +boolean canBecomeKeyWindow (int /*long*/ id, int /*long*/ sel) { + return callSuperBoolean (id, sel); +} + +NSSize cellSize (int /*long*/ id, int /*long*/ sel) { + NSSize result = new NSSize(); + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + OS.objc_msgSendSuper_stret(result, super_struct, sel); + return result; +} + +boolean callSuperBoolean(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + return OS.objc_msgSendSuper(super_struct, sel, arg0) != 0; +} + +boolean callSuperBoolean(int /*long*/ id, int /*long*/ sel, NSRange range, int /*long*/ arg1) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + return OS.objc_msgSendSuper_bool(super_struct, sel, range, arg1); +} + +int /*long*/ callSuperObject(int /*long*/ id, int /*long*/ sel) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + return OS.objc_msgSendSuper(super_struct, sel); +} + +int /*long*/ callSuperObject(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + return OS.objc_msgSendSuper(super_struct, sel, arg0); +} + +boolean canDragRowsWithIndexes_atPoint(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1) { + // Trees/tables are not draggable unless explicitly told they are. + return false; +} + +int /*long*/ characterIndexForPoint (int /*long*/ id, int /*long*/ sel, int /*long*/ point) { + return OS.NSNotFound; +} + +boolean acceptsFirstMouse (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + return OS.objc_msgSendSuper(super_struct, sel, theEvent) != 0; +} + +boolean acceptsFirstResponder (int /*long*/ id, int /*long*/ sel) { + return callSuperBoolean(id, sel); +} + +boolean becomeFirstResponder (int /*long*/ id, int /*long*/ sel) { + return callSuperBoolean(id, sel); +} + +void becomeKeyWindow (int /*long*/ id, int /*long*/ sel) { + callSuper(id, sel); +} + +boolean resignFirstResponder (int /*long*/ id, int /*long*/ sel) { + return callSuperBoolean(id, sel); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when an event of the given type occurs. When the + * event does occur in the widget, the listener is notified by + * sending it the <code>handleEvent()</code> message. The event + * type is one of the event constants defined in class <code>SWT</code>. + * + * @param eventType the type of event to listen for + * @param listener the listener which should be notified when the event occurs + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Listener + * @see SWT + * @see #getListeners(int) + * @see #removeListener(int, Listener) + * @see #notifyListeners + */ +public void addListener (int eventType, Listener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + _addListener (eventType, listener); +} + +void _addListener (int eventType, Listener listener) { + if (eventTable == null) eventTable = new EventTable (); + eventTable.hook (eventType, listener); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when the widget is disposed. When the widget is + * disposed, the listener is notified by sending it the + * <code>widgetDisposed()</code> message. + * + * @param listener the listener which should be notified when the receiver is disposed + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see DisposeListener + * @see #removeDisposeListener + */ +public void addDisposeListener (DisposeListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Dispose, typedListener); +} + +boolean canBecomeKeyView(int /*long*/ id, int /*long*/ sel) { + return true; +} + +static int checkBits (int style, int int0, int int1, int int2, int int3, int int4, int int5) { + int mask = int0 | int1 | int2 | int3 | int4 | int5; + if ((style & mask) == 0) style |= int0; + if ((style & int0) != 0) style = (style & ~mask) | int0; + if ((style & int1) != 0) style = (style & ~mask) | int1; + if ((style & int2) != 0) style = (style & ~mask) | int2; + if ((style & int3) != 0) style = (style & ~mask) | int3; + if ((style & int4) != 0) style = (style & ~mask) | int4; + if ((style & int5) != 0) style = (style & ~mask) | int5; + return style; +} + +void checkOpen () { + /* Do nothing */ +} + +void checkOrientation (Widget parent) { + style &= ~SWT.MIRRORED; + if ((style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT)) == 0) { + if (parent != null) { + if ((parent.style & SWT.LEFT_TO_RIGHT) != 0) style |= SWT.LEFT_TO_RIGHT; + if ((parent.style & SWT.RIGHT_TO_LEFT) != 0) style |= SWT.RIGHT_TO_LEFT; + } + } + style = checkBits (style, SWT.LEFT_TO_RIGHT, SWT.RIGHT_TO_LEFT, 0, 0, 0, 0); +} + +void checkParent (Widget parent) { + if (parent == null) error (SWT.ERROR_NULL_ARGUMENT); + if (parent.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + parent.checkWidget (); + parent.checkOpen (); +} + +/** + * Checks that this class can be subclassed. + * <p> + * The SWT class library is intended to be subclassed + * only at specific, controlled points (most notably, + * <code>Composite</code> and <code>Canvas</code> when + * implementing new widgets). This method enforces this + * rule unless it is overridden. + * </p><p> + * <em>IMPORTANT:</em> By providing an implementation of this + * method that allows a subclass of a class which does not + * normally allow subclassing to be created, the implementer + * agrees to be fully responsible for the fact that any such + * subclass will likely fail between SWT releases and will be + * strongly platform specific. No support is provided for + * user-written classes which are implemented in this fashion. + * </p><p> + * The ability to subclass outside of the allowed SWT classes + * is intended purely to enable those not on the SWT development + * team to implement patches in order to get around specific + * limitations in advance of when those limitations can be + * addressed by the team. Subclassing should not be attempted + * without an intimate and detailed understanding of the hierarchy. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + */ +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +/** + * Throws an <code>SWTException</code> if the receiver can not + * be accessed by the caller. This may include both checks on + * the state of the receiver and more generally on the entire + * execution context. This method <em>should</em> be called by + * widget implementors to enforce the standard SWT invariants. + * <p> + * Currently, it is an error to invoke any method (other than + * <code>isDisposed()</code>) on a widget that has had its + * <code>dispose()</code> method called. It is also an error + * to call widget methods from any thread that is different + * from the thread that created the widget. + * </p><p> + * In future releases of SWT, there may be more or fewer error + * checks and exceptions may be thrown for different reasons. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +protected void checkWidget () { + Display display = this.display; + if (display == null) error (SWT.ERROR_WIDGET_DISPOSED); + if (display.thread != Thread.currentThread () && !display.isEmbedded) error (SWT.ERROR_THREAD_INVALID_ACCESS); + if ((state & DISPOSED) != 0) error (SWT.ERROR_WIDGET_DISPOSED); +} + +boolean textView_clickOnLink_atIndex(int /*long*/ id, int /*long*/ sel, int /*long*/ textView, int /*long*/ link, int /*long*/ charIndex) { + return true; +} + +void collapseItem_collapseChildren (int /*long*/ id, int /*long*/ sel, int /*long*/ item, boolean children) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + OS.objc_msgSendSuper(super_struct, sel, item, children); +} + +void copyToClipboard (char [] buffer) { + if (buffer.length == 0) return; + NSPasteboard pasteboard = NSPasteboard.generalPasteboard (); + pasteboard.declareTypes (NSArray.arrayWithObject (OS.NSStringPboardType), null); + pasteboard.setString (NSString.stringWithCharacters (buffer, buffer.length), OS.NSStringPboardType); +} + +void createHandle () { +} + +void createJNIRef () { + jniRef = OS.NewGlobalRef(this); + if (jniRef == 0) error (SWT.ERROR_NO_HANDLES); +} + +void createWidget () { + createJNIRef (); + createHandle (); + register (); +} + +void deregister () { +} + +void destroyJNIRef () { + if (jniRef != 0) OS.DeleteGlobalRef (jniRef); + jniRef = 0; +} + +void destroyWidget () { + releaseHandle (); +} + +/** + * Disposes of the operating system resources associated with + * the receiver and all its descendants. After this method has + * been invoked, the receiver and all descendants will answer + * <code>true</code> when sent the message <code>isDisposed()</code>. + * Any internal connections between the widgets in the tree will + * have been removed to facilitate garbage collection. + * <p> + * NOTE: This method is not called recursively on the descendants + * of the receiver. This means that, widget implementers can not + * detect when a widget is being disposed of by re-implementing + * this method, but should instead listen for the <code>Dispose</code> + * event. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #addDisposeListener + * @see #removeDisposeListener + * @see #checkWidget + */ +public void dispose () { + /* + * Note: It is valid to attempt to dispose a widget + * more than once. If this happens, fail silently. + */ + if (isDisposed ()) return; + if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS); + release (true); +} + +void doCommandBySelector (int /*long*/ id, int /*long*/ sel, int /*long*/ aSelector) { + callSuper (id, sel, aSelector); +} + +boolean dragSelectionWithEvent(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1, int /*long*/ arg2) { + return false; +} + +void drawBackground (int /*long*/ id, NSGraphicsContext context, NSRect rect) { + /* Do nothing */ +} + +void drawImageWithFrameInView (int /*long*/ id, int /*long*/ sel, int /*long*/ image, NSRect rect, int /*long*/ view) { +} + +void drawInteriorWithFrame_inView (int /*long*/ id, int /*long*/ sel, NSRect cellFrame, int /*long*/ view) { + callSuper(id, sel, cellFrame, view); +} + +void drawWithExpansionFrame_inView (int /*long*/ id, int /*long*/ sel, NSRect cellFrame, int /*long*/ view) { + callSuper(id, sel, cellFrame, view); +} + +void drawRect (int /*long*/ id, int /*long*/ sel, NSRect rect) { + if (!isDrawing()) return; + Display display = this.display; + NSView view = new NSView(id); + display.isPainting.addObject(view); + NSGraphicsContext context = NSGraphicsContext.currentContext(); + context.saveGraphicsState(); + setClipRegion(0, 0); + drawBackground (id, context, rect); + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + OS.objc_msgSendSuper(super_struct, sel, rect); + if (!isDisposed()) { + /* + * Feature in Cocoa. There are widgets that draw outside of the UI thread, + * such as the progress bar and default button. The fix is to draw the + * widget but not send paint events. + */ + drawWidget (id, context, rect); + } + context.restoreGraphicsState(); + display.isPainting.removeObjectIdenticalTo(view); +} + +void _drawThemeProgressArea (int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + OS.objc_msgSendSuper(super_struct, sel, arg0); +} + +void drawWidget (int /*long*/ id, NSGraphicsContext context, NSRect rect) { +} + +void redrawWidget (NSView view, boolean children) { + view.setNeedsDisplay(true); +} + +void redrawWidget (NSView view, int /*long*/ x, int /*long*/ y, int /*long*/ width, int /*long*/ height, boolean children) { + NSRect rect = new NSRect(); + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + view.setNeedsDisplayInRect(rect); +} + +void error (int code) { + SWT.error(code); +} + +void expandItem_expandChildren (int /*long*/ id, int /*long*/ sel, int /*long*/ item, boolean children) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + OS.objc_msgSendSuper(super_struct, sel, item, children); +} + +NSRect expansionFrameWithFrame_inView(int /*long*/ id, int /*long*/ sel, NSRect cellRect, int /*long*/ view) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + NSRect result = new NSRect(); + OS.objc_msgSendSuper_stret(result, super_struct, sel, cellRect, view); + return result; +} + +boolean filters (int eventType) { + return display.filters (eventType); +} + +NSRect firstRectForCharacterRange(int /*long*/ id, int /*long*/ sel, int /*long*/ range) { + return new NSRect (); +} + +int fixMnemonic (char [] buffer) { + int i=0, j=0; + while (i < buffer.length) { + if ((buffer [j++] = buffer [i++]) == '&') { + if (i == buffer.length) {continue;} + if (buffer [i] == '&') {i++; continue;} + j--; + } + } + return j; +} + +/** + * Returns the application defined widget data associated + * with the receiver, or null if it has not been set. The + * <em>widget data</em> is a single, unnamed field that is + * stored with every widget. + * <p> + * Applications may put arbitrary objects in this field. If + * the object stored in the widget data needs to be notified + * when the widget is disposed of, it is the application's + * responsibility to hook the Dispose event on the widget and + * do so. + * </p> + * + * @return the widget data + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - when the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - when called from the wrong thread</li> + * </ul> + * + * @see #setData(Object) + */ +public Object getData () { + checkWidget(); + return (state & KEYED_DATA) != 0 ? ((Object []) data) [0] : data; +} + +/** + * Returns the application defined property of the receiver + * with the specified name, or null if it has not been set. + * <p> + * Applications may have associated arbitrary objects with the + * receiver in this fashion. If the objects stored in the + * properties need to be notified when the widget is disposed + * of, it is the application's responsibility to hook the + * Dispose event on the widget and do so. + * </p> + * + * @param key the name of the property + * @return the value of the property or null if it has not been set + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the key is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #setData(String, Object) + */ +public Object getData (String key) { + checkWidget(); + if (key == null) error (SWT.ERROR_NULL_ARGUMENT); + if ((state & KEYED_DATA) != 0) { + Object [] table = (Object []) data; + for (int i=1; i<table.length; i+=2) { + if (key.equals (table [i])) return table [i+1]; + } + } + return null; +} + +/** + * Returns the <code>Display</code> that is associated with + * the receiver. + * <p> + * A widget's display is either provided when it is created + * (for example, top level <code>Shell</code>s) or is the + * same as its parent's display. + * </p> + * + * @return the receiver's display + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * </ul> + */ +public Display getDisplay () { + Display display = this.display; + if (display == null) error (SWT.ERROR_WIDGET_DISPOSED); + return display; +} + +boolean getDrawing () { + return true; +} + +/** + * Returns an array of listeners who will be notified when an event + * of the given type occurs. The event type is one of the event constants + * defined in class <code>SWT</code>. + * + * @param eventType the type of event to listen for + * @return an array of listeners that will be notified when the event occurs + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Listener + * @see SWT + * @see #addListener(int, Listener) + * @see #removeListener(int, Listener) + * @see #notifyListeners + * + * @since 3.4 + */ +public Listener[] getListeners (int eventType) { + checkWidget(); + if (eventTable == null) return new Listener[0]; + return eventTable.getListeners(eventType); +} + +String getName () { + String string = getClass ().getName (); + int index = string.lastIndexOf ('.'); + if (index == -1) return string; + return string.substring (index + 1, string.length ()); +} + +String getNameText () { + return ""; +} + +/** + * Returns the receiver's style information. + * <p> + * Note that the value which is returned by this method <em>may + * not match</em> the value which was provided to the constructor + * when the receiver was created. This can occur when the underlying + * operating system does not support a particular combination of + * requested styles. For example, if the platform widget used to + * implement a particular SWT widget always has scroll bars, the + * result of calling this method would always have the + * <code>SWT.H_SCROLL</code> and <code>SWT.V_SCROLL</code> bits set. + * </p> + * + * @return the style bits + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public int getStyle () { + checkWidget(); + return style; +} + +boolean hasMarkedText (int /*long*/ id, int /*long*/ sel) { + return false; +} + +void helpRequested(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { +} + +void highlightSelectionInClipRect(int /*long*/ id, int /*long*/ sel, int /*long*/ rect) { +} + +int /*long*/ hitTest (int /*long*/ id, int /*long*/ sel, NSPoint point) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + return OS.objc_msgSendSuper(super_struct, sel, point); +} + +int /*long*/ hitTestForEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ event, NSRect rect, int /*long*/ controlView) { + return 0; +} + +boolean hooks (int eventType) { + if (eventTable == null) return false; + return eventTable.hooks (eventType); +} + +int /*long*/ image (int /*long*/ id, int /*long*/ sel) { + return 0; +} + +NSRect imageRectForBounds (int /*long*/ id, int /*long*/ sel, NSRect cellFrame) { + return new NSRect(); +} + +boolean insertText (int /*long*/ id, int /*long*/ sel, int /*long*/ string) { + callSuper (id, sel, string); + return true; +} + +/** + * Returns <code>true</code> if the widget has been disposed, + * and <code>false</code> otherwise. + * <p> + * This method gets the dispose state for the widget. + * When a widget has been disposed, it is an error to + * invoke any other method using the widget. + * </p> + * + * @return <code>true</code> when the widget is disposed and <code>false</code> otherwise + */ +public boolean isDisposed () { + return (state & DISPOSED) != 0; +} + +boolean isDrawing () { + return true; +} + +boolean isFlipped(int /*long*/ id, int /*long*/ sel) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + return OS.objc_msgSendSuper(super_struct, sel) != 0; +} + +/** + * Returns <code>true</code> if there are any listeners + * for the specified event type associated with the receiver, + * and <code>false</code> otherwise. The event type is one of + * the event constants defined in class <code>SWT</code>. + * + * @param eventType the type of event + * @return true if the event is hooked + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SWT + */ +public boolean isListening (int eventType) { + checkWidget(); + return hooks (eventType); +} + +boolean isOpaque(int /*long*/ id, int /*long*/ sel) { + return false; +} + +boolean isValidSubclass () { + return Display.isValidClass (getClass ()); +} + +boolean isValidThread () { + return getDisplay ().isValidThread (); +} + +void flagsChanged (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper (id, sel, theEvent); +} + +void keyDown (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + superKeyDown(id, sel, theEvent); +} + +void keyUp (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + superKeyUp(id, sel, theEvent); +} + +void mouseDown(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper(id, sel, theEvent); +} + +void mouseUp(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper(id, sel, theEvent); +} + +void mouseMoved(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper(id, sel, theEvent); +} + +void mouseDragged(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper(id, sel, theEvent); +} + +void mouseEntered(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper(id, sel, theEvent); +} + +void mouseExited(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper(id, sel, theEvent); +} + +void cursorUpdate(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper(id, sel, theEvent); +} + +void rightMouseDown(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper(id, sel, theEvent); +} + +void rightMouseUp(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper(id, sel, theEvent); +} + +void rightMouseDragged(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper(id, sel, theEvent); +} + +void otherMouseDown(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper(id, sel, theEvent); +} + +void otherMouseUp(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper(id, sel, theEvent); +} + +void otherMouseDragged(int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper(id, sel, theEvent); +} + +boolean shouldDelayWindowOrderingForEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + return OS.objc_msgSendSuper(super_struct, sel, theEvent) != 0; +} + +boolean menuHasKeyEquivalent_forEvent_target_action(int /*long*/ id, int /*long*/ sel, int /*long*/ menu, int /*long*/ event, int /*long*/ target, int /*long*/ action) { + return true; +} + +int /*long*/ menuForEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + return OS.objc_msgSendSuper(super_struct, sel, theEvent); +} + +void menuNeedsUpdate(int /*long*/ id, int /*long*/ sel, int /*long*/ menu) { +} + +boolean makeFirstResponder(int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { + return callSuperBoolean(id, sel, notification); +} + +NSRange markedRange (int /*long*/ id, int /*long*/ sel) { + return new NSRange (); +} + +void menu_willHighlightItem(int /*long*/ id, int /*long*/ sel, int /*long*/ menu, int /*long*/ item) { +} + +void menuDidClose(int /*long*/ id, int /*long*/ sel, int /*long*/ menu) { +} + +void menuWillOpen(int /*long*/ id, int /*long*/ sel, int /*long*/ menu) { +} + +void noResponderFor(int /*long*/ id, int /*long*/ sel, int /*long*/ selector) { + callSuper(id, sel, selector); +} + +int /*long*/ numberOfRowsInTableView(int /*long*/ id, int /*long*/ sel, int /*long*/ aTableView) { + return 0; +} + +int /*long*/ outlineView_child_ofItem(int /*long*/ id, int /*long*/ sel, int /*long*/ outlineView, int /*long*/ index, int /*long*/ item) { + return 0; +} + +void outlineView_didClickTableColumn(int /*long*/ id, int /*long*/ sel, int /*long*/ outlineView, int /*long*/ tableColumn) { +} + +int /*long*/ outlineView_objectValueForTableColumn_byItem(int /*long*/ id, int /*long*/ sel, int /*long*/ outlineView, int /*long*/ tableColumn, int /*long*/ item) { + return 0; +} + +boolean outlineView_isItemExpandable(int /*long*/ id, int /*long*/ sel, int /*long*/ outlineView, int /*long*/ item) { + return false; +} + +int /*long*/ outlineView_numberOfChildrenOfItem(int /*long*/ id, int /*long*/ sel, int /*long*/ outlineView, int /*long*/ item) { + return 0; +} + +void outlineView_willDisplayCell_forTableColumn_item(int /*long*/ id, int /*long*/ sel, int /*long*/ outlineView, int /*long*/ cell, int /*long*/ tableColumn, int /*long*/ item) { +} + +void outlineViewColumnDidMove (int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { +} + +void outlineViewColumnDidResize (int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { +} + +void outlineViewSelectionDidChange(int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { +} + +void outlineView_setObjectValue_forTableColumn_byItem(int /*long*/ id, int /*long*/ sel, int /*long*/ outlineView, int /*long*/ object, int /*long*/ tableColumn, int /*long*/ item) { +} + +boolean outlineView_writeItems_toPasteboard(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1, int /*long*/ arg2) { + return false; +} + + +/** + * Notifies all of the receiver's listeners for events + * of the given type that one such event has occurred by + * invoking their <code>handleEvent()</code> method. The + * event type is one of the event constants defined in class + * <code>SWT</code>. + * + * @param eventType the type of event which has occurred + * @param event the event data + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see SWT + * @see #addListener + * @see #getListeners(int) + * @see #removeListener(int, Listener) + */ +public void notifyListeners (int eventType, Event event) { + checkWidget(); + if (event == null) event = new Event (); + sendEvent (eventType, event); +} + +void pageDown (int /*long*/ id, int /*long*/ sel, int /*long*/ sender) { + callSuper(id, sel, sender); +} + +void pageUp (int /*long*/ id, int /*long*/ sel, int /*long*/ sender) { + callSuper(id, sel, sender); +} + +void postEvent (int eventType) { + sendEvent (eventType, null, false); +} + +void postEvent (int eventType, Event event) { + sendEvent (eventType, event, false); +} + +void reflectScrolledClipView (int /*long*/ id, int /*long*/ sel, int /*long*/ aClipView) { + callSuper (id, sel, aClipView); +} + +void register () { +} + +void release (boolean destroy) { + if ((state & DISPOSE_SENT) == 0) { + state |= DISPOSE_SENT; + sendEvent (SWT.Dispose); + } + if ((state & DISPOSED) == 0) { + releaseChildren (destroy); + } + if ((state & RELEASED) == 0) { + state |= RELEASED; + if (destroy) { + releaseParent (); + releaseWidget (); + destroyWidget (); + } else { + releaseWidget (); + releaseHandle (); + } + } +} + +void releaseChildren (boolean destroy) { +} + +void releaseHandle () { + state |= DISPOSED; + display = null; + destroyJNIRef (); +} + +void releaseParent () { + /* Do nothing */ +} + +void releaseWidget () { + deregister (); + if (display.tooltipTarget == this) display.tooltipTarget = null; + eventTable = null; + data = null; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when an event of the given type occurs. The event + * type is one of the event constants defined in class <code>SWT</code>. + * + * @param eventType the type of event to listen for + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Listener + * @see SWT + * @see #addListener + * @see #getListeners(int) + * @see #notifyListeners + */ +public void removeListener (int eventType, Listener handler) { + checkWidget(); + if (handler == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (eventType, handler); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when an event of the given type occurs. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It should never be + * referenced from application code. + * </p> + * + * @param eventType the type of event to listen for + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Listener + * @see #addListener + */ +protected void removeListener (int eventType, SWTEventListener handler) { + checkWidget(); + if (handler == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (eventType, handler); +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when the widget is disposed. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see DisposeListener + * @see #addDisposeListener + */ +public void removeDisposeListener (DisposeListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Dispose, listener); +} + +void scrollWheel (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper(id, sel, theEvent); +} + +NSRange selectedRange (int /*long*/ id, int /*long*/ sel) { + return new NSRange (); +} + +int /*long*/ nextValidKeyView (int /*long*/ id, int /*long*/ sel) { + return callSuperObject(id, sel); +} + +int /*long*/ previousValidKeyView (int /*long*/ id, int /*long*/ sel) { + return callSuperObject(id, sel); +} + +void sendDoubleSelection() { +} + +void sendEvent (Event event) { + display.sendEvent (eventTable, event); +} + +void sendEvent (int eventType) { + sendEvent (eventType, null, true); +} + +void sendEvent (int eventType, Event event) { + sendEvent (eventType, event, true); +} + +void sendEvent (int eventType, Event event, boolean send) { + if (eventTable == null && !display.filters (eventType)) { + return; + } + if (event == null) event = new Event (); + event.type = eventType; + event.display = display; + event.widget = this; + if (event.time == 0) { + event.time = display.getLastEventTime (); + } + if (send) { + sendEvent (event); + } else { + display.postEvent (event); + } +} + +boolean sendKeyEvent (NSEvent nsEvent, int type) { + if ((state & SAFARI_EVENTS_FIX) != 0) return true; + Event event = new Event (); + if (!setKeyState (event, type, nsEvent)) return true; + return sendKeyEvent (type, event); +} + +boolean sendKeyEvent (int type, Event event) { + sendEvent (type, event); + // widget could be disposed at this point + + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the key + * events. If this happens, end the processing of + * the key by returning false. + */ + if (isDisposed ()) return false; + return event.doit; +} + +void sendHorizontalSelection () { +} + +void sendCancelSelection () { +} + +void sendSearchSelection () { +} + +void sendSelection () { +} + +void sendVerticalSelection () { +} + +/** + * Sets the application defined widget data associated + * with the receiver to be the argument. The <em>widget + * data</em> is a single, unnamed field that is stored + * with every widget. + * <p> + * Applications may put arbitrary objects in this field. If + * the object stored in the widget data needs to be notified + * when the widget is disposed of, it is the application's + * responsibility to hook the Dispose event on the widget and + * do so. + * </p> + * + * @param data the widget data + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - when the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - when called from the wrong thread</li> + * </ul> + * + * @see #getData() + */ +public void setData (Object data) { + checkWidget(); + if (SAFARI_EVENTS_FIX_KEY.equals (data)) { + state |= SAFARI_EVENTS_FIX; + return; + } + if ((state & KEYED_DATA) != 0) { + ((Object []) this.data) [0] = data; + } else { + this.data = data; + } +} + +/** + * Sets the application defined property of the receiver + * with the specified name to the given value. + * <p> + * Applications may associate arbitrary objects with the + * receiver in this fashion. If the objects stored in the + * properties need to be notified when the widget is disposed + * of, it is the application's responsibility to hook the + * Dispose event on the widget and do so. + * </p> + * + * @param key the name of the property + * @param value the new value for the property + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the key is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see #getData(String) + */ +public void setData (String key, Object value) { + checkWidget(); + if (key == null) error (SWT.ERROR_NULL_ARGUMENT); + if (GLCONTEXT_KEY.equals (key)) { + setOpenGLContext(value); + return; + } + int index = 1; + Object [] table = null; + if ((state & KEYED_DATA) != 0) { + table = (Object []) data; + while (index < table.length) { + if (key.equals (table [index])) break; + index += 2; + } + } + if (value != null) { + if ((state & KEYED_DATA) != 0) { + if (index == table.length) { + Object [] newTable = new Object [table.length + 2]; + System.arraycopy (table, 0, newTable, 0, table.length); + data = table = newTable; + } + } else { + table = new Object [3]; + table [0] = data; + data = table; + state |= KEYED_DATA; + } + table [index] = key; + table [index + 1] = value; + } else { + if ((state & KEYED_DATA) != 0) { + if (index != table.length) { + int length = table.length - 2; + if (length == 1) { + data = table [0]; + state &= ~KEYED_DATA; + } else { + Object [] newTable = new Object [length]; + System.arraycopy (table, 0, newTable, 0, index); + System.arraycopy (table, index + 2, newTable, index, length - index); + data = newTable; + } + } + } + } +} + +void setOpenGLContext(Object value) { +} + +void setFrameOrigin (int /*long*/ id, int /*long*/ sel, NSPoint point) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + OS.objc_msgSendSuper(super_struct, sel, point); +} + +void setFrameSize (int /*long*/ id, int /*long*/ sel, NSSize size) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + OS.objc_msgSendSuper(super_struct, sel, size); +} + +void setImage (int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { +} + +boolean setInputState (Event event, NSEvent nsEvent, int type) { + if (nsEvent == null) return true; + int /*long*/ modifierFlags = nsEvent.modifierFlags(); + if ((modifierFlags & OS.NSAlternateKeyMask) != 0) event.stateMask |= SWT.ALT; + if ((modifierFlags & OS.NSShiftKeyMask) != 0) event.stateMask |= SWT.SHIFT; + if ((modifierFlags & OS.NSControlKeyMask) != 0) event.stateMask |= SWT.CONTROL; + if ((modifierFlags & OS.NSCommandKeyMask) != 0) event.stateMask |= SWT.COMMAND; + //TODO multiple mouse buttons pressed + switch ((int)/*64*/nsEvent.type()) { + case OS.NSLeftMouseDragged: + case OS.NSRightMouseDragged: + case OS.NSOtherMouseDragged: + switch ((int)/*64*/nsEvent.buttonNumber()) { + case 0: event.stateMask |= SWT.BUTTON1; break; + case 1: event.stateMask |= SWT.BUTTON3; break; + case 2: event.stateMask |= SWT.BUTTON2; break; + case 3: event.stateMask |= SWT.BUTTON4; break; + case 4: event.stateMask |= SWT.BUTTON5; break; + } + break; + case OS.NSScrollWheel: + case OS.NSKeyDown: + case OS.NSKeyUp: + int state = OS.GetCurrentButtonState (); + if ((state & 0x1) != 0) event.stateMask |= SWT.BUTTON1; + if ((state & 0x2) != 0) event.stateMask |= SWT.BUTTON3; + if ((state & 0x4) != 0) event.stateMask |= SWT.BUTTON2; + if ((state & 0x8) != 0) event.stateMask |= SWT.BUTTON4; + if ((state & 0x10) != 0) event.stateMask |= SWT.BUTTON5; + break; + } + switch (type) { + case SWT.MouseDown: + case SWT.MouseDoubleClick: + if (event.button == 1) event.stateMask &= ~SWT.BUTTON1; + if (event.button == 2) event.stateMask &= ~SWT.BUTTON2; + if (event.button == 3) event.stateMask &= ~SWT.BUTTON3; + if (event.button == 4) event.stateMask &= ~SWT.BUTTON4; + if (event.button == 5) event.stateMask &= ~SWT.BUTTON5; + break; + case SWT.MouseUp: + if (event.button == 1) event.stateMask |= SWT.BUTTON1; + if (event.button == 2) event.stateMask |= SWT.BUTTON2; + if (event.button == 3) event.stateMask |= SWT.BUTTON3; + if (event.button == 4) event.stateMask |= SWT.BUTTON4; + if (event.button == 5) event.stateMask |= SWT.BUTTON5; + break; + case SWT.KeyDown: + case SWT.Traverse: + if (event.keyCode == SWT.ALT) event.stateMask &= ~SWT.ALT; + if (event.keyCode == SWT.SHIFT) event.stateMask &= ~SWT.SHIFT; + if (event.keyCode == SWT.CONTROL) event.stateMask &= ~SWT.CONTROL; + if (event.keyCode == SWT.COMMAND) event.stateMask &= ~SWT.COMMAND; + break; + case SWT.KeyUp: + if (event.keyCode == SWT.ALT) event.stateMask |= SWT.ALT; + if (event.keyCode == SWT.SHIFT) event.stateMask |= SWT.SHIFT; + if (event.keyCode == SWT.CONTROL) event.stateMask |= SWT.CONTROL; + if (event.keyCode == SWT.COMMAND) event.stateMask |= SWT.COMMAND; + break; + } + return true; +} + +boolean setKeyState (Event event, int type, NSEvent nsEvent) { + boolean isNull = false; + int keyCode = nsEvent.keyCode (); + event.keyCode = Display.translateKey (keyCode); + switch (event.keyCode) { + case SWT.LF: { + /* + * Feature in the Macintosh. When the numeric key pad + * Enter key is pressed, it generates '\n'. This is the + * correct platform behavior but is not portable. The + * fix is to convert the '\n' into '\r'. + */ + event.keyCode = SWT.KEYPAD_CR; + event.character = '\r'; + break; + } + case SWT.BS: event.character = '\b'; break; + case SWT.CR: event.character = '\r'; break; + case SWT.DEL: event.character = 0x7F; break; + case SWT.ESC: event.character = 0x1B; break; + case SWT.TAB: event.character = '\t'; break; + default: + if (event.keyCode == 0 || (SWT.KEYPAD_MULTIPLY <= event.keyCode && event.keyCode <= SWT.KEYPAD_CR)) { + NSString chars = nsEvent.characters (); + if (chars.length() > 0) event.character = (char)chars.characterAtIndex (0); + } + if (event.keyCode == 0) { + int /*long*/ uchrPtr = 0; + int /*long*/ currentKbd = OS.TISCopyCurrentKeyboardInputSource(); + int /*long*/ uchrCFData = OS.TISGetInputSourceProperty(currentKbd, OS.kTISPropertyUnicodeKeyLayoutData()); + + if (uchrCFData != 0) { + // If the keyboard changed since the last keystroke clear the dead key state. + if (uchrCFData != display.currentKeyboardUCHRdata) display.deadKeyState[0] = 0; + uchrPtr = OS.CFDataGetBytePtr(uchrCFData); + + if (uchrPtr != 0 && OS.CFDataGetLength(uchrCFData) > 0) { + int /*long*/ cgEvent = nsEvent.CGEvent(); + long keyboardType = OS.CGEventGetIntegerValueField(cgEvent, OS.kCGKeyboardEventKeyboardType); + + int maxStringLength = 256; + char [] output = new char [maxStringLength]; + int [] actualStringLength = new int [1]; + OS.UCKeyTranslate (uchrPtr, (short)keyCode, (short)OS.kUCKeyActionDown, 0, (int)keyboardType, 0, display.deadKeyState, maxStringLength, actualStringLength, output); + if (actualStringLength[0] < 1) { + // part of a multi-key key + event.keyCode = 0; + } else { + event.keyCode = output[0]; + } + } + } else { + // KCHR keyboard layouts are no longer supported, so fall back to the basic but flawed + // method of determining which key was pressed. + NSString unmodifiedChars = nsEvent.charactersIgnoringModifiers ().lowercaseString(); + if (unmodifiedChars.length() > 0) event.keyCode = (char)unmodifiedChars.characterAtIndex(0); + } + + if (currentKbd != 0) OS.CFRelease(currentKbd); + } + } + if (event.keyCode == 0 && event.character == 0) { + if (!isNull) return false; + } + setInputState (event, nsEvent, type); + return true; +} + +boolean setMarkedText_selectedRange (int /*long*/ id, int /*long*/ sel, int /*long*/ string, int /*long*/ range) { + return true; +} + +void setNeedsDisplay (int /*long*/ id, int /*long*/ sel, boolean flag) { + if (flag && !isDrawing()) return; + NSView view = new NSView(id); + if (flag && display.isPainting.containsObject(view)) { + NSMutableArray needsDisplay = display.needsDisplay; + if (needsDisplay == null) { + needsDisplay = (NSMutableArray)new NSMutableArray().alloc(); + display.needsDisplay = needsDisplay = needsDisplay.initWithCapacity(12); + } + needsDisplay.addObject(view); + return; + } + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + OS.objc_msgSendSuper(super_struct, sel, flag); +} + +void setNeedsDisplayInRect (int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + if (!isDrawing()) return; + NSRect rect = new NSRect(); + OS.memmove(rect, arg0, NSRect.sizeof); + NSView view = new NSView(id); + if (display.isPainting.containsObject(view)) { + NSMutableArray needsDisplayInRect = display.needsDisplayInRect; + if (needsDisplayInRect == null) { + needsDisplayInRect = (NSMutableArray)new NSMutableArray().alloc(); + display.needsDisplayInRect = needsDisplayInRect = needsDisplayInRect.initWithCapacity(12); + } + needsDisplayInRect.addObject(view); + needsDisplayInRect.addObject(NSValue.valueWithRect(rect)); + return; + } + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + OS.objc_msgSendSuper(super_struct, sel, rect); +} + +void setObjectValue(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + callSuper(id, sel, arg0); +} + +boolean setTabGroupFocus () { + return setTabItemFocus (); +} + +boolean setTabItemFocus () { + return false; +} + +boolean shouldChangeTextInRange_replacementString(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1) { + return true; +} + +void superKeyDown (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper (id, sel, theEvent); +} + +void superKeyUp (int /*long*/ id, int /*long*/ sel, int /*long*/ theEvent) { + callSuper (id, sel, theEvent); +} + +void tableViewColumnDidMove (int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { +} + +void tableViewColumnDidResize (int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { +} + +void tableViewSelectionDidChange (int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { +} + +void tableView_didClickTableColumn(int /*long*/ id, int /*long*/ sel, int /*long*/ tableView, int /*long*/ tableColumn) { +} + +int /*long*/ tableView_objectValueForTableColumn_row(int /*long*/ id, int /*long*/ sel, int /*long*/ aTableView, int /*long*/ aTableColumn, int /*long*/ rowIndex) { + return 0; +} + +void tableView_setObjectValue_forTableColumn_row(int /*long*/ id, int /*long*/ sel, int /*long*/ aTableView, int /*long*/ anObject, int /*long*/ aTableColumn, int /*long*/ rowIndex) { +} + +boolean tableView_shouldEditTableColumn_row(int /*long*/ id, int /*long*/ sel, int /*long*/ aTableView, int /*long*/ aTableColumn, int /*long*/ rowIndex) { + return true; +} + +void tableView_willDisplayCell_forTableColumn_row(int /*long*/ id, int /*long*/ sel, int /*long*/ aTableView, int /*long*/ aCell, int /*long*/ aTableColumn, int /*long*/ rowIndex) { +} + +void textViewDidChangeSelection(int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { +} + +void textDidChange(int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { + callSuper (id, sel, aNotification); +} + +void textDidEndEditing(int /*long*/ id, int /*long*/ sel, int /*long*/ aNotification) { + callSuper(id, sel, aNotification); +} + +NSRange textView_willChangeSelectionFromCharacterRange_toCharacterRange(int /*long*/ id, int /*long*/ sel, int /*long*/ aTextView, int /*long*/ oldSelectedCharRange, int /*long*/ newSelectedCharRange) { + return new NSRange(); +} + +NSRect titleRectForBounds (int /*long*/ id, int /*long*/ sel, NSRect cellFrame) { + objc_super super_struct = new objc_super(); + super_struct.receiver = id; + super_struct.super_class = OS.objc_msgSend(id, OS.sel_superclass); + NSRect result = new NSRect(); + OS.objc_msgSendSuper_stret(result, super_struct, sel, cellFrame); + return result; +} + +String tooltipText () { + return null; +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the receiver + */ +public String toString () { + String string = "*Disposed*"; + if (!isDisposed ()) { + string = "*Wrong Thread*"; + if (isValidThread ()) string = getNameText (); + } + return getName () + " {" + string + "}"; +} + +void resetCursorRects (int /*long*/ id, int /*long*/ sel) { + callSuper (id, sel); +} + +void updateTrackingAreas (int /*long*/ id, int /*long*/ sel) { + callSuper (id, sel); +} + +int /*long*/ validAttributesForMarkedText (int /*long*/ id, int /*long*/ sel) { + return 0; +} + +void tabView_didSelectTabViewItem(int /*long*/ id, int /*long*/ sel, int /*long*/ tabView, int /*long*/ tabViewItem) { +} + +void tabView_willSelectTabViewItem(int /*long*/ id, int /*long*/ sel, int /*long*/ tabView, int /*long*/ tabViewItem) { +} + +boolean tableView_writeRowsWithIndexes_toPasteboard(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0, int /*long*/ arg1, int /*long*/ arg2) { + return false; +} + +int /*long*/ view_stringForToolTip_point_userData (int /*long*/ id, int /*long*/ sel, int /*long*/ view, int /*long*/ tag, int /*long*/ point, int /*long*/ userData) { + return 0; +} + +void viewDidMoveToWindow(int /*long*/ id, int /*long*/ sel) { +} + +void windowDidMove(int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { +} + +void windowDidResize(int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { +} + +void windowDidResignKey(int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { +} + +void windowDidBecomeKey(int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { +} + +void windowSendEvent(int /*long*/ id, int /*long*/ sel, int /*long*/ event) { + callSuper(id, sel, event); +} + +boolean windowShouldClose(int /*long*/ id, int /*long*/ sel, int /*long*/ window) { + return false; +} + +void windowWillClose(int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { +} + +int /*long*/ nextState(int /*long*/ id, int /*long*/ sel) { + return callSuperObject(id, sel); +} + +void updateOpenGLContext(int /*long*/ id, int /*long*/ sel, int /*long*/ notification) { +} + +} |