diff options
Diffstat (limited to 'bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse')
65 files changed, 86115 insertions, 0 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Color.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Color.java new file mode 100755 index 0000000000..3603b53a65 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Color.java @@ -0,0 +1,325 @@ +/******************************************************************************* + * 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.win32.*; +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 int handle; + +/** + * Prevents uninitialized instances from being created outside the package. + */ +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() { + /* + * If this is a palette-based device, + * Decrease the reference count for this color. + * If the reference count reaches 0, the slot may + * be reused when another color is allocated. + */ + int /*long*/ hPal = device.hPalette; + if (hPal != 0) { + int index = OS.GetNearestPaletteIndex(hPal, handle); + int[] colorRefCount = device.colorRefCount; + if (colorRefCount[index] > 0) { + colorRefCount[index]--; + } + } + handle = -1; +} + +/** + * 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; + return device == color.device && (handle & 0xFFFFFF) == (color.handle & 0xFFFFFF); +} + +/** + * 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 (handle & 0xFF0000) >> 16; +} + +/** + * 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 (handle & 0xFF00) >> 8 ; +} + +/** + * 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 handle & 0xFF; +} + +/** + * 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(handle & 0xFF, (handle & 0xFF00) >> 8, (handle & 0xFF0000) >> 16); +} + +/** + * 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; +} + +/** + * Allocates the operating system resources associated + * with the receiver. + * + * @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_INVALID_ARGUMENT - if the red, green or blue argument is not between 0 and 255</li> + * </ul> + * + * @see #dispose + */ +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); + } + handle = (red & 0xFF) | ((green & 0xFF) << 8) | ((blue & 0xFF) << 16); + + /* If this is not a palette-based device, return */ + int /*long*/ hPal = device.hPalette; + if (hPal == 0) return; + + int[] colorRefCount = device.colorRefCount; + /* Add this color to the default palette now */ + /* First find out if the color already exists */ + int index = OS.GetNearestPaletteIndex(hPal, handle); + /* See if the nearest color actually is the color */ + byte[] entry = new byte[4]; + OS.GetPaletteEntries(hPal, index, 1, entry); + if ((entry[0] == (byte)red) && (entry[1] == (byte)green) && + (entry[2] == (byte)blue)) { + /* Found the color. Increment the ref count and return */ + colorRefCount[index]++; + return; + } + /* Didn't find the color, allocate it now. Find the first free entry */ + int i = 0; + while (i < colorRefCount.length) { + if (colorRefCount[i] == 0) { + index = i; + break; + } + i++; + } + if (i == colorRefCount.length) { + /* No free entries, use the closest one */ + /* Remake the handle from the actual rgbs */ + handle = (entry[0] & 0xFF) | ((entry[1] & 0xFF) << 8) | + ((entry[2] & 0xFF) << 16); + } else { + /* Found a free entry */ + entry = new byte[] { (byte)(red & 0xFF), (byte)(green & 0xFF), (byte)(blue & 0xFF), 0 }; + OS.SetPaletteEntries(hPal, index, 1, entry); + } + colorRefCount[index]++; +} + +/** + * 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 == -1; +} + +/** + * 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*}"; //$NON-NLS-1$ + return "Color {" + getRed() + ", " + getGreen() + ", " + getBlue() + "}"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ +} + +/** + * 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 + * @return a new color object containing the specified device and handle + */ +public static Color win32_new(Device device, int handle) { + Color color = new Color(device); + color.handle = handle; + return color; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Cursor.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Cursor.java new file mode 100755 index 0000000000..6c5273cc22 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Cursor.java @@ -0,0 +1,460 @@ +/******************************************************************************* + * 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.win32.*; +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 { + + /** + * 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 int /*long*/ handle; + + boolean isIcon; + + /** + * data used to create a HAND cursor. + */ + static final byte[] HAND_SOURCE = { + (byte)0xf9,(byte)0xff,(byte)0xff,(byte)0xff, + (byte)0xf0,(byte)0xff,(byte)0xff,(byte)0xff, + (byte)0xf0,(byte)0xff,(byte)0xff,(byte)0xff, + (byte)0xf0,(byte)0xff,(byte)0xff,(byte)0xff, + (byte)0xf0,(byte)0x3f,(byte)0xff,(byte)0xff, + (byte)0xf0,(byte)0x07,(byte)0xff,(byte)0xff, + (byte)0xf0,(byte)0x03,(byte)0xff,(byte)0xff, + (byte)0xf0,(byte)0x00,(byte)0xff,(byte)0xff, + + (byte)0x10,(byte)0x00,(byte)0x7f,(byte)0xff, + (byte)0x00,(byte)0x00,(byte)0x7f,(byte)0xff, + (byte)0x80,(byte)0x00,(byte)0x7f,(byte)0xff, + (byte)0xc0,(byte)0x00,(byte)0x7f,(byte)0xff, + (byte)0xe0,(byte)0x00,(byte)0x7f,(byte)0xff, + (byte)0xf0,(byte)0x00,(byte)0x7f,(byte)0xff, + (byte)0xf8,(byte)0x00,(byte)0xff,(byte)0xff, + (byte)0xfc,(byte)0x01,(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)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)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 + }; + static final byte[] HAND_MASK = { + (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00, + (byte)0x06,(byte)0x00,(byte)0x00,(byte)0x00, + (byte)0x06,(byte)0x00,(byte)0x00,(byte)0x00, + (byte)0x06,(byte)0x00,(byte)0x00,(byte)0x00, + (byte)0x06,(byte)0x00,(byte)0x00,(byte)0x00, + (byte)0x06,(byte)0xc0,(byte)0x00,(byte)0x00, + (byte)0x06,(byte)0xd8,(byte)0x00,(byte)0x00, + (byte)0x06,(byte)0xd8,(byte)0x00,(byte)0x00, + + (byte)0x07,(byte)0xdb,(byte)0x00,(byte)0x00, + (byte)0x67,(byte)0xfb,(byte)0x00,(byte)0x00, + (byte)0x3f,(byte)0xff,(byte)0x00,(byte)0x00, + (byte)0x1f,(byte)0xff,(byte)0x00,(byte)0x00, + (byte)0x0f,(byte)0xff,(byte)0x00,(byte)0x00, + (byte)0x07,(byte)0xff,(byte)0x00,(byte)0x00, + (byte)0x03,(byte)0xfe,(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 + }; + +/** + * 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); + int /*long*/ lpCursorName = 0; + switch (style) { + case SWT.CURSOR_HAND: lpCursorName = OS.IDC_HAND; break; + case SWT.CURSOR_ARROW: lpCursorName = OS.IDC_ARROW; break; + case SWT.CURSOR_WAIT: lpCursorName = OS.IDC_WAIT; break; + case SWT.CURSOR_CROSS: lpCursorName = OS.IDC_CROSS; break; + case SWT.CURSOR_APPSTARTING: lpCursorName = OS.IDC_APPSTARTING; break; + case SWT.CURSOR_HELP: lpCursorName = OS.IDC_HELP; break; + case SWT.CURSOR_SIZEALL: lpCursorName = OS.IDC_SIZEALL; break; + case SWT.CURSOR_SIZENESW: lpCursorName = OS.IDC_SIZENESW; break; + case SWT.CURSOR_SIZENS: lpCursorName = OS.IDC_SIZENS; break; + case SWT.CURSOR_SIZENWSE: lpCursorName = OS.IDC_SIZENWSE; break; + case SWT.CURSOR_SIZEWE: lpCursorName = OS.IDC_SIZEWE; break; + case SWT.CURSOR_SIZEN: lpCursorName = OS.IDC_SIZENS; break; + case SWT.CURSOR_SIZES: lpCursorName = OS.IDC_SIZENS; break; + case SWT.CURSOR_SIZEE: lpCursorName = OS.IDC_SIZEWE; break; + case SWT.CURSOR_SIZEW: lpCursorName = OS.IDC_SIZEWE; break; + case SWT.CURSOR_SIZENE: lpCursorName = OS.IDC_SIZENESW; break; + case SWT.CURSOR_SIZESE: lpCursorName = OS.IDC_SIZENWSE; break; + case SWT.CURSOR_SIZESW: lpCursorName = OS.IDC_SIZENESW; break; + case SWT.CURSOR_SIZENW: lpCursorName = OS.IDC_SIZENWSE; break; + case SWT.CURSOR_UPARROW: lpCursorName = OS.IDC_UPARROW; break; + case SWT.CURSOR_IBEAM: lpCursorName = OS.IDC_IBEAM; break; + case SWT.CURSOR_NO: lpCursorName = OS.IDC_NO; break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + handle = OS.LoadCursor(0, lpCursorName); + /* + * IDC_HAND is supported only on Windows 2000 and Windows 98. + * Create a hand cursor if running in other Windows platforms. + */ + if (handle == 0 && style == SWT.CURSOR_HAND) { + int width = OS.GetSystemMetrics(OS.SM_CXCURSOR); + int height = OS.GetSystemMetrics(OS.SM_CYCURSOR); + if (width == 32 && height == 32) { + int /*long*/ hInst = OS.GetModuleHandle(null); + if (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + handle = OS.CreateCursor(hInst, 5, 0, 32, 32, HAND_SOURCE, HAND_MASK); + + } + } + if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); + init(); +} + +/** + * 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); + } + /* Convert depth to 1 */ + mask = ImageData.convertMask(mask); + source = ImageData.convertMask(source); + + /* Make sure source and mask scanline pad is 2 */ + byte[] sourceData = ImageData.convertPad(source.data, source.width, source.height, source.depth, source.scanlinePad, 2); + byte[] maskData = ImageData.convertPad(mask.data, mask.width, mask.height, mask.depth, mask.scanlinePad, 2); + + /* Create the cursor */ + int /*long*/ hInst = OS.GetModuleHandle(null); + if (OS.IsWinCE) SWT.error (SWT.ERROR_NOT_IMPLEMENTED); + handle = OS.CreateCursor(hInst, hotspotX, hotspotY, source.width, source.height, sourceData, maskData); + if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); + init(); +} + +/** + * 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); + /* Check the hotspots */ + if (hotspotX >= source.width || hotspotX < 0 || + hotspotY >= source.height || hotspotY < 0) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + ImageData mask = source.getTransparencyMask(); + int /*long*/ [] result = Image.init(this.device, null, source, mask); + int /*long*/ hBitmap = result[0]; + int /*long*/ hMask = result[1]; + /* Create the icon */ + ICONINFO info = new ICONINFO(); + info.fIcon = false; + info.hbmColor = hBitmap; + info.hbmMask = hMask; + info.xHotspot = hotspotX; + info.yHotspot = hotspotY; + handle = OS.CreateIconIndirect(info); + if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.DeleteObject(hBitmap); + OS.DeleteObject(hMask); + isIcon = true; + init(); +} + +void destroy () { + /* + * It is an error in Windows to destroy the current + * cursor. Check that the cursor that is about to + * be destroyed is the current cursor. If so, set + * the current cursor to be IDC_ARROW. Note that + * Windows shares predefined cursors so the call to + * LoadCursor() does not leak. + */ + // TEMPORARY CODE +// if (OS.GetCursor() == handle) { +// OS.SetCursor(OS.LoadCursor(0, OS.IDC_ARROW)); +// } + + if (isIcon) { + OS.DestroyIcon(handle); + } else { + /* + * The MSDN states that one should not destroy a shared + * cursor, that is, one obtained from LoadCursor. + * However, it does not appear to do any harm, so rather + * than keep track of how a cursor was created, we just + * destroy them all. If this causes problems in the future, + * put the flag back in. + */ + if (!OS.IsWinCE) OS.DestroyCursor(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 (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 (int)/*64*/handle; +} + +/** + * 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 == 0; +} + +/** + * 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 + * @return a new cursor object containing the specified device and handle + */ +public static Cursor win32_new(Device device, int handle) { + Cursor cursor = new Cursor(device); + cursor.handle = handle; + return cursor; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Device.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Device.java new file mode 100755 index 0000000000..7745d9088a --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Device.java @@ -0,0 +1,972 @@ +/******************************************************************************* + * 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.gdip.*; +import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.*; + +/** + * 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; + + /** + * Palette + * (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*/ hPalette = 0; + int [] colorRefCount; + + /* System Font */ + Font systemFont; + + /* Font Enumeration */ + int nFonts = 256; + LOGFONT [] logFonts; + TEXTMETRIC metrics; + int[] pixels; + + /* Scripts */ + int /*long*/ [] scripts; + + /* Advanced Graphics */ + int /*long*/ [] gdipToken; + int /*long*/ fontCollection; + String[] loadedFonts; + + boolean disposed; + + /* + * 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"); //$NON-NLS-1$ + } 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 (); + } + create (data); + init (); + } +} + +void addFont (String font) { + if (loadedFonts == null) loadedFonts = new String [4]; + int length = loadedFonts.length; + for (int i=0; i<length; i++) { + if (font.equals(loadedFonts [i])) return; + } + int index = 0; + while (index < length) { + if (loadedFonts [index] == null) break; + index++; + } + if (index == length) { + String [] temp = new String [length + 4]; + System.arraycopy (loadedFonts, 0, temp, 0, length); + loadedFonts = temp; + } + loadedFonts [index] = font; +} + +/** + * 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); +} + +void checkGDIP() { + if (gdipToken != null) return; + int oldErrorMode = 0; + if (!OS.IsWinCE) oldErrorMode = OS.SetErrorMode (OS.SEM_FAILCRITICALERRORS); + try { + int /*long*/ [] token = new int /*long*/ [1]; + GdiplusStartupInput input = new GdiplusStartupInput (); + input.GdiplusVersion = 1; + if (Gdip.GdiplusStartup (token, input, 0) == 0) { + gdipToken = token; + if (loadedFonts != null) { + fontCollection = Gdip.PrivateFontCollection_new(); + if (fontCollection == 0) SWT.error(SWT.ERROR_NO_HANDLES); + for (int i = 0; i < loadedFonts.length; i++) { + String path = loadedFonts[i]; + if (path == null) break; + int length = path.length(); + char [] buffer = new char [length + 1]; + path.getChars(0, length, buffer, 0); + Gdip.PrivateFontCollection_AddFontFile(fontCollection, buffer); + } + loadedFonts = null; + } + } + } catch (Throwable t) { + SWT.error (SWT.ERROR_NO_GRAPHICS_LIBRARY, t, " [GDI+ is required]"); //$NON-NLS-1$ + } finally { + if (!OS.IsWinCE) OS.SetErrorMode (oldErrorMode); + } +} + +/** + * 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) { +} + +int computePixels(float height) { + int /*long*/ hDC = internal_new_GC (null); + int pixels = -(int)(0.5f + (height * OS.GetDeviceCaps(hDC, OS.LOGPIXELSY) / 72f)); + internal_dispose_GC (hDC, null); + return pixels; +} + +float computePoints(LOGFONT logFont, int /*long*/ hFont) { + int /*long*/ hDC = internal_new_GC (null); + int logPixelsY = OS.GetDeviceCaps(hDC, OS.LOGPIXELSY); + int pixels = 0; + if (logFont.lfHeight > 0) { + /* + * Feature in Windows. If the lfHeight of the LOGFONT structure + * is positive, the lfHeight measures the height of the entire + * cell, including internal leading, in logical units. Since the + * height of a font in points does not include the internal leading, + * we must subtract the internal leading, which requires a TEXTMETRIC. + */ + int /*long*/ oldFont = OS.SelectObject(hDC, hFont); + TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA(); + OS.GetTextMetrics(hDC, lptm); + OS.SelectObject(hDC, oldFont); + pixels = logFont.lfHeight - lptm.tmInternalLeading; + } else { + pixels = -logFont.lfHeight; + } + internal_dispose_GC (hDC, null); + return pixels * 72f / logPixelsY; +} + +/** + * 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 () { +} + +/** + * 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; + } + } + } +} + +int /*long*/ EnumFontFamProc (int /*long*/ lpelfe, int /*long*/ lpntme, int /*long*/ FontType, int /*long*/ lParam) { + boolean isScalable = ((int)/*64*/FontType & OS.RASTER_FONTTYPE) == 0; + boolean scalable = lParam == 1; + if (isScalable == scalable) { + /* Add the log font to the list of log fonts */ + if (nFonts == logFonts.length) { + LOGFONT [] newLogFonts = new LOGFONT [logFonts.length + 128]; + System.arraycopy (logFonts, 0, newLogFonts, 0, nFonts); + logFonts = newLogFonts; + int[] newPixels = new int[newLogFonts.length]; + System.arraycopy (pixels, 0, newPixels, 0, nFonts); + pixels = newPixels; + } + LOGFONT logFont = logFonts [nFonts]; + if (logFont == null) logFont = OS.IsUnicode ? (LOGFONT)new LOGFONTW () : new LOGFONTA (); + OS.MoveMemory (logFont, lpelfe, LOGFONT.sizeof); + logFonts [nFonts] = logFont; + if (logFont.lfHeight > 0) { + /* + * Feature in Windows. If the lfHeight of the LOGFONT structure + * is positive, the lfHeight measures the height of the entire + * cell, including internal leading, in logical units. Since the + * height of a font in points does not include the internal leading, + * we must subtract the internal leading, which requires a TEXTMETRIC, + * which in turn requires font creation. + */ + OS.MoveMemory(metrics, lpntme, TEXTMETRIC.sizeof); + pixels[nFonts] = logFont.lfHeight - metrics.tmInternalLeading; + } else { + pixels[nFonts] = -logFont.lfHeight; + } + nFonts++; + } + return 1; +} + +/** + * 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 (); + int /*long*/ hDC = internal_new_GC (null); + int width = OS.GetDeviceCaps (hDC, OS.HORZRES); + int height = OS.GetDeviceCaps (hDC, OS.VERTRES); + internal_dispose_GC (hDC, null); + return new Rectangle (0, 0, width, 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 () { + 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 (); + int /*long*/ hDC = internal_new_GC (null); + int bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL); + int planes = OS.GetDeviceCaps (hDC, OS.PLANES); + internal_dispose_GC (hDC, null); + return bits * planes; +} + +/** + * 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 (); + int /*long*/ hDC = internal_new_GC (null); + int dpiX = OS.GetDeviceCaps (hDC, OS.LOGPIXELSX); + int dpiY = OS.GetDeviceCaps (hDC, OS.LOGPIXELSY); + internal_dispose_GC (hDC, null); + return new Point (dpiX, dpiY); +} + +/** + * 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 (); + + /* Create the callback */ + Callback callback = new Callback (this, "EnumFontFamProc", 4); //$NON-NLS-1$ + int /*long*/ lpEnumFontFamProc = callback.getAddress (); + if (lpEnumFontFamProc == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + + /* Initialize the instance variables */ + metrics = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA(); + pixels = new int[nFonts]; + logFonts = new LOGFONT [nFonts]; + for (int i=0; i<logFonts.length; i++) { + logFonts [i] = OS.IsUnicode ? (LOGFONT) new LOGFONTW () : new LOGFONTA (); + } + nFonts = 0; + + /* Enumerate */ + int offset = 0; + int /*long*/ hDC = internal_new_GC (null); + if (faceName == null) { + /* The user did not specify a face name, so they want all versions of all available face names */ + OS.EnumFontFamilies (hDC, null, lpEnumFontFamProc, scalable ? 1 : 0); + + /** + * For bitmapped fonts, EnumFontFamilies only enumerates once for each font, regardless + * of how many styles are available. If the user wants bitmapped fonts, enumerate on + * each face name now. + */ + offset = nFonts; + for (int i=0; i<offset; i++) { + LOGFONT lf = logFonts [i]; + /** + * Bug in Windows 98. When EnumFontFamiliesEx is called with a specified face name, it + * should enumerate for each available style of that font. Instead, it only enumerates + * once. The fix is to call EnumFontFamilies, which works as expected. + */ + if (OS.IsUnicode) { + OS.EnumFontFamiliesW (hDC, ((LOGFONTW)lf).lfFaceName, lpEnumFontFamProc, scalable ? 1 : 0); + } else { + OS.EnumFontFamiliesA (hDC, ((LOGFONTA)lf).lfFaceName, lpEnumFontFamProc, scalable ? 1 : 0); + } + } + } else { + /* Use the character encoding for the default locale */ + TCHAR lpFaceName = new TCHAR (0, faceName, true); + /** + * Bug in Windows 98. When EnumFontFamiliesEx is called with a specified face name, it + * should enumerate for each available style of that font. Instead, it only enumerates + * once. The fix is to call EnumFontFamilies, which works as expected. + */ + OS.EnumFontFamilies (hDC, lpFaceName, lpEnumFontFamProc, scalable ? 1 : 0); + } + int logPixelsY = OS.GetDeviceCaps(hDC, OS.LOGPIXELSY); + internal_dispose_GC (hDC, null); + + /* Create the fontData from the logfonts */ + int count = 0; + FontData [] result = new FontData [nFonts - offset]; + for (int i=offset; i<nFonts; i++) { + FontData fd = FontData.win32_new (logFonts [i], pixels [i] * 72f / logPixelsY); + int j; + for (j = 0; j < count; j++) { + if (fd.equals (result [j])) break; + } + if (j == count) result [count++] = fd; + } + if (count != result.length) { + FontData [] newResult = new FontData [count]; + System.arraycopy (result, 0, newResult, 0, count); + result = newResult; + } + + /* Clean up */ + callback.dispose (); + logFonts = null; + pixels = null; + metrics = null; + return result; +} + +String getLastError () { + int error = OS.GetLastError(); + if (error == 0) return ""; //$NON-NLS-1$ + return " [GetLastError=0x" + Integer.toHexString(error) + "]"; //$NON-NLS-1$ //$NON-NLS-2$ +} + +String getLastErrorText () { + int error = OS.GetLastError(); + if (error == 0) return ""; //$NON-NLS-1$ + int /*long*/ [] buffer = new int /*long*/ [1]; + int dwFlags = OS.FORMAT_MESSAGE_ALLOCATE_BUFFER | OS.FORMAT_MESSAGE_FROM_SYSTEM | OS.FORMAT_MESSAGE_IGNORE_INSERTS; + int length = OS.FormatMessage(dwFlags, 0, error, OS.LANG_USER_DEFAULT, buffer, 0, 0); + if (length == 0) return " [GetLastError=0x" + Integer.toHexString(error) + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + TCHAR buffer1 = new TCHAR(0, length); + OS.MoveMemory(buffer1, buffer[0], length * TCHAR.sizeof); + if (buffer[0] != 0) OS.LocalFree(buffer[0]); + return buffer1.toString(0, length); +} + +/** + * 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 (); + int pixel = 0x00000000; + switch (id) { + case SWT.COLOR_WHITE: pixel = 0x00FFFFFF; break; + case SWT.COLOR_BLACK: pixel = 0x00000000; break; + case SWT.COLOR_RED: pixel = 0x000000FF; break; + case SWT.COLOR_DARK_RED: pixel = 0x00000080; break; + case SWT.COLOR_GREEN: pixel = 0x0000FF00; break; + case SWT.COLOR_DARK_GREEN: pixel = 0x00008000; break; + case SWT.COLOR_YELLOW: pixel = 0x0000FFFF; break; + case SWT.COLOR_DARK_YELLOW: pixel = 0x00008080; break; + case SWT.COLOR_BLUE: pixel = 0x00FF0000; break; + case SWT.COLOR_DARK_BLUE: pixel = 0x00800000; break; + case SWT.COLOR_MAGENTA: pixel = 0x00FF00FF; break; + case SWT.COLOR_DARK_MAGENTA: pixel = 0x00800080; break; + case SWT.COLOR_CYAN: pixel = 0x00FFFF00; break; + case SWT.COLOR_DARK_CYAN: pixel = 0x00808000; break; + case SWT.COLOR_GRAY: pixel = 0x00C0C0C0; break; + case SWT.COLOR_DARK_GRAY: pixel = 0x00808080; break; + } + return Color.win32_new (this, pixel); +} + +/** + * 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 (); + int /*long*/ hFont = OS.GetStockObject (OS.SYSTEM_FONT); + return Font.win32_new (this, hFont); +} + +/** + * 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 false; +} + +/** + * 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 () { + if (debug) { + if (!OS.IsWinCE) OS.GdiSetBatchLimit(1); + } + + /* Initialize the system font slot */ + systemFont = getSystemFont(); + + /* Initialize scripts list */ + if (!OS.IsWinCE) { + int /*long*/ [] ppSp = new int /*long*/ [1]; + int [] piNumScripts = new int [1]; + OS.ScriptGetProperties (ppSp, piNumScripts); + scripts = new int /*long*/ [piNumScripts [0]]; + OS.MoveMemory (scripts, ppSp [0], scripts.length * OS.PTR_SIZEOF); + } + + /* + * If we're not on a device which supports palettes, + * don't create one. + */ + int /*long*/ hDC = internal_new_GC (null); + int rc = OS.GetDeviceCaps (hDC, OS.RASTERCAPS); + int bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL); + int planes = OS.GetDeviceCaps (hDC, OS.PLANES); + + bits *= planes; + if ((rc & OS.RC_PALETTE) == 0 || bits != 8) { + internal_dispose_GC (hDC, null); + return; + } + + int numReserved = OS.GetDeviceCaps (hDC, OS.NUMRESERVED); + int numEntries = OS.GetDeviceCaps (hDC, OS.SIZEPALETTE); + + if (OS.IsWinCE) { + /* + * Feature on WinCE. For some reason, certain 8 bit WinCE + * devices return 0 for the number of reserved entries in + * the system palette. Their system palette correctly contains + * the usual 20 system colors. The workaround is to assume + * there are 20 reserved system colors instead of 0. + */ + if (numReserved == 0 && numEntries >= 20) numReserved = 20; + } + + /* Create the palette and reference counter */ + colorRefCount = new int [numEntries]; + + /* 4 bytes header + 4 bytes per entry * numEntries entries */ + byte [] logPalette = new byte [4 + 4 * numEntries]; + + /* 2 bytes = special header */ + logPalette [0] = 0x00; + logPalette [1] = 0x03; + + /* 2 bytes = number of colors, LSB first */ + logPalette [2] = 0; + logPalette [3] = 1; + + /* + * Create a palette which contains the system entries + * as they are located in the system palette. The + * MSDN article 'Memory Device Contexts' describes + * where system entries are located. On an 8 bit + * display with 20 reserved colors, the system colors + * will be the first 10 entries and the last 10 ones. + */ + byte[] lppe = new byte [4 * numEntries]; + OS.GetSystemPaletteEntries (hDC, 0, numEntries, lppe); + /* Copy all entries from the system palette */ + System.arraycopy (lppe, 0, logPalette, 4, 4 * numEntries); + /* Lock the indices corresponding to the system entries */ + for (int i = 0; i < numReserved / 2; i++) { + colorRefCount [i] = 1; + colorRefCount [numEntries - 1 - i] = 1; + } + internal_dispose_GC (hDC, null); + hPalette = OS.CreatePalette (logPalette); +} +/** + * 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 /*long*/ internal_dispose_GC (int /*long*/ hDC, 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); + if (OS.IsWinNT && OS.WIN32_VERSION >= OS.VERSION (4, 10)) { + TCHAR lpszFilename = new TCHAR (0, path, true); + boolean loaded = OS.AddFontResourceEx (lpszFilename, OS.FR_PRIVATE, 0) != 0; + if (loaded) { + if (gdipToken != null) { + if (fontCollection == 0) { + fontCollection = Gdip.PrivateFontCollection_new(); + if (fontCollection == 0) SWT.error(SWT.ERROR_NO_HANDLES); + } + int length = path.length(); + char [] buffer = new char [length + 1]; + path.getChars(0, length, buffer, 0); + Gdip.PrivateFontCollection_AddFontFile(fontCollection, buffer); + } else { + addFont(path); + } + } + return loaded; + } + return false; +} + +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.err.println (string); + } + for (int i=0; i<errors.length; i++) { + if (errors [i] != null) errors [i].printStackTrace (System.err); + } + } + } + } +} + +/** + * 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 (gdipToken != null) { + if (fontCollection != 0) { + Gdip.PrivateFontCollection_delete(fontCollection); + } + fontCollection = 0; + Gdip.GdiplusShutdown (gdipToken[0]); + } + gdipToken = null; + scripts = null; + if (hPalette != 0) OS.DeleteObject (hPalette); + hPalette = 0; + colorRefCount = null; + logFonts = null; + nFonts = 0; +} + +/** + * 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 (); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/DeviceData.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/DeviceData.java new file mode 100755 index 0000000000..9b95c99718 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/DeviceData.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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/win32/org/eclipse/swt/graphics/Font.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Font.java new file mode 100755 index 0000000000..23cc7aa24f --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Font.java @@ -0,0 +1,258 @@ +/******************************************************************************* + * 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.win32.*; +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 int /*long*/ handle; + +/** + * Prevents uninitialized instances from being created outside the package. + */ +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); + init(fd); + init(); +} + +/** + * 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); + } + init(fds[0]); + init(); +} + +/** + * 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); + if (name == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + init(new FontData (name, height, style)); + init(); +} + +/*public*/ Font(Device device, String name, float height, int style) { + super(device); + if (name == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + init(new FontData (name, height, style)); + init(); +} +void destroy() { + OS.DeleteObject(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 (object == this) return true; + if (!(object instanceof Font)) return false; + Font font = (Font) object; + return device == font.device && 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); + LOGFONT logFont = OS.IsUnicode ? (LOGFONT)new LOGFONTW() : new LOGFONTA(); + OS.GetObject(handle, LOGFONT.sizeof, logFont); + return new FontData[] {FontData.win32_new(logFont, device.computePoints(logFont, 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 (int)/*64*/handle; +} + +void init (FontData fd) { + if (fd == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + LOGFONT logFont = fd.data; + int lfHeight = logFont.lfHeight; + logFont.lfHeight = device.computePixels(fd.height); + handle = OS.CreateFontIndirect(logFont); + logFont.lfHeight = lfHeight; + if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); +} + +/** + * 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 == 0; +} + +/** + * 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 + "}"; +} + +/** + * 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 + * @return a new font object containing the specified device and handle + */ +public static Font win32_new(Device device, int /*long*/ handle) { + Font font = new Font(device); + font.handle = handle; + return font; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/FontData.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/FontData.java new file mode 100755 index 0000000000..15e6cb2472 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/FontData.java @@ -0,0 +1,669 @@ +/******************************************************************************* + * 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.win32.*; +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 { + + /** + * A Win32 LOGFONT struct + * (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 LOGFONT data; + + /** + * 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 locales of the font + */ + String lang, country, variant; + +/** + * Constructs a new uninitialized font data. + */ +public FontData() { + data = OS.IsUnicode ? (LOGFONT)new LOGFONTW() : new LOGFONTA(); + // We set the charset field so that + // wildcard searching will work properly + // out of the box + data.lfCharSet = (byte)OS.DEFAULT_CHARSET; + height = 12; +} + +/** + * Constructs a new font data given the Windows <code>LOGFONT</code> + * that it should represent. + * + * @param data the <code>LOGFONT</code> for the result + */ +FontData(LOGFONT data, float height) { + this.data = data; + this.height = height; +} + +/** + * 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); + data = OS.IsUnicode ? (LOGFONT)new LOGFONTW() : new LOGFONTA(); + data.lfCharSet = (byte)OS.DEFAULT_CHARSET; + 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("WINDOWS") && version2.equals("1")) { //$NON-NLS-1$//$NON-NLS-2$ + LOGFONT newData = OS.IsUnicode ? (LOGFONT)new LOGFONTW() : new LOGFONTA(); + try { + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) return; + newData.lfHeight = Integer.parseInt(string.substring(start, end)); + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) return; + newData.lfWidth = Integer.parseInt(string.substring(start, end)); + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) return; + newData.lfEscapement = Integer.parseInt(string.substring(start, end)); + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) return; + newData.lfOrientation = Integer.parseInt(string.substring(start, end)); + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) return; + newData.lfWeight = Integer.parseInt(string.substring(start, end)); + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) return; + newData.lfItalic = Byte.parseByte(string.substring(start, end)); + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) return; + newData.lfUnderline = Byte.parseByte(string.substring(start, end)); + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) return; + newData.lfStrikeOut = Byte.parseByte(string.substring(start, end)); + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) return; + newData.lfCharSet = Byte.parseByte(string.substring(start, end)); + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) return; + newData.lfOutPrecision = Byte.parseByte(string.substring(start, end)); + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) return; + newData.lfClipPrecision = Byte.parseByte(string.substring(start, end)); + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) return; + newData.lfQuality = Byte.parseByte(string.substring(start, end)); + start = end + 1; + end = string.indexOf('|', start); + if (end == -1) return; + newData.lfPitchAndFamily = Byte.parseByte(string.substring(start, end)); + start = end + 1; + } catch (NumberFormatException e) { + setName(name); + setHeight(height); + setStyle(style); + return; + } + TCHAR buffer = new TCHAR(0, string.substring(start), false); + int length = Math.min(OS.LF_FACESIZE - 1, buffer.length()); + if (OS.IsUnicode) { + char[] lfFaceName = ((LOGFONTW)newData).lfFaceName; + System.arraycopy(buffer.chars, 0, lfFaceName, 0, length); + } else { + byte[] lfFaceName = ((LOGFONTA)newData).lfFaceName; + System.arraycopy(buffer.bytes, 0, lfFaceName, 0, length); + } + data = newData; + } +} + +/** + * 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) { + if (name == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + data = OS.IsUnicode ? (LOGFONT)new LOGFONTW() : new LOGFONTA(); + setName(name); + setHeight(height); + setStyle(style); + // We set the charset field so that + // wildcard searching will work properly + // out of the box + data.lfCharSet = (byte)OS.DEFAULT_CHARSET; +} + +/*public*/ FontData(String name, float height, int style) { + if (name == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + data = OS.IsUnicode ? (LOGFONT)new LOGFONTW() : new LOGFONTA(); + setName(name); + setHeight(height); + setStyle(style); + // We set the charset field so that + // wildcard searching will work properly + // out of the box + data.lfCharSet = (byte)OS.DEFAULT_CHARSET; +} + +/** + * 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 fd = (FontData)object; + LOGFONT lf = fd.data; + return data.lfCharSet == lf.lfCharSet && + /* + * This code is intentionally commented. When creating + * a FontData, lfHeight is not necessarily set. Instead + * we check the height field which is always set. + */ +// data.lfHeight == lf.lfHeight && + height == fd.height && + data.lfWidth == lf.lfWidth && + data.lfEscapement == lf.lfEscapement && + data.lfOrientation == lf.lfOrientation && + data.lfWeight == lf.lfWeight && + data.lfItalic == lf.lfItalic && + data.lfUnderline == lf.lfUnderline && + data.lfStrikeOut == lf.lfStrikeOut && + data.lfCharSet == lf.lfCharSet && + data.lfOutPrecision == lf.lfOutPrecision && + data.lfClipPrecision == lf.lfClipPrecision && + data.lfQuality == lf.lfQuality && + data.lfPitchAndFamily == lf.lfPitchAndFamily && + getName().equals(fd.getName()); +} + +int /*long*/ EnumLocalesProc(int /*long*/ lpLocaleString) { + + /* Get the locale ID */ + int length = 8; + TCHAR buffer = new TCHAR(0, length); + int byteCount = length * TCHAR.sizeof; + OS.MoveMemory(buffer, lpLocaleString, byteCount); + int lcid = Integer.parseInt(buffer.toString(0, buffer.strlen ()), 16); + + /* Check the language */ + int size = OS.GetLocaleInfo(lcid, OS.LOCALE_SISO639LANGNAME, buffer, length); + if (size <= 0 || !lang.equals(buffer.toString(0, size - 1))) return 1; + + /* Check the country */ + if (country != null) { + size = OS.GetLocaleInfo(lcid, OS.LOCALE_SISO3166CTRYNAME, buffer, length); + if (size <= 0 || !country.equals(buffer.toString(0, size - 1))) return 1; + } + + /* Get the charset */ + size = OS.GetLocaleInfo(lcid, OS.LOCALE_IDEFAULTANSICODEPAGE, buffer, length); + if (size <= 0) return 1; + int cp = Integer.parseInt(buffer.toString(0, size - 1)); + int [] lpCs = new int[8]; + OS.TranslateCharsetInfo(cp, lpCs, OS.TCI_SRCCODEPAGE); + data.lfCharSet = (byte)lpCs[0]; + + return 0; +} + +/** + * Returns the height of the receiver in points. + * + * @return the height of this FontData + * + * @see #setHeight(int) + */ +public int getHeight() { + return (int)(0.5f + 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() { + char[] chars; + if (OS.IsUnicode) { + chars = ((LOGFONTW)data).lfFaceName; + } else { + chars = new char[OS.LF_FACESIZE]; + byte[] bytes = ((LOGFONTA)data).lfFaceName; + OS.MultiByteToWideChar (OS.CP_ACP, OS.MB_PRECOMPOSED, bytes, bytes.length, chars, chars.length); + } + int index = 0; + while (index < chars.length) { + if (chars [index] == 0) break; + index++; + } + return new String (chars, 0, index); +} + +/** + * 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() { + int style = SWT.NORMAL; + if (data.lfWeight == 700) style |= SWT.BOLD; + if (data.lfItalic != 0) style |= SWT.ITALIC; + 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 data.lfCharSet ^ getHeight() ^ data.lfWidth ^ data.lfEscapement ^ + data.lfOrientation ^ data.lfWeight ^ data.lfItalic ^data.lfUnderline ^ + data.lfStrikeOut ^ data.lfCharSet ^ data.lfOutPrecision ^ + data.lfClipPrecision ^ data.lfQuality ^ data.lfPitchAndFamily ^ + getName().hashCode(); +} + +/** + * 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); + } + if (lang == null) { + data.lfCharSet = (byte)OS.DEFAULT_CHARSET; + } else { + Callback callback = new Callback (this, "EnumLocalesProc", 1); //$NON-NLS-1$ + int /*long*/ lpEnumLocalesProc = callback.getAddress (); + if (lpEnumLocalesProc == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + OS.EnumSystemLocales(lpEnumLocalesProc, OS.LCID_SUPPORTED); + callback.dispose (); + } +} + +/** + * 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); + + /* The field lfFaceName must be NULL terminated */ + TCHAR buffer = new TCHAR(0, name, true); + int length = Math.min(OS.LF_FACESIZE - 1, buffer.length()); + if (OS.IsUnicode) { + char[] lfFaceName = ((LOGFONTW)data).lfFaceName; + for (int i = 0; i < lfFaceName.length; i++) lfFaceName[i] = 0; + System.arraycopy(buffer.chars, 0, lfFaceName, 0, length); + } else { + byte[] lfFaceName = ((LOGFONTA)data).lfFaceName; + for (int i = 0; i < lfFaceName.length; i++) lfFaceName[i] = 0; + System.arraycopy(buffer.bytes, 0, lfFaceName, 0, length); + } +} + +/** + * 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) { + if ((style & SWT.BOLD) == SWT.BOLD) { + data.lfWeight = 700; + } else { + data.lfWeight = 0; + } + if ((style & SWT.ITALIC) == SWT.ITALIC) { + data.lfItalic = 1; + } else { + data.lfItalic = 0; + } +} + +/** + * 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|"); //$NON-NLS-1$ + String name = getName(); + buffer.append(name); + buffer.append("|"); //$NON-NLS-1$ + buffer.append(getHeightF()); + buffer.append("|"); //$NON-NLS-1$ + buffer.append(getStyle()); + buffer.append("|"); //$NON-NLS-1$ + buffer.append("WINDOWS|1|"); //$NON-NLS-1$ + buffer.append(data.lfHeight); + buffer.append("|"); //$NON-NLS-1$ + buffer.append(data.lfWidth); + buffer.append("|"); //$NON-NLS-1$ + buffer.append(data.lfEscapement); + buffer.append("|"); //$NON-NLS-1$ + buffer.append(data.lfOrientation); + buffer.append("|"); //$NON-NLS-1$ + buffer.append(data.lfWeight); + buffer.append("|"); //$NON-NLS-1$ + buffer.append(data.lfItalic); + buffer.append("|"); //$NON-NLS-1$ + buffer.append(data.lfUnderline); + buffer.append("|"); //$NON-NLS-1$ + buffer.append(data.lfStrikeOut); + buffer.append("|"); //$NON-NLS-1$ + buffer.append(data.lfCharSet); + buffer.append("|"); //$NON-NLS-1$ + buffer.append(data.lfOutPrecision); + buffer.append("|"); //$NON-NLS-1$ + buffer.append(data.lfClipPrecision); + buffer.append("|"); //$NON-NLS-1$ + buffer.append(data.lfQuality); + buffer.append("|"); //$NON-NLS-1$ + buffer.append(data.lfPitchAndFamily); + buffer.append("|"); //$NON-NLS-1$ + buffer.append(name); + return buffer.toString(); +} + +/** + * Invokes platform specific functionality to allocate a new font data. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>FontData</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 <code>LOGFONT</code> for the font data + * @param height the height of the font data + * @return a new font data object containing the specified <code>LOGFONT</code> and height + */ +public static FontData win32_new(LOGFONT data, float height) { + return new FontData(data, height); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/FontMetrics.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/FontMetrics.java new file mode 100755 index 0000000000..bfa851cd98 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/FontMetrics.java @@ -0,0 +1,183 @@ +/******************************************************************************* + * 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.win32.*; + +/** + * 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 { + + /** + * On Windows, handle is a Win32 TEXTMETRIC struct + * On Photon, handle is a Photon FontQueryInfo struct + * (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 TEXTMETRIC handle; + +/** + * Prevents instances from being created outside the package. + */ +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; + TEXTMETRIC metric = ((FontMetrics)object).handle; + return handle.tmHeight == metric.tmHeight && + handle.tmAscent == metric.tmAscent && + handle.tmDescent == metric.tmDescent && + handle.tmInternalLeading == metric.tmInternalLeading && + handle.tmExternalLeading == metric.tmExternalLeading && + handle.tmAveCharWidth == metric.tmAveCharWidth && + handle.tmMaxCharWidth == metric.tmMaxCharWidth && + handle.tmWeight == metric.tmWeight && + handle.tmOverhang == metric.tmOverhang && + handle.tmDigitizedAspectX == metric.tmDigitizedAspectX && + handle.tmDigitizedAspectY == metric.tmDigitizedAspectY && +// handle.tmFirstChar == metric.tmFirstChar && +// handle.tmLastChar == metric.tmLastChar && +// handle.tmDefaultChar == metric.tmDefaultChar && +// handle.tmBreakChar == metric.tmBreakChar && + handle.tmItalic == metric.tmItalic && + handle.tmUnderlined == metric.tmUnderlined && + handle.tmStruckOut == metric.tmStruckOut && + handle.tmPitchAndFamily == metric.tmPitchAndFamily && + handle.tmCharSet == metric.tmCharSet; +} + +/** + * 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 handle.tmAscent - handle.tmInternalLeading; +} + +/** + * 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 handle.tmAveCharWidth; +} + +/** + * 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 handle.tmDescent; +} + +/** + * 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 handle.tmHeight; +} + +/** + * 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 handle.tmInternalLeading; +} + +/** + * 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.tmHeight ^ handle.tmAscent ^ handle.tmDescent ^ + handle.tmInternalLeading ^ handle.tmExternalLeading ^ + handle.tmAveCharWidth ^ handle.tmMaxCharWidth ^ handle.tmWeight ^ + handle.tmOverhang ^ handle.tmDigitizedAspectX ^ handle.tmDigitizedAspectY ^ +// handle.tmFirstChar ^ handle.tmLastChar ^ handle.tmDefaultChar ^ handle.tmBreakChar ^ + handle.tmItalic ^ handle.tmUnderlined ^ handle.tmStruckOut ^ + handle.tmPitchAndFamily ^ handle.tmCharSet; +} + +/** + * Invokes platform specific functionality to allocate a new font metrics. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>FontMetrics</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 handle the <code>TEXTMETRIC</code> containing information about a font + * @return a new font metrics object containing the specified <code>TEXTMETRIC</code> + */ +public static FontMetrics win32_new(TEXTMETRIC handle) { + FontMetrics fontMetrics = new FontMetrics(); + fontMetrics.handle = handle; + return fontMetrics; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java new file mode 100755 index 0000000000..e08cbf3e48 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java @@ -0,0 +1,4979 @@ +/******************************************************************************* + * 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.gdip.*; +import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.*; + +/** + * 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 int /*long*/ handle; + + Drawable drawable; + GCData data; + + static final int FOREGROUND = 1 << 0; + static final int BACKGROUND = 1 << 1; + static final int FONT = 1 << 2; + static final int LINE_STYLE = 1 << 3; + static final int LINE_WIDTH = 1 << 4; + static final int LINE_CAP = 1 << 5; + static final int LINE_JOIN = 1 << 6; + static final int LINE_MITERLIMIT = 1 << 7; + static final int FOREGROUND_TEXT = 1 << 8; + static final int BACKGROUND_TEXT = 1 << 9; + static final int BRUSH = 1 << 10; + static final int PEN = 1 << 11; + static final int NULL_BRUSH = 1 << 12; + static final int NULL_PEN = 1 << 13; + static final int DRAW_OFFSET = 1 << 14; + + static final int DRAW = FOREGROUND | LINE_STYLE | LINE_WIDTH | LINE_CAP | LINE_JOIN | LINE_MITERLIMIT | PEN | NULL_BRUSH | DRAW_OFFSET; + static final int FILL = BACKGROUND | BRUSH | NULL_PEN; + + 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}; + +/** + * Prevents uninitialized instances from being created outside the package. + */ +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, SWT.NONE); +} + +/** + * 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); + GCData data = new GCData (); + data.style = checkStyle(style); + int /*long*/ hDC = 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, hDC); + init(); +} + +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); +} + +void checkGC(int mask) { + int state = data.state; + if ((state & mask) == mask) return; + state = (state ^ mask) & mask; + data.state |= mask; + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + int /*long*/ pen = data.gdipPen; + float width = data.lineWidth; + if ((state & FOREGROUND) != 0 || (pen == 0 && (state & (LINE_WIDTH | LINE_STYLE | LINE_MITERLIMIT | LINE_JOIN | LINE_CAP)) != 0)) { + if (data.gdipFgBrush != 0) Gdip.SolidBrush_delete(data.gdipFgBrush); + data.gdipFgBrush = 0; + int /*long*/ brush; + Pattern pattern = data.foregroundPattern; + if (pattern != null) { + brush = pattern.handle; + if ((data.style & SWT.MIRRORED) != 0) { + switch (Gdip.Brush_GetType(brush)) { + case Gdip.BrushTypeTextureFill: + brush = Gdip.Brush_Clone(brush); + if (brush == 0) SWT.error(SWT.ERROR_NO_HANDLES); + Gdip.TextureBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend); + data.gdipFgBrush = brush; + } + } + } else { + int foreground = data.foreground; + int rgb = ((foreground >> 16) & 0xFF) | (foreground & 0xFF00) | ((foreground & 0xFF) << 16); + int /*long*/ color = Gdip.Color_new(data.alpha << 24 | rgb); + if (color == 0) SWT.error(SWT.ERROR_NO_HANDLES); + brush = Gdip.SolidBrush_new(color); + if (brush == 0) SWT.error(SWT.ERROR_NO_HANDLES); + Gdip.Color_delete(color); + data.gdipFgBrush = brush; + } + if (pen != 0) { + Gdip.Pen_SetBrush(pen, brush); + } else { + pen = data.gdipPen = Gdip.Pen_new(brush, width); + } + } + if ((state & LINE_WIDTH) != 0) { + Gdip.Pen_SetWidth(pen, width); + switch (data.lineStyle) { + case SWT.LINE_CUSTOM: + state |= LINE_STYLE; + } + } + if ((state & LINE_STYLE) != 0) { + float[] dashes = null; + float dashOffset = 0; + int dashStyle = Gdip.DashStyleSolid; + switch (data.lineStyle) { + case SWT.LINE_SOLID: break; + case SWT.LINE_DOT: dashStyle = Gdip.DashStyleDot; if (width == 0) dashes = LINE_DOT_ZERO; break; + case SWT.LINE_DASH: dashStyle = Gdip.DashStyleDash; if (width == 0) dashes = LINE_DASH_ZERO; break; + case SWT.LINE_DASHDOT: dashStyle = Gdip.DashStyleDashDot; if (width == 0) dashes = LINE_DASHDOT_ZERO; break; + case SWT.LINE_DASHDOTDOT: dashStyle = Gdip.DashStyleDashDotDot; if (width == 0) dashes = LINE_DASHDOTDOT_ZERO; break; + case SWT.LINE_CUSTOM: { + if (data.lineDashes != null) { + dashOffset = data.lineDashesOffset / Math.max (1, width); + dashes = new float[data.lineDashes.length * 2]; + for (int i = 0; i < data.lineDashes.length; i++) { + float dash = data.lineDashes[i] / Math.max (1, width); + dashes[i] = dash; + dashes[i + data.lineDashes.length] = dash; + } + } + } + } + if (dashes != null) { + Gdip.Pen_SetDashPattern(pen, dashes, dashes.length); + Gdip.Pen_SetDashStyle(pen, Gdip.DashStyleCustom); + Gdip.Pen_SetDashOffset(pen, dashOffset); + } else { + Gdip.Pen_SetDashStyle(pen, dashStyle); + } + } + if ((state & LINE_MITERLIMIT) != 0) { + Gdip.Pen_SetMiterLimit(pen, data.lineMiterLimit); + } + if ((state & LINE_JOIN) != 0) { + int joinStyle = 0; + switch (data.lineJoin) { + case SWT.JOIN_MITER: joinStyle = Gdip.LineJoinMiter; break; + case SWT.JOIN_BEVEL: joinStyle = Gdip.LineJoinBevel; break; + case SWT.JOIN_ROUND: joinStyle = Gdip.LineJoinRound; break; + } + Gdip.Pen_SetLineJoin(pen, joinStyle); + } + if ((state & LINE_CAP) != 0) { + int dashCap = Gdip.DashCapFlat, capStyle = 0; + switch (data.lineCap) { + case SWT.CAP_FLAT: capStyle = Gdip.LineCapFlat; break; + case SWT.CAP_ROUND: capStyle = Gdip.LineCapRound; dashCap = Gdip.DashCapRound; break; + case SWT.CAP_SQUARE: capStyle = Gdip.LineCapSquare; break; + } + Gdip.Pen_SetLineCap(pen, capStyle, capStyle, dashCap); + } + if ((state & BACKGROUND) != 0) { + if (data.gdipBgBrush != 0) Gdip.SolidBrush_delete(data.gdipBgBrush); + data.gdipBgBrush = 0; + Pattern pattern = data.backgroundPattern; + if (pattern != null) { + data.gdipBrush = pattern.handle; + if ((data.style & SWT.MIRRORED) != 0) { + switch (Gdip.Brush_GetType(data.gdipBrush)) { + case Gdip.BrushTypeTextureFill: + int /*long*/ brush = Gdip.Brush_Clone(data.gdipBrush); + if (brush == 0) SWT.error(SWT.ERROR_NO_HANDLES); + Gdip.TextureBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend); + data.gdipBrush = data.gdipBgBrush = brush; + } + } + } else { + int background = data.background; + int rgb = ((background >> 16) & 0xFF) | (background & 0xFF00) | ((background & 0xFF) << 16); + int /*long*/ color = Gdip.Color_new(data.alpha << 24 | rgb); + if (color == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ brush = Gdip.SolidBrush_new(color); + if (brush == 0) SWT.error(SWT.ERROR_NO_HANDLES); + Gdip.Color_delete(color); + data.gdipBrush = data.gdipBgBrush = brush; + } + } + if ((state & FONT) != 0) { + Font font = data.font; + OS.SelectObject(handle, font.handle); + int /*long*/[] hFont = new int /*long*/[1]; + int /*long*/ gdipFont = createGdipFont(handle, font.handle, gdipGraphics, device.fontCollection, null, hFont); + if (hFont[0] != 0) { + OS.SelectObject(handle, hFont[0]); + if (data.hGDIFont != 0) OS.DeleteObject(data.hGDIFont); + data.hGDIFont = hFont[0]; + } + if (data.gdipFont != 0) Gdip.Font_delete(data.gdipFont); + data.gdipFont = gdipFont; + } + if ((state & DRAW_OFFSET) != 0) { + data.gdipXOffset = data.gdipYOffset = 0; + int /*long*/ matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0); + PointF point = new PointF(); + point.X = point.Y = 1; + Gdip.Graphics_GetTransform(gdipGraphics, matrix); + Gdip.Matrix_TransformVectors(matrix, point, 1); + Gdip.Matrix_delete(matrix); + float scaling = point.X; + if (scaling < 0) scaling = -scaling; + float penWidth = data.lineWidth * scaling; + if (penWidth == 0 || ((int)penWidth % 2) == 1) { + data.gdipXOffset = 0.5f / scaling; + } + scaling = point.Y; + if (scaling < 0) scaling = -scaling; + penWidth = data.lineWidth * scaling; + if (penWidth == 0 || ((int)penWidth % 2) == 1) { + data.gdipYOffset = 0.5f / scaling; + } + } + return; + } + if ((state & (FOREGROUND | LINE_CAP | LINE_JOIN | LINE_STYLE | LINE_WIDTH)) != 0) { + int color = data.foreground; + int width = (int)data.lineWidth; + int[] dashes = null; + int lineStyle = OS.PS_SOLID; + switch (data.lineStyle) { + case SWT.LINE_SOLID: break; + case SWT.LINE_DASH: lineStyle = OS.PS_DASH; break; + case SWT.LINE_DOT: lineStyle = OS.PS_DOT; break; + case SWT.LINE_DASHDOT: lineStyle = OS.PS_DASHDOT; break; + case SWT.LINE_DASHDOTDOT: lineStyle = OS.PS_DASHDOTDOT; break; + case SWT.LINE_CUSTOM: { + if (data.lineDashes != null) { + lineStyle = OS.PS_USERSTYLE; + dashes = new int[data.lineDashes.length]; + for (int i = 0; i < dashes.length; i++) { + dashes[i] = (int)data.lineDashes[i]; + } + } + break; + } + } + if ((state & LINE_STYLE) != 0) { + OS.SetBkMode(handle, data.lineStyle == SWT.LINE_SOLID ? OS.OPAQUE : OS.TRANSPARENT); + } + int joinStyle = 0; + switch (data.lineJoin) { + case SWT.JOIN_MITER: joinStyle = OS.PS_JOIN_MITER; break; + case SWT.JOIN_ROUND: joinStyle = OS.PS_JOIN_ROUND; break; + case SWT.JOIN_BEVEL: joinStyle = OS.PS_JOIN_BEVEL; break; + } + int capStyle = 0; + switch (data.lineCap) { + case SWT.CAP_ROUND: capStyle = OS.PS_ENDCAP_ROUND; break; + case SWT.CAP_FLAT: capStyle = OS.PS_ENDCAP_FLAT; break; + case SWT.CAP_SQUARE: capStyle = OS.PS_ENDCAP_SQUARE;break; + } + int style = lineStyle | joinStyle | capStyle; + /* + * Feature in Windows. Windows does not honour line styles other then + * PS_SOLID for pens wider than 1 pixel created with CreatePen(). The fix + * is to use ExtCreatePen() instead. + */ + int /*long*/ newPen; + if (OS.IsWinCE || (width == 0 && lineStyle != OS.PS_USERSTYLE) || style == 0) { + newPen = OS.CreatePen(style & OS.PS_STYLE_MASK, width, color); + } else { + LOGBRUSH logBrush = new LOGBRUSH(); + logBrush.lbStyle = OS.BS_SOLID; + logBrush.lbColor = color; + /* Feature in Windows. PS_GEOMETRIC pens cannot have zero width. */ + newPen = OS.ExtCreatePen (style | OS.PS_GEOMETRIC, Math.max(1, width), logBrush, dashes != null ? dashes.length : 0, dashes); + } + OS.SelectObject(handle, newPen); + data.state |= PEN; + data.state &= ~NULL_PEN; + if (data.hPen != 0) OS.DeleteObject(data.hPen); + data.hPen = data.hOldPen = newPen; + } else if ((state & PEN) != 0) { + OS.SelectObject(handle, data.hOldPen); + data.state &= ~NULL_PEN; + } else if ((state & NULL_PEN) != 0) { + data.hOldPen = OS.SelectObject(handle, OS.GetStockObject(OS.NULL_PEN)); + data.state &= ~PEN; + } + if ((state & BACKGROUND) != 0) { + int /*long*/ newBrush = OS.CreateSolidBrush(data.background); + OS.SelectObject(handle, newBrush); + data.state |= BRUSH; + data.state &= ~NULL_BRUSH; + if (data.hBrush != 0) OS.DeleteObject(data.hBrush); + data.hOldBrush = data.hBrush = newBrush; + } else if ((state & BRUSH) != 0) { + OS.SelectObject(handle, data.hOldBrush); + data.state &= ~NULL_BRUSH; + } else if ((state & NULL_BRUSH) != 0) { + data.hOldBrush = OS.SelectObject(handle, OS.GetStockObject(OS.NULL_BRUSH)); + data.state &= ~BRUSH; + } + if ((state & BACKGROUND_TEXT) != 0) { + OS.SetBkColor(handle, data.background); + } + if ((state & FOREGROUND_TEXT) != 0) { + OS.SetTextColor(handle, data.foreground); + } + if ((state & FONT) != 0) { + Font font = data.font; + OS.SelectObject(handle, font.handle); + } +} + +/** + * 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 == 0) 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); + + /* Copy the bitmap area */ + Rectangle rect = image.getBounds(); + int /*long*/ memHdc = OS.CreateCompatibleDC(handle); + int /*long*/ hOldBitmap = OS.SelectObject(memHdc, image.handle); + OS.BitBlt(memHdc, 0, 0, rect.width, rect.height, handle, x, y, OS.SRCCOPY); + OS.SelectObject(memHdc, hOldBitmap); + OS.DeleteDC(memHdc); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + + /* + * Feature in WinCE. The function WindowFromDC is not part of the + * WinCE SDK. The fix is to remember the HWND. + */ + int /*long*/ hwnd = data.hwnd; + if (hwnd == 0) { + OS.BitBlt(handle, destX, destY, width, height, handle, srcX, srcY, OS.SRCCOPY); + } else { + RECT lprcClip = null; + int /*long*/ hrgn = OS.CreateRectRgn(0, 0, 0, 0); + if (OS.GetClipRgn(handle, hrgn) == 1) { + lprcClip = new RECT(); + OS.GetRgnBox(hrgn, lprcClip); + } + OS.DeleteObject(hrgn); + RECT lprcScroll = new RECT(); + OS.SetRect(lprcScroll, srcX, srcY, srcX + width, srcY + height); + int flags = paint ? OS.SW_INVALIDATE | OS.SW_ERASE : 0; + int res = OS.ScrollWindowEx(hwnd, destX - srcX, destY - srcY, lprcScroll, lprcClip, 0, null, flags); + + /* + * Feature in WinCE. ScrollWindowEx does not accept combined + * vertical and horizontal scrolling. The fix is to do a + * BitBlt and invalidate the appropriate source area. + */ + if (res == 0 && OS.IsWinCE) { + OS.BitBlt(handle, destX, destY, width, height, handle, srcX, srcY, OS.SRCCOPY); + if (paint) { + int deltaX = destX - srcX, deltaY = destY - srcY; + boolean disjoint = (destX + width < srcX) || (srcX + width < destX) || (destY + height < srcY) || (srcY + height < destY); + if (disjoint) { + OS.InvalidateRect(hwnd, lprcScroll, true); + } else { + if (deltaX != 0) { + int newX = destX - deltaX; + if (deltaX < 0) newX = destX + width; + OS.SetRect(lprcScroll, newX, srcY, newX + Math.abs(deltaX), srcY + height); + OS.InvalidateRect(hwnd, lprcScroll, true); + } + if (deltaY != 0) { + int newY = destY - deltaY; + if (deltaY < 0) newY = destY + height; + OS.SetRect(lprcScroll, srcX, newY, srcX + width, newY + Math.abs(deltaY)); + OS.InvalidateRect(hwnd, lprcScroll, true); + } + } + } + } + } +} +static int /*long*/ createGdipFont(int /*long*/ hDC, int /*long*/ hFont, int /*long*/ graphics, int /*long*/ fontCollection, int /*long*/ [] outFamily, int /*long*/[] outFont) { + int /*long*/ font = Gdip.Font_new(hDC, hFont); + if (font == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ family = 0; + if (!Gdip.Font_IsAvailable(font)) { + Gdip.Font_delete(font); + LOGFONT logFont = OS.IsUnicode ? (LOGFONT)new LOGFONTW() : new LOGFONTA(); + OS.GetObject(hFont, LOGFONT.sizeof, logFont); + int size = Math.abs(logFont.lfHeight); + int style = Gdip.FontStyleRegular; + if (logFont.lfWeight == 700) style |= Gdip.FontStyleBold; + if (logFont.lfItalic != 0) style |= Gdip.FontStyleItalic; + char[] chars; + if (OS.IsUnicode) { + chars = ((LOGFONTW)logFont).lfFaceName; + } else { + chars = new char[OS.LF_FACESIZE]; + byte[] bytes = ((LOGFONTA)logFont).lfFaceName; + OS.MultiByteToWideChar (OS.CP_ACP, OS.MB_PRECOMPOSED, bytes, bytes.length, chars, chars.length); + } + int index = 0; + while (index < chars.length) { + if (chars [index] == 0) break; + index++; + } + String name = new String (chars, 0, index); + if (Compatibility.equalsIgnoreCase(name, "Courier")) { //$NON-NLS-1$ + name = "Courier New"; //$NON-NLS-1$ + } + char[] buffer = new char[name.length() + 1]; + name.getChars(0, name.length(), buffer, 0); + if (fontCollection != 0) { + family = Gdip.FontFamily_new(buffer, fontCollection); + if (!Gdip.FontFamily_IsAvailable(family)) { + Gdip.FontFamily_delete(family); + family = Gdip.FontFamily_new(buffer, 0); + if (!Gdip.FontFamily_IsAvailable(family)) { + Gdip.FontFamily_delete(family); + family = 0; + } + } + } + if (family != 0) { + font = Gdip.Font_new(family, size, style, Gdip.UnitPixel); + } else { + font = Gdip.Font_new(buffer, size, style, Gdip.UnitPixel, 0); + } + if (outFont != null && font != 0) { + int /*long*/ hHeap = OS.GetProcessHeap(); + int /*long*/ pLogFont = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, LOGFONTW.sizeof); + Gdip.Font_GetLogFontW(font, graphics, pLogFont); + outFont[0] = OS.CreateFontIndirectW(pLogFont); + OS.HeapFree(hHeap, 0, pLogFont); + } + } + if (outFamily != null && font != 0) { + if (family == 0) { + family = Gdip.FontFamily_new(); + Gdip.Font_GetFamily(font, family); + } + outFamily [0] = family; + } else { + if (family != 0) Gdip.FontFamily_delete(family); + } + if (font == 0) SWT.error(SWT.ERROR_NO_HANDLES); + return font; +} + +static void destroyGdipBrush(int /*long*/ brush) { + int type = Gdip.Brush_GetType(brush); + switch (type) { + case Gdip.BrushTypeSolidColor: + Gdip.SolidBrush_delete(brush); + break; + case Gdip.BrushTypeHatchFill: + Gdip.HatchBrush_delete(brush); + break; + case Gdip.BrushTypeLinearGradient: + Gdip.LinearGradientBrush_delete(brush); + break; + case Gdip.BrushTypeTextureFill: + Gdip.TextureBrush_delete(brush); + break; + } +} + +/** + * Disposes of the operating system resources associated with + * the graphics context. Applications must dispose of all GCs + * which they allocate. + * + * @exception SWTError <ul> + * <li>ERROR_THREAD_INVALID_ACCESS if not called from the thread that created the drawable</li> + * </ul> + */ +void destroy() { + boolean gdip = data.gdipGraphics != 0; + disposeGdip(); + if (gdip && (data.style & SWT.MIRRORED) != 0) { + OS.SetLayout(handle, OS.GetLayout(handle) | OS.LAYOUT_RTL); + } + + /* Select stock pen and brush objects and free resources */ + if (data.hPen != 0) { + OS.SelectObject(handle, OS.GetStockObject(OS.NULL_PEN)); + OS.DeleteObject(data.hPen); + data.hPen = 0; + } + if (data.hBrush != 0) { + OS.SelectObject(handle, OS.GetStockObject(OS.NULL_BRUSH)); + OS.DeleteObject(data.hBrush); + data.hBrush = 0; + } + + /* + * Put back the original bitmap into the device context. + * This will ensure that we have not left a bitmap + * selected in it when we delete the HDC. + */ + int /*long*/ hNullBitmap = data.hNullBitmap; + if (hNullBitmap != 0) { + OS.SelectObject(handle, hNullBitmap); + data.hNullBitmap = 0; + } + Image image = data.image; + if (image != null) image.memGC = null; + + /* + * Dispose the HDC. + */ + if (drawable != null) drawable.internal_dispose_GC(handle, data); + drawable = null; + handle = 0; + data.image = null; + data.ps = null; + data = null; +} + +void disposeGdip() { + if (data.gdipPen != 0) Gdip.Pen_delete(data.gdipPen); + if (data.gdipBgBrush != 0) destroyGdipBrush(data.gdipBgBrush); + if (data.gdipFgBrush != 0) destroyGdipBrush(data.gdipFgBrush); + if (data.gdipFont != 0) Gdip.Font_delete(data.gdipFont); + if (data.hGDIFont != 0) OS.DeleteObject(data.hGDIFont); + if (data.gdipGraphics != 0) Gdip.Graphics_delete(data.gdipGraphics); + data.gdipGraphics = data.gdipBrush = data.gdipBgBrush = data.gdipFgBrush = + data.gdipFont = data.gdipPen = data.hGDIFont = 0; +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + checkGC(DRAW); + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + if (width == 0 || height == 0 || arcAngle == 0) return; + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend); + if (width == height) { + Gdip.Graphics_DrawArc(gdipGraphics, data.gdipPen, x, y, width, height, -startAngle, -arcAngle); + } else { + int /*long*/ path = Gdip.GraphicsPath_new(Gdip.FillModeAlternate); + if (path == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ matrix = Gdip.Matrix_new(width, 0, 0, height, x, y); + if (matrix == 0) SWT.error(SWT.ERROR_NO_HANDLES); + Gdip.GraphicsPath_AddArc(path, 0, 0, 1, 1, -startAngle, -arcAngle); + Gdip.GraphicsPath_Transform(path, matrix); + Gdip.Graphics_DrawPath(gdipGraphics, data.gdipPen, path); + Gdip.Matrix_delete(matrix); + Gdip.GraphicsPath_delete(path); + } + Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend); + return; + } + if ((data.style & SWT.MIRRORED) != 0) { + if (data.lineWidth != 0 && data.lineWidth % 2 == 0) x--; + } + /* + * Feature in WinCE. The function Arc is not present in the + * WinCE SDK. The fix is to emulate arc drawing by using + * Polyline. + */ + if (OS.IsWinCE) { + /* compute arc with a simple linear interpolation */ + if (arcAngle < 0) { + startAngle += arcAngle; + arcAngle = -arcAngle; + } + if (arcAngle > 360) arcAngle = 360; + int[] points = new int[(arcAngle + 1) * 2]; + int cteX = 2 * x + width; + int cteY = 2 * y + height; + int index = 0; + for (int i = 0; i <= arcAngle; i++) { + points[index++] = (Compatibility.cos(startAngle + i, width) + cteX) >> 1; + points[index++] = (cteY - Compatibility.sin(startAngle + i, height)) >> 1; + } + OS.Polyline(handle, points, points.length / 2); + } else { + int x1, y1, x2, y2,tmp; + boolean isNegative; + if (arcAngle >= 360 || arcAngle <= -360) { + x1 = x2 = x + width; + y1 = y2 = y + height / 2; + } else { + isNegative = arcAngle < 0; + + arcAngle = arcAngle + startAngle; + if (isNegative) { + // swap angles + tmp = startAngle; + startAngle = arcAngle; + arcAngle = tmp; + } + x1 = Compatibility.cos(startAngle, width) + x + width/2; + y1 = -1 * Compatibility.sin(startAngle, height) + y + height/2; + + x2 = Compatibility.cos(arcAngle, width) + x + width/2; + y2 = -1 * Compatibility.sin(arcAngle, height) + y + height/2; + } + OS.Arc(handle, x, y, x + width + 1, y + height + 1, x1, y1, x2, y2); + } +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if ((data.uiState & OS.UISF_HIDEFOCUS) != 0) return; + data.focusDrawn = true; + int /*long*/ hdc = handle; + int state = 0; + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + int /*long*/ clipRgn = 0; + Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone); + int /*long*/ rgn = Gdip.Region_new(); + if (rgn == 0) SWT.error(SWT.ERROR_NO_HANDLES); + Gdip.Graphics_GetClip(gdipGraphics, rgn); + if (!Gdip.Region_IsInfinite(rgn, gdipGraphics)) { + clipRgn = Gdip.Region_GetHRGN(rgn, gdipGraphics); + } + Gdip.Region_delete(rgn); + Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf); + float[] lpXform = null; + int /*long*/ matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0); + if (matrix == 0) SWT.error(SWT.ERROR_NO_HANDLES); + Gdip.Graphics_GetTransform(gdipGraphics, matrix); + if (!Gdip.Matrix_IsIdentity(matrix)) { + lpXform = new float[6]; + Gdip.Matrix_GetElements(matrix, lpXform); + } + Gdip.Matrix_delete(matrix); + hdc = Gdip.Graphics_GetHDC(gdipGraphics); + state = OS.SaveDC(hdc); + if (lpXform != null) { + OS.SetGraphicsMode(hdc, OS.GM_ADVANCED); + OS.SetWorldTransform(hdc, lpXform); + } + if (clipRgn != 0) { + OS.SelectClipRgn(hdc, clipRgn); + OS.DeleteObject(clipRgn); + } + } + OS.SetBkColor(hdc, 0xFFFFFF); + OS.SetTextColor(hdc, 0x000000); + RECT rect = new RECT(); + OS.SetRect(rect, x, y, x + width, y + height); + OS.DrawFocusRect(hdc, rect); + if (gdipGraphics != 0) { + OS.RestoreDC(hdc, state); + Gdip.Graphics_ReleaseHDC(gdipGraphics, hdc); + } else { + data.state &= ~(BACKGROUND_TEXT | FOREGROUND_TEXT); + } +} + +/** + * 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 == 0) 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 == 0) 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) { + if (data.gdipGraphics != 0) { + //TODO - cache bitmap + int /*long*/ [] gdipImage = srcImage.createGdipImage(); + int /*long*/ img = gdipImage[0]; + int imgWidth = Gdip.Image_GetWidth(img); + int imgHeight = Gdip.Image_GetHeight(img); + if (simple) { + srcWidth = destWidth = imgWidth; + srcHeight = destHeight = imgHeight; + } else { + if (srcX + srcWidth > imgWidth || srcY + srcHeight > imgHeight) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + simple = srcX == 0 && srcY == 0 && + srcWidth == destWidth && destWidth == imgWidth && + srcHeight == destHeight && destHeight == imgHeight; + } + Rect rect = new Rect(); + rect.X = destX; + rect.Y = destY; + rect.Width = destWidth; + rect.Height = destHeight; + /* + * Note that if the wrap mode is not WrapModeTileFlipXY, the scaled image + * is translucent around the borders. + */ + int /*long*/ attrib = Gdip.ImageAttributes_new(); + Gdip.ImageAttributes_SetWrapMode(attrib, Gdip.WrapModeTileFlipXY); + if (data.alpha != 0xFF) { + float[] matrix = new float[]{ + 1,0,0,0,0, + 0,1,0,0,0, + 0,0,1,0,0, + 0,0,0,data.alpha / (float)0xFF,0, + 0,0,0,0,1, + }; + Gdip.ImageAttributes_SetColorMatrix(attrib, matrix, Gdip.ColorMatrixFlagsDefault, Gdip.ColorAdjustTypeBitmap); + } + int gstate = 0; + if ((data.style & SWT.MIRRORED) != 0) { + gstate = Gdip.Graphics_Save(data.gdipGraphics); + Gdip.Graphics_ScaleTransform(data.gdipGraphics, -1, 1, Gdip.MatrixOrderPrepend); + Gdip.Graphics_TranslateTransform(data.gdipGraphics, - 2 * destX - destWidth, 0, Gdip.MatrixOrderPrepend); + } + Gdip.Graphics_DrawImage(data.gdipGraphics, img, rect, srcX, srcY, srcWidth, srcHeight, Gdip.UnitPixel, attrib, 0, 0); + if ((data.style & SWT.MIRRORED) != 0) { + Gdip.Graphics_Restore(data.gdipGraphics, gstate); + } + Gdip.ImageAttributes_delete(attrib); + Gdip.Bitmap_delete(img); + if (gdipImage[1] != 0) { + int /*long*/ hHeap = OS.GetProcessHeap (); + OS.HeapFree(hHeap, 0, gdipImage[1]); + } + return; + } + switch (srcImage.type) { + case SWT.BITMAP: + drawBitmap(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple); + break; + case SWT.ICON: + drawIcon(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple); + break; + } +} + +void drawIcon(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple) { + int technology = OS.GetDeviceCaps(handle, OS.TECHNOLOGY); + + boolean drawIcon = true; + int flags = OS.DI_NORMAL; + int offsetX = 0, offsetY = 0; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(5, 1)) { + if ((OS.GetLayout(handle) & OS.LAYOUT_RTL) != 0) { + flags |= OS.DI_NOMIRROR; + /* + * Bug in Windows. For some reason, DrawIconEx() does not take + * into account the window origin when the DI_NOMIRROR and + * LAYOUT_RTL are set. The fix is to set the window origin to + * (0, 0) and offset the drawing ourselves. + */ + POINT pt = new POINT(); + OS.GetWindowOrgEx(handle, pt); + offsetX = pt.x; + offsetY = pt.y; + } + } else { + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) { + drawIcon = (OS.GetLayout(handle) & OS.LAYOUT_RTL) == 0; + } + } + + /* Simple case: no stretching, entire icon */ + if (simple && technology != OS.DT_RASPRINTER && drawIcon) { + if (offsetX != 0 || offsetY != 0) OS.SetWindowOrgEx(handle, 0, 0, null); + OS.DrawIconEx(handle, destX - offsetX, destY - offsetY, srcImage.handle, 0, 0, 0, 0, flags); + if (offsetX != 0 || offsetY != 0) OS.SetWindowOrgEx(handle, offsetX, offsetY, null); + return; + } + + /* Get the icon info */ + ICONINFO srcIconInfo = new ICONINFO(); + if (OS.IsWinCE) { + Image.GetIconInfo(srcImage, srcIconInfo); + } else { + OS.GetIconInfo(srcImage.handle, srcIconInfo); + } + + /* Get the icon width and height */ + int /*long*/ hBitmap = srcIconInfo.hbmColor; + if (hBitmap == 0) hBitmap = srcIconInfo.hbmMask; + BITMAP bm = new BITMAP(); + OS.GetObject(hBitmap, BITMAP.sizeof, bm); + int iconWidth = bm.bmWidth, iconHeight = bm.bmHeight; + if (hBitmap == srcIconInfo.hbmMask) iconHeight /= 2; + + if (simple) { + srcWidth = destWidth = iconWidth; + srcHeight = destHeight = iconHeight; + } + + /* Draw the icon */ + boolean failed = srcX + srcWidth > iconWidth || srcY + srcHeight > iconHeight; + if (!failed) { + simple = srcX == 0 && srcY == 0 && + srcWidth == destWidth && srcHeight == destHeight && + srcWidth == iconWidth && srcHeight == iconHeight; + if (!drawIcon) { + drawBitmapMask(srcImage, srcIconInfo.hbmColor, srcIconInfo.hbmMask, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, iconWidth, iconHeight, false); + } else if (simple && technology != OS.DT_RASPRINTER) { + /* Simple case: no stretching, entire icon */ + if (offsetX != 0 || offsetY != 0) OS.SetWindowOrgEx(handle, 0, 0, null); + OS.DrawIconEx(handle, destX - offsetX, destY - offsetY, srcImage.handle, 0, 0, 0, 0, flags); + if (offsetX != 0 || offsetY != 0) OS.SetWindowOrgEx(handle, offsetX, offsetY, null); + } else { + /* Create the icon info and HDC's */ + ICONINFO newIconInfo = new ICONINFO(); + newIconInfo.fIcon = true; + int /*long*/ srcHdc = OS.CreateCompatibleDC(handle); + int /*long*/ dstHdc = OS.CreateCompatibleDC(handle); + + /* Blt the color bitmap */ + int srcColorY = srcY; + int /*long*/ srcColor = srcIconInfo.hbmColor; + if (srcColor == 0) { + srcColor = srcIconInfo.hbmMask; + srcColorY += iconHeight; + } + int /*long*/ oldSrcBitmap = OS.SelectObject(srcHdc, srcColor); + newIconInfo.hbmColor = OS.CreateCompatibleBitmap(srcHdc, destWidth, destHeight); + if (newIconInfo.hbmColor == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ oldDestBitmap = OS.SelectObject(dstHdc, newIconInfo.hbmColor); + boolean stretch = !simple && (srcWidth != destWidth || srcHeight != destHeight); + if (stretch) { + if (!OS.IsWinCE) OS.SetStretchBltMode(dstHdc, OS.COLORONCOLOR); + OS.StretchBlt(dstHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcColorY, srcWidth, srcHeight, OS.SRCCOPY); + } else { + OS.BitBlt(dstHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcColorY, OS.SRCCOPY); + } + + /* Blt the mask bitmap */ + OS.SelectObject(srcHdc, srcIconInfo.hbmMask); + newIconInfo.hbmMask = OS.CreateBitmap(destWidth, destHeight, 1, 1, null); + if (newIconInfo.hbmMask == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.SelectObject(dstHdc, newIconInfo.hbmMask); + if (stretch) { + OS.StretchBlt(dstHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, OS.SRCCOPY); + } else { + OS.BitBlt(dstHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcY, OS.SRCCOPY); + } + + if (technology == OS.DT_RASPRINTER) { + OS.SelectObject(srcHdc, newIconInfo.hbmColor); + OS.SelectObject(dstHdc, newIconInfo.hbmMask); + drawBitmapTransparentByClipping(srcHdc, dstHdc, 0, 0, destWidth, destHeight, destX, destY, destWidth, destHeight, true, destWidth, destHeight); + OS.SelectObject(srcHdc, oldSrcBitmap); + OS.SelectObject(dstHdc, oldDestBitmap); + } else { + OS.SelectObject(srcHdc, oldSrcBitmap); + OS.SelectObject(dstHdc, oldDestBitmap); + int /*long*/ hIcon = OS.CreateIconIndirect(newIconInfo); + if (hIcon == 0) SWT.error(SWT.ERROR_NO_HANDLES); + if (offsetX != 0 || offsetY != 0) OS.SetWindowOrgEx(handle, 0, 0, null); + OS.DrawIconEx(handle, destX - offsetX, destY - offsetY, hIcon, destWidth, destHeight, 0, 0, flags); + if (offsetX != 0 || offsetY != 0) OS.SetWindowOrgEx(handle, offsetX, offsetY, null); + OS.DestroyIcon(hIcon); + } + + /* Destroy the new icon src and mask and hdc's*/ + OS.DeleteObject(newIconInfo.hbmMask); + OS.DeleteObject(newIconInfo.hbmColor); + OS.DeleteDC(dstHdc); + OS.DeleteDC(srcHdc); + } + } + + /* Free icon info */ + OS.DeleteObject(srcIconInfo.hbmMask); + if (srcIconInfo.hbmColor != 0) { + OS.DeleteObject(srcIconInfo.hbmColor); + } + + if (failed) SWT.error(SWT.ERROR_INVALID_ARGUMENT); +} + +void drawBitmap(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple) { + BITMAP bm = new BITMAP(); + OS.GetObject(srcImage.handle, BITMAP.sizeof, bm); + int imgWidth = bm.bmWidth; + int imgHeight = bm.bmHeight; + if (simple) { + srcWidth = destWidth = imgWidth; + srcHeight = destHeight = imgHeight; + } else { + if (srcX + srcWidth > imgWidth || srcY + srcHeight > imgHeight) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + simple = srcX == 0 && srcY == 0 && + srcWidth == destWidth && destWidth == imgWidth && + srcHeight == destHeight && destHeight == imgHeight; + } + boolean mustRestore = false; + GC memGC = srcImage.memGC; + if (memGC != null && !memGC.isDisposed()) { + memGC.flush(); + mustRestore = true; + GCData data = memGC.data; + if (data.hNullBitmap != 0) { + OS.SelectObject(memGC.handle, data.hNullBitmap); + data.hNullBitmap = 0; + } + } + if (srcImage.alpha != -1 || srcImage.alphaData != null) { + drawBitmapAlpha(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight); + } else if (srcImage.transparentPixel != -1) { + drawBitmapTransparent(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight); + } else { + drawBitmap(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight); + } + if (mustRestore) { + int /*long*/ hOldBitmap = OS.SelectObject(memGC.handle, srcImage.handle); + memGC.data.hNullBitmap = hOldBitmap; + } +} + +void drawBitmapAlpha(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple, BITMAP bm, int imgWidth, int imgHeight) { + /* Simple cases */ + if (srcImage.alpha == 0) return; + if (srcImage.alpha == 255) { + drawBitmap(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight); + return; + } + + if (OS.IsWinNT && OS.WIN32_VERSION >= OS.VERSION(4, 10)) { + BLENDFUNCTION blend = new BLENDFUNCTION(); + blend.BlendOp = OS.AC_SRC_OVER; + int /*long*/ srcHdc = OS.CreateCompatibleDC(handle); + int /*long*/ oldSrcBitmap = OS.SelectObject(srcHdc, srcImage.handle); + if (srcImage.alpha != -1) { + blend.SourceConstantAlpha = (byte)srcImage.alpha; + OS.AlphaBlend(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, blend); + } else { + int /*long*/ memDib = Image.createDIB(srcWidth, srcHeight, 32); + if (memDib == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ memHdc = OS.CreateCompatibleDC(handle); + int /*long*/ oldMemBitmap = OS.SelectObject(memHdc, memDib); + BITMAP dibBM = new BITMAP(); + OS.GetObject(memDib, BITMAP.sizeof, dibBM); + OS.BitBlt(memHdc, 0, 0, srcWidth, srcHeight, srcHdc, srcX, srcY, OS.SRCCOPY); + byte[] srcData = new byte[dibBM.bmWidthBytes * dibBM.bmHeight]; + OS.MoveMemory(srcData, dibBM.bmBits, srcData.length); + final int apinc = imgWidth - srcWidth; + int ap = srcY * imgWidth + srcX, sp = 0; + byte[] alphaData = srcImage.alphaData; + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; ++x) { + int alpha = alphaData[ap++] & 0xff; + int r = ((srcData[sp + 0] & 0xFF) * alpha) + 128; + r = (r + (r >> 8)) >> 8; + int g = ((srcData[sp + 1] & 0xFF) * alpha) + 128; + g = (g + (g >> 8)) >> 8; + int b = ((srcData[sp + 2] & 0xFF) * alpha) + 128; + b = (b + (b >> 8)) >> 8; + srcData[sp+0] = (byte)r; + srcData[sp+1] = (byte)g; + srcData[sp+2] = (byte)b; + srcData[sp+3] = (byte)alpha; + sp += 4; + } + ap += apinc; + } + OS.MoveMemory(dibBM.bmBits, srcData, srcData.length); + blend.SourceConstantAlpha = (byte)0xff; + blend.AlphaFormat = OS.AC_SRC_ALPHA; + OS.AlphaBlend(handle, destX, destY, destWidth, destHeight, memHdc, 0, 0, srcWidth, srcHeight, blend); + OS.SelectObject(memHdc, oldMemBitmap); + OS.DeleteDC(memHdc); + OS.DeleteObject(memDib); + } + OS.SelectObject(srcHdc, oldSrcBitmap); + OS.DeleteDC(srcHdc); + return; + } + + /* Check clipping */ + Rectangle rect = getClipping(); + rect = rect.intersection(new Rectangle(destX, destY, destWidth, destHeight)); + if (rect.isEmpty()) return; + + /* + * Optimization. Recalculate src and dest rectangles so that + * only the clipping area is drawn. + */ + int sx1 = srcX + (((rect.x - destX) * srcWidth) / destWidth); + int sx2 = srcX + ((((rect.x + rect.width) - destX) * srcWidth) / destWidth); + int sy1 = srcY + (((rect.y - destY) * srcHeight) / destHeight); + int sy2 = srcY + ((((rect.y + rect.height) - destY) * srcHeight) / destHeight); + destX = rect.x; + destY = rect.y; + destWidth = rect.width; + destHeight = rect.height; + srcX = sx1; + srcY = sy1; + srcWidth = Math.max(1, sx2 - sx1); + srcHeight = Math.max(1, sy2 - sy1); + + /* Create resources */ + int /*long*/ srcHdc = OS.CreateCompatibleDC(handle); + int /*long*/ oldSrcBitmap = OS.SelectObject(srcHdc, srcImage.handle); + int /*long*/ memHdc = OS.CreateCompatibleDC(handle); + int /*long*/ memDib = Image.createDIB(Math.max(srcWidth, destWidth), Math.max(srcHeight, destHeight), 32); + if (memDib == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ oldMemBitmap = OS.SelectObject(memHdc, memDib); + + BITMAP dibBM = new BITMAP(); + OS.GetObject(memDib, BITMAP.sizeof, dibBM); + int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight; + + /* Get the background pixels */ + OS.BitBlt(memHdc, 0, 0, destWidth, destHeight, handle, destX, destY, OS.SRCCOPY); + byte[] destData = new byte[sizeInBytes]; + OS.MoveMemory(destData, dibBM.bmBits, sizeInBytes); + + /* Get the foreground pixels */ + OS.BitBlt(memHdc, 0, 0, srcWidth, srcHeight, srcHdc, srcX, srcY, OS.SRCCOPY); + byte[] srcData = new byte[sizeInBytes]; + OS.MoveMemory(srcData, dibBM.bmBits, sizeInBytes); + + /* Merge the alpha channel in place */ + int alpha = srcImage.alpha; + final boolean hasAlphaChannel = (srcImage.alpha == -1); + if (hasAlphaChannel) { + final int apinc = imgWidth - srcWidth; + final int spinc = dibBM.bmWidthBytes - srcWidth * 4; + int ap = srcY * imgWidth + srcX, sp = 3; + byte[] alphaData = srcImage.alphaData; + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; ++x) { + srcData[sp] = alphaData[ap++]; + sp += 4; + } + ap += apinc; + sp += spinc; + } + } + + /* Scale the foreground pixels with alpha */ + OS.MoveMemory(dibBM.bmBits, srcData, sizeInBytes); + /* + * Bug in WinCE and Win98. StretchBlt does not correctly stretch when + * the source and destination HDCs are the same. The workaround is to + * stretch to a temporary HDC and blit back into the original HDC. + * Note that on WinCE StretchBlt correctly compresses the image when the + * source and destination HDCs are the same. + */ + if ((OS.IsWinCE && (destWidth > srcWidth || destHeight > srcHeight)) || (!OS.IsWinNT && !OS.IsWinCE)) { + int /*long*/ tempHdc = OS.CreateCompatibleDC(handle); + int /*long*/ tempDib = Image.createDIB(destWidth, destHeight, 32); + if (tempDib == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ oldTempBitmap = OS.SelectObject(tempHdc, tempDib); + if (!simple && (srcWidth != destWidth || srcHeight != destHeight)) { + if (!OS.IsWinCE) OS.SetStretchBltMode(memHdc, OS.COLORONCOLOR); + OS.StretchBlt(tempHdc, 0, 0, destWidth, destHeight, memHdc, 0, 0, srcWidth, srcHeight, OS.SRCCOPY); + } else { + OS.BitBlt(tempHdc, 0, 0, destWidth, destHeight, memHdc, 0, 0, OS.SRCCOPY); + } + OS.BitBlt(memHdc, 0, 0, destWidth, destHeight, tempHdc, 0, 0, OS.SRCCOPY); + OS.SelectObject(tempHdc, oldTempBitmap); + OS.DeleteObject(tempDib); + OS.DeleteDC(tempHdc); + } else { + if (!simple && (srcWidth != destWidth || srcHeight != destHeight)) { + if (!OS.IsWinCE) OS.SetStretchBltMode(memHdc, OS.COLORONCOLOR); + OS.StretchBlt(memHdc, 0, 0, destWidth, destHeight, memHdc, 0, 0, srcWidth, srcHeight, OS.SRCCOPY); + } else { + OS.BitBlt(memHdc, 0, 0, destWidth, destHeight, memHdc, 0, 0, OS.SRCCOPY); + } + } + OS.MoveMemory(srcData, dibBM.bmBits, sizeInBytes); + + /* Compose the pixels */ + final int dpinc = dibBM.bmWidthBytes - destWidth * 4; + int dp = 0; + for (int y = 0; y < destHeight; ++y) { + for (int x = 0; x < destWidth; ++x) { + if (hasAlphaChannel) alpha = srcData[dp + 3] & 0xff; + destData[dp] += ((srcData[dp] & 0xff) - (destData[dp] & 0xff)) * alpha / 255; + destData[dp + 1] += ((srcData[dp + 1] & 0xff) - (destData[dp + 1] & 0xff)) * alpha / 255; + destData[dp + 2] += ((srcData[dp + 2] & 0xff) - (destData[dp + 2] & 0xff)) * alpha / 255; + dp += 4; + } + dp += dpinc; + } + + /* Draw the composed pixels */ + OS.MoveMemory(dibBM.bmBits, destData, sizeInBytes); + OS.BitBlt(handle, destX, destY, destWidth, destHeight, memHdc, 0, 0, OS.SRCCOPY); + + /* Free resources */ + OS.SelectObject(memHdc, oldMemBitmap); + OS.DeleteDC(memHdc); + OS.DeleteObject(memDib); + OS.SelectObject(srcHdc, oldSrcBitmap); + OS.DeleteDC(srcHdc); +} + +void drawBitmapTransparentByClipping(int /*long*/ srcHdc, int /*long*/ maskHdc, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple, int imgWidth, int imgHeight) { + /* Create a clipping region from the mask */ + int /*long*/ rgn = OS.CreateRectRgn(0, 0, 0, 0); + for (int y=0; y<imgHeight; y++) { + for (int x=0; x<imgWidth; x++) { + if (OS.GetPixel(maskHdc, x, y) == 0) { + int /*long*/ tempRgn = OS.CreateRectRgn(x, y, x+1, y+1); + OS.CombineRgn(rgn, rgn, tempRgn, OS.RGN_OR); + OS.DeleteObject(tempRgn); + } + } + } + /* Stretch the clipping mask if needed */ + if (destWidth != srcWidth || destHeight != srcHeight) { + int nBytes = OS.GetRegionData (rgn, 0, null); + int[] lpRgnData = new int[nBytes / 4]; + OS.GetRegionData (rgn, nBytes, lpRgnData); + float[] lpXform = new float[] {(float)destWidth/srcWidth, 0, 0, (float)destHeight/srcHeight, 0, 0}; + int /*long*/ tmpRgn = OS.ExtCreateRegion(lpXform, nBytes, lpRgnData); + OS.DeleteObject(rgn); + rgn = tmpRgn; + } + OS.OffsetRgn(rgn, destX, destY); + int /*long*/ clip = OS.CreateRectRgn(0, 0, 0, 0); + int result = OS.GetClipRgn(handle, clip); + if (result == 1) OS.CombineRgn(rgn, rgn, clip, OS.RGN_AND); + OS.SelectClipRgn(handle, rgn); + int rop2 = 0; + if (!OS.IsWinCE) { + rop2 = OS.GetROP2(handle); + } else { + rop2 = OS.SetROP2 (handle, OS.R2_COPYPEN); + OS.SetROP2 (handle, rop2); + } + int dwRop = rop2 == OS.R2_XORPEN ? OS.SRCINVERT : OS.SRCCOPY; + if (!simple && (srcWidth != destWidth || srcHeight != destHeight)) { + int mode = 0; + if (!OS.IsWinCE) mode = OS.SetStretchBltMode(handle, OS.COLORONCOLOR); + OS.StretchBlt(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, dwRop); + if (!OS.IsWinCE) OS.SetStretchBltMode(handle, mode); + } else { + OS.BitBlt(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, dwRop); + } + OS.SelectClipRgn(handle, result == 1 ? clip : 0); + OS.DeleteObject(clip); + OS.DeleteObject(rgn); +} + +void drawBitmapMask(Image srcImage, int /*long*/ srcColor, int /*long*/ srcMask, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple, int imgWidth, int imgHeight, boolean offscreen) { + int srcColorY = srcY; + if (srcColor == 0) { + srcColor = srcMask; + srcColorY += imgHeight; + } + int /*long*/ srcHdc = OS.CreateCompatibleDC(handle); + int /*long*/ oldSrcBitmap = OS.SelectObject(srcHdc, srcColor); + int /*long*/ destHdc = handle; + int x = destX, y = destY; + int /*long*/ tempHdc = 0, tempBitmap = 0, oldTempBitmap = 0; + int oldBkColor = 0, oldTextColor = 0; + if (offscreen) { + tempHdc = OS.CreateCompatibleDC(handle); + tempBitmap = OS.CreateCompatibleBitmap(handle, destWidth, destHeight); + oldTempBitmap = OS.SelectObject(tempHdc, tempBitmap); + OS.BitBlt(tempHdc, 0, 0, destWidth, destHeight, handle, destX, destY, OS.SRCCOPY); + destHdc = tempHdc; + x = y = 0; + } else { + oldBkColor = OS.SetBkColor(handle, 0xFFFFFF); + oldTextColor = OS.SetTextColor(handle, 0); + } + if (!simple && (srcWidth != destWidth || srcHeight != destHeight)) { + int mode = 0; + if (!OS.IsWinCE) mode = OS.SetStretchBltMode(handle, OS.COLORONCOLOR); + OS.StretchBlt(destHdc, x, y, destWidth, destHeight, srcHdc, srcX, srcColorY, srcWidth, srcHeight, OS.SRCINVERT); + OS.SelectObject(srcHdc, srcMask); + OS.StretchBlt(destHdc, x, y, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, OS.SRCAND); + OS.SelectObject(srcHdc, srcColor); + OS.StretchBlt(destHdc, x, y, destWidth, destHeight, srcHdc, srcX, srcColorY, srcWidth, srcHeight, OS.SRCINVERT); + if (!OS.IsWinCE) OS.SetStretchBltMode(handle, mode); + } else { + OS.BitBlt(destHdc, x, y, destWidth, destHeight, srcHdc, srcX, srcColorY, OS.SRCINVERT); + OS.SetTextColor(destHdc, 0); + OS.SelectObject(srcHdc, srcMask); + OS.BitBlt(destHdc, x, y, destWidth, destHeight, srcHdc, srcX, srcY, OS.SRCAND); + OS.SelectObject(srcHdc, srcColor); + OS.BitBlt(destHdc, x, y, destWidth, destHeight, srcHdc, srcX, srcColorY, OS.SRCINVERT); + } + if (offscreen) { + OS.BitBlt(handle, destX, destY, destWidth, destHeight, tempHdc, 0, 0, OS.SRCCOPY); + OS.SelectObject(tempHdc, oldTempBitmap); + OS.DeleteDC(tempHdc); + OS.DeleteObject(tempBitmap); + } else { + OS.SetBkColor(handle, oldBkColor); + OS.SetTextColor(handle, oldTextColor); + } + OS.SelectObject(srcHdc, oldSrcBitmap); + OS.DeleteDC(srcHdc); +} + +void drawBitmapTransparent(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple, BITMAP bm, int imgWidth, int imgHeight) { + + /* Find the RGB values for the transparent pixel. */ + boolean isDib = bm.bmBits != 0; + int /*long*/ hBitmap = srcImage.handle; + int /*long*/ srcHdc = OS.CreateCompatibleDC(handle); + int /*long*/ oldSrcBitmap = OS.SelectObject(srcHdc, hBitmap); + byte[] originalColors = null; + int transparentColor = srcImage.transparentColor; + if (transparentColor == -1) { + int transBlue = 0, transGreen = 0, transRed = 0; + boolean fixPalette = false; + if (bm.bmBitsPixel <= 8) { + if (isDib) { + /* Palette-based DIBSECTION */ + if (OS.IsWinCE) { + byte[] pBits = new byte[1]; + OS.MoveMemory(pBits, bm.bmBits, 1); + byte oldValue = pBits[0]; + int mask = (0xFF << (8 - bm.bmBitsPixel)) & 0x00FF; + pBits[0] = (byte)((srcImage.transparentPixel << (8 - bm.bmBitsPixel)) | (pBits[0] & ~mask)); + OS.MoveMemory(bm.bmBits, pBits, 1); + int color = OS.GetPixel(srcHdc, 0, 0); + pBits[0] = oldValue; + OS.MoveMemory(bm.bmBits, pBits, 1); + transBlue = (color & 0xFF0000) >> 16; + transGreen = (color & 0xFF00) >> 8; + transRed = color & 0xFF; + } else { + int maxColors = 1 << bm.bmBitsPixel; + byte[] oldColors = new byte[maxColors * 4]; + OS.GetDIBColorTable(srcHdc, 0, maxColors, oldColors); + int offset = srcImage.transparentPixel * 4; + for (int i = 0; i < oldColors.length; i += 4) { + if (i != offset) { + if (oldColors[offset] == oldColors[i] && oldColors[offset+1] == oldColors[i+1] && oldColors[offset+2] == oldColors[i+2]) { + fixPalette = true; + break; + } + } + } + if (fixPalette) { + byte[] newColors = new byte[oldColors.length]; + transRed = transGreen = transBlue = 0xff; + newColors[offset] = (byte)transBlue; + newColors[offset+1] = (byte)transGreen; + newColors[offset+2] = (byte)transRed; + OS.SetDIBColorTable(srcHdc, 0, maxColors, newColors); + originalColors = oldColors; + } else { + transBlue = oldColors[offset] & 0xFF; + transGreen = oldColors[offset+1] & 0xFF; + transRed = oldColors[offset+2] & 0xFF; + } + } + } else { + /* Palette-based bitmap */ + int numColors = 1 << bm.bmBitsPixel; + /* Set the few fields necessary to get the RGB data out */ + BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER(); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biPlanes = bm.bmPlanes; + bmiHeader.biBitCount = bm.bmBitsPixel; + byte[] bmi = new byte[BITMAPINFOHEADER.sizeof + numColors * 4]; + OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + if (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + OS.GetDIBits(srcHdc, srcImage.handle, 0, 0, 0, bmi, OS.DIB_RGB_COLORS); + int offset = BITMAPINFOHEADER.sizeof + 4 * srcImage.transparentPixel; + transRed = bmi[offset + 2] & 0xFF; + transGreen = bmi[offset + 1] & 0xFF; + transBlue = bmi[offset] & 0xFF; + } + } else { + /* Direct color image */ + int pixel = srcImage.transparentPixel; + switch (bm.bmBitsPixel) { + case 16: + transBlue = (pixel & 0x1F) << 3; + transGreen = (pixel & 0x3E0) >> 2; + transRed = (pixel & 0x7C00) >> 7; + break; + case 24: + transBlue = (pixel & 0xFF0000) >> 16; + transGreen = (pixel & 0xFF00) >> 8; + transRed = pixel & 0xFF; + break; + case 32: + transBlue = (pixel & 0xFF000000) >>> 24; + transGreen = (pixel & 0xFF0000) >> 16; + transRed = (pixel & 0xFF00) >> 8; + break; + } + } + transparentColor = transBlue << 16 | transGreen << 8 | transRed; + if (!fixPalette) srcImage.transparentColor = transparentColor; + } + + if (OS.IsWinCE) { + /* + * Note in WinCE. TransparentImage uses the first entry of a palette + * based image when there are multiple entries that have the same + * transparent color. + */ + OS.TransparentImage(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, transparentColor); + } else if (originalColors == null && OS.IsWinNT && OS.WIN32_VERSION >= OS.VERSION(4, 10)) { + int mode = OS.SetStretchBltMode(handle, OS.COLORONCOLOR); + OS.TransparentBlt(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, transparentColor); + OS.SetStretchBltMode(handle, mode); + } else { + /* Create the mask for the source image */ + int /*long*/ maskHdc = OS.CreateCompatibleDC(handle); + int /*long*/ maskBitmap = OS.CreateBitmap(imgWidth, imgHeight, 1, 1, null); + int /*long*/ oldMaskBitmap = OS.SelectObject(maskHdc, maskBitmap); + OS.SetBkColor(srcHdc, transparentColor); + OS.BitBlt(maskHdc, 0, 0, imgWidth, imgHeight, srcHdc, 0, 0, OS.SRCCOPY); + if (originalColors != null) OS.SetDIBColorTable(srcHdc, 0, 1 << bm.bmBitsPixel, originalColors); + + if (OS.GetDeviceCaps(handle, OS.TECHNOLOGY) == OS.DT_RASPRINTER) { + /* Most printers do not support BitBlt(), draw the source bitmap transparently using clipping */ + drawBitmapTransparentByClipping(srcHdc, maskHdc, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, imgWidth, imgHeight); + } else { + /* Draw the source bitmap transparently using invert/and mask/invert */ + int /*long*/ tempHdc = OS.CreateCompatibleDC(handle); + int /*long*/ tempBitmap = OS.CreateCompatibleBitmap(handle, destWidth, destHeight); + int /*long*/ oldTempBitmap = OS.SelectObject(tempHdc, tempBitmap); + OS.BitBlt(tempHdc, 0, 0, destWidth, destHeight, handle, destX, destY, OS.SRCCOPY); + if (!simple && (srcWidth != destWidth || srcHeight != destHeight)) { + if (!OS.IsWinCE) OS.SetStretchBltMode(tempHdc, OS.COLORONCOLOR); + OS.StretchBlt(tempHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, OS.SRCINVERT); + OS.StretchBlt(tempHdc, 0, 0, destWidth, destHeight, maskHdc, srcX, srcY, srcWidth, srcHeight, OS.SRCAND); + OS.StretchBlt(tempHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, OS.SRCINVERT); + } else { + OS.BitBlt(tempHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcY, OS.SRCINVERT); + OS.BitBlt(tempHdc, 0, 0, destWidth, destHeight, maskHdc, srcX, srcY, OS.SRCAND); + OS.BitBlt(tempHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcY, OS.SRCINVERT); + } + OS.BitBlt(handle, destX, destY, destWidth, destHeight, tempHdc, 0, 0, OS.SRCCOPY); + OS.SelectObject(tempHdc, oldTempBitmap); + OS.DeleteDC(tempHdc); + OS.DeleteObject(tempBitmap); + } + OS.SelectObject(maskHdc, oldMaskBitmap); + OS.DeleteDC(maskHdc); + OS.DeleteObject(maskBitmap); + } + OS.SelectObject(srcHdc, oldSrcBitmap); + if (hBitmap != srcImage.handle) OS.DeleteObject(hBitmap); + OS.DeleteDC(srcHdc); +} + +void drawBitmap(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple, BITMAP bm, int imgWidth, int imgHeight) { + int /*long*/ srcHdc = OS.CreateCompatibleDC(handle); + int /*long*/ oldSrcBitmap = OS.SelectObject(srcHdc, srcImage.handle); + int rop2 = 0; + if (!OS.IsWinCE) { + rop2 = OS.GetROP2(handle); + } else { + rop2 = OS.SetROP2 (handle, OS.R2_COPYPEN); + OS.SetROP2 (handle, rop2); + } + int dwRop = rop2 == OS.R2_XORPEN ? OS.SRCINVERT : OS.SRCCOPY; + if (!simple && (srcWidth != destWidth || srcHeight != destHeight)) { + int mode = 0; + if (!OS.IsWinCE) mode = OS.SetStretchBltMode(handle, OS.COLORONCOLOR); + OS.StretchBlt(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, dwRop); + if (!OS.IsWinCE) OS.SetStretchBltMode(handle, mode); + } else { + OS.BitBlt(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, dwRop); + } + OS.SelectObject(srcHdc, oldSrcBitmap); + OS.DeleteDC(srcHdc); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + checkGC(DRAW); + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend); + Gdip.Graphics_DrawLine(gdipGraphics, data.gdipPen, x1, y1, x2, y2); + Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend); + return; + } + if ((data.style & SWT.MIRRORED) != 0) { + if (data.lineWidth != 0 && data.lineWidth % 2 == 0) { + x1--; + x2--; + } + } + if (OS.IsWinCE) { + int [] points = new int [] {x1, y1, x2, y2}; + OS.Polyline (handle, points, points.length / 2); + } else { + OS.MoveToEx (handle, x1, y1, 0); + OS.LineTo (handle, x2, y2); + } + if (data.lineWidth <= 1) { + OS.SetPixel (handle, x2, y2, data.foreground); + } +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + checkGC(DRAW); + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend); + Gdip.Graphics_DrawEllipse(gdipGraphics, data.gdipPen, x, y, width, height); + Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend); + return; + } + if ((data.style & SWT.MIRRORED) != 0) { + if (data.lineWidth != 0 && data.lineWidth % 2 == 0) x--; + } + OS.Ellipse(handle, x, y, x + width + 1, y + height + 1); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (path == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (path.handle == 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + initGdip(); + checkGC(DRAW); + int /*long*/ gdipGraphics = data.gdipGraphics; + Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend); + Gdip.Graphics_DrawPath(gdipGraphics, data.gdipPen, path.handle); + Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (data.gdipGraphics != 0) { + checkGC(DRAW); + Gdip.Graphics_FillRectangle(data.gdipGraphics, getFgBrush(), x, y, 1, 1); + return; + } + OS.SetPixel (handle, x, y, data.foreground); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (pointArray == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + checkGC(DRAW); + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend); + Gdip.Graphics_DrawPolygon(gdipGraphics, data.gdipPen, pointArray, pointArray.length / 2); + Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend); + return; + } + if ((data.style & SWT.MIRRORED) != 0) { + if (data.lineWidth != 0 && data.lineWidth % 2 == 0) { + for (int i = 0; i < pointArray.length; i+=2) { + pointArray[i]--; + } + } + } + OS.Polygon(handle, pointArray, pointArray.length / 2); + if ((data.style & SWT.MIRRORED) != 0) { + if (data.lineWidth != 0 && data.lineWidth % 2 == 0) { + for (int i = 0; i < pointArray.length; i+=2) { + pointArray[i]++; + } + } + } +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (pointArray == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + checkGC(DRAW); + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend); + Gdip.Graphics_DrawLines(gdipGraphics, data.gdipPen, pointArray, pointArray.length / 2); + Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend); + return; + } + if ((data.style & SWT.MIRRORED) != 0) { + if (data.lineWidth != 0 && data.lineWidth % 2 == 0) { + for (int i = 0; i < pointArray.length; i+=2) { + pointArray[i]--; + } + } + } + OS.Polyline(handle, pointArray, pointArray.length / 2); + int length = pointArray.length; + if (length >= 2) { + if (data.lineWidth <= 1) { + OS.SetPixel (handle, pointArray[length - 2], pointArray[length - 1], data.foreground); + } + } + if ((data.style & SWT.MIRRORED) != 0) { + if (data.lineWidth != 0 && data.lineWidth % 2 == 0) { + for (int i = 0; i < pointArray.length; i+=2) { + pointArray[i]++; + } + } + } +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + checkGC(DRAW); + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend); + Gdip.Graphics_DrawRectangle(gdipGraphics, data.gdipPen, x, y, width, height); + Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend); + return; + } + if ((data.style & SWT.MIRRORED) != 0) { + /* + * Note that Rectangle() subtracts one pixel in MIRRORED mode when + * the pen was created with CreatePen() and its width is 0 or 1. + */ + if (data.lineWidth > 1) { + if ((data.lineWidth % 2) == 1) x++; + } else { + if (data.hPen != 0 && OS.GetObject(data.hPen, 0, 0) != LOGPEN.sizeof) { + x++; + } + } + } + OS.Rectangle (handle, x, y, x + width + 1, y + height + 1); +} + +/** + * 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 (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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + checkGC(DRAW); + if (data.gdipGraphics != 0) { + drawRoundRectangleGdip(data.gdipGraphics, data.gdipPen, x, y, width, height, arcWidth, arcHeight); + return; + } + if ((data.style & SWT.MIRRORED) != 0) { + if (data.lineWidth != 0 && data.lineWidth % 2 == 0) x--; + } + if (OS.IsWinCE) { + /* + * Bug in WinCE PPC. On certain devices, RoundRect does not draw + * all the pixels. The workaround is to draw a round rectangle + * using lines and arcs. + */ + if (width == 0 || height == 0) return; + if (arcWidth == 0 || arcHeight == 0) { + drawRectangle(x, y, width, height); + return; + } + if (width < 0) { + x += width; + width = -width; + } + if (height < 0) { + y += height; + height = -height; + } + if (arcWidth < 0) arcWidth = -arcWidth; + if (arcHeight < 0) arcHeight = -arcHeight; + if (arcWidth > width) arcWidth = width; + if (arcHeight > height) arcHeight = height; + + if (arcWidth < width) { + drawLine(x+arcWidth/2, y, x+width-arcWidth/2, y); + drawLine(x+arcWidth/2, y+height, x+width-arcWidth/2, y+height); + } + if (arcHeight < height) { + drawLine(x, y+arcHeight/2, x, y+height-arcHeight/2); + drawLine(x+width, y+arcHeight/2, x+width, y+height-arcHeight/2); + } + if (arcWidth != 0 && arcHeight != 0) { + drawArc(x, y, arcWidth, arcHeight, 90, 90); + drawArc(x+width-arcWidth, y, arcWidth, arcHeight, 0, 90); + drawArc(x+width-arcWidth, y+height-arcHeight, arcWidth, arcHeight, 0, -90); + drawArc(x, y+height-arcHeight, arcWidth, arcHeight, 180, 90); + } + } else { + OS.RoundRect(handle, x,y,x+width+1,y+height+1, arcWidth, arcHeight); + } +} + +void drawRoundRectangleGdip (int /*long*/ gdipGraphics, int /*long*/ pen, int x, int y, int width, int height, int arcWidth, int arcHeight) { + int nx = x; + int ny = y; + int nw = width; + int nh = height; + int naw = arcWidth; + int nah = arcHeight; + + if (nw < 0) { + nw = 0 - nw; + nx = nx - nw; + } + if (nh < 0) { + nh = 0 - nh; + ny = ny - nh; + } + if (naw < 0) + naw = 0 - naw; + if (nah < 0) + nah = 0 - nah; + + Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend); + if (naw == 0 || nah == 0) { + Gdip.Graphics_DrawRectangle(gdipGraphics, data.gdipPen, x, y, width, height); + } else { + int /*long*/ path = Gdip.GraphicsPath_new(Gdip.FillModeAlternate); + if (path == 0) SWT.error(SWT.ERROR_NO_HANDLES); + if (nw > naw) { + if (nh > nah) { + Gdip.GraphicsPath_AddArc(path, nx + nw - naw, ny, naw, nah, 0, -90); + Gdip.GraphicsPath_AddArc(path, nx, ny, naw, nah, -90, -90); + Gdip.GraphicsPath_AddArc(path, nx, ny + nh - nah, naw, nah, -180, -90); + Gdip.GraphicsPath_AddArc(path, nx + nw - naw, ny + nh - nah, naw, nah, -270, -90); + } else { + Gdip.GraphicsPath_AddArc(path, nx + nw - naw, ny, naw, nh, -270, -180); + Gdip.GraphicsPath_AddArc(path, nx, ny, naw, nh, -90, -180); + } + } else { + if (nh > nah) { + Gdip.GraphicsPath_AddArc(path, nx, ny, nw, nah, 0, -180); + Gdip.GraphicsPath_AddArc(path, nx, ny + nh - nah, nw, nah, -180, -180); + } else { + Gdip.GraphicsPath_AddArc(path, nx, ny, nw, nh, 0, 360); + } + } + Gdip.GraphicsPath_CloseFigure(path); + Gdip.Graphics_DrawPath(gdipGraphics, pen, path); + Gdip.GraphicsPath_delete(path); + } + Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend); +} + +/** + * 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) { + if (handle == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (string == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); +// TCHAR buffer = new TCHAR (getCodePage(), string, false); + int length = string.length(); + if (length == 0) return; + char[] buffer = new char [length]; + string.getChars(0, length, buffer, 0); + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + checkGC(FONT | FOREGROUND | (isTransparent ? 0 : BACKGROUND)); + int nGlyphs = (length * 3 / 2) + 16; + GCP_RESULTS result = new GCP_RESULTS(); + result.lStructSize = GCP_RESULTS.sizeof; + result.nGlyphs = nGlyphs; + int /*long*/ hHeap = OS.GetProcessHeap(); + int /*long*/ lpDx = result.lpDx = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, nGlyphs * 4); + int /*long*/ lpGlyphs = result.lpGlyphs = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, nGlyphs * 2); + int dwFlags = OS.GCP_GLYPHSHAPE | OS.GCP_REORDER | OS.GCP_LIGATE; + int /*long*/ hdc = Gdip.Graphics_GetHDC(gdipGraphics); + int /*long*/ hFont = data.hGDIFont; + if (hFont == 0 && data.font != null) hFont = data.font.handle; + int /*long*/ oldFont = 0; + if (hFont != 0) oldFont = OS.SelectObject(hdc, hFont); + if ((data.style & SWT.MIRRORED) != 0) OS.SetLayout(hdc, OS.GetLayout(hdc) | OS.LAYOUT_RTL); + OS.GetCharacterPlacementW(hdc, buffer, length, 0, result, dwFlags); + if ((data.style & SWT.MIRRORED) != 0) OS.SetLayout(hdc, OS.GetLayout(hdc) & ~OS.LAYOUT_RTL); + TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA(); + OS.GetTextMetrics(hdc, lptm); + if (hFont != 0) OS.SelectObject(hdc, oldFont); + Gdip.Graphics_ReleaseHDC(gdipGraphics, hdc); + nGlyphs = result.nGlyphs; + int drawX = x, drawY = y + lptm.tmAscent; + int[] dx = new int[nGlyphs]; + OS.MoveMemory(dx, result.lpDx, nGlyphs * 4); + float[] points = new float[dx.length * 2]; + for (int i = 0, j = 0; i < dx.length; i++) { + points[j++] = drawX; + points[j++] = drawY; + drawX += dx[i]; + } + RectF bounds = null; + if (!isTransparent || (data.style & SWT.MIRRORED) != 0) { + bounds = new RectF(); + Gdip.Graphics_MeasureDriverString(gdipGraphics, lpGlyphs, nGlyphs, data.gdipFont, points, 0, 0, bounds); + if (!isTransparent) { + Gdip.Graphics_FillRectangle(gdipGraphics, data.gdipBrush, x, y, Math.round(bounds.Width), Math.round(bounds.Height)); + } + } + int gstate = 0; + int /*long*/ brush = getFgBrush(); + if ((data.style & SWT.MIRRORED) != 0) { + switch (Gdip.Brush_GetType(brush)) { + case Gdip.BrushTypeLinearGradient: + Gdip.LinearGradientBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend); + Gdip.LinearGradientBrush_TranslateTransform(brush, - 2 * x - bounds.Width, 0, Gdip.MatrixOrderPrepend); + break; + case Gdip.BrushTypeTextureFill: + Gdip.TextureBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend); + Gdip.TextureBrush_TranslateTransform(brush, - 2 * x - bounds.Width, 0, Gdip.MatrixOrderPrepend); + break; + } + gstate = Gdip.Graphics_Save(gdipGraphics); + Gdip.Graphics_ScaleTransform(gdipGraphics, -1, 1, Gdip.MatrixOrderPrepend); + Gdip.Graphics_TranslateTransform(gdipGraphics, - 2 * x - bounds.Width, 0, Gdip.MatrixOrderPrepend); + } + Gdip.Graphics_DrawDriverString(gdipGraphics, lpGlyphs, result.nGlyphs, data.gdipFont, brush, points, 0, 0); + if ((data.style & SWT.MIRRORED) != 0) { + switch (Gdip.Brush_GetType(brush)) { + case Gdip.BrushTypeLinearGradient: + Gdip.LinearGradientBrush_ResetTransform(brush); + break; + case Gdip.BrushTypeTextureFill: + Gdip.TextureBrush_ResetTransform(brush); + break; + } + Gdip.Graphics_Restore(gdipGraphics, gstate); + } + OS.HeapFree(hHeap, 0, lpGlyphs); + OS.HeapFree(hHeap, 0, lpDx); + return; + } + int rop2 = 0; + if (OS.IsWinCE) { + rop2 = OS.SetROP2(handle, OS.R2_COPYPEN); + OS.SetROP2(handle, rop2); + } else { + rop2 = OS.GetROP2(handle); + } + checkGC(FONT | FOREGROUND_TEXT | BACKGROUND_TEXT); + int oldBkMode = OS.SetBkMode(handle, isTransparent ? OS.TRANSPARENT : OS.OPAQUE); + RECT rect = null; + SIZE size = null; + int flags = 0; + if ((data.style & SWT.MIRRORED) != 0) { + if (!isTransparent) { + size = new SIZE(); + OS.GetTextExtentPoint32W(handle, buffer, length, size); + rect = new RECT (); + rect.left = x; + rect.right = x + size.cx; + rect.top = y; + rect.bottom = y + size.cy; + flags = OS.ETO_CLIPPED; + } + x--; + } + if (rop2 != OS.R2_XORPEN) { + OS.ExtTextOutW(handle, x, y, flags, rect, buffer, length, null); + } else { + int foreground = OS.GetTextColor(handle); + if (isTransparent) { + if (size == null) { + size = new SIZE(); + OS.GetTextExtentPoint32W(handle, buffer, length, size); + } + int width = size.cx, height = size.cy; + int /*long*/ hBitmap = OS.CreateCompatibleBitmap(handle, width, height); + if (hBitmap == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ memDC = OS.CreateCompatibleDC(handle); + int /*long*/ hOldBitmap = OS.SelectObject(memDC, hBitmap); + OS.PatBlt(memDC, 0, 0, width, height, OS.BLACKNESS); + OS.SetBkMode(memDC, OS.TRANSPARENT); + OS.SetTextColor(memDC, foreground); + OS.SelectObject(memDC, OS.GetCurrentObject(handle, OS.OBJ_FONT)); + OS.ExtTextOutW(memDC, 0, 0, 0, null, buffer, length, null); + OS.BitBlt(handle, x, y, width, height, memDC, 0, 0, OS.SRCINVERT); + OS.SelectObject(memDC, hOldBitmap); + OS.DeleteDC(memDC); + OS.DeleteObject(hBitmap); + } else { + int background = OS.GetBkColor(handle); + OS.SetTextColor(handle, foreground ^ background); + OS.ExtTextOutW(handle, x, y, flags, rect, buffer, length, null); + OS.SetTextColor(handle, foreground); + } + } + OS.SetBkMode(handle, oldBkMode); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (string == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (string.length() == 0) return; + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + checkGC(FONT | FOREGROUND | ((flags & SWT.DRAW_TRANSPARENT) != 0 ? 0 : BACKGROUND)); + int length = string.length(); + char[] buffer = new char [length]; + string.getChars(0, length, buffer, 0); + PointF pt = new PointF(); + int /*long*/ format = Gdip.StringFormat_Clone(Gdip.StringFormat_GenericTypographic()); + int formatFlags = Gdip.StringFormat_GetFormatFlags(format) | Gdip.StringFormatFlagsMeasureTrailingSpaces; + if ((data.style & SWT.MIRRORED) != 0) formatFlags |= Gdip.StringFormatFlagsDirectionRightToLeft; + Gdip.StringFormat_SetFormatFlags(format, formatFlags); + float[] tabs = (flags & SWT.DRAW_TAB) != 0 ? new float[]{measureSpace(data.gdipFont, format) * 8} : new float[1]; + Gdip.StringFormat_SetTabStops(format, 0, tabs.length, tabs); + int hotkeyPrefix = (flags & SWT.DRAW_MNEMONIC) != 0 ? Gdip.HotkeyPrefixShow : Gdip.HotkeyPrefixNone; + if ((flags & SWT.DRAW_MNEMONIC) != 0 && (data.uiState & OS.UISF_HIDEACCEL) != 0) hotkeyPrefix = Gdip.HotkeyPrefixHide; + Gdip.StringFormat_SetHotkeyPrefix(format, hotkeyPrefix); + if ((flags & SWT.DRAW_TRANSPARENT) == 0) { + RectF bounds = new RectF(); + Gdip.Graphics_MeasureString(gdipGraphics, buffer, length, data.gdipFont, pt, format, bounds); + Gdip.Graphics_FillRectangle(gdipGraphics, data.gdipBrush, x, y, Math.round(bounds.Width), Math.round(bounds.Height)); + } + int gstate = 0; + int /*long*/ brush = getFgBrush(); + if ((data.style & SWT.MIRRORED) != 0) { + switch (Gdip.Brush_GetType(brush)) { + case Gdip.BrushTypeLinearGradient: + Gdip.LinearGradientBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend); + Gdip.LinearGradientBrush_TranslateTransform(brush, - 2 * x, 0, Gdip.MatrixOrderPrepend); + break; + case Gdip.BrushTypeTextureFill: + Gdip.TextureBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend); + Gdip.TextureBrush_TranslateTransform(brush, - 2 * x, 0, Gdip.MatrixOrderPrepend); + break; + } + gstate = Gdip.Graphics_Save(gdipGraphics); + Gdip.Graphics_ScaleTransform(gdipGraphics, -1, 1, Gdip.MatrixOrderPrepend); + Gdip.Graphics_TranslateTransform(gdipGraphics, - 2 * x, 0, Gdip.MatrixOrderPrepend); + } + pt.X = x; + pt.Y = y; + Gdip.Graphics_DrawString(gdipGraphics, buffer, length, data.gdipFont, pt, format, brush); + if ((data.style & SWT.MIRRORED) != 0) { + switch (Gdip.Brush_GetType(brush)) { + case Gdip.BrushTypeLinearGradient: + Gdip.LinearGradientBrush_ResetTransform(brush); + break; + case Gdip.BrushTypeTextureFill: + Gdip.TextureBrush_ResetTransform(brush); + break; + } + Gdip.Graphics_Restore(gdipGraphics, gstate); + } + Gdip.StringFormat_delete(format); + return; + } + TCHAR buffer = new TCHAR(getCodePage(), string, false); + int length = buffer.length(); + if (length == 0) return; + RECT rect = new RECT(); + /* + * Feature in Windows. For some reason DrawText(), the maximum + * value for the bottom and right coordinates for the RECT that + * is used to position the text is different on between Windows + * versions. If this value is larger than the maximum, nothing + * is drawn. On Windows 98, the limit is 0x7FFF. On Windows CE, + * NT, and 2000 it is 0x6FFFFFF. And on XP, it is 0x7FFFFFFF. + * The fix is to use the the smaller limit for Windows 98 and the + * larger limit on the other Windows platforms. + */ + int limit = OS.IsWin95 ? 0x7FFF : 0x6FFFFFF; + OS.SetRect(rect, x, y, limit, limit); + int uFormat = OS.DT_LEFT; + if ((flags & SWT.DRAW_DELIMITER) == 0) uFormat |= OS.DT_SINGLELINE; + if ((flags & SWT.DRAW_TAB) != 0) uFormat |= OS.DT_EXPANDTABS; + if ((flags & SWT.DRAW_MNEMONIC) == 0) uFormat |= OS.DT_NOPREFIX; + if ((flags & SWT.DRAW_MNEMONIC) != 0 && (data.uiState & OS.UISF_HIDEACCEL) != 0) { + uFormat |= OS.DT_HIDEPREFIX; + } + int rop2 = 0; + if (OS.IsWinCE) { + rop2 = OS.SetROP2(handle, OS.R2_COPYPEN); + OS.SetROP2(handle, rop2); + } else { + rop2 = OS.GetROP2(handle); + } + checkGC(FONT | FOREGROUND_TEXT | BACKGROUND_TEXT); + int oldBkMode = OS.SetBkMode(handle, (flags & SWT.DRAW_TRANSPARENT) != 0 ? OS.TRANSPARENT : OS.OPAQUE); + if (rop2 != OS.R2_XORPEN) { + OS.DrawText(handle, buffer, length, rect, uFormat); + } else { + int foreground = OS.GetTextColor(handle); + if ((flags & SWT.DRAW_TRANSPARENT) != 0) { + OS.DrawText(handle, buffer, buffer.length(), rect, uFormat | OS.DT_CALCRECT); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + int /*long*/ hBitmap = OS.CreateCompatibleBitmap(handle, width, height); + if (hBitmap == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ memDC = OS.CreateCompatibleDC(handle); + int /*long*/ hOldBitmap = OS.SelectObject(memDC, hBitmap); + OS.PatBlt(memDC, 0, 0, width, height, OS.BLACKNESS); + OS.SetBkMode(memDC, OS.TRANSPARENT); + OS.SetTextColor(memDC, foreground); + OS.SelectObject(memDC, OS.GetCurrentObject(handle, OS.OBJ_FONT)); + OS.SetRect(rect, 0, 0, 0x7FFF, 0x7FFF); + OS.DrawText(memDC, buffer, length, rect, uFormat); + OS.BitBlt(handle, x, y, width, height, memDC, 0, 0, OS.SRCINVERT); + OS.SelectObject(memDC, hOldBitmap); + OS.DeleteDC(memDC); + OS.DeleteObject(hBitmap); + } else { + int background = OS.GetBkColor(handle); + OS.SetTextColor(handle, foreground ^ background); + OS.DrawText(handle, buffer, length, rect, uFormat); + OS.SetTextColor(handle, foreground); + } + } + OS.SetBkMode(handle, oldBkMode); +} + +/** + * 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) { + return (object == this) || ((object instanceof GC) && (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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + checkGC(FILL); + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + if (width == 0 || height == 0 || arcAngle == 0) return; + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + if (width == height) { + Gdip.Graphics_FillPie(gdipGraphics, data.gdipBrush, x, y, width, height, -startAngle, -arcAngle); + } else { + int state = Gdip.Graphics_Save(gdipGraphics); + Gdip.Graphics_TranslateTransform(gdipGraphics, x, y, Gdip.MatrixOrderPrepend); + Gdip.Graphics_ScaleTransform(gdipGraphics, width, height, Gdip.MatrixOrderPrepend); + Gdip.Graphics_FillPie(gdipGraphics, data.gdipBrush, 0, 0, 1, 1, -startAngle, -arcAngle); + Gdip.Graphics_Restore(gdipGraphics, state); + } + return; + } + + if ((data.style & SWT.MIRRORED) != 0) x--; + /* + * Feature in WinCE. The function Pie is not present in the + * WinCE SDK. The fix is to emulate it by using Polygon. + */ + if (OS.IsWinCE) { + /* compute arc with a simple linear interpolation */ + if (arcAngle < 0) { + startAngle += arcAngle; + arcAngle = -arcAngle; + } + boolean drawSegments = true; + if (arcAngle >= 360) { + arcAngle = 360; + drawSegments = false; + } + int[] points = new int[(arcAngle + 1) * 2 + (drawSegments ? 4 : 0)]; + int cteX = 2 * x + width; + int cteY = 2 * y + height; + int index = (drawSegments ? 2 : 0); + for (int i = 0; i <= arcAngle; i++) { + points[index++] = (Compatibility.cos(startAngle + i, width) + cteX) >> 1; + points[index++] = (cteY - Compatibility.sin(startAngle + i, height)) >> 1; + } + if (drawSegments) { + points[0] = points[points.length - 2] = cteX >> 1; + points[1] = points[points.length - 1] = cteY >> 1; + } + OS.Polygon(handle, points, points.length / 2); + } else { + int x1, y1, x2, y2,tmp; + boolean isNegative; + if (arcAngle >= 360 || arcAngle <= -360) { + x1 = x2 = x + width; + y1 = y2 = y + height / 2; + } else { + isNegative = arcAngle < 0; + + arcAngle = arcAngle + startAngle; + if (isNegative) { + // swap angles + tmp = startAngle; + startAngle = arcAngle; + arcAngle = tmp; + } + x1 = Compatibility.cos(startAngle, width) + x + width/2; + y1 = -1 * Compatibility.sin(startAngle, height) + y + height/2; + + x2 = Compatibility.cos(arcAngle, width) + x + width/2; + y2 = -1 * Compatibility.sin(arcAngle, height) + y + height/2; + } + OS.Pie(handle, x, y, x + width + 1, y + height + 1, x1, y1, x2, y2); + } +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (width == 0 || height == 0) return; + + 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); + return; + } + if (data.gdipGraphics != 0) { + initGdip(); + PointF p1= new PointF(), p2 = new PointF(); + p1.X = x; + p1.Y = y; + if (vertical) { + p2.X = p1.X; + p2.Y = p1.Y + height; + } else { + p2.X = p1.X + width; + p2.Y = p1.Y; + } + int rgb = ((fromRGB.red & 0xFF) << 16) | ((fromRGB.green & 0xFF) << 8) | (fromRGB.blue & 0xFF); + int /*long*/ fromGpColor = Gdip.Color_new(data.alpha << 24 | rgb); + if (fromGpColor == 0) SWT.error(SWT.ERROR_NO_HANDLES); + rgb = ((toRGB.red & 0xFF) << 16) | ((toRGB.green & 0xFF) << 8) | (toRGB.blue & 0xFF); + int /*long*/ toGpColor = Gdip.Color_new(data.alpha << 24 | rgb); + if (toGpColor == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ brush = Gdip.LinearGradientBrush_new(p1, p2, fromGpColor, toGpColor); + Gdip.Graphics_FillRectangle(data.gdipGraphics, brush, x, y, width, height); + Gdip.LinearGradientBrush_delete(brush); + Gdip.Color_delete(fromGpColor); + Gdip.Color_delete(toGpColor); + return; + } + /* Use GradientFill if supported, only on Windows 98, 2000 and newer. */ + /* + * Bug in Windows: On Windows 2000 when the device is a printer, + * GradientFill swaps red and blue color components, causing the + * gradient to be printed in the wrong color. On Windows 98 when + * the device is a printer, GradientFill does not fill completely + * to the right edge of the rectangle. The fix is not to use + * GradientFill for printer devices. + */ + int rop2 = 0; + if (OS.IsWinCE) { + rop2 = OS.SetROP2(handle, OS.R2_COPYPEN); + OS.SetROP2(handle, rop2); + } else { + rop2 = OS.GetROP2(handle); + } + if (OS.IsWinNT && rop2 != OS.R2_XORPEN && OS.GetDeviceCaps(handle, OS.TECHNOLOGY) != OS.DT_RASPRINTER) { + final int /*long*/ hHeap = OS.GetProcessHeap(); + final int /*long*/ pMesh = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, GRADIENT_RECT.sizeof + TRIVERTEX.sizeof * 2); + if (pMesh == 0) SWT.error(SWT.ERROR_NO_HANDLES); + final int /*long*/ pVertex = pMesh + GRADIENT_RECT.sizeof; + + GRADIENT_RECT gradientRect = new GRADIENT_RECT(); + gradientRect.UpperLeft = 0; + gradientRect.LowerRight = 1; + OS.MoveMemory(pMesh, gradientRect, GRADIENT_RECT.sizeof); + + TRIVERTEX trivertex = new TRIVERTEX(); + trivertex.x = x; + trivertex.y = y; + trivertex.Red = (short)((fromRGB.red << 8) | fromRGB.red); + trivertex.Green = (short)((fromRGB.green << 8) | fromRGB.green); + trivertex.Blue = (short)((fromRGB.blue << 8) | fromRGB.blue); + trivertex.Alpha = -1; + OS.MoveMemory(pVertex, trivertex, TRIVERTEX.sizeof); + + trivertex.x = x + width; + trivertex.y = y + height; + trivertex.Red = (short)((toRGB.red << 8) | toRGB.red); + trivertex.Green = (short)((toRGB.green << 8) | toRGB.green); + trivertex.Blue = (short)((toRGB.blue << 8) | toRGB.blue); + trivertex.Alpha = -1; + OS.MoveMemory(pVertex + TRIVERTEX.sizeof, trivertex, TRIVERTEX.sizeof); + + boolean success = OS.GradientFill(handle, pVertex, 2, pMesh, 1, vertical ? OS.GRADIENT_FILL_RECT_V : OS.GRADIENT_FILL_RECT_H); + OS.HeapFree(hHeap, 0, pMesh); + if (success) return; + } + + final int depth = OS.GetDeviceCaps(handle, OS.BITSPIXEL); + final int bitResolution = (depth >= 24) ? 8 : (depth >= 15) ? 5 : 0; + ImageData.fillGradientRectangle(this, data.device, + x, y, width, height, vertical, fromRGB, toRGB, + bitResolution, bitResolution, bitResolution); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + checkGC(FILL); + if (data.gdipGraphics != 0) { + Gdip.Graphics_FillEllipse(data.gdipGraphics, data.gdipBrush, x, y, width, height); + return; + } + if ((data.style & SWT.MIRRORED) != 0) x--; + OS.Ellipse(handle, x, y, x + width + 1, y + height + 1); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (path == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (path.handle == 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + initGdip(); + checkGC(FILL); + int mode = OS.GetPolyFillMode(handle) == OS.WINDING ? Gdip.FillModeWinding : Gdip.FillModeAlternate; + Gdip.GraphicsPath_SetFillMode(path.handle, mode); + Gdip.Graphics_FillPath(data.gdipGraphics, data.gdipBrush, path.handle); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (pointArray == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + checkGC(FILL); + if (data.gdipGraphics != 0) { + int mode = OS.GetPolyFillMode(handle) == OS.WINDING ? Gdip.FillModeWinding : Gdip.FillModeAlternate; + Gdip.Graphics_FillPolygon(data.gdipGraphics, data.gdipBrush, pointArray, pointArray.length / 2, mode); + return; + } + if ((data.style & SWT.MIRRORED) != 0) { + for (int i = 0; i < pointArray.length; i+=2) { + pointArray[i]--; + } + } + OS.Polygon(handle, pointArray, pointArray.length / 2); + if ((data.style & SWT.MIRRORED) != 0) { + for (int i = 0; i < pointArray.length; i+=2) { + pointArray[i]++; + } + } +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + checkGC(FILL); + if (data.gdipGraphics != 0) { + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + Gdip.Graphics_FillRectangle(data.gdipGraphics, data.gdipBrush, x, y, width, height); + return; + } + int rop2 = 0; + if (OS.IsWinCE) { + rop2 = OS.SetROP2(handle, OS.R2_COPYPEN); + OS.SetROP2(handle, rop2); + } else { + rop2 = OS.GetROP2(handle); + } + int dwRop = rop2 == OS.R2_XORPEN ? OS.PATINVERT : OS.PATCOPY; + OS.PatBlt(handle, x, y, width, height, dwRop); +} + +/** + * 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 (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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + checkGC(FILL); + if (data.gdipGraphics != 0) { + fillRoundRectangleGdip(data.gdipGraphics, data.gdipBrush, x, y, width, height, arcWidth, arcHeight); + return; + } + if ((data.style & SWT.MIRRORED) != 0) x--; + OS.RoundRect(handle, x,y,x+width+1,y+height+1,arcWidth, arcHeight); +} + +void fillRoundRectangleGdip (int /*long*/ gdipGraphics, int /*long*/ brush, int x, int y, int width, int height, int arcWidth, int arcHeight) { + int nx = x; + int ny = y; + int nw = width; + int nh = height; + int naw = arcWidth; + int nah = arcHeight; + + if (nw < 0) { + nw = 0 - nw; + nx = nx - nw; + } + if (nh < 0) { + nh = 0 - nh; + ny = ny -nh; + } + if (naw < 0) + naw = 0 - naw; + if (nah < 0) + nah = 0 - nah; + + if (naw == 0 || nah == 0) { + Gdip.Graphics_FillRectangle(data.gdipGraphics, data.gdipBrush, x, y, width, height); + } else { + int /*long*/ path = Gdip.GraphicsPath_new(Gdip.FillModeAlternate); + if (path == 0) SWT.error(SWT.ERROR_NO_HANDLES); + if (nw > naw) { + if (nh > nah) { + Gdip.GraphicsPath_AddArc(path, nx + nw - naw, ny, naw, nah, 0, -90); + Gdip.GraphicsPath_AddArc(path, nx, ny, naw, nah, -90, -90); + Gdip.GraphicsPath_AddArc(path, nx, ny + nh - nah, naw, nah, -180, -90); + Gdip.GraphicsPath_AddArc(path, nx + nw - naw, ny + nh - nah, naw, nah, -270, -90); + } else { + Gdip.GraphicsPath_AddArc(path, nx + nw - naw, ny, naw, nh, -270, -180); + Gdip.GraphicsPath_AddArc(path, nx, ny, naw, nh, -90, -180); + } + } else { + if (nh > nah) { + Gdip.GraphicsPath_AddArc(path, nx, ny, nw, nah, 0, -180); + Gdip.GraphicsPath_AddArc(path, nx, ny + nh - nah, nw, nah, -180, -180); + } else { + Gdip.GraphicsPath_AddArc(path, nx, ny, nw, nh, 0, 360); + } + } + Gdip.GraphicsPath_CloseFigure(path); + Gdip.Graphics_FillPath(gdipGraphics, brush, path); + Gdip.GraphicsPath_delete(path); + } +} + +void flush () { + if (data.gdipGraphics != 0) { + Gdip.Graphics_Flush(data.gdipGraphics, 0); + /* + * Note Flush() does not flush the output to the + * underline HDC. This is done by calling GetHDC() + * followed by ReleaseHDC(). + */ + int /*long*/ hdc = Gdip.Graphics_GetHDC(data.gdipGraphics); + Gdip.Graphics_ReleaseHDC(data.gdipGraphics, hdc); + } +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + checkGC(FONT); + if (OS.IsWinCE) { + SIZE size = new SIZE(); + OS.GetTextExtentPoint32W(handle, new char[]{ch}, 1, size); + return size.cx; + } + int tch = ch; + if (ch > 0x7F) { + TCHAR buffer = new TCHAR(getCodePage(), ch, false); + tch = buffer.tcharAt(0); + } + int[] width = new int[1]; + OS.GetCharWidth(handle, tch, tch, width); + return width[0]; +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return data.gdipGraphics != 0; +} + +/** + * 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 == 0) 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (data.gdipGraphics == 0) return SWT.DEFAULT; + int mode = Gdip.Graphics_GetSmoothingMode(data.gdipGraphics); + switch (mode) { + case Gdip.SmoothingModeDefault: return SWT.DEFAULT; + case Gdip.SmoothingModeHighSpeed: + case Gdip.SmoothingModeNone: return SWT.OFF; + case Gdip.SmoothingModeAntiAlias: + case Gdip.SmoothingModeAntiAlias8x8: + case Gdip.SmoothingModeHighQuality: return SWT.ON; + } + return SWT.DEFAULT; +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return Color.win32_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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return data.backgroundPattern; +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + checkGC(FONT); + + /* GetCharABCWidths only succeeds on truetype fonts */ + if (!OS.IsWinCE) { + int tch = ch; + if (ch > 0x7F) { + TCHAR buffer = new TCHAR(getCodePage(), ch, false); + tch = buffer.tcharAt (0); + } + int[] width = new int[3]; + if (OS.GetCharABCWidths(handle, tch, tch, width)) { + return width[1]; + } + } + + /* It wasn't a truetype font */ + TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA(); + OS.GetTextMetrics(handle, lptm); + SIZE size = new SIZE(); + OS.GetTextExtentPoint32W(handle, new char[]{ch}, 1, size); + return size.cx - lptm.tmOverhang; +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + Rect rect = new Rect(); + Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone); + Gdip.Graphics_GetVisibleClipBounds(gdipGraphics, rect); + Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf); + return new Rectangle(rect.X, rect.Y, rect.Width, rect.Height); + } + RECT rect = new RECT(); + OS.GetClipBox(handle, rect); + return new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (region == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + if (region.isDisposed()) SWT.error (SWT.ERROR_INVALID_ARGUMENT); + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + int /*long*/ rgn = Gdip.Region_new(); + Gdip.Graphics_GetClip(data.gdipGraphics, rgn); + if (Gdip.Region_IsInfinite(rgn, gdipGraphics)) { + Rect rect = new Rect(); + Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone); + Gdip.Graphics_GetVisibleClipBounds(gdipGraphics, rect); + Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf); + OS.SetRectRgn(region.handle, rect.X, rect.Y, rect.X + rect.Width, rect.Y + rect.Height); + } else { + int /*long*/ matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0); + int /*long*/ identity = Gdip.Matrix_new(1, 0, 0, 1, 0, 0); + Gdip.Graphics_GetTransform(gdipGraphics, matrix); + Gdip.Graphics_SetTransform(gdipGraphics, identity); + int /*long*/ hRgn = Gdip.Region_GetHRGN(rgn, data.gdipGraphics); + Gdip.Graphics_SetTransform(gdipGraphics, matrix); + Gdip.Matrix_delete(identity); + Gdip.Matrix_delete(matrix); + if (!OS.IsWinCE) { + POINT pt = new POINT (); + OS.GetWindowOrgEx (handle, pt); + OS.OffsetRgn (hRgn, pt.x, pt.y); + } + OS.CombineRgn(region.handle, hRgn, 0, OS.RGN_COPY); + OS.DeleteObject(hRgn); + } + Gdip.Region_delete(rgn); + return; + } + POINT pt = new POINT (); + if (!OS.IsWinCE) OS.GetWindowOrgEx (handle, pt); + int result = OS.GetClipRgn (handle, region.handle); + if (result != 1) { + RECT rect = new RECT(); + OS.GetClipBox(handle, rect); + OS.SetRectRgn(region.handle, rect.left, rect.top, rect.right, rect.bottom); + } else { + OS.OffsetRgn (region.handle, pt.x, pt.y); + } + if (!OS.IsWinCE) { + int /*long*/ metaRgn = OS.CreateRectRgn (0, 0, 0, 0); + if (OS.GetMetaRgn (handle, metaRgn) != 0) { + OS.OffsetRgn (metaRgn, pt.x, pt.y); + OS.CombineRgn (region.handle, metaRgn, region.handle, OS.RGN_AND); + } + OS.DeleteObject(metaRgn); + int /*long*/ hwnd = data.hwnd; + if (hwnd != 0 && data.ps != null) { + int /*long*/ sysRgn = OS.CreateRectRgn (0, 0, 0, 0); + if (OS.GetRandomRgn (handle, sysRgn, OS.SYSRGN) == 1) { + if (OS.WIN32_VERSION >= OS.VERSION(4, 10)) { + if ((OS.GetLayout(handle) & OS.LAYOUT_RTL) != 0) { + int nBytes = OS.GetRegionData (sysRgn, 0, null); + int [] lpRgnData = new int [nBytes / 4]; + OS.GetRegionData (sysRgn, nBytes, lpRgnData); + int /*long*/ newSysRgn = OS.ExtCreateRegion(new float [] {-1, 0, 0, 1, 0, 0}, nBytes, lpRgnData); + OS.DeleteObject(sysRgn); + sysRgn = newSysRgn; + } + } + if (OS.IsWinNT) { + OS.MapWindowPoints(0, hwnd, pt, 1); + OS.OffsetRgn(sysRgn, pt.x, pt.y); + } + OS.CombineRgn (region.handle, sysRgn, region.handle, OS.RGN_AND); + } + OS.DeleteObject(sysRgn); + } + } +} + +int getCodePage () { + if (OS.IsUnicode) return OS.CP_ACP; + int[] lpCs = new int[8]; + int cs = OS.GetTextCharset(handle); + OS.TranslateCharsetInfo(cs, lpCs, OS.TCI_SRCCHARSET); + return lpCs[1]; +} + +int /*long*/ getFgBrush() { + return data.foregroundPattern != null ? data.foregroundPattern.handle : data.gdipFgBrush; +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (OS.IsWinCE) return SWT.FILL_EVEN_ODD; + return OS.GetPolyFillMode(handle) == OS.WINDING ? SWT.FILL_WINDING : SWT.FILL_EVEN_ODD; +} + +/** + * 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 == 0) 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + checkGC(FONT); + TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA(); + OS.GetTextMetrics(handle, lptm); + return FontMetrics.win32_new(lptm); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + return Color.win32_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 == 0) SWT.error(SWT.ERROR_GRAPHIC_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 == 0) SWT.error(SWT.ERROR_GRAPHIC_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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (data.gdipGraphics == 0) return SWT.DEFAULT; + int mode = Gdip.Graphics_GetInterpolationMode(data.gdipGraphics); + switch (mode) { + case Gdip.InterpolationModeDefault: return SWT.DEFAULT; + case Gdip.InterpolationModeNearestNeighbor: return SWT.NONE; + case Gdip.InterpolationModeBilinear: + case Gdip.InterpolationModeLowQuality: return SWT.LOW; + case Gdip.InterpolationModeBicubic: + case Gdip.InterpolationModeHighQualityBilinear: + case Gdip.InterpolationModeHighQualityBicubic: + case Gdip.InterpolationModeHighQuality: 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 == 0) 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 == 0) 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 == 0) 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 == 0) 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 == 0) 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 == 0) 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 == 0) 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (data.gdipGraphics == 0) return SWT.DEFAULT; + int mode = Gdip.Graphics_GetTextRenderingHint(data.gdipGraphics); + switch (mode) { + case Gdip.TextRenderingHintSystemDefault: return SWT.DEFAULT; + case Gdip.TextRenderingHintSingleBitPerPixel: + case Gdip.TextRenderingHintSingleBitPerPixelGridFit: return SWT.OFF; + case Gdip.TextRenderingHintAntiAlias: + case Gdip.TextRenderingHintAntiAliasGridFit: + case Gdip.TextRenderingHintClearTypeGridFit: return SWT.ON; + } + return SWT.DEFAULT; +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (transform == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (transform.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + Gdip.Graphics_GetTransform(gdipGraphics, transform.handle); + int /*long*/ identity = identity(); + Gdip.Matrix_Invert(identity); + Gdip.Matrix_Multiply(transform.handle, identity, Gdip.MatrixOrderAppend); + Gdip.Matrix_delete(identity); + } 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + int rop2 = 0; + if (OS.IsWinCE) { + rop2 = OS.SetROP2 (handle, OS.R2_COPYPEN); + OS.SetROP2 (handle, rop2); + } else { + rop2 = OS.GetROP2(handle); + } + return rop2 == OS.R2_XORPEN; +} + +void initGdip() { + data.device.checkGDIP(); + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) return; + /* + * Feature in GDI+. The GDI+ clipping set with Graphics->SetClip() + * is always intersected with the GDI clipping at the time the + * GDI+ graphics is created. This means that the clipping + * cannot be reset. The fix is to clear the clipping before + * the GDI+ graphics is created and reset it afterwards. + */ + int /*long*/ hRgn = OS.CreateRectRgn(0, 0, 0, 0); + int result = OS.GetClipRgn(handle, hRgn); + if (!OS.IsWinCE) { + POINT pt = new POINT (); + OS.GetWindowOrgEx (handle, pt); + OS.OffsetRgn (hRgn, pt.x, pt.y); + } + OS.SelectClipRgn(handle, 0); + + /* + * Bug in GDI+. GDI+ does not work when the HDC layout is RTL. There + * are many issues like pixel corruption, but the most visible problem + * is that it does not have an effect when drawing to an bitmap. The + * fix is to clear the bit before creating the GDI+ graphics and install + * a mirroring matrix ourselves. + */ + if ((data.style & SWT.MIRRORED) != 0) { + OS.SetLayout(handle, OS.GetLayout(handle) & ~OS.LAYOUT_RTL); + } + + gdipGraphics = data.gdipGraphics = Gdip.Graphics_new(handle); + if (gdipGraphics == 0) SWT.error(SWT.ERROR_NO_HANDLES); + Gdip.Graphics_SetPageUnit(gdipGraphics, Gdip.UnitPixel); + Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf); + if ((data.style & SWT.MIRRORED) != 0) { + int /*long*/ matrix = identity(); + Gdip.Graphics_SetTransform(gdipGraphics, matrix); + Gdip.Matrix_delete(matrix); + } + if (result == 1) setClipping(hRgn); + OS.DeleteObject(hRgn); + data.state = 0; + if (data.hPen != 0) { + OS.SelectObject(handle, OS.GetStockObject(OS.NULL_PEN)); + OS.DeleteObject(data.hPen); + data.hPen = 0; + } + if (data.hBrush != 0) { + OS.SelectObject(handle, OS.GetStockObject(OS.NULL_BRUSH)); + OS.DeleteObject(data.hBrush); + data.hBrush = 0; + } +} + +int /*long*/ identity() { + if ((data.style & SWT.MIRRORED) != 0) { + int width = 0; + int technology = OS.GetDeviceCaps(handle, OS.TECHNOLOGY); + if (technology == OS.DT_RASPRINTER) { + width = OS.GetDeviceCaps(handle, OS.PHYSICALWIDTH); + } else { + Image image = data.image; + if (image != null) { + BITMAP bm = new BITMAP(); + OS.GetObject(image.handle, BITMAP.sizeof, bm); + width = bm.bmWidth; + } else { + int /*long*/ hwnd = OS.IsWinCE ? data.hwnd : OS.WindowFromDC(handle); + if (hwnd != 0) { + RECT rect = new RECT(); + OS.GetClientRect(hwnd, rect); + width = rect.right - rect.left; + } else { + int /*long*/ hBitmap = OS.GetCurrentObject(handle, OS.OBJ_BITMAP); + BITMAP bm = new BITMAP(); + OS.GetObject(hBitmap, BITMAP.sizeof, bm); + width = bm.bmWidth; + } + } + } + POINT pt = new POINT (); + if (!OS.IsWinCE) OS.GetWindowOrgEx (handle, pt); + return Gdip.Matrix_new(-1, 0, 0, 1, width + 2 * pt.x, 0); + } + return Gdip.Matrix_new(1, 0, 0, 1, 0, 0); +} + +void init(Drawable drawable, GCData data, int /*long*/ hDC) { + int foreground = data.foreground; + if (foreground != -1) { + data.state &= ~(FOREGROUND | FOREGROUND_TEXT | PEN); + } else { + data.foreground = OS.GetTextColor(hDC); + } + int background = data.background; + if (background != -1) { + data.state &= ~(BACKGROUND | BACKGROUND_TEXT | BRUSH); + } else { + data.background = OS.GetBkColor(hDC); + } + data.state &= ~(NULL_BRUSH | NULL_PEN); + Font font = data.font; + if (font != null) { + data.state &= ~FONT; + } else { + data.font = Font.win32_new(device, OS.GetCurrentObject(hDC, OS.OBJ_FONT)); + } + int /*long*/ hPalette = data.device.hPalette; + if (hPalette != 0) { + OS.SelectPalette(hDC, hPalette, true); + OS.RealizePalette(hDC); + } + Image image = data.image; + if (image != null) { + data.hNullBitmap = OS.SelectObject(hDC, image.handle); + image.memGC = this; + } + int layout = data.layout; + if (layout != -1) { + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) { + int flags = OS.GetLayout(hDC); + if ((flags & OS.LAYOUT_RTL) != (layout & OS.LAYOUT_RTL)) { + flags &= ~OS.LAYOUT_RTL; + OS.SetLayout(hDC, flags | layout); + } + if ((data.style & SWT.RIGHT_TO_LEFT) != 0) data.style |= SWT.MIRRORED; + } + } + this.drawable = drawable; + this.data = data; + handle = hDC; +} + +/** + * 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 (int)/*64*/handle; +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + int /*long*/ rgn = Gdip.Region_new(); + Gdip.Graphics_GetClip(data.gdipGraphics, rgn); + boolean isInfinite = Gdip.Region_IsInfinite(rgn, gdipGraphics); + Gdip.Region_delete(rgn); + return !isInfinite; + } + int /*long*/ region = OS.CreateRectRgn(0, 0, 0, 0); + int result = OS.GetClipRgn(handle, region); + OS.DeleteObject(region); + return result > 0; +} + +/** + * 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 == 0; +} + +float measureSpace(int /*long*/ font, int /*long*/ format) { + PointF pt = new PointF(); + RectF bounds = new RectF(); + Gdip.Graphics_MeasureString(data.gdipGraphics, new char[]{' '}, 1, font, pt, format, bounds); + return bounds.Width; +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (advanced && data.gdipGraphics != 0) return; + if (advanced) { + try { + initGdip(); + } catch (SWTException e) {} + } else { + disposeGdip(); + data.alpha = 0xFF; + data.backgroundPattern = data.foregroundPattern = null; + data.state = 0; + setClipping(0); + if ((data.style & SWT.MIRRORED) != 0) { + OS.SetLayout(handle, OS.GetLayout(handle) | OS.LAYOUT_RTL); + } + } +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (data.gdipGraphics == 0 && antialias == SWT.DEFAULT) return; + int mode = 0; + switch (antialias) { + case SWT.DEFAULT: + mode = Gdip.SmoothingModeDefault; + break; + case SWT.OFF: + mode = Gdip.SmoothingModeNone; + break; + case SWT.ON: + mode = Gdip.SmoothingModeAntiAlias; + break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + initGdip(); + Gdip.Graphics_SetSmoothingMode(data.gdipGraphics, mode); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (data.gdipGraphics == 0 && (alpha & 0xFF) == 0xFF) return; + initGdip(); + data.alpha = alpha & 0xFF; + data.state &= ~(BACKGROUND | FOREGROUND); +} + +/** + * 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 == 0) 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 (data.backgroundPattern == null && data.background == color.handle) return; + data.backgroundPattern = null; + data.background = color.handle; + data.state &= ~(BACKGROUND | BACKGROUND_TEXT); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (pattern != null && pattern.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (data.gdipGraphics == 0 && pattern == null) return; + initGdip(); + if (data.backgroundPattern == pattern) return; + data.backgroundPattern = pattern; + data.state &= ~BACKGROUND; +} + +void setClipping(int /*long*/ clipRgn) { + int /*long*/ hRgn = clipRgn; + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + if (hRgn != 0) { + int /*long*/ region = Gdip.Region_new(hRgn); + Gdip.Graphics_SetClip(gdipGraphics, region, Gdip.CombineModeReplace); + Gdip.Region_delete(region); + } else { + Gdip.Graphics_ResetClip(gdipGraphics); + } + } else { + POINT pt = null; + if (hRgn != 0 && !OS.IsWinCE) { + pt = new POINT(); + OS.GetWindowOrgEx(handle, pt); + OS.OffsetRgn(hRgn, -pt.x, -pt.y); + } + OS.SelectClipRgn(handle, hRgn); + if (hRgn != 0 && !OS.IsWinCE) { + OS.OffsetRgn(hRgn, pt.x, pt.y); + } + } + if (hRgn != 0 && hRgn != clipRgn) { + OS.DeleteObject(hRgn); + } +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + int /*long*/ hRgn = OS.CreateRectRgn(x, y, x + width, y + height); + setClipping(hRgn); + OS.DeleteObject(hRgn); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (path != null && path.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + setClipping(0); + if (path != null) { + initGdip(); + int mode = OS.GetPolyFillMode(handle) == OS.WINDING ? Gdip.FillModeWinding : Gdip.FillModeAlternate; + Gdip.GraphicsPath_SetFillMode(path.handle, mode); + Gdip.Graphics_SetClipPath(data.gdipGraphics, path.handle); + } +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (rect == null) { + setClipping(0); + } 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (region != null && region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + setClipping(region != null ? region.handle : 0); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (OS.IsWinCE) return; + int mode = OS.ALTERNATE; + switch (rule) { + case SWT.FILL_WINDING: mode = OS.WINDING; break; + case SWT.FILL_EVEN_ODD: mode = OS.ALTERNATE; break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + OS.SetPolyFillMode(handle, mode); +} + +/** + * 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 == 0) 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 == 0) 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 (data.foregroundPattern == null && color.handle == data.foreground) return; + data.foregroundPattern = null; + data.foreground = color.handle; + data.state &= ~(FOREGROUND | FOREGROUND_TEXT); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (pattern != null && pattern.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (data.gdipGraphics == 0 && pattern == null) return; + initGdip(); + if (data.foregroundPattern == pattern) return; + data.foregroundPattern = pattern; + data.state &= ~FOREGROUND; +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (data.gdipGraphics == 0 && interpolation == SWT.DEFAULT) return; + int mode = 0; + switch (interpolation) { + case SWT.DEFAULT: mode = Gdip.InterpolationModeDefault; break; + case SWT.NONE: mode = Gdip.InterpolationModeNearestNeighbor; break; + case SWT.LOW: mode = Gdip.InterpolationModeLowQuality; break; + case SWT.HIGH: mode = Gdip.InterpolationModeHighQuality; break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + initGdip(); + Gdip.Graphics_SetInterpolationMode(data.gdipGraphics, mode); +} + +/** + * 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 == 0) 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; + } + initGdip(); + 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 == 0) 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 == 0) 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 == 0) 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 == 0) 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 == 0) 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + OS.SetROP2(handle, xor ? OS.R2_XORPEN : OS.R2_COPYPEN); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (data.gdipGraphics == 0 && antialias == SWT.DEFAULT) return; + int textMode = 0; + switch (antialias) { + case SWT.DEFAULT: + textMode = Gdip.TextRenderingHintSystemDefault; + break; + case SWT.OFF: + textMode = Gdip.TextRenderingHintSingleBitPerPixelGridFit; + break; + case SWT.ON: + int[] type = new int[1]; + OS.SystemParametersInfo(OS.SPI_GETFONTSMOOTHINGTYPE, 0, type, 0); + if (type[0] == OS.FE_FONTSMOOTHINGCLEARTYPE) { + textMode = Gdip.TextRenderingHintClearTypeGridFit; + } else { + textMode = Gdip.TextRenderingHintAntiAliasGridFit; + } + break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + initGdip(); + Gdip.Graphics_SetTextRenderingHint(data.gdipGraphics, textMode); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (transform != null && transform.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + if (data.gdipGraphics == 0 && transform == null) return; + initGdip(); + int /*long*/ identity = identity(); + if (transform != null) { + Gdip.Matrix_Multiply(identity, transform.handle, Gdip.MatrixOrderPrepend); + } + Gdip.Graphics_SetTransform(data.gdipGraphics, identity); + Gdip.Matrix_delete(identity); + data.state &= ~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) { + if (handle == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + checkGC(FONT); + int length = string.length(); + int /*long*/ gdipGraphics = data.gdipGraphics; + if (gdipGraphics != 0) { + RectF bounds = new RectF(); + char[] buffer; + if (length != 0) { + buffer = new char [length]; + string.getChars(0, length, buffer, 0); + } else { + buffer = new char[]{' '}; + } + int nGlyphs = (length * 3 / 2) + 16; + GCP_RESULTS result = new GCP_RESULTS(); + result.lStructSize = GCP_RESULTS.sizeof; + result.nGlyphs = nGlyphs; + int /*long*/ hHeap = OS.GetProcessHeap(); + int /*long*/ lpDx = result.lpDx = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, nGlyphs * 4); + int /*long*/ lpGlyphs = result.lpGlyphs = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, nGlyphs * 2); + int dwFlags = OS.GCP_GLYPHSHAPE | OS.GCP_REORDER | OS.GCP_LIGATE; + int /*long*/ hdc = Gdip.Graphics_GetHDC(gdipGraphics); + int /*long*/ hFont = data.hGDIFont; + if (hFont == 0 && data.font != null) hFont = data.font.handle; + int /*long*/ oldFont = 0; + if (hFont != 0) oldFont = OS.SelectObject(hdc, hFont); + if ((data.style & SWT.MIRRORED) != 0) OS.SetLayout(hdc, OS.GetLayout(hdc) | OS.LAYOUT_RTL); + OS.GetCharacterPlacementW(hdc, buffer, length, 0, result, dwFlags); + if ((data.style & SWT.MIRRORED) != 0) OS.SetLayout(hdc, OS.GetLayout(hdc) & ~OS.LAYOUT_RTL); + if (hFont != 0) OS.SelectObject(hdc, oldFont); + Gdip.Graphics_ReleaseHDC(gdipGraphics, hdc); + int drawX = 0; + int[] dx = new int[result.nGlyphs]; + OS.MoveMemory(dx, lpDx, result.nGlyphs * 4); + float[] points = new float[dx.length * 2]; + for (int i = 0, j = 0; i < dx.length; i++, j += 2) { + points[j] = drawX; + drawX += dx[i]; + } + Gdip.Graphics_MeasureDriverString(gdipGraphics, lpGlyphs, result.nGlyphs, data.gdipFont, points, 0, 0, bounds); + OS.HeapFree(hHeap, 0, lpGlyphs); + OS.HeapFree(hHeap, 0, lpDx); + return new Point(length == 0 ? 0 : Math.round(bounds.Width), Math.round(bounds.Height)); + } + SIZE size = new SIZE(); + if (length == 0) { +// OS.GetTextExtentPoint32(handle, SPACE, SPACE.length(), size); + OS.GetTextExtentPoint32W(handle, new char[]{' '}, 1, size); + return new Point(0, size.cy); + } else { +// TCHAR buffer = new TCHAR (getCodePage(), string, false); + char[] buffer = new char [length]; + string.getChars(0, length, buffer, 0); + OS.GetTextExtentPoint32W(handle, buffer, length, size); + return new Point(size.cx, size.cy); + } +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + checkGC(FONT); + if (data.gdipGraphics != 0) { + PointF pt = new PointF(); + RectF bounds = new RectF(); + char[] buffer; + int length = string.length(); + if (length != 0) { + buffer = new char [length]; + string.getChars(0, length, buffer, 0); + } else { + buffer = new char[]{' '}; + } + int /*long*/ format = Gdip.StringFormat_Clone(Gdip.StringFormat_GenericTypographic()); + int formatFlags = Gdip.StringFormat_GetFormatFlags(format) | Gdip.StringFormatFlagsMeasureTrailingSpaces; + if ((data.style & SWT.MIRRORED) != 0) formatFlags |= Gdip.StringFormatFlagsDirectionRightToLeft; + Gdip.StringFormat_SetFormatFlags(format, formatFlags); + float[] tabs = (flags & SWT.DRAW_TAB) != 0 ? new float[]{measureSpace(data.gdipFont, format) * 8} : new float[1]; + Gdip.StringFormat_SetTabStops(format, 0, tabs.length, tabs); + Gdip.StringFormat_SetHotkeyPrefix(format, (flags & SWT.DRAW_MNEMONIC) != 0 ? Gdip.HotkeyPrefixShow : Gdip.HotkeyPrefixNone); + Gdip.Graphics_MeasureString(data.gdipGraphics, buffer, buffer.length, data.gdipFont, pt, format, bounds); + Gdip.StringFormat_delete(format); + return new Point(length == 0 ? 0 : Math.round(bounds.Width), Math.round(bounds.Height)); + } + if (string.length () == 0) { + SIZE size = new SIZE(); +// OS.GetTextExtentPoint32(handle, SPACE, SPACE.length(), size); + OS.GetTextExtentPoint32W(handle, new char [] {' '}, 1, size); + return new Point(0, size.cy); + } + RECT rect = new RECT(); + TCHAR buffer = new TCHAR(getCodePage(), string, false); + int uFormat = OS.DT_LEFT | OS.DT_CALCRECT; + if ((flags & SWT.DRAW_DELIMITER) == 0) uFormat |= OS.DT_SINGLELINE; + if ((flags & SWT.DRAW_TAB) != 0) uFormat |= OS.DT_EXPANDTABS; + if ((flags & SWT.DRAW_MNEMONIC) == 0) uFormat |= OS.DT_NOPREFIX; + OS.DrawText(handle, buffer, buffer.length(), rect, uFormat); + return new Point(rect.right, rect.bottom); +} + +/** + * 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 + "}"; +} + +/** + * 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> + */ +public static GC win32_new(Drawable drawable, GCData data) { + GC gc = new GC(); + int /*long*/ hDC = drawable.internal_new_GC(data); + gc.device = data.device; + gc.init(drawable, data, hDC); + return gc; +} + +/** + * Invokes platform specific functionality to wrap a 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 hDC the Windows HDC. + * @param data the data for the receiver. + * + * @return a new <code>GC</code> + */ +public static GC win32_new(int /*long*/ hDC, GCData data) { + GC gc = new GC(); + gc.device = data.device; + data.style |= SWT.LEFT_TO_RIGHT; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) { + int flags = OS.GetLayout (hDC); + if ((flags & OS.LAYOUT_RTL) != 0) { + data.style |= SWT.RIGHT_TO_LEFT | SWT.MIRRORED; + } + } + gc.init(null, data, hDC); + return gc; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GCData.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GCData.java new file mode 100755 index 0000000000..8ccb803401 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GCData.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * 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.win32.*; + +/** + * 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 int foreground = -1; + public int background = -1; + public Font font; + public Pattern foregroundPattern; + public Pattern backgroundPattern; + public int lineStyle = SWT.LINE_SOLID; + public float lineWidth; + public int lineCap = SWT.CAP_FLAT; + public int lineJoin = SWT.JOIN_MITER; + public float lineDashesOffset; + public float[] lineDashes; + public float lineMiterLimit = 10; + public int alpha = 0xFF; + + public Image image; + public int /*long*/ hPen, hOldPen; + public int /*long*/ hBrush, hOldBrush; + public int /*long*/ hNullBitmap; + public int /*long*/ hwnd; + public PAINTSTRUCT ps; + public int layout = -1; + public int /*long*/ gdipGraphics; + public int /*long*/ gdipPen; + public int /*long*/ gdipBrush; + public int /*long*/ gdipFgBrush; + public int /*long*/ gdipBgBrush; + public int /*long*/ gdipFont; + public int /*long*/ hGDIFont; + public float gdipXOffset, gdipYOffset; + public int uiState = 0; + public boolean focusDrawn; +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java new file mode 100755 index 0000000000..012f72e537 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java @@ -0,0 +1,2129 @@ +/******************************************************************************* + * 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.gdip.*; +import org.eclipse.swt.internal.win32.*; +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 int /*long*/ handle; + + /** + * specifies the transparent pixel + */ + int transparentPixel = -1, transparentColor = -1; + + /** + * the GC which is drawing on the image + */ + GC memGC; + + /** + * the alpha data for the image + */ + byte[] alphaData; + + /** + * the global alpha value to be used for every pixel + */ + int alpha = -1; + + /** + * the image data used to create this image if it is a + * icon. Used only in WinCE + */ + ImageData data; + + /** + * width of the image + */ + int width = -1; + + /** + * height of the image + */ + int height = -1; + + /** + * specifies the default scanline padding + */ + static final int DEFAULT_SCANLINE_PAD = 4; + +/** + * Prevents uninitialized instances from being created outside the package. + */ +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); + init(width, height); + init(); +} + +/** + * 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); + device = this.device; + if (srcImage == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (srcImage.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + Rectangle rect = srcImage.getBounds(); + this.type = srcImage.type; + switch (flag) { + case SWT.IMAGE_COPY: { + switch (type) { + case SWT.BITMAP: + /* Get the HDC for the device */ + int /*long*/ hDC = device.internal_new_GC(null); + + /* Copy the bitmap */ + int /*long*/ hdcSource = OS.CreateCompatibleDC(hDC); + int /*long*/ hdcDest = OS.CreateCompatibleDC(hDC); + int /*long*/ hOldSrc = OS.SelectObject(hdcSource, srcImage.handle); + BITMAP bm = new BITMAP(); + OS.GetObject(srcImage.handle, BITMAP.sizeof, bm); + handle = OS.CreateCompatibleBitmap(hdcSource, rect.width, bm.bmBits != 0 ? -rect.height : rect.height); + if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ hOldDest = OS.SelectObject(hdcDest, handle); + OS.BitBlt(hdcDest, 0, 0, rect.width, rect.height, hdcSource, 0, 0, OS.SRCCOPY); + OS.SelectObject(hdcSource, hOldSrc); + OS.SelectObject(hdcDest, hOldDest); + OS.DeleteDC(hdcSource); + OS.DeleteDC(hdcDest); + + /* Release the HDC for the device */ + device.internal_dispose_GC(hDC, null); + + 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); + } + break; + case SWT.ICON: + if (OS.IsWinCE) { + init(srcImage.data); + } else { + handle = OS.CopyImage(srcImage.handle, OS.IMAGE_ICON, rect.width, rect.height, 0); + if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); + } + break; + default: + SWT.error(SWT.ERROR_INVALID_IMAGE); + } + break; + } + case SWT.IMAGE_DISABLE: { + ImageData data = srcImage.getImageData(); + PaletteData palette = data.palette; + RGB[] rgbs = new RGB[3]; + rgbs[0] = device.getSystemColor(SWT.COLOR_BLACK).getRGB(); + rgbs[1] = device.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW).getRGB(); + rgbs[2] = device.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND).getRGB(); + ImageData newData = new ImageData(rect.width, rect.height, 8, new PaletteData(rgbs)); + newData.alpha = data.alpha; + newData.alphaData = data.alphaData; + newData.maskData = data.maskData; + newData.maskPad = data.maskPad; + if (data.transparentPixel != -1) newData.transparentPixel = 0; + + /* Convert the pixels. */ + int[] scanline = new int[rect.width]; + int[] maskScanline = null; + ImageData mask = null; + if (data.maskData != null) mask = data.getTransparencyMask(); + if (mask != null) maskScanline = new int[rect.width]; + int redMask = palette.redMask; + int greenMask = palette.greenMask; + int blueMask = palette.blueMask; + int redShift = palette.redShift; + int greenShift = palette.greenShift; + int blueShift = palette.blueShift; + for (int y=0; y<rect.height; y++) { + int offset = y * newData.bytesPerLine; + data.getPixels(0, y, rect.width, scanline, 0); + if (mask != null) mask.getPixels(0, y, rect.width, maskScanline, 0); + for (int x=0; x<rect.width; x++) { + int pixel = scanline[x]; + if (!((data.transparentPixel != -1 && pixel == data.transparentPixel) || (mask != null && maskScanline[x] == 0))) { + int red, green, blue; + if (palette.isDirect) { + red = pixel & redMask; + red = (redShift < 0) ? red >>> -redShift : red << redShift; + green = pixel & greenMask; + green = (greenShift < 0) ? green >>> -greenShift : green << greenShift; + blue = pixel & blueMask; + blue = (blueShift < 0) ? blue >>> -blueShift : blue << blueShift; + } else { + red = palette.colors[pixel].red; + green = palette.colors[pixel].green; + blue = palette.colors[pixel].blue; + } + int intensity = red * red + green * green + blue * blue; + if (intensity < 98304) { + newData.data[offset] = (byte)1; + } else { + newData.data[offset] = (byte)2; + } + } + offset++; + } + } + init (newData); + break; + } + case SWT.IMAGE_GRAY: { + ImageData data = srcImage.getImageData(); + PaletteData palette = data.palette; + ImageData newData = data; + if (!palette.isDirect) { + /* Convert the palette entries to gray. */ + RGB [] rgbs = palette.getRGBs(); + for (int i=0; i<rgbs.length; i++) { + if (data.transparentPixel != i) { + RGB color = rgbs [i]; + int red = color.red; + int green = color.green; + int blue = color.blue; + int intensity = (red+red+green+green+green+green+green+blue) >> 3; + color.red = color.green = color.blue = intensity; + } + } + newData.palette = new PaletteData(rgbs); + } else { + /* Create a 8 bit depth image data with a gray palette. */ + RGB[] rgbs = new RGB[256]; + for (int i=0; i<rgbs.length; i++) { + rgbs[i] = new RGB(i, i, i); + } + newData = new ImageData(rect.width, rect.height, 8, new PaletteData(rgbs)); + newData.alpha = data.alpha; + newData.alphaData = data.alphaData; + newData.maskData = data.maskData; + newData.maskPad = data.maskPad; + if (data.transparentPixel != -1) newData.transparentPixel = 254; + + /* Convert the pixels. */ + int[] scanline = new int[rect.width]; + int redMask = palette.redMask; + int greenMask = palette.greenMask; + int blueMask = palette.blueMask; + int redShift = palette.redShift; + int greenShift = palette.greenShift; + int blueShift = palette.blueShift; + for (int y=0; y<rect.height; y++) { + int offset = y * newData.bytesPerLine; + data.getPixels(0, y, rect.width, scanline, 0); + for (int x=0; x<rect.width; x++) { + int pixel = scanline[x]; + if (pixel != data.transparentPixel) { + int red = pixel & redMask; + red = (redShift < 0) ? red >>> -redShift : red << redShift; + int green = pixel & greenMask; + green = (greenShift < 0) ? green >>> -greenShift : green << greenShift; + int blue = pixel & blueMask; + blue = (blueShift < 0) ? blue >>> -blueShift : blue << blueShift; + int intensity = (red+red+green+green+green+green+green+blue) >> 3; + if (newData.transparentPixel == intensity) intensity = 255; + newData.data[offset] = (byte)intensity; + } else { + newData.data[offset] = (byte)254; + } + offset++; + } + } + } + init (newData); + break; + } + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + init(); +} + +/** + * 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); + init(bounds.width, bounds.height); + init(); +} + +/** + * 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); + init(data); + init(); +} + +/** + * 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); + } + mask = ImageData.convertMask(mask); + init(this.device, this, source, mask); + init(); +} + +/** + * 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); + init(new ImageData(stream)); + init(); +} + +/** + * 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); + if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + initNative(filename); + if (this.handle == 0) init(new ImageData(filename)); + init(); +} + +void initNative(String filename) { + boolean gdip = true; + try { + device.checkGDIP(); + } catch (SWTException e) { + gdip = false; + } + /* + * Bug in GDI+. For some reason, Bitmap.LockBits() segment faults + * when loading GIF files in 64-bit Windows. The fix is to not use + * GDI+ image loading in this case. + */ + if (gdip && OS.PTR_SIZEOF == 8 && filename.toLowerCase().endsWith(".gif")) gdip = false; + if (gdip) { + int length = filename.length(); + char[] chars = new char[length+1]; + filename.getChars(0, length, chars, 0); + int /*long*/ bitmap = Gdip.Bitmap_new(chars, false); + if (bitmap != 0) { + int error = SWT.ERROR_NO_HANDLES; + int status = Gdip.Image_GetLastStatus(bitmap); + if (status == 0) { + if (filename.toLowerCase().endsWith(".ico")) { + this.type = SWT.ICON; + int /*long*/[] hicon = new int /*long*/[1]; + status = Gdip.Bitmap_GetHICON(bitmap, hicon); + this.handle = hicon[0]; + } else { + this.type = SWT.BITMAP; + int width = Gdip.Image_GetWidth(bitmap); + int height = Gdip.Image_GetHeight(bitmap); + int pixelFormat = Gdip.Image_GetPixelFormat(bitmap); + switch (pixelFormat) { + case Gdip.PixelFormat16bppRGB555: + case Gdip.PixelFormat16bppRGB565: + this.handle = createDIB(width, height, 16); + break; + case Gdip.PixelFormat24bppRGB: + this.handle = createDIB(width, height, 24); + break; + case Gdip.PixelFormat32bppRGB: + // These will loose either precision or transparency + case Gdip.PixelFormat16bppGrayScale: + case Gdip.PixelFormat48bppRGB: + case Gdip.PixelFormat32bppPARGB: + case Gdip.PixelFormat64bppARGB: + case Gdip.PixelFormat64bppPARGB: + this.handle = createDIB(width, height, 32); + break; + } + if (this.handle != 0) { + /* + * This performs better than getting the bits with Bitmap.LockBits(), + * but it cannot be used when there is transparency. + */ + int /*long*/ hDC = device.internal_new_GC(null); + int /*long*/ srcHDC = OS.CreateCompatibleDC(hDC); + int /*long*/ oldSrcBitmap = OS.SelectObject(srcHDC, this.handle); + int /*long*/ graphics = Gdip.Graphics_new(srcHDC); + if (graphics != 0) { + Rect rect = new Rect(); + rect.Width = width; + rect.Height = height; + status = Gdip.Graphics_DrawImage(graphics, bitmap, rect, 0, 0, width, height, Gdip.UnitPixel, 0, 0, 0); + if (status != 0) { + error = SWT.ERROR_INVALID_IMAGE; + OS.DeleteObject(handle); + this.handle = 0; + } + Gdip.Graphics_delete(graphics); + } + OS.SelectObject(srcHDC, oldSrcBitmap); + OS.DeleteDC(srcHDC); + device.internal_dispose_GC(hDC, null); + } else { + int /*long*/ lockedBitmapData = Gdip.BitmapData_new(); + if (lockedBitmapData != 0) { + status = Gdip.Bitmap_LockBits(bitmap, 0, 0, pixelFormat, lockedBitmapData); + if (status == 0) { + BitmapData bitmapData = new BitmapData(); + Gdip.MoveMemory(bitmapData, lockedBitmapData); + int stride = bitmapData.Stride; + int /*long*/ pixels = bitmapData.Scan0; + int depth = 0, scanlinePad = 4, transparentPixel = -1; + switch (bitmapData.PixelFormat) { + case Gdip.PixelFormat1bppIndexed: depth = 1; break; + case Gdip.PixelFormat4bppIndexed: depth = 4; break; + case Gdip.PixelFormat8bppIndexed: depth = 8; break; + case Gdip.PixelFormat16bppARGB1555: + case Gdip.PixelFormat16bppRGB555: + case Gdip.PixelFormat16bppRGB565: depth = 16; break; + case Gdip.PixelFormat24bppRGB: depth = 24; break; + case Gdip.PixelFormat32bppRGB: + case Gdip.PixelFormat32bppARGB: depth = 32; break; + } + if (depth != 0) { + PaletteData paletteData = null; + switch (bitmapData.PixelFormat) { + case Gdip.PixelFormat1bppIndexed: + case Gdip.PixelFormat4bppIndexed: + case Gdip.PixelFormat8bppIndexed: + int paletteSize = Gdip.Image_GetPaletteSize(bitmap); + int /*long*/ hHeap = OS.GetProcessHeap(); + int /*long*/ palette = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, paletteSize); + if (palette == 0) SWT.error(SWT.ERROR_NO_HANDLES); + Gdip.Image_GetPalette(bitmap, palette, paletteSize); + ColorPalette colorPalette = new ColorPalette(); + Gdip.MoveMemory(colorPalette, palette, ColorPalette.sizeof); + int[] entries = new int[colorPalette.Count]; + OS.MoveMemory(entries, palette + 8, entries.length * 4); + OS.HeapFree(hHeap, 0, palette); + RGB[] rgbs = new RGB[colorPalette.Count]; + paletteData = new PaletteData(rgbs); + for (int i = 0; i < entries.length; i++) { + if (((entries[i] >> 24) & 0xFF) == 0 && (colorPalette.Flags & Gdip.PaletteFlagsHasAlpha) != 0) { + transparentPixel = i; + } + rgbs[i] = new RGB(((entries[i] & 0xFF0000) >> 16), ((entries[i] & 0xFF00) >> 8), ((entries[i] & 0xFF) >> 0)); + } + break; + case Gdip.PixelFormat16bppARGB1555: + case Gdip.PixelFormat16bppRGB555: paletteData = new PaletteData(0x7C00, 0x3E0, 0x1F); break; + case Gdip.PixelFormat16bppRGB565: paletteData = new PaletteData(0xF800, 0x7E0, 0x1F); break; + case Gdip.PixelFormat24bppRGB: paletteData = new PaletteData(0xFF, 0xFF00, 0xFF0000); break; + case Gdip.PixelFormat32bppRGB: + case Gdip.PixelFormat32bppARGB: paletteData = new PaletteData(0xFF00, 0xFF0000, 0xFF000000); break; + } + byte[] data = new byte[stride * height], alphaData = null; + OS.MoveMemory(data, pixels, data.length); + switch (bitmapData.PixelFormat) { + case Gdip.PixelFormat16bppARGB1555: + alphaData = new byte[width * height]; + for (int i = 1, j = 0; i < data.length; i += 2, j++) { + alphaData[j] = (byte)((data[i] & 0x80) != 0 ? 255 : 0); + } + break; + case Gdip.PixelFormat32bppARGB: + alphaData = new byte[width * height]; + for (int i = 3, j = 0; i < data.length; i += 4, j++) { + alphaData[j] = data[i]; + } + break; + } + ImageData img = new ImageData(width, height, depth, paletteData, scanlinePad, data); + img.transparentPixel = transparentPixel; + img.alphaData = alphaData; + init(img); + } + Gdip.Bitmap_UnlockBits(bitmap, lockedBitmapData); + } else { + error = SWT.ERROR_INVALID_IMAGE; + } + Gdip.BitmapData_delete(lockedBitmapData); + } + } + } + } + Gdip.Bitmap_delete(bitmap); + if (status == 0) { + if (this.handle == 0) SWT.error(error); + } + } + } +} + +/** + * Create a DIB from a DDB without using GetDIBits. Note that + * the DDB should not be selected into a HDC. + */ +int /*long*/ createDIBFromDDB(int /*long*/ hDC, int /*long*/ hBitmap, int width, int height) { + + /* Determine the DDB depth */ + int bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL); + int planes = OS.GetDeviceCaps (hDC, OS.PLANES); + int depth = bits * planes; + + /* Determine the DIB palette */ + boolean isDirect = depth > 8; + RGB[] rgbs = null; + if (!isDirect) { + int numColors = 1 << depth; + byte[] logPalette = new byte[4 * numColors]; + OS.GetPaletteEntries(device.hPalette, 0, numColors, logPalette); + rgbs = new RGB[numColors]; + for (int i = 0; i < numColors; i++) { + rgbs[i] = new RGB(logPalette[i] & 0xFF, logPalette[i + 1] & 0xFF, logPalette[i + 2] & 0xFF); + } + } + + boolean useBitfields = OS.IsWinCE && (depth == 16 || depth == 32); + BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER(); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biWidth = width; + bmiHeader.biHeight = -height; + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = (short)depth; + if (useBitfields) bmiHeader.biCompression = OS.BI_BITFIELDS; + else bmiHeader.biCompression = OS.BI_RGB; + byte[] bmi; + if (isDirect) bmi = new byte[BITMAPINFOHEADER.sizeof + (useBitfields ? 12 : 0)]; + else bmi = new byte[BITMAPINFOHEADER.sizeof + rgbs.length * 4]; + OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + + /* Set the rgb colors into the bitmap info */ + int offset = BITMAPINFOHEADER.sizeof; + if (isDirect) { + if (useBitfields) { + int redMask = 0; + int greenMask = 0; + int blueMask = 0; + switch (depth) { + case 16: + redMask = 0x7C00; + greenMask = 0x3E0; + blueMask = 0x1F; + /* little endian */ + bmi[offset] = (byte)((redMask & 0xFF) >> 0); + bmi[offset + 1] = (byte)((redMask & 0xFF00) >> 8); + bmi[offset + 2] = (byte)((redMask & 0xFF0000) >> 16); + bmi[offset + 3] = (byte)((redMask & 0xFF000000) >> 24); + bmi[offset + 4] = (byte)((greenMask & 0xFF) >> 0); + bmi[offset + 5] = (byte)((greenMask & 0xFF00) >> 8); + bmi[offset + 6] = (byte)((greenMask & 0xFF0000) >> 16); + bmi[offset + 7] = (byte)((greenMask & 0xFF000000) >> 24); + bmi[offset + 8] = (byte)((blueMask & 0xFF) >> 0); + bmi[offset + 9] = (byte)((blueMask & 0xFF00) >> 8); + bmi[offset + 10] = (byte)((blueMask & 0xFF0000) >> 16); + bmi[offset + 11] = (byte)((blueMask & 0xFF000000) >> 24); + break; + case 32: + redMask = 0xFF00; + greenMask = 0xFF0000; + blueMask = 0xFF000000; + /* big endian */ + bmi[offset] = (byte)((redMask & 0xFF000000) >> 24); + bmi[offset + 1] = (byte)((redMask & 0xFF0000) >> 16); + bmi[offset + 2] = (byte)((redMask & 0xFF00) >> 8); + bmi[offset + 3] = (byte)((redMask & 0xFF) >> 0); + bmi[offset + 4] = (byte)((greenMask & 0xFF000000) >> 24); + bmi[offset + 5] = (byte)((greenMask & 0xFF0000) >> 16); + bmi[offset + 6] = (byte)((greenMask & 0xFF00) >> 8); + bmi[offset + 7] = (byte)((greenMask & 0xFF) >> 0); + bmi[offset + 8] = (byte)((blueMask & 0xFF000000) >> 24); + bmi[offset + 9] = (byte)((blueMask & 0xFF0000) >> 16); + bmi[offset + 10] = (byte)((blueMask & 0xFF00) >> 8); + bmi[offset + 11] = (byte)((blueMask & 0xFF) >> 0); + break; + default: + SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); + } + } + } else { + for (int j = 0; j < rgbs.length; j++) { + bmi[offset] = (byte)rgbs[j].blue; + bmi[offset + 1] = (byte)rgbs[j].green; + bmi[offset + 2] = (byte)rgbs[j].red; + bmi[offset + 3] = 0; + offset += 4; + } + } + int /*long*/[] pBits = new int /*long*/[1]; + int /*long*/ hDib = OS.CreateDIBSection(0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0); + if (hDib == 0) SWT.error(SWT.ERROR_NO_HANDLES); + + /* Bitblt DDB into DIB */ + int /*long*/ hdcSource = OS.CreateCompatibleDC(hDC); + int /*long*/ hdcDest = OS.CreateCompatibleDC(hDC); + int /*long*/ hOldSrc = OS.SelectObject(hdcSource, hBitmap); + int /*long*/ hOldDest = OS.SelectObject(hdcDest, hDib); + OS.BitBlt(hdcDest, 0, 0, width, height, hdcSource, 0, 0, OS.SRCCOPY); + OS.SelectObject(hdcSource, hOldSrc); + OS.SelectObject(hdcDest, hOldDest); + OS.DeleteDC(hdcSource); + OS.DeleteDC(hdcDest); + + return hDib; +} + +int /*long*/ [] createGdipImage() { + switch (type) { + case SWT.BITMAP: { + if (alpha != -1 || alphaData != null || transparentPixel != -1) { + BITMAP bm = new BITMAP(); + OS.GetObject(handle, BITMAP.sizeof, bm); + int imgWidth = bm.bmWidth; + int imgHeight = bm.bmHeight; + int /*long*/ hDC = device.internal_new_GC(null); + int /*long*/ srcHdc = OS.CreateCompatibleDC(hDC); + int /*long*/ oldSrcBitmap = OS.SelectObject(srcHdc, handle); + int /*long*/ memHdc = OS.CreateCompatibleDC(hDC); + int /*long*/ memDib = createDIB(imgWidth, imgHeight, 32); + if (memDib == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ oldMemBitmap = OS.SelectObject(memHdc, memDib); + BITMAP dibBM = new BITMAP(); + OS.GetObject(memDib, BITMAP.sizeof, dibBM); + int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight; + OS.BitBlt(memHdc, 0, 0, imgWidth, imgHeight, srcHdc, 0, 0, OS.SRCCOPY); + byte red = 0, green = 0, blue = 0; + if (transparentPixel != -1) { + if (bm.bmBitsPixel <= 8) { + byte[] color = new byte[4]; + OS.GetDIBColorTable(srcHdc, transparentPixel, 1, color); + blue = color[0]; + green = color[1]; + red = color[2]; + } else { + switch (bm.bmBitsPixel) { + case 16: + int blueMask = 0x1F; + int blueShift = ImageData.getChannelShift(blueMask); + byte[] blues = ImageData.ANY_TO_EIGHT[ImageData.getChannelWidth(blueMask, blueShift)]; + blue = blues[(transparentPixel & blueMask) >> blueShift]; + int greenMask = 0x3E0; + int greenShift = ImageData.getChannelShift(greenMask); + byte[] greens = ImageData.ANY_TO_EIGHT[ImageData.getChannelWidth(greenMask, greenShift)]; + green = greens[(transparentPixel & greenMask) >> greenShift]; + int redMask = 0x7C00; + int redShift = ImageData.getChannelShift(redMask); + byte[] reds = ImageData.ANY_TO_EIGHT[ImageData.getChannelWidth(redMask, redShift)]; + red = reds[(transparentPixel & redMask) >> redShift]; + break; + case 24: + blue = (byte)((transparentPixel & 0xFF0000) >> 16); + green = (byte)((transparentPixel & 0xFF00) >> 8); + red = (byte)(transparentPixel & 0xFF); + break; + case 32: + blue = (byte)((transparentPixel & 0xFF000000) >>> 24); + green = (byte)((transparentPixel & 0xFF0000) >> 16); + red = (byte)((transparentPixel & 0xFF00) >> 8); + break; + } + } + } + OS.SelectObject(srcHdc, oldSrcBitmap); + OS.SelectObject(memHdc, oldMemBitmap); + OS.DeleteObject(srcHdc); + OS.DeleteObject(memHdc); + byte[] srcData = new byte[sizeInBytes]; + OS.MoveMemory(srcData, dibBM.bmBits, sizeInBytes); + OS.DeleteObject(memDib); + device.internal_dispose_GC(hDC, null); + if (alpha != -1) { + for (int y = 0, dp = 0; y < imgHeight; ++y) { + for (int x = 0; x < imgWidth; ++x) { + srcData[dp + 3] = (byte)alpha; + dp += 4; + } + } + } else if (alphaData != null) { + for (int y = 0, dp = 0, ap = 0; y < imgHeight; ++y) { + for (int x = 0; x < imgWidth; ++x) { + srcData[dp + 3] = alphaData[ap++]; + dp += 4; + } + } + } else if (transparentPixel != -1) { + for (int y = 0, dp = 0; y < imgHeight; ++y) { + for (int x = 0; x < imgWidth; ++x) { + if (srcData[dp] == blue && srcData[dp + 1] == green && srcData[dp + 2] == red) { + srcData[dp + 3] = (byte)0; + } else { + srcData[dp + 3] = (byte)0xFF; + } + dp += 4; + } + } + } + int /*long*/ hHeap = OS.GetProcessHeap(); + int /*long*/ pixels = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, srcData.length); + if (pixels == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.MoveMemory(pixels, srcData, sizeInBytes); + return new int /*long*/ []{Gdip.Bitmap_new(imgWidth, imgHeight, dibBM.bmWidthBytes, Gdip.PixelFormat32bppARGB, pixels), pixels}; + } + return new int /*long*/ []{Gdip.Bitmap_new(handle, 0), 0}; + } + case SWT.ICON: { + /* + * Bug in GDI+. Creating a new GDI+ Bitmap from a HICON segment faults + * when the icon width is bigger than the icon height. The fix is to + * detect this and create a PixelFormat32bppARGB image instead. + */ + ICONINFO iconInfo = new ICONINFO(); + if (OS.IsWinCE) { + GetIconInfo(this, iconInfo); + } else { + OS.GetIconInfo(handle, iconInfo); + } + int /*long*/ hBitmap = iconInfo.hbmColor; + if (hBitmap == 0) hBitmap = iconInfo.hbmMask; + BITMAP bm = new BITMAP(); + OS.GetObject(hBitmap, BITMAP.sizeof, bm); + int imgWidth = bm.bmWidth; + int imgHeight = hBitmap == iconInfo.hbmMask ? bm.bmHeight / 2 : bm.bmHeight; + int /*long*/ img = 0, pixels = 0; + /* + * Bug in GDI+. Bitmap_new() segments fault if the image width + * is greater than the image height. + * + * Note that it also fails to generated an appropriate alpha + * channel when the icon depth is 32. + */ + if (imgWidth > imgHeight || bm.bmBitsPixel == 32) { + int /*long*/ hDC = device.internal_new_GC(null); + int /*long*/ srcHdc = OS.CreateCompatibleDC(hDC); + int /*long*/ oldSrcBitmap = OS.SelectObject(srcHdc, hBitmap); + int /*long*/ memHdc = OS.CreateCompatibleDC(hDC); + int /*long*/ memDib = createDIB(imgWidth, imgHeight, 32); + if (memDib == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ oldMemBitmap = OS.SelectObject(memHdc, memDib); + BITMAP dibBM = new BITMAP(); + OS.GetObject(memDib, BITMAP.sizeof, dibBM); + OS.BitBlt(memHdc, 0, 0, imgWidth, imgHeight, srcHdc, 0, hBitmap == iconInfo.hbmMask ? imgHeight : 0, OS.SRCCOPY); + OS.SelectObject(memHdc, oldMemBitmap); + OS.DeleteObject(memHdc); + byte[] srcData = new byte[dibBM.bmWidthBytes * dibBM.bmHeight]; + OS.MoveMemory(srcData, dibBM.bmBits, srcData.length); + OS.DeleteObject(memDib); + OS.SelectObject(srcHdc, iconInfo.hbmMask); + for (int y = 0, dp = 3; y < imgHeight; ++y) { + for (int x = 0; x < imgWidth; ++x) { + if (srcData[dp] == 0) { + if (OS.GetPixel(srcHdc, x, y) != 0) { + srcData[dp] = (byte)0; + } else { + srcData[dp] = (byte)0xFF; + } + } + dp += 4; + } + } + OS.SelectObject(srcHdc, oldSrcBitmap); + OS.DeleteObject(srcHdc); + device.internal_dispose_GC(hDC, null); + int /*long*/ hHeap = OS.GetProcessHeap(); + pixels = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, srcData.length); + if (pixels == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.MoveMemory(pixels, srcData, srcData.length); + img = Gdip.Bitmap_new(imgWidth, imgHeight, dibBM.bmWidthBytes, Gdip.PixelFormat32bppARGB, pixels); + } else { + img = Gdip.Bitmap_new(handle); + } + if (iconInfo.hbmColor != 0) OS.DeleteObject(iconInfo.hbmColor); + if (iconInfo.hbmMask != 0) OS.DeleteObject(iconInfo.hbmMask); + return new int /*long*/ []{img, pixels}; + } + default: SWT.error(SWT.ERROR_INVALID_IMAGE); + } + return null; +} + +void destroy () { + if (memGC != null) memGC.dispose(); + if (type == SWT.ICON) { + if (OS.IsWinCE) data = null; + OS.DestroyIcon (handle); + } else { + OS.DeleteObject (handle); + } + handle = 0; + 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; +} + +/** + * 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; + + /* Get the HDC for the device */ + int /*long*/ hDC = device.internal_new_GC(null); + + /* Compute the background color */ + BITMAP bm = new BITMAP(); + OS.GetObject(handle, BITMAP.sizeof, bm); + int /*long*/ hdcMem = OS.CreateCompatibleDC(hDC); + int /*long*/ hOldObject = OS.SelectObject(hdcMem, handle); + int red = 0, green = 0, blue = 0; + if (bm.bmBitsPixel <= 8) { + if (OS.IsWinCE) { + byte[] pBits = new byte[1]; + OS.MoveMemory(pBits, bm.bmBits, 1); + byte oldValue = pBits[0]; + int mask = (0xFF << (8 - bm.bmBitsPixel)) & 0x00FF; + pBits[0] = (byte)((transparentPixel << (8 - bm.bmBitsPixel)) | (pBits[0] & ~mask)); + OS.MoveMemory(bm.bmBits, pBits, 1); + int color = OS.GetPixel(hdcMem, 0, 0); + pBits[0] = oldValue; + OS.MoveMemory(bm.bmBits, pBits, 1); + blue = (color & 0xFF0000) >> 16; + green = (color & 0xFF00) >> 8; + red = color & 0xFF; + } else { + byte[] color = new byte[4]; + OS.GetDIBColorTable(hdcMem, transparentPixel, 1, color); + blue = color[0] & 0xFF; + green = color[1] & 0xFF; + red = color[2] & 0xFF; + } + } else { + switch (bm.bmBitsPixel) { + case 16: + blue = (transparentPixel & 0x1F) << 3; + green = (transparentPixel & 0x3E0) >> 2; + red = (transparentPixel & 0x7C00) >> 7; + break; + case 24: + blue = (transparentPixel & 0xFF0000) >> 16; + green = (transparentPixel & 0xFF00) >> 8; + red = transparentPixel & 0xFF; + break; + case 32: + blue = (transparentPixel & 0xFF000000) >>> 24; + green = (transparentPixel & 0xFF0000) >> 16; + red = (transparentPixel & 0xFF00) >> 8; + break; + default: + return null; + } + } + OS.SelectObject(hdcMem, hOldObject); + OS.DeleteDC(hdcMem); + + /* Release the HDC for the device */ + device.internal_dispose_GC(hDC, null); + return Color.win32_new(device, (blue << 16) | (green << 8) | red); +} + +/** + * 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); + if (width != -1 && height != -1) { + return new Rectangle(0, 0, width, height); + } + switch (type) { + case SWT.BITMAP: + BITMAP bm = new BITMAP(); + OS.GetObject(handle, BITMAP.sizeof, bm); + return new Rectangle(0, 0, width = bm.bmWidth, height = bm.bmHeight); + case SWT.ICON: + if (OS.IsWinCE) { + return new Rectangle(0, 0, width = data.width, height = data.height); + } else { + ICONINFO info = new ICONINFO(); + OS.GetIconInfo(handle, info); + int /*long*/ hBitmap = info.hbmColor; + if (hBitmap == 0) hBitmap = info.hbmMask; + bm = new BITMAP(); + OS.GetObject(hBitmap, BITMAP.sizeof, bm); + if (hBitmap == info.hbmMask) bm.bmHeight /= 2; + if (info.hbmColor != 0) OS.DeleteObject(info.hbmColor); + if (info.hbmMask != 0) OS.DeleteObject(info.hbmMask); + return new Rectangle(0, 0, width = bm.bmWidth, height = bm.bmHeight); + } + default: + SWT.error(SWT.ERROR_INVALID_IMAGE); + return null; + } +} + +/** + * 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); + BITMAP bm; + int depth, width, height; + switch (type) { + case SWT.ICON: { + if (OS.IsWinCE) return data; + ICONINFO info = new ICONINFO(); + if (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + OS.GetIconInfo(handle, info); + /* Get the basic BITMAP information */ + int /*long*/ hBitmap = info.hbmColor; + if (hBitmap == 0) hBitmap = info.hbmMask; + bm = new BITMAP(); + OS.GetObject(hBitmap, BITMAP.sizeof, bm); + depth = bm.bmPlanes * bm.bmBitsPixel; + width = bm.bmWidth; + if (hBitmap == info.hbmMask) bm.bmHeight /= 2; + height = bm.bmHeight; + int numColors = 0; + if (depth <= 8) numColors = 1 << depth; + /* Create the BITMAPINFO */ + BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER(); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biWidth = width; + bmiHeader.biHeight = -height; + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = (short)depth; + bmiHeader.biCompression = OS.BI_RGB; + byte[] bmi = new byte[BITMAPINFOHEADER.sizeof + numColors * 4]; + OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + + /* Get the HDC for the device */ + int /*long*/ hDC = device.internal_new_GC(null); + + /* Create the DC and select the bitmap */ + int /*long*/ hBitmapDC = OS.CreateCompatibleDC(hDC); + int /*long*/ hOldBitmap = OS.SelectObject(hBitmapDC, hBitmap); + /* Select the palette if necessary */ + int /*long*/ oldPalette = 0; + if (depth <= 8) { + int /*long*/ hPalette = device.hPalette; + if (hPalette != 0) { + oldPalette = OS.SelectPalette(hBitmapDC, hPalette, false); + OS.RealizePalette(hBitmapDC); + } + } + /* Find the size of the image and allocate data */ + int imageSize; + /* Call with null lpBits to get the image size */ + if (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + OS.GetDIBits(hBitmapDC, hBitmap, 0, height, 0, bmi, OS.DIB_RGB_COLORS); + OS.MoveMemory(bmiHeader, bmi, BITMAPINFOHEADER.sizeof); + imageSize = bmiHeader.biSizeImage; + byte[] data = new byte[imageSize]; + /* Get the bitmap data */ + int /*long*/ hHeap = OS.GetProcessHeap(); + int /*long*/ lpvBits = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, imageSize); + if (lpvBits == 0) SWT.error(SWT.ERROR_NO_HANDLES); + if (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + OS.GetDIBits(hBitmapDC, hBitmap, 0, height, lpvBits, bmi, OS.DIB_RGB_COLORS); + OS.MoveMemory(data, lpvBits, imageSize); + /* Calculate the palette */ + PaletteData palette = null; + if (depth <= 8) { + RGB[] rgbs = new RGB[numColors]; + int srcIndex = 40; + for (int i = 0; i < numColors; i++) { + rgbs[i] = new RGB(bmi[srcIndex + 2] & 0xFF, bmi[srcIndex + 1] & 0xFF, bmi[srcIndex] & 0xFF); + srcIndex += 4; + } + palette = new PaletteData(rgbs); + } else if (depth == 16) { + palette = new PaletteData(0x7C00, 0x3E0, 0x1F); + } else if (depth == 24) { + palette = new PaletteData(0xFF, 0xFF00, 0xFF0000); + } else if (depth == 32) { + palette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000); + } else { + SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); + } + + /* Do the mask */ + byte [] maskData = null; + if (info.hbmColor == 0) { + /* Do the bottom half of the mask */ + maskData = new byte[imageSize]; + if (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + OS.GetDIBits(hBitmapDC, hBitmap, height, height, lpvBits, bmi, OS.DIB_RGB_COLORS); + OS.MoveMemory(maskData, lpvBits, imageSize); + } else { + /* Do the entire mask */ + /* Create the BITMAPINFO */ + bmiHeader = new BITMAPINFOHEADER(); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biWidth = width; + bmiHeader.biHeight = -height; + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = 1; + bmiHeader.biCompression = OS.BI_RGB; + bmi = new byte[BITMAPINFOHEADER.sizeof + 8]; + OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + + /* First color black, second color white */ + int offset = BITMAPINFOHEADER.sizeof; + bmi[offset + 4] = bmi[offset + 5] = bmi[offset + 6] = (byte)0xFF; + bmi[offset + 7] = 0; + OS.SelectObject(hBitmapDC, info.hbmMask); + /* Call with null lpBits to get the image size */ + if (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + OS.GetDIBits(hBitmapDC, info.hbmMask, 0, height, 0, bmi, OS.DIB_RGB_COLORS); + OS.MoveMemory(bmiHeader, bmi, BITMAPINFOHEADER.sizeof); + imageSize = bmiHeader.biSizeImage; + maskData = new byte[imageSize]; + int /*long*/ lpvMaskBits = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, imageSize); + if (lpvMaskBits == 0) SWT.error(SWT.ERROR_NO_HANDLES); + if (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + OS.GetDIBits(hBitmapDC, info.hbmMask, 0, height, lpvMaskBits, bmi, OS.DIB_RGB_COLORS); + OS.MoveMemory(maskData, lpvMaskBits, imageSize); + OS.HeapFree(hHeap, 0, lpvMaskBits); + /* Loop to invert the mask */ + for (int i = 0; i < maskData.length; i++) { + maskData[i] ^= -1; + } + /* Make sure mask scanlinePad is 2 */ + int maskPad; + int bpl = imageSize / height; + for (maskPad = 1; maskPad < 128; maskPad++) { + int calcBpl = (((width + 7) / 8) + (maskPad - 1)) / maskPad * maskPad; + if (calcBpl == bpl) break; + } + maskData = ImageData.convertPad(maskData, width, height, 1, maskPad, 2); + } + /* Clean up */ + OS.HeapFree(hHeap, 0, lpvBits); + OS.SelectObject(hBitmapDC, hOldBitmap); + if (oldPalette != 0) { + OS.SelectPalette(hBitmapDC, oldPalette, false); + OS.RealizePalette(hBitmapDC); + } + OS.DeleteDC(hBitmapDC); + + /* Release the HDC for the device */ + device.internal_dispose_GC(hDC, null); + + if (info.hbmColor != 0) OS.DeleteObject(info.hbmColor); + if (info.hbmMask != 0) OS.DeleteObject(info.hbmMask); + /* Construct and return the ImageData */ + ImageData imageData = new ImageData(width, height, depth, palette, 4, data); + imageData.maskData = maskData; + imageData.maskPad = 2; + return imageData; + } + case SWT.BITMAP: { + /* Get the basic BITMAP information */ + bm = new BITMAP(); + OS.GetObject(handle, BITMAP.sizeof, bm); + depth = bm.bmPlanes * bm.bmBitsPixel; + width = bm.bmWidth; + height = bm.bmHeight; + /* Find out whether this is a DIB or a DDB. */ + boolean isDib = (bm.bmBits != 0); + /* Get the HDC for the device */ + int /*long*/ hDC = device.internal_new_GC(null); + + /* + * Feature in WinCE. GetDIBits is not available in WinCE. The + * workaround is to create a temporary DIB from the DDB and use + * the bmBits field of DIBSECTION to retrieve the image data. + */ + int /*long*/ handle = this.handle; + if (OS.IsWinCE) { + if (!isDib) { + boolean mustRestore = false; + if (memGC != null && !memGC.isDisposed()) { + memGC.flush (); + mustRestore = true; + GCData data = memGC.data; + if (data.hNullBitmap != 0) { + OS.SelectObject(memGC.handle, data.hNullBitmap); + data.hNullBitmap = 0; + } + } + handle = createDIBFromDDB(hDC, this.handle, width, height); + if (mustRestore) { + int /*long*/ hOldBitmap = OS.SelectObject(memGC.handle, this.handle); + memGC.data.hNullBitmap = hOldBitmap; + } + isDib = true; + } + } + DIBSECTION dib = null; + if (isDib) { + dib = new DIBSECTION(); + OS.GetObject(handle, DIBSECTION.sizeof, dib); + } + /* Calculate number of colors */ + int numColors = 0; + if (depth <= 8) { + if (isDib) { + numColors = dib.biClrUsed; + } else { + numColors = 1 << depth; + } + } + /* Create the BITMAPINFO */ + byte[] bmi = null; + BITMAPINFOHEADER bmiHeader = null; + if (!isDib) { + bmiHeader = new BITMAPINFOHEADER(); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biWidth = width; + bmiHeader.biHeight = -height; + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = (short)depth; + bmiHeader.biCompression = OS.BI_RGB; + bmi = new byte[BITMAPINFOHEADER.sizeof + numColors * 4]; + OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + } + + /* Create the DC and select the bitmap */ + int /*long*/ hBitmapDC = OS.CreateCompatibleDC(hDC); + int /*long*/ hOldBitmap = OS.SelectObject(hBitmapDC, handle); + /* Select the palette if necessary */ + int /*long*/ oldPalette = 0; + if (!isDib && depth <= 8) { + int /*long*/ hPalette = device.hPalette; + if (hPalette != 0) { + oldPalette = OS.SelectPalette(hBitmapDC, hPalette, false); + OS.RealizePalette(hBitmapDC); + } + } + /* Find the size of the image and allocate data */ + int imageSize; + if (isDib) { + imageSize = dib.biSizeImage; + } else { + /* Call with null lpBits to get the image size */ + if (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + OS.GetDIBits(hBitmapDC, handle, 0, height, 0, bmi, OS.DIB_RGB_COLORS); + OS.MoveMemory(bmiHeader, bmi, BITMAPINFOHEADER.sizeof); + imageSize = bmiHeader.biSizeImage; + } + byte[] data = new byte[imageSize]; + /* Get the bitmap data */ + if (isDib) { + if (OS.IsWinCE && this.handle != handle) { + /* get image data from the temporary DIB */ + OS.MoveMemory(data, dib.bmBits, imageSize); + } else { + OS.MoveMemory(data, bm.bmBits, imageSize); + } + } else { + int /*long*/ hHeap = OS.GetProcessHeap(); + int /*long*/ lpvBits = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, imageSize); + if (lpvBits == 0) SWT.error(SWT.ERROR_NO_HANDLES); + if (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + OS.GetDIBits(hBitmapDC, handle, 0, height, lpvBits, bmi, OS.DIB_RGB_COLORS); + OS.MoveMemory(data, lpvBits, imageSize); + OS.HeapFree(hHeap, 0, lpvBits); + } + /* Calculate the palette */ + PaletteData palette = null; + if (depth <= 8) { + RGB[] rgbs = new RGB[numColors]; + if (isDib) { + if (OS.IsWinCE) { + /* + * Feature on WinCE. GetDIBColorTable is not supported. + * The workaround is to set a pixel to the desired + * palette index and use getPixel to get the corresponding + * RGB value. + */ + int red = 0, green = 0, blue = 0; + byte[] pBits = new byte[1]; + OS.MoveMemory(pBits, bm.bmBits, 1); + byte oldValue = pBits[0]; + int mask = (0xFF << (8 - bm.bmBitsPixel)) & 0x00FF; + for (int i = 0; i < numColors; i++) { + pBits[0] = (byte)((i << (8 - bm.bmBitsPixel)) | (pBits[0] & ~mask)); + OS.MoveMemory(bm.bmBits, pBits, 1); + int color = OS.GetPixel(hBitmapDC, 0, 0); + blue = (color & 0xFF0000) >> 16; + green = (color & 0xFF00) >> 8; + red = color & 0xFF; + rgbs[i] = new RGB(red, green, blue); + } + pBits[0] = oldValue; + OS.MoveMemory(bm.bmBits, pBits, 1); + } else { + byte[] colors = new byte[numColors * 4]; + OS.GetDIBColorTable(hBitmapDC, 0, numColors, colors); + int colorIndex = 0; + for (int i = 0; i < rgbs.length; i++) { + rgbs[i] = new RGB(colors[colorIndex + 2] & 0xFF, colors[colorIndex + 1] & 0xFF, colors[colorIndex] & 0xFF); + colorIndex += 4; + } + } + } else { + int srcIndex = BITMAPINFOHEADER.sizeof; + for (int i = 0; i < numColors; i++) { + rgbs[i] = new RGB(bmi[srcIndex + 2] & 0xFF, bmi[srcIndex + 1] & 0xFF, bmi[srcIndex] & 0xFF); + srcIndex += 4; + } + } + palette = new PaletteData(rgbs); + } else if (depth == 16) { + palette = new PaletteData(0x7C00, 0x3E0, 0x1F); + } else if (depth == 24) { + palette = new PaletteData(0xFF, 0xFF00, 0xFF0000); + } else if (depth == 32) { + palette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000); + } else { + SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); + } + /* Clean up */ + OS.SelectObject(hBitmapDC, hOldBitmap); + if (oldPalette != 0) { + OS.SelectPalette(hBitmapDC, oldPalette, false); + OS.RealizePalette(hBitmapDC); + } + if (OS.IsWinCE) { + if (handle != this.handle) { + /* free temporary DIB */ + OS.DeleteObject (handle); + } + } + OS.DeleteDC(hBitmapDC); + + /* Release the HDC for the device */ + device.internal_dispose_GC(hDC, null); + + /* Construct and return the ImageData */ + ImageData imageData = new ImageData(width, height, depth, palette, 4, data); + imageData.transparentPixel = this.transparentPixel; + imageData.alpha = alpha; + if (alpha == -1 && alphaData != null) { + imageData.alphaData = new byte[alphaData.length]; + System.arraycopy(alphaData, 0, imageData.alphaData, 0, alphaData.length); + } + return imageData; + } + default: + SWT.error(SWT.ERROR_INVALID_IMAGE); + return null; + } +} + +/** + * 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; +} + +void init(int width, int height) { + if (width <= 0 || height <= 0) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + type = SWT.BITMAP; + int /*long*/ hDC = device.internal_new_GC(null); + handle = OS.CreateCompatibleBitmap(hDC, width, height); + /* + * Feature in Windows. CreateCompatibleBitmap() may fail + * for large images. The fix is to create a DIB section + * in that case. + */ + if (handle == 0) { + int bits = OS.GetDeviceCaps(hDC, OS.BITSPIXEL); + int planes = OS.GetDeviceCaps(hDC, OS.PLANES); + int depth = bits * planes; + if (depth < 16) depth = 16; + handle = createDIB(width, height, depth); + } + if (handle != 0) { + int /*long*/ memDC = OS.CreateCompatibleDC(hDC); + int /*long*/ hOldBitmap = OS.SelectObject(memDC, handle); + OS.PatBlt(memDC, 0, 0, width, height, OS.PATCOPY); + OS.SelectObject(memDC, hOldBitmap); + OS.DeleteDC(memDC); + } + device.internal_dispose_GC(hDC, null); + if (handle == 0) { + SWT.error(SWT.ERROR_NO_HANDLES, null, device.getLastError()); + } +} + +static int /*long*/ createDIB(int width, int height, int depth) { + BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER(); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biWidth = width; + bmiHeader.biHeight = -height; + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = (short)depth; + if (OS.IsWinCE) bmiHeader.biCompression = OS.BI_BITFIELDS; + else bmiHeader.biCompression = OS.BI_RGB; + byte[] bmi = new byte[BITMAPINFOHEADER.sizeof + (OS.IsWinCE ? 12 : 0)]; + OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + /* Set the rgb colors into the bitmap info */ + if (OS.IsWinCE) { + int redMask = 0xFF00; + int greenMask = 0xFF0000; + int blueMask = 0xFF000000; + /* big endian */ + int offset = BITMAPINFOHEADER.sizeof; + bmi[offset] = (byte)((redMask & 0xFF000000) >> 24); + bmi[offset + 1] = (byte)((redMask & 0xFF0000) >> 16); + bmi[offset + 2] = (byte)((redMask & 0xFF00) >> 8); + bmi[offset + 3] = (byte)((redMask & 0xFF) >> 0); + bmi[offset + 4] = (byte)((greenMask & 0xFF000000) >> 24); + bmi[offset + 5] = (byte)((greenMask & 0xFF0000) >> 16); + bmi[offset + 6] = (byte)((greenMask & 0xFF00) >> 8); + bmi[offset + 7] = (byte)((greenMask & 0xFF) >> 0); + bmi[offset + 8] = (byte)((blueMask & 0xFF000000) >> 24); + bmi[offset + 9] = (byte)((blueMask & 0xFF0000) >> 16); + bmi[offset + 10] = (byte)((blueMask & 0xFF00) >> 8); + bmi[offset + 11] = (byte)((blueMask & 0xFF) >> 0); + } + + int /*long*/[] pBits = new int /*long*/[1]; + return OS.CreateDIBSection(0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0); +} + +/** + * Feature in WinCE. GetIconInfo is not available in WinCE. + * The workaround is to cache the object ImageData for images + * of type SWT.ICON. The bitmaps hbmMask and hbmColor can then + * be reconstructed by using our version of getIconInfo. + * This function takes an ICONINFO object and sets the fields + * hbmMask and hbmColor with the corresponding bitmaps it has + * created. + * Note. These bitmaps must be freed - as they would have to be + * if the regular GetIconInfo had been used. + */ +static void GetIconInfo(Image image, ICONINFO info) { + int /*long*/ [] result = init(image.device, null, image.data); + info.hbmColor = result[0]; + info.hbmMask = result[1]; +} + +static int /*long*/ [] init(Device device, Image image, ImageData i) { + /* + * BUG in Windows 98: + * A monochrome DIBSection will display as solid black + * on Windows 98 machines, even though it contains the + * correct data. The fix is to convert 1-bit ImageData + * into 4-bit ImageData before creating the image. + */ + /* Windows does not support 2-bit images. Convert to 4-bit image. */ + if ((OS.IsWin95 && i.depth == 1 && i.getTransparencyType() != SWT.TRANSPARENCY_MASK) || i.depth == 2) { + ImageData img = new ImageData(i.width, i.height, 4, i.palette); + ImageData.blit(ImageData.BLIT_SRC, + i.data, i.depth, i.bytesPerLine, i.getByteOrder(), 0, 0, i.width, i.height, null, null, null, + ImageData.ALPHA_OPAQUE, null, 0, 0, 0, + img.data, img.depth, img.bytesPerLine, i.getByteOrder(), 0, 0, img.width, img.height, null, null, null, + false, false); + img.transparentPixel = i.transparentPixel; + img.maskPad = i.maskPad; + img.maskData = i.maskData; + img.alpha = i.alpha; + img.alphaData = i.alphaData; + i = img; + } + /* + * Windows supports 16-bit mask of (0x7C00, 0x3E0, 0x1F), + * 24-bit mask of (0xFF0000, 0xFF00, 0xFF) and 32-bit mask + * (0x00FF0000, 0x0000FF00, 0x000000FF) as documented in + * MSDN BITMAPINFOHEADER. Make sure the image is + * Windows-supported. + */ + /* + * Note on WinCE. CreateDIBSection requires the biCompression + * field of the BITMAPINFOHEADER to be set to BI_BITFIELDS for + * 16 and 32 bit direct images (see MSDN for CreateDIBSection). + * In this case, the color mask can be set to any value. For + * consistency, it is set to the same mask used by non WinCE + * platforms in BI_RGB mode. + */ + if (i.palette.isDirect) { + final PaletteData palette = i.palette; + final int redMask = palette.redMask; + final int greenMask = palette.greenMask; + final int blueMask = palette.blueMask; + int newDepth = i.depth; + int newOrder = ImageData.MSB_FIRST; + PaletteData newPalette = null; + + switch (i.depth) { + case 8: + newDepth = 16; + newOrder = ImageData.LSB_FIRST; + newPalette = new PaletteData(0x7C00, 0x3E0, 0x1F); + break; + case 16: + newOrder = ImageData.LSB_FIRST; + if (!(redMask == 0x7C00 && greenMask == 0x3E0 && blueMask == 0x1F)) { + newPalette = new PaletteData(0x7C00, 0x3E0, 0x1F); + } + break; + case 24: + if (!(redMask == 0xFF && greenMask == 0xFF00 && blueMask == 0xFF0000)) { + newPalette = new PaletteData(0xFF, 0xFF00, 0xFF0000); + } + break; + case 32: + if (!(redMask == 0xFF00 && greenMask == 0xFF0000 && blueMask == 0xFF000000)) { + newPalette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000); + } + break; + default: + SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); + } + if (newPalette != null) { + ImageData img = new ImageData(i.width, i.height, newDepth, newPalette); + ImageData.blit(ImageData.BLIT_SRC, + i.data, i.depth, i.bytesPerLine, i.getByteOrder(), 0, 0, i.width, i.height, redMask, greenMask, blueMask, + ImageData.ALPHA_OPAQUE, null, 0, 0, 0, + img.data, img.depth, img.bytesPerLine, newOrder, 0, 0, img.width, img.height, newPalette.redMask, newPalette.greenMask, newPalette.blueMask, + false, false); + if (i.transparentPixel != -1) { + img.transparentPixel = newPalette.getPixel(palette.getRGB(i.transparentPixel)); + } + img.maskPad = i.maskPad; + img.maskData = i.maskData; + img.alpha = i.alpha; + img.alphaData = i.alphaData; + i = img; + } + } + /* Construct bitmap info header by hand */ + RGB[] rgbs = i.palette.getRGBs(); + boolean useBitfields = OS.IsWinCE && (i.depth == 16 || i.depth == 32); + BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER(); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biWidth = i.width; + bmiHeader.biHeight = -i.height; + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = (short)i.depth; + if (useBitfields) bmiHeader.biCompression = OS.BI_BITFIELDS; + else bmiHeader.biCompression = OS.BI_RGB; + bmiHeader.biClrUsed = rgbs == null ? 0 : rgbs.length; + byte[] bmi; + if (i.palette.isDirect) + bmi = new byte[BITMAPINFOHEADER.sizeof + (useBitfields ? 12 : 0)]; + else + bmi = new byte[BITMAPINFOHEADER.sizeof + rgbs.length * 4]; + OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + /* Set the rgb colors into the bitmap info */ + int offset = BITMAPINFOHEADER.sizeof; + if (i.palette.isDirect) { + if (useBitfields) { + PaletteData palette = i.palette; + int redMask = palette.redMask; + int greenMask = palette.greenMask; + int blueMask = palette.blueMask; + /* + * The color masks must be written based on the + * endianness of the ImageData. + */ + if (i.getByteOrder() == ImageData.LSB_FIRST) { + bmi[offset] = (byte)((redMask & 0xFF) >> 0); + bmi[offset + 1] = (byte)((redMask & 0xFF00) >> 8); + bmi[offset + 2] = (byte)((redMask & 0xFF0000) >> 16); + bmi[offset + 3] = (byte)((redMask & 0xFF000000) >> 24); + bmi[offset + 4] = (byte)((greenMask & 0xFF) >> 0); + bmi[offset + 5] = (byte)((greenMask & 0xFF00) >> 8); + bmi[offset + 6] = (byte)((greenMask & 0xFF0000) >> 16); + bmi[offset + 7] = (byte)((greenMask & 0xFF000000) >> 24); + bmi[offset + 8] = (byte)((blueMask & 0xFF) >> 0); + bmi[offset + 9] = (byte)((blueMask & 0xFF00) >> 8); + bmi[offset + 10] = (byte)((blueMask & 0xFF0000) >> 16); + bmi[offset + 11] = (byte)((blueMask & 0xFF000000) >> 24); + } else { + bmi[offset] = (byte)((redMask & 0xFF000000) >> 24); + bmi[offset + 1] = (byte)((redMask & 0xFF0000) >> 16); + bmi[offset + 2] = (byte)((redMask & 0xFF00) >> 8); + bmi[offset + 3] = (byte)((redMask & 0xFF) >> 0); + bmi[offset + 4] = (byte)((greenMask & 0xFF000000) >> 24); + bmi[offset + 5] = (byte)((greenMask & 0xFF0000) >> 16); + bmi[offset + 6] = (byte)((greenMask & 0xFF00) >> 8); + bmi[offset + 7] = (byte)((greenMask & 0xFF) >> 0); + bmi[offset + 8] = (byte)((blueMask & 0xFF000000) >> 24); + bmi[offset + 9] = (byte)((blueMask & 0xFF0000) >> 16); + bmi[offset + 10] = (byte)((blueMask & 0xFF00) >> 8); + bmi[offset + 11] = (byte)((blueMask & 0xFF) >> 0); + } + } + } else { + for (int j = 0; j < rgbs.length; j++) { + bmi[offset] = (byte)rgbs[j].blue; + bmi[offset + 1] = (byte)rgbs[j].green; + bmi[offset + 2] = (byte)rgbs[j].red; + bmi[offset + 3] = 0; + offset += 4; + } + } + int /*long*/[] pBits = new int /*long*/[1]; + int /*long*/ hDib = OS.CreateDIBSection(0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0); + if (hDib == 0) SWT.error(SWT.ERROR_NO_HANDLES); + /* In case of a scanline pad other than 4, do the work to convert it */ + byte[] data = i.data; + if (i.scanlinePad != 4 && (i.bytesPerLine % 4 != 0)) { + data = ImageData.convertPad(data, i.width, i.height, i.depth, i.scanlinePad, 4); + } + OS.MoveMemory(pBits[0], data, data.length); + + int /*long*/ [] result = null; + if (i.getTransparencyType() == SWT.TRANSPARENCY_MASK) { + /* Get the HDC for the device */ + int /*long*/ hDC = device.internal_new_GC(null); + + /* Create the color bitmap */ + int /*long*/ hdcSrc = OS.CreateCompatibleDC(hDC); + OS.SelectObject(hdcSrc, hDib); + int /*long*/ hBitmap = OS.CreateCompatibleBitmap(hDC, i.width, i.height); + if (hBitmap == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ hdcDest = OS.CreateCompatibleDC(hDC); + OS.SelectObject(hdcDest, hBitmap); + OS.BitBlt(hdcDest, 0, 0, i.width, i.height, hdcSrc, 0, 0, OS.SRCCOPY); + + /* Release the HDC for the device */ + device.internal_dispose_GC(hDC, null); + + /* Create the mask. Windows requires icon masks to have a scanline pad of 2. */ + byte[] maskData = ImageData.convertPad(i.maskData, i.width, i.height, 1, i.maskPad, 2); + int /*long*/ hMask = OS.CreateBitmap(i.width, i.height, 1, 1, maskData); + if (hMask == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.SelectObject(hdcSrc, hMask); + OS.PatBlt(hdcSrc, 0, 0, i.width, i.height, OS.DSTINVERT); + OS.DeleteDC(hdcSrc); + OS.DeleteDC(hdcDest); + OS.DeleteObject(hDib); + + if (image == null) { + result = new int /*long*/ []{hBitmap, hMask}; + } else { + /* Create the icon */ + ICONINFO info = new ICONINFO(); + info.fIcon = true; + info.hbmColor = hBitmap; + info.hbmMask = hMask; + int /*long*/ hIcon = OS.CreateIconIndirect(info); + if (hIcon == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.DeleteObject(hBitmap); + OS.DeleteObject(hMask); + image.handle = hIcon; + image.type = SWT.ICON; + if (OS.IsWinCE) image.data = i; + } + } else { + if (image == null) { + result = new int /*long*/ []{hDib}; + } else { + image.handle = hDib; + image.type = SWT.BITMAP; + image.transparentPixel = i.transparentPixel; + if (image.transparentPixel == -1) { + image.alpha = i.alpha; + if (i.alpha == -1 && i.alphaData != null) { + int length = i.alphaData.length; + image.alphaData = new byte[length]; + System.arraycopy(i.alphaData, 0, image.alphaData, 0, length); + } + } + } + } + return result; +} + +static int /*long*/ [] init(Device device, Image image, ImageData source, ImageData mask) { + /* Create a temporary image and locate the black pixel */ + ImageData imageData; + int blackIndex = 0; + if (source.palette.isDirect) { + imageData = new ImageData(source.width, source.height, source.depth, source.palette); + } else { + RGB black = new RGB(0, 0, 0); + RGB[] rgbs = source.getRGBs(); + if (source.transparentPixel != -1) { + /* + * The source had transparency, so we can use the transparent pixel + * for black. + */ + RGB[] newRGBs = new RGB[rgbs.length]; + System.arraycopy(rgbs, 0, newRGBs, 0, rgbs.length); + if (source.transparentPixel >= newRGBs.length) { + /* Grow the palette with black */ + rgbs = new RGB[source.transparentPixel + 1]; + System.arraycopy(newRGBs, 0, rgbs, 0, newRGBs.length); + for (int i = newRGBs.length; i <= source.transparentPixel; i++) { + rgbs[i] = new RGB(0, 0, 0); + } + } else { + newRGBs[source.transparentPixel] = black; + rgbs = newRGBs; + } + blackIndex = source.transparentPixel; + imageData = new ImageData(source.width, source.height, source.depth, new PaletteData(rgbs)); + } else { + while (blackIndex < rgbs.length) { + if (rgbs[blackIndex].equals(black)) break; + blackIndex++; + } + if (blackIndex == rgbs.length) { + /* + * We didn't find black in the palette, and there is no transparent + * pixel we can use. + */ + if ((1 << source.depth) > rgbs.length) { + /* We can grow the palette and add black */ + RGB[] newRGBs = new RGB[rgbs.length + 1]; + System.arraycopy(rgbs, 0, newRGBs, 0, rgbs.length); + newRGBs[rgbs.length] = black; + rgbs = newRGBs; + } else { + /* No room to grow the palette */ + blackIndex = -1; + } + } + imageData = new ImageData(source.width, source.height, source.depth, new PaletteData(rgbs)); + } + } + if (blackIndex == -1) { + /* There was no black in the palette, so just copy the data over */ + System.arraycopy(source.data, 0, imageData.data, 0, imageData.data.length); + } else { + /* Modify the source image to contain black wherever the mask is 0 */ + int[] imagePixels = new int[imageData.width]; + int[] maskPixels = new int[mask.width]; + for (int y = 0; y < imageData.height; y++) { + source.getPixels(0, y, imageData.width, imagePixels, 0); + mask.getPixels(0, y, mask.width, maskPixels, 0); + for (int i = 0; i < imagePixels.length; i++) { + if (maskPixels[i] == 0) imagePixels[i] = blackIndex; + } + imageData.setPixels(0, y, source.width, imagePixels, 0); + } + } + imageData.maskPad = mask.scanlinePad; + imageData.maskData = mask.data; + return init(device, image, imageData); +} +void init(ImageData i) { + if (i == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + init(device, this, i); +} + +/** + * 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 == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + /* + * Create a new GC that can draw into the image. + * Only supported for bitmaps. + */ + if (type != SWT.BITMAP || memGC != null) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + /* Create a compatible HDC for the device */ + int /*long*/ hDC = device.internal_new_GC(null); + int /*long*/ imageDC = OS.CreateCompatibleDC(hDC); + device.internal_dispose_GC(hDC, null); + if (imageDC == 0) SWT.error(SWT.ERROR_NO_HANDLES); + + if (data != null) { + /* Set the GCData fields */ + int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT; + if ((data.style & mask) != 0) { + data.layout = (data.style & SWT.RIGHT_TO_LEFT) != 0 ? OS.LAYOUT_RTL : 0; + } else { + data.style |= SWT.LEFT_TO_RIGHT; + } + data.device = device; + data.image = this; + data.font = device.systemFont; + } + return imageDC; +} + +/** + * 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*/ hDC, GCData data) { + OS.DeleteDC(hDC); +} + +/** + * 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 == 0; +} + +/** + * 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) { + /* + * Note. Not implemented on WinCE. + */ + if (OS.IsWinCE) return; + 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; + transparentColor = -1; + + /* Get the HDC for the device */ + int /*long*/ hDC = device.internal_new_GC(null); + + /* Change the background color in the image */ + BITMAP bm = new BITMAP(); + OS.GetObject(handle, BITMAP.sizeof, bm); + int /*long*/ hdcMem = OS.CreateCompatibleDC(hDC); + OS.SelectObject(hdcMem, handle); + int maxColors = 1 << bm.bmBitsPixel; + byte[] colors = new byte[maxColors * 4]; + if (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + int numColors = OS.GetDIBColorTable(hdcMem, 0, maxColors, colors); + int offset = transparentPixel * 4; + colors[offset] = (byte)color.getBlue(); + colors[offset + 1] = (byte)color.getGreen(); + colors[offset + 2] = (byte)color.getRed(); + if (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + OS.SetDIBColorTable(hdcMem, 0, numColors, colors); + OS.DeleteDC(hdcMem); + + /* Release the HDC for the device */ + device.internal_dispose_GC(hDC, 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 "Image {*DISPOSED*}"; + return "Image {" + handle + "}"; +} + +/** + * 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 + * @return a new image object containing the specified device, type and handle + */ +public static Image win32_new(Device device, int type, int /*long*/ handle) { + Image image = new Image(device); + image.type = type; + image.handle = handle; + return image; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Path.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Path.java new file mode 100644 index 0000000000..db666da42b --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Path.java @@ -0,0 +1,602 @@ +/******************************************************************************* + * 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.gdip.*; +import org.eclipse.swt.internal.win32.*; + +/** + * 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 int /*long*/ handle; + + PointF currentPoint = new PointF(), startPoint = new PointF(); + +/** + * 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); + this.device.checkGDIP(); + handle = Gdip.GraphicsPath_new(Gdip.FillModeAlternate); + if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); + init(); +} + +/** + * 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); + if (path == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + if (path.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + flatness = Math.max(0, flatness); + handle = Gdip.GraphicsPath_Clone(path.handle); + if (flatness != 0) Gdip.GraphicsPath_Flatten(handle, 0, flatness); + if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); + init(); +} + +/** + * 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); + if (data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + init(data); +} + +/** + * 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); + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + if (width == 0 || height == 0 || arcAngle == 0) return; + if (width == height) { + Gdip.GraphicsPath_AddArc(handle, x, y, width, height, -startAngle, -arcAngle); + } else { + int /*long*/ path = Gdip.GraphicsPath_new(Gdip.FillModeAlternate); + if (path == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ matrix = Gdip.Matrix_new(width, 0, 0, height, x, y); + if (matrix == 0) SWT.error(SWT.ERROR_NO_HANDLES); + Gdip.GraphicsPath_AddArc(path, 0, 0, 1, 1, -startAngle, -arcAngle); + Gdip.GraphicsPath_Transform(path, matrix); + Gdip.GraphicsPath_AddPath(handle, path, true); + Gdip.Matrix_delete(matrix); + Gdip.GraphicsPath_delete(path); + } + Gdip.GraphicsPath_GetLastPoint(handle, currentPoint); +} + +/** + * 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); + //TODO - expose connect? + Gdip.GraphicsPath_AddPath(handle, path.handle, false); + currentPoint.X = path.currentPoint.X; + currentPoint.Y = path.currentPoint.Y; +} + +/** + * 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); + RectF rect = new RectF(); + rect.X = x; + rect.Y = y; + rect.Width = width; + rect.Height = height; + Gdip.GraphicsPath_AddRectangle(handle, rect); + currentPoint.X = x; + currentPoint.Y = y; +} + +/** + * 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); + int length = string.length(); + char[] buffer = new char[length]; + string.getChars(0, length, buffer, 0); + int /*long*/ hDC = device.internal_new_GC(null); + int /*long*/ [] family = new int /*long*/ [1]; + int /*long*/ gdipFont = GC.createGdipFont(hDC, font.handle, 0, device.fontCollection, family, null); + PointF point = new PointF(); + point.X = x - (Gdip.Font_GetSize(gdipFont) / 6); + point.Y = y; + int style = Gdip.Font_GetStyle(gdipFont); + float size = Gdip.Font_GetSize(gdipFont); + Gdip.GraphicsPath_AddString(handle, buffer, length, family[0], style, size, point, 0); + Gdip.GraphicsPath_GetLastPoint(handle, currentPoint); + Gdip.FontFamily_delete(family[0]); + Gdip.Font_delete(gdipFont); + device.internal_dispose_GC(hDC, null); +} + +/** + * 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); + Gdip.GraphicsPath_CloseFigure(handle); + /* + * Feature in GDI+. CloseFigure() does affect the last + * point, so GetLastPoint() does not return the starting + * point of the subpath after calling CloseFigure(). The + * fix is to remember the subpath starting point and use + * it instead. + */ + currentPoint.X = startPoint.X; + currentPoint.Y = startPoint.Y; +} + +/** + * 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); + //TODO - should use GC transformation + gc.initGdip(); + gc.checkGC(GC.LINE_CAP | GC.LINE_JOIN | GC.LINE_STYLE | GC.LINE_WIDTH); + int mode = OS.GetPolyFillMode(gc.handle) == OS.WINDING ? Gdip.FillModeWinding : Gdip.FillModeAlternate; + Gdip.GraphicsPath_SetFillMode(handle, mode); + if (outline) { + return Gdip.GraphicsPath_IsOutlineVisible(handle, x, y, gc.data.gdipPen, gc.data.gdipGraphics); + } else { + return Gdip.GraphicsPath_IsVisible(handle, x, y, gc.data.gdipGraphics); + } +} + +/** + * 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); + Gdip.GraphicsPath_AddBezier(handle, currentPoint.X, currentPoint.Y, cx1, cy1, cx2, cy2, x, y); + Gdip.GraphicsPath_GetLastPoint(handle, currentPoint); +} + +void destroy() { + Gdip.GraphicsPath_delete(handle); + handle = 0; +} + +/** + * 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); + RectF rect = new RectF(); + Gdip.GraphicsPath_GetBounds(handle, rect, 0, 0); + bounds[0] = rect.X; + bounds[1] = rect.Y; + bounds[2] = rect.Width; + bounds[3] = rect.Height; +} + +/** + * 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); + point[0] = currentPoint.X; + point[1] = currentPoint.Y; +} + +/** + * 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); + int count = Gdip.GraphicsPath_GetPointCount(handle); + byte[] gdipTypes = new byte[count]; + float[] points = new float[count * 2]; + Gdip.GraphicsPath_GetPathTypes(handle, gdipTypes, count); + Gdip.GraphicsPath_GetPathPoints(handle, points, count); + byte[] types = new byte[count * 2]; + int index = 0, typesIndex = 0; + while (index < count) { + byte type = gdipTypes[index]; + boolean close = false; + switch (type & Gdip.PathPointTypePathTypeMask) { + case Gdip.PathPointTypeStart: + types[typesIndex++] = SWT.PATH_MOVE_TO; + close = (type & Gdip.PathPointTypeCloseSubpath) != 0; + index += 1; + break; + case Gdip.PathPointTypeLine: + types[typesIndex++] = SWT.PATH_LINE_TO; + close = (type & Gdip.PathPointTypeCloseSubpath) != 0; + index += 1; + break; + case Gdip.PathPointTypeBezier: + types[typesIndex++] = SWT.PATH_CUBIC_TO; + close = (gdipTypes[index + 2] & Gdip.PathPointTypeCloseSubpath) != 0; + index += 3; + break; + default: + index++; + } + if (close) { + types[typesIndex++] = SWT.PATH_CLOSE; + } + } + if (typesIndex != types.length) { + byte[] newTypes = new byte[typesIndex]; + System.arraycopy(types, 0, newTypes, 0, typesIndex); + types = newTypes; + } + PathData result = new PathData(); + result.types = types; + result.points = points; + return result; +} + +/** + * 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); + Gdip.GraphicsPath_AddLine(handle, currentPoint.X, currentPoint.Y, x, y); + Gdip.GraphicsPath_GetLastPoint(handle, currentPoint); +} + +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 == 0; +} + +/** + * 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); + Gdip.GraphicsPath_StartFigure(handle); + currentPoint.X = startPoint.X = x; + currentPoint.Y = startPoint.Y = y; +} + +/** + * 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); + float cx1 = currentPoint.X + 2 * (cx - currentPoint.X) / 3; + float cy1 = currentPoint.Y + 2 * (cy - currentPoint.Y) / 3; + float cx2 = cx1 + (x - currentPoint.X) / 3; + float cy2 = cy1 + (y - currentPoint.Y) / 3; + Gdip.GraphicsPath_AddBezier(handle, currentPoint.X, currentPoint.Y, cx1, cy1, cx2, cy2, x, y); + Gdip.GraphicsPath_GetLastPoint(handle, currentPoint); +} + +/** + * 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/win32/org/eclipse/swt/graphics/Pattern.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Pattern.java new file mode 100644 index 0000000000..406fcec965 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Pattern.java @@ -0,0 +1,250 @@ +/******************************************************************************* + * 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.gdip.*; +import org.eclipse.swt.internal.win32.*; + +/** + * 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 { + + /** + * the OS resource for the Pattern + * (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 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); + this.device.checkGDIP(); + int /*long*/[] gdipImage = image.createGdipImage(); + int /*long*/ img = gdipImage[0]; + int width = Gdip.Image_GetWidth(img); + int height = Gdip.Image_GetHeight(img); + handle = Gdip.TextureBrush_new(img, Gdip.WrapModeTile, 0, 0, width, height); + Gdip.Bitmap_delete(img); + if (gdipImage[1] != 0) { + int /*long*/ hHeap = OS.GetProcessHeap (); + OS.HeapFree(hHeap, 0, gdipImage[1]); + } + if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); + init(); +} + +/** + * 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); + this.device.checkGDIP(); + int colorRef1 = color1.handle; + int rgb = ((colorRef1 >> 16) & 0xFF) | (colorRef1 & 0xFF00) | ((colorRef1 & 0xFF) << 16); + int /*long*/ foreColor = Gdip.Color_new((alpha1 & 0xFF) << 24 | rgb); + if (x1 == x2 && y1 == y2) { + handle = Gdip.SolidBrush_new(foreColor); + if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); + } else { + int colorRef2 = color2.handle; + rgb = ((colorRef2 >> 16) & 0xFF) | (colorRef2 & 0xFF00) | ((colorRef2 & 0xFF) << 16); + int /*long*/ backColor = Gdip.Color_new((alpha2 & 0xFF) << 24 | rgb); + PointF p1 = new PointF(); + p1.X = x1; + p1.Y = y1; + PointF p2 = new PointF(); + p2.X = x2; + p2.Y = y2; + handle = Gdip.LinearGradientBrush_new(p1, p2, foreColor, backColor); + if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); + if (alpha1 != 0xFF || alpha2 != 0xFF) { + int a = (int)((alpha1 & 0xFF) * 0.5f + (alpha2 & 0xFF) * 0.5f); + int r = (int)(((colorRef1 & 0xFF) >> 0) * 0.5f + ((colorRef2 & 0xFF) >> 0) * 0.5f); + int g = (int)(((colorRef1 & 0xFF00) >> 8) * 0.5f + ((colorRef2 & 0xFF00) >> 8) * 0.5f); + int b = (int)(((colorRef1 & 0xFF0000) >> 16) * 0.5f + ((colorRef2 & 0xFF0000) >> 16) * 0.5f); + int /*long*/ midColor = Gdip.Color_new(a << 24 | r << 16 | g << 8 | b); + Gdip.LinearGradientBrush_SetInterpolationColors(handle, new int /*long*/ []{foreColor, midColor, backColor}, new float[]{0, 0.5f, 1}, 3); + Gdip.Color_delete(midColor); + } + Gdip.Color_delete(backColor); + } + Gdip.Color_delete(foreColor); + init(); +} + +void destroy() { + int type = Gdip.Brush_GetType(handle); + switch (type) { + case Gdip.BrushTypeSolidColor: + Gdip.SolidBrush_delete(handle); + break; + case Gdip.BrushTypeHatchFill: + Gdip.HatchBrush_delete(handle); + break; + case Gdip.BrushTypeLinearGradient: + Gdip.LinearGradientBrush_delete(handle); + break; + case Gdip.BrushTypeTextureFill: + Gdip.TextureBrush_delete(handle); + break; + } + handle = 0; +} + +/** + * 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 handle == 0; +} + +/** + * 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 {" + handle + "}"; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Region.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Region.java new file mode 100755 index 0000000000..5c6383328a --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Region.java @@ -0,0 +1,597 @@ +/******************************************************************************* + * 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.win32.*; +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); + handle = OS.CreateRectRgn (0, 0, 0, 0); + if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); + init(); +} + +/** + * Constructs a new region given a handle to the operating + * system resources that it should represent. + * + * @param handle the handle for the result + */ +Region(Device device, int handle) { + super(device); + this.handle = handle; +} + +/** + * 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); + if (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + int /*long*/ polyRgn = OS.CreatePolygonRgn(pointArray, pointArray.length / 2, OS.ALTERNATE); + OS.CombineRgn (handle, handle, polyRgn, OS.RGN_OR); + OS.DeleteObject (polyRgn); +} + +/** + * 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); + add (rect.x, rect.y, rect.width, rect.height); +} + +/** + * 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); + int /*long*/ rectRgn = OS.CreateRectRgn (x, y, x + width, y + height); + OS.CombineRgn (handle, handle, rectRgn, OS.RGN_OR); + OS.DeleteObject (rectRgn); +} + +/** + * 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); + OS.CombineRgn (handle, handle, region.handle, OS.RGN_OR); +} + +/** + * 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); + return OS.PtInRegion (handle, x, y); +} + +/** + * 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 (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + if (pt == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + return contains(pt.x, pt.y); +} + +void destroy () { + OS.DeleteObject(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 rgn = (Region)object; + return handle == rgn.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); + RECT rect = new RECT(); + OS.GetRgnBox(handle, rect); + return new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); +} + +/** + * 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); + int /*long*/ rectRgn = OS.CreateRectRgn (x, y, x + width, y + height); + OS.CombineRgn (handle, handle, rectRgn, OS.RGN_AND); + OS.DeleteObject (rectRgn); +} + +/** + * 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); + OS.CombineRgn (handle, handle, region.handle, OS.RGN_AND); +} + +/** + * 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); + RECT r = new RECT (); + OS.SetRect (r, x, y, x + width, y + height); + return OS.RectInRegion (handle, r); +} + +/** + * 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 (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); + 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); + RECT rect = new RECT (); + int result = OS.GetRgnBox (handle, rect); + if (result == OS.NULLREGION) return true; + return ((rect.right - rect.left) <= 0) || ((rect.bottom - rect.top) <= 0); +} + +/** + * 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 (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + int /*long*/ polyRgn = OS.CreatePolygonRgn(pointArray, pointArray.length / 2, OS.ALTERNATE); + OS.CombineRgn (handle, handle, polyRgn, OS.RGN_DIFF); + OS.DeleteObject (polyRgn); +} + +/** + * 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); + int /*long*/ rectRgn = OS.CreateRectRgn (x, y, x + width, y + height); + OS.CombineRgn (handle, handle, rectRgn, OS.RGN_DIFF); + OS.DeleteObject (rectRgn); +} + +/** + * 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); + OS.CombineRgn (handle, handle, region.handle, OS.RGN_DIFF); +} + +/** + * 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); + OS.OffsetRgn (handle, x, y); +} + +/** + * 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); + translate (pt.x, pt.y); +} + +/** + * 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 + "}"; +} + +/** + * Invokes platform specific functionality to allocate a new region. + * <p> + * <b>IMPORTANT:</b> This method is <em>not</em> part of the public + * API for <code>Region</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 region + * @param handle the handle for the region + * @return a new region object containing the specified device and handle + */ +public static Region win32_new(Device device, int handle) { + return new Region(device, handle); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java new file mode 100644 index 0000000000..d4701a120f --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java @@ -0,0 +1,3328 @@ +/******************************************************************************* + * 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.gdip.*; +import org.eclipse.swt.internal.win32.*; +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 { + Font font; + String text, segmentsText; + int lineSpacing; + int ascent, descent; + int alignment; + int wrapWidth; + int orientation; + int indent; + boolean justify; + int[] tabs; + int[] segments; + StyleItem[] styles; + int stylesCount; + + StyleItem[] allRuns; + StyleItem[][] runs; + int[] lineOffset, lineY, lineWidth; + int /*long*/ mLangFontLink2; + + static final char LTR_MARK = '\u200E', RTL_MARK = '\u200F'; + static final int SCRIPT_VISATTR_SIZEOF = 2; + static final int GOFFSET_SIZEOF = 8; + static final byte[] CLSID_CMultiLanguage = new byte[16]; + static final byte[] IID_IMLangFontLink2 = new byte[16]; + static { + OS.IIDFromString("{275c23e2-3747-11d0-9fea-00aa003f8646}\0".toCharArray(), CLSID_CMultiLanguage); + OS.IIDFromString("{DCCFC162-2B38-11d2-B7EC-00C04F8F5D9A}\0".toCharArray(), IID_IMLangFontLink2); + } + + static final int MERGE_MAX = 512; + static final int TOO_MANY_RUNS = 1024; + + /* IME has a copy of these constants */ + static final int UNDERLINE_IME_DOT = 1 << 16; + static final int UNDERLINE_IME_DASH = 2 << 16; + static final int UNDERLINE_IME_THICK = 3 << 16; + + class StyleItem { + TextStyle style; + int start, length; + boolean lineBreak, softBreak, tab; + + /*Script cache and analysis */ + SCRIPT_ANALYSIS analysis; + int /*long*/ psc = 0; + + /*Shape info (malloc when the run is shaped) */ + int /*long*/ glyphs; + int glyphCount; + int /*long*/ clusters; + int /*long*/ visAttrs; + + /*Place info (malloc when the run is placed) */ + int /*long*/ advances; + int /*long*/ goffsets; + int width; + int ascent; + int descent; + int leading; + int x; + int underlinePos, underlineThickness; + int strikeoutPos, strikeoutThickness; + + /* Justify info (malloc during computeRuns) */ + int /*long*/ justify; + + /* ScriptBreak */ + int /*long*/ psla; + + int /*long*/ fallbackFont; + + void free() { + int /*long*/ hHeap = OS.GetProcessHeap(); + if (psc != 0) { + OS.ScriptFreeCache (psc); + OS.HeapFree(hHeap, 0, psc); + psc = 0; + } + if (glyphs != 0) { + OS.HeapFree(hHeap, 0, glyphs); + glyphs = 0; + glyphCount = 0; + } + if (clusters != 0) { + OS.HeapFree(hHeap, 0, clusters); + clusters = 0; + } + if (visAttrs != 0) { + OS.HeapFree(hHeap, 0, visAttrs); + visAttrs = 0; + } + if (advances != 0) { + OS.HeapFree(hHeap, 0, advances); + advances = 0; + } + if (goffsets != 0) { + OS.HeapFree(hHeap, 0, goffsets); + goffsets = 0; + } + if (justify != 0) { + OS.HeapFree(hHeap, 0, justify); + justify = 0; + } + if (psla != 0) { + OS.HeapFree(hHeap, 0, psla); + psla = 0; + } + if (fallbackFont != 0) { + OS.DeleteObject(fallbackFont); + fallbackFont = 0; + } + width = ascent = descent = x = 0; + lineBreak = softBreak = false; + } + 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; + lineSpacing = 0; + orientation = SWT.LEFT_TO_RIGHT; + styles = new StyleItem[2]; + styles[0] = new StyleItem(); + styles[1] = new StyleItem(); + stylesCount = 2; + text = ""; //$NON-NLS-1$ + int /*long*/[] ppv = new int /*long*/[1]; + OS.OleInitialize(0); + if (OS.CoCreateInstance(CLSID_CMultiLanguage, 0, OS.CLSCTX_INPROC_SERVER, IID_IMLangFontLink2, ppv) == OS.S_OK) { + mLangFontLink2 = ppv[0]; + } + init(); +} + +RECT addClipRect(StyleItem run, RECT clipRect, RECT rect, int selectionStart, int selectionEnd) { + if (rect != null) { + if (clipRect == null) { + clipRect = new RECT (); + OS.SetRect(clipRect, -1, rect.top, -1, rect.bottom); + } + boolean isRTL = (orientation & SWT.RIGHT_TO_LEFT) != 0; + if (run.start <= selectionStart && selectionStart <= run.start + run.length) { + if (run.analysis.fRTL ^ isRTL) { + clipRect.right = rect.left; + } else { + clipRect.left = rect.left; + } + } + if (run.start <= selectionEnd && selectionEnd <= run.start + run.length) { + if (run.analysis.fRTL ^ isRTL) { + clipRect.left = rect.right; + } else { + clipRect.right = rect.right; + } + } + } + return clipRect; +} + +void breakRun(StyleItem run) { + if (run.psla != 0) return; + char[] chars = new char[run.length]; + segmentsText.getChars(run.start, run.start + run.length, chars, 0); + int /*long*/ hHeap = OS.GetProcessHeap(); + run.psla = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, SCRIPT_LOGATTR.sizeof * chars.length); + if (run.psla == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.ScriptBreak(chars, chars.length, run.analysis, run.psla); +} + +void checkLayout () { + if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); +} + +/* +* Compute the runs: itemize, shape, place, and reorder the runs. +* Break paragraphs into lines, wraps the text, and initialize caches. +*/ +void computeRuns (GC gc) { + if (runs != null) return; + int /*long*/ hDC = gc != null ? gc.handle : device.internal_new_GC(null); + int /*long*/ srcHdc = OS.CreateCompatibleDC(hDC); + allRuns = itemize(); + for (int i=0; i<allRuns.length - 1; i++) { + StyleItem run = allRuns[i]; + OS.SelectObject(srcHdc, getItemFont(run)); + shape(srcHdc, run); + } + SCRIPT_LOGATTR logAttr = new SCRIPT_LOGATTR(); + SCRIPT_PROPERTIES properties = new SCRIPT_PROPERTIES(); + int lineWidth = indent, lineStart = 0, lineCount = 1; + for (int i=0; i<allRuns.length - 1; i++) { + StyleItem run = allRuns[i]; + if (tabs != null && run.tab) { + int tabsLength = tabs.length, j; + for (j = 0; j < tabsLength; j++) { + if (tabs[j] > lineWidth) { + run.width = tabs[j] - lineWidth; + break; + } + } + if (j == tabsLength) { + int tabX = tabs[tabsLength-1]; + int lastTabWidth = tabsLength > 1 ? tabs[tabsLength-1] - tabs[tabsLength-2] : tabs[0]; + if (lastTabWidth > 0) { + while (tabX <= lineWidth) tabX += lastTabWidth; + run.width = tabX - lineWidth; + } + } + int length = run.length; + if (length > 1) { + int stop = j + length - 1; + if (stop < tabsLength) { + run.width += tabs[stop] - tabs[j]; + } else { + if (j < tabsLength) { + run.width += tabs[tabsLength - 1] - tabs[j]; + length -= (tabsLength - 1) - j; + } + int lastTabWidth = tabsLength > 1 ? tabs[tabsLength-1] - tabs[tabsLength-2] : tabs[0]; + run.width += lastTabWidth * (length - 1); + } + } + } + if (wrapWidth != -1 && lineWidth + run.width > wrapWidth && !run.tab) { + int start = 0; + int[] piDx = new int[run.length]; + if (run.style != null && run.style.metrics != null) { + piDx[0] = run.width; + } else { + OS.ScriptGetLogicalWidths(run.analysis, run.length, run.glyphCount, run.advances, run.clusters, run.visAttrs, piDx); + } + int width = 0, maxWidth = wrapWidth - lineWidth; + while (width + piDx[start] < maxWidth) { + width += piDx[start++]; + } + int firstStart = start; + int firstIndice = i; + while (i >= lineStart) { + breakRun(run); + while (start >= 0) { + OS.MoveMemory(logAttr, run.psla + (start * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof); + if (logAttr.fSoftBreak || logAttr.fWhiteSpace) break; + start--; + } + + /* + * Bug in Windows. For some reason Uniscribe sets the fSoftBreak flag for the first letter + * after a letter with an accent. This cause a break line to be set in the middle of a word. + * The fix is to detect the case and ignore fSoftBreak forcing the algorithm keep searching. + */ + if (start == 0 && i != lineStart && !run.tab) { + if (logAttr.fSoftBreak && !logAttr.fWhiteSpace) { + OS.MoveMemory(properties, device.scripts[run.analysis.eScript], SCRIPT_PROPERTIES.sizeof); + int langID = properties.langid; + StyleItem pRun = allRuns[i - 1]; + OS.MoveMemory(properties, device.scripts[pRun.analysis.eScript], SCRIPT_PROPERTIES.sizeof); + if (properties.langid == langID || langID == OS.LANG_NEUTRAL || properties.langid == OS.LANG_NEUTRAL) { + breakRun(pRun); + OS.MoveMemory(logAttr, pRun.psla + ((pRun.length - 1) * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof); + if (!logAttr.fWhiteSpace) start = -1; + } + } + } + if (start >= 0 || i == lineStart) break; + run = allRuns[--i]; + start = run.length - 1; + } + if (start == 0 && i != lineStart && !run.tab) { + run = allRuns[--i]; + } else if (start <= 0 && i == lineStart) { + if (lineWidth == wrapWidth && firstIndice > 0) { + i = firstIndice - 1; + run = allRuns[i]; + start = run.length; + } else { + i = firstIndice; + run = allRuns[i]; + start = Math.max(1, firstStart); + } + } + breakRun(run); + while (start < run.length) { + OS.MoveMemory(logAttr, run.psla + (start * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof); + if (!logAttr.fWhiteSpace) break; + start++; + } + if (0 < start && start < run.length) { + StyleItem newRun = new StyleItem(); + newRun.start = run.start + start; + newRun.length = run.length - start; + newRun.style = run.style; + newRun.analysis = cloneScriptAnalysis(run.analysis); + run.free(); + run.length = start; + OS.SelectObject(srcHdc, getItemFont(run)); + run.analysis.fNoGlyphIndex = false; + shape (srcHdc, run); + OS.SelectObject(srcHdc, getItemFont(newRun)); + newRun.analysis.fNoGlyphIndex = false; + shape (srcHdc, newRun); + StyleItem[] newAllRuns = new StyleItem[allRuns.length + 1]; + System.arraycopy(allRuns, 0, newAllRuns, 0, i + 1); + System.arraycopy(allRuns, i + 1, newAllRuns, i + 2, allRuns.length - i - 1); + allRuns = newAllRuns; + allRuns[i + 1] = newRun; + } + if (i != allRuns.length - 2) { + run.softBreak = run.lineBreak = true; + } + } + lineWidth += run.width; + if (run.lineBreak) { + lineStart = i + 1; + lineWidth = run.softBreak ? 0 : indent; + lineCount++; + } + } + lineWidth = 0; + runs = new StyleItem[lineCount][]; + lineOffset = new int[lineCount + 1]; + lineY = new int[lineCount + 1]; + this.lineWidth = new int[lineCount]; + int lineRunCount = 0, line = 0; + int ascent = Math.max(0, this.ascent); + int descent = Math.max(0, this.descent); + StyleItem[] lineRuns = new StyleItem[allRuns.length]; + for (int i=0; i<allRuns.length; i++) { + StyleItem run = allRuns[i]; + lineRuns[lineRunCount++] = run; + lineWidth += run.width; + ascent = Math.max(ascent, run.ascent); + descent = Math.max(descent, run.descent); + if (run.lineBreak || i == allRuns.length - 1) { + /* Update the run metrics if the last run is a hard break. */ + if (lineRunCount == 1 && (i == allRuns.length - 1 || !run.softBreak)) { + TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA(); + OS.SelectObject(srcHdc, getItemFont(run)); + OS.GetTextMetrics(srcHdc, lptm); + run.ascent = lptm.tmAscent; + run.descent = lptm.tmDescent; + ascent = Math.max(ascent, run.ascent); + descent = Math.max(descent, run.descent); + } + runs[line] = new StyleItem[lineRunCount]; + System.arraycopy(lineRuns, 0, runs[line], 0, lineRunCount); + + if (justify && wrapWidth != -1 && run.softBreak && lineWidth > 0) { + if (line == 0) { + lineWidth += indent; + } else { + StyleItem[] previousLine = runs[line - 1]; + StyleItem previousRun = previousLine[previousLine.length - 1]; + if (previousRun.lineBreak && !previousRun.softBreak) { + lineWidth += indent; + } + } + int /*long*/ hHeap = OS.GetProcessHeap(); + int newLineWidth = 0; + for (int j = 0; j < runs[line].length; j++) { + StyleItem item = runs[line][j]; + int iDx = item.width * wrapWidth / lineWidth; + if (iDx != item.width) { + item.justify = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, item.glyphCount * 4); + if (item.justify == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.ScriptJustify(item.visAttrs, item.advances, item.glyphCount, iDx - item.width, 2, item.justify); + item.width = iDx; + } + newLineWidth += item.width; + } + lineWidth = newLineWidth; + } + this.lineWidth[line] = lineWidth; + + StyleItem lastRun = runs[line][lineRunCount - 1]; + int lastOffset = lastRun.start + lastRun.length; + runs[line] = reorder(runs[line], i == allRuns.length - 1); + lastRun = runs[line][lineRunCount - 1]; + if (run.softBreak && run != lastRun) { + run.softBreak = run.lineBreak = false; + lastRun.softBreak = lastRun.lineBreak = true; + } + + lineWidth = getLineIndent(line); + for (int j = 0; j < runs[line].length; j++) { + runs[line][j].x = lineWidth; + lineWidth += runs[line][j].width; + } + line++; + lineY[line] = lineY[line - 1] + ascent + descent + lineSpacing; + lineOffset[line] = lastOffset; + lineRunCount = lineWidth = 0; + ascent = Math.max(0, this.ascent); + descent = Math.max(0, this.descent); + } + } + if (srcHdc != 0) OS.DeleteDC(srcHdc); + if (gc == null) device.internal_dispose_GC(hDC, null); +} + +void destroy () { + freeRuns(); + font = null; + text = null; + segmentsText = null; + tabs = null; + styles = null; + runs = null; + lineOffset = null; + lineY = null; + lineWidth = null; + if (mLangFontLink2 != 0) { + /* Release() */ + OS.VtblCall(2, mLangFontLink2); + mLangFontLink2 = 0; + } + OS.OleUninitialize(); +} + +SCRIPT_ANALYSIS cloneScriptAnalysis (SCRIPT_ANALYSIS src) { + SCRIPT_ANALYSIS dst = new SCRIPT_ANALYSIS(); + dst.eScript = src.eScript; + dst.fRTL = src.fRTL; + dst.fLayoutRTL = src.fLayoutRTL; + dst.fLinkBefore = src.fLinkBefore; + dst.fLinkAfter = src.fLinkAfter; + dst.fLogicalOrder = src.fLogicalOrder; + dst.fNoGlyphIndex = src.fNoGlyphIndex; + dst.s = new SCRIPT_STATE(); + dst.s.uBidiLevel = src.s.uBidiLevel; + dst.s.fOverrideDirection = src.s.fOverrideDirection; + dst.s.fInhibitSymSwap = src.s.fInhibitSymSwap; + dst.s.fCharShape = src.s.fCharShape; + dst.s.fDigitSubstitute = src.s.fDigitSubstitute; + dst.s.fInhibitLigate = src.s.fInhibitLigate; + dst.s.fDisplayZWG = src.s.fDisplayZWG; + dst.s.fArabicNumContext = src.s.fArabicNumContext; + dst.s.fGcpClusters = src.s.fGcpClusters; + dst.s.fReserved = src.s.fReserved; + dst.s.fEngineReserved = src.s.fEngineReserved; + return dst; +} + +int[] 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 int[0]; + + int[] coordinates = new int[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; +} + +int /*long*/ createGdipBrush(int pixel, int alpha) { + int argb = ((alpha & 0xFF) << 24) | ((pixel >> 16) & 0xFF) | (pixel & 0xFF00) | ((pixel & 0xFF) << 16); + int /*long*/ gdiColor = Gdip.Color_new(argb); + int /*long*/ brush = Gdip.SolidBrush_new(gdiColor); + Gdip.Color_delete(gdiColor); + return brush; +} + +int /*long*/ createGdipBrush(Color color, int alpha) { + return createGdipBrush(color.handle, alpha); +} + +/** + * 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(); + computeRuns(gc); + 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); + int length = text.length(); + if (length == 0 && flags == 0) return; + int /*long*/ hdc = gc.handle; + Rectangle clip = gc.getClipping(); + GCData data = gc.data; + int /*long*/ gdipGraphics = data.gdipGraphics; + int foreground = data.foreground; + int linkColor = OS.GetSysColor (OS.COLOR_HOTLIGHT); + int alpha = data.alpha; + boolean gdip = gdipGraphics != 0; + int /*long*/ gdipForeground = 0; + int /*long*/ gdipLinkColor = 0; + int state = 0; + if (gdip) { + gc.checkGC(GC.FOREGROUND); + gdipForeground = gc.getFgBrush(); + } else { + state = OS.SaveDC(hdc); + if ((data.style & SWT.MIRRORED) != 0) { + OS.SetLayout(hdc, OS.GetLayout(hdc) | OS.LAYOUT_RTL); + } + } + boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; + int /*long*/ gdipSelBackground = 0, gdipSelForeground = 0, gdipFont = 0, lastHFont = 0; + int /*long*/ selBackground = 0; + int selForeground = 0; + if (hasSelection || (flags & SWT.LAST_LINE_SELECTION) != 0) { + int fgSel = selectionForeground != null ? selectionForeground.handle : OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT); + int bgSel = selectionBackground != null ? selectionBackground.handle : OS.GetSysColor (OS.COLOR_HIGHLIGHT); + if (gdip) { + gdipSelBackground = createGdipBrush(bgSel, alpha); + gdipSelForeground = createGdipBrush(fgSel, alpha); + } else { + selBackground = OS.CreateSolidBrush(bgSel); + selForeground = fgSel; + } + if (hasSelection) { + selectionStart = translateOffset(Math.min(Math.max(0, selectionStart), length - 1)); + selectionEnd = translateOffset(Math.min(Math.max(0, selectionEnd), length - 1)); + } + } + RECT rect = new RECT(); + OS.SetBkMode(hdc, OS.TRANSPARENT); + for (int line=0; line<runs.length; line++) { + int drawX = x + getLineIndent(line); + int drawY = y + lineY[line]; + StyleItem[] lineRuns = runs[line]; + int lineHeight = lineY[line+1] - lineY[line] - lineSpacing; + + //Draw last line selection + if (flags != 0 && (hasSelection || (flags & SWT.LAST_LINE_SELECTION) != 0)) { + boolean extents = false; + if (line == runs.length - 1 && (flags & SWT.LAST_LINE_SELECTION) != 0) { + extents = true; + } else { + StyleItem run = lineRuns[lineRuns.length - 1]; + if (run.lineBreak && !run.softBreak) { + if (selectionStart <= run.start && run.start <= selectionEnd) extents = true; + } else { + int endOffset = run.start + run.length - 1; + if (selectionStart <= endOffset && endOffset < selectionEnd && (flags & SWT.FULL_SELECTION) != 0) { + extents = true; + } + } + } + if (extents) { + int width; + if ((flags & SWT.FULL_SELECTION) != 0) { + width = OS.IsWin95 ? 0x7FFF : 0x6FFFFFF; + } else { + width = lineHeight / 3; + } + if (gdip) { + Gdip.Graphics_FillRectangle(gdipGraphics, gdipSelBackground, drawX + lineWidth[line], drawY, width, lineHeight); + } else { + OS.SelectObject(hdc, selBackground); + OS.PatBlt(hdc, drawX + lineWidth[line], drawY, width, lineHeight, OS.PATCOPY); + } + } + } + if (drawX > clip.x + clip.width) continue; + if (drawX + lineWidth[line] < clip.x) continue; + + //Draw the background of the runs in the line + int alignmentX = drawX; + for (int i = 0; i < lineRuns.length; i++) { + StyleItem run = lineRuns[i]; + if (run.length == 0) continue; + if (drawX > clip.x + clip.width) break; + if (drawX + run.width >= clip.x) { + if (!run.lineBreak || run.softBreak) { + OS.SetRect(rect, drawX, drawY, drawX + run.width, drawY + lineHeight); + if (gdip) { + drawRunBackgroundGDIP(run, gdipGraphics, rect, selectionStart, selectionEnd, alpha, gdipSelBackground, hasSelection); + } else { + drawRunBackground(run, hdc, rect, selectionStart, selectionEnd, selBackground, hasSelection); + } + } + } + drawX += run.width; + } + + //Draw the text, underline, strikeout, and border of the runs in the line + int baseline = Math.max(0, this.ascent); + int lineUnderlinePos = 0; + for (int i = 0; i < lineRuns.length; i++) { + baseline = Math.max(baseline, lineRuns[i].ascent); + lineUnderlinePos = Math.min(lineUnderlinePos, lineRuns[i].underlinePos); + } + RECT borderClip = null, underlineClip = null, strikeoutClip = null, pRect = null; + drawX = alignmentX; + for (int i = 0; i < lineRuns.length; i++) { + StyleItem run = lineRuns[i]; + TextStyle style = run.style; + boolean hasAdorners = style != null && (style.underline || style.strikeout || style.borderStyle != SWT.NONE); + if (run.length == 0) continue; + if (drawX > clip.x + clip.width && !hasAdorners) break; + if (drawX + run.width >= clip.x || hasAdorners) { + boolean skipTab = run.tab && !hasAdorners; + if (!skipTab && (!run.lineBreak || run.softBreak) && !(style != null && style.metrics != null)) { + OS.SetRect(rect, drawX, drawY, drawX + run.width, drawY + lineHeight); + if (gdip) { + int /*long*/ hFont = getItemFont(run); + if (hFont != lastHFont) { + lastHFont = hFont; + if (gdipFont != 0) Gdip.Font_delete(gdipFont); + gdipFont = Gdip.Font_new(hdc, hFont); + if (gdipFont == 0) SWT.error(SWT.ERROR_NO_HANDLES); + if (!Gdip.Font_IsAvailable(gdipFont)) { + Gdip.Font_delete(gdipFont); + gdipFont = 0; + } + } + int /*long*/ gdipFg = gdipForeground; + if (style != null && style.underline && style.underlineStyle == SWT.UNDERLINE_LINK) { + if (gdipLinkColor == 0) gdipLinkColor = createGdipBrush(linkColor, alpha); + gdipFg = gdipLinkColor; + } + if (gdipFont != 0) { + pRect = drawRunTextGDIP(gdipGraphics, run, rect, gdipFont, baseline, gdipFg, gdipSelForeground, selectionStart, selectionEnd, alpha); + } else { + int fg = style != null && style.underline && style.underlineStyle == SWT.UNDERLINE_LINK ? linkColor : foreground; + pRect = drawRunTextGDIPRaster(gdipGraphics, run, rect, baseline, fg, selForeground, selectionStart, selectionEnd); + } + underlineClip = drawUnderlineGDIP(gdipGraphics, x, drawY + baseline, lineUnderlinePos, drawY + lineHeight, lineRuns, i, gdipFg, gdipSelForeground, underlineClip, pRect, selectionStart, selectionEnd, alpha); + strikeoutClip = drawStrikeoutGDIP(gdipGraphics, x, drawY + baseline, lineRuns, i, gdipFg, gdipSelForeground, strikeoutClip, pRect, selectionStart, selectionEnd, alpha); + borderClip = drawBorderGDIP(gdipGraphics, x, drawY, lineHeight, lineRuns, i, gdipFg, gdipSelForeground, borderClip, pRect, selectionStart, selectionEnd, alpha); + } else { + int fg = style != null && style.underline && style.underlineStyle == SWT.UNDERLINE_LINK ? linkColor : foreground; + pRect = drawRunText(hdc, run, rect, baseline, fg, selForeground, selectionStart, selectionEnd); + underlineClip = drawUnderline(hdc, x, drawY + baseline, lineUnderlinePos, drawY + lineHeight, lineRuns, i, fg, selForeground, underlineClip, pRect, selectionStart, selectionEnd); + strikeoutClip = drawStrikeout(hdc, x, drawY + baseline, lineRuns, i, fg, selForeground, strikeoutClip, pRect, selectionStart, selectionEnd); + borderClip = drawBorder(hdc, x, drawY, lineHeight, lineRuns, i, fg, selForeground, borderClip, pRect, selectionStart, selectionEnd); + } + } + } + drawX += run.width; + } + } + if (gdipSelBackground != 0) Gdip.SolidBrush_delete(gdipSelBackground); + if (gdipSelForeground != 0) Gdip.SolidBrush_delete(gdipSelForeground); + if (gdipLinkColor != 0) Gdip.SolidBrush_delete(gdipLinkColor); + if (gdipFont != 0) Gdip.Font_delete(gdipFont); + if (state != 0) OS.RestoreDC(hdc, state); + if (selBackground != 0) OS.DeleteObject (selBackground); +} + +RECT drawBorder(int /*long*/ hdc, int x, int y, int lineHeight, StyleItem[] line, int index, int color, int selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd) { + StyleItem run = line[index]; + TextStyle style = run.style; + if (style == null) return null; + if (style.borderStyle == SWT.NONE) return null; + clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd); + if (index + 1 >= line.length || !style.isAdherentBorder(line[index + 1].style)) { + int left = run.x; + int start = run.start; + int end = run.start + run.length - 1; + for (int i = index; i > 0 && style.isAdherentBorder(line[i - 1].style); i--) { + left = line[i - 1].x; + start = Math.min(start, line[i - 1].start); + end = Math.max(end, line[i - 1].start + line[i - 1].length - 1); + } + boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; + boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd; + if (style.borderColor != null) { + color = style.borderColor.handle; + clipRect = null; + } else { + if (fullSelection) { + color = selectionColor; + clipRect = null; + } else { + if (style.foreground != null) { + color = style.foreground.handle; + } + } + } + int lineWidth = 1; + int lineStyle = OS.PS_SOLID; + switch (style.borderStyle) { + case SWT.BORDER_SOLID: break; + case SWT.BORDER_DASH: lineStyle = OS.PS_DASH; break; + case SWT.BORDER_DOT: lineStyle = OS.PS_DOT; break; + } + int /*long*/ oldBrush = OS.SelectObject(hdc, OS.GetStockObject(OS.NULL_BRUSH)); + LOGBRUSH logBrush = new LOGBRUSH(); + logBrush.lbStyle = OS.BS_SOLID; + logBrush.lbColor = /*64*/(int)color; + int /*long*/ newPen = OS.ExtCreatePen(lineStyle | OS.PS_GEOMETRIC, Math.max(1, lineWidth), logBrush, 0, null); + int /*long*/ oldPen = OS.SelectObject(hdc, newPen); + OS.Rectangle(hdc, x + left, y, x + run.x + run.width, y + lineHeight); + OS.SelectObject(hdc, oldPen); + OS.DeleteObject(newPen); + if (clipRect != null) { + int state = OS.SaveDC(hdc); + if (clipRect.left == -1) clipRect.left = 0; + if (clipRect.right == -1) clipRect.right = 0x7ffff; + OS.IntersectClipRect(hdc, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); + logBrush.lbColor = /*64*/(int)selectionColor; + int /*long*/ selPen = OS.ExtCreatePen (lineStyle | OS.PS_GEOMETRIC, Math.max(1, lineWidth), logBrush, 0, null); + oldPen = OS.SelectObject(hdc, selPen); + OS.Rectangle(hdc, x + left, y, x + run.x + run.width, y + lineHeight); + OS.RestoreDC(hdc, state); + OS.SelectObject(hdc, oldPen); + OS.DeleteObject(selPen); + } + OS.SelectObject(hdc, oldBrush); + return null; + } + return clipRect; +} + +RECT drawBorderGDIP(int /*long*/ graphics, int x, int y, int lineHeight, StyleItem[] line, int index, int /*long*/ color, int /*long*/ selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, int alpha) { + StyleItem run = line[index]; + TextStyle style = run.style; + if (style == null) return null; + if (style.borderStyle == SWT.NONE) return null; + clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd); + if (index + 1 >= line.length || !style.isAdherentBorder(line[index + 1].style)) { + int left = run.x; + int start = run.start; + int end = run.start + run.length - 1; + for (int i = index; i > 0 && style.isAdherentBorder(line[i - 1].style); i--) { + left = line[i - 1].x; + start = Math.min(start, line[i - 1].start); + end = Math.max(end, line[i - 1].start + line[i - 1].length - 1); + } + boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; + boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd; + int /*long*/ brush = color; + if (style.borderColor != null) { + brush = createGdipBrush(style.borderColor, alpha); + clipRect = null; + } else { + if (fullSelection) { + brush = selectionColor; + clipRect = null; + } else { + if (style.foreground != null) { + brush = createGdipBrush(style.foreground, alpha); + } + } + } + int lineWidth = 1; + int lineStyle = Gdip.DashStyleSolid; + switch (style.borderStyle) { + case SWT.BORDER_SOLID: break; + case SWT.BORDER_DASH: lineStyle = Gdip.DashStyleDash; break; + case SWT.BORDER_DOT: lineStyle = Gdip.DashStyleDot; break; + } + int /*long*/ pen = Gdip.Pen_new(brush, lineWidth); + Gdip.Pen_SetDashStyle(pen, lineStyle); + Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeNone); + if (clipRect != null) { + int gstate = Gdip.Graphics_Save(graphics); + if (clipRect.left == -1) clipRect.left = 0; + if (clipRect.right == -1) clipRect.right = 0x7ffff; + Rect gdipRect = new Rect(); + gdipRect.X = clipRect.left; + gdipRect.Y = clipRect.top; + gdipRect.Width = clipRect.right - clipRect.left; + gdipRect.Height = clipRect.bottom - clipRect.top; + Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude); + Gdip.Graphics_DrawRectangle(graphics, pen, x + left, y, run.x + run.width - left - 1, lineHeight - 1); + Gdip.Graphics_Restore(graphics, gstate); + gstate = Gdip.Graphics_Save(graphics); + Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect); + int /*long*/ selPen = Gdip.Pen_new(selectionColor, lineWidth); + Gdip.Pen_SetDashStyle(selPen, lineStyle); + Gdip.Graphics_DrawRectangle(graphics, selPen, x + left, y, run.x + run.width - left - 1, lineHeight - 1); + Gdip.Pen_delete(selPen); + Gdip.Graphics_Restore(graphics, gstate); + } else { + Gdip.Graphics_DrawRectangle(graphics, pen, x + left, y, run.x + run.width - left - 1, lineHeight - 1); + } + Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeHalf); + Gdip.Pen_delete(pen); + if (brush != selectionColor && brush != color) Gdip.SolidBrush_delete(brush); + return null; + } + return clipRect; +} + +void drawRunBackground(StyleItem run, int /*long*/ hdc, RECT rect, int selectionStart, int selectionEnd, int /*long*/ selBrush, boolean hasSelection) { + int end = run.start + run.length - 1; + boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end; + if (fullSelection) { + OS.SelectObject(hdc, selBrush); + OS.PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY); + } else { + if (run.style != null && run.style.background != null) { + int bg = run.style.background.handle; + int /*long*/ hBrush = OS.CreateSolidBrush (bg); + int /*long*/ oldBrush = OS.SelectObject(hdc, hBrush); + OS.PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY); + OS.SelectObject(hdc, oldBrush); + OS.DeleteObject(hBrush); + } + boolean partialSelection = hasSelection && !(selectionStart > end || run.start > selectionEnd); + if (partialSelection) { + getPartialSelection(run, selectionStart, selectionEnd, rect); + OS.SelectObject(hdc, selBrush); + OS.PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY); + } + } +} + +void drawRunBackgroundGDIP(StyleItem run, int /*long*/ graphics, RECT rect, int selectionStart, int selectionEnd, int alpha, int /*long*/ selBrush, boolean hasSelection) { + int end = run.start + run.length - 1; + boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end; + if (fullSelection) { + Gdip.Graphics_FillRectangle(graphics, selBrush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); + } else { + if (run.style != null && run.style.background != null) { + int /*long*/ brush = createGdipBrush(run.style.background, alpha); + Gdip.Graphics_FillRectangle(graphics, brush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); + Gdip.SolidBrush_delete(brush); + } + boolean partialSelection = hasSelection && !(selectionStart > end || run.start > selectionEnd); + if (partialSelection) { + getPartialSelection(run, selectionStart, selectionEnd, rect); + if (rect.left > rect.right) { + int tmp = rect.left; + rect.left = rect.right; + rect.right = tmp; + } + Gdip.Graphics_FillRectangle(graphics, selBrush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); + } + } +} + +RECT drawRunText(int /*long*/ hdc, StyleItem run, RECT rect, int baseline, int color, int selectionColor, int selectionStart, int selectionEnd) { + int end = run.start + run.length - 1; + boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; + boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end; + boolean partialSelection = hasSelection && !fullSelection && !(selectionStart > end || run.start > selectionEnd); + int offset = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? -1 : 0; + int x = rect.left + offset; + int y = rect.top + (baseline - run.ascent); + int /*long*/ hFont = getItemFont(run); + OS.SelectObject(hdc, hFont); + if (fullSelection) { + color = selectionColor; + } else { + if (run.style != null && run.style.foreground != null) { + color = run.style.foreground.handle; + } + } + OS.SetTextColor(hdc, color); + OS.ScriptTextOut(hdc, run.psc, x, y, 0, null, run.analysis , 0, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets); + if (partialSelection) { + getPartialSelection(run, selectionStart, selectionEnd, rect); + OS.SetTextColor(hdc, selectionColor); + OS.ScriptTextOut(hdc, run.psc, x, y, OS.ETO_CLIPPED, rect, run.analysis , 0, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets); + } + return fullSelection || partialSelection ? rect : null; +} + +RECT drawRunTextGDIP(int /*long*/ graphics, StyleItem run, RECT rect, int /*long*/ gdipFont, int baseline, int /*long*/ color, int /*long*/ selectionColor, int selectionStart, int selectionEnd, int alpha) { + int end = run.start + run.length - 1; + boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; + boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end; + boolean partialSelection = hasSelection && !fullSelection && !(selectionStart > end || run.start > selectionEnd); + int drawY = rect.top + baseline; + int drawX = rect.left; + int /*long*/ brush = color; + if (fullSelection) { + brush = selectionColor; + } else { + if (run.style != null && run.style.foreground != null) { + brush = createGdipBrush(run.style.foreground, alpha); + } + } + int gstate = 0; + Rect gdipRect = null; + if (partialSelection) { + gdipRect = new Rect(); + getPartialSelection(run, selectionStart, selectionEnd, rect); + gdipRect.X = rect.left; + gdipRect.Y = rect.top; + gdipRect.Width = rect.right - rect.left; + gdipRect.Height = rect.bottom - rect.top; + gstate = Gdip.Graphics_Save(graphics); + Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude); + } + int gstateMirrored = 0; + boolean isMirrored = (orientation & SWT.RIGHT_TO_LEFT) != 0; + if (isMirrored) { + switch (Gdip.Brush_GetType(brush)) { + case Gdip.BrushTypeLinearGradient: + Gdip.LinearGradientBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend); + Gdip.LinearGradientBrush_TranslateTransform(brush, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend); + break; + case Gdip.BrushTypeTextureFill: + Gdip.TextureBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend); + Gdip.TextureBrush_TranslateTransform(brush, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend); + break; + } + gstateMirrored = Gdip.Graphics_Save(graphics); + Gdip.Graphics_ScaleTransform(graphics, -1, 1, Gdip.MatrixOrderPrepend); + Gdip.Graphics_TranslateTransform(graphics, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend); + } + int[] advances = new int[run.glyphCount]; + float[] points = new float[run.glyphCount * 2]; + OS.memmove(advances, run.justify != 0 ? run.justify : run.advances, run.glyphCount * 4); + int glyphX = drawX; + for (int h = 0, j = 0; h < advances.length; h++) { + points[j++] = glyphX; + points[j++] = drawY; + glyphX += advances[h]; + } + Gdip.Graphics_DrawDriverString(graphics, run.glyphs, run.glyphCount, gdipFont, brush, points, 0, 0); + if (partialSelection) { + if (isMirrored) { + Gdip.Graphics_Restore(graphics, gstateMirrored); + } + Gdip.Graphics_Restore(graphics, gstate); + gstate = Gdip.Graphics_Save(graphics); + Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect); + if (isMirrored) { + gstateMirrored = Gdip.Graphics_Save(graphics); + Gdip.Graphics_ScaleTransform(graphics, -1, 1, Gdip.MatrixOrderPrepend); + Gdip.Graphics_TranslateTransform(graphics, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend); + } + Gdip.Graphics_DrawDriverString(graphics, run.glyphs, run.glyphCount, gdipFont, selectionColor, points, 0, 0); + Gdip.Graphics_Restore(graphics, gstate); + } + if (isMirrored) { + switch (Gdip.Brush_GetType(brush)) { + case Gdip.BrushTypeLinearGradient: + Gdip.LinearGradientBrush_ResetTransform(brush); + break; + case Gdip.BrushTypeTextureFill: + Gdip.TextureBrush_ResetTransform(brush); + break; + } + Gdip.Graphics_Restore(graphics, gstateMirrored); + } + if (brush != selectionColor && brush != color) Gdip.SolidBrush_delete(brush); + return fullSelection || partialSelection ? rect : null; +} + +RECT drawRunTextGDIPRaster(int /*long*/ graphics, StyleItem run, RECT rect, int baseline, int color, int selectionColor, int selectionStart, int selectionEnd) { + int /*long*/ clipRgn = 0; + Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeNone); + int /*long*/ rgn = Gdip.Region_new(); + if (rgn == 0) SWT.error(SWT.ERROR_NO_HANDLES); + Gdip.Graphics_GetClip(graphics, rgn); + if (!Gdip.Region_IsInfinite(rgn, graphics)) { + clipRgn = Gdip.Region_GetHRGN(rgn, graphics); + } + Gdip.Region_delete(rgn); + Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeHalf); + float[] lpXform = null; + int /*long*/ matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0); + if (matrix == 0) SWT.error(SWT.ERROR_NO_HANDLES); + Gdip.Graphics_GetTransform(graphics, matrix); + if (!Gdip.Matrix_IsIdentity(matrix)) { + lpXform = new float[6]; + Gdip.Matrix_GetElements(matrix, lpXform); + } + Gdip.Matrix_delete(matrix); + int /*long*/ hdc = Gdip.Graphics_GetHDC(graphics); + int state = OS.SaveDC(hdc); + if (lpXform != null) { + OS.SetGraphicsMode(hdc, OS.GM_ADVANCED); + OS.SetWorldTransform(hdc, lpXform); + } + if (clipRgn != 0) { + OS.SelectClipRgn(hdc, clipRgn); + OS.DeleteObject(clipRgn); + } + if ((orientation & SWT.RIGHT_TO_LEFT) != 0) { + OS.SetLayout(hdc, OS.GetLayout(hdc) | OS.LAYOUT_RTL); + } + OS.SetBkMode(hdc, OS.TRANSPARENT); + RECT pRect = drawRunText(hdc, run, rect, baseline, color, selectionColor, selectionStart, selectionEnd); + OS.RestoreDC(hdc, state); + Gdip.Graphics_ReleaseHDC(graphics, hdc); + return pRect; +} + +RECT drawStrikeout(int /*long*/ hdc, int x, int baseline, StyleItem[] line, int index, int color, int selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd) { + StyleItem run = line[index]; + TextStyle style = run.style; + if (style == null) return null; + if (!style.strikeout) return null; + clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd); + if (index + 1 >= line.length || !style.isAdherentStrikeout(line[index + 1].style)) { + int left = run.x; + int start = run.start; + int end = run.start + run.length - 1; + for (int i = index; i > 0 && style.isAdherentStrikeout(line[i - 1].style); i--) { + left = line[i - 1].x; + start = Math.min(start, line[i - 1].start); + end = Math.max(end, line[i - 1].start + line[i - 1].length - 1); + } + boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; + boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd; + if (style.strikeoutColor != null) { + color = style.strikeoutColor.handle; + clipRect = null; + } else { + if (fullSelection) { + color = selectionColor; + clipRect = null; + } else { + if (style.foreground != null) { + color = style.foreground.handle; + } + } + } + RECT rect = new RECT(); + OS.SetRect(rect, x + left, baseline - run.strikeoutPos, x + run.x + run.width, baseline - run.strikeoutPos + run.strikeoutThickness); + int /*long*/ brush = OS.CreateSolidBrush(color); + OS.FillRect(hdc, rect, brush); + OS.DeleteObject(brush); + if (clipRect != null) { + int /*long*/ selBrush = OS.CreateSolidBrush(selectionColor); + if (clipRect.left == -1) clipRect.left = 0; + if (clipRect.right == -1) clipRect.right = 0x7ffff; + OS.SetRect(clipRect, Math.max(rect.left, clipRect.left), rect.top, Math.min(rect.right, clipRect.right), rect.bottom); + OS.FillRect(hdc, clipRect, selBrush); + OS.DeleteObject(selBrush); + } + return null; + } + return clipRect; +} + +RECT drawStrikeoutGDIP(int /*long*/ graphics, int x, int baseline, StyleItem[] line, int index, int /*long*/ color, int /*long*/ selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, int alpha) { + StyleItem run = line[index]; + TextStyle style = run.style; + if (style == null) return null; + if (!style.strikeout) return null; + clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd); + if (index + 1 >= line.length || !style.isAdherentStrikeout(line[index + 1].style)) { + int left = run.x; + int start = run.start; + int end = run.start + run.length - 1; + for (int i = index; i > 0 && style.isAdherentStrikeout(line[i - 1].style); i--) { + left = line[i - 1].x; + start = Math.min(start, line[i - 1].start); + end = Math.max(end, line[i - 1].start + line[i - 1].length - 1); + } + boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; + boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd; + int /*long*/ brush = color; + if (style.strikeoutColor != null) { + brush = createGdipBrush(style.strikeoutColor, alpha); + clipRect = null; + } else { + if (fullSelection) { + color = selectionColor; + clipRect = null; + } else { + if (style.foreground != null) { + brush = createGdipBrush(style.foreground, alpha); + } + } + } + if (clipRect != null) { + int gstate = Gdip.Graphics_Save(graphics); + if (clipRect.left == -1) clipRect.left = 0; + if (clipRect.right == -1) clipRect.right = 0x7ffff; + Rect gdipRect = new Rect(); + gdipRect.X = clipRect.left; + gdipRect.Y = clipRect.top; + gdipRect.Width = clipRect.right - clipRect.left; + gdipRect.Height = clipRect.bottom - clipRect.top; + Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude); + Gdip.Graphics_FillRectangle(graphics, brush, x + left, baseline - run.strikeoutPos, run.x + run.width - left, run.strikeoutThickness); + Gdip.Graphics_Restore(graphics, gstate); + gstate = Gdip.Graphics_Save(graphics); + Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect); + Gdip.Graphics_FillRectangle(graphics, selectionColor, x + left, baseline - run.strikeoutPos, run.x + run.width - left, run.strikeoutThickness); + Gdip.Graphics_Restore(graphics, gstate); + } else { + Gdip.Graphics_FillRectangle(graphics, brush, x + left, baseline - run.strikeoutPos, run.x + run.width - left, run.strikeoutThickness); + } + if (brush != selectionColor && brush != color) Gdip.SolidBrush_delete(brush); + return null; + } + return clipRect; +} + +RECT drawUnderline(int /*long*/ hdc, int x, int baseline, int lineUnderlinePos, int lineBottom, StyleItem[] line, int index, int color, int selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd) { + StyleItem run = line[index]; + TextStyle style = run.style; + if (style == null) return null; + if (!style.underline) return null; + clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd); + if (index + 1 >= line.length || !style.isAdherentUnderline(line[index + 1].style)) { + int left = run.x; + int start = run.start; + int end = run.start + run.length - 1; + for (int i = index; i > 0 && style.isAdherentUnderline(line[i - 1].style); i--) { + left = line[i - 1].x; + start = Math.min(start, line[i - 1].start); + end = Math.max(end, line[i - 1].start + line[i - 1].length - 1); + } + boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; + boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd; + if (style.underlineColor != null) { + color = style.underlineColor.handle; + clipRect = null; + } else { + if (fullSelection) { + color = selectionColor; + clipRect = null; + } else { + if (style.foreground != null) { + color = style.foreground.handle; + } + } + } + RECT rect = new RECT(); + OS.SetRect(rect, x + left, baseline - lineUnderlinePos, x + run.x + run.width, baseline - lineUnderlinePos + run.underlineThickness); + if (clipRect != null) { + if (clipRect.left == -1) clipRect.left = 0; + if (clipRect.right == -1) clipRect.right = 0x7ffff; + OS.SetRect(clipRect, Math.max(rect.left, clipRect.left), rect.top, Math.min(rect.right, clipRect.right), rect.bottom); + } + switch (style.underlineStyle) { + case SWT.UNDERLINE_SQUIGGLE: + case SWT.UNDERLINE_ERROR: { + int squigglyThickness = 1; + int squigglyHeight = 2 * squigglyThickness; + int squigglyY = Math.min(rect.top - squigglyHeight / 2, lineBottom - squigglyHeight - 1); + int[] points = computePolyline(rect.left, squigglyY, rect.right, squigglyY + squigglyHeight); + int /*long*/ pen = OS.CreatePen(OS.PS_SOLID, squigglyThickness, color); + int /*long*/ oldPen = OS.SelectObject(hdc, pen); + int state = OS.SaveDC(hdc); + OS.IntersectClipRect(hdc, rect.left, squigglyY, rect.right + 1, squigglyY + squigglyHeight + 1); + OS.Polyline(hdc, points, points.length / 2); + int length = points.length; + if (length >= 2 && squigglyThickness <= 1) { + OS.SetPixel (hdc, points[length - 2], points[length - 1], color); + } + OS.SelectObject(hdc, oldPen); + OS.DeleteObject(pen); + OS.RestoreDC(hdc, state); + if (clipRect != null) { + pen = OS.CreatePen(OS.PS_SOLID, squigglyThickness, selectionColor); + oldPen = OS.SelectObject(hdc, pen); + state = OS.SaveDC(hdc); + OS.IntersectClipRect(hdc, clipRect.left, squigglyY, clipRect.right + 1, squigglyY + squigglyHeight + 1); + OS.Polyline(hdc, points, points.length / 2); + if (length >= 2 && squigglyThickness <= 1) { + OS.SetPixel (hdc, points[length - 2], points[length - 1], selectionColor); + } + OS.SelectObject(hdc, oldPen); + OS.DeleteObject(pen); + OS.RestoreDC(hdc, state); + } + break; + } + case SWT.UNDERLINE_SINGLE: + case SWT.UNDERLINE_DOUBLE: + case SWT.UNDERLINE_LINK: + case UNDERLINE_IME_THICK: + if (style.underlineStyle == UNDERLINE_IME_THICK) { + rect.top -= run.underlineThickness; + if (clipRect != null) clipRect.top -= run.underlineThickness; + } + int bottom = style.underlineStyle == SWT.UNDERLINE_DOUBLE ? rect.bottom + run.underlineThickness * 2 : rect.bottom; + if (bottom > lineBottom) { + OS.OffsetRect(rect, 0, lineBottom - bottom); + if (clipRect != null) OS.OffsetRect(clipRect, 0, lineBottom - bottom); + } + int /*long*/ brush = OS.CreateSolidBrush(color); + OS.FillRect(hdc, rect, brush); + if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) { + OS.SetRect(rect, rect.left, rect.top + run.underlineThickness * 2, rect.right, rect.bottom + run.underlineThickness * 2); + OS.FillRect(hdc, rect, brush); + } + OS.DeleteObject(brush); + if (clipRect != null) { + int /*long*/ selBrush = OS.CreateSolidBrush(selectionColor); + OS.FillRect(hdc, clipRect, selBrush); + if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) { + OS.SetRect(clipRect, clipRect.left, rect.top, clipRect.right, rect.bottom); + OS.FillRect(hdc, clipRect, selBrush); + } + OS.DeleteObject(selBrush); + } + break; + case UNDERLINE_IME_DASH: + case UNDERLINE_IME_DOT: { + int penStyle = style.underlineStyle == UNDERLINE_IME_DASH ? OS.PS_DASH : OS.PS_DOT; + int /*long*/ pen = OS.CreatePen(penStyle, 1, color); + int /*long*/ oldPen = OS.SelectObject(hdc, pen); + OS.SetRect(rect, rect.left, baseline + run.descent, rect.right, baseline + run.descent + run.underlineThickness); + OS.MoveToEx(hdc, rect.left, rect.top, 0); + OS.LineTo(hdc, rect.right, rect.top); + OS.SelectObject(hdc, oldPen); + OS.DeleteObject(pen); + if (clipRect != null) { + pen = OS.CreatePen(penStyle, 1, selectionColor); + oldPen = OS.SelectObject(hdc, pen); + OS.SetRect(clipRect, clipRect.left, rect.top, clipRect.right, rect.bottom); + OS.MoveToEx(hdc, clipRect.left, clipRect.top, 0); + OS.LineTo(hdc, clipRect.right, clipRect.top); + OS.SelectObject(hdc, oldPen); + OS.DeleteObject(pen); + } + break; + } + } + return null; + } + return clipRect; +} + +RECT drawUnderlineGDIP (int /*long*/ graphics, int x, int baseline, int lineUnderlinePos, int lineBottom, StyleItem[] line, int index, int /*long*/ color, int /*long*/ selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, int alpha) { + StyleItem run = line[index]; + TextStyle style = run.style; + if (style == null) return null; + if (!style.underline) return null; + clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd); + if (index + 1 >= line.length || !style.isAdherentUnderline(line[index + 1].style)) { + int left = run.x; + int start = run.start; + int end = run.start + run.length - 1; + for (int i = index; i > 0 && style.isAdherentUnderline(line[i - 1].style); i--) { + left = line[i - 1].x; + start = Math.min(start, line[i - 1].start); + end = Math.max(end, line[i - 1].start + line[i - 1].length - 1); + } + boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; + boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd; + int /*long*/ brush = color; + if (style.underlineColor != null) { + brush = createGdipBrush(style.underlineColor, alpha); + clipRect = null; + } else { + if (fullSelection) { + brush = selectionColor; + clipRect = null; + } else { + if (style.foreground != null) { + brush = createGdipBrush(style.foreground, alpha); + } + } + } + RECT rect = new RECT(); + OS.SetRect(rect, x + left, baseline - lineUnderlinePos, x + run.x + run.width, baseline - lineUnderlinePos + run.underlineThickness); + Rect gdipRect = null; + if (clipRect != null) { + if (clipRect.left == -1) clipRect.left = 0; + if (clipRect.right == -1) clipRect.right = 0x7ffff; + OS.SetRect(clipRect, Math.max(rect.left, clipRect.left), rect.top, Math.min(rect.right, clipRect.right), rect.bottom); + gdipRect = new Rect(); + gdipRect.X = clipRect.left; + gdipRect.Y = clipRect.top; + gdipRect.Width = clipRect.right - clipRect.left; + gdipRect.Height = clipRect.bottom - clipRect.top; + } + int gstate = 0; + Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeNone); + switch (style.underlineStyle) { + case SWT.UNDERLINE_SQUIGGLE: + case SWT.UNDERLINE_ERROR: { + int squigglyThickness = 1; + int squigglyHeight = 2 * squigglyThickness; + int squigglyY = Math.min(rect.top - squigglyHeight / 2, lineBottom - squigglyHeight - 1); + int[] points = computePolyline(rect.left, squigglyY, rect.right, squigglyY + squigglyHeight); + int /*long*/ pen = Gdip.Pen_new(brush, squigglyThickness); + gstate = Gdip.Graphics_Save(graphics); + if (gdipRect != null) { + Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude); + } else { + Rect r = new Rect(); + r.X = rect.left; + r.Y = squigglyY; + r.Width = rect.right - rect.left; + r.Height = squigglyHeight + 1; + Gdip.Graphics_SetClip(graphics, r, Gdip.CombineModeIntersect); + } + Gdip.Graphics_DrawLines(graphics, pen, points, points.length / 2); + if (gdipRect != null) { + int /*long*/ selPen = Gdip.Pen_new(selectionColor, squigglyThickness); + Gdip.Graphics_Restore(graphics, gstate); + gstate = Gdip.Graphics_Save(graphics); + Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect); + Gdip.Graphics_DrawLines(graphics, selPen, points, points.length / 2); + Gdip.Pen_delete(selPen); + } + Gdip.Graphics_Restore(graphics, gstate); + Gdip.Pen_delete(pen); + if (gstate != 0) Gdip.Graphics_Restore(graphics, gstate); + break; + } + case SWT.UNDERLINE_SINGLE: + case SWT.UNDERLINE_DOUBLE: + case SWT.UNDERLINE_LINK: + case UNDERLINE_IME_THICK: + if (style.underlineStyle == UNDERLINE_IME_THICK) { + rect.top -= run.underlineThickness; + } + int bottom = style.underlineStyle == SWT.UNDERLINE_DOUBLE ? rect.bottom + run.underlineThickness * 2 : rect.bottom; + if (bottom > lineBottom) { + OS.OffsetRect(rect, 0, lineBottom - bottom); + } + if (gdipRect != null) { + gdipRect.Y = rect.top; + if (style.underlineStyle == UNDERLINE_IME_THICK) { + gdipRect.Height = run.underlineThickness * 2; + } + if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) { + gdipRect.Height = run.underlineThickness * 3; + } + gstate = Gdip.Graphics_Save(graphics); + Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude); + } + Gdip.Graphics_FillRectangle(graphics, brush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); + if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) { + Gdip.Graphics_FillRectangle(graphics, brush, rect.left, rect.top + run.underlineThickness * 2, rect.right - rect.left, rect.bottom - rect.top); + } + if (gdipRect != null) { + Gdip.Graphics_Restore(graphics, gstate); + gstate = Gdip.Graphics_Save(graphics); + Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect); + Gdip.Graphics_FillRectangle(graphics, selectionColor, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); + if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) { + Gdip.Graphics_FillRectangle(graphics, selectionColor, rect.left, rect.top + run.underlineThickness * 2, rect.right - rect.left, rect.bottom - rect.top); + } + Gdip.Graphics_Restore(graphics, gstate); + } + break; + case UNDERLINE_IME_DOT: + case UNDERLINE_IME_DASH: { + int /*long*/ pen = Gdip.Pen_new(brush, 1); + int dashStyle = style.underlineStyle == UNDERLINE_IME_DOT ? Gdip.DashStyleDot : Gdip.DashStyleDash; + Gdip.Pen_SetDashStyle(pen, dashStyle); + if (gdipRect != null) { + gstate = Gdip.Graphics_Save(graphics); + Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude); + } + Gdip.Graphics_DrawLine(graphics, pen, rect.left, baseline + run.descent, run.width - run.length, baseline + run.descent); + if (gdipRect != null) { + Gdip.Graphics_Restore(graphics, gstate); + gstate = Gdip.Graphics_Save(graphics); + Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect); + int /*long*/ selPen = Gdip.Pen_new(brush, 1); + Gdip.Pen_SetDashStyle(selPen, dashStyle); + Gdip.Graphics_DrawLine(graphics, selPen, rect.left, baseline + run.descent, run.width - run.length, baseline + run.descent); + Gdip.Graphics_Restore(graphics, gstate); + Gdip.Pen_delete(selPen); + } + Gdip.Pen_delete(pen); + break; + } + } + if (brush != selectionColor && brush != color) Gdip.SolidBrush_delete(brush); + Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeHalf); + return null; + } + return clipRect; +} + +void freeRuns () { + if (allRuns == null) return; + for (int i=0; i<allRuns.length; i++) { + StyleItem run = allRuns[i]; + run.free(); + } + allRuns = null; + runs = null; + segmentsText = 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(); + computeRuns(null); + int width = 0; + if (wrapWidth != -1) { + width = wrapWidth; + } else { + for (int line=0; line<runs.length; line++) { + width = Math.max(width, lineWidth[line] + getLineIndent(line)); + } + } + return new Rectangle (0, 0, width, lineY[lineY.length - 1]); +} + +/** + * 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(); + computeRuns(null); + 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); + int left = 0x7fffffff, right = 0; + int top = 0x7fffffff, bottom = 0; + boolean isRTL = (orientation & SWT.RIGHT_TO_LEFT) != 0; + for (int i = 0; i < allRuns.length - 1; i++) { + StyleItem run = allRuns[i]; + int runEnd = run.start + run.length; + if (runEnd <= start) continue; + if (run.start > end) break; + int runLead = run.x; + int runTrail = run.x + run.width; + if (run.start <= start && start < runEnd) { + int cx = 0; + if (run.style != null && run.style.metrics != null) { + GlyphMetrics metrics = run.style.metrics; + cx = metrics.width * (start - run.start); + } else if (!run.tab) { + int[] piX = new int[1]; + int /*long*/ advances = run.justify != 0 ? run.justify : run.advances; + OS.ScriptCPtoX(start - run.start, false, run.length, run.glyphCount, run.clusters, run.visAttrs, advances, run.analysis, piX); + cx = isRTL ? run.width - piX[0] : piX[0]; + } + if (run.analysis.fRTL ^ isRTL) { + runTrail = run.x + cx; + } else { + runLead = run.x + cx; + } + } + if (run.start <= end && end < runEnd) { + int cx = run.width; + if (run.style != null && run.style.metrics != null) { + GlyphMetrics metrics = run.style.metrics; + cx = metrics.width * (end - run.start + 1); + } else if (!run.tab) { + int[] piX = new int[1]; + int /*long*/ advances = run.justify != 0 ? run.justify : run.advances; + OS.ScriptCPtoX(end - run.start, true, run.length, run.glyphCount, run.clusters, run.visAttrs, advances, run.analysis, piX); + cx = isRTL ? run.width - piX[0] : piX[0]; + } + if (run.analysis.fRTL ^ isRTL) { + runLead = run.x + cx; + } else { + runTrail = run.x + cx; + } + } + int lineIndex = 0; + while (lineIndex < runs.length && lineOffset[lineIndex + 1] <= run.start) { + lineIndex++; + } + left = Math.min(left, runLead); + right = Math.max(right, runTrail); + top = Math.min(top, lineY[lineIndex]); + bottom = Math.max(bottom, lineY[lineIndex + 1] - lineSpacing); + } + return new Rectangle(left, top, right - left, bottom - top); +} + +/** + * 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; +} + +int /*long*/ getItemFont (StyleItem item) { + if (item.fallbackFont != 0) return item.fallbackFont; + if (item.style != null && item.style.font != null) { + return item.style.font.handle; + } + if (this.font != null) { + return this.font.handle; + } + return device.systemFont.handle; +} + +/** + * 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(); + computeRuns(null); + int length = text.length(); + if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE); + offset = translateOffset(offset); + for (int i=1; i<allRuns.length; i++) { + if (allRuns[i].start > offset) { + return allRuns[i - 1].analysis.s.uBidiLevel; + } + } + return (orientation & SWT.RIGHT_TO_LEFT) != 0 ? 1 : 0; +} + +/** + * 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(); + computeRuns(null); + if (!(0 <= lineIndex && lineIndex < runs.length)) SWT.error(SWT.ERROR_INVALID_RANGE); + int x = getLineIndent(lineIndex); + int y = lineY[lineIndex]; + int width = lineWidth[lineIndex]; + int height = lineY[lineIndex + 1] - y - lineSpacing; + return new Rectangle (x, y, width, height); +} + +/** + * 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(); + computeRuns(null); + return runs.length; +} + +int getLineIndent (int lineIndex) { + int lineIndent = 0; + if (lineIndex == 0) { + lineIndent = indent; + } else { + StyleItem[] previousLine = runs[lineIndex - 1]; + StyleItem previousRun = previousLine[previousLine.length - 1]; + if (previousRun.lineBreak && !previousRun.softBreak) { + lineIndent = indent; + } + } + if (wrapWidth != -1) { + boolean partialLine = true; + if (justify) { + StyleItem[] lineRun = runs[lineIndex]; + if (lineRun[lineRun.length - 1].softBreak) { + partialLine = false; + } + } + if (partialLine) { + int lineWidth = this.lineWidth[lineIndex] + lineIndent; + switch (alignment) { + case SWT.CENTER: lineIndent += (wrapWidth - lineWidth) / 2; break; + case SWT.RIGHT: lineIndent += wrapWidth - lineWidth; break; + } + } + } + return lineIndent; +} + +/** + * 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(); + computeRuns(null); + int length = text.length(); + if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE); + offset = translateOffset(offset); + for (int line=0; line<runs.length; line++) { + if (lineOffset[line + 1] > offset) { + return line; + } + } + return runs.length - 1; +} + +/** + * 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(); + computeRuns(null); + if (!(0 <= lineIndex && lineIndex < runs.length)) SWT.error(SWT.ERROR_INVALID_RANGE); + int /*long*/ hDC = device.internal_new_GC(null); + int /*long*/ srcHdc = OS.CreateCompatibleDC(hDC); + TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA(); + OS.SelectObject(srcHdc, font != null ? font.handle : device.systemFont.handle); + OS.GetTextMetrics(srcHdc, lptm); + OS.DeleteDC(srcHdc); + device.internal_dispose_GC(hDC, null); + + int ascent = Math.max(lptm.tmAscent, this.ascent); + int descent = Math.max(lptm.tmDescent, this.descent); + int leading = lptm.tmInternalLeading; + if (text.length() != 0) { + StyleItem[] lineRuns = runs[lineIndex]; + for (int i = 0; i<lineRuns.length; i++) { + StyleItem run = lineRuns[i]; + if (run.ascent > ascent) { + ascent = run.ascent; + leading = run.leading; + } + descent = Math.max(descent, run.descent); + } + } + lptm.tmAscent = ascent; + lptm.tmDescent = descent; + lptm.tmHeight = ascent + descent; + lptm.tmInternalLeading = leading; + lptm.tmAveCharWidth = 0; + return FontMetrics.win32_new(lptm); +} + +/** + * 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(); + computeRuns(null); + int[] offsets = new int[lineOffset.length]; + for (int i = 0; i < offsets.length; i++) { + offsets[i] = untranslateOffset(lineOffset[i]); + } + return offsets; +} + +/** + * 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(); + computeRuns(null); + int length = text.length(); + if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE); + length = segmentsText.length(); + offset = translateOffset(offset); + int line; + for (line=0; line<runs.length; line++) { + if (lineOffset[line + 1] > offset) break; + } + line = Math.min(line, runs.length - 1); + if (offset == length) { + return new Point(getLineIndent(line) + lineWidth[line], lineY[line]); + } + int low = -1; + int high = allRuns.length; + while (high - low > 1) { + int index = ((high + low) / 2); + StyleItem run = allRuns[index]; + if (run.start > offset) { + high = index; + } else if (run.start + run.length <= offset) { + low = index; + } else { + int width; + if (run.style != null && run.style.metrics != null) { + GlyphMetrics metrics = run.style.metrics; + width = metrics.width * (offset - run.start + (trailing ? 1 : 0)); + } else if (run.tab) { + width = (trailing || (offset == length)) ? run.width : 0; + } else { + int runOffset = offset - run.start; + int cChars = run.length; + int gGlyphs = run.glyphCount; + int[] piX = new int[1]; + int /*long*/ advances = run.justify != 0 ? run.justify : run.advances; + OS.ScriptCPtoX(runOffset, trailing, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX); + width = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0]; + } + return new Point(run.x + width, lineY[line]); + } + } + return new Point(0, 0); +} + +/** + * 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) { + checkLayout(); + return _getOffset (offset, movement, true); +} + +int _getOffset(int offset, int movement, boolean forward) { + computeRuns(null); + int length = text.length(); + if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE); + if (forward && offset == length) return length; + if (!forward && offset == 0) return 0; + int step = forward ? 1 : -1; + if ((movement & SWT.MOVEMENT_CHAR) != 0) return offset + step; + length = segmentsText.length(); + offset = translateOffset(offset); + SCRIPT_LOGATTR logAttr = new SCRIPT_LOGATTR(); + SCRIPT_PROPERTIES properties = new SCRIPT_PROPERTIES(); + int i = forward ? 0 : allRuns.length - 1; + offset = validadeOffset(offset, step); + do { + StyleItem run = allRuns[i]; + if (run.start <= offset && offset < run.start + run.length) { + if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start); + if (run.tab) return untranslateOffset(run.start); + OS.MoveMemory(properties, device.scripts[run.analysis.eScript], SCRIPT_PROPERTIES.sizeof); + boolean isComplex = properties.fNeedsCaretInfo || properties.fNeedsWordBreaking; + if (isComplex) breakRun(run); + while (run.start <= offset && offset < run.start + run.length) { + if (isComplex) { + OS.MoveMemory(logAttr, run.psla + ((offset - run.start) * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof); + } + switch (movement) { + case SWT.MOVEMENT_CLUSTER: { + if (properties.fNeedsCaretInfo) { + if (!logAttr.fInvalid && logAttr.fCharStop) return untranslateOffset(offset); + } else { + return untranslateOffset(offset); + } + break; + } + case SWT.MOVEMENT_WORD_START: + case SWT.MOVEMENT_WORD: { + if (properties.fNeedsWordBreaking) { + if (!logAttr.fInvalid && logAttr.fWordStop) return untranslateOffset(offset); + } else { + if (offset > 0) { + boolean letterOrDigit = Compatibility.isLetterOrDigit(segmentsText.charAt(offset)); + boolean previousLetterOrDigit = Compatibility.isLetterOrDigit(segmentsText.charAt(offset - 1)); + if (letterOrDigit != previousLetterOrDigit || !letterOrDigit) { + if (!Compatibility.isWhitespace(segmentsText.charAt(offset))) { + return untranslateOffset(offset); + } + } + } + } + break; + } + case SWT.MOVEMENT_WORD_END: { + if (offset > 0) { + boolean isLetterOrDigit = Compatibility.isLetterOrDigit(segmentsText.charAt(offset)); + boolean previousLetterOrDigit = Compatibility.isLetterOrDigit(segmentsText.charAt(offset - 1)); + if (!isLetterOrDigit && previousLetterOrDigit) { + return untranslateOffset(offset); + } + } + break; + } + } + offset = validadeOffset(offset, step); + } + } + i += step; + } while (0 <= i && i < allRuns.length - 1 && 0 <= offset && offset < length); + return forward ? text.length() : 0; +} + +/** + * 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(); + if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return getOffset (point.x, point.y, trailing) ; +} + +/** + * 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(); + computeRuns(null); + if (trailing != null && trailing.length < 1) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + int line; + int lineCount = runs.length; + for (line=0; line<lineCount; line++) { + if (lineY[line + 1] > y) break; + } + line = Math.min(line, runs.length - 1); + StyleItem[] lineRuns = runs[line]; + int lineIndent = getLineIndent(line); + if (x >= lineIndent + lineWidth[line]) x = lineIndent + lineWidth[line] - 1; + if (x < lineIndent) x = lineIndent; + int low = -1; + int high = lineRuns.length; + while (high - low > 1) { + int index = ((high + low) / 2); + StyleItem run = lineRuns[index]; + if (run.x > x) { + high = index; + } else if (run.x + run.width <= x) { + low = index; + } else { + if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start); + int xRun = x - run.x; + if (run.style != null && run.style.metrics != null) { + GlyphMetrics metrics = run.style.metrics; + if (metrics.width > 0) { + if (trailing != null) { + trailing[0] = (xRun % metrics.width < metrics.width / 2) ? 0 : 1; + } + return untranslateOffset(run.start + xRun / metrics.width); + } + } + if (run.tab) { + if (trailing != null) trailing[0] = x < (run.x + run.width / 2) ? 0 : 1; + return untranslateOffset(run.start); + } + int cChars = run.length; + int cGlyphs = run.glyphCount; + int[] piCP = new int[1]; + int[] piTrailing = new int[1]; + if ((orientation & SWT.RIGHT_TO_LEFT) != 0) { + xRun = run.width - xRun; + } + int /*long*/ advances = run.justify != 0 ? run.justify : run.advances; + OS.ScriptXtoCP(xRun, cChars, cGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piCP, piTrailing); + if (trailing != null) trailing[0] = piTrailing[0]; + return untranslateOffset(run.start + piCP[0]); + } + } + if (trailing != null) trailing[0] = 0; + if (lineRuns.length == 1) { + StyleItem run = lineRuns[0]; + if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start); + } + return untranslateOffset(lineOffset[line + 1]); +} + +/** + * 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; +} + +void getPartialSelection(StyleItem run, int selectionStart, int selectionEnd, RECT rect) { + int end = run.start + run.length - 1; + int selStart = Math.max(selectionStart, run.start) - run.start; + int selEnd = Math.min(selectionEnd, end) - run.start; + int cChars = run.length; + int gGlyphs = run.glyphCount; + int[] piX = new int[1]; + int x = rect.left; + int /*long*/ advances = run.justify != 0 ? run.justify : run.advances; + OS.ScriptCPtoX(selStart, false, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX); + int runX = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0]; + rect.left = x + runX; + OS.ScriptCPtoX(selEnd, true, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX); + runX = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0]; + rect.right = x + runX; +} + +/** + * 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 offset, int movement) { + checkLayout(); + return _getOffset (offset, movement, false); +} + +/** + * 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[stylesCount * 2]; + int count = 0; + for (int i=0; i<stylesCount - 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; + } + char[] oldChars = new char[length]; + text.getChars(0, length, oldChars, 0); + char[] newChars = new char[length + nSegments]; + int charCount = 0, segmentCount = 0; + char separator = orientation == SWT.RIGHT_TO_LEFT ? RTL_MARK : LTR_MARK; + while (charCount < length) { + if (segmentCount < nSegments && charCount == segments[segmentCount]) { + newChars[charCount + segmentCount++] = separator; + } else { + newChars[charCount + segmentCount] = oldChars[charCount++]; + } + } + if (segmentCount < nSegments) { + segments[segmentCount] = charCount; + newChars[charCount + segmentCount++] = separator; + } + 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 lineSpacing; +} + +/** + * 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<stylesCount; i++) { + if (styles[i].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[stylesCount]; + int count = 0; + for (int i=0; i<stylesCount; 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; +} + +/* + * Itemize the receiver text + */ +StyleItem[] itemize () { + segmentsText = getSegmentsText(); + int length = segmentsText.length(); + SCRIPT_CONTROL scriptControl = new SCRIPT_CONTROL(); + SCRIPT_STATE scriptState = new SCRIPT_STATE(); + final int MAX_ITEM = length + 1; + + if ((orientation & SWT.RIGHT_TO_LEFT) != 0) { + scriptState.uBidiLevel = 1; + scriptState.fArabicNumContext = true; + SCRIPT_DIGITSUBSTITUTE psds = new SCRIPT_DIGITSUBSTITUTE(); + OS.ScriptRecordDigitSubstitution(OS.LOCALE_USER_DEFAULT, psds); + OS.ScriptApplyDigitSubstitution(psds, scriptControl, scriptState); + } + + int /*long*/ hHeap = OS.GetProcessHeap(); + int /*long*/ pItems = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, MAX_ITEM * SCRIPT_ITEM.sizeof); + if (pItems == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int[] pcItems = new int[1]; + char[] chars = new char[length]; + segmentsText.getChars(0, length, chars, 0); + OS.ScriptItemize(chars, length, MAX_ITEM, scriptControl, scriptState, pItems, pcItems); +// if (hr == E_OUTOFMEMORY) //TODO handle it + + StyleItem[] runs = merge(pItems, pcItems[0]); + OS.HeapFree(hHeap, 0, pItems); + return runs; +} + +/* + * Merge styles ranges and script items + */ +StyleItem[] merge (int /*long*/ items, int itemCount) { + if (styles.length > stylesCount) { + StyleItem[] newStyles = new StyleItem[stylesCount]; + System.arraycopy(styles, 0, newStyles, 0, stylesCount); + styles = newStyles; + } + int count = 0, start = 0, end = segmentsText.length(), itemIndex = 0, styleIndex = 0; + StyleItem[] runs = new StyleItem[itemCount + stylesCount]; + SCRIPT_ITEM scriptItem = new SCRIPT_ITEM(); + int itemLimit = -1; + int nextItemIndex = 0; + boolean linkBefore = false; + boolean merge = itemCount > TOO_MANY_RUNS; + SCRIPT_PROPERTIES sp = new SCRIPT_PROPERTIES(); + while (start < end) { + StyleItem item = new StyleItem(); + item.start = start; + item.style = styles[styleIndex].style; + runs[count++] = item; + OS.MoveMemory(scriptItem, items + itemIndex * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof); + item.analysis = scriptItem.a; + scriptItem.a = new SCRIPT_ANALYSIS(); + if (linkBefore) { + item.analysis.fLinkBefore = true; + linkBefore = false; + } + char ch = segmentsText.charAt(start); + switch (ch) { + case '\r': + case '\n': + item.lineBreak = true; + break; + case '\t': + item.tab = true; + break; + } + if (itemLimit == -1) { + nextItemIndex = itemIndex + 1; + OS.MoveMemory(scriptItem, items + nextItemIndex * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof); + itemLimit = scriptItem.iCharPos; + if (nextItemIndex < itemCount && ch == '\r' && segmentsText.charAt(itemLimit) == '\n') { + nextItemIndex = itemIndex + 2; + OS.MoveMemory(scriptItem, items + nextItemIndex * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof); + itemLimit = scriptItem.iCharPos; + } + if (nextItemIndex < itemCount && merge) { + if (!item.lineBreak) { + OS.MoveMemory(sp, device.scripts[item.analysis.eScript], SCRIPT_PROPERTIES.sizeof); + if (!sp.fComplex || item.tab) { + for (int i = 0; i < MERGE_MAX; i++) { + if (nextItemIndex == itemCount) break; + char c = segmentsText.charAt(itemLimit); + if (c == '\n' || c == '\r') break; + if (c == '\t' != item.tab) break; + OS.MoveMemory(sp, device.scripts[scriptItem.a.eScript], SCRIPT_PROPERTIES.sizeof); + if (!item.tab && sp.fComplex) break; + nextItemIndex++; + OS.MoveMemory(scriptItem, items + nextItemIndex * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof); + itemLimit = scriptItem.iCharPos; + } + } + } + } + } + + int styleLimit = translateOffset(styles[styleIndex + 1].start); + if (styleLimit <= itemLimit) { + styleIndex++; + start = styleLimit; + if (start < itemLimit && 0 < start && start < end) { + char pChar = segmentsText.charAt(start - 1); + char tChar = segmentsText.charAt(start); + if (Compatibility.isLetter(pChar) && Compatibility.isLetter(tChar)) { + item.analysis.fLinkAfter = true; + linkBefore = true; + } + } + } + if (itemLimit <= styleLimit) { + itemIndex = nextItemIndex; + start = itemLimit; + itemLimit = -1; + } + item.length = start - item.start; + } + StyleItem item = new StyleItem(); + item.start = end; + OS.MoveMemory(scriptItem, items + itemCount * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof); + item.analysis = scriptItem.a; + runs[count++] = item; + if (runs.length != count) { + StyleItem[] result = new StyleItem[count]; + System.arraycopy(runs, 0, result, 0, count); + return result; + } + return runs; +} + +/* + * Reorder the run + */ +StyleItem[] reorder (StyleItem[] runs, boolean terminate) { + int length = runs.length; + if (length <= 1) return runs; + byte[] bidiLevels = new byte[length]; + for (int i=0; i<length; i++) { + bidiLevels[i] = (byte)(runs[i].analysis.s.uBidiLevel & 0x1F); + } + /* + * Feature in Windows. If the orientation is RTL Uniscribe will + * resolve the level of line breaks to 1, this can cause the line + * break to be reorder to the middle of the line. The fix is to set + * the level to zero to prevent it to be reordered. + */ + StyleItem lastRun = runs[length - 1]; + if (lastRun.lineBreak && !lastRun.softBreak) { + bidiLevels[length - 1] = 0; + } + int[] log2vis = new int[length]; + OS.ScriptLayout(length, bidiLevels, null, log2vis); + StyleItem[] result = new StyleItem[length]; + for (int i=0; i<length; i++) { + result[log2vis[i]] = runs[i]; + } + if ((orientation & SWT.RIGHT_TO_LEFT) != 0) { + if (terminate) length--; + for (int i = 0; i < length / 2 ; i++) { + StyleItem tmp = result[i]; + result[i] = result[length - i - 1]; + result[length - i - 1] = tmp; + } + } + return result; +} + +/** + * 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; + freeRuns(); + this.alignment = alignment; +} + +/** + * 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; + freeRuns(); + this.ascent = ascent; +} + +/** + * 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; + freeRuns(); + this.descent = descent; +} + +/** + * 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; + freeRuns(); +} + +/** + * 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; + freeRuns(); + this.indent = indent; +} + +/** + * 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 (this.justify == justify) return; + freeRuns(); + this.justify = justify; +} + +/** + * 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; + freeRuns(); +} + +/** + * 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; + } + } + freeRuns(); + this.segments = segments; +} + +/** + * 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.lineSpacing == spacing) return; + freeRuns(); + this.lineSpacing = spacing; +} + +/** + * 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(); + 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 = stylesCount; + while (high - low > 1) { + int index = (high + low) / 2; + if (styles[index + 1].start > start) { + high = index; + } else { + low = index; + } + } + if (0 <= high && high < stylesCount) { + 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 < stylesCount) { + 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) { + int newLength = stylesCount + 2; + if (newLength > styles.length) { + int newSize = Math.min(newLength + 1024, Math.max(64, newLength * 2)); + StyleItem[] newStyles = new StyleItem[newSize]; + System.arraycopy(styles, 0, newStyles, 0, stylesCount); + styles = newStyles; + } + System.arraycopy(styles, modifyEnd + 1, styles, modifyEnd + 3, stylesCount - modifyEnd - 1); + StyleItem item = new StyleItem(); + item.start = start; + item.style = style; + styles[modifyStart + 1] = item; + item = new StyleItem(); + item.start = end + 1; + item.style = styles[modifyStart].style; + styles[modifyStart + 2] = item; + stylesCount = newLength; + return; + } + } + if (start == styles[modifyStart].start) modifyStart--; + if (end == styles[modifyEnd + 1].start - 1) modifyEnd++; + int newLength = stylesCount + 1 - (modifyEnd - modifyStart - 1); + if (newLength > styles.length) { + int newSize = Math.min(newLength + 1024, Math.max(64, newLength * 2)); + StyleItem[] newStyles = new StyleItem[newSize]; + System.arraycopy(styles, 0, newStyles, 0, stylesCount); + styles = newStyles; + } + System.arraycopy(styles, modifyEnd, styles, modifyStart + 2, stylesCount - modifyEnd); + StyleItem item = new StyleItem(); + item.start = start; + item.style = style; + styles[modifyStart + 1] = item; + styles[modifyStart + 2].start = end + 1; + stylesCount = newLength; +} + +/** + * 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; + } + } + freeRuns(); + this.tabs = tabs; +} + +/** + * 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; + freeRuns(); + this.text = text; + styles = new StyleItem[2]; + styles[0] = new StyleItem(); + styles[1] = new StyleItem(); + styles[1].start = text.length(); + stylesCount = 2; +} + +/** + * 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; + freeRuns(); + this.wrapWidth = width; +} + +boolean shape (int /*long*/ hdc, StyleItem run, char[] chars, int[] glyphCount, int maxGlyphs, SCRIPT_PROPERTIES sp) { + boolean useCMAPcheck = !sp.fComplex && !run.analysis.fNoGlyphIndex; + if (useCMAPcheck) { + short[] glyphs = new short[chars.length]; + if (OS.ScriptGetCMap(hdc, run.psc, chars, chars.length, 0, glyphs) != OS.S_OK) { + if (run.psc != 0) { + OS.ScriptFreeCache(run.psc); + glyphCount[0] = 0; + OS.MoveMemory(run.psc, new int /*long*/ [1], OS.PTR_SIZEOF); + } + return false; + } + } + int hr = OS.ScriptShape(hdc, run.psc, chars, chars.length, maxGlyphs, run.analysis, run.glyphs, run.clusters, run.visAttrs, glyphCount); + run.glyphCount = glyphCount[0]; + if (useCMAPcheck) return true; + + if (hr != OS.USP_E_SCRIPT_NOT_IN_FONT) { + if (run.analysis.fNoGlyphIndex) return true; + SCRIPT_FONTPROPERTIES fp = new SCRIPT_FONTPROPERTIES (); + fp.cBytes = SCRIPT_FONTPROPERTIES.sizeof; + OS.ScriptGetFontProperties(hdc, run.psc, fp); + short[] glyphs = new short[glyphCount[0]]; + OS.MoveMemory(glyphs, run.glyphs, glyphs.length * 2); + int i; + for (i = 0; i < glyphs.length; i++) { + if (glyphs[i] == fp.wgDefault) break; + } + if (i == glyphs.length) return true; + } + if (run.psc != 0) { + OS.ScriptFreeCache(run.psc); + glyphCount[0] = 0; + OS.MoveMemory(run.psc, new int /*long*/ [1], OS.PTR_SIZEOF); + } + run.glyphCount = 0; + return false; +} + +/* + * Generate glyphs for one Run. + */ +void shape (final int /*long*/ hdc, final StyleItem run) { + if (run.tab || run.lineBreak) return; + if (run.glyphs != 0) return; + final int[] buffer = new int[1]; + final char[] chars = new char[run.length]; + segmentsText.getChars(run.start, run.start + run.length, chars, 0); + + final int maxGlyphs = (chars.length * 3 / 2) + 16; + int /*long*/ hHeap = OS.GetProcessHeap(); + run.glyphs = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2); + if (run.glyphs == 0) SWT.error(SWT.ERROR_NO_HANDLES); + run.clusters = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2); + if (run.clusters == 0) SWT.error(SWT.ERROR_NO_HANDLES); + run.visAttrs = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * SCRIPT_VISATTR_SIZEOF); + if (run.visAttrs == 0) SWT.error(SWT.ERROR_NO_HANDLES); + run.psc = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, OS.PTR_SIZEOF); + if (run.psc == 0) SWT.error(SWT.ERROR_NO_HANDLES); + final short script = run.analysis.eScript; + final SCRIPT_PROPERTIES sp = new SCRIPT_PROPERTIES(); + OS.MoveMemory(sp, device.scripts[script], SCRIPT_PROPERTIES.sizeof); + boolean shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs, sp); + if (!shapeSucceed) { + int /*long*/ hFont = OS.GetCurrentObject(hdc, OS.OBJ_FONT); + int /*long*/ newFont = 0; + /* + * Bug in Uniscribe. In some version of Uniscribe, ScriptStringAnalyse crashes + * when the character array is too long. The fix is to limit the size of character + * array to two. Note, limiting the array to only one character would cause surrogate + * pairs to stop working. + */ + char[] sampleChars = new char[Math.min(chars.length, 2)]; + SCRIPT_LOGATTR logAttr = new SCRIPT_LOGATTR(); + breakRun(run); + int count = 0; + for (int i = 0; i < chars.length; i++) { + OS.MoveMemory(logAttr, run.psla + (i * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof); + if (!logAttr.fWhiteSpace) { + sampleChars[count++] = chars[i]; + if (count == sampleChars.length) break; + } + } + if (count > 0) { + int /*long*/ ssa = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, OS.SCRIPT_STRING_ANALYSIS_sizeof()); + int /*long*/ metaFileDc = OS.CreateEnhMetaFile(hdc, null, null, null); + int /*long*/ oldMetaFont = OS.SelectObject(metaFileDc, hFont); + int flags = OS.SSA_METAFILE | OS.SSA_FALLBACK | OS.SSA_GLYPHS | OS.SSA_LINK; + if (OS.ScriptStringAnalyse(metaFileDc, sampleChars, count, 0, -1, flags, 0, null, null, 0, 0, 0, ssa) == OS.S_OK) { + OS.ScriptStringOut(ssa, 0, 0, 0, null, 0, 0, false); + OS.ScriptStringFree(ssa); + } + OS.HeapFree(hHeap, 0, ssa); + OS.SelectObject(metaFileDc, oldMetaFont); + int /*long*/ metaFile = OS.CloseEnhMetaFile(metaFileDc); + final EMREXTCREATEFONTINDIRECTW emr = new EMREXTCREATEFONTINDIRECTW(); + class MetaFileEnumProc { + int /*long*/ metaFileEnumProc (int /*long*/ hDC, int /*long*/ table, int /*long*/ record, int /*long*/ nObj, int /*long*/ lpData) { + OS.MoveMemory(emr.emr, record, EMR.sizeof); + switch (emr.emr.iType) { + case OS.EMR_EXTCREATEFONTINDIRECTW: + OS.MoveMemory(emr, record, EMREXTCREATEFONTINDIRECTW.sizeof); + break; + case OS.EMR_EXTTEXTOUTW: + return 0; + } + return 1; + } + }; + MetaFileEnumProc object = new MetaFileEnumProc(); + /* Avoid compiler warnings */ + boolean compilerWarningWorkaround = false; + if (compilerWarningWorkaround) object.metaFileEnumProc(0, 0, 0, 0, 0); + Callback callback = new Callback(object, "metaFileEnumProc", 5); + int /*long*/ address = callback.getAddress(); + if (address == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + OS.EnumEnhMetaFile(0, metaFile, address, 0, null); + OS.DeleteEnhMetaFile(metaFile); + callback.dispose(); + newFont = OS.CreateFontIndirectW(emr.elfw.elfLogFont); + } else { + /* + * The run is composed only by white spaces, this happens when a run is split + * by a visual style. The font fallback for the script can not be determined + * using only white spaces. The solution is to use the font fallback of the + * previous or next run of the same script. + */ + int index = 0; + while (index < allRuns.length - 1) { + if (allRuns[index] == run) { + if (index > 0) { + StyleItem pRun = allRuns[index - 1]; + if (pRun.fallbackFont != 0 && pRun.analysis.eScript == run.analysis.eScript) { + LOGFONT logFont = OS.IsUnicode ? (LOGFONT)new LOGFONTW() : new LOGFONTA(); + OS.GetObject(pRun.fallbackFont, LOGFONT.sizeof, logFont); + newFont = OS.CreateFontIndirect(logFont); + } + } + if (newFont == 0) { + if (index + 1 < allRuns.length - 1) { + StyleItem nRun = allRuns[index + 1]; + if (nRun.analysis.eScript == run.analysis.eScript) { + shape(hdc, nRun); + if (nRun.fallbackFont != 0) { + LOGFONT logFont = OS.IsUnicode ? (LOGFONT)new LOGFONTW() : new LOGFONTA(); + OS.GetObject(nRun.fallbackFont, LOGFONT.sizeof, logFont); + newFont = OS.CreateFontIndirect(logFont); + } + } + } + } + break; + } + index++; + } + } + if (newFont != 0) { + OS.SelectObject(hdc, newFont); + if (shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs, sp)) { + run.fallbackFont = newFont; + } + } + if (!shapeSucceed) { + if (!sp.fComplex) { + run.analysis.fNoGlyphIndex = true; + if (shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs, sp)) { + run.fallbackFont = newFont; + } else { + run.analysis.fNoGlyphIndex = false; + } + } + } + if (!shapeSucceed) { + if (mLangFontLink2 != 0) { + int /*long*/[] hNewFont = new int /*long*/[1]; + int[] dwCodePages = new int[1], cchCodePages = new int[1]; + /* GetStrCodePages() */ + OS.VtblCall(4, mLangFontLink2, chars, chars.length, 0, dwCodePages, cchCodePages); + /* MapFont() */ + if (OS.VtblCall(10, mLangFontLink2, hdc, dwCodePages[0], chars[0], hNewFont) == OS.S_OK) { + LOGFONT logFont = OS.IsUnicode ? (LOGFONT)new LOGFONTW () : new LOGFONTA (); + OS.GetObject(hNewFont[0], LOGFONT.sizeof, logFont); + /* ReleaseFont() */ + OS.VtblCall(8, mLangFontLink2, hNewFont[0]); + int /*long*/ mLangFont = OS.CreateFontIndirect(logFont); + int /*long*/ oldFont = OS.SelectObject(hdc, mLangFont); + if (shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs, sp)) { + run.fallbackFont = mLangFont; + } else { + OS.SelectObject(hdc, oldFont); + OS.DeleteObject(mLangFont); + } + } + } + } + if (!shapeSucceed) OS.SelectObject(hdc, hFont); + if (newFont != 0 && newFont != run.fallbackFont) OS.DeleteObject(newFont); + } + + if (!shapeSucceed) { + /* + * Shape Failed. + * Give up and shape the run with the default font. + * Missing glyphs typically will be represent as black boxes in the text. + */ + OS.ScriptShape(hdc, run.psc, chars, chars.length, maxGlyphs, run.analysis, run.glyphs, run.clusters, run.visAttrs, buffer); + run.glyphCount = buffer[0]; + } + int[] abc = new int[3]; + run.advances = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * 4); + if (run.advances == 0) SWT.error(SWT.ERROR_NO_HANDLES); + run.goffsets = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * GOFFSET_SIZEOF); + if (run.goffsets == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.ScriptPlace(hdc, run.psc, run.glyphs, run.glyphCount, run.visAttrs, run.analysis, run.advances, run.goffsets, abc); + run.width = abc[0] + abc[1] + abc[2]; + TextStyle style = run.style; + if (style != null) { + OUTLINETEXTMETRIC lotm = null; + if (style.underline || style.strikeout) { + lotm = OS.IsUnicode ? (OUTLINETEXTMETRIC)new OUTLINETEXTMETRICW() : new OUTLINETEXTMETRICA(); + if (OS.GetOutlineTextMetrics(hdc, OUTLINETEXTMETRIC.sizeof, lotm) == 0) { + lotm = null; + } + } + if (style.metrics != null) { + GlyphMetrics metrics = style.metrics; + /* + * Bug in Windows, on a Japanese machine, Uniscribe returns glyphcount + * equals zero for FFFC (possibly other unicode code points), the fix + * is to make sure the glyph is at least one pixel wide. + */ + run.width = metrics.width * Math.max (1, run.glyphCount); + run.ascent = metrics.ascent; + run.descent = metrics.descent; + run.leading = 0; + } else { + TEXTMETRIC lptm = null; + if (lotm != null) { + lptm = OS.IsUnicode ? (TEXTMETRIC)((OUTLINETEXTMETRICW)lotm).otmTextMetrics : ((OUTLINETEXTMETRICA)lotm).otmTextMetrics; + } else { + lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA(); + OS.GetTextMetrics(hdc, lptm); + } + run.ascent = lptm.tmAscent; + run.descent = lptm.tmDescent; + run.leading = lptm.tmInternalLeading; + } + if (lotm != null) { + run.underlinePos = lotm.otmsUnderscorePosition; + run.underlineThickness = Math.max(1, lotm.otmsUnderscoreSize); + run.strikeoutPos = lotm.otmsStrikeoutPosition; + run.strikeoutThickness = Math.max(1, lotm.otmsStrikeoutSize); + } else { + run.underlinePos = 1; + run.underlineThickness = 1; + run.strikeoutPos = run.ascent / 2; + run.strikeoutThickness = 1; + } + run.ascent += style.rise; + run.descent -= style.rise; + } else { + TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA(); + OS.GetTextMetrics(hdc, lptm); + run.ascent = lptm.tmAscent; + run.descent = lptm.tmDescent; + run.leading = lptm.tmInternalLeading; + } +} + +int validadeOffset(int offset, int step) { + offset += step; + if (segments != null && segments.length > 2) { + for (int i = 0; i < segments.length; i++) { + if (translateOffset(segments[i]) - 1 == offset) { + offset += step; + break; + } + } + } + return offset; +} + +/** + * 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 {}"; +} + +int translateOffset(int offset) { + if (segments == null) return offset; + int nSegments = segments.length; + if (nSegments <= 1) return offset; + int length = text.length(); + if (length == 0) return offset; + if (nSegments == 2) { + if (segments[0] == 0 && segments[1] == length) return offset; + } + for (int i = 0; i < nSegments && offset - i >= segments[i]; i++) { + offset++; + } + return offset; +} + +int untranslateOffset(int offset) { + if (segments == null) return offset; + int nSegments = segments.length; + if (nSegments <= 1) return offset; + int length = text.length(); + if (length == 0) return offset; + if (nSegments == 2) { + if (segments[0] == 0 && segments[1] == length) return offset; + } + for (int i = 0; i < nSegments && offset > segments[i]; i++) { + offset--; + } + return offset; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Transform.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Transform.java new file mode 100644 index 0000000000..c4236ad294 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Transform.java @@ -0,0 +1,369 @@ +/******************************************************************************* + * 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.gdip.*; + +/** + * 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 int /*long*/ 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); + this.device.checkGDIP(); + handle = Gdip.Matrix_new(m11, m12, m21, m22, dx, dy); + if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); + init(); +} + +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() { + Gdip.Matrix_delete(handle); + handle = 0; +} + +/** + * 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); + Gdip.Matrix_GetElements(handle, elements); +} + +/** + * 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); + Gdip.Matrix_SetElements(handle, 1, 0, 0, 1, 0, 0); +} + +/** + * 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); + if (Gdip.Matrix_Invert(handle) != 0) SWT.error(SWT.ERROR_CANNOT_INVERT_MATRIX); +} + +/** + * 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 == 0; +} + +/** + * 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); + return Gdip.Matrix_IsIdentity(handle); +} + +/** + * 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); + Gdip.Matrix_Multiply(handle, matrix.handle, Gdip.MatrixOrderPrepend); +} + +/** + * 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); + Gdip.Matrix_Rotate(handle, angle, Gdip.MatrixOrderPrepend); +} + +/** + * 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); + Gdip.Matrix_Scale(handle, scaleX, scaleY, Gdip.MatrixOrderPrepend); +} + +/** + * 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); + Gdip.Matrix_SetElements(handle, m11, m12, m21, m22, dx, dy); +} + +/** + * 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); + Gdip.Matrix_Shear(handle, shearX, shearY, Gdip.MatrixOrderPrepend); +} + +/** + * 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); + Gdip.Matrix_TransformPoints(handle, pointArray, pointArray.length / 2); +} + +/** + * 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); + Gdip.Matrix_Translate(handle, offsetX, offsetY, Gdip.MatrixOrderPrepend); +} + +/** + * 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/win32/org/eclipse/swt/internal/BidiUtil.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/BidiUtil.java new file mode 100644 index 0000000000..ebd449179e --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/BidiUtil.java @@ -0,0 +1,642 @@ +/******************************************************************************* + * 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.internal; + + +import java.util.Hashtable; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.widgets.*; +/* + * Wraps Win32 API used to bidi enable the StyledText widget. + */ +public class BidiUtil { + + // Keyboard language ids + public static final int KEYBOARD_NON_BIDI = 0; + public static final int KEYBOARD_BIDI = 1; + + // bidi flag + static int isBidiPlatform = -1; + + // getRenderInfo flag values + public static final int CLASSIN = 1; + public static final int LINKBEFORE = 2; + public static final int LINKAFTER = 4; + + // variables used for providing a listener mechanism for keyboard language + // switching + static Hashtable languageMap = new Hashtable (); + static Hashtable keyMap = new Hashtable (); + static Hashtable oldProcMap = new Hashtable (); + /* + * This code is intentionally commented. In order + * to support CLDC, .class cannot be used because + * it does not compile on some Java compilers when + * they are targeted for CLDC. + */ + // static Callback callback = new Callback (BidiUtil.class, "windowProc", 4); + static final String CLASS_NAME = "org.eclipse.swt.internal.BidiUtil"; //$NON-NLS-1$ + static Callback callback; + static { + try { + callback = new Callback (Class.forName (CLASS_NAME), "windowProc", 4); //$NON-NLS-1$ + if (callback.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + } catch (ClassNotFoundException e) {} + } + + // GetCharacterPlacement constants + static final int GCP_REORDER = 0x0002; + static final int GCP_GLYPHSHAPE = 0x0010; + static final int GCP_LIGATE = 0x0020; + static final int GCP_CLASSIN = 0x00080000; + static final byte GCPCLASS_ARABIC = 2; + static final byte GCPCLASS_HEBREW = 2; + static final byte GCPCLASS_LOCALNUMBER = 4; + static final byte GCPCLASS_LATINNUMBER = 5; + static final int GCPGLYPH_LINKBEFORE = 0x8000; + static final int GCPGLYPH_LINKAFTER = 0x4000; + // ExtTextOut constants + static final int ETO_CLIPPED = 0x4; + static final int ETO_GLYPH_INDEX = 0x0010; + // Windows primary language identifiers + static final int LANG_ARABIC = 0x01; + static final int LANG_HEBREW = 0x0d; + static final int LANG_FARSI = 0x29; + // code page identifiers + static final String CD_PG_HEBREW = "1255"; //$NON-NLS-1$ + static final String CD_PG_ARABIC = "1256"; //$NON-NLS-1$ + // ActivateKeyboard constants + static final int HKL_NEXT = 1; + static final int HKL_PREV = 0; + + /* + * Public character class constants are the same as Windows + * platform constants. + * Saves conversion of class array in getRenderInfo to arbitrary + * constants for now. + */ + public static final int CLASS_HEBREW = GCPCLASS_ARABIC; + public static final int CLASS_ARABIC = GCPCLASS_HEBREW; + public static final int CLASS_LOCALNUMBER = GCPCLASS_LOCALNUMBER; + public static final int CLASS_LATINNUMBER = GCPCLASS_LATINNUMBER; + public static final int REORDER = GCP_REORDER; + public static final int LIGATE = GCP_LIGATE; + public static final int GLYPHSHAPE = GCP_GLYPHSHAPE; + +/** + * Adds a language listener. The listener will get notified when the language of + * the keyboard changes (via Alt-Shift on Win platforms). Do this by creating a + * window proc for the Control so that the window messages for the Control can be + * monitored. + * <p> + * + * @param hwnd the handle of the Control that is listening for keyboard language + * changes + * @param runnable the code that should be executed when a keyboard language change + * occurs + */ +public static void addLanguageListener (int /*long*/ hwnd, Runnable runnable) { + languageMap.put(new LONG(hwnd), runnable); + subclass(hwnd); +} +public static void addLanguageListener (Control control, Runnable runnable) { + addLanguageListener(control.handle, runnable); +} +/** + * Proc used for OS.EnumSystemLanguageGroups call during isBidiPlatform test. + */ +static int /*long*/ EnumSystemLanguageGroupsProc(int /*long*/ lpLangGrpId, int /*long*/ lpLangGrpIdString, int /*long*/ lpLangGrpName, int /*long*/ options, int /*long*/ lParam) { + if ((int)/*64*/lpLangGrpId == OS.LGRPID_HEBREW) { + isBidiPlatform = 1; + return 0; + } + if ((int)/*64*/lpLangGrpId == OS.LGRPID_ARABIC) { + isBidiPlatform = 1; + return 0; + } + return 1; +} +/** + * Wraps the ExtTextOut function. + * <p> + * + * @param gc the gc to use for rendering + * @param renderBuffer the glyphs to render as an array of characters + * @param renderDx the width of each glyph in renderBuffer + * @param x x position to start rendering + * @param y y position to start rendering + */ +public static void drawGlyphs(GC gc, char[] renderBuffer, int[] renderDx, int x, int y) { + int length = renderDx.length; + + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) { + if (OS.GetLayout (gc.handle) != 0) { + reverse(renderDx); + renderDx[length-1]--; //fixes bug 40006 + reverse(renderBuffer); + } + } + // render transparently to avoid overlapping segments. fixes bug 40006 + int oldBkMode = OS.SetBkMode(gc.handle, OS.TRANSPARENT); + OS.ExtTextOutW(gc.handle, x, y, ETO_GLYPH_INDEX , null, renderBuffer, renderBuffer.length, renderDx); + OS.SetBkMode(gc.handle, oldBkMode); +} +/** + * Return ordering and rendering information for the given text. Wraps the GetFontLanguageInfo + * and GetCharacterPlacement functions. + * <p> + * + * @param gc the GC to use for measuring of this line, input parameter + * @param text text that bidi data should be calculated for, input parameter + * @param order an array of integers representing the visual position of each character in + * the text array, output parameter + * @param classBuffer an array of integers representing the type (e.g., ARABIC, HEBREW, + * LOCALNUMBER) of each character in the text array, input/output parameter + * @param dx an array of integers representing the pixel width of each glyph in the returned + * glyph buffer, output parameter + * @param flags an integer representing rendering flag information, input parameter + * @param offsets text segments that should be measured and reordered separately, input + * parameter. See org.eclipse.swt.custom.BidiSegmentEvent for details. + * @return buffer with the glyphs that should be rendered for the given text + */ +public static char[] getRenderInfo(GC gc, String text, int[] order, byte[] classBuffer, int[] dx, int flags, int [] offsets) { + int fontLanguageInfo = OS.GetFontLanguageInfo(gc.handle); + int /*long*/ hHeap = OS.GetProcessHeap(); + int[] lpCs = new int[8]; + int cs = OS.GetTextCharset(gc.handle); + boolean isRightOriented = false; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) { + isRightOriented = OS.GetLayout(gc.handle) != 0; + } + OS.TranslateCharsetInfo(cs, lpCs, OS.TCI_SRCCHARSET); + TCHAR textBuffer = new TCHAR(lpCs[1], text, false); + int byteCount = textBuffer.length(); + boolean linkBefore = (flags & LINKBEFORE) == LINKBEFORE; + boolean linkAfter = (flags & LINKAFTER) == LINKAFTER; + + GCP_RESULTS result = new GCP_RESULTS(); + result.lStructSize = GCP_RESULTS.sizeof; + result.nGlyphs = byteCount; + int /*long*/ lpOrder = result.lpOrder = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount * 4); + int /*long*/ lpDx = result.lpDx = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount * 4); + int /*long*/ lpClass = result.lpClass = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + int /*long*/ lpGlyphs = result.lpGlyphs = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount * 2); + + // set required dwFlags + int dwFlags = 0; + int glyphFlags = 0; + // Always reorder. We assume that if we are calling this function we're + // on a platform that supports bidi. Fixes 20690. + dwFlags |= GCP_REORDER; + if ((fontLanguageInfo & GCP_LIGATE) == GCP_LIGATE) { + dwFlags |= GCP_LIGATE; + glyphFlags |= 0; + } + if ((fontLanguageInfo & GCP_GLYPHSHAPE) == GCP_GLYPHSHAPE) { + dwFlags |= GCP_GLYPHSHAPE; + if (linkBefore) { + glyphFlags |= GCPGLYPH_LINKBEFORE; + } + if (linkAfter) { + glyphFlags |= GCPGLYPH_LINKAFTER; + } + } + byte[] lpGlyphs2; + if (linkBefore || linkAfter) { + lpGlyphs2 = new byte[2]; + lpGlyphs2[0]=(byte)glyphFlags; + lpGlyphs2[1]=(byte)(glyphFlags >> 8); + } + else { + lpGlyphs2 = new byte[] {(byte) glyphFlags}; + } + OS.MoveMemory(result.lpGlyphs, lpGlyphs2, lpGlyphs2.length); + + if ((flags & CLASSIN) == CLASSIN) { + // set classification values for the substring + dwFlags |= GCP_CLASSIN; + OS.MoveMemory(result.lpClass, classBuffer, classBuffer.length); + } + + char[] glyphBuffer = new char[result.nGlyphs]; + int glyphCount = 0; + for (int i=0; i<offsets.length-1; i++) { + int offset = offsets [i]; + int length = offsets [i+1] - offsets [i]; + + // The number of glyphs expected is <= length (segment length); + // the actual number returned may be less in case of Arabic ligatures. + result.nGlyphs = length; + TCHAR textBuffer2 = new TCHAR(lpCs[1], text.substring(offset, offset + length), false); + OS.GetCharacterPlacement(gc.handle, textBuffer2, textBuffer2.length(), 0, result, dwFlags); + + if (dx != null) { + int [] dx2 = new int [result.nGlyphs]; + OS.MoveMemory(dx2, result.lpDx, dx2.length * 4); + if (isRightOriented) { + reverse(dx2); + } + System.arraycopy (dx2, 0, dx, glyphCount, dx2.length); + } + if (order != null) { + int [] order2 = new int [length]; + OS.MoveMemory(order2, result.lpOrder, order2.length * 4); + translateOrder(order2, glyphCount, isRightOriented); + System.arraycopy (order2, 0, order, offset, length); + } + if (classBuffer != null) { + byte [] classBuffer2 = new byte [length]; + OS.MoveMemory(classBuffer2, result.lpClass, classBuffer2.length); + System.arraycopy (classBuffer2, 0, classBuffer, offset, length); + } + char[] glyphBuffer2 = new char[result.nGlyphs]; + OS.MoveMemory(glyphBuffer2, result.lpGlyphs, glyphBuffer2.length * 2); + if (isRightOriented) { + reverse(glyphBuffer2); + } + System.arraycopy (glyphBuffer2, 0, glyphBuffer, glyphCount, glyphBuffer2.length); + glyphCount += glyphBuffer2.length; + + // We concatenate successive results of calls to GCP. + // For Arabic, it is the only good method since the number of output + // glyphs might be less than the number of input characters. + // This assumes that the whole line is built by successive adjacent + // segments without overlapping. + result.lpOrder += length * 4; + result.lpDx += length * 4; + result.lpClass += length; + result.lpGlyphs += glyphBuffer2.length * 2; + } + + /* Free the memory that was allocated. */ + OS.HeapFree(hHeap, 0, lpGlyphs); + OS.HeapFree(hHeap, 0, lpClass); + OS.HeapFree(hHeap, 0, lpDx); + OS.HeapFree(hHeap, 0, lpOrder); + return glyphBuffer; +} +/** + * Return bidi ordering information for the given text. Does not return rendering + * information (e.g., glyphs, glyph distances). Use this method when you only need + * ordering information. Doing so will improve performance. Wraps the + * GetFontLanguageInfo and GetCharacterPlacement functions. + * <p> + * + * @param gc the GC to use for measuring of this line, input parameter + * @param text text that bidi data should be calculated for, input parameter + * @param order an array of integers representing the visual position of each character in + * the text array, output parameter + * @param classBuffer an array of integers representing the type (e.g., ARABIC, HEBREW, + * LOCALNUMBER) of each character in the text array, input/output parameter + * @param flags an integer representing rendering flag information, input parameter + * @param offsets text segments that should be measured and reordered separately, input + * parameter. See org.eclipse.swt.custom.BidiSegmentEvent for details. + */ +public static void getOrderInfo(GC gc, String text, int[] order, byte[] classBuffer, int flags, int [] offsets) { + int fontLanguageInfo = OS.GetFontLanguageInfo(gc.handle); + int /*long*/ hHeap = OS.GetProcessHeap(); + int[] lpCs = new int[8]; + int cs = OS.GetTextCharset(gc.handle); + OS.TranslateCharsetInfo(cs, lpCs, OS.TCI_SRCCHARSET); + TCHAR textBuffer = new TCHAR(lpCs[1], text, false); + int byteCount = textBuffer.length(); + boolean isRightOriented = false; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) { + isRightOriented = OS.GetLayout(gc.handle) != 0; + } + + GCP_RESULTS result = new GCP_RESULTS(); + result.lStructSize = GCP_RESULTS.sizeof; + result.nGlyphs = byteCount; + int /*long*/ lpOrder = result.lpOrder = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount * 4); + int /*long*/ lpClass = result.lpClass = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + + // set required dwFlags, these values will affect how the text gets rendered and + // ordered + int dwFlags = 0; + // Always reorder. We assume that if we are calling this function we're + // on a platform that supports bidi. Fixes 20690. + dwFlags |= GCP_REORDER; + if ((fontLanguageInfo & GCP_LIGATE) == GCP_LIGATE) { + dwFlags |= GCP_LIGATE; + } + if ((fontLanguageInfo & GCP_GLYPHSHAPE) == GCP_GLYPHSHAPE) { + dwFlags |= GCP_GLYPHSHAPE; + } + if ((flags & CLASSIN) == CLASSIN) { + // set classification values for the substring, classification values + // can be specified on input + dwFlags |= GCP_CLASSIN; + OS.MoveMemory(result.lpClass, classBuffer, classBuffer.length); + } + + int glyphCount = 0; + for (int i=0; i<offsets.length-1; i++) { + int offset = offsets [i]; + int length = offsets [i+1] - offsets [i]; + // The number of glyphs expected is <= length (segment length); + // the actual number returned may be less in case of Arabic ligatures. + result.nGlyphs = length; + TCHAR textBuffer2 = new TCHAR(lpCs[1], text.substring(offset, offset + length), false); + OS.GetCharacterPlacement(gc.handle, textBuffer2, textBuffer2.length(), 0, result, dwFlags); + + if (order != null) { + int [] order2 = new int [length]; + OS.MoveMemory(order2, result.lpOrder, order2.length * 4); + translateOrder(order2, glyphCount, isRightOriented); + System.arraycopy (order2, 0, order, offset, length); + } + if (classBuffer != null) { + byte [] classBuffer2 = new byte [length]; + OS.MoveMemory(classBuffer2, result.lpClass, classBuffer2.length); + System.arraycopy (classBuffer2, 0, classBuffer, offset, length); + } + glyphCount += result.nGlyphs; + + // We concatenate successive results of calls to GCP. + // For Arabic, it is the only good method since the number of output + // glyphs might be less than the number of input characters. + // This assumes that the whole line is built by successive adjacent + // segments without overlapping. + result.lpOrder += length * 4; + result.lpClass += length; + } + + /* Free the memory that was allocated. */ + OS.HeapFree(hHeap, 0, lpClass); + OS.HeapFree(hHeap, 0, lpOrder); +} +/** + * Return bidi attribute information for the font in the specified gc. + * <p> + * + * @param gc the gc to query + * @return bitwise OR of the REORDER, LIGATE and GLYPHSHAPE flags + * defined by this class. + */ +public static int getFontBidiAttributes(GC gc) { + int fontStyle = 0; + int fontLanguageInfo = OS.GetFontLanguageInfo(gc.handle); + if (((fontLanguageInfo & GCP_REORDER) != 0)) { + fontStyle |= REORDER; + } + if (((fontLanguageInfo & GCP_LIGATE) != 0)) { + fontStyle |= LIGATE; + } + if (((fontLanguageInfo & GCP_GLYPHSHAPE) != 0)) { + fontStyle |= GLYPHSHAPE; + } + return fontStyle; +} +/** + * Return the active keyboard language type. + * <p> + * + * @return an integer representing the active keyboard language (KEYBOARD_BIDI, + * KEYBOARD_NON_BIDI) + */ +public static int getKeyboardLanguage() { + int /*long*/ layout = OS.GetKeyboardLayout(0); + return isBidiLang(layout) ? KEYBOARD_BIDI : KEYBOARD_NON_BIDI; +} +/** + * Return the languages that are installed for the keyboard. + * <p> + * + * @return integer array with an entry for each installed language + */ +static int /*long*/[] getKeyboardLanguageList() { + int maxSize = 10; + int /*long*/[] tempList = new int /*long*/[maxSize]; + int size = OS.GetKeyboardLayoutList(maxSize, tempList); + int /*long*/[] list = new int /*long*/[size]; + System.arraycopy(tempList, 0, list, 0, size); + return list; +} +static boolean isBidiLang(int /*long*/ lang) { + int id = OS.PRIMARYLANGID(OS.LOWORD(lang)); + return id == LANG_ARABIC || id == LANG_HEBREW || id == LANG_FARSI; +} +/** + * Return whether or not the platform supports a bidi language. Determine this + * by looking at the languages that are installed. + * <p> + * + * @return true if bidi is supported, false otherwise. Always + * false on Windows CE. + */ +public static boolean isBidiPlatform() { + if (OS.IsWinCE) return false; + if (isBidiPlatform != -1) return isBidiPlatform == 1; // already set + + isBidiPlatform = 0; + + // The following test is a workaround for bug report 27629. On WinXP, + // both bidi and complex script (e.g., Thai) languages must be installed + // at the same time. Since the bidi platform calls do not support + // double byte characters, there is no way to run Eclipse using the + // complex script languages on XP, so constrain this test to answer true + // only if a bidi input language is defined. Doing so will allow complex + // script languages to work (e.g., one can install bidi and complex script + // languages, but only install the Thai keyboard). + if (!isKeyboardBidi()) return false; + + Callback callback = null; + try { + callback = new Callback (Class.forName (CLASS_NAME), "EnumSystemLanguageGroupsProc", 5); //$NON-NLS-1$ + int /*long*/ lpEnumSystemLanguageGroupsProc = callback.getAddress (); + if (lpEnumSystemLanguageGroupsProc == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + OS.EnumSystemLanguageGroups(lpEnumSystemLanguageGroupsProc, OS.LGRPID_INSTALLED, 0); + callback.dispose (); + } catch (ClassNotFoundException e) { + if (callback != null) callback.dispose(); + } + if (isBidiPlatform == 1) return true; + // need to look at system code page for NT & 98 platforms since EnumSystemLanguageGroups is + // not supported for these platforms + String codePage = String.valueOf(OS.GetACP()); + if (CD_PG_ARABIC.equals(codePage) || CD_PG_HEBREW.equals(codePage)) { + isBidiPlatform = 1; + } + return isBidiPlatform == 1; +} +/** + * Return whether or not the keyboard supports input of a bidi language. Determine this + * by looking at the languages that are installed for the keyboard. + * <p> + * + * @return true if bidi is supported, false otherwise. + */ +public static boolean isKeyboardBidi() { + int /*long*/[] list = getKeyboardLanguageList(); + for (int i=0; i<list.length; i++) { + if (isBidiLang(list[i])) { + return true; + } + } + return false; +} +/** + * Removes the specified language listener. + * <p> + * + * @param hwnd the handle of the Control that is listening for keyboard language changes + */ +public static void removeLanguageListener (int /*long*/ hwnd) { + languageMap.remove(new LONG(hwnd)); + unsubclass(hwnd); +} +public static void removeLanguageListener (Control control) { + removeLanguageListener(control.handle); +} +/** + * Switch the keyboard language to the specified language type. We do + * not distinguish between multiple bidi or multiple non-bidi languages, so + * set the keyboard to the first language of the given type. + * <p> + * + * @param language integer representing language. One of + * KEYBOARD_BIDI, KEYBOARD_NON_BIDI. + */ +public static void setKeyboardLanguage(int language) { + if (language == getKeyboardLanguage()) return; + boolean bidi = language == KEYBOARD_BIDI; + int /*long*/[] list = getKeyboardLanguageList(); + for (int i=0; i<list.length; i++) { + if (bidi == isBidiLang(list[i])) { + OS.ActivateKeyboardLayout(list[i], 0); + return; + } + } +} +/** + * Sets the orientation (writing order) of the specified control. Text will + * be right aligned for right to left writing order. + * <p> + * + * @param hwnd the handle of the Control to change the orientation of + * @param orientation one of SWT.RIGHT_TO_LEFT or SWT.LEFT_TO_RIGHT + * @return true if the orientation was changed, false if the orientation + * could not be changed + */ +public static boolean setOrientation (int /*long*/ hwnd, int orientation) { + if (OS.IsWinCE) return false; + if (OS.WIN32_VERSION < OS.VERSION(4, 10)) return false; + int bits = OS.GetWindowLong (hwnd, OS.GWL_EXSTYLE); + if ((orientation & SWT.RIGHT_TO_LEFT) != 0) { + bits |= OS.WS_EX_LAYOUTRTL; + } else { + bits &= ~OS.WS_EX_LAYOUTRTL; + } + OS.SetWindowLong (hwnd, OS.GWL_EXSTYLE, bits); + return true; +} +public static boolean setOrientation (Control control, int orientation) { + return setOrientation(control.handle, orientation); +} +/** + * Override the window proc. + * + * @param hwnd control to override the window proc of + */ +static void subclass(int /*long*/ hwnd) { + LONG key = new LONG(hwnd); + if (oldProcMap.get(key) == null) { + int /*long*/ oldProc = OS.GetWindowLongPtr(hwnd, OS.GWLP_WNDPROC); + oldProcMap.put(key, new LONG(oldProc)); + OS.SetWindowLongPtr(hwnd, OS.GWLP_WNDPROC, callback.getAddress()); + } +} +/** + * Reverse the character array. Used for right orientation. + * + * @param charArray character array to reverse + */ +static void reverse(char[] charArray) { + int length = charArray.length; + for (int i = 0; i <= (length - 1) / 2; i++) { + char tmp = charArray[i]; + charArray[i] = charArray[length - 1 - i]; + charArray[length - 1 - i] = tmp; + } +} +/** + * Reverse the integer array. Used for right orientation. + * + * @param intArray integer array to reverse + */ +static void reverse(int[] intArray) { + int length = intArray.length; + for (int i = 0; i <= (length - 1) / 2; i++) { + int tmp = intArray[i]; + intArray[i] = intArray[length - 1 - i]; + intArray[length - 1 - i] = tmp; + } +} +/** + * Adjust the order array so that it is relative to the start of the line. Also reverse the order array if the orientation + * is to the right. + * + * @param orderArray integer array of order values to translate + * @param glyphCount number of glyphs that have been processed for the current line + * @param isRightOriented flag indicating whether or not current orientation is to the right +*/ +static void translateOrder(int[] orderArray, int glyphCount, boolean isRightOriented) { + int maxOrder = 0; + int length = orderArray.length; + if (isRightOriented) { + for (int i=0; i<length; i++) { + maxOrder = Math.max(maxOrder, orderArray[i]); + } + } + for (int i=0; i<length; i++) { + if (isRightOriented) orderArray[i] = maxOrder - orderArray[i]; + orderArray [i] += glyphCount; + } +} +/** + * Remove the overridden the window proc. + * + * @param hwnd control to remove the window proc override for + */ +static void unsubclass(int /*long*/ hwnd) { + LONG key = new LONG(hwnd); + if (languageMap.get(key) == null && keyMap.get(key) == null) { + LONG proc = (LONG) oldProcMap.remove(key); + if (proc == null) return; + OS.SetWindowLongPtr(hwnd, OS.GWLP_WNDPROC, proc.value); + } +} +/** + * Window proc to intercept keyboard language switch event (WS_INPUTLANGCHANGE) + * and widget orientation changes. + * Run the Control's registered runnable when the keyboard language is switched. + * + * @param hwnd handle of the control that is listening for the keyboard language + * change event + * @param msg window message + */ +static int /*long*/ windowProc (int /*long*/ hwnd, int /*long*/ msg, int /*long*/ wParam, int /*long*/ lParam) { + LONG key = new LONG (hwnd); + switch ((int)/*64*/msg) { + case 0x51 /*OS.WM_INPUTLANGCHANGE*/: + Runnable runnable = (Runnable) languageMap.get (key); + if (runnable != null) runnable.run (); + break; + } + LONG oldProc = (LONG)oldProcMap.get(key); + return OS.CallWindowProc (oldProc.value, hwnd, (int)/*64*/msg, wParam, lParam); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java new file mode 100644 index 0000000000..e82f7285e8 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java @@ -0,0 +1,459 @@ +/******************************************************************************* + * 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.internal; + + +import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +public class ImageList { + int /*long*/ handle; + int style, refCount; + Image [] images; + +public ImageList (int style) { + this.style = style; + int flags = OS.ILC_MASK; + if (OS.IsWinCE) { + flags |= OS.ILC_COLOR; + } else { + if (OS.COMCTL32_MAJOR >= 6) { + flags |= OS.ILC_COLOR32; + } else { + int /*long*/ hDC = OS.GetDC (0); + int bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL); + int planes = OS.GetDeviceCaps (hDC, OS.PLANES); + OS.ReleaseDC (0, hDC); + int depth = bits * planes; + switch (depth) { + case 4: flags |= OS.ILC_COLOR4; break; + case 8: flags |= OS.ILC_COLOR8; break; + case 16: flags |= OS.ILC_COLOR16; break; + case 24: flags |= OS.ILC_COLOR24; break; + case 32: flags |= OS.ILC_COLOR32; break; + default: flags |= OS.ILC_COLOR; break; + } + } + } + if ((style & SWT.RIGHT_TO_LEFT) != 0) flags |= OS.ILC_MIRROR; + handle = OS.ImageList_Create (32, 32, flags, 16, 16); + images = new Image [4]; +} + +public int add (Image image) { + int count = OS.ImageList_GetImageCount (handle); + int index = 0; + while (index < count) { + if (images [index] != null) { + if (images [index].isDisposed ()) images [index] = null; + } + if (images [index] == null) break; + index++; + } + if (count == 0) { + Rectangle rect = image.getBounds (); + OS.ImageList_SetIconSize (handle, rect.width, rect.height); + } + set (index, image, count); + if (index == images.length) { + Image [] newImages = new Image [images.length + 4]; + System.arraycopy (images, 0, newImages, 0, images.length); + images = newImages; + } + images [index] = image; + return index; +} + +public int addRef() { + return ++refCount; +} + +int /*long*/ copyBitmap (int /*long*/ hImage, int width, int height) { + BITMAP bm = new BITMAP (); + OS.GetObject (hImage, BITMAP.sizeof, bm); + int /*long*/ hDC = OS.GetDC (0); + int /*long*/ hdc1 = OS.CreateCompatibleDC (hDC); + OS.SelectObject (hdc1, hImage); + int /*long*/ hdc2 = OS.CreateCompatibleDC (hDC); + /* + * Feature in Windows. If a bitmap has a 32-bit depth and any + * pixel has an alpha value different than zero, common controls + * version 6.0 assumes that the bitmap should be alpha blended. + * AlphaBlend() composes the alpha channel of a destination 32-bit + * depth image with the alpha channel of the source image. This + * may cause opaque images to draw transparently. The fix is + * remove the alpha channel of opaque images by down sampling + * it to 24-bit depth. + */ + int /*long*/ hBitmap; + if (bm.bmBitsPixel == 32 && OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER(); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biWidth = width; + bmiHeader.biHeight = -height; + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = (short)24; + if (OS.IsWinCE) bmiHeader.biCompression = OS.BI_BITFIELDS; + else bmiHeader.biCompression = OS.BI_RGB; + byte[] bmi = new byte[BITMAPINFOHEADER.sizeof + (OS.IsWinCE ? 12 : 0)]; + OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + /* Set the rgb colors into the bitmap info */ + if (OS.IsWinCE) { + int redMask = 0xFF00; + int greenMask = 0xFF0000; + int blueMask = 0xFF000000; + /* big endian */ + int offset = BITMAPINFOHEADER.sizeof; + bmi[offset] = (byte)((redMask & 0xFF000000) >> 24); + bmi[offset + 1] = (byte)((redMask & 0xFF0000) >> 16); + bmi[offset + 2] = (byte)((redMask & 0xFF00) >> 8); + bmi[offset + 3] = (byte)((redMask & 0xFF) >> 0); + bmi[offset + 4] = (byte)((greenMask & 0xFF000000) >> 24); + bmi[offset + 5] = (byte)((greenMask & 0xFF0000) >> 16); + bmi[offset + 6] = (byte)((greenMask & 0xFF00) >> 8); + bmi[offset + 7] = (byte)((greenMask & 0xFF) >> 0); + bmi[offset + 8] = (byte)((blueMask & 0xFF000000) >> 24); + bmi[offset + 9] = (byte)((blueMask & 0xFF0000) >> 16); + bmi[offset + 10] = (byte)((blueMask & 0xFF00) >> 8); + bmi[offset + 11] = (byte)((blueMask & 0xFF) >> 0); + } + int /*long*/[] pBits = new int /*long*/[1]; + hBitmap = OS.CreateDIBSection(0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0); + } else { + hBitmap = OS.CreateCompatibleBitmap (hDC, width, height); + } + OS.SelectObject (hdc2, hBitmap); + if (width != bm.bmWidth || height != bm.bmHeight) { + if (!OS.IsWinCE) OS.SetStretchBltMode(hdc2, OS.COLORONCOLOR); + OS.StretchBlt (hdc2, 0, 0, width, height, hdc1, 0, 0, bm.bmWidth, bm.bmHeight, OS.SRCCOPY); + } else { + OS.BitBlt (hdc2, 0, 0, width, height, hdc1, 0, 0, OS.SRCCOPY); + } + OS.DeleteDC (hdc1); + OS.DeleteDC (hdc2); + OS.ReleaseDC (0, hDC); + return hBitmap; +} + +int /*long*/ copyIcon (int /*long*/ hImage, int width, int height) { + if (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + int /*long*/ hIcon = OS.CopyImage (hImage, OS.IMAGE_ICON, width, height, 0); + return hIcon != 0 ? hIcon : hImage; +} + +int /*long*/ copyWithAlpha (int /*long*/ hBitmap, int background, byte[] alphaData, int destWidth, int destHeight) { + BITMAP bm = new BITMAP (); + OS.GetObject (hBitmap, BITMAP.sizeof, bm); + int srcWidth = bm.bmWidth; + int srcHeight = bm.bmHeight; + + /* Create resources */ + int /*long*/ hdc = OS.GetDC (0); + int /*long*/ srcHdc = OS.CreateCompatibleDC (hdc); + int /*long*/ oldSrcBitmap = OS.SelectObject (srcHdc, hBitmap); + int /*long*/ memHdc = OS.CreateCompatibleDC (hdc); + BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER (); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biWidth = srcWidth; + bmiHeader.biHeight = -srcHeight; + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = 32; + bmiHeader.biCompression = OS.BI_RGB; + byte [] bmi = new byte[BITMAPINFOHEADER.sizeof]; + OS.MoveMemory (bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + int /*long*/ [] pBits = new int /*long*/ [1]; + int /*long*/ memDib = OS.CreateDIBSection (0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0); + if (memDib == 0) SWT.error (SWT.ERROR_NO_HANDLES); + int /*long*/ oldMemBitmap = OS.SelectObject (memHdc, memDib); + + BITMAP dibBM = new BITMAP (); + OS.GetObject (memDib, BITMAP.sizeof, dibBM); + int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight; + + /* Get the foreground pixels */ + OS.BitBlt (memHdc, 0, 0, srcWidth, srcHeight, srcHdc, 0, 0, OS.SRCCOPY); + byte[] srcData = new byte [sizeInBytes]; + OS.MoveMemory (srcData, dibBM.bmBits, sizeInBytes); + + /* Merge the alpha channel in place */ + if (alphaData != null) { + int spinc = dibBM.bmWidthBytes - srcWidth * 4; + int ap = 0, sp = 3; + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; ++x) { + srcData [sp] = alphaData [ap++]; + sp += 4; + } + sp += spinc; + } + } else { + byte transRed = (byte)(background & 0xFF); + byte transGreen = (byte)((background >> 8) & 0xFF); + byte transBlue = (byte)((background >> 16) & 0xFF); + final int spinc = dibBM.bmWidthBytes - srcWidth * 4; + int sp = 3; + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; ++x) { + srcData [sp] = (srcData[sp-1] == transRed && srcData[sp-2] == transGreen && srcData[sp-3] == transBlue) ? 0 : (byte)255; + sp += 4; + } + sp += spinc; + } + } + OS.MoveMemory (dibBM.bmBits, srcData, sizeInBytes); + + /* Stretch and free resources */ + if (srcWidth != destWidth || srcHeight != destHeight) { + BITMAPINFOHEADER bmiHeader2 = new BITMAPINFOHEADER (); + bmiHeader2.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader2.biWidth = destWidth; + bmiHeader2.biHeight = -destHeight; + bmiHeader2.biPlanes = 1; + bmiHeader2.biBitCount = 32; + bmiHeader2.biCompression = OS.BI_RGB; + byte [] bmi2 = new byte[BITMAPINFOHEADER.sizeof]; + OS.MoveMemory (bmi2, bmiHeader2, BITMAPINFOHEADER.sizeof); + int /*long*/ [] pBits2 = new int /*long*/ [1]; + int /*long*/ memDib2 = OS.CreateDIBSection (0, bmi2, OS.DIB_RGB_COLORS, pBits2, 0, 0); + int /*long*/ memHdc2 = OS.CreateCompatibleDC (hdc); + int /*long*/ oldMemBitmap2 = OS.SelectObject (memHdc2, memDib2); + if (!OS.IsWinCE) OS.SetStretchBltMode(memHdc2, OS.COLORONCOLOR); + OS.StretchBlt (memHdc2, 0, 0, destWidth, destHeight, memHdc, 0, 0, srcWidth, srcHeight, OS.SRCCOPY); + OS.SelectObject (memHdc2, oldMemBitmap2); + OS.DeleteDC (memHdc2); + OS.SelectObject (memHdc, oldMemBitmap); + OS.DeleteDC (memHdc); + OS.DeleteObject (memDib); + memDib = memDib2; + } else { + OS.SelectObject (memHdc, oldMemBitmap); + OS.DeleteDC (memHdc); + } + OS.SelectObject (srcHdc, oldSrcBitmap); + OS.DeleteDC (srcHdc); + OS.ReleaseDC (0, hdc); + return memDib; +} + +int /*long*/ createMaskFromAlpha (ImageData data, int destWidth, int destHeight) { + int srcWidth = data.width; + int srcHeight = data.height; + ImageData mask = ImageData.internal_new (srcWidth, srcHeight, 1, + new PaletteData(new RGB [] {new RGB (0, 0, 0), new RGB (0xff, 0xff, 0xff)}), + 2, null, 1, null, null, -1, -1, -1, 0, 0, 0, 0); + int ap = 0; + for (int y = 0; y < mask.height; y++) { + for (int x = 0; x < mask.width; x++) { + mask.setPixel (x, y, (data.alphaData [ap++] & 0xff) <= 127 ? 1 : 0); + } + } + int /*long*/ hMask = OS.CreateBitmap (srcWidth, srcHeight, 1, 1, mask.data); + if (srcWidth != destWidth || srcHeight != destHeight) { + int /*long*/ hdc = OS.GetDC (0); + int /*long*/ hdc1 = OS.CreateCompatibleDC (hdc); + OS.SelectObject (hdc1, hMask); + int /*long*/ hdc2 = OS.CreateCompatibleDC (hdc); + int /*long*/ hMask2 = OS.CreateBitmap (destWidth, destHeight, 1, 1, null); + OS.SelectObject (hdc2, hMask2); + if (!OS.IsWinCE) OS.SetStretchBltMode(hdc2, OS.COLORONCOLOR); + OS.StretchBlt (hdc2, 0, 0, destWidth, destHeight, hdc1, 0, 0, srcWidth, srcHeight, OS.SRCCOPY); + OS.DeleteDC (hdc1); + OS.DeleteDC (hdc2); + OS.ReleaseDC (0, hdc); + OS.DeleteObject(hMask); + hMask = hMask2; + } + return hMask; +} + +int /*long*/ createMask (int /*long*/ hBitmap, int destWidth, int destHeight, int background, int transparentPixel) { + BITMAP bm = new BITMAP (); + OS.GetObject (hBitmap, BITMAP.sizeof, bm); + int srcWidth = bm.bmWidth; + int srcHeight = bm.bmHeight; + int /*long*/ hMask = OS.CreateBitmap (destWidth, destHeight, 1, 1, null); + int /*long*/ hDC = OS.GetDC (0); + int /*long*/ hdc1 = OS.CreateCompatibleDC (hDC); + if (background != -1) { + OS.SelectObject (hdc1, hBitmap); + + /* + * If the image has a palette with multiple entries having + * the same color and one of those entries is the transparentPixel, + * only the first entry becomes transparent. To avoid this + * problem, temporarily change the image palette to a palette + * where the transparentPixel is white and everything else is + * black. + */ + boolean isDib = bm.bmBits != 0; + byte[] originalColors = null; + if (!OS.IsWinCE && transparentPixel != -1 && isDib && bm.bmBitsPixel <= 8) { + int maxColors = 1 << bm.bmBitsPixel; + byte[] oldColors = new byte[maxColors * 4]; + OS.GetDIBColorTable(hdc1, 0, maxColors, oldColors); + int offset = transparentPixel * 4; + byte[] newColors = new byte[oldColors.length]; + newColors[offset] = (byte)0xFF; + newColors[offset+1] = (byte)0xFF; + newColors[offset+2] = (byte)0xFF; + OS.SetDIBColorTable(hdc1, 0, maxColors, newColors); + originalColors = oldColors; + OS.SetBkColor (hdc1, 0xFFFFFF); + } else { + OS.SetBkColor (hdc1, background); + } + + int /*long*/ hdc2 = OS.CreateCompatibleDC (hDC); + OS.SelectObject (hdc2, hMask); + if (destWidth != srcWidth || destHeight != srcHeight) { + if (!OS.IsWinCE) OS.SetStretchBltMode (hdc2, OS.COLORONCOLOR); + OS.StretchBlt (hdc2, 0, 0, destWidth, destHeight, hdc1, 0, 0, srcWidth, srcHeight, OS.SRCCOPY); + } else { + OS.BitBlt (hdc2, 0, 0, destWidth, destHeight, hdc1, 0, 0, OS.SRCCOPY); + } + OS.DeleteDC (hdc2); + + /* Put back the original palette */ + if (originalColors != null) OS.SetDIBColorTable(hdc1, 0, 1 << bm.bmBitsPixel, originalColors); + } else { + int /*long*/ hOldBitmap = OS.SelectObject (hdc1, hMask); + OS.PatBlt (hdc1, 0, 0, destWidth, destHeight, OS.BLACKNESS); + OS.SelectObject (hdc1, hOldBitmap); + } + OS.ReleaseDC (0, hDC); + OS.DeleteDC (hdc1); + return hMask; +} + +public void dispose () { + if (handle != 0) OS.ImageList_Destroy (handle); + handle = 0; + images = null; +} + +public Image get (int index) { + return images [index]; +} + +public int getStyle () { + return style; +} + +public int /*long*/ getHandle () { + return handle; +} + +public Point getImageSize() { + int [] cx = new int [1], cy = new int [1]; + OS.ImageList_GetIconSize (handle, cx, cy); + return new Point (cx [0], cy [0]); +} + +public int indexOf (Image image) { + int count = OS.ImageList_GetImageCount (handle); + for (int i=0; i<count; i++) { + if (images [i] != null) { + if (images [i].isDisposed ()) images [i] = null; + if (images [i] != null && images [i].equals (image)) return i; + } + } + return -1; +} + +public void put (int index, Image image) { + int count = OS.ImageList_GetImageCount (handle); + if (!(0 <= index && index < count)) return; + if (image != null) set(index, image, count); + images [index] = image; +} + +public void remove (int index) { + int count = OS.ImageList_GetImageCount (handle); + if (!(0 <= index && index < count)) return; + OS.ImageList_Remove (handle, index); + System.arraycopy (images, index + 1, images, index, --count - index); + images [index] = null; +} + +public int removeRef() { + return --refCount; +} + +void set (int index, Image image, int count) { + int /*long*/ hImage = image.handle; + int [] cx = new int [1], cy = new int [1]; + OS.ImageList_GetIconSize (handle, cx, cy); + switch (image.type) { + case SWT.BITMAP: { + /* + * Note that the image size has to match the image list icon size. + */ + int /*long*/ hBitmap = 0, hMask = 0; + ImageData data = image.getImageData (); + switch (data.getTransparencyType ()) { + case SWT.TRANSPARENCY_ALPHA: + if (OS.COMCTL32_MAJOR >= 6) { + hBitmap = copyWithAlpha (hImage, -1, data.alphaData, cx [0], cy [0]); + } else { + hBitmap = copyBitmap (hImage, cx [0], cy [0]); + hMask = createMaskFromAlpha (data, cx [0], cy [0]); + } + break; + case SWT.TRANSPARENCY_PIXEL: + int background = -1; + Color color = image.getBackground (); + if (color != null) background = color.handle; + hBitmap = copyBitmap (hImage, cx [0], cy [0]); + hMask = createMask (hImage, cx [0], cy [0], background, data.transparentPixel); + break; + case SWT.TRANSPARENCY_NONE: + default: + hBitmap = copyBitmap (hImage, cx [0], cy [0]); + if (index != count) hMask = createMask (hImage, cx [0], cy [0], -1, -1); + break; + } + if (index == count) { + OS.ImageList_Add (handle, hBitmap, hMask); + } else { + /* Note that the mask must always be replaced even for TRANSPARENCY_NONE */ + OS.ImageList_Replace (handle, index, hBitmap, hMask); + } + if (hMask != 0) OS.DeleteObject (hMask); + if (hBitmap != hImage) OS.DeleteObject (hBitmap); + break; + } + case SWT.ICON: { + if (OS.IsWinCE) { + OS.ImageList_ReplaceIcon (handle, index == count ? -1 : index, hImage); + } else { + int /*long*/ hIcon = copyIcon (hImage, cx [0], cy [0]); + OS.ImageList_ReplaceIcon (handle, index == count ? -1 : index, hIcon); + OS.DestroyIcon (hIcon); + } + break; + } + } +} + +public int size () { + int result = 0; + int count = OS.ImageList_GetImageCount (handle); + for (int i=0; i<count; i++) { + if (images [i] != null) { + if (images [i].isDisposed ()) images [i] = null; + if (images [i] != null) result++; + } + } + return result; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Button.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Button.java new file mode 100755 index 0000000000..130a78aa3c --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Button.java @@ -0,0 +1,1388 @@ +/******************************************************************************* + * 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.win32.*; +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>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 = "", message = ""; + Image image, image2, disabledImage; + ImageList imageList; + boolean ignoreMouse, grayed; + static final int MARGIN = 4; + static final int CHECK_WIDTH, CHECK_HEIGHT; + static final int ICON_WIDTH = 128, ICON_HEIGHT = 128; + static /*final*/ boolean COMMAND_LINK = false; + static final int /*long*/ ButtonProc; + static final TCHAR ButtonClass = new TCHAR (0, "BUTTON", true); + static { + int /*long*/ hBitmap = OS.LoadBitmap (0, OS.OBM_CHECKBOXES); + if (hBitmap == 0) { + CHECK_WIDTH = OS.GetSystemMetrics (OS.IsWinCE ? OS.SM_CXSMICON : OS.SM_CXVSCROLL); + CHECK_HEIGHT = OS.GetSystemMetrics (OS.IsWinCE ? OS.SM_CYSMICON : OS.SM_CYVSCROLL); + } else { + BITMAP bitmap = new BITMAP (); + OS.GetObject (hBitmap, BITMAP.sizeof, bitmap); + OS.DeleteObject (hBitmap); + CHECK_WIDTH = bitmap.bmWidth / 4; + CHECK_HEIGHT = bitmap.bmHeight / 3; + } + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, ButtonClass, lpWndClass); + ButtonProc = lpWndClass.lpfnWndProc; + } + +/** + * 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)); +} + +void _setImage (Image image) { + if ((style & SWT.COMMAND) != 0) return; + if (OS.COMCTL32_MAJOR >= 6) { + if (imageList != null) imageList.dispose (); + imageList = null; + if (image != null) { + imageList = new ImageList (style & SWT.RIGHT_TO_LEFT); + if (OS.IsWindowEnabled (handle)) { + imageList.add (image); + } else { + if (disabledImage != null) disabledImage.dispose (); + disabledImage = new Image (display, image, SWT.IMAGE_DISABLE); + imageList.add (disabledImage); + } + BUTTON_IMAGELIST buttonImageList = new BUTTON_IMAGELIST (); + buttonImageList.himl = imageList.getHandle (); + int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits; + newBits &= ~(OS.BS_LEFT | OS.BS_CENTER | OS.BS_RIGHT); + if ((style & SWT.LEFT) != 0) newBits |= OS.BS_LEFT; + if ((style & SWT.CENTER) != 0) newBits |= OS.BS_CENTER; + if ((style & SWT.RIGHT) != 0) newBits |= OS.BS_RIGHT; + if (text.length () == 0) { + if ((style & SWT.LEFT) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT; + if ((style & SWT.CENTER) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_CENTER; + if ((style & SWT.RIGHT) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_RIGHT; + } else { + buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT; + buttonImageList.margin_left = computeLeftMargin (); + buttonImageList.margin_right = MARGIN; + newBits &= ~(OS.BS_CENTER | OS.BS_RIGHT); + newBits |= OS.BS_LEFT; + } + if (newBits != oldBits) { + OS.SetWindowLong (handle, OS.GWL_STYLE, newBits); + OS.InvalidateRect (handle, null, true); + } + OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList); + } else { + OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, 0); + } + /* + * Bug in Windows. Under certain cirumstances yet to be + * isolated, BCM_SETIMAGELIST does not redraw the control + * when a new image is set. The fix is to force a redraw. + */ + OS.InvalidateRect (handle, null, true); + } else { + if (image2 != null) image2.dispose (); + image2 = null; + int /*long*/ hImage = 0; + int imageBits = 0, fImageType = 0; + if (image != null) { + switch (image.type) { + case SWT.BITMAP: { + Rectangle rect = image.getBounds (); + ImageData data = image.getImageData (); + switch (data.getTransparencyType ()) { + case SWT.TRANSPARENCY_PIXEL: + if (rect.width <= ICON_WIDTH && rect.height <= ICON_HEIGHT) { + image2 = new Image (display, data, data.getTransparencyMask ()); + hImage = image2.handle; + imageBits = OS.BS_ICON; + fImageType = OS.IMAGE_ICON; + break; + } + //FALL THROUGH + case SWT.TRANSPARENCY_ALPHA: + image2 = new Image (display, rect.width, rect.height); + GC gc = new GC (image2); + gc.setBackground (getBackground ()); + gc.fillRectangle (rect); + gc.drawImage (image, 0, 0); + gc.dispose (); + hImage = image2.handle; + imageBits = OS.BS_BITMAP; + fImageType = OS.IMAGE_BITMAP; + break; + case SWT.TRANSPARENCY_NONE: + hImage = image.handle; + imageBits = OS.BS_BITMAP; + fImageType = OS.IMAGE_BITMAP; + break; + } + break; + } + case SWT.ICON: { + hImage = image.handle; + imageBits = OS.BS_ICON; + fImageType = OS.IMAGE_ICON; + break; + } + } + /* + * Feature in Windows. The button control mirrors its image when the + * flag WS_EX_LAYOUTRTL is set. This behaviour is not desirable in SWT. + * The fix is to set a mirrored version of real image in the button. + */ + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) { + Rectangle rect = image.getBounds (); + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ dstHdc = OS.CreateCompatibleDC (hDC); + int /*long*/ hBitmap = OS.CreateCompatibleBitmap (hDC, rect.width, rect.height); + int /*long*/ oldBitmap = OS.SelectObject (dstHdc, hBitmap); + OS.SetLayout (dstHdc, OS.LAYOUT_RTL); + if (fImageType == OS.IMAGE_BITMAP) { + int /*long*/ srcHdc = OS.CreateCompatibleDC (hDC); + int /*long*/ oldSrcBitmap = OS.SelectObject (srcHdc, hImage); + OS.SetLayout (dstHdc, 0); + OS.BitBlt (dstHdc, 0, 0, rect.width, rect.height, srcHdc, 0, 0, OS.SRCCOPY); + OS.SelectObject (srcHdc, oldSrcBitmap); + OS.DeleteDC (srcHdc); + } else { + Control control = findBackgroundControl (); + if (control == null) control = this; + int /*long*/ newBrush = OS.CreateSolidBrush (control.getBackgroundPixel ()); + int /*long*/ oldBrush = OS.SelectObject (dstHdc, newBrush); + OS.PatBlt (dstHdc, 0, 0, rect.width, rect.height, OS.PATCOPY); + OS.DrawIconEx (dstHdc, 0, 0, hImage, 0, 0, 0, 0, OS.DI_NORMAL); + OS.SelectObject (dstHdc, oldBrush); + OS.DeleteObject (newBrush); + } + OS.SelectObject (dstHdc, oldBitmap); + OS.DeleteDC (dstHdc); + OS.ReleaseDC (handle, hDC); + if (image2 != null) image2.dispose (); + image2 = Image.win32_new (display, SWT.BITMAP, hBitmap); + imageBits = OS.BS_BITMAP; + fImageType = OS.IMAGE_BITMAP; + hImage = hBitmap; + } + } + } + int newBits = OS.GetWindowLong (handle, OS.GWL_STYLE), oldBits = newBits; + newBits &= ~(OS.BS_BITMAP | OS.BS_ICON); + newBits |= imageBits; + if (newBits != oldBits) OS.SetWindowLong (handle, OS.GWL_STYLE, newBits); + OS.SendMessage (handle, OS.BM_SETIMAGE, fImageType, hImage); + } +} + +void _setText (String text) { + int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits; + if (OS.COMCTL32_MAJOR >= 6) { + newBits &= ~(OS.BS_LEFT | OS.BS_CENTER | OS.BS_RIGHT); + if ((style & SWT.LEFT) != 0) newBits |= OS.BS_LEFT; + if ((style & SWT.CENTER) != 0) newBits |= OS.BS_CENTER; + if ((style & SWT.RIGHT) != 0) newBits |= OS.BS_RIGHT; + if (imageList != null) { + BUTTON_IMAGELIST buttonImageList = new BUTTON_IMAGELIST (); + buttonImageList.himl = imageList.getHandle (); + if (text.length () == 0) { + if ((style & SWT.LEFT) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT; + if ((style & SWT.CENTER) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_CENTER; + if ((style & SWT.RIGHT) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_RIGHT; + } else { + buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT; + buttonImageList.margin_left = computeLeftMargin (); + buttonImageList.margin_right = MARGIN; + newBits &= ~(OS.BS_CENTER | OS.BS_RIGHT); + newBits |= OS.BS_LEFT; + } + OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList); + } + } else { + newBits &= ~(OS.BS_BITMAP | OS.BS_ICON); + } + if (newBits != oldBits) { + OS.SetWindowLong (handle, OS.GWL_STYLE, newBits); + OS.InvalidateRect (handle, null, true); + } + /* + * Bug in Windows. When a Button control is right-to-left and + * is disabled, the first pixel of the text is clipped. The + * fix is to add a space to both sides of the text. + */ + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) { + text = OS.IsWindowEnabled (handle) ? text : " " + text + " "; + } + } + TCHAR buffer = new TCHAR (getCodePage (), text, true); + OS.SetWindowText (handle, buffer); +} + +/** + * 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); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + return OS.CallWindowProc (ButtonProc, hwnd, msg, wParam, lParam); +} + +static int checkStyle (int style) { + style = checkBits (style, SWT.PUSH, SWT.ARROW, SWT.CHECK, SWT.RADIO, SWT.TOGGLE, COMMAND_LINK ? SWT.COMMAND : 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 () { + /* + * Feature in Windows. BM_CLICK sends a fake WM_LBUTTONDOWN and + * WM_LBUTTONUP in order to click the button. This causes the + * application to get unexpected mouse events. The fix is to + * ignore mouse events when they are caused by BM_CLICK. + */ + ignoreMouse = true; + OS.SendMessage (handle, OS.BM_CLICK, 0, 0); + ignoreMouse = false; +} + +int computeLeftMargin () { + if (OS.COMCTL32_MAJOR < 6) return MARGIN; + if ((style & (SWT.PUSH | SWT.TOGGLE)) == 0) return MARGIN; + int margin = 0; + if (image != null && text.length () != 0) { + Rectangle bounds = image.getBounds (); + margin += bounds.width + MARGIN * 2; + int /*long*/ oldFont = 0; + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + TCHAR buffer = new TCHAR (getCodePage (), text, true); + RECT rect = new RECT (); + int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE; + OS.DrawText (hDC, buffer, -1, rect, flags); + margin += rect.right - rect.left; + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + OS.GetClientRect (handle, rect); + margin = Math.max (MARGIN, (rect.right - rect.left - margin) / 2); + } + return margin; +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + int width = 0, height = 0, border = getBorderWidth (); + if ((style & SWT.ARROW) != 0) { + if ((style & (SWT.UP | SWT.DOWN)) != 0) { + width += OS.GetSystemMetrics (OS.SM_CXVSCROLL); + height += OS.GetSystemMetrics (OS.SM_CYVSCROLL); + } else { + width += OS.GetSystemMetrics (OS.SM_CXHSCROLL); + height += OS.GetSystemMetrics (OS.SM_CYHSCROLL); + } + } else { + if ((style & SWT.COMMAND) != 0) { + SIZE size = new SIZE (); + if (wHint != SWT.DEFAULT) { + size.cx = wHint; + OS.SendMessage (handle, OS.BCM_GETIDEALSIZE, 0, size); + width = size.cx; + height = size.cy; + } else { + OS.SendMessage (handle, OS.BCM_GETIDEALSIZE, 0, size); + width = size.cy; + height = size.cy; + size.cy = 0; + while (size.cy != height) { + size.cx = width++; + size.cy = 0; + OS.SendMessage (handle, OS.BCM_GETIDEALSIZE, 0, size); + } + } + } else { + int extra = 0; + boolean hasImage = image != null, hasText = true; + if (OS.COMCTL32_MAJOR < 6) { + if ((style & SWT.PUSH) == 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + hasImage = (bits & (OS.BS_BITMAP | OS.BS_ICON)) != 0; + if (hasImage) hasText = false; + } + } + if (hasImage) { + if (image != null) { + Rectangle rect = image.getBounds (); + width = rect.width; + if (hasText && text.length () != 0) { + width += MARGIN * 2; + } + height = rect.height; + extra = MARGIN * 2; + } + } + if (hasText) { + int /*long*/ oldFont = 0; + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA (); + OS.GetTextMetrics (hDC, lptm); + int length = text.length (); + if (length == 0) { + height = Math.max (height, lptm.tmHeight); + } else { + extra = Math.max (MARGIN * 2, lptm.tmAveCharWidth); + TCHAR buffer = new TCHAR (getCodePage (), text, true); + RECT rect = new RECT (); + int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE; + OS.DrawText (hDC, buffer, -1, rect, flags); + width += rect.right - rect.left; + height = Math.max (height, rect.bottom - rect.top); + } + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + } + if ((style & (SWT.CHECK | SWT.RADIO)) != 0) { + width += CHECK_WIDTH + extra; + height = Math.max (height, CHECK_HEIGHT + 3); + } + if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) { + width += 12; height += 10; + } + } + } + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + width += border * 2; + height += border * 2; + return new Point (width, height); +} + +void createHandle () { + /* + * Feature in Windows. When a button is created, + * it clears the UI state for all controls in the + * shell by sending WM_CHANGEUISTATE with UIS_SET, + * UISF_HIDEACCEL and UISF_HIDEFOCUS to the parent. + * This is undocumented and unexpected. The fix + * is to ignore the WM_CHANGEUISTATE, when sent + * from CreateWindowEx(). + */ + parent.state |= IGNORE_WM_CHANGEUISTATE; + super.createHandle (); + parent.state &= ~IGNORE_WM_CHANGEUISTATE; + + /* Set the theme background */ + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + /* + * NOTE: On Vista this causes problems when the tab + * key is pressed for push buttons so disable the + * theme background drawing for these widgets for + * now. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION < OS.VERSION (6, 0)) { + state |= THEME_BACKGROUND; + } else { + if ((style & (SWT.PUSH | SWT.TOGGLE)) == 0) { + state |= THEME_BACKGROUND; + } + } + } + + /* + * Bug in Windows. For some reason, the HBRUSH that + * is returned from WM_CTRLCOLOR is misaligned when + * the button uses it to draw. If the brush is a solid + * color, this does not matter. However, if the brush + * contains an image, the image is misaligned. The + * fix is to draw the background in WM_CTRLCOLOR. + * + * NOTE: For comctl32.dll 6.0 with themes disabled, + * drawing in WM_ERASEBKGND will draw on top of the + * text of the control. + */ + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if ((style & SWT.RADIO) != 0) state |= DRAW_BACKGROUND; + } + + /* + * Feature in Windows. Push buttons draw border around + * the button using the default background color instead + * of using the color provided by WM_CTRLCOLOR. The fix + * is to draw the background in WM_CTRLCOLOR. + * + * NOTE: On Vista this causes problems when the tab key + * is pressed for push buttons so disable the fix for now. + */ + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if (!OS.IsWinCE && OS.WIN32_VERSION < OS.VERSION (6, 0)) { + if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) { + state |= DRAW_BACKGROUND; + } + } + } +} + +int defaultBackground () { + if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) { + return OS.GetSysColor (OS.COLOR_BTNFACE); + } + return super.defaultBackground (); +} + +int defaultForeground () { + return OS.GetSysColor (OS.COLOR_BTNTEXT); +} + +void enableWidget (boolean enabled) { + super.enableWidget (enabled); + /* + * Bug in Windows. When a button control is right-to-left and + * is disabled, the first pixel of the text is clipped. The + * fix is to add a space to both sides of the text. + */ + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + boolean hasImage = (bits & (OS.BS_BITMAP | OS.BS_ICON)) != 0; + if (!hasImage) { + String string = enabled ? text : " " + text + " "; + TCHAR buffer = new TCHAR (getCodePage (), string, true); + OS.SetWindowText (handle, buffer); + } + } + } + /* + * Bug in Windows. When a button has the style BS_CHECKBOX + * or BS_RADIOBUTTON, is checked, and is displaying both an + * image and some text, when BCM_SETIMAGELIST is used to + * assign an image list for each of the button states, the + * button does not draw properly. When the user drags the + * mouse in and out of the button, it draws using a blank + * image. The fix is to set the complete image list only + * when the button is disabled. + */ + if (OS.COMCTL32_MAJOR >= 6) { + if (imageList != null) { + BUTTON_IMAGELIST buttonImageList = new BUTTON_IMAGELIST (); + OS.SendMessage (handle, OS.BCM_GETIMAGELIST, 0, buttonImageList); + if (imageList != null) imageList.dispose (); + imageList = new ImageList (style & SWT.RIGHT_TO_LEFT); + if (OS.IsWindowEnabled (handle)) { + imageList.add (image); + } else { + if (disabledImage != null) disabledImage.dispose (); + disabledImage = new Image (display, image, SWT.IMAGE_DISABLE); + imageList.add (disabledImage); + } + buttonImageList.himl = imageList.getHandle (); + OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList); + /* + * Bug in Windows. Under certain cirumstances yet to be + * isolated, BCM_SETIMAGELIST does not redraw the control + * when an image is set. The fix is to force a redraw. + */ + OS.InvalidateRect (handle, null, true); + } + } +} + +/** + * 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; +} + +boolean getDefault () { + if ((style & SWT.PUSH) == 0) return false; + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + return (bits & OS.BS_DEFPUSHBUTTON) != 0; +} + +/** + * 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; +} + +/** + * Returns the widget message. When the widget is created + * with the style <code>SWT.COMMAND</code>, the message text + * is displayed to provide further information for the user. + * + * @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; +} + +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; + int /*long*/ flags = OS.SendMessage (handle, OS.BM_GETCHECK, 0, 0); + return flags != OS.BST_UNCHECKED; +} + +/** + * 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 (); + if ((style & SWT.ARROW) != 0) return ""; + return text; +} + +boolean isTabItem () { + if ((style & SWT.PUSH) != 0) return isTabGroup (); + return super.isTabItem (); +} + +boolean mnemonicHit (char ch) { + if (!setFocus ()) return false; + /* + * Feature in Windows. When a radio button gets focus, + * it selects the button in WM_SETFOCUS. Therefore, it + * is not necessary to click the button or send events + * because this has already happened in WM_SETFOCUS. + */ + if ((style & SWT.RADIO) == 0) click (); + return true; +} + +boolean mnemonicMatch (char key) { + char mnemonic = findMnemonic (getText ()); + if (mnemonic == '\0') return false; + return Character.toUpperCase (key) == Character.toUpperCase (mnemonic); +} + +void releaseWidget () { + super.releaseWidget (); + if (imageList != null) imageList.dispose (); + imageList = null; + if (disabledImage != null) disabledImage.dispose (); + disabledImage = null; + if (image2 != null) image2.dispose (); + image2 = null; + text = null; + image = 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); +} + +/** + * 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 (); + 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); + OS.InvalidateRect (handle, null, true); + 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); + int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits; + newBits &= ~(OS.BS_LEFT | OS.BS_CENTER | OS.BS_RIGHT); + if ((style & SWT.LEFT) != 0) newBits |= OS.BS_LEFT; + if ((style & SWT.CENTER) != 0) newBits |= OS.BS_CENTER; + if ((style & SWT.RIGHT) != 0) newBits |= OS.BS_RIGHT; + if (OS.COMCTL32_MAJOR >= 6) { + if (imageList != null) { + BUTTON_IMAGELIST buttonImageList = new BUTTON_IMAGELIST (); + buttonImageList.himl = imageList.getHandle (); + if (text.length () == 0) { + if ((style & SWT.LEFT) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT; + if ((style & SWT.CENTER) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_CENTER; + if ((style & SWT.RIGHT) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_RIGHT; + } else { + buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT; + buttonImageList.margin_left = computeLeftMargin (); + buttonImageList.margin_right = MARGIN; + newBits &= ~(OS.BS_CENTER | OS.BS_RIGHT); + newBits |= OS.BS_LEFT; + } + OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList); + } + } + if (newBits != oldBits) { + OS.SetWindowLong (handle, OS.GWL_STYLE, newBits); + OS.InvalidateRect (handle, null, true); + } +} + +void setDefault (boolean value) { + if ((style & SWT.PUSH) == 0) return; + int /*long*/ hwndShell = menuShell ().handle; + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if (value) { + bits |= OS.BS_DEFPUSHBUTTON; + OS.SendMessage (hwndShell, OS.DM_SETDEFID, handle, 0); + } else { + bits &= ~OS.BS_DEFPUSHBUTTON; + OS.SendMessage (hwndShell, OS.DM_SETDEFID, 0, 0); + } + OS.SendMessage (handle, OS.BM_SETSTYLE, bits, 1); +} + +boolean setFixedFocus () { + /* + * Feature in Windows. When a radio button gets focus, + * it selects the button in WM_SETFOCUS. The fix is to + * not assign focus to an unselected radio button. + */ + if ((style & SWT.RADIO) != 0 && !getSelection ()) return false; + return super.setFixedFocus (); +} + +/** + * 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; + /* This code is intentionally commented */ +// if (OS.COMCTL32_MAJOR < 6) { +// if (image == null || text.length () != 0) { +// _setText (text); +// return; +// } +// } + _setImage (image); +} + +/** + * 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; + this.grayed = grayed; + int /*long*/ flags = OS.SendMessage (handle, OS.BM_GETCHECK, 0, 0); + if (grayed) { + if (flags == OS.BST_CHECKED) updateSelection (OS.BST_INDETERMINATE); + } else { + if (flags == OS.BST_INDETERMINATE) updateSelection (OS.BST_CHECKED); + } +} + +/** + * Sets the widget message. When the widget is created + * with the style <code>SWT.COMMAND</code>, the message text + * is displayed to provide further information for the user. + * + * @param message the new message + * + * @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> + * + * @since 3.3 + */ +/*public*/ void setMessage (String message) { + checkWidget (); + if (message == null) error (SWT.ERROR_NULL_ARGUMENT); + this.message = message; + if (OS.COMCTL32_VERSION >= OS.VERSION (6, 1)) { + if ((style & SWT.COMMAND) != 0) { + int length = message.length (); + char [] chars = new char [length + 1]; + message.getChars(0, length, chars, 0); + OS.SendMessage (handle, OS.BCM_SETNOTE, 0, chars); + } + } +} + +boolean setRadioFocus (boolean tabbing) { + if ((style & SWT.RADIO) == 0 || !getSelection ()) return false; + return tabbing ? setTabItemFocus () : setFocus (); +} + +boolean setRadioSelection (boolean value) { + if ((style & SWT.RADIO) == 0) return false; + if (getSelection () != value) { + setSelection (value); + postEvent (SWT.Selection); + } + return true; +} + +boolean setSavedFocus () { + /* + * Feature in Windows. When a radio button gets focus, + * it selects the button in WM_SETFOCUS. If the previous + * saved focus widget was a radio button, allowing the shell + * to automatically restore the focus to the previous radio + * button will unexpectedly check that button. The fix is to + * not assign focus to an unselected radio button. + */ + if ((style & SWT.RADIO) != 0 && !getSelection ()) return false; + return super.setSavedFocus (); +} + +/** + * 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; + int flags = selected ? OS.BST_CHECKED : OS.BST_UNCHECKED; + if ((style & SWT.CHECK) != 0) { + if (selected && grayed) flags = OS.BST_INDETERMINATE; + } + updateSelection (flags); +} + +/** + * 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; + /* This code is intentionally commented */ +// if (OS.COMCTL32_MAJOR < 6) { +// if (text.length () == 0 && image != null) { +// _setImage (image); +// return; +// } +// } + _setText (string); +} + +void updateSelection (int flags) { + if (flags != OS.SendMessage (handle, OS.BM_GETCHECK, 0, 0)) { + /* + * Feature in Windows. When BM_SETCHECK is used + * to set the checked state of a radio or check + * button, it sets the WM_TABSTOP style. This + * is undocumented and unwanted. The fix is + * to save and restore the window style bits. + */ + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((style & SWT.CHECK) != 0) { + if (flags == OS.BST_INDETERMINATE) { + bits &= ~OS.BS_CHECKBOX; + bits |= OS.BS_3STATE; + } else { + bits |= OS.BS_CHECKBOX; + bits &= ~OS.BS_3STATE; + } + if (bits != OS.GetWindowLong (handle, OS.GWL_STYLE)) { + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); + } + } + OS.SendMessage (handle, OS.BM_SETCHECK, flags, 0); + if (bits != OS.GetWindowLong (handle, OS.GWL_STYLE)) { + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); + } + } +} + +int widgetStyle () { + int bits = super.widgetStyle (); + if ((style & SWT.FLAT) != 0) bits |= OS.BS_FLAT; + if ((style & SWT.ARROW) != 0) return bits | OS.BS_OWNERDRAW; + if ((style & SWT.LEFT) != 0) bits |= OS.BS_LEFT; + if ((style & SWT.CENTER) != 0) bits |= OS.BS_CENTER; + if ((style & SWT.RIGHT) != 0) bits |= OS.BS_RIGHT; + if ((style & SWT.PUSH) != 0) return bits | OS.BS_PUSHBUTTON | OS.WS_TABSTOP; + if ((style & SWT.CHECK) != 0) return bits | OS.BS_CHECKBOX | OS.WS_TABSTOP; + if ((style & SWT.RADIO) != 0) return bits | OS.BS_RADIOBUTTON; + if ((style & SWT.TOGGLE) != 0) return bits | OS.BS_PUSHLIKE | OS.BS_CHECKBOX | OS.WS_TABSTOP; + if ((style & SWT.COMMAND) != 0) return bits | OS.BS_COMMANDLINK | OS.WS_TABSTOP; + return bits | OS.BS_PUSHBUTTON | OS.WS_TABSTOP; +} + +TCHAR windowClass () { + return ButtonClass; +} + +int /*long*/ windowProc () { + return ButtonProc; +} + + +LRESULT WM_ERASEBKGND (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_ERASEBKGND (wParam, lParam); + if (result != null) return result; + /* + * Bug in Windows. For some reason, the HBRUSH that + * is returned from WM_CTRLCOLOR is misaligned when + * the button uses it to draw. If the brush is a solid + * color, this does not matter. However, if the brush + * contains an image, the image is misaligned. The + * fix is to draw the background in WM_ERASEBKGND. + */ + if (OS.COMCTL32_MAJOR < 6) { + if ((style & (SWT.RADIO | SWT.CHECK)) != 0) { + if (findImageControl () != null) { + drawBackground (wParam); + return LRESULT.ONE; + } + } + } + return result; +} + +LRESULT WM_GETDLGCODE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_GETDLGCODE (wParam, lParam); + if (result != null) return result; + if ((style & SWT.ARROW) != 0) { + return new LRESULT (OS.DLGC_STATIC); + } + return result; +} + +LRESULT WM_KILLFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_KILLFOCUS (wParam, lParam); + if ((style & SWT.PUSH) != 0 && getDefault ()) { + menuShell ().setDefaultButton (null, false); + } + return result; +} + +LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreMouse) return null; + return super.WM_LBUTTONDOWN (wParam, lParam); +} + +LRESULT WM_LBUTTONUP (int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreMouse) return null; + return super.WM_LBUTTONUP (wParam, lParam); +} + +LRESULT WM_SETFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. When Windows sets focus to + * a radio button, it sets the WM_TABSTOP style. + * This is undocumented and unwanted. The fix is + * to save and restore the window style bits. + */ + int bits = 0; + if ((style & SWT.RADIO) != 0) { + bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + } + LRESULT result = super.WM_SETFOCUS (wParam, lParam); + if ((style & SWT.RADIO) != 0) { + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); + } + if ((style & SWT.PUSH) != 0) { + menuShell ().setDefaultButton (this, false); + } + return result; +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SIZE (wParam, lParam); + if (result != null) return result; + if (OS.COMCTL32_MAJOR >= 6) { + if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) { + if (imageList != null && text.length () != 0) { + BUTTON_IMAGELIST buttonImageList = new BUTTON_IMAGELIST (); + OS.SendMessage (handle, OS.BCM_GETIMAGELIST, 0, buttonImageList); + buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT; + buttonImageList.margin_left = computeLeftMargin (); + buttonImageList.margin_right = MARGIN; + OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList); + } + } + } + return result; +} + +LRESULT WM_SYSCOLORCHANGE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam); + if (result != null) return result; + if (image2 != null) _setImage (image); + return result; +} + +LRESULT WM_UPDATEUISTATE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_UPDATEUISTATE (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. When WM_UPDATEUISTATE is sent to + * a button, it sends WM_CTLCOLORBTN to get the foreground + * and background. If drawing happens in WM_CTLCOLORBTN, + * it will overwrite the contents of the control. The + * fix is draw the button without drawing the background + * and avoid the button window proc. + * + * NOTE: This only happens for radio, check and toggle + * buttons. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + if ((style & (SWT.RADIO | SWT.CHECK | SWT.TOGGLE)) != 0) { + boolean redraw = findImageControl () != null; + if (!redraw) { + if ((state & THEME_BACKGROUND) != 0) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + redraw = findThemeControl () != null; + } + } + if (!redraw) redraw = findBackgroundControl () != null; + } + if (redraw) { + OS.InvalidateRect (handle, null, false); + int /*long*/ code = OS.DefWindowProc (handle, OS.WM_UPDATEUISTATE, wParam, lParam); + return new LRESULT (code); + } + } + } + /* + * Feature in Windows. Push and toggle buttons draw directly + * in WM_UPDATEUISTATE rather than damaging and drawing later + * in WM_PAINT. This means that clients who hook WM_PAINT + * expecting to get all the drawing will not. The fix is to + * redraw the control when paint events are hooked. + */ + if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) { + if (hooks (SWT.Paint) || filters (SWT.Paint)) OS.InvalidateRect (handle, null, true); + } + return result; +} + +LRESULT wmCommandChild (int /*long*/ wParam, int /*long*/ lParam) { + int code = OS.HIWORD (wParam); + switch (code) { + case OS.BN_CLICKED: + case OS.BN_DOUBLECLICKED: + if ((style & (SWT.CHECK | SWT.TOGGLE)) != 0) { + setSelection (!getSelection ()); + } else { + if ((style & SWT.RADIO) != 0) { + if ((parent.getStyle () & SWT.NO_RADIO_GROUP) != 0) { + setSelection (!getSelection ()); + } else { + selectRadio (); + } + } + } + postEvent (SWT.Selection); + } + return super.wmCommandChild (wParam, lParam); +} + +LRESULT wmColorChild (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Bug in Windows. For some reason, the HBRUSH that + * is returned from WM_CTRLCOLOR is misaligned when + * the button uses it to draw. If the brush is a solid + * color, this does not matter. However, if the brush + * contains an image, the image is misaligned. The + * fix is to draw the background in WM_ERASEBKGND. + */ + LRESULT result = super.wmColorChild (wParam, lParam); + if (OS.COMCTL32_MAJOR < 6) { + if ((style & (SWT.RADIO | SWT.CHECK)) != 0) { + if (findImageControl () != null) { + OS.SetBkMode (wParam, OS.TRANSPARENT); + return new LRESULT (OS.GetStockObject (OS.NULL_BRUSH)); + } + } + } + return result; +} + +LRESULT wmDrawChild (int /*long*/ wParam, int /*long*/ lParam) { + if ((style & SWT.ARROW) == 0) return super.wmDrawChild (wParam, lParam); + DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT (); + OS.MoveMemory (struct, lParam, DRAWITEMSTRUCT.sizeof); + RECT rect = new RECT (); + OS.SetRect (rect, struct.left, struct.top, struct.right, struct.bottom); + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + int iStateId = OS.ABS_LEFTNORMAL; + switch (style & (SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT)) { + case SWT.UP: iStateId = OS.ABS_UPNORMAL; break; + case SWT.DOWN: iStateId = OS.ABS_DOWNNORMAL; break; + case SWT.LEFT: iStateId = OS.ABS_LEFTNORMAL; break; + case SWT.RIGHT: iStateId = OS.ABS_RIGHTNORMAL; break; + } + /* + * Feature in Windows. On Vista only, DrawThemeBackground() + * does not mirror the drawing. The fix is switch left to right + * and right to left. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + if ((style & SWT.MIRRORED) != 0) { + if ((style & (SWT.LEFT | SWT.RIGHT)) != 0) { + iStateId = iStateId == OS.ABS_RIGHTNORMAL ? OS.ABS_LEFTNORMAL : OS.ABS_RIGHTNORMAL; + } + } + } + /* + * NOTE: The normal, hot, pressed and disabled state is + * computed relying on the fact that the increment between + * the direction states is invariant (always separated by 4). + */ + if (!getEnabled ()) iStateId += OS.ABS_UPDISABLED - OS.ABS_UPNORMAL; + if ((struct.itemState & OS.ODS_SELECTED) != 0) iStateId += OS.ABS_UPPRESSED - OS.ABS_UPNORMAL; + OS.DrawThemeBackground (display.hScrollBarTheme (), struct.hDC, OS.SBP_ARROWBTN, iStateId, rect, null); + } else { + int uState = OS.DFCS_SCROLLLEFT; + switch (style & (SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT)) { + case SWT.UP: uState = OS.DFCS_SCROLLUP; break; + case SWT.DOWN: uState = OS.DFCS_SCROLLDOWN; break; + case SWT.LEFT: uState = OS.DFCS_SCROLLLEFT; break; + case SWT.RIGHT: uState = OS.DFCS_SCROLLRIGHT; break; + } + if (!getEnabled ()) uState |= OS.DFCS_INACTIVE; + if ((style & SWT.FLAT) == SWT.FLAT) uState |= OS.DFCS_FLAT; + if ((struct.itemState & OS.ODS_SELECTED) != 0) uState |= OS.DFCS_PUSHED; + OS.DrawFrameControl (struct.hDC, rect, OS.DFC_SCROLL, uState); + } + return null; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Canvas.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Canvas.java new file mode 100755 index 0000000000..3440be9300 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Canvas.java @@ -0,0 +1,490 @@ +/******************************************************************************* + * 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.widgets; + + +import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * 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; + +/** + * Prevents uninitialized instances from being created outside the package. + */ +Canvas () { +} + +/** + * 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); +} + +void clearArea (int x, int y, int width, int height) { + checkWidget (); + if (OS.IsWindowVisible (handle)) { + RECT rect = new RECT (); + OS.SetRect (rect, x, y, x + width, y + height); + int /*long*/ hDC = OS.GetDCEx (handle, 0, OS.DCX_CACHE | OS.DCX_CLIPCHILDREN | OS.DCX_CLIPSIBLINGS); + drawBackground (hDC, rect); + OS.ReleaseDC (handle, hDC); + } +} + +/** + * 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); + RECT rect = new RECT (); + OS.SetRect (rect, x, y, x + width, y + height); + int /*long*/ hDC = gc.handle; + int pixel = background == -1 ? gc.getBackground ().handle : -1; + drawBackground (hDC, rect, pixel); +} + +/** + * 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; +} + +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 (); + forceResize (); + boolean isFocus = caret != null && caret.isFocusCaret (); + if (isFocus) caret.killFocus (); + RECT sourceRect = new RECT (); + OS.SetRect (sourceRect, x, y, x + width, y + height); + RECT clientRect = new RECT (); + OS.GetClientRect (handle, clientRect); + if (OS.IntersectRect (clientRect, sourceRect, clientRect)) { + if (OS.IsWinCE) { + OS.UpdateWindow (handle); + } else { + int flags = OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, null, 0, flags); + } + } + int deltaX = destX - x, deltaY = destY - y; + if (findImageControl () != null) { + if (OS.IsWinCE) { + OS.InvalidateRect (handle, sourceRect, true); + } else { + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE; + if (all) flags |= OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, sourceRect, 0, flags); + } + OS.OffsetRect (sourceRect, deltaX, deltaY); + if (OS.IsWinCE) { + OS.InvalidateRect (handle, sourceRect, true); + } else { + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE; + if (all) flags |= OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, sourceRect, 0, flags); + } + } else { + int flags = OS.SW_INVALIDATE | OS.SW_ERASE; + /* + * Feature in Windows. If any child in the widget tree partially + * intersects the scrolling rectangle, Windows moves the child + * and copies the bits that intersect the scrolling rectangle but + * does not redraw the child. + * + * Feature in Windows. When any child in the widget tree does not + * intersect the scrolling rectangle but the parent does intersect, + * Windows does not move the child. This is the documented (but + * strange) Windows behavior. + * + * The fix is to not use SW_SCROLLCHILDREN and move the children + * explicitly after scrolling. + */ +// if (all) flags |= OS.SW_SCROLLCHILDREN; + OS.ScrollWindowEx (handle, deltaX, deltaY, sourceRect, null, 0, null, flags); + } + 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 (); +} + +/** + * 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); +} + +/** + * 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; +} + +TCHAR windowClass () { + if (display.useOwnDC) return display.windowOwnDCClass; + return super.windowClass (); +} + +int /*long*/ windowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (msg == Display.SWT_RESTORECARET) { + if ((state & CANVAS) != 0) { + if (caret != null) { + caret.killFocus (); + caret.setFocus (); + return 1; + } + } + } + return super.windowProc (hwnd, msg, wParam, lParam); +} + +LRESULT WM_CHAR (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_CHAR (wParam, lParam); + if (result != null) return result; + if (caret != null) { + switch ((int)/*64*/wParam) { + case SWT.DEL: + case SWT.BS: + case SWT.ESC: + break; + default: { + if (OS.GetKeyState (OS.VK_CONTROL) >= 0) { + int [] value = new int [1]; + if (OS.SystemParametersInfo (OS.SPI_GETMOUSEVANISH, 0, value, 0)) { + if (value [0] != 0) OS.SetCursor (0); + } + } + } + } + } + return result; +} + +LRESULT WM_IME_COMPOSITION (int /*long*/ wParam, int /*long*/ lParam) { + if (ime != null) { + LRESULT result = ime.WM_IME_COMPOSITION (wParam, lParam); + if (result != null) return result; + } + + /* + * Bug in Windows. On Korean Windows XP, the IME window + * for the Korean Input System (MS-IME 2002) always opens + * in the top left corner of the screen, despite the fact + * that ImmSetCompositionWindow() was called to position + * the IME when focus is gained. The fix is to position + * the IME on every WM_IME_COMPOSITION message. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION == OS.VERSION (5, 1)) { + if (OS.IsDBLocale) { + short langID = OS.GetSystemDefaultUILanguage (); + short primaryLang = OS.PRIMARYLANGID (langID); + if (primaryLang == OS.LANG_KOREAN) { + if (caret != null && caret.isFocusCaret ()) { + POINT ptCurrentPos = new POINT (); + if (OS.GetCaretPos (ptCurrentPos)) { + COMPOSITIONFORM lpCompForm = new COMPOSITIONFORM (); + lpCompForm.dwStyle = OS.CFS_POINT; + lpCompForm.x = ptCurrentPos.x; + lpCompForm.y = ptCurrentPos.y; + int /*long*/ hIMC = OS.ImmGetContext (handle); + OS.ImmSetCompositionWindow (hIMC, lpCompForm); + OS.ImmReleaseContext (handle, hIMC); + } + } + } + } + } + return super.WM_IME_COMPOSITION (wParam, lParam); +} + +LRESULT WM_IME_COMPOSITION_START (int /*long*/ wParam, int /*long*/ lParam) { + if (ime != null) { + LRESULT result = ime.WM_IME_COMPOSITION_START (wParam, lParam); + if (result != null) return result; + } + return super.WM_IME_COMPOSITION_START (wParam, lParam); +} + +LRESULT WM_IME_ENDCOMPOSITION (int /*long*/ wParam, int /*long*/ lParam) { + if (ime != null) { + LRESULT result = ime.WM_IME_ENDCOMPOSITION (wParam, lParam); + if (result != null) return result; + } + return super.WM_IME_ENDCOMPOSITION (wParam, lParam); +} + +LRESULT WM_INPUTLANGCHANGE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_INPUTLANGCHANGE (wParam, lParam); + if (caret != null && caret.isFocusCaret ()) { + caret.setIMEFont (); + caret.resizeIME (); + } + return result; +} + +LRESULT WM_KILLFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + if (ime != null) { + LRESULT result = ime.WM_KILLFOCUS (wParam, lParam); + if (result != null) return result; + } + Caret caret = this.caret; + LRESULT result = super.WM_KILLFOCUS (wParam, lParam); + if (caret != null) caret.killFocus (); + return result; +} + +LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + if (ime != null) { + LRESULT result = ime.WM_LBUTTONDOWN (wParam, lParam); + if (result != null) return result; + } + return super.WM_LBUTTONDOWN (wParam, lParam); +} + +LRESULT WM_SETFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETFOCUS (wParam, lParam); + if (caret != null && caret.isFocusCaret ()) caret.setFocus (); + return result; +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SIZE (wParam, lParam); + if (caret != null && caret.isFocusCaret ()) caret.resizeIME (); + return result; +} + +LRESULT WM_WINDOWPOSCHANGED (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_WINDOWPOSCHANGED (wParam, lParam); + //if (result != null) return result; + /* + * Bug in Windows. When a window with style WS_EX_LAYOUTRTL + * that contains a caret is resized, Windows does not move the + * caret in relation to the mirrored origin in the top right. + * The fix is to hide the caret in WM_WINDOWPOSCHANGING and + * show the caret in WM_WINDOWPOSCHANGED. + */ + boolean isFocus = (style & SWT.RIGHT_TO_LEFT) != 0 && caret != null && caret.isFocusCaret (); + if (isFocus) caret.setFocus (); + return result; +} + +LRESULT WM_WINDOWPOSCHANGING (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_WINDOWPOSCHANGING (wParam, lParam); + if (result != null) return result; + /* + * Bug in Windows. When a window with style WS_EX_LAYOUTRTL + * that contains a caret is resized, Windows does not move the + * caret in relation to the mirrored origin in the top right. + * The fix is to hide the caret in WM_WINDOWPOSCHANGING and + * show the caret in WM_WINDOWPOSCHANGED. + */ + boolean isFocus = (style & SWT.RIGHT_TO_LEFT) != 0 && caret != null && caret.isFocusCaret (); + if (isFocus) caret.killFocus (); + return result; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Caret.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Caret.java new file mode 100755 index 0000000000..3423c383a6 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Caret.java @@ -0,0 +1,600 @@ +/******************************************************************************* + * 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.win32.*; +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 moved, resized; + boolean isVisible; + Image image; + Font font; + LOGFONT oldFont; + +/** + * 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 (); +} + +void createWidget () { + isVisible = true; + if (parent.getCaret () == null) { + parent.setCaret (this); + } +} + +int /*long*/ defaultFont () { + int /*long*/ hwnd = parent.handle; + int /*long*/ hwndIME = OS.ImmGetDefaultIMEWnd (hwnd); + int /*long*/ hFont = 0; + if (hwndIME != 0) { + hFont = OS.SendMessage (hwndIME, OS.WM_GETFONT, 0, 0); + } + if (hFont == 0) { + hFont = OS.SendMessage (hwnd, OS.WM_GETFONT, 0, 0); + } + if (hFont == 0) return parent.defaultFont (); + return hFont; +} + +/** + * 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 (!OS.IsWinCE && width == 0) { + int [] buffer = new int [1]; + if (OS.SystemParametersInfo (OS.SPI_GETCARETWIDTH, 0, buffer, 0)) { + return new Rectangle (x, y, buffer [0], 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) { + int /*long*/ hFont = defaultFont (); + return Font.win32_new (display, hFont); + } + return font; +} + +/** + * 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 (!OS.IsWinCE && width == 0) { + int [] buffer = new int [1]; + if (OS.SystemParametersInfo (OS.SPI_GETCARETWIDTH, 0, buffer, 0)) { + return new Point (buffer [0], 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 hasFocus () { + return parent.handle == OS.GetFocus (); +} + +boolean isFocusCaret () { + return parent.caret == this && hasFocus (); +} + +/** + * 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 () && hasFocus (); +} + +void killFocus () { + OS.DestroyCaret (); + restoreIMEFont (); +} + +void move () { + moved = false; + if (!OS.SetCaretPos (x, y)) return; + resizeIME (); +} + +void resizeIME () { + if (!OS.IsDBLocale) return; + POINT ptCurrentPos = new POINT (); + if (!OS.GetCaretPos (ptCurrentPos)) return; + int /*long*/ hwnd = parent.handle; + int /*long*/ hIMC = OS.ImmGetContext (hwnd); + IME ime = parent.getIME (); + if (ime != null && ime.isInlineEnabled ()) { + Point size = getSize (); + CANDIDATEFORM lpCandidate = new CANDIDATEFORM (); + lpCandidate.dwStyle = OS.CFS_EXCLUDE; + lpCandidate.ptCurrentPos = ptCurrentPos; + lpCandidate.rcArea = new RECT (); + OS.SetRect (lpCandidate.rcArea, ptCurrentPos.x, ptCurrentPos.y, ptCurrentPos.x + size.x, ptCurrentPos.y + size.y); + OS.ImmSetCandidateWindow (hIMC, lpCandidate); + } else { + RECT rect = new RECT (); + OS.GetClientRect (hwnd, rect); + COMPOSITIONFORM lpCompForm = new COMPOSITIONFORM (); + lpCompForm.dwStyle = OS.CFS_RECT; + lpCompForm.x = ptCurrentPos.x; + lpCompForm.y = ptCurrentPos.y; + lpCompForm.left = rect.left; + lpCompForm.right = rect.right; + lpCompForm.top = rect.top; + lpCompForm.bottom = rect.bottom; + OS.ImmSetCompositionWindow (hIMC, lpCompForm); + } + OS.ImmReleaseContext (hwnd, hIMC); +} + +void releaseParent () { + super.releaseParent (); + if (this == parent.getCaret ()) parent.setCaret (null); +} + +void releaseWidget () { + super.releaseWidget (); + parent = null; + image = null; + font = null; + oldFont = null; +} + +void resize () { + resized = false; + int /*long*/ hwnd = parent.handle; + OS.DestroyCaret (); + int /*long*/ hBitmap = image != null ? image.handle : 0; + int width = this.width; + if (!OS.IsWinCE && image == null && width == 0) { + int [] buffer = new int [1]; + if (OS.SystemParametersInfo (OS.SPI_GETCARETWIDTH, 0, buffer, 0)) { + width = buffer [0]; + } + } + OS.CreateCaret (hwnd, hBitmap, width, height); + OS.SetCaretPos (x, y); + OS.ShowCaret (hwnd); + move (); +} + +void restoreIMEFont () { + if (!OS.IsDBLocale) return; + if (oldFont == null) return; + int /*long*/ hwnd = parent.handle; + int /*long*/ hIMC = OS.ImmGetContext (hwnd); + OS.ImmSetCompositionFont (hIMC, oldFont); + OS.ImmReleaseContext (hwnd, hIMC); + oldFont = 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(); + boolean samePosition = this.x == x && this.y == y; + boolean sameExtent = this.width == width && this.height == height; + if (samePosition && sameExtent) return; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + if (sameExtent) { + moved = true; + if (isVisible && hasFocus ()) move (); + } else { + resized = true; + if (isVisible && hasFocus ()) resize (); + } +} + +/** + * 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) { + if (rect == null) error (SWT.ERROR_NULL_ARGUMENT); + setBounds (rect.x, rect.y, rect.width, rect.height); +} + +void setFocus () { + int /*long*/ hwnd = parent.handle; + int /*long*/ hBitmap = 0; + if (image != null) hBitmap = image.handle; + int width = this.width; + if (!OS.IsWinCE && image == null && width == 0) { + int [] buffer = new int [1]; + if (OS.SystemParametersInfo (OS.SPI_GETCARETWIDTH, 0, buffer, 0)) { + width = buffer [0]; + } + } + OS.CreateCaret (hwnd, hBitmap, width, height); + move (); + setIMEFont (); + if (isVisible) OS.ShowCaret (hwnd); +} + +/** + * 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; + if (hasFocus ()) setIMEFont (); +} + +/** + * 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); + } + this.image = image; + if (isVisible && hasFocus ()) resize (); +} + +void setIMEFont () { + if (!OS.IsDBLocale) return; + int /*long*/ hFont = 0; + if (font != null) hFont = font.handle; + if (hFont == 0) hFont = defaultFont (); + int /*long*/ hwnd = parent.handle; + int /*long*/ hIMC = OS.ImmGetContext (hwnd); + /* Save the current IME font */ + if (oldFont == null) { + oldFont = OS.IsUnicode ? (LOGFONT) new LOGFONTW () : new LOGFONTA (); + if (!OS.ImmGetCompositionFont (hIMC, oldFont)) oldFont = null; + } + /* Set new IME font */ + LOGFONT logFont = OS.IsUnicode ? (LOGFONT) new LOGFONTW () : new LOGFONTA (); + if (OS.GetObject (hFont, LOGFONT.sizeof, logFont) != 0) { + OS.ImmSetCompositionFont (hIMC, logFont); + } + OS.ImmReleaseContext (hwnd, hIMC); +} + +/** + * 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(); + if (this.x == x && this.y == y) return; + this.x = x; this.y = y; + moved = true; + if (isVisible && hasFocus ()) move (); +} + +/** + * 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(); + if (this.width == width && this.height == height) return; + this.width = width; this.height = height; + resized = true; + if (isVisible && hasFocus ()) resize (); +} + +/** + * 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; + int /*long*/ hwnd = parent.handle; + if (OS.GetFocus () != hwnd) return; + if (!isVisible) { + OS.HideCaret (hwnd); + } else { + if (resized) { + resize (); + } else { + if (moved) move (); + } + OS.ShowCaret (hwnd); + } +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ColorDialog.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ColorDialog.java new file mode 100755 index 0000000000..21dad771d7 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ColorDialog.java @@ -0,0 +1,281 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * 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 { + Display display; + int width, height; + RGB rgb; + +/** + * 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 (); +} + +int /*long*/ CCHookProc (int /*long*/ hdlg, int /*long*/ uiMsg, int /*long*/ lParam, int /*long*/ lpData) { + switch ((int)/*64*/uiMsg) { + case OS.WM_INITDIALOG: { + RECT rect = new RECT (); + OS.GetWindowRect (hdlg, rect); + width = rect.right - rect.left; + height = rect.bottom - rect.top; + if (title != null && title.length () != 0) { + /* Use the character encoding for the default locale */ + TCHAR buffer = new TCHAR (0, title, true); + OS.SetWindowText (hdlg, buffer); + } + break; + } + case OS.WM_DESTROY: { + RECT rect = new RECT (); + OS.GetWindowRect (hdlg, rect); + int newWidth = rect.right - rect.left; + int newHeight = rect.bottom - rect.top; + if (newWidth < width || newHeight < height) { + //display.fullOpen = false; + } else { + if (newWidth > width || newHeight > height) { + //display.fullOpen = true; + } + } + break; + } + } + return 0; +} + +/** + * 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 () { + + /* Get the owner HWND for the dialog */ + int /*long*/ hwndOwner = parent.handle; + int /*long*/ hwndParent = parent.handle; + + /* + * Feature in Windows. There is no API to set the orientation of a + * color dialog. It is always inherited from the parent. The fix is + * to create a hidden parent and set the orientation in the hidden + * parent for the dialog to inherit. + */ + boolean enabled = false; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) { + int dialogOrientation = style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT); + int parentOrientation = parent.style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT); + if (dialogOrientation != parentOrientation) { + int exStyle = OS.WS_EX_NOINHERITLAYOUT; + if (dialogOrientation == SWT.RIGHT_TO_LEFT) exStyle |= OS.WS_EX_LAYOUTRTL; + hwndOwner = OS.CreateWindowEx ( + exStyle, + Shell.DialogClass, + null, + 0, + OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0, + hwndParent, + 0, + OS.GetModuleHandle (null), + null); + enabled = OS.IsWindowEnabled (hwndParent); + if (enabled) OS.EnableWindow (hwndParent, false); + } + } + + /* Create the CCHookProc */ + Callback callback = new Callback (this, "CCHookProc", 4); //$NON-NLS-1$ + int /*long*/ lpfnHook = callback.getAddress (); + if (lpfnHook == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + + /* Allocate the Custom Colors */ + display = parent.display; + if (display.lpCustColors == 0) { + int /*long*/ hHeap = OS.GetProcessHeap (); + display.lpCustColors = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, 16 * 4); + } + + /* Open the dialog */ + CHOOSECOLOR lpcc = new CHOOSECOLOR (); + lpcc.lStructSize = CHOOSECOLOR.sizeof; + lpcc.Flags = OS.CC_ANYCOLOR | OS.CC_ENABLEHOOK; + //if (display.fullOpen) lpcc.Flags |= OS.CC_FULLOPEN; + lpcc.lpfnHook = lpfnHook; + lpcc.hwndOwner = hwndOwner; + lpcc.lpCustColors = display.lpCustColors; + if (rgb != null) { + lpcc.Flags |= OS.CC_RGBINIT; + int red = rgb.red & 0xFF; + int green = (rgb.green << 8) & 0xFF00; + int blue = (rgb.blue << 16) & 0xFF0000; + lpcc.rgbResult = red | green | blue; + } + + /* Make the parent shell be temporary modal */ + Dialog oldModal = null; + if ((style & (SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL)) != 0) { + oldModal = display.getModalDialog (); + display.setModalDialog (this); + } + + /* Open the dialog */ + boolean success = OS.ChooseColor (lpcc); + + /* Clear the temporary dialog modal parent */ + if ((style & (SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL)) != 0) { + display.setModalDialog (oldModal); + } + + if (success) { + int red = lpcc.rgbResult & 0xFF; + int green = (lpcc.rgbResult >> 8) & 0xFF; + int blue = (lpcc.rgbResult >> 16) & 0xFF; + rgb = new RGB (red, green, blue); + } + + /* Free the CCHookProc */ + callback.dispose (); + + /* Free the Custom Colors */ + /* + * This code is intentionally commented. Currently, + * there is exactly one set of custom colors per display. + * The memory associated with these colors is released + * when the display is disposed. + */ +// if (lpCustColors != 0) OS.HeapFree (hHeap, 0, lpCustColors); + + /* Destroy the BIDI orientation window */ + if (hwndParent != hwndOwner) { + if (enabled) OS.EnableWindow (hwndParent, true); + OS.SetActiveWindow (hwndParent); + OS.DestroyWindow (hwndOwner); + } + + /* + * This code is intentionally commented. On some + * platforms, the owner window is repainted right + * away when a dialog window exits. This behavior + * is currently unspecified. + */ +// if (hwndOwner != 0) OS.UpdateWindow (hwndOwner); + + display = null; + if (!success) return null; + 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; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Combo.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Combo.java new file mode 100755 index 0000000000..694b566adb --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Combo.java @@ -0,0 +1,2489 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * 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 { + boolean noSelection, ignoreDefaultSelection, ignoreCharacter, ignoreModify, ignoreResize, lockText; + int scrollWidth, visibleCount = 5; + int /*long*/ cbtHook; + /** + * 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 = OS.IsWinNT ? 0x7FFFFFFF : 0x7FFF; + } + + /* + * These are the undocumented control id's for the children of + * a combo box. Since there are no constants for these values, + * they may change with different versions of Windows (but have + * been the same since Windows 3.0). + */ + static final int CBID_LIST = 1000; + static final int CBID_EDIT = 1001; + static /*final*/ int /*long*/ EditProc, ListProc; + + static final int /*long*/ ComboProc; + static final TCHAR ComboClass = new TCHAR (0, "COMBOBOX", true); + static { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, ComboClass, lpWndClass); + ComboProc = lpWndClass.lpfnWndProc; + } + +/** + * 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)); + /* This code is intentionally commented */ + //if ((style & SWT.H_SCROLL) != 0) this.style |= SWT.H_SCROLL; + this.style |= SWT.H_SCROLL; +} + +/** + * 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); + TCHAR buffer = new TCHAR (getCodePage (), string, true); + int result = (int)/*64*/OS.SendMessage (handle, OS.CB_ADDSTRING, 0, buffer); + if (result == OS.CB_ERR) error (SWT.ERROR_ITEM_NOT_ADDED); + if (result == OS.CB_ERRSPACE) error (SWT.ERROR_ITEM_NOT_ADDED); + if ((style & SWT.H_SCROLL) != 0) setScrollWidth (buffer, true); +} + +/** + * 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 = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0); + if (!(0 <= index && index <= count)) { + error (SWT.ERROR_INVALID_RANGE); + } + TCHAR buffer = new TCHAR (getCodePage (), string, true); + int result = (int)/*64*/OS.SendMessage (handle, OS.CB_INSERTSTRING, index, buffer); + if (result == OS.CB_ERRSPACE || result == OS.CB_ERR) { + error (SWT.ERROR_ITEM_NOT_ADDED); + } + if ((style & SWT.H_SCROLL) != 0) setScrollWidth (buffer, true); +} + +/** + * 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); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + if (hwnd == handle) { + switch (msg) { + case OS.WM_SIZE: { + ignoreResize = true; + int /*long*/ result = OS.CallWindowProc (ComboProc, hwnd, msg, wParam, lParam); + ignoreResize = false; + return result; + } + } + return OS.CallWindowProc (ComboProc, hwnd, msg, wParam, lParam); + } + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwnd == hwndText) { + if (lockText && msg == OS.WM_SETTEXT) return 0; + return OS.CallWindowProc (EditProc, hwnd, msg, wParam, lParam); + } + int /*long*/ hwndList = OS.GetDlgItem (handle, CBID_LIST); + if (hwnd == hwndList) { + return OS.CallWindowProc (ListProc, hwnd, msg, wParam, lParam); + } + return OS.DefWindowProc (hwnd, msg, wParam, lParam); +} + +int /*long*/ CBTProc (int /*long*/ nCode, int /*long*/ wParam, int /*long*/ lParam) { + if ((int)/*64*/nCode == OS.HCBT_CREATEWND) { + TCHAR buffer = new TCHAR (0, 128); + OS.GetClassName (wParam, buffer, buffer.length ()); + String className = buffer.toString (0, buffer.strlen ()); + if (className.equals ("Edit") || className.equals ("EDIT")) { //$NON-NLS-1$ //$NON-NLS-2$ + int bits = OS.GetWindowLong (wParam, OS.GWL_STYLE); + OS.SetWindowLong (wParam, OS.GWL_STYLE, bits & ~OS.ES_NOHIDESEL); + } + } + return OS.CallNextHookEx (cbtHook, (int)/*64*/nCode, wParam, lParam); +} + +boolean checkHandle (int /*long*/ hwnd) { + return hwnd == handle || hwnd == OS.GetDlgItem (handle, CBID_EDIT) || hwnd == OS.GetDlgItem (handle, CBID_LIST); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +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; +} + +/** + * 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 (); + OS.SendMessage (handle, OS.CB_SETEDITSEL, 0, -1); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + int width = 0, height = 0; + if (wHint == SWT.DEFAULT) { + int /*long*/ newFont, oldFont = 0; + int /*long*/ hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + int count = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0); + RECT rect = new RECT (); + int flags = OS.DT_CALCRECT | OS.DT_NOPREFIX; + if ((style & SWT.READ_ONLY) == 0) flags |= OS.DT_EDITCONTROL; + int length = OS.GetWindowTextLength (handle); + int cp = getCodePage (); + TCHAR buffer = new TCHAR (cp, length + 1); + OS.GetWindowText (handle, buffer, length + 1); + OS.DrawText (hDC, buffer, length, rect, flags); + width = Math.max (width, rect.right - rect.left); + if ((style & SWT.H_SCROLL) != 0) { + width = Math.max (width, scrollWidth); + } else { + for (int i=0; i<count; i++) { + length = (int)/*64*/OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, i, 0); + if (length != OS.CB_ERR) { + if (length + 1 > buffer.length ()) buffer = new TCHAR (cp, length + 1); + int result = (int)/*64*/OS.SendMessage (handle, OS.CB_GETLBTEXT, i, buffer); + if (result != OS.CB_ERR) { + OS.DrawText (hDC, buffer, length, rect, flags); + width = Math.max (width, rect.right - rect.left); + } + } + } + } + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + } + if (hHint == SWT.DEFAULT) { + if ((style & SWT.SIMPLE) != 0) { + int count = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0); + int itemHeight = (int)/*64*/OS.SendMessage (handle, OS.CB_GETITEMHEIGHT, 0, 0); + height = count * itemHeight; + } + } + 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 ((style & SWT.READ_ONLY) != 0) { + width += 8; + } else { + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText != 0) { + int /*long*/ margins = OS.SendMessage (hwndText, OS.EM_GETMARGINS, 0, 0); + int marginWidth = OS.LOWORD (margins) + OS.HIWORD (margins); + width += marginWidth + 3; + } + } + COMBOBOXINFO pcbi = new COMBOBOXINFO (); + pcbi.cbSize = COMBOBOXINFO.sizeof; + if (((style & SWT.SIMPLE) == 0) && !OS.IsWinCE && OS.GetComboBoxInfo (handle, pcbi)) { + width += pcbi.itemLeft + (pcbi.buttonRight - pcbi.buttonLeft); + height = (pcbi.buttonBottom - pcbi.buttonTop) + pcbi.buttonTop * 2; + } else { + int border = OS.GetSystemMetrics (OS.SM_CXEDGE); + width += OS.GetSystemMetrics (OS.SM_CXVSCROLL) + border * 2; + int textHeight = (int)/*64*/OS.SendMessage (handle, OS.CB_GETITEMHEIGHT, -1, 0); + if ((style & SWT.DROP_DOWN) != 0) { + height = textHeight + 6; + } else { + height += textHeight + 10; + } + } + if ((style & SWT.SIMPLE) != 0 && (style & SWT.H_SCROLL) != 0) { + height += OS.GetSystemMetrics (OS.SM_CYHSCROLL); + } + 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 (); + OS.SendMessage (handle, OS.WM_COPY, 0, 0); +} + +void createHandle () { + /* + * Feature in Windows. When the selection changes in a combo box, + * Windows draws the selection, even when the combo box does not + * have focus. Strictly speaking, this is the correct Windows + * behavior because the combo box sets ES_NOHIDESEL on the text + * control that it creates. Despite this, it looks strange because + * Windows also clears the selection and selects all the text when + * the combo box gets focus. The fix is use the CBT hook to clear + * the ES_NOHIDESEL style bit when the text control is created. + */ + if (OS.IsWinCE || (style & (SWT.READ_ONLY | SWT.SIMPLE)) != 0) { + super.createHandle (); + } else { + int threadId = OS.GetCurrentThreadId (); + Callback cbtCallback = new Callback (this, "CBTProc", 3); //$NON-NLS-1$ + int /*long*/ cbtProc = cbtCallback.getAddress (); + if (cbtProc == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + cbtHook = OS.SetWindowsHookEx (OS.WH_CBT, cbtProc, 0, threadId); + super.createHandle (); + if (cbtHook != 0) OS.UnhookWindowsHookEx (cbtHook); + cbtHook = 0; + cbtCallback.dispose (); + } + state &= ~(CANVAS | THEME_BACKGROUND); + + /* Get the text and list window procs */ + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText != 0 && EditProc == 0) { + EditProc = OS.GetWindowLongPtr (hwndText, OS.GWLP_WNDPROC); + } + int /*long*/ hwndList = OS.GetDlgItem (handle, CBID_LIST); + if (hwndList != 0 && ListProc == 0) { + ListProc = OS.GetWindowLongPtr (hwndList, OS.GWLP_WNDPROC); + } + + /* + * Bug in Windows. If the combo box has the CBS_SIMPLE style, + * the list portion of the combo box is not drawn correctly the + * first time, causing pixel corruption. The fix is to ensure + * that the combo box has been resized more than once. + */ + if ((style & SWT.SIMPLE) != 0) { + int flags = OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE; + SetWindowPos (handle, 0, 0, 0, 0x3FFF, 0x3FFF, flags); + SetWindowPos (handle, 0, 0, 0, 0, 0, flags); + } +} + +/** + * 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; + OS.SendMessage (handle, OS.WM_CUT, 0, 0); +} + +int defaultBackground () { + return OS.GetSysColor (OS.COLOR_WINDOW); +} + +void deregister () { + super.deregister (); + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText != 0) display.removeControl (hwndText); + int /*long*/ hwndList = OS.GetDlgItem (handle, CBID_LIST); + if (hwndList != 0) display.removeControl (hwndList); +} + +/** + * 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 (); + int selection = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0); + if (index != selection) return; + OS.SendMessage (handle, OS.CB_SETCURSEL, -1, 0); + sendEvent (SWT.Modify); + // widget could be disposed at this point +} + +/** + * 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 (); + OS.SendMessage (handle, OS.CB_SETCURSEL, -1, 0); + sendEvent (SWT.Modify); + // widget could be disposed at this point +} + +boolean dragDetect (int /*long*/ hwnd, int x, int y, boolean filter, boolean [] detect, boolean [] consume) { + if (filter && (style & SWT.READ_ONLY) == 0) { + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText != 0) { + int [] start = new int [1], end = new int [1]; + OS.SendMessage (handle, OS.CB_GETEDITSEL, start, end); + if (start [0] != end [0]) { + int /*long*/ lParam = OS.MAKELPARAM (x, y); + int position = OS.LOWORD (OS.SendMessage (hwndText, OS.EM_CHARFROMPOS, 0, lParam)); + if (start [0] <= position && position < end [0]) { + if (super.dragDetect (hwnd, x, y, filter, detect, consume)) { + if (consume != null) consume [0] = true; + return true; + } + } + } + return false; + } + } + return super.dragDetect (hwnd, x, y, filter, detect, consume); +} + +/** + * 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 length = (int)/*64*/OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, index, 0); + if (length != OS.CB_ERR) { + TCHAR buffer = new TCHAR (getCodePage (), length + 1); + int result = (int)/*64*/OS.SendMessage (handle, OS.CB_GETLBTEXT, index, buffer); + if (result != OS.CB_ERR) return buffer.toString (0, length); + } + int count = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0); + if (0 <= index && index < count) error (SWT.ERROR_CANNOT_GET_ITEM); + error (SWT.ERROR_INVALID_RANGE); + return ""; +} + +/** + * 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 (); + int count = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0); + if (count == OS.CB_ERR) error (SWT.ERROR_CANNOT_GET_COUNT); + return count; +} + +/** + * 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 (); + int result = (int)/*64*/OS.SendMessage (handle, OS.CB_GETITEMHEIGHT, 0, 0); + if (result == OS.CB_ERR) error (SWT.ERROR_CANNOT_GET_ITEM_HEIGHT); + return result; +} + +/** + * 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 () { + checkWidget (); + if ((style & SWT.DROP_DOWN) != 0) { + return OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0; + } + return true; +} + +String getNameText () { + return getText (); +} + +/** + * 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 (); + OS.SendMessage (handle, OS.CB_SHOWDROPDOWN, visible ? 1 : 0, 0); +} + +/** + * 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.DROP_DOWN) != 0 && (style & SWT.READ_ONLY) != 0) { + return new Point (0, OS.GetWindowTextLength (handle)); + } + int [] start = new int [1], end = new int [1]; + OS.SendMessage (handle, OS.CB_GETEDITSEL, start, end); + if (!OS.IsUnicode && OS.IsDBLocale) { + start [0] = mbcsToWcsPos (start [0]); + end [0] = mbcsToWcsPos (end [0]); + } + return new Point (start [0], end [0]); +} + +/** + * 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 (noSelection) return -1; + return (int)/*64*/OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0); +} + +/** + * 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 (); + int length = OS.GetWindowTextLength (handle); + if (length == 0) return ""; + TCHAR buffer = new TCHAR (getCodePage (), length + 1); + OS.GetWindowText (handle, buffer, length + 1); + return buffer.toString (0, length); +} + +/** + * 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 (); + COMBOBOXINFO pcbi = new COMBOBOXINFO (); + pcbi.cbSize = COMBOBOXINFO.sizeof; + if (((style & SWT.SIMPLE) == 0) && !OS.IsWinCE && OS.GetComboBoxInfo (handle, pcbi)) { + return (pcbi.buttonBottom - pcbi.buttonTop) + pcbi.buttonTop * 2; + } + int result = (int)/*64*/OS.SendMessage (handle, OS.CB_GETITEMHEIGHT, -1, 0); + if (result == OS.CB_ERR) error (SWT.ERROR_CANNOT_GET_ITEM_HEIGHT); + return (style & SWT.DROP_DOWN) != 0 ? result + 6 : result + 10; +} + +/** + * 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 (); + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText == 0) return LIMIT; + return (int)/*64*/OS.SendMessage (hwndText, OS.EM_GETLIMITTEXT, 0, 0) & 0x7FFFFFFF; +} + +/** + * 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 (); + return visibleCount; +} + +boolean hasFocus () { + int /*long*/ hwndFocus = OS.GetFocus (); + if (hwndFocus == handle) return true; + if (hwndFocus == 0) return false; + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndFocus == hwndText) return true; + int /*long*/ hwndList = OS.GetDlgItem (handle, CBID_LIST); + if (hwndFocus == hwndList) return true; + return false; +} + +/** + * 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); + + /* + * Bug in Windows. For some reason, CB_FINDSTRINGEXACT + * will not find empty strings even though it is legal + * to insert an empty string into a combo. The fix is + * to search the combo, an item at a time. + */ + if (string.length () == 0) { + int count = getItemCount (); + for (int i=start; i<count; i++) { + if (string.equals (getItem (i))) return i; + } + return -1; + } + + /* Use CB_FINDSTRINGEXACT to search for the item */ + int count = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0); + if (!(0 <= start && start < count)) return -1; + int index = start - 1, last = 0; + TCHAR buffer = new TCHAR (getCodePage (), string, true); + do { + index = (int)/*64*/OS.SendMessage (handle, OS.CB_FINDSTRINGEXACT, last = index, buffer); + if (index == OS.CB_ERR || index <= last) return -1; + } while (!string.equals (getItem (index))); + return index; +} + +int mbcsToWcsPos (int mbcsPos) { + if (mbcsPos <= 0) return 0; + if (OS.IsUnicode) return mbcsPos; + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText == 0) return mbcsPos; + int mbcsSize = OS.GetWindowTextLengthA (hwndText); + if (mbcsSize == 0) return 0; + if (mbcsPos >= mbcsSize) return mbcsSize; + byte [] buffer = new byte [mbcsSize + 1]; + OS.GetWindowTextA (hwndText, buffer, mbcsSize + 1); + return OS.MultiByteToWideChar (getCodePage (), OS.MB_PRECOMPOSED, buffer, mbcsPos, null, 0); +} + +/** + * 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; + OS.SendMessage (handle, OS.WM_PASTE, 0, 0); +} + +void register () { + super.register (); + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText != 0) display.addControl (hwndText, this); + int /*long*/ hwndList = OS.GetDlgItem (handle, CBID_LIST); + if (hwndList != 0) display.addControl (hwndList, this); +} + +/** + * 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 (); + remove (index, true); +} + +void remove (int index, boolean notify) { + TCHAR buffer = null; + if ((style & SWT.H_SCROLL) != 0) { + int length = (int)/*64*/OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, index, 0); + if (length == OS.CB_ERR) { + int count = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0); + if (0 <= index && index < count) error (SWT.ERROR_ITEM_NOT_REMOVED); + error (SWT.ERROR_INVALID_RANGE); + } + buffer = new TCHAR (getCodePage (), length + 1); + int result = (int)/*64*/OS.SendMessage (handle, OS.CB_GETLBTEXT, index, buffer); + if (result == OS.CB_ERR) { + int count = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0); + if (0 <= index && index < count) error (SWT.ERROR_ITEM_NOT_REMOVED); + error (SWT.ERROR_INVALID_RANGE); + } + } + int length = OS.GetWindowTextLength (handle); + int code = (int)/*64*/OS.SendMessage (handle, OS.CB_DELETESTRING, index, 0); + if (code == OS.CB_ERR) { + int count = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0); + if (0 <= index && index < count) error (SWT.ERROR_ITEM_NOT_REMOVED); + error (SWT.ERROR_INVALID_RANGE); + } + if ((style & SWT.H_SCROLL) != 0) setScrollWidth (buffer, true); + if (notify && length != OS.GetWindowTextLength (handle)) { + sendEvent (SWT.Modify); + if (isDisposed ()) return; + } + /* + * Bug in Windows. When the combo box is read only + * with exactly one item that is currently selected + * and that item is removed, the combo box does not + * redraw to clear the text area. The fix is to + * force a redraw. + */ + if ((style & SWT.READ_ONLY) != 0) { + int count = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0); + if (count == 0) OS.InvalidateRect (handle, null, true); + } +} + +/** + * 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 = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0); + if (!(0 <= start && start <= end && end < count)) { + error (SWT.ERROR_INVALID_RANGE); + } + int textLength = OS.GetWindowTextLength (handle); + RECT rect = null; + int /*long*/ hDC = 0, oldFont = 0, newFont = 0; + int newWidth = 0; + if ((style & SWT.H_SCROLL) != 0) { + rect = new RECT (); + hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + } + int cp = getCodePage (); + int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX; + for (int i=start; i<=end; i++) { + TCHAR buffer = null; + if ((style & SWT.H_SCROLL) != 0) { + int length = (int)/*64*/OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, start, 0); + if (length == OS.CB_ERR) break; + buffer = new TCHAR (cp, length + 1); + int result = (int)/*64*/OS.SendMessage (handle, OS.CB_GETLBTEXT, start, buffer); + if (result == OS.CB_ERR) break; + } + int result = (int)/*64*/OS.SendMessage (handle, OS.CB_DELETESTRING, start, 0); + if (result == OS.CB_ERR) error (SWT.ERROR_ITEM_NOT_REMOVED); + if ((style & SWT.H_SCROLL) != 0) { + OS.DrawText (hDC, buffer, -1, rect, flags); + newWidth = Math.max (newWidth, rect.right - rect.left); + } + } + if ((style & SWT.H_SCROLL) != 0) { + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + setScrollWidth (newWidth, false); + } + if (textLength != OS.GetWindowTextLength (handle)) { + sendEvent (SWT.Modify); + if (isDisposed ()) return; + } + /* + * Bug in Windows. When the combo box is read only + * with exactly one item that is currently selected + * and that item is removed, the combo box does not + * redraw to clear the text area. The fix is to + * force a redraw. + */ + if ((style & SWT.READ_ONLY) != 0) { + count = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0); + if (count == 0) OS.InvalidateRect (handle, null, true); + } +} + +/** + * 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 (); + OS.SendMessage (handle, OS.CB_RESETCONTENT, 0, 0); + sendEvent (SWT.Modify); + if (isDisposed ()) return; + if ((style & SWT.H_SCROLL) != 0) setScrollWidth (0); +} + +/** + * 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); +} + +boolean sendKeyEvent (int type, int msg, int /*long*/ wParam, int /*long*/ lParam, Event event) { + if (!super.sendKeyEvent (type, msg, wParam, lParam, event)) { + return false; + } + if ((style & SWT.READ_ONLY) != 0) return true; + if (type != SWT.KeyDown) return true; + if (msg != OS.WM_CHAR && msg != OS.WM_KEYDOWN && msg != OS.WM_IME_CHAR) { + return true; + } + if (event.character == 0) return true; + if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return true; + char key = event.character; + int stateMask = event.stateMask; + + /* + * Disable all magic keys that could modify the text + * and don't send events when Alt, Shift or Ctrl is + * pressed. + */ + switch (msg) { + case OS.WM_CHAR: + if (key != 0x08 && key != 0x7F && key != '\r' && key != '\t' && key != '\n') break; + // FALL THROUGH + case OS.WM_KEYDOWN: + if ((stateMask & (SWT.ALT | SWT.SHIFT | SWT.CONTROL)) != 0) return false; + break; + } + + /* + * If the left button is down, the text widget refuses the character. + */ + if (OS.GetKeyState (OS.VK_LBUTTON) < 0) { + return true; + } + + /* Verify the character */ + String oldText = ""; + int [] start = new int [1], end = new int [1]; + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText == 0) return true; + OS.SendMessage (hwndText, OS.EM_GETSEL, start, end); + switch (key) { + case 0x08: /* Bs */ + if (start [0] == end [0]) { + if (start [0] == 0) return true; + start [0] = start [0] - 1; + if (!OS.IsUnicode && OS.IsDBLocale) { + int [] newStart = new int [1], newEnd = new int [1]; + OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]); + OS.SendMessage (hwndText, OS.EM_GETSEL, newStart, newEnd); + if (start [0] != newStart [0]) start [0] = start [0] - 1; + } + start [0] = Math.max (start [0], 0); + } + break; + case 0x7F: /* Del */ + if (start [0] == end [0]) { + int length = OS.GetWindowTextLength (hwndText); + if (start [0] == length) return true; + end [0] = end [0] + 1; + if (!OS.IsUnicode && OS.IsDBLocale) { + int [] newStart = new int [1], newEnd = new int [1]; + OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]); + OS.SendMessage (hwndText, OS.EM_GETSEL, newStart, newEnd); + if (end [0] != newEnd [0]) end [0] = end [0] + 1; + } + end [0] = Math.min (end [0], length); + } + break; + case '\r': /* Return */ + return true; + default: /* Tab and other characters */ + if (key != '\t' && key < 0x20) return true; + oldText = new String (new char [] {key}); + break; + } + String newText = verifyText (oldText, start [0], end [0], event); + if (newText == null) return false; + if (newText == oldText) return true; + TCHAR buffer = new TCHAR (getCodePage (), newText, true); + OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]); + OS.SendMessage (hwndText, OS.EM_REPLACESEL, 0, buffer); + return false; +} + +/** + * 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 = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0); + if (0 <= index && index < count) { + int selection = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0); + int code = (int)/*64*/OS.SendMessage (handle, OS.CB_SETCURSEL, index, 0); + if (code != OS.CB_ERR && code != selection) { + sendEvent (SWT.Modify); + // widget could be disposed at this point + } + } +} + +void setBackgroundImage (int /*long*/ hBitmap) { + super.setBackgroundImage (hBitmap); + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText != 0) OS.InvalidateRect (hwndText, null, true); + int /*long*/ hwndList = OS.GetDlgItem (handle, CBID_LIST); + if (hwndList != 0) OS.InvalidateRect (hwndList, null, true); +} + +void setBackgroundPixel (int pixel) { + super.setBackgroundPixel (pixel); + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText != 0) OS.InvalidateRect (hwndText, null, true); + int /*long*/ hwndList = OS.GetDlgItem (handle, CBID_LIST); + if (hwndList != 0) OS.InvalidateRect (hwndList, null, true); +} + +void setBounds (int x, int y, int width, int height, int flags) { + /* + * Feature in Windows. If the combo box has the CBS_DROPDOWN + * or CBS_DROPDOWNLIST style, Windows uses the height that the + * programmer sets in SetWindowPos () to control height of the + * drop down list. When the width is non-zero, Windows remembers + * this value and sets the height to be the height of the text + * field part of the combo box. If the width is zero, Windows + * allows the height to have any value. Therefore, when the + * programmer sets and then queries the height, the values can + * be different depending on the width. The problem occurs when + * the programmer uses computeSize () to determine the preferred + * height (always the height of the text field) and then uses + * this value to set the height of the combo box. The result + * is a combo box with a zero size drop down list. The fix, is + * to always set the height to show a fixed number of combo box + * items and ignore the height value that the programmer supplies. + */ + if ((style & SWT.DROP_DOWN) != 0) { + height = getTextHeight () + (getItemHeight () * visibleCount) + 2; + /* + * Feature in Windows. When a drop down combo box is resized, + * the combo box resizes the height of the text field and uses + * the height provided in SetWindowPos () to determine the height + * of the drop down list. For some reason, the combo box redraws + * the whole area, not just the text field. The fix is to set the + * SWP_NOSIZE bits when the height of text field and the drop down + * list is the same as the requested height. + * + * NOTE: Setting the width of a combo box to zero does not update + * the width of the drop down control rect. If the width of the + * combo box is zero, then do not set SWP_NOSIZE. + */ + RECT rect = new RECT (); + OS.GetWindowRect (handle, rect); + if (rect.right - rect.left != 0) { + if (OS.SendMessage (handle, OS.CB_GETDROPPEDCONTROLRECT, 0, rect) != 0) { + int oldWidth = rect.right - rect.left, oldHeight = rect.bottom - rect.top; + if (oldWidth == width && oldHeight == height) flags |= OS.SWP_NOSIZE; + } + } + SetWindowPos (handle, 0, x, y, width, height, flags); + } else { + super.setBounds (x, y, width, height, flags); + } +} + +public void setFont (Font font) { + checkWidget (); + super.setFont (font); + if ((style & SWT.H_SCROLL) != 0) setScrollWidth (); +} + +void setForegroundPixel (int pixel) { + super.setForegroundPixel (pixel); + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText != 0) OS.InvalidateRect (hwndText, null, true); + int /*long*/ hwndList = OS.GetDlgItem (handle, CBID_LIST); + if (hwndList != 0) OS.InvalidateRect (hwndList, null, true); +} + +/** + * 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 selection = getSelectionIndex (); + remove (index, false); + if (isDisposed ()) return; + add (string, index); + if (selection != -1) select (selection); +} + +/** + * 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); + } + RECT rect = null; + int /*long*/ hDC = 0, oldFont = 0, newFont = 0; + int newWidth = 0; + if ((style & SWT.H_SCROLL) != 0) { + rect = new RECT (); + hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + setScrollWidth (0); + } + OS.SendMessage (handle, OS.CB_RESETCONTENT, 0, 0); + int codePage = getCodePage (); + for (int i=0; i<items.length; i++) { + String string = items [i]; + TCHAR buffer = new TCHAR (codePage, string, true); + int code = (int)/*64*/OS.SendMessage (handle, OS.CB_ADDSTRING, 0, buffer); + if (code == OS.CB_ERR) error (SWT.ERROR_ITEM_NOT_ADDED); + if (code == OS.CB_ERRSPACE) error (SWT.ERROR_ITEM_NOT_ADDED); + if ((style & SWT.H_SCROLL) != 0) { + int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX; + OS.DrawText (hDC, buffer, -1, rect, flags); + newWidth = Math.max (newWidth, rect.right - rect.left); + } + } + if ((style & SWT.H_SCROLL) != 0) { + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + setScrollWidth (newWidth + 3); + } + sendEvent (SWT.Modify); + // widget could be disposed at this point +} + +/** + * 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(); + if (OS.IsWinCE) return; + if (OS.WIN32_VERSION < OS.VERSION (4, 10)) return; + int flags = SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT; + if ((orientation & flags) == 0 || (orientation & flags) == flags) return; + style &= ~flags; + style |= orientation & flags; + int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + style |= SWT.MIRRORED; + bits |= OS.WS_EX_LAYOUTRTL; + } else { + style &= ~SWT.MIRRORED; + bits &= ~OS.WS_EX_LAYOUTRTL; + } + OS.SetWindowLong (handle, OS.GWL_EXSTYLE, bits); + int /*long*/ hwndText = 0, hwndList = 0; + COMBOBOXINFO pcbi = new COMBOBOXINFO (); + pcbi.cbSize = COMBOBOXINFO.sizeof; + if (OS.GetComboBoxInfo (handle, pcbi)) { + hwndText = pcbi.hwndItem; + hwndList = pcbi.hwndList; + } + if (hwndText != 0) { + int bits1 = OS.GetWindowLong (hwndText, OS.GWL_EXSTYLE); + int bits2 = OS.GetWindowLong (hwndText, OS.GWL_STYLE); + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + bits1 |= OS.WS_EX_RIGHT | OS.WS_EX_RTLREADING; + bits2 |= OS.ES_RIGHT; + } else { + bits1 &= ~(OS.WS_EX_RIGHT | OS.WS_EX_RTLREADING); + bits2 &= ~OS.ES_RIGHT; + } + OS.SetWindowLong (hwndText, OS.GWL_EXSTYLE, bits1); + OS.SetWindowLong (hwndText, OS.GWL_STYLE, bits2); + + /* + * Bug in Windows. For some reason, the single line text field + * portion of the combo box does not redraw to reflect the new + * style bits. The fix is to force the widget to be resized by + * temporarily shrinking and then growing the width and height. + */ + RECT rect = new RECT (); + OS.GetWindowRect (hwndText, rect); + int width = rect.right - rect.left, height = rect.bottom - rect.top; + OS.GetWindowRect (handle, rect); + int widthCombo = rect.right - rect.left, heightCombo = rect.bottom - rect.top; + int uFlags = OS.SWP_NOMOVE | OS.SWP_NOZORDER | OS.SWP_NOACTIVATE; + SetWindowPos (hwndText, 0, 0, 0, width - 1, height - 1, uFlags); + SetWindowPos (handle, 0, 0, 0, widthCombo - 1, heightCombo - 1, uFlags); + SetWindowPos (hwndText, 0, 0, 0, width, height, uFlags); + SetWindowPos (handle, 0, 0, 0, widthCombo, heightCombo, uFlags); + OS.InvalidateRect (handle, null, true); + } + if (hwndList != 0) { + int bits1 = OS.GetWindowLong (hwndList, OS.GWL_EXSTYLE); + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + bits1 |= OS.WS_EX_LAYOUTRTL; + } else { + bits1 &= ~OS.WS_EX_LAYOUTRTL; + } + OS.SetWindowLong (hwndList, OS.GWL_EXSTYLE, bits1); + } +} + +void setScrollWidth () { + int newWidth = 0; + RECT rect = new RECT (); + int /*long*/ newFont, oldFont = 0; + int /*long*/ hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + int cp = getCodePage (); + int count = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0); + int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX; + for (int i=0; i<count; i++) { + int length = (int)/*64*/OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, i, 0); + if (length != OS.CB_ERR) { + TCHAR buffer = new TCHAR (cp, length + 1); + int result = (int)/*64*/OS.SendMessage (handle, OS.CB_GETLBTEXT, i, buffer); + if (result != OS.CB_ERR) { + OS.DrawText (hDC, buffer, -1, rect, flags); + newWidth = Math.max (newWidth, rect.right - rect.left); + } + } + } + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + setScrollWidth (newWidth + 3); +} + +void setScrollWidth (int scrollWidth) { + this.scrollWidth = scrollWidth; + if ((style & SWT.SIMPLE) != 0) { + OS.SendMessage (handle, OS.CB_SETHORIZONTALEXTENT, scrollWidth, 0); + return; + } + boolean scroll = false; + int count = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0); + if (count > 3) { + int maxWidth = 0; + if (OS.IsWinCE || OS.WIN32_VERSION < OS.VERSION (4, 10)) { + RECT rect = new RECT (); + OS.SystemParametersInfo (OS.SPI_GETWORKAREA, 0, rect, 0); + maxWidth = (rect.right - rect.left) / 4; + } else { + int /*long*/ hmonitor = OS.MonitorFromWindow (handle, OS.MONITOR_DEFAULTTONEAREST); + MONITORINFO lpmi = new MONITORINFO (); + lpmi.cbSize = MONITORINFO.sizeof; + OS.GetMonitorInfo (hmonitor, lpmi); + maxWidth = (lpmi.rcWork_right - lpmi.rcWork_left) / 4; + } + scroll = scrollWidth > maxWidth; + } + /* + * Feature in Windows. For some reason, in a editable combo box, + * when CB_SETDROPPEDWIDTH is used to set the width of the drop + * down list and the current text does not match an item in the + * list, Windows selects the item that most closely matches the + * contents of the combo. The fix is to lock the current text + * by ignoring all WM_SETTEXT messages during processing of + * CB_SETDROPPEDWIDTH. + */ + if ((style & SWT.READ_ONLY) == 0) lockText = true; + if (scroll) { + OS.SendMessage (handle, OS.CB_SETDROPPEDWIDTH, 0, 0); + OS.SendMessage (handle, OS.CB_SETHORIZONTALEXTENT, scrollWidth, 0); + } else { + scrollWidth += OS.GetSystemMetrics (OS.SM_CYHSCROLL); + OS.SendMessage (handle, OS.CB_SETDROPPEDWIDTH, scrollWidth, 0); + OS.SendMessage (handle, OS.CB_SETHORIZONTALEXTENT, 0, 0); + } + if ((style & SWT.READ_ONLY) == 0) lockText = false; +} + +void setScrollWidth (TCHAR buffer, boolean grow) { + RECT rect = new RECT (); + int /*long*/ newFont, oldFont = 0; + int /*long*/ hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX; + OS.DrawText (hDC, buffer, -1, rect, flags); + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + setScrollWidth (rect.right - rect.left, grow); +} + +void setScrollWidth (int newWidth, boolean grow) { + if (grow) { + if (newWidth <= scrollWidth) return; + setScrollWidth (newWidth + 3); + } else { + if (newWidth < scrollWidth) return; + setScrollWidth (); + } +} + +/** + * 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); + int start = selection.x, end = selection.y; + if (!OS.IsUnicode && OS.IsDBLocale) { + start = wcsToMbcsPos (start); + end = wcsToMbcsPos (end); + } + int /*long*/ bits = OS.MAKELPARAM (start, end); + OS.SendMessage (handle, OS.CB_SETEDITSEL, 0, bits); +} + +/** + * 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); + if ((style & SWT.READ_ONLY) != 0) { + int index = indexOf (string); + if (index != -1) select (index); + return; + } + int limit = LIMIT; + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText != 0) { + limit = (int)/*64*/OS.SendMessage (hwndText, OS.EM_GETLIMITTEXT, 0, 0) & 0x7FFFFFFF; + } + if (string.length () > limit) string = string.substring (0, limit); + TCHAR buffer = new TCHAR (getCodePage (), string, true); + if (OS.SetWindowText (handle, buffer)) { + sendEvent (SWT.Modify); + // widget could be disposed at this point + } +} + +/** + * 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); + OS.SendMessage (handle, OS.CB_LIMITTEXT, limit, 0); +} + +void setToolTipText (Shell shell, String string) { + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + int /*long*/ hwndList = OS.GetDlgItem (handle, CBID_LIST); + if (hwndText != 0) shell.setToolTipText (hwndText, string); + if (hwndList != 0) shell.setToolTipText (hwndList, string); + shell.setToolTipText (handle, string); +} + +/** + * 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; + visibleCount = count; + if ((style & SWT.DROP_DOWN) != 0) { + forceResize (); + RECT rect = new RECT (); + OS.GetWindowRect (handle, rect); + int flags = OS.SWP_NOMOVE | OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE; + setBounds (0, 0, rect.right - rect.left, rect.bottom - rect.top, flags); + } +} + +void subclass () { + super.subclass (); + int /*long*/ newProc = display.windowProc; + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText != 0) { + OS.SetWindowLongPtr (hwndText, OS.GWLP_WNDPROC, newProc); + } + int /*long*/ hwndList = OS.GetDlgItem (handle, CBID_LIST); + if (hwndList != 0) { + OS.SetWindowLongPtr (hwndList, OS.GWLP_WNDPROC, newProc); + } +} + +boolean translateTraversal (MSG msg) { + /* + * When the combo box is dropped down, allow return + * to select an item in the list and escape to close + * the combo box. + */ + switch ((int)/*64*/(msg.wParam)) { + case OS.VK_RETURN: + case OS.VK_ESCAPE: + if ((style & SWT.DROP_DOWN) != 0) { + if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0) { + return false; + } + } + } + return super.translateTraversal (msg); +} + +boolean traverseEscape () { + if ((style & SWT.DROP_DOWN) != 0) { + if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0) { + OS.SendMessage (handle, OS.CB_SHOWDROPDOWN, 0, 0); + return true; + } + } + return super.traverseEscape (); +} + +boolean traverseReturn () { + if ((style & SWT.DROP_DOWN) != 0) { + if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0) { + OS.SendMessage (handle, OS.CB_SHOWDROPDOWN, 0, 0); + return true; + } + } + return super.traverseReturn (); +} + +void unsubclass () { + super.unsubclass (); + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText != 0 && EditProc != 0) { + OS.SetWindowLongPtr (hwndText, OS.GWLP_WNDPROC, EditProc); + } + int /*long*/ hwndList = OS.GetDlgItem (handle, CBID_LIST); + if (hwndList != 0 && ListProc != 0) { + OS.SetWindowLongPtr (hwndList, OS.GWLP_WNDPROC, ListProc); + } +} + +String verifyText (String string, int start, int end, Event keyEvent) { + Event event = new Event (); + event.text = string; + event.start = start; + event.end = end; + if (keyEvent != null) { + event.character = keyEvent.character; + event.keyCode = keyEvent.keyCode; + event.stateMask = keyEvent.stateMask; + } + if (!OS.IsUnicode && OS.IsDBLocale) { + event.start = mbcsToWcsPos (start); + event.end = mbcsToWcsPos (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; +} + +int wcsToMbcsPos (int wcsPos) { + if (wcsPos <= 0) return 0; + if (OS.IsUnicode) return wcsPos; + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText == 0) return wcsPos; + int mbcsSize = OS.GetWindowTextLengthA (hwndText); + if (mbcsSize == 0) return 0; + byte [] buffer = new byte [mbcsSize + 1]; + OS.GetWindowTextA (hwndText, buffer, mbcsSize + 1); + int mbcsPos = 0, wcsCount = 0; + while (mbcsPos < mbcsSize) { + if (wcsPos == wcsCount) break; + if (OS.IsDBCSLeadByte (buffer [mbcsPos++])) mbcsPos++; + wcsCount++; + } + return mbcsPos; +} + +int widgetExtStyle () { + return super.widgetExtStyle () & ~OS.WS_EX_NOINHERITLAYOUT; +} + +int widgetStyle () { + int bits = super.widgetStyle () | OS.CBS_AUTOHSCROLL | OS.CBS_NOINTEGRALHEIGHT | OS.WS_HSCROLL |OS.WS_VSCROLL; + if ((style & SWT.SIMPLE) != 0) return bits | OS.CBS_SIMPLE; + if ((style & SWT.READ_ONLY) != 0) return bits | OS.CBS_DROPDOWNLIST; + return bits | OS.CBS_DROPDOWN; +} + +TCHAR windowClass () { + return ComboClass; +} + +int /*long*/ windowProc () { + return ComboProc; +} + +int /*long*/ windowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + if (hwnd != handle) { + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + int /*long*/ hwndList = OS.GetDlgItem (handle, CBID_LIST); + if ((hwndText != 0 && hwnd == hwndText) || (hwndList != 0 && hwnd == hwndList)) { + LRESULT result = null; + switch (msg) { + /* Keyboard messages */ + case OS.WM_CHAR: result = wmChar (hwnd, wParam, lParam); break; + case OS.WM_IME_CHAR: result = wmIMEChar (hwnd, wParam, lParam); break; + case OS.WM_KEYDOWN: result = wmKeyDown (hwnd, wParam, lParam); break; + case OS.WM_KEYUP: result = wmKeyUp (hwnd, wParam, lParam); break; + case OS.WM_SYSCHAR: result = wmSysChar (hwnd, wParam, lParam); break; + case OS.WM_SYSKEYDOWN: result = wmSysKeyDown (hwnd, wParam, lParam); break; + case OS.WM_SYSKEYUP: result = wmSysKeyUp (hwnd, wParam, lParam); break; + + /* Mouse Messages */ + case OS.WM_CAPTURECHANGED: result = wmCaptureChanged (hwnd, wParam, lParam); break; + case OS.WM_LBUTTONDBLCLK: result = wmLButtonDblClk (hwnd, wParam, lParam); break; + case OS.WM_LBUTTONDOWN: result = wmLButtonDown (hwnd, wParam, lParam); break; + case OS.WM_LBUTTONUP: result = wmLButtonUp (hwnd, wParam, lParam); break; + case OS.WM_MBUTTONDBLCLK: result = wmMButtonDblClk (hwnd, wParam, lParam); break; + case OS.WM_MBUTTONDOWN: result = wmMButtonDown (hwnd, wParam, lParam); break; + case OS.WM_MBUTTONUP: result = wmMButtonUp (hwnd, wParam, lParam); break; + case OS.WM_MOUSEHOVER: result = wmMouseHover (hwnd, wParam, lParam); break; + case OS.WM_MOUSELEAVE: result = wmMouseLeave (hwnd, wParam, lParam); break; + case OS.WM_MOUSEMOVE: result = wmMouseMove (hwnd, wParam, lParam); break; +// case OS.WM_MOUSEWHEEL: result = wmMouseWheel (hwnd, wParam, lParam); break; + case OS.WM_RBUTTONDBLCLK: result = wmRButtonDblClk (hwnd, wParam, lParam); break; + case OS.WM_RBUTTONDOWN: result = wmRButtonDown (hwnd, wParam, lParam); break; + case OS.WM_RBUTTONUP: result = wmRButtonUp (hwnd, wParam, lParam); break; + case OS.WM_XBUTTONDBLCLK: result = wmXButtonDblClk (hwnd, wParam, lParam); break; + case OS.WM_XBUTTONDOWN: result = wmXButtonDown (hwnd, wParam, lParam); break; + case OS.WM_XBUTTONUP: result = wmXButtonUp (hwnd, wParam, lParam); break; + + /* Paint messages */ + case OS.WM_PAINT: result = wmPaint (hwnd, wParam, lParam); break; + + /* Menu messages */ + case OS.WM_CONTEXTMENU: result = wmContextMenu (hwnd, wParam, lParam); break; + + /* Clipboard messages */ + case OS.WM_CLEAR: + case OS.WM_CUT: + case OS.WM_PASTE: + case OS.WM_UNDO: + case OS.EM_UNDO: + case OS.WM_SETTEXT: + if (hwnd == hwndText) { + result = wmClipboard (hwnd, msg, wParam, lParam); + } + break; + } + if (result != null) return result.value; + return callWindowProc (hwnd, msg, wParam, lParam); + } + } + if (msg == OS.CB_SETCURSEL) { + if ((style & SWT.READ_ONLY) != 0) { + if (hooks (SWT.Verify) || filters (SWT.Verify)) { + String oldText = getText (), newText = null; + if (wParam == -1) { + newText = ""; + } else { + if (0 <= wParam && wParam < getItemCount ()) { + newText = getItem ((int)/*64*/wParam); + } + } + if (newText != null && !newText.equals (oldText)) { + int length = OS.GetWindowTextLength (handle); + oldText = newText; + newText = verifyText (newText, 0, length, null); + if (newText == null) return 0; + if (!newText.equals (oldText)) { + int index = indexOf (newText); + if (index != -1 && index != wParam) { + return callWindowProc (handle, OS.CB_SETCURSEL, index, lParam); + } + } + } + } + } + } + return super.windowProc (hwnd, msg, wParam, lParam); +} + +LRESULT WM_CTLCOLOR (int /*long*/ wParam, int /*long*/ lParam) { + return wmColorChild (wParam, lParam); +} + +LRESULT WM_GETDLGCODE (int /*long*/ wParam, int /*long*/ lParam) { + int /*long*/ code = callWindowProc (handle, OS.WM_GETDLGCODE, wParam, lParam); + return new LRESULT (code | OS.DLGC_WANTARROWS); +} + +LRESULT WM_KILLFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Bug in Windows. When a combo box that is read only + * is disposed in CBN_KILLFOCUS, Windows segment faults. + * The fix is to send focus from WM_KILLFOCUS instead + * of CBN_KILLFOCUS. + * + * NOTE: In version 6 of COMCTL32.DLL, the bug is fixed. + */ + if ((style & SWT.READ_ONLY) != 0) { + return super.WM_KILLFOCUS (wParam, lParam); + } + + /* + * Return NULL - Focus notification is + * done in WM_COMMAND by CBN_KILLFOCUS. + */ + return null; +} + +LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. When an editable combo box is dropped + * down and the text in the entry field partially matches an + * item in the list, Windows selects the item but doesn't send + * WM_COMMAND with CBN_SELCHANGE. The fix is to detect that + * the selection has changed and issue the notification. + */ + int oldSelection = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0); + LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam); + if (result == LRESULT.ZERO) return result; + if ((style & SWT.READ_ONLY) == 0) { + int newSelection = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0); + if (oldSelection != newSelection) { + sendEvent (SWT.Modify); + if (isDisposed ()) return LRESULT.ZERO; + sendEvent (SWT.Selection); + if (isDisposed ()) return LRESULT.ZERO; + } + } + return result; +} + +LRESULT WM_SETFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Return NULL - Focus notification is + * done by WM_COMMAND with CBN_SETFOCUS. + */ + return null; +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. When a combo box is resized, + * the size of the drop down rectangle is specified + * using the height and then the combo box resizes + * to be the height of the text field. This causes + * two WM_SIZE messages to be sent and two SWT.Resize + * events to be issued. The fix is to ignore the + * second resize. + */ + if (ignoreResize) return null; + /* + * Bug in Windows. If the combo box has the CBS_SIMPLE style, + * the list portion of the combo box is not redrawn when the + * combo box is resized. The fix is to force a redraw when + * the size has changed. + */ + if ((style & SWT.SIMPLE) != 0) { + LRESULT result = super.WM_SIZE (wParam, lParam); + if (OS.IsWindowVisible (handle)) { + if (OS.IsWinCE) { + int /*long*/ hwndText = OS.GetDlgItem (handle, CBID_EDIT); + if (hwndText != 0) OS.InvalidateRect (hwndText, null, true); + int /*long*/ hwndList = OS.GetDlgItem (handle, CBID_LIST); + if (hwndList != 0) OS.InvalidateRect (hwndList, null, true); + } else { + int uFlags = OS.RDW_ERASE | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, null, 0, uFlags); + } + } + return result; + } + + /* + * Feature in Windows. When an editable drop down combo box + * contains text that does not correspond to an item in the + * list, when the widget is resized, it selects the closest + * match from the list. The fix is to lock the current text + * by ignoring all WM_SETTEXT messages during processing of + * WM_SIZE. + */ + if ((style & SWT.READ_ONLY) == 0) lockText = true; + LRESULT result = super.WM_SIZE (wParam, lParam); + if ((style & SWT.READ_ONLY) == 0) lockText = false; + /* + * Feature in Windows. When CB_SETDROPPEDWIDTH is called with + * a width that is smaller than the current size of the combo + * box, it is ignored. This the fix is to set the width after + * the combo box has been resized. + */ + if ((style & SWT.H_SCROLL) != 0) setScrollWidth (scrollWidth); + return result; +} + +LRESULT WM_WINDOWPOSCHANGING (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_WINDOWPOSCHANGING (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. When a combo box is resized, + * the size of the drop down rectangle is specified + * using the height and then the combo box resizes + * to be the height of the text field. This causes + * sibling windows that intersect with the original + * bounds to redrawn. The fix is to stop the redraw + * using SWP_NOREDRAW and then damage the combo box + * text field and the area in the parent where the + * combo box used to be. + */ + if (OS.IsWinCE) return result; + if (!getDrawing ()) return result; + if (!OS.IsWindowVisible (handle)) return result; + if (ignoreResize) { + WINDOWPOS lpwp = new WINDOWPOS (); + OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof); + if ((lpwp.flags & OS.SWP_NOSIZE) == 0) { + lpwp.flags |= OS.SWP_NOREDRAW; + OS.MoveMemory (lParam, lpwp, WINDOWPOS.sizeof); + OS.InvalidateRect (handle, null, true); + RECT rect = new RECT (); + OS.GetWindowRect (handle, rect); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + if (width != 0 && height != 0) { + int /*long*/ hwndParent = parent.handle; + int /*long*/ hwndChild = OS.GetWindow (hwndParent, OS.GW_CHILD); + OS.MapWindowPoints (0, hwndParent, rect, 2); + int /*long*/ rgn1 = OS.CreateRectRgn (rect.left, rect.top, rect.right, rect.bottom); + while (hwndChild != 0) { + if (hwndChild != handle) { + OS.GetWindowRect (hwndChild, rect); + OS.MapWindowPoints (0, hwndParent, rect, 2); + int /*long*/ rgn2 = OS.CreateRectRgn (rect.left, rect.top, rect.right, rect.bottom); + OS.CombineRgn (rgn1, rgn1, rgn2, OS.RGN_DIFF); + OS.DeleteObject (rgn2); + } + hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT); + } + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE; + OS.RedrawWindow (hwndParent, null, rgn1, flags); + OS.DeleteObject (rgn1); + } + } + } + return result; +} + +LRESULT wmChar (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreCharacter) return null; + LRESULT result = super.wmChar (hwnd, wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. For some reason, when the + * widget is a single line text widget, when the + * user presses tab, return or escape, Windows beeps. + * The fix is to look for these keys and not call + * the window proc. + * + * NOTE: This only happens when the drop down list + * is not visible. + */ + switch ((int)/*64*/wParam) { + case SWT.TAB: return LRESULT.ZERO; + case SWT.CR: + if (!ignoreDefaultSelection) postEvent (SWT.DefaultSelection); + ignoreDefaultSelection = false; + // FALL THROUGH + case SWT.ESC: + if ((style & SWT.DROP_DOWN) != 0) { + if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) == 0) { + return LRESULT.ZERO; + } + } + } + return result; +} + +LRESULT wmClipboard (int /*long*/ hwndText, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if ((style & SWT.READ_ONLY) != 0) return null; + if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return null; + boolean call = false; + int [] start = new int [1], end = new int [1]; + String newText = null; + switch (msg) { + case OS.WM_CLEAR: + case OS.WM_CUT: + OS.SendMessage (hwndText, OS.EM_GETSEL, start, end); + if (start [0] != end [0]) { + newText = ""; + call = true; + } + break; + case OS.WM_PASTE: + OS.SendMessage (hwndText, OS.EM_GETSEL, start, end); + newText = getClipboardText (); + break; + case OS.EM_UNDO: + case OS.WM_UNDO: + if (OS.SendMessage (hwndText, OS.EM_CANUNDO, 0, 0) != 0) { + ignoreModify = true; + OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam); + int length = OS.GetWindowTextLength (hwndText); + int [] newStart = new int [1], newEnd = new int [1]; + OS.SendMessage (hwndText, OS.EM_GETSEL, newStart, newEnd); + if (length != 0 && newStart [0] != newEnd [0]) { + TCHAR buffer = new TCHAR (getCodePage (), length + 1); + OS.GetWindowText (hwndText, buffer, length + 1); + newText = buffer.toString (newStart [0], newEnd [0] - newStart [0]); + } else { + newText = ""; + } + OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam); + OS.SendMessage (hwndText, OS.EM_GETSEL, start, end); + ignoreModify = false; + } + break; + case OS.WM_SETTEXT: + end [0] = OS.GetWindowTextLength (hwndText); + int length = OS.IsUnicode ? OS.wcslen (lParam) : OS.strlen (lParam); + TCHAR buffer = new TCHAR (getCodePage (), length); + int byteCount = buffer.length () * TCHAR.sizeof; + OS.MoveMemory (buffer, lParam, byteCount); + newText = buffer.toString (0, length); + break; + } + if (newText != null) { + String oldText = newText; + newText = verifyText (newText, start [0], end [0], null); + if (newText == null) return LRESULT.ZERO; + if (!newText.equals (oldText)) { + if (call) { + OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam); + } + TCHAR buffer = new TCHAR (getCodePage (), newText, true); + if (msg == OS.WM_SETTEXT) { + int /*long*/ hHeap = OS.GetProcessHeap (); + int byteCount = buffer.length () * TCHAR.sizeof; + int /*long*/ pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (pszText, buffer, byteCount); + int /*long*/ code = OS.CallWindowProc (EditProc, hwndText, msg, wParam, pszText); + OS.HeapFree (hHeap, 0, pszText); + return new LRESULT (code); + } else { + OS.SendMessage (hwndText, OS.EM_REPLACESEL, 0, buffer); + return LRESULT.ZERO; + } + } + } + return null; +} + +LRESULT wmCommandChild (int /*long*/ wParam, int /*long*/ lParam) { + int code = OS.HIWORD (wParam); + switch (code) { + case OS.CBN_EDITCHANGE: + if (ignoreModify) break; + /* + * Feature in Windows. If the combo box list selection is + * queried using CB_GETCURSEL before the WM_COMMAND (with + * CBN_EDITCHANGE) returns, CB_GETCURSEL returns the previous + * selection in the list. It seems that the combo box sends + * the WM_COMMAND before it makes the selection in the list box + * match the entry field. The fix is remember that no selection + * in the list should exist in this case. + */ + noSelection = true; + sendEvent (SWT.Modify); + if (isDisposed ()) return LRESULT.ZERO; + noSelection = false; + break; + case OS.CBN_SELCHANGE: + /* + * Feature in Windows. If the text in an editable combo box + * is queried using GetWindowText () before the WM_COMMAND + * (with CBN_SELCHANGE) returns, GetWindowText () returns is + * the previous text in the combo box. It seems that the combo + * box sends the WM_COMMAND before it updates the text field to + * match the list selection. The fix is to force the text field + * to match the list selection by re-selecting the list item. + */ + int index = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0); + if (index != OS.CB_ERR) { + OS.SendMessage (handle, OS.CB_SETCURSEL, index, 0); + } + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the modify + * event. If this happens, end the processing of the + * Windows message by returning zero as the result of + * the window proc. + */ + sendEvent (SWT.Modify); + if (isDisposed ()) return LRESULT.ZERO; + postEvent (SWT.Selection); + break; + case OS.CBN_SETFOCUS: + sendFocusEvent (SWT.FocusIn); + if (isDisposed ()) return LRESULT.ZERO; + break; + case OS.CBN_KILLFOCUS: + /* + * Bug in Windows. When a combo box that is read only + * is disposed in CBN_KILLFOCUS, Windows segment faults. + * The fix is to send focus from WM_KILLFOCUS instead + * of CBN_KILLFOCUS. + * + * NOTE: In version 6 of COMCTL32.DLL, the bug is fixed. + */ + if ((style & SWT.READ_ONLY) != 0) break; + sendFocusEvent (SWT.FocusOut); + if (isDisposed ()) return LRESULT.ZERO; + break; + } + return super.wmCommandChild (wParam, lParam); +} + +LRESULT wmIMEChar (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + + /* Process a DBCS character */ + Display display = this.display; + display.lastKey = 0; + display.lastAscii = (int)/*64*/wParam; + display.lastVirtual = display.lastNull = display.lastDead = false; + if (!sendKeyEvent (SWT.KeyDown, OS.WM_IME_CHAR, wParam, lParam)) { + return LRESULT.ZERO; + } + + /* + * Feature in Windows. The Windows text widget uses + * two 2 WM_CHAR's to process a DBCS key instead of + * using WM_IME_CHAR. The fix is to allow the text + * widget to get the WM_CHAR's but ignore sending + * them to the application. + */ + ignoreCharacter = true; + int /*long*/ result = callWindowProc (hwnd, OS.WM_IME_CHAR, wParam, lParam); + MSG msg = new MSG (); + int flags = OS.PM_REMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE; + while (OS.PeekMessage (msg, hwnd, OS.WM_CHAR, OS.WM_CHAR, flags)) { + OS.TranslateMessage (msg); + OS.DispatchMessage (msg); + } + ignoreCharacter = false; + + sendKeyEvent (SWT.KeyUp, OS.WM_IME_CHAR, wParam, lParam); + // widget could be disposed at this point + display.lastKey = display.lastAscii = 0; + return new LRESULT (result); +} + +LRESULT wmKeyDown (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreCharacter) return null; + LRESULT result = super.wmKeyDown (hwnd, wParam, lParam); + if (result != null) return result; + ignoreDefaultSelection = false; + if (wParam == OS.VK_RETURN) { + if ((style & SWT.DROP_DOWN) != 0) { + if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0) { + ignoreDefaultSelection = true; + } + } + } + return result; +} + +LRESULT wmSysKeyDown (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. When an editable combo box is dropped + * down using Alt+Down and the text in the entry field partially + * matches an item in the list, Windows selects the item but doesn't + * send WM_COMMAND with CBN_SELCHANGE. The fix is to detect that + * the selection has changed and issue the notification. + */ + int oldSelection = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0); + LRESULT result = super.wmSysKeyDown (hwnd, wParam, lParam); + if (result != null) return result; + if ((style & SWT.READ_ONLY) == 0) { + if (wParam == OS.VK_DOWN) { + int /*long*/ code = callWindowProc (hwnd, OS.WM_SYSKEYDOWN, wParam, lParam); + int newSelection = (int)/*64*/OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0); + if (oldSelection != newSelection) { + sendEvent (SWT.Modify); + if (isDisposed ()) return LRESULT.ZERO; + sendEvent (SWT.Selection); + if (isDisposed ()) return LRESULT.ZERO; + } + return new LRESULT (code); + } + } + return result; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Composite.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Composite.java new file mode 100755 index 0000000000..aefc81663e --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Composite.java @@ -0,0 +1,1760 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * 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; + WINDOWPOS [] lpwp; + Control [] tabList; + int layoutCount, backgroundMode; + +/** + * Prevents uninitialized instances from being created outside the package. + */ +Composite () { +} + +/** + * 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 () { + int count = 0; + int /*long*/ hwndChild = OS.GetWindow (handle, OS.GW_CHILD); + if (hwndChild == 0) return new Control [0]; + while (hwndChild != 0) { + count++; + hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT); + } + Control [] children = new Control [count]; + int index = 0; + hwndChild = OS.GetWindow (handle, OS.GW_CHILD); + while (hwndChild != 0) { + Control control = display.getControl (hwndChild); + if (control != null && control != this) { + children [index++] = control; + } + hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT); + } + if (count == index) return children; + Control [] newChildren = new Control [index]; + System.arraycopy (children, 0, newChildren, 0, index); + return newChildren; +} + +Control [] _getTabList () { + if (tabList == null) return tabList; + 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; +} + +/** + * 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; + } + } +} + +void checkBuffered () { + if (OS.IsWinCE || (state & CANVAS) == 0) { + super.checkBuffered (); + } +} + +void checkComposited () { + if ((state & CANVAS) != 0) { + if ((style & SWT.TRANSPARENT) != 0) { + int /*long*/ hwndParent = parent.handle; + int bits = OS.GetWindowLong (hwndParent, OS.GWL_EXSTYLE); + bits |= OS.WS_EX_COMPOSITED; + OS.SetWindowLong (hwndParent, OS.GWL_EXSTYLE, bits); + } + } +} + +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; +} + +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; + state &= ~LAYOUT_CHANGED; + size = layout.computeSize (this, wHint, hHint, 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); +} + +/** + * Copies a rectangular area of the receiver at the specified + * position using the gc. + * + * @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> + */ +/*public*/ void copyArea (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); + + //XP only, no GDI+ + //#define PW_CLIENTONLY 0x00000001 + //DCOrg() wrong + //topHandle wrong for Tree? + int /*long*/ hDC = gc.handle; + int nSavedDC = OS.SaveDC (hDC); + OS.IntersectClipRect (hDC, 0, 0, width, height); + + //WRONG PARENT + POINT lpPoint = new POINT (); + int /*long*/ hwndParent = OS.GetParent (handle); + OS.MapWindowPoints (handle, hwndParent, lpPoint, 1); + RECT rect = new RECT (); + OS.GetWindowRect (handle, rect); + POINT lpPoint1 = new POINT (), lpPoint2 = new POINT (); + x = x + (lpPoint.x - rect.left); + y = y + (lpPoint.y - rect.top); + OS.SetWindowOrgEx (hDC, x, y, lpPoint1); + OS.SetBrushOrgEx (hDC, x, y, lpPoint2); + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.WS_VISIBLE) == 0) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + } + //NECESSARY? + OS.RedrawWindow (handle, null, 0, OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN); + OS.PrintWindow (handle, hDC, 0);//0x00000001); + if ((bits & OS.WS_VISIBLE) == 0) { + OS.DefWindowProc(handle, OS.WM_SETREDRAW, 0, 0); + } + OS.RestoreDC (hDC, nSavedDC); +} + +void createHandle () { + super.createHandle (); + state |= CANVAS; + if ((style & (SWT.H_SCROLL | SWT.V_SCROLL)) == 0) { + state |= THEME_BACKGROUND; + } + if ((style & SWT.TRANSPARENT) != 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); + bits |= OS.WS_EX_TRANSPARENT; + OS.SetWindowLong (handle, OS.GWL_EXSTYLE, bits); + } +} + +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 (); +} + +int getChildrenCount () { + /* + * NOTE: The current implementation will count + * non-registered children. + */ + int count = 0; + int /*long*/ hwndChild = OS.GetWindow (handle, OS.GW_CHILD); + while (hwndChild != 0) { + count++; + hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT); + } + return count; +} + +/** + * 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; +} + +/** + * 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); +} + +/** + * 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 ; +} + +/** + * 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; +} + +/** + * 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 (true, 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 (true, 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 hHint, 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 redrawChildren () { + if (!super.redrawChildren ()) return false; + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + children [i].redrawChildren (); + } + return true; +} + +void releaseParent () { + super.releaseParent (); + if ((state & CANVAS) != 0) { + if ((style & SWT.TRANSPARENT) != 0) { + int /*long*/ hwndParent = parent.handle; + int /*long*/ hwndChild = OS.GetWindow (hwndParent, OS.GW_CHILD); + while (hwndChild != 0) { + if (hwndChild != handle) { + int bits = OS.GetWindowLong (hwndParent, OS.GWL_EXSTYLE); + if ((bits & OS.WS_EX_TRANSPARENT) != 0) return; + } + hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT); + } + int bits = OS.GetWindowLong (hwndParent, OS.GWL_EXSTYLE); + bits &= ~OS.WS_EX_COMPOSITED; + OS.SetWindowLong (hwndParent, OS.GWL_EXSTYLE, bits); + } + } +} + +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 (); + if ((state & CANVAS) != 0 && (style & SWT.EMBEDDED) != 0) { + int /*long*/ hwndChild = OS.GetWindow (handle, OS.GW_CHILD); + if (hwndChild != 0) { + int threadId = OS.GetWindowThreadProcessId (hwndChild, null); + if (threadId != OS.GetCurrentThreadId ()) { + OS.ShowWindow (hwndChild, OS.SW_HIDE); + OS.SetParent (hwndChild, 0); + } + } + } + layout = null; + tabList = null; + lpwp = null; +} + +void removeControl (Control control) { + fixTabList (control); + resizeChildren (); +} + +void resizeChildren () { + if (lpwp == null) return; + do { + WINDOWPOS [] currentLpwp = lpwp; + lpwp = null; + if (!resizeChildren (true, currentLpwp)) { + resizeChildren (false, currentLpwp); + } + } while (lpwp != null); +} + +boolean resizeChildren (boolean defer, WINDOWPOS [] pwp) { + if (pwp == null) return true; + int /*long*/ hdwp = 0; + if (defer) { + hdwp = OS.BeginDeferWindowPos (pwp.length); + if (hdwp == 0) return false; + } + for (int i=0; i<pwp.length; i++) { + WINDOWPOS wp = pwp [i]; + if (wp != null) { + /* + * This code is intentionally commented. All widgets that + * are created by SWT have WS_CLIPSIBLINGS to ensure that + * application code does not draw outside of the control. + */ +// int count = parent.getChildrenCount (); +// if (count > 1) { +// int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); +// if ((bits & OS.WS_CLIPSIBLINGS) == 0) wp.flags |= OS.SWP_NOCOPYBITS; +// } + if (defer) { + hdwp = DeferWindowPos (hdwp, wp.hwnd, 0, wp.x, wp.y, wp.cx, wp.cy, wp.flags); + if (hdwp == 0) return false; + } else { + SetWindowPos (wp.hwnd, 0, wp.x, wp.y, wp.cx, wp.cy, wp.flags); + } + } + } + if (defer) return OS.EndDeferWindowPos (hdwp); + return true; +} + +void resizeEmbeddedHandle(int /*long*/ embeddedHandle, int width, int height) { + if (embeddedHandle == 0) return; + int [] processID = new int [1]; + int threadId = OS.GetWindowThreadProcessId (embeddedHandle, processID); + if (threadId != OS.GetCurrentThreadId ()) { + if (processID [0] == OS.GetCurrentProcessId ()) { + if (display.msgHook == 0) { + if (!OS.IsWinCE) { + display.getMsgCallback = new Callback (display, "getMsgProc", 3); + display.getMsgProc = display.getMsgCallback.getAddress (); + if (display.getMsgProc == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + display.msgHook = OS.SetWindowsHookEx (OS.WH_GETMESSAGE, display.getMsgProc, OS.GetLibraryHandle(), threadId); + OS.PostThreadMessage (threadId, OS.WM_NULL, 0, 0); + } + } + } + int flags = OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE | OS.SWP_ASYNCWINDOWPOS; + OS.SetWindowPos (embeddedHandle, 0, 0, 0, width, height, flags); + } +} + +void sendResize () { + setResizeChildren (false); + super.sendResize (); + if (isDisposed ()) return; + if (layout != null) { + markLayout (false, false); + updateLayout (false, false); + } + setResizeChildren (true); +} + +/** + * 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 (); + } +} + +void setBounds (int x, int y, int width, int height, int flags, boolean defer) { + if (display.resizeCount > Display.RESIZE_LIMIT) { + defer = false; + } + if (!defer && (state & CANVAS) != 0) { + state &= ~(RESIZE_OCCURRED | MOVE_OCCURRED); + state |= RESIZE_DEFERRED | MOVE_DEFERRED; + } + super.setBounds (x, y, width, height, flags, defer); + if (!defer && (state & CANVAS) != 0) { + boolean wasMoved = (state & MOVE_OCCURRED) != 0; + boolean wasResized = (state & RESIZE_OCCURRED) != 0; + state &= ~(RESIZE_DEFERRED | MOVE_DEFERRED); + if (wasMoved && !isDisposed ()) sendMove (); + if (wasResized && !isDisposed ()) sendResize (); + } +} + +boolean setFixedFocus () { + checkWidget (); + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + Control child = children [i]; + if (child.setRadioFocus (false)) return true; + } + for (int i=0; i<children.length; i++) { + Control child = children [i]; + if (child.setFixedFocus ()) return true; + } + return super.setFixedFocus (); +} + +public boolean setFocus () { + checkWidget (); + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + Control child = children [i]; + if (child.setRadioFocus (false)) return true; + } + for (int i=0; i<children.length; i++) { + Control child = children [i]; + if (child.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, true); + } + } + } else { + layoutCount++; + } +} +/** + * 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; +} + +void setResizeChildren (boolean resize) { + if (resize) { + resizeChildren (); + } else { + if (display.resizeCount > Display.RESIZE_LIMIT) { + return; + } + int count = getChildrenCount (); + if (count > 1 && lpwp == null) { + lpwp = new WINDOWPOS [count]; + } + } +} + +boolean setTabGroupFocus () { + if (isTabItem ()) return setTabItemFocus (); + boolean takeFocus = (style & SWT.NO_FOCUS) == 0; + if ((state & CANVAS) != 0) { + takeFocus = hooksKeys (); + if ((style & SWT.EMBEDDED) != 0) takeFocus = true; + } + if (takeFocus && setTabItemFocus ()) return true; + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + Control child = children [i]; + if (child.isTabItem () && child.setRadioFocus (true)) return true; + } + for (int i=0; i<children.length; i++) { + Control child = children [i]; + if (child.isTabItem () && !child.isTabGroup () && child.setTabItemFocus ()) { + return true; + } + } + return false; +} + +String toolTipText (NMTTDISPINFO hdr) { + Shell shell = getShell (); + if ((hdr.uFlags & OS.TTF_IDISHWND) == 0) { + String string = null; + ToolTip toolTip = shell.findToolTip ((int)/*64*/hdr.idFrom); + if (toolTip != null) { + string = toolTip.message; + if (string == null || string.length () == 0) string = " "; + } + return string; + } + shell.setToolTipTitle (hdr.hwndFrom, null, 0); + OS.SendMessage (hdr.hwndFrom, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF); + Control control = display.getControl (hdr.idFrom); + return control != null ? control.toolTipText : null; +} + +boolean translateMnemonic (Event event, Control control) { + if (super.translateMnemonic (event, control)) return true; + if (control != null) { + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + Control child = children [i]; + if (child.translateMnemonic (event, control)) return true; + } + } + return false; +} + +boolean translateTraversal (MSG msg) { + if ((state & CANVAS) != 0 ) { + if ((style & SWT.EMBEDDED) != 0) return false; + switch ((int)/*64*/msg.wParam) { + case OS.VK_UP: + case OS.VK_LEFT: + case OS.VK_DOWN: + case OS.VK_RIGHT: + case OS.VK_PRIOR: + case OS.VK_NEXT: + int uiState = (int)/*64*/OS.SendMessage (msg.hwnd, OS.WM_QUERYUISTATE, 0, 0); + if ((uiState & OS.UISF_HIDEFOCUS) != 0) { + OS.SendMessage (msg.hwnd, OS.WM_UPDATEUISTATE, OS.MAKEWPARAM (OS.UIS_CLEAR, OS.UISF_HIDEFOCUS), 0); + } + break; + } + } + return super.translateTraversal (msg); +} + +void updateBackgroundColor () { + super.updateBackgroundColor (); + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + if ((children [i].state & PARENT_BACKGROUND) != 0) { + children [i].updateBackgroundColor (); + } + } +} + +void updateBackgroundImage () { + super.updateBackgroundImage (); + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + if ((children [i].state & PARENT_BACKGROUND) != 0) { + children [i].updateBackgroundImage (); + } + } +} + +void updateBackgroundMode () { + super.updateBackgroundMode (); + Control [] children = _getChildren (); + for (int i = 0; i < children.length; i++) { + children [i].updateBackgroundMode (); + } +} + +void updateFont (Font oldFont, Font newFont) { + super.updateFont (oldFont, newFont); + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + Control control = children [i]; + if (!control.isDisposed ()) { + control.updateFont (oldFont, newFont); + } + } +} + +void updateLayout (boolean resize, 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); + if (resize) setResizeChildren (false); + layout.layout (this, changed); + if (resize) setResizeChildren (true); + } + if (all) { + state &= ~LAYOUT_CHILD; + Control [] children = _getChildren (); + for (int i=0; i<children.length; i++) { + children [i].updateLayout (resize, all); + } + } +} + +void updateUIState () { + int /*long*/ hwndShell = getShell ().handle; + int uiState = /*64*/(int)OS.SendMessage (hwndShell, OS.WM_QUERYUISTATE, 0, 0); + if ((uiState & OS.UISF_HIDEFOCUS) != 0) { + OS.SendMessage (hwndShell, OS.WM_CHANGEUISTATE, OS.MAKEWPARAM (OS.UIS_CLEAR, OS.UISF_HIDEFOCUS), 0); + } +} + +int widgetStyle () { + /* Force clipping of children by setting WS_CLIPCHILDREN */ + return super.widgetStyle () | OS.WS_CLIPCHILDREN; +} + +LRESULT WM_ERASEBKGND (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_ERASEBKGND (wParam, lParam); + if (result != null) return result; + if ((state & CANVAS) != 0) { + /* Return zero to indicate that the background was not erased */ + if ((style & (SWT.NO_BACKGROUND | SWT.TRANSPARENT)) != 0) { + return LRESULT.ZERO; + } + } + return result; +} + +LRESULT WM_GETDLGCODE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_GETDLGCODE (wParam, lParam); + if (result != null) return result; + if ((state & CANVAS) != 0) { + int flags = 0; + if (hooksKeys ()) { + flags |= OS.DLGC_WANTALLKEYS | OS.DLGC_WANTARROWS | OS.DLGC_WANTTAB; + } + if ((style & SWT.NO_FOCUS) != 0) flags |= OS.DLGC_STATIC; + if (OS.GetWindow (handle, OS.GW_CHILD) != 0) flags |= OS.DLGC_STATIC; + if (flags != 0) return new LRESULT (flags); + } + return result; +} + +LRESULT WM_GETFONT (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_GETFONT (wParam, lParam); + if (result != null) return result; + int /*long*/ code = callWindowProc (handle, OS.WM_GETFONT, wParam, lParam); + if (code != 0) return new LRESULT (code); + return new LRESULT (font != null ? font.handle : defaultFont ()); +} + +LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam); + if (result == LRESULT.ZERO) return result; + + /* Set focus for a canvas with no children */ + if ((state & CANVAS) != 0) { + if ((style & SWT.NO_FOCUS) == 0 && hooksKeys ()) { + if (OS.GetWindow (handle, OS.GW_CHILD) == 0) setFocus (); + } + } + return result; +} + +LRESULT WM_NCHITTEST (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_NCHITTEST (wParam, lParam); + if (result != null) return result; + /* + * Bug in Windows. For some reason, under circumstances + * that are not understood, when one scrolled window is + * embedded in another and the outer window scrolls the + * inner horizontally by moving the location of the inner + * one, the vertical scroll bars of the inner window no + * longer function. Specifically, WM_NCHITTEST returns + * HTCLIENT instead of HTVSCROLL. The fix is to detect + * the case where the result of WM_NCHITTEST is HTCLIENT + * and the point is not in the client area, and redraw + * the trim, which somehow fixes the next WM_NCHITTEST. + */ + if (!OS.IsWinCE && OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if ((state & CANVAS)!= 0) { + int /*long*/ code = callWindowProc (handle, OS.WM_NCHITTEST, wParam, lParam); + if (code == OS.HTCLIENT) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + POINT pt = new POINT (); + pt.x = OS.GET_X_LPARAM (lParam); + pt.y = OS.GET_Y_LPARAM (lParam); + OS.MapWindowPoints (0, handle, pt, 1); + if (!OS.PtInRect (rect, pt)) { + int flags = OS.RDW_FRAME | OS.RDW_INVALIDATE; + OS.RedrawWindow (handle, null, 0, flags); + } + } + return new LRESULT (code); + } + } + return result; +} + +LRESULT WM_PARENTNOTIFY (int /*long*/ wParam, int /*long*/ lParam) { + if ((state & CANVAS) != 0 && (style & SWT.EMBEDDED) != 0) { + if (OS.LOWORD (wParam) == OS.WM_CREATE) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + resizeEmbeddedHandle (lParam, rect.right - rect.left, rect.bottom - rect.top); + } + } + return super.WM_PARENTNOTIFY (wParam, lParam); +} + +LRESULT WM_PAINT (int /*long*/ wParam, int /*long*/ lParam) { + if ((state & CANVAS) == 0 || (state & FOREIGN_HANDLE) != 0) { + return super.WM_PAINT (wParam, lParam); + } + + /* Set the clipping bits */ + int oldBits = 0, newBits = 0; + if (!OS.IsWinCE) { + oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE); + newBits = oldBits | OS.WS_CLIPSIBLINGS | OS.WS_CLIPCHILDREN; + if (newBits != oldBits) OS.SetWindowLong (handle, OS.GWL_STYLE, newBits); + } + + /* Paint the control and the background */ + PAINTSTRUCT ps = new PAINTSTRUCT (); + if (hooks (SWT.Paint) || filters (SWT.Paint)) { + + /* Use the buffered paint when possible */ + boolean bufferedPaint = false; + if ((style & SWT.DOUBLE_BUFFERED) != 0) { + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + if ((style & (SWT.NO_MERGE_PAINTS | SWT.RIGHT_TO_LEFT)) == 0) { + if ((style & SWT.TRANSPARENT) == 0) bufferedPaint = true; + } + } + } + if (bufferedPaint) { + int /*long*/ hDC = OS.BeginPaint (handle, ps); + int width = ps.right - ps.left; + int height = ps.bottom - ps.top; + if (width != 0 && height != 0) { + int /*long*/ [] phdc = new int /*long*/ [1]; + int flags = OS.BPBF_COMPATIBLEBITMAP; + RECT prcTarget = new RECT (); + OS.SetRect (prcTarget, ps.left, ps.top, ps.right, ps.bottom); + int /*long*/ hBufferedPaint = OS.BeginBufferedPaint (hDC, prcTarget, flags, null, phdc); + GCData data = new GCData (); + data.device = display; + data.foreground = getForegroundPixel (); + Control control = findBackgroundControl (); + if (control == null) control = this; + data.background = control.getBackgroundPixel (); + data.font = Font.win32_new(display, OS.SendMessage (handle, OS.WM_GETFONT, 0, 0)); + data.uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + if ((style & SWT.NO_BACKGROUND) != 0) { + /* This code is intentionally commented because it may be slow to copy bits from the screen */ + //paintGC.copyArea (image, ps.left, ps.top); + } else { + RECT rect = new RECT (); + OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom); + drawBackground (phdc [0], rect); + } + GC gc = GC.win32_new (phdc [0], data); + Event event = new Event (); + event.gc = gc; + event.x = ps.left; + event.y = ps.top; + event.width = width; + event.height = height; + sendEvent (SWT.Paint, event); + if (data.focusDrawn && !isDisposed ()) updateUIState (); + gc.dispose (); + OS.EndBufferedPaint (hBufferedPaint, true); + } + OS.EndPaint (handle, ps); + } else { + + /* Create the paint GC */ + GCData data = new GCData (); + data.ps = ps; + data.hwnd = handle; + GC gc = GC.win32_new (this, data); + + /* Get the system region for the paint HDC */ + int /*long*/ sysRgn = 0; + if ((style & (SWT.DOUBLE_BUFFERED | SWT.TRANSPARENT)) != 0 || (style & SWT.NO_MERGE_PAINTS) != 0) { + sysRgn = OS.CreateRectRgn (0, 0, 0, 0); + if (OS.GetRandomRgn (gc.handle, sysRgn, OS.SYSRGN) == 1) { + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) { + if ((OS.GetLayout (gc.handle) & OS.LAYOUT_RTL) != 0) { + int nBytes = OS.GetRegionData (sysRgn, 0, null); + int [] lpRgnData = new int [nBytes / 4]; + OS.GetRegionData (sysRgn, nBytes, lpRgnData); + int /*long*/ newSysRgn = OS.ExtCreateRegion (new float [] {-1, 0, 0, 1, 0, 0}, nBytes, lpRgnData); + OS.DeleteObject (sysRgn); + sysRgn = newSysRgn; + } + } + if (OS.IsWinNT) { + POINT pt = new POINT(); + OS.MapWindowPoints (0, handle, pt, 1); + OS.OffsetRgn (sysRgn, pt.x, pt.y); + } + } + } + + /* Send the paint event */ + int width = ps.right - ps.left; + int height = ps.bottom - ps.top; + if (width != 0 && height != 0) { + GC paintGC = null; + Image image = null; + if ((style & (SWT.DOUBLE_BUFFERED | SWT.TRANSPARENT)) != 0) { + image = new Image (display, width, height); + paintGC = gc; + gc = new GC (image, paintGC.getStyle() & SWT.RIGHT_TO_LEFT); + GCData gcData = gc.getGCData (); + gcData.uiState = data.uiState; + gc.setForeground (getForeground ()); + gc.setBackground (getBackground ()); + gc.setFont (getFont ()); + if ((style & SWT.TRANSPARENT) != 0) { + OS.BitBlt (gc.handle, 0, 0, width, height, paintGC.handle, ps.left, ps.top, OS.SRCCOPY); + } + OS.OffsetRgn (sysRgn, -ps.left, -ps.top); + OS.SelectClipRgn (gc.handle, sysRgn); + OS.OffsetRgn (sysRgn, ps.left, ps.top); + OS.SetMetaRgn (gc.handle); + OS.SetWindowOrgEx (gc.handle, ps.left, ps.top, null); + OS.SetBrushOrgEx (gc.handle, ps.left, ps.top, null); + if ((style & (SWT.NO_BACKGROUND | SWT.TRANSPARENT)) != 0) { + /* This code is intentionally commented because it may be slow to copy bits from the screen */ + //paintGC.copyArea (image, ps.left, ps.top); + } else { + RECT rect = new RECT (); + OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom); + drawBackground (gc.handle, rect); + } + } + Event event = new Event (); + event.gc = gc; + RECT rect = null; + if ((style & SWT.NO_MERGE_PAINTS) != 0 && OS.GetRgnBox (sysRgn, rect = new RECT ()) == OS.COMPLEXREGION) { + int nBytes = OS.GetRegionData (sysRgn, 0, null); + int [] lpRgnData = new int [nBytes / 4]; + OS.GetRegionData (sysRgn, nBytes, lpRgnData); + int count = lpRgnData [2]; + for (int i=0; i<count; i++) { + int offset = 8 + (i << 2); + OS.SetRect (rect, lpRgnData [offset], lpRgnData [offset + 1], lpRgnData [offset + 2], lpRgnData [offset + 3]); + if ((style & (SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.TRANSPARENT)) == 0) { + drawBackground (gc.handle, rect); + } + event.x = rect.left; + event.y = rect.top; + event.width = rect.right - rect.left; + event.height = rect.bottom - rect.top; + event.count = count - 1 - i; + sendEvent (SWT.Paint, event); + } + } else { + if ((style & (SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.TRANSPARENT)) == 0) { + if (rect == null) rect = new RECT (); + OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom); + drawBackground (gc.handle, rect); + } + event.x = ps.left; + event.y = ps.top; + event.width = width; + event.height = height; + sendEvent (SWT.Paint, event); + } + // widget could be disposed at this point + event.gc = null; + if ((style & (SWT.DOUBLE_BUFFERED | SWT.TRANSPARENT)) != 0) { + if (!gc.isDisposed ()) { + GCData gcData = gc.getGCData (); + if (gcData.focusDrawn && !isDisposed ()) updateUIState (); + } + gc.dispose(); + if (!isDisposed ()) paintGC.drawImage (image, ps.left, ps.top); + image.dispose (); + gc = paintGC; + } + } + if (sysRgn != 0) OS.DeleteObject (sysRgn); + if (data.focusDrawn && !isDisposed ()) updateUIState (); + + /* Dispose the paint GC */ + gc.dispose (); + } + } else { + int /*long*/ hDC = OS.BeginPaint (handle, ps); + if ((style & (SWT.NO_BACKGROUND | SWT.TRANSPARENT)) == 0) { + RECT rect = new RECT (); + OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom); + drawBackground (hDC, rect); + } + OS.EndPaint (handle, ps); + } + + /* Restore the clipping bits */ + if (!OS.IsWinCE && !isDisposed ()) { + if (newBits != oldBits) { + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the paint + * event. If this happens, don't attempt to restore + * the style. + */ + if (!isDisposed ()) { + OS.SetWindowLong (handle, OS.GWL_STYLE, oldBits); + } + } + } + return LRESULT.ZERO; +} + +LRESULT WM_PRINTCLIENT (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_PRINTCLIENT (wParam, lParam); + if (result != null) return result; + if ((state & CANVAS) != 0) { + forceResize (); + int nSavedDC = OS.SaveDC (wParam); + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + if ((style & (SWT.NO_BACKGROUND | SWT.TRANSPARENT)) == 0) { + drawBackground (wParam, rect); + } + if (hooks (SWT.Paint) || filters (SWT.Paint)) { + GCData data = new GCData (); + data.device = display; + data.foreground = getForegroundPixel (); + Control control = findBackgroundControl (); + if (control == null) control = this; + data.background = control.getBackgroundPixel (); + data.font = Font.win32_new(display, OS.SendMessage (handle, OS.WM_GETFONT, 0, 0)); + data.uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + GC gc = GC.win32_new (wParam, data); + Event event = new Event (); + event.gc = gc; + event.x = rect.left; + event.y = rect.top; + event.width = rect.right - rect.left; + event.height = rect.bottom - rect.top; + sendEvent (SWT.Paint, event); + event.gc = null; + gc.dispose (); + } + OS.RestoreDC (wParam, nSavedDC); + } + return result; +} + +LRESULT WM_SETFONT (int /*long*/ wParam, int /*long*/ lParam) { + if (lParam != 0) OS.InvalidateRect (handle, null, true); + return super.WM_SETFONT (wParam, lParam); +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = null; + if ((state & RESIZE_DEFERRED) != 0) { + result = super.WM_SIZE (wParam, lParam); + } else { + /* Begin deferred window positioning */ + setResizeChildren (false); + + /* Resize and Layout */ + result = super.WM_SIZE (wParam, lParam); + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the resize + * event. If this happens, end the processing of the + * Windows message by returning the result of the + * WM_SIZE message. + */ + if (isDisposed ()) return result; + if (layout != null) { + markLayout (false, false); + updateLayout (false, false); + } + + /* End deferred window positioning */ + setResizeChildren (true); + } + + /* Damage the widget to cause a repaint */ + if (OS.IsWindowVisible (handle)) { + if ((state & CANVAS) != 0) { + if ((style & SWT.NO_REDRAW_RESIZE) == 0) { + if (hooks (SWT.Paint)) { + OS.InvalidateRect (handle, null, true); + } + } + } + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if (findThemeControl () != null) redrawChildren (); + } + } + + /* Resize the embedded window */ + if ((state & CANVAS) != 0 && (style & SWT.EMBEDDED) != 0) { + resizeEmbeddedHandle (OS.GetWindow (handle, OS.GW_CHILD), OS.LOWORD (lParam), OS.HIWORD (lParam)); + } + return result; +} + +LRESULT WM_SYSCOLORCHANGE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam); + if (result != null) return result; + int /*long*/ hwndChild = OS.GetWindow (handle, OS.GW_CHILD); + while (hwndChild != 0) { + OS.SendMessage (hwndChild, OS.WM_SYSCOLORCHANGE, 0, 0); + hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT); + } + return result; +} + +LRESULT WM_SYSCOMMAND (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SYSCOMMAND (wParam, lParam); + if (result != null) return result; + + /* + * Check to see if the command is a system command or + * a user menu item that was added to the system menu. + * + * NOTE: This is undocumented. + */ + if ((wParam & 0xF000) == 0) return result; + + /* + * Bug in Windows. When a vertical or horizontal scroll bar is + * hidden or shown while the opposite scroll bar is being scrolled + * by the user (with WM_HSCROLL code SB_LINEDOWN), the scroll bar + * does not redraw properly. The fix is to detect this case and + * redraw the non-client area. + */ + if (!OS.IsWinCE) { + int cmd = (int)/*64*/wParam & 0xFFF0; + switch (cmd) { + case OS.SC_HSCROLL: + case OS.SC_VSCROLL: + boolean showHBar = horizontalBar != null && horizontalBar.getVisible (); + boolean showVBar = verticalBar != null && verticalBar.getVisible (); + int /*long*/ code = callWindowProc (handle, OS.WM_SYSCOMMAND, wParam, lParam); + if ((showHBar != (horizontalBar != null && horizontalBar.getVisible ())) || + (showVBar != (verticalBar != null && verticalBar.getVisible ()))) { + int flags = OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_UPDATENOW; + OS.RedrawWindow (handle, null, 0, flags); + } + if (code == 0) return LRESULT.ZERO; + return new LRESULT (code); + } + } + /* Return the result */ + return result; +} + +LRESULT WM_UPDATEUISTATE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_UPDATEUISTATE (wParam, lParam); + if (result != null) return result; + if ((state & CANVAS) != 0 && hooks (SWT.Paint)) { + OS.InvalidateRect (handle, null, true); + } + return result; +} + +LRESULT wmNCPaint (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.wmNCPaint (hwnd, wParam, lParam); + if (result != null) return result; + int /*long*/ borderHandle = borderHandle (); + if ((state & CANVAS) != 0 || (hwnd == borderHandle && handle != borderHandle)) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + int bits1 = OS.GetWindowLong (hwnd, OS.GWL_EXSTYLE); + if ((bits1 & OS.WS_EX_CLIENTEDGE) != 0) { + int /*long*/ code = 0; + int bits2 = OS.GetWindowLong (hwnd, OS.GWL_STYLE); + if ((bits2 & (OS.WS_HSCROLL | OS.WS_VSCROLL)) != 0) { + code = callWindowProc (hwnd, OS.WM_NCPAINT, wParam, lParam); + } + int /*long*/ hDC = OS.GetWindowDC (hwnd); + RECT rect = new RECT (); + OS.GetWindowRect (hwnd, rect); + rect.right -= rect.left; + rect.bottom -= rect.top; + rect.left = rect.top = 0; + int border = OS.GetSystemMetrics (OS.SM_CXEDGE); + OS.ExcludeClipRect (hDC, border, border, rect.right - border, rect.bottom - border); + OS.DrawThemeBackground (display.hEditTheme (), hDC, OS.EP_EDITTEXT, OS.ETS_NORMAL, rect, null); + OS.ReleaseDC (hwnd, hDC); + return new LRESULT (code); + } + } + } + return result; +} + +LRESULT wmNotify (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + if (!OS.IsWinCE) { + switch (hdr.code) { + /* + * Feature in Windows. When the tool tip control is + * created, the parent of the tool tip is the shell. + * If SetParent () is used to reparent the tool bar + * into a new shell, the tool tip is not reparented + * and pops up underneath the new shell. The fix is + * to make sure the tool tip is a topmost window. + */ + case OS.TTN_SHOW: + case OS.TTN_POP: { + /* + * Bug in Windows 98 and NT. Setting the tool tip to be the + * top most window using HWND_TOPMOST can result in a parent + * dialog shell being moved behind its parent if the dialog + * has a sibling that is currently on top. The fix is to + * lock the z-order of the active window. + * + * Feature in Windows. Using SetWindowPos() with HWND_NOTOPMOST + * to clear the topmost state of a window whose parent is already + * topmost clears the topmost state of the parent. The fix is to + * check if the parent is already on top and neither set or clear + * the topmost status of the tool tip. + */ + int /*long*/ hwndParent = hdr.hwndFrom; + do { + hwndParent = OS.GetParent (hwndParent); + if (hwndParent == 0) break; + int bits = OS.GetWindowLong (hwndParent, OS.GWL_EXSTYLE); + if ((bits & OS.WS_EX_TOPMOST) != 0) break; + } while (true); + if (hwndParent != 0) break; + display.lockActiveWindow = true; + int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOSIZE; + int /*long*/ hwndInsertAfter = hdr.code == OS.TTN_SHOW ? OS.HWND_TOPMOST : OS.HWND_NOTOPMOST; + SetWindowPos (hdr.hwndFrom, hwndInsertAfter, 0, 0, 0, 0, flags); + display.lockActiveWindow = false; + break; + } + /* + * Bug in Windows 98. For some reason, the tool bar control + * sends both TTN_GETDISPINFOW and TTN_GETDISPINFOA to get + * the tool tip text and the tab folder control sends only + * TTN_GETDISPINFOW. The fix is to handle only TTN_GETDISPINFOW, + * even though it should never be sent on Windows 98. + * + * NOTE: Because the size of NMTTDISPINFO differs between + * Windows 98 and NT, guard against the case where the wrong + * kind of message occurs by inlining the memory moves and + * the UNICODE conversion code. + */ + case OS.TTN_GETDISPINFOA: + case OS.TTN_GETDISPINFOW: { + NMTTDISPINFO lpnmtdi; + if (hdr.code == OS.TTN_GETDISPINFOA) { + lpnmtdi = new NMTTDISPINFOA (); + OS.MoveMemory ((NMTTDISPINFOA)lpnmtdi, lParam, NMTTDISPINFOA.sizeof); + } else { + lpnmtdi = new NMTTDISPINFOW (); + OS.MoveMemory ((NMTTDISPINFOW)lpnmtdi, lParam, NMTTDISPINFOW.sizeof); + } + String string = toolTipText (lpnmtdi); + if (string != null) { + Shell shell = getShell (); + string = Display.withCrLf (string); + char [] chars = fixMnemonic (string); + + /* + * Ensure that the orientation of the tool tip matches + * the orientation of the control. + */ + Widget widget = null; + int /*long*/ hwnd = hdr.idFrom; + if ((lpnmtdi.uFlags & OS.TTF_IDISHWND) != 0) { + widget = display.getControl (hwnd); + } else { + if (hdr.hwndFrom == shell.toolTipHandle || hdr.hwndFrom == shell.balloonTipHandle) { + widget = shell.findToolTip ((int)/*64*/hdr.idFrom); + } + } + if (widget != null) { + if ((widget.getStyle () & SWT.RIGHT_TO_LEFT) != 0) { + lpnmtdi.uFlags |= OS.TTF_RTLREADING; + } else { + lpnmtdi.uFlags &= ~OS.TTF_RTLREADING; + } + } + + if (hdr.code == OS.TTN_GETDISPINFOA) { + byte [] bytes = new byte [chars.length * 2]; + OS.WideCharToMultiByte (getCodePage (), 0, chars, chars.length, bytes, bytes.length, null, null); + shell.setToolTipText (lpnmtdi, bytes); + OS.MoveMemory (lParam, (NMTTDISPINFOA)lpnmtdi, NMTTDISPINFOA.sizeof); + } else { + shell.setToolTipText (lpnmtdi, chars); + OS.MoveMemory (lParam, (NMTTDISPINFOW)lpnmtdi, NMTTDISPINFOW.sizeof); + } + return LRESULT.ZERO; + } + break; + } + } + } + return super.wmNotify (hdr, wParam, lParam); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java new file mode 100755 index 0000000000..1edef54d8b --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java @@ -0,0 +1,4894 @@ +/******************************************************************************* + * 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.gdip.*; +import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.accessibility.*; + +/** + * 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 int /*long*/ handle; + Composite parent; + Cursor cursor; + Menu menu; + String toolTipText; + Object layoutData; + Accessible accessible; + Image backgroundImage; + Region region; + Font font; + int drawCount, foreground, background; + +/** + * Prevents uninitialized instances from being created outside the package. + */ +Control () { +} + +/** + * 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 (); +} + +/** + * 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); +} + +/** + * 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); +} + +/** + * 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); +} + +int /*long*/ borderHandle () { + return handle; +} + +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 checkBorder () { + if (getBorderWidth () == 0) style &= ~SWT.BORDER; +} + +void checkBuffered () { + style &= ~SWT.DOUBLE_BUFFERED; +} + +void checkComposited () { + /* Do nothing */ +} + +boolean checkHandle (int /*long*/ hwnd) { + return hwnd == handle; +} + +void checkMirrored () { + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); + if ((bits & OS.WS_EX_LAYOUTRTL) != 0) style |= SWT.MIRRORED; + } +} + +/** + * 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 (); +} + +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 (); +} + +Widget [] computeTabList () { + if (isTabGroup ()) { + if (getVisible () && getEnabled ()) { + return new Widget [] {this}; + } + } + return new Widget [0]; +} + +void createHandle () { + int /*long*/ hwndParent = widgetParent (); + handle = OS.CreateWindowEx ( + widgetExtStyle (), + windowClass (), + null, + widgetStyle (), + OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0, + hwndParent, + 0, + OS.GetModuleHandle (null), + widgetCreateStruct ()); + if (handle == 0) error (SWT.ERROR_NO_HANDLES); + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.WS_CHILD) != 0) { + OS.SetWindowLongPtr (handle, OS.GWLP_ID, handle); + } + if (OS.IsDBLocale && hwndParent != 0) { + int /*long*/ hIMC = OS.ImmGetContext (hwndParent); + OS.ImmAssociateContext (handle, hIMC); + OS.ImmReleaseContext (hwndParent, hIMC); + } +} + +void createWidget () { + state |= DRAG_DETECT; + foreground = background = -1; + checkOrientation (parent); + createHandle (); + checkBackground (); + checkBuffered (); + checkComposited (); + register (); + subclass (); + setDefaultFont (); + checkMirrored (); + checkBorder (); + if ((state & PARENT_BACKGROUND) != 0) { + setBackground (); + } +} + +int defaultBackground () { + if (OS.IsWinCE) return OS.GetSysColor (OS.COLOR_WINDOW); + return OS.GetSysColor (OS.COLOR_BTNFACE); +} + +int /*long*/ defaultFont () { + return display.getSystemFont ().handle; +} + +int defaultForeground () { + return OS.GetSysColor (OS.COLOR_WINDOWTEXT); +} + +void deregister () { + display.removeControl (handle); +} + +void destroyWidget () { + int /*long*/ hwnd = topHandle (); + releaseHandle (); + if (hwnd != 0) { + OS.DestroyWindow (hwnd); + } +} + +/** + * 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; + boolean dragging = dragDetect (handle, x, y, false, null, null); + if (OS.GetKeyState (OS.VK_LBUTTON) < 0) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + if (!dragging) { + /* + * Feature in Windows. DragDetect() captures the mouse + * and tracks its movement until the user releases the + * left mouse button, presses the ESC key, or moves the + * mouse outside the drag rectangle. If the user moves + * the mouse outside of the drag rectangle, DragDetect() + * returns true and a drag and drop operation can be + * started. When the left mouse button is released or + * the ESC key is pressed, these events are consumed by + * DragDetect() so that application code that matches + * mouse down/up pairs or looks for the ESC key will not + * function properly. The fix is to send the missing + * events when the drag has not started. + * + * NOTE: For now, don't send a fake WM_KEYDOWN/WM_KEYUP + * events for the ESC key. This would require computing + * wParam (the key) and lParam (the repeat count, scan code, + * extended-key flag, context code, previous key-state flag, + * and transition-state flag) which is non-trivial. + */ + if (button == 1 && OS.GetKeyState (OS.VK_ESCAPE) >= 0) { + int wParam = 0; + if ((stateMask & SWT.CTRL) != 0) wParam |= OS.MK_CONTROL; + if ((stateMask & SWT.SHIFT) != 0) wParam |= OS.MK_SHIFT; + if ((stateMask & SWT.ALT) != 0) wParam |= OS.MK_ALT; + if ((stateMask & SWT.BUTTON1) != 0) wParam |= OS.MK_LBUTTON; + if ((stateMask & SWT.BUTTON2) != 0) wParam |= OS.MK_MBUTTON; + if ((stateMask & SWT.BUTTON3) != 0) wParam |= OS.MK_RBUTTON; + if ((stateMask & SWT.BUTTON4) != 0) wParam |= OS.MK_XBUTTON1; + if ((stateMask & SWT.BUTTON5) != 0) wParam |= OS.MK_XBUTTON2; + int /*long*/ lParam = OS.MAKELPARAM (x, y); + OS.SendMessage (handle, OS.WM_LBUTTONUP, wParam, lParam); + } + return false; + } + return sendDragEvent (button, stateMask, x, y); +} + +void drawBackground (int /*long*/ hDC) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + drawBackground (hDC, rect); +} + +void drawBackground (int /*long*/ hDC, RECT rect) { + drawBackground (hDC, rect, -1); +} + +void drawBackground (int /*long*/ hDC, RECT rect, int pixel) { + Control control = findBackgroundControl (); + if (control != null) { + if (control.backgroundImage != null) { + fillImageBackground (hDC, control, rect); + return; + } + pixel = control.getBackgroundPixel (); + } + if (pixel == -1) { + if ((state & THEME_BACKGROUND) != 0) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + control = findThemeControl (); + if (control != null) { + fillThemeBackground (hDC, control, rect); + return; + } + } + } + } + if (pixel == -1) pixel = getBackgroundPixel (); + fillBackground (hDC, pixel, rect); +} + +void drawImageBackground (int /*long*/ hDC, int /*long*/ hwnd, int /*long*/ hBitmap, RECT rect) { + RECT rect2 = new RECT (); + OS.GetClientRect (hwnd, rect2); + OS.MapWindowPoints (hwnd, handle, rect2, 2); + int /*long*/ hBrush = findBrush (hBitmap, OS.BS_PATTERN); + POINT lpPoint = new POINT (); + OS.GetWindowOrgEx (hDC, lpPoint); + OS.SetBrushOrgEx (hDC, -rect2.left - lpPoint.x, -rect2.top - lpPoint.y, lpPoint); + int /*long*/ hOldBrush = OS.SelectObject (hDC, hBrush); + OS.PatBlt (hDC, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY); + OS.SetBrushOrgEx (hDC, lpPoint.x, lpPoint.y, null); + OS.SelectObject (hDC, hOldBrush); +} + +void drawThemeBackground (int /*long*/ hDC, int /*long*/ hwnd, RECT rect) { + /* Do nothing */ +} + +void enableDrag (boolean enabled) { + /* Do nothing */ +} + +void enableWidget (boolean enabled) { + OS.EnableWindow (handle, enabled); +} + +void fillBackground (int /*long*/ hDC, int pixel, RECT rect) { + if (rect.left > rect.right || rect.top > rect.bottom) return; + int /*long*/ hPalette = display.hPalette; + if (hPalette != 0) { + OS.SelectPalette (hDC, hPalette, false); + OS.RealizePalette (hDC); + } + OS.FillRect (hDC, rect, findBrush (pixel, OS.BS_SOLID)); +} + +void fillImageBackground (int /*long*/ hDC, Control control, RECT rect) { + if (rect.left > rect.right || rect.top > rect.bottom) return; + if (control != null) { + Image image = control.backgroundImage; + if (image != null) { + control.drawImageBackground (hDC, handle, image.handle, rect); + } + } +} + +void fillThemeBackground (int /*long*/ hDC, Control control, RECT rect) { + if (rect.left > rect.right || rect.top > rect.bottom) return; + if (control != null) { + control.drawThemeBackground (hDC, handle, rect); + } +} + +Control findBackgroundControl () { + if (background != -1 || backgroundImage != null) return this; + return (state & PARENT_BACKGROUND) != 0 ? parent.findBackgroundControl () : null; +} + +int /*long*/ findBrush (int /*long*/ value, int lbStyle) { + return parent.findBrush (value, lbStyle); +} + +Cursor findCursor () { + if (cursor != null) return cursor; + return parent.findCursor (); +} + +Control findImageControl () { + Control control = findBackgroundControl (); + return control != null && control.backgroundImage != null ? control : null; +} + +Control findThemeControl () { + return background == -1 && backgroundImage == null ? parent.findThemeControl () : null; +} + +Menu [] findMenus (Control control) { + if (menu != null && this != control) return new Menu [] {menu}; + return new Menu [0]; +} + +char findMnemonic (String string) { + int index = 0; + int length = string.length (); + do { + while (index < length && string.charAt (index) != '&') index++; + if (++index >= length) return '\0'; + if (string.charAt (index) != '&') return string.charAt (index); + index++; + } while (index < length); + return '\0'; +} + +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.setFixedFocus ()) return; + } + shell.setSavedFocus (focusControl); + OS.SetFocus (0); +} + +/** + * 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); + /* + * This code is intentionally commented. + * + * When setting focus to a control, it is + * possible that application code can set + * the focus to another control inside of + * WM_SETFOCUS. In this case, the original + * control will no longer have the focus + * and the call to setFocus() will return + * false indicating failure. + * + * We are still working on a solution at + * this time. + */ +// if (OS.GetFocus () != OS.SetFocus (handle)) return false; + OS.SetFocus (handle); + if (isDisposed ()) return false; + shell.setSavedFocus (this); + return isFocusControl (); +} + +void forceResize () { + if (parent == null) return; + WINDOWPOS [] lpwp = parent.lpwp; + if (lpwp == null) return; + for (int i=0; i<lpwp.length; i++) { + WINDOWPOS wp = lpwp [i]; + if (wp != null && wp.hwnd == handle) { + /* + * This code is intentionally commented. All widgets that + * are created by SWT have WS_CLIPSIBLINGS to ensure that + * application code does not draw outside of the control. + */ +// int count = parent.getChildrenCount (); +// if (count > 1) { +// int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); +// if ((bits & OS.WS_CLIPSIBLINGS) == 0) wp.flags |= OS.SWP_NOCOPYBITS; +// } + SetWindowPos (wp.hwnd, 0, wp.x, wp.y, wp.cx, wp.cy, wp.flags); + lpwp [i] = null; + return; + } + } +} + +/** + * 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 Color.win32_new (display, control.getBackgroundPixel ()); +} + +/** + * 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; +} + +int getBackgroundPixel () { + return background != -1 ? background : defaultBackground (); +} + +/** + * 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 (); + int /*long*/ borderHandle = borderHandle (); + int bits1 = OS.GetWindowLong (borderHandle, OS.GWL_EXSTYLE); + if ((bits1 & OS.WS_EX_CLIENTEDGE) != 0) return OS.GetSystemMetrics (OS.SM_CXEDGE); + if ((bits1 & OS.WS_EX_STATICEDGE) != 0) return OS.GetSystemMetrics (OS.SM_CXBORDER); + int bits2 = OS.GetWindowLong (borderHandle, OS.GWL_STYLE); + if ((bits2 & OS.WS_BORDER) != 0) return OS.GetSystemMetrics (OS.SM_CXBORDER); + 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 (); + forceResize (); + RECT rect = new RECT (); + OS.GetWindowRect (topHandle (), rect); + int /*long*/ hwndParent = parent == null ? 0 : parent.handle; + OS.MapWindowPoints (0, hwndParent, rect, 2); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + return new Rectangle (rect.left, rect.top, width, height); +} + +int getCodePage () { + if (OS.IsUnicode) return OS.CP_ACP; + int /*long*/ hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + LOGFONT logFont = OS.IsUnicode ? (LOGFONT) new LOGFONTW () : new LOGFONTA (); + OS.GetObject (hFont, LOGFONT.sizeof, logFont); + int cs = logFont.lfCharSet & 0xFF; + int [] lpCs = new int [8]; + if (OS.TranslateCharsetInfo (cs, lpCs, OS.TCI_SRCCHARSET)) { + return lpCs [1]; + } + return OS.GetACP (); +} + +String getClipboardText () { + String string = ""; + if (OS.OpenClipboard (0)) { + int /*long*/ hMem = OS.GetClipboardData (OS.IsUnicode ? OS.CF_UNICODETEXT : OS.CF_TEXT); + if (hMem != 0) { + /* Ensure byteCount is a multiple of 2 bytes on UNICODE platforms */ + int byteCount = OS.GlobalSize (hMem) / TCHAR.sizeof * TCHAR.sizeof; + int /*long*/ ptr = OS.GlobalLock (hMem); + if (ptr != 0) { + /* Use the character encoding for the default locale */ + TCHAR buffer = new TCHAR (0, byteCount / TCHAR.sizeof); + OS.MoveMemory (buffer, ptr, byteCount); + string = buffer.toString (0, buffer.strlen ()); + OS.GlobalUnlock (hMem); + } + } + OS.CloseClipboard (); + } + return string; +} + +/** + * 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 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 <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 OS.IsWindowEnabled (handle); +} + +/** + * 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; + int /*long*/ hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (hFont == 0) hFont = defaultFont (); + return Font.win32_new (display, hFont); +} + +/** + * 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 Color.win32_new (display, getForegroundPixel ()); +} + +int getForegroundPixel () { + return foreground != -1 ? 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 (); + forceResize (); + RECT rect = new RECT (); + OS.GetWindowRect (topHandle (), rect); + int /*long*/ hwndParent = parent == null ? 0 : parent.handle; + OS.MapWindowPoints (0, hwndParent, rect, 2); + return new Point (rect.left, rect.top); +} + +/** + * 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; +} + +/** + * Returns the receiver's monitor. + * + * @return the receiver's monitor + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - 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 Monitor getMonitor () { + checkWidget (); + if (OS.IsWinCE || OS.WIN32_VERSION < OS.VERSION (4, 10)) { + return display.getPrimaryMonitor (); + } + int /*long*/ hmonitor = OS.MonitorFromWindow (handle, OS.MONITOR_DEFAULTTONEAREST); + MONITORINFO lpmi = new MONITORINFO (); + lpmi.cbSize = MONITORINFO.sizeof; + OS.GetMonitorInfo (hmonitor, lpmi); + Monitor monitor = new Monitor (); + monitor.handle = hmonitor; + monitor.x = lpmi.rcMonitor_left; + monitor.y = lpmi.rcMonitor_top; + monitor.width = lpmi.rcMonitor_right - lpmi.rcMonitor_left; + monitor.height = lpmi.rcMonitor_bottom - lpmi.rcMonitor_top; + monitor.clientX = lpmi.rcWork_left; + monitor.clientY = lpmi.rcWork_top; + monitor.clientWidth = lpmi.rcWork_right - lpmi.rcWork_left; + monitor.clientHeight = lpmi.rcWork_bottom - lpmi.rcWork_top; + return monitor; +} + +/** + * 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; +} + +/** + * 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 (); + forceResize (); + RECT rect = new RECT (); + OS.GetWindowRect (topHandle (), rect); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + return new Point (width, height); +} + +/** + * 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 (); + if (!getDrawing()) return (state & HIDDEN) == 0; + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + return (bits & OS.WS_VISIBLE) != 0; +} + +boolean hasCursor () { + RECT rect = new RECT (); + if (!OS.GetClientRect (handle, rect)) return false; + OS.MapWindowPoints (handle, 0, rect, 2); + POINT pt = new POINT (); + return OS.GetCursorPos (pt) && OS.PtInRect (rect, pt); +} + +boolean hasFocus () { + /* + * If a non-SWT child of the control has focus, + * then this control is considered to have focus + * even though it does not have focus in Windows. + */ + int /*long*/ hwndFocus = OS.GetFocus (); + while (hwndFocus != 0) { + if (hwndFocus == handle) return true; + if (display.getControl (hwndFocus) != null) { + return false; + } + hwndFocus = OS.GetParent (hwndFocus); + } + return false; +} + +/** + * 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(); + int /*long*/ hwnd = handle; + if (data != null && data.hwnd != 0) hwnd = data.hwnd; + if (data != null) data.hwnd = hwnd; + int /*long*/ hDC = 0; + if (data == null || data.ps == null) { + hDC = OS.GetDC (hwnd); + } else { + hDC = OS.BeginPaint (hwnd, data.ps); + } + if (hDC == 0) SWT.error(SWT.ERROR_NO_HANDLES); + if (data != null) { + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) { + int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT; + if ((data.style & mask) != 0) { + data.layout = (data.style & SWT.RIGHT_TO_LEFT) != 0 ? OS.LAYOUT_RTL : 0; + } else { + int flags = OS.GetLayout (hDC); + if ((flags & OS.LAYOUT_RTL) != 0) { + data.style |= SWT.RIGHT_TO_LEFT | SWT.MIRRORED; + } else { + data.style |= SWT.LEFT_TO_RIGHT; + } + } + } else { + data.style |= SWT.LEFT_TO_RIGHT; + } + data.device = display; + int foreground = getForegroundPixel (); + if (foreground != OS.GetTextColor (hDC)) data.foreground = foreground; + Control control = findBackgroundControl (); + if (control == null) control = this; + int background = control.getBackgroundPixel (); + if (background != OS.GetBkColor (hDC)) data.background = background; + data.font = font != null ? font : Font.win32_new (display, OS.SendMessage (hwnd, OS.WM_GETFONT, 0, 0)); + data.uiState = (int)/*64*/OS.SendMessage (hwnd, OS.WM_QUERYUISTATE, 0, 0); + } + return hDC; +} + +/** + * 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*/ hDC, GCData data) { + checkWidget (); + int /*long*/ hwnd = handle; + if (data != null && data.hwnd != 0) { + hwnd = data.hwnd; + } + if (data == null || data.ps == null) { + OS.ReleaseDC (hwnd, hDC); + } else { + OS.EndPaint (hwnd, data.ps); + } +} + +boolean isActive () { + Dialog dialog = display.getModalDialog (); + if (dialog != null) { + Shell dialogShell = dialog.parent; + if (dialogShell != null && !dialogShell.isDisposed ()) { + if (dialogShell != getShell ()) return false; + } + } + 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 false; + break; + } + if ((modal.style & SWT.PRIMARY_MODAL) != 0) { + if (shell == null) shell = getShell (); + if (modal.parent == shell) return false; + } + } + } + } + if (shell == null) shell = getShell (); + return shell.getEnabled (); +} + +/** + * 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 (); +} + +/** + * 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 isFocusAncestor (Control control) { + while (control != null && control != this && !(control instanceof Shell)) { + control = control.parent; + } + return control == this; +} + +/** + * 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 obscured 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; + /* + * Check to see if current damage is included. + */ +// if (!OS.IsWindowVisible (handle)) return false; +// int flags = OS.DCX_CACHE | OS.DCX_CLIPCHILDREN | OS.DCX_CLIPSIBLINGS; +// int /*long*/ hDC = OS.GetDCEx (handle, 0, flags); +// int result = OS.GetClipBox (hDC, new RECT ()); +// OS.ReleaseDC (handle, hDC); +// return result != OS.NULLREGION; +} + +boolean isTabGroup () { + Control [] tabList = parent._getTabList (); + if (tabList != null) { + for (int i=0; i<tabList.length; i++) { + if (tabList [i] == this) return true; + } + } + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + return (bits & OS.WS_TABSTOP) != 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 bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.WS_TABSTOP) != 0) return false; + int /*long*/ code = OS.SendMessage (handle, OS.WM_GETDLGCODE, 0, 0); + if ((code & OS.DLGC_STATIC) != 0) return false; + if ((code & OS.DLGC_WANTALLKEYS) != 0) return false; + if ((code & OS.DLGC_WANTARROWS) != 0) return false; + if ((code & OS.DLGC_WANTTAB) != 0) return false; + return true; +} + +/** + * 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 (); + if (OS.IsWindowVisible (handle)) return true; + return getVisible () && parent.isVisible (); +} + +void mapEvent (int /*long*/ hwnd, Event event) { + if (hwnd != handle) { + POINT point = new POINT (); + point.x = event.x; + point.y = event.y; + OS.MapWindowPoints (hwnd, handle, point, 1); + event.x = point.x; + event.y = point.y; + } +} + +void markLayout (boolean changed, boolean all) { + /* Do nothing */ +} + +Decorations menuShell () { + return parent.menuShell (); +} + +boolean mnemonicHit (char key) { + return false; +} + +boolean mnemonicMatch (char key) { + return false; +} + +/** + * 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 (); + int /*long*/ topHandle = topHandle (), hwndAbove = OS.HWND_TOP; + if (control != null) { + if (control.isDisposed ()) error(SWT.ERROR_INVALID_ARGUMENT); + if (parent != control.parent) return; + int /*long*/ hwnd = control.topHandle (); + if (hwnd == 0 || hwnd == topHandle) return; + hwndAbove = OS.GetWindow (hwnd, OS.GW_HWNDPREV); + /* + * Bug in Windows. For some reason, when GetWindow () + * with GW_HWNDPREV is used to query the previous window + * in the z-order with the first child, Windows returns + * the first child instead of NULL. The fix is to detect + * this case and move the control to the top. + */ + if (hwndAbove == 0 || hwndAbove == hwnd) { + hwndAbove = OS.HWND_TOP; + } + } + int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE; + SetWindowPos (topHandle, hwndAbove, 0, 0, 0, 0, flags); +} + +/** + * 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 (); + int /*long*/ topHandle = topHandle (), hwndAbove = OS.HWND_BOTTOM; + if (control != null) { + if (control.isDisposed ()) error(SWT.ERROR_INVALID_ARGUMENT); + if (parent != control.parent) return; + hwndAbove = control.topHandle (); + } else { + /* + * Bug in Windows. When SetWindowPos() is called + * with HWND_BOTTOM on a dialog shell, the dialog + * and the parent are moved to the bottom of the + * desktop stack. The fix is to move the dialog + * to the bottom of the dialog window stack by + * moving behind the first dialog child. + */ + Shell shell = getShell (); + if (this == shell && parent != null) { + /* + * Bug in Windows. For some reason, when GetWindow () + * with GW_HWNDPREV is used to query the previous window + * in the z-order with the first child, Windows returns + * the first child instead of NULL. The fix is to detect + * this case and do nothing because the control is already + * at the bottom. + */ + int /*long*/ hwndParent = parent.handle, hwnd = hwndParent; + hwndAbove = OS.GetWindow (hwnd, OS.GW_HWNDPREV); + while (hwndAbove != 0 && hwndAbove != hwnd) { + if (OS.GetWindow (hwndAbove, OS.GW_OWNER) == hwndParent) break; + hwndAbove = OS.GetWindow (hwnd = hwndAbove, OS.GW_HWNDPREV); + } + if (hwndAbove == hwnd) return; + } + } + if (hwndAbove == 0 || hwndAbove == topHandle) return; + int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE; + SetWindowPos (topHandle, hwndAbove, 0, 0, 0, 0, flags); +} + +Accessible new_Accessible (Control control) { + return Accessible.internal_new_Accessible (this); +} + +GC new_GC (GCData data) { + return GC.win32_new (this, data); +} + +/** + * 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)); +} + +/** + * 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); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) { + int /*long*/ topHandle = topHandle (); + int /*long*/ hdc = gc.handle; + int state = 0; + int /*long*/ gdipGraphics = gc.getGCData().gdipGraphics; + if (gdipGraphics != 0) { + int /*long*/ clipRgn = 0; + Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone); + int /*long*/ rgn = Gdip.Region_new(); + if (rgn == 0) SWT.error(SWT.ERROR_NO_HANDLES); + Gdip.Graphics_GetClip(gdipGraphics, rgn); + if (!Gdip.Region_IsInfinite(rgn, gdipGraphics)) { + clipRgn = Gdip.Region_GetHRGN(rgn, gdipGraphics); + } + Gdip.Region_delete(rgn); + Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf); + float[] lpXform = null; + int /*long*/ matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0); + if (matrix == 0) SWT.error(SWT.ERROR_NO_HANDLES); + Gdip.Graphics_GetTransform(gdipGraphics, matrix); + if (!Gdip.Matrix_IsIdentity(matrix)) { + lpXform = new float[6]; + Gdip.Matrix_GetElements(matrix, lpXform); + } + Gdip.Matrix_delete(matrix); + hdc = Gdip.Graphics_GetHDC(gdipGraphics); + state = OS.SaveDC(hdc); + if (lpXform != null) { + OS.SetGraphicsMode(hdc, OS.GM_ADVANCED); + OS.SetWorldTransform(hdc, lpXform); + } + if (clipRgn != 0) { + OS.SelectClipRgn(hdc, clipRgn); + OS.DeleteObject(clipRgn); + } + } + if (OS.IsWinCE) { + OS.UpdateWindow (topHandle); + } else { + int flags = OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (topHandle, null, 0, flags); + } + printWidget (topHandle, hdc, gc); + if (gdipGraphics != 0) { + OS.RestoreDC(hdc, state); + Gdip.Graphics_ReleaseHDC(gdipGraphics, hdc); + } + return true; + } + return false; +} + +void printWidget (int /*long*/ hwnd, int /*long*/ hdc, GC gc) { + /* + * Bug in Windows. For some reason, PrintWindow() + * returns success but does nothing when it is called + * on a printer. The fix is to just go directly to + * WM_PRINT in this case. + */ + boolean success = false; + if (!(OS.GetDeviceCaps(gc.handle, OS.TECHNOLOGY) == OS.DT_RASPRINTER)) { + /* + * Bug in Windows. When PrintWindow() will only draw that + * portion of a control that is not obscured by the shell. + * The fix is temporarily reparent the window to the desktop, + * call PrintWindow() then reparent the window back. + */ + int /*long*/ hwndParent = OS.GetParent (hwnd); + int /*long*/ hwndShell = hwndParent; + while (OS.GetParent (hwndShell) != 0) { + if (OS.GetWindow (hwndShell, OS.GW_OWNER) != 0) break; + hwndShell = OS.GetParent (hwndShell); + } + RECT rect1 = new RECT (); + OS.GetWindowRect (hwnd, rect1); + boolean fixPrintWindow = !OS.IsWindowVisible(hwnd); + if (!fixPrintWindow) { + RECT rect2 = new RECT (); + OS.GetWindowRect (hwndShell, rect2); + OS.IntersectRect (rect2, rect1, rect2); + fixPrintWindow = !OS.EqualRect (rect2, rect1); + } + /* + * Bug in Windows. PrintWindow() does not print portions + * of the receiver that are clipped out using SetWindowRgn() + * in a parent. The fix is temporarily reparent the window + * to the desktop, call PrintWindow() then reparent the window + * back. + */ + if (!fixPrintWindow) { + int /*long*/ rgn = OS.CreateRectRgn(0, 0, 0, 0); + int /*long*/ parent = OS.GetParent(hwnd); + while (parent != hwndShell && !fixPrintWindow) { + if (OS.GetWindowRgn(parent, rgn) != 0) { + fixPrintWindow = true; + } + parent = OS.GetParent(parent); + } + OS.DeleteObject(rgn); + } + int bits = OS.GetWindowLong (hwnd, OS.GWL_STYLE); + int /*long*/ hwndInsertAfter = OS.GetWindow (hwnd, OS.GW_HWNDPREV); + /* + * Bug in Windows. For some reason, when GetWindow () + * with GW_HWNDPREV is used to query the previous window + * in the z-order with the first child, Windows returns + * the first child instead of NULL. The fix is to detect + * this case and move the control to the top. + */ + if (hwndInsertAfter == 0 || hwndInsertAfter == hwnd) { + hwndInsertAfter = OS.HWND_TOP; + } + if (fixPrintWindow) { + int x = OS.GetSystemMetrics (OS.SM_XVIRTUALSCREEN); + int y = OS.GetSystemMetrics (OS.SM_YVIRTUALSCREEN); + int width = OS.GetSystemMetrics (OS.SM_CXVIRTUALSCREEN); + int height = OS.GetSystemMetrics (OS.SM_CYVIRTUALSCREEN); + int flags = OS.SWP_NOSIZE | OS.SWP_NOZORDER | OS.SWP_NOACTIVATE | OS.SWP_DRAWFRAME; + if ((bits & OS.WS_VISIBLE) != 0) { + OS.DefWindowProc (hwnd, OS.WM_SETREDRAW, 0, 0); + } + SetWindowPos (hwnd, 0, x + width, y + height, 0, 0, flags); + OS.SetParent (hwnd, 0); + if ((bits & OS.WS_VISIBLE) != 0) { + OS.DefWindowProc (hwnd, OS.WM_SETREDRAW, 1, 0); + } + } + if ((bits & OS.WS_VISIBLE) == 0) { + OS.DefWindowProc (hwnd, OS.WM_SETREDRAW, 1, 0); + } + success = OS.PrintWindow (hwnd, hdc, 0); + if ((bits & OS.WS_VISIBLE) == 0) { + OS.DefWindowProc (hwnd, OS.WM_SETREDRAW, 0, 0); + } + if (fixPrintWindow) { + if ((bits & OS.WS_VISIBLE) != 0) { + OS.DefWindowProc (hwnd, OS.WM_SETREDRAW, 0, 0); + } + OS.SetParent (hwnd, hwndParent); + OS.MapWindowPoints (0, hwndParent, rect1, 2); + int flags = OS.SWP_NOSIZE | OS.SWP_NOACTIVATE | OS.SWP_DRAWFRAME; + SetWindowPos (hwnd, hwndInsertAfter, rect1.left, rect1.top, rect1.right - rect1.left, rect1.bottom - rect1.top, flags); + if ((bits & OS.WS_VISIBLE) != 0) { + OS.DefWindowProc (hwnd, OS.WM_SETREDRAW, 1, 0); + } + } + } + + /* + * Bug in Windows. For some reason, PrintWindow() fails + * when it is called on a push button. The fix is to + * detect the failure and use WM_PRINT instead. Note + * that WM_PRINT cannot be used all the time because it + * fails for browser controls when the browser has focus. + */ + if (!success) { + int flags = OS.PRF_CLIENT | OS.PRF_NONCLIENT | OS.PRF_ERASEBKGND | OS.PRF_CHILDREN; + OS.SendMessage (hwnd, OS.WM_PRINT, hdc, flags); + } +} + +/** + * 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 (); + redraw (false); +} + +void redraw (boolean all) { +// checkWidget (); + if (!OS.IsWindowVisible (handle)) return; + if (OS.IsWinCE) { + OS.InvalidateRect (handle, null, true); + } else { + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE; + if (all) flags |= OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, null, 0, flags); + } +} +/** + * 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 (); + if (width <= 0 || height <= 0) return; + if (!OS.IsWindowVisible (handle)) return; + RECT rect = new RECT (); + OS.SetRect (rect, x, y, x + width, y + height); + if (OS.IsWinCE) { + OS.InvalidateRect (handle, rect, true); + } else { + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE; + if (all) flags |= OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, rect, 0, flags); + } +} + +boolean redrawChildren () { + if (!OS.IsWindowVisible (handle)) return false; + Control control = findBackgroundControl (); + if (control == null) { + if ((state & THEME_BACKGROUND) != 0) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + OS.InvalidateRect (handle, null, true); + return true; + } + } + } else { + if (control.backgroundImage != null) { + OS.InvalidateRect (handle, null, true); + return true; + } + } + return false; +} + +void register () { + display.addControl (handle, this); +} + +void releaseHandle () { + super.releaseHandle (); + handle = 0; + parent = null; +} + +void releaseParent () { + parent.removeControl (this); +} + +void releaseWidget () { + super.releaseWidget (); + if (OS.IsDBLocale) { + OS.ImmAssociateContext (handle, 0); + } + if (toolTipText != null) { + setToolTipText (getShell (), null); + } + toolTipText = null; + if (menu != null && !menu.isDisposed ()) { + menu.dispose (); + } + backgroundImage = null; + menu = null; + cursor = null; + unsubclass (); + deregister (); + layoutData = null; + if (accessible != null) { + accessible.internal_dispose_Accessible (); + } + accessible = null; + region = null; + font = 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 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 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 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); +} + +/** + * 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 showWidget (boolean visible) { + int /*long*/ topHandle = topHandle (); + OS.ShowWindow (topHandle, visible ? OS.SW_SHOW : OS.SW_HIDE); + if (handle != topHandle) OS.ShowWindow (handle, visible ? OS.SW_SHOW : OS.SW_HIDE); +} + +boolean sendFocusEvent (int type) { + Shell shell = getShell (); + + /* + * Feature in Windows. During the processing of WM_KILLFOCUS, + * when the focus window is queried using GetFocus(), it has + * already been assigned to the new window. The fix is to + * remember the control that is losing or gaining focus and + * answer it during WM_KILLFOCUS. If a WM_SETFOCUS occurs + * during WM_KILLFOCUS, the focus control needs to be updated + * to the current control. At any other time, the focus + * control matches Windows. + */ + Display display = this.display; + 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; + } + } + return true; +} + +void sendMove () { + sendEvent (SWT.Move); +} + +void sendResize () { + sendEvent (SWT.Resize); +} + +void setBackground () { + Control control = findBackgroundControl (); + if (control == null) control = this; + if (control.backgroundImage != null) { + Shell shell = getShell (); + shell.releaseBrushes (); + setBackgroundImage (control.backgroundImage.handle); + } else { + setBackgroundPixel (control.background == -1 ? control.defaultBackground() : control.background); + } +} + +/** + * 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 (); + int pixel = -1; + if (color != null) { + if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + pixel = color.handle; + } + if (pixel == background) return; + background = pixel; + updateBackgroundColor (); +} + +/** + * 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) { + if (image.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + if (image.type != SWT.BITMAP) error (SWT.ERROR_INVALID_ARGUMENT); + } + if (backgroundImage == image) return; + backgroundImage = image; + Shell shell = getShell (); + shell.releaseBrushes (); + updateBackgroundImage (); +} + +void setBackgroundImage (int /*long*/ hBitmap) { + if (OS.IsWinCE) { + OS.InvalidateRect (handle, null, true); + } else { + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE; + OS.RedrawWindow (handle, null, 0, flags); + } +} + +void setBackgroundPixel (int pixel) { + if (OS.IsWinCE) { + OS.InvalidateRect (handle, null, true); + } else { + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE; + OS.RedrawWindow (handle, null, 0, flags); + } +} + +/** + * 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 (); + int flags = OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE; + setBounds (x, y, Math.max (0, width), Math.max (0, height), flags); +} + +void setBounds (int x, int y, int width, int height, int flags) { + setBounds (x, y, width, height, flags, true); +} + +void setBounds (int x, int y, int width, int height, int flags, boolean defer) { + if (findImageControl () != null) { + if (backgroundImage == null) flags |= OS.SWP_NOCOPYBITS; + } else { + if (OS.GetWindow (handle, OS.GW_CHILD) == 0) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if (findThemeControl () != null) flags |= OS.SWP_NOCOPYBITS; + } + } + } + int /*long*/ topHandle = topHandle (); + if (defer && parent != null) { + forceResize (); + if (parent.lpwp != null) { + int index = 0; + WINDOWPOS [] lpwp = parent.lpwp; + while (index < lpwp.length) { + if (lpwp [index] == null) break; + index ++; + } + if (index == lpwp.length) { + WINDOWPOS [] newLpwp = new WINDOWPOS [lpwp.length + 4]; + System.arraycopy (lpwp, 0, newLpwp, 0, lpwp.length); + parent.lpwp = lpwp = newLpwp; + } + WINDOWPOS wp = new WINDOWPOS (); + wp.hwnd = topHandle; + wp.x = x; + wp.y = y; + wp.cx = width; + wp.cy = height; + wp.flags = flags; + lpwp [index] = wp; + return; + } + } + SetWindowPos (topHandle, 0, x, y, width, height, flags); +} + +/** + * 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, rect.width, rect.height); +} + +/** + * 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 (); + if (capture) { + OS.SetCapture (handle); + } else { + if (OS.GetCapture () == handle) { + OS.ReleaseCapture (); + } + } +} + +void setCursor () { + int /*long*/ lParam = OS.MAKELPARAM (OS.HTCLIENT, OS.WM_MOUSEMOVE); + OS.SendMessage (handle, OS.WM_SETCURSOR, handle, lParam); +} + +/** + * 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()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + this.cursor = cursor; + if (OS.IsWinCE) { + int /*long*/ hCursor = cursor != null ? cursor.handle : 0; + OS.SetCursor (hCursor); + return; + } + int /*long*/ hwndCursor = OS.GetCapture (); + if (hwndCursor == 0) { + POINT pt = new POINT (); + if (!OS.GetCursorPos (pt)) return; + int /*long*/ hwnd = hwndCursor = OS.WindowFromPoint (pt); + while (hwnd != 0 && hwnd != handle) { + hwnd = OS.GetParent (hwnd); + } + if (hwnd == 0) return; + } + Control control = display.getControl (hwndCursor); + if (control == null) control = this; + control.setCursor (); +} + +void setDefaultFont () { + int /*long*/ hFont = display.getSystemFont ().handle; + OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0); +} + +/** + * 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; + } + enableDrag (dragDetect); +} + +/** + * 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 (); + /* + * Feature in Windows. If the receiver has focus, disabling + * the receiver causes no window to have focus. The fix is + * to assign focus to the first ancestor window that takes + * focus. If no window will take focus, set focus to the + * desktop. + */ + Control control = null; + boolean fixFocus = false; + if (!enabled) { + if (display.focusEvent != SWT.FocusOut) { + control = display.getFocusControl (); + fixFocus = isFocusAncestor (control); + } + } + enableWidget (enabled); + if (fixFocus) fixFocus (control); +} + +boolean setFixedFocus () { + if ((style & SWT.NO_FOCUS) != 0) return false; + return forceFocus (); +} + +/** + * 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 (); + int /*long*/ hFont = 0; + if (font != null) { + if (font.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + hFont = font.handle; + } + this.font = font; + if (hFont == 0) hFont = defaultFont (); + OS.SendMessage (handle, OS.WM_SETFONT, hFont, 1); +} + +/** + * 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 (); + int pixel = -1; + if (color != null) { + if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + pixel = color.handle; + } + if (pixel == foreground) return; + foreground = pixel; + setForegroundPixel (pixel); +} + +void setForegroundPixel (int pixel) { + OS.InvalidateRect (handle, null, true); +} + +/** + * 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 (); + int flags = OS.SWP_NOSIZE | OS.SWP_NOZORDER | OS.SWP_NOACTIVATE; + /* + * Feature in WinCE. The SWP_DRAWFRAME flag for SetWindowPos() + * causes a WM_SIZE message to be sent even when the SWP_NOSIZE + * flag is specified. The fix is to set SWP_DRAWFRAME only when + * not running on WinCE. + */ + if (!OS.IsWinCE) flags |= OS.SWP_DRAWFRAME; + setBounds (x, y, 0, 0, flags); +} + +/** + * 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); + setLocation (location.x, location.y); +} + +/** + * 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; +} + +boolean setRadioFocus (boolean tabbing) { + return false; +} + +boolean setRadioSelection (boolean value) { + return false; +} + +/** + * 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 (); + /* + * Feature in Windows. When WM_SETREDRAW is used to turn + * off drawing in a widget, it clears the WS_VISIBLE bits + * and then sets them when redraw is turned back on. This + * means that WM_SETREDRAW will make a widget unexpectedly + * visible. The fix is to track the visibility state while + * drawing is turned off and restore it when drawing is + * turned back on. + */ + if (drawCount == 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.WS_VISIBLE) == 0) state |= HIDDEN; + } + if (redraw) { + if (--drawCount == 0) { + int /*long*/ topHandle = topHandle (); + OS.SendMessage (topHandle, OS.WM_SETREDRAW, 1, 0); + if (handle != topHandle) OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0); + if ((state & HIDDEN) != 0) { + state &= ~HIDDEN; + OS.ShowWindow (topHandle, OS.SW_HIDE); + if (handle != topHandle) OS.ShowWindow (handle, OS.SW_HIDE); + } else { + if (OS.IsWinCE) { + OS.InvalidateRect (topHandle, null, true); + if (handle != topHandle) OS.InvalidateRect (handle, null, true); + } else { + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (topHandle, null, 0, flags); + } + } + } + } else { + if (drawCount++ == 0) { + int /*long*/ topHandle = topHandle (); + OS.SendMessage (topHandle, OS.WM_SETREDRAW, 0, 0); + if (handle != topHandle) OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); + } + } +} + +/** + * 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); + int /*long*/ hRegion = 0; + if (region != null) { + hRegion = OS.CreateRectRgn (0, 0, 0, 0); + OS.CombineRgn (hRegion, region.handle, hRegion, OS.RGN_OR); + } + OS.SetWindowRgn (handle, hRegion, true); + this.region = region; +} + +boolean setSavedFocus () { + return forceFocus (); +} + +/** + * 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 (); + int flags = OS.SWP_NOMOVE | OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE; + setBounds (0, 0, Math.max (0, width), Math.max (0, height), flags); +} + +/** + * 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); + setSize (size.x, size.y); +} + +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; + setToolTipText (getShell (), string); +} + +void setToolTipText (Shell shell, String string) { + shell.setToolTipText (handle, string); +} + +/** + * 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 (!getDrawing()) { + if (((state & HIDDEN) == 0) == visible) return; + } else { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if (((bits & OS.WS_VISIBLE) != 0) == visible) return; + } + if (visible) { + sendEvent (SWT.Show); + if (isDisposed ()) return; + } + + /* + * Feature in Windows. If the receiver has focus, hiding + * the receiver causes no window to have focus. The fix is + * to assign focus to the first ancestor window that takes + * focus. If no window will take focus, set focus to the + * desktop. + */ + Control control = null; + boolean fixFocus = false; + if (!visible) { + if (display.focusEvent != SWT.FocusOut) { + control = display.getFocusControl (); + fixFocus = isFocusAncestor (control); + } + } + if (!getDrawing()) { + state = visible ? state & ~HIDDEN : state | HIDDEN; + } else { + showWidget (visible); + if (isDisposed ()) return; + } + if (!visible) { + sendEvent (SWT.Hide); + if (isDisposed ()) return; + } + if (fixFocus) fixFocus (control); +} + +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; + } + } + } + } +} + +void subclass () { + int /*long*/ oldProc = windowProc (); + int /*long*/ newProc = display.windowProc; + if (oldProc == newProc) return; + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, newProc); +} + +/** + * 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 (); + POINT pt = new POINT (); + pt.x = x; pt.y = y; + OS.ScreenToClient (handle, pt); + return new Point (pt.x, pt.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 (); + POINT pt = new POINT (); + pt.x = x; pt.y = y; + OS.ClientToScreen (handle, pt); + return new Point (pt.x, pt.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); +} + +int /*long*/ topHandle () { + return handle; +} + +boolean translateAccelerator (MSG msg) { + return menuShell ().translateAccelerator (msg); +} + +boolean translateMnemonic (Event event, Control control) { + if (control == this) return false; + if (!isVisible () || !isEnabled ()) return false; + event.doit = mnemonicMatch (event.character); + return traverse (event); +} + +boolean translateMnemonic (MSG msg) { + if (msg.wParam < 0x20) return false; + int /*long*/ hwnd = msg.hwnd; + if (OS.GetKeyState (OS.VK_MENU) >= 0) { + int /*long*/ code = OS.SendMessage (hwnd, OS.WM_GETDLGCODE, 0, 0); + if ((code & OS.DLGC_WANTALLKEYS) != 0) return false; + if ((code & OS.DLGC_BUTTON) == 0) return false; + } + Decorations shell = menuShell (); + if (shell.isVisible () && shell.isEnabled ()) { + display.lastAscii = (int)/*64*/msg.wParam; + display.lastNull = display.lastDead = false; + Event event = new Event (); + event.detail = SWT.TRAVERSE_MNEMONIC; + if (setKeyState (event, SWT.Traverse, msg.wParam, msg.lParam)) { + return translateMnemonic (event, null) || shell.translateMnemonic (event, this); + } + } + return false; +} + +boolean translateTraversal (MSG msg) { + int /*long*/ hwnd = msg.hwnd; + int key = (int)/*64*/msg.wParam; + if (key == OS.VK_MENU) { + OS.SendMessage (hwnd, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0); + return false; + } + int detail = SWT.TRAVERSE_NONE; + boolean doit = true, all = false; + boolean lastVirtual = false; + int lastKey = key, lastAscii = 0; + switch (key) { + case OS.VK_ESCAPE: { + all = true; + lastAscii = 27; + int /*long*/ code = OS.SendMessage (hwnd, OS.WM_GETDLGCODE, 0, 0); + if ((code & OS.DLGC_WANTALLKEYS) != 0) { + /* + * Use DLGC_HASSETSEL to determine that the control + * is a text widget. A text widget normally wants + * all keys except VK_ESCAPE. If this bit is not + * set, then assume the control wants all keys, + * including VK_ESCAPE. + */ + if ((code & OS.DLGC_HASSETSEL) == 0) doit = false; + } + detail = SWT.TRAVERSE_ESCAPE; + break; + } + case OS.VK_RETURN: { + all = true; + lastAscii = '\r'; + int /*long*/ code = OS.SendMessage (hwnd, OS.WM_GETDLGCODE, 0, 0); + if ((code & OS.DLGC_WANTALLKEYS) != 0) doit = false; + detail = SWT.TRAVERSE_RETURN; + break; + } + case OS.VK_TAB: { + lastAscii = '\t'; + boolean next = OS.GetKeyState (OS.VK_SHIFT) >= 0; + int /*long*/ code = OS.SendMessage (hwnd, OS.WM_GETDLGCODE, 0, 0); + if ((code & (OS.DLGC_WANTTAB | OS.DLGC_WANTALLKEYS)) != 0) { + /* + * Use DLGC_HASSETSEL to determine that the control is a + * text widget. If the control is a text widget, then + * Ctrl+Tab and Shift+Tab should traverse out of the widget. + * If the control is not a text widget, the correct behavior + * is to give every character, including Tab, Ctrl+Tab and + * Shift+Tab to the control. + */ + if ((code & OS.DLGC_HASSETSEL) != 0) { + if (next && OS.GetKeyState (OS.VK_CONTROL) >= 0) { + doit = false; + } + } else { + doit = false; + } + } + detail = next ? SWT.TRAVERSE_TAB_NEXT : SWT.TRAVERSE_TAB_PREVIOUS; + break; + } + case OS.VK_UP: + case OS.VK_LEFT: + case OS.VK_DOWN: + case OS.VK_RIGHT: { + /* + * On WinCE SP there is no tab key. Focus is assigned + * using the VK_UP and VK_DOWN keys, not with VK_LEFT + * or VK_RIGHT. + */ + if (OS.IsSP) { + if (key == OS.VK_LEFT || key == OS.VK_RIGHT) return false; + } + lastVirtual = true; + int /*long*/ code = OS.SendMessage (hwnd, OS.WM_GETDLGCODE, 0, 0); + if ((code & (OS.DLGC_WANTARROWS /*| OS.DLGC_WANTALLKEYS*/)) != 0) doit = false; + boolean next = key == OS.VK_DOWN || key == OS.VK_RIGHT; + if (parent != null && (parent.style & SWT.MIRRORED) != 0) { + if (key == OS.VK_LEFT || key == OS.VK_RIGHT) next = !next; + } + detail = next ? SWT.TRAVERSE_ARROW_NEXT : SWT.TRAVERSE_ARROW_PREVIOUS; + break; + } + case OS.VK_PRIOR: + case OS.VK_NEXT: { + all = true; + lastVirtual = true; + if (OS.GetKeyState (OS.VK_CONTROL) >= 0) return false; + int /*long*/ code = OS.SendMessage (hwnd, OS.WM_GETDLGCODE, 0, 0); + if ((code & OS.DLGC_WANTALLKEYS) != 0) { + /* + * Use DLGC_HASSETSEL to determine that the control is a + * text widget. If the control is a text widget, then + * Ctrl+PgUp and Ctrl+PgDn should traverse out of the widget. + */ + if ((code & OS.DLGC_HASSETSEL) == 0) doit = false; + } + detail = key == OS.VK_PRIOR ? SWT.TRAVERSE_PAGE_PREVIOUS : SWT.TRAVERSE_PAGE_NEXT; + break; + } + default: + return false; + } + Event event = new Event (); + event.doit = doit; + event.detail = detail; + display.lastKey = lastKey; + display.lastAscii = lastAscii; + display.lastVirtual = lastVirtual; + display.lastNull = display.lastDead = false; + if (!setKeyState (event, SWT.Traverse, msg.wParam, msg.lParam)) return false; + Shell shell = getShell (); + Control control = this; + do { + if (control.traverse (event)) { + OS.SendMessage (hwnd, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0); + 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; +} + +boolean traverse (Event event) { + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the traverse + * event. If this happens, return true to stop further + * event processing. + */ + 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.character); + case SWT.TRAVERSE_PAGE_NEXT: return traversePage (true); + case SWT.TRAVERSE_PAGE_PREVIOUS: return traversePage (false); + } + 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 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 traverseMnemonic (char key) { + if (mnemonicHit (key)) { + OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0); + return true; + } + return false; +} + +boolean traversePage (boolean next) { + return false; +} + +boolean traverseReturn () { + return false; +} + +void unsubclass () { + int /*long*/ newProc = windowProc (); + int /*long*/ oldProc = display.windowProc; + if (oldProc == newProc) return; + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, newProc); +} + +/** + * 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 (OS.IsWinCE) { + OS.UpdateWindow (handle); + } else { + int flags = OS.RDW_UPDATENOW; + if (all) flags |= OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, null, 0, flags); + } +} + +void updateBackgroundColor () { + Control control = findBackgroundControl (); + if (control == null) control = this; + setBackgroundPixel (control.background); +} + +void updateBackgroundImage () { + Control control = findBackgroundControl (); + Image image = control != null ? control.backgroundImage : backgroundImage; + setBackgroundImage (image != null ? image.handle : 0); +} + +void updateBackgroundMode () { + int oldState = state & PARENT_BACKGROUND; + checkBackground (); + if (oldState != (state & PARENT_BACKGROUND)) { + setBackground (); + } +} + +void updateFont (Font oldFont, Font newFont) { + if (getFont ().equals (oldFont)) setFont (newFont); +} + +void updateImages () { + /* Do nothing */ +} + +void updateLayout (boolean resize, boolean all) { + /* Do nothing */ +} + +CREATESTRUCT widgetCreateStruct () { + return null; +} + +int widgetExtStyle () { + int bits = 0; + if (!OS.IsPPC) { + if ((style & SWT.BORDER) != 0) bits |= OS.WS_EX_CLIENTEDGE; + } +// if ((style & SWT.BORDER) != 0) { +// if ((style & SWT.FLAT) == 0) bits |= OS.WS_EX_CLIENTEDGE; +// } + /* + * Feature in Windows NT. When CreateWindowEx() is called with + * WS_EX_LAYOUTRTL or WS_EX_NOINHERITLAYOUT, CreateWindowEx() + * fails to create the HWND. The fix is to not use these bits. + */ + if (OS.WIN32_VERSION < OS.VERSION (4, 10)) { + return bits; + } + bits |= OS.WS_EX_NOINHERITLAYOUT; + if ((style & SWT.RIGHT_TO_LEFT) != 0) bits |= OS.WS_EX_LAYOUTRTL; + return bits; +} + +int /*long*/ widgetParent () { + return parent.handle; +} + +int widgetStyle () { + /* Force clipping of siblings by setting WS_CLIPSIBLINGS */ + int bits = OS.WS_CHILD | OS.WS_VISIBLE | OS.WS_CLIPSIBLINGS; +// if ((style & SWT.BORDER) != 0) { +// if ((style & SWT.FLAT) != 0) bits |= OS.WS_BORDER; +// } + if (OS.IsPPC) { + if ((style & SWT.BORDER) != 0) bits |= OS.WS_BORDER; + } + return bits; + + /* + * This code is intentionally commented. When clipping + * of both siblings and children is not enforced, it is + * possible for application code to draw outside of the + * control. + */ +// int bits = OS.WS_CHILD | OS.WS_VISIBLE; +// if ((style & SWT.CLIP_SIBLINGS) != 0) bits |= OS.WS_CLIPSIBLINGS; +// if ((style & SWT.CLIP_CHILDREN) != 0) bits |= OS.WS_CLIPCHILDREN; +// return bits; +} + +/** + * 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); + } + int /*long*/ topHandle = topHandle (); + if (OS.SetParent (topHandle, parent.handle) == 0) return false; + this.parent = parent; + int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE; + SetWindowPos (topHandle, OS.HWND_BOTTOM, 0, 0, 0, 0, flags); + return true; +} + +abstract TCHAR windowClass (); + +abstract int /*long*/ windowProc (); + +int /*long*/ windowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = null; + switch (msg) { + case OS.WM_ACTIVATE: result = WM_ACTIVATE (wParam, lParam); break; + case OS.WM_CAPTURECHANGED: result = WM_CAPTURECHANGED (wParam, lParam); break; + case OS.WM_CHANGEUISTATE: result = WM_CHANGEUISTATE (wParam, lParam); break; + case OS.WM_CHAR: result = WM_CHAR (wParam, lParam); break; + case OS.WM_CLEAR: result = WM_CLEAR (wParam, lParam); break; + case OS.WM_CLOSE: result = WM_CLOSE (wParam, lParam); break; + case OS.WM_COMMAND: result = WM_COMMAND (wParam, lParam); break; + case OS.WM_CONTEXTMENU: result = WM_CONTEXTMENU (wParam, lParam); break; + case OS.WM_CTLCOLORBTN: + case OS.WM_CTLCOLORDLG: + case OS.WM_CTLCOLOREDIT: + case OS.WM_CTLCOLORLISTBOX: + case OS.WM_CTLCOLORMSGBOX: + case OS.WM_CTLCOLORSCROLLBAR: + case OS.WM_CTLCOLORSTATIC: result = WM_CTLCOLOR (wParam, lParam); break; + case OS.WM_CUT: result = WM_CUT (wParam, lParam); break; + case OS.WM_DESTROY: result = WM_DESTROY (wParam, lParam); break; + case OS.WM_DRAWITEM: result = WM_DRAWITEM (wParam, lParam); break; + case OS.WM_ENDSESSION: result = WM_ENDSESSION (wParam, lParam); break; + case OS.WM_ENTERIDLE: result = WM_ENTERIDLE (wParam, lParam); break; + case OS.WM_ERASEBKGND: result = WM_ERASEBKGND (wParam, lParam); break; + case OS.WM_GETDLGCODE: result = WM_GETDLGCODE (wParam, lParam); break; + case OS.WM_GETFONT: result = WM_GETFONT (wParam, lParam); break; + case OS.WM_GETOBJECT: result = WM_GETOBJECT (wParam, lParam); break; + case OS.WM_GETMINMAXINFO: result = WM_GETMINMAXINFO (wParam, lParam); break; + case OS.WM_HELP: result = WM_HELP (wParam, lParam); break; + case OS.WM_HSCROLL: result = WM_HSCROLL (wParam, lParam); break; + case OS.WM_IME_CHAR: result = WM_IME_CHAR (wParam, lParam); break; + case OS.WM_IME_COMPOSITION: result = WM_IME_COMPOSITION (wParam, lParam); break; + case OS.WM_IME_COMPOSITION_START: result = WM_IME_COMPOSITION_START (wParam, lParam); break; + case OS.WM_IME_ENDCOMPOSITION: result = WM_IME_ENDCOMPOSITION (wParam, lParam); break; + case OS.WM_INITMENUPOPUP: result = WM_INITMENUPOPUP (wParam, lParam); break; + case OS.WM_INPUTLANGCHANGE: result = WM_INPUTLANGCHANGE (wParam, lParam); break; + case OS.WM_HOTKEY: result = WM_HOTKEY (wParam, lParam); break; + case OS.WM_KEYDOWN: result = WM_KEYDOWN (wParam, lParam); break; + case OS.WM_KEYUP: result = WM_KEYUP (wParam, lParam); break; + case OS.WM_KILLFOCUS: result = WM_KILLFOCUS (wParam, lParam); break; + case OS.WM_LBUTTONDBLCLK: result = WM_LBUTTONDBLCLK (wParam, lParam); break; + case OS.WM_LBUTTONDOWN: result = WM_LBUTTONDOWN (wParam, lParam); break; + case OS.WM_LBUTTONUP: result = WM_LBUTTONUP (wParam, lParam); break; + case OS.WM_MBUTTONDBLCLK: result = WM_MBUTTONDBLCLK (wParam, lParam); break; + case OS.WM_MBUTTONDOWN: result = WM_MBUTTONDOWN (wParam, lParam); break; + case OS.WM_MBUTTONUP: result = WM_MBUTTONUP (wParam, lParam); break; + case OS.WM_MEASUREITEM: result = WM_MEASUREITEM (wParam, lParam); break; + case OS.WM_MENUCHAR: result = WM_MENUCHAR (wParam, lParam); break; + case OS.WM_MENUSELECT: result = WM_MENUSELECT (wParam, lParam); break; + case OS.WM_MOUSEACTIVATE: result = WM_MOUSEACTIVATE (wParam, lParam); break; + case OS.WM_MOUSEHOVER: result = WM_MOUSEHOVER (wParam, lParam); break; + case OS.WM_MOUSELEAVE: result = WM_MOUSELEAVE (wParam, lParam); break; + case OS.WM_MOUSEMOVE: result = WM_MOUSEMOVE (wParam, lParam); break; + case OS.WM_MOUSEWHEEL: result = WM_MOUSEWHEEL (wParam, lParam); break; + case OS.WM_MOVE: result = WM_MOVE (wParam, lParam); break; + case OS.WM_NCACTIVATE: result = WM_NCACTIVATE (wParam, lParam); break; + case OS.WM_NCCALCSIZE: result = WM_NCCALCSIZE (wParam, lParam); break; + case OS.WM_NCHITTEST: result = WM_NCHITTEST (wParam, lParam); break; + case OS.WM_NCLBUTTONDOWN: result = WM_NCLBUTTONDOWN (wParam, lParam); break; + case OS.WM_NCPAINT: result = WM_NCPAINT (wParam, lParam); break; + case OS.WM_NOTIFY: result = WM_NOTIFY (wParam, lParam); break; + case OS.WM_PAINT: result = WM_PAINT (wParam, lParam); break; + case OS.WM_PALETTECHANGED: result = WM_PALETTECHANGED (wParam, lParam); break; + case OS.WM_PARENTNOTIFY: result = WM_PARENTNOTIFY (wParam, lParam); break; + case OS.WM_PASTE: result = WM_PASTE (wParam, lParam); break; + case OS.WM_PRINT: result = WM_PRINT (wParam, lParam); break; + case OS.WM_PRINTCLIENT: result = WM_PRINTCLIENT (wParam, lParam); break; + case OS.WM_QUERYENDSESSION: result = WM_QUERYENDSESSION (wParam, lParam); break; + case OS.WM_QUERYNEWPALETTE: result = WM_QUERYNEWPALETTE (wParam, lParam); break; + case OS.WM_QUERYOPEN: result = WM_QUERYOPEN (wParam, lParam); break; + case OS.WM_RBUTTONDBLCLK: result = WM_RBUTTONDBLCLK (wParam, lParam); break; + case OS.WM_RBUTTONDOWN: result = WM_RBUTTONDOWN (wParam, lParam); break; + case OS.WM_RBUTTONUP: result = WM_RBUTTONUP (wParam, lParam); break; + case OS.WM_SETCURSOR: result = WM_SETCURSOR (wParam, lParam); break; + case OS.WM_SETFOCUS: result = WM_SETFOCUS (wParam, lParam); break; + case OS.WM_SETFONT: result = WM_SETFONT (wParam, lParam); break; + case OS.WM_SETTINGCHANGE: result = WM_SETTINGCHANGE (wParam, lParam); break; + case OS.WM_SETREDRAW: result = WM_SETREDRAW (wParam, lParam); break; + case OS.WM_SHOWWINDOW: result = WM_SHOWWINDOW (wParam, lParam); break; + case OS.WM_SIZE: result = WM_SIZE (wParam, lParam); break; + case OS.WM_SYSCHAR: result = WM_SYSCHAR (wParam, lParam); break; + case OS.WM_SYSCOLORCHANGE: result = WM_SYSCOLORCHANGE (wParam, lParam); break; + case OS.WM_SYSCOMMAND: result = WM_SYSCOMMAND (wParam, lParam); break; + case OS.WM_SYSKEYDOWN: result = WM_SYSKEYDOWN (wParam, lParam); break; + case OS.WM_SYSKEYUP: result = WM_SYSKEYUP (wParam, lParam); break; + case OS.WM_TIMER: result = WM_TIMER (wParam, lParam); break; + case OS.WM_UNDO: result = WM_UNDO (wParam, lParam); break; + case OS.WM_UPDATEUISTATE: result = WM_UPDATEUISTATE (wParam, lParam); break; + case OS.WM_VSCROLL: result = WM_VSCROLL (wParam, lParam); break; + case OS.WM_WINDOWPOSCHANGED: result = WM_WINDOWPOSCHANGED (wParam, lParam); break; + case OS.WM_WINDOWPOSCHANGING: result = WM_WINDOWPOSCHANGING (wParam, lParam); break; + case OS.WM_XBUTTONDBLCLK: result = WM_XBUTTONDBLCLK (wParam, lParam); break; + case OS.WM_XBUTTONDOWN: result = WM_XBUTTONDOWN (wParam, lParam); break; + case OS.WM_XBUTTONUP: result = WM_XBUTTONUP (wParam, lParam); break; + } + if (result != null) return result.value; + return callWindowProc (hwnd, msg, wParam, lParam); +} + +LRESULT WM_ACTIVATE (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_CAPTURECHANGED (int /*long*/ wParam, int /*long*/ lParam) { + return wmCaptureChanged (handle, wParam, lParam); +} + +LRESULT WM_CHANGEUISTATE (int /*long*/ wParam, int /*long*/ lParam) { + if ((state & IGNORE_WM_CHANGEUISTATE) != 0) return LRESULT.ZERO; + return null; +} + +LRESULT WM_CHAR (int /*long*/ wParam, int /*long*/ lParam) { + return wmChar (handle, wParam, lParam); +} + +LRESULT WM_CLEAR (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_CLOSE (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_COMMAND (int /*long*/ wParam, int /*long*/ lParam) { + /* + * When the WM_COMMAND message is sent from a + * menu, the HWND parameter in LPARAM is zero. + */ + if (lParam == 0) { + Decorations shell = menuShell (); + if (shell.isEnabled ()) { + int id = OS.LOWORD (wParam); + MenuItem item = display.getMenuItem (id); + if (item != null && item.isEnabled ()) { + return item.wmCommandChild (wParam, lParam); + } + } + return null; + } + Control control = display.getControl (lParam); + if (control == null) return null; + return control.wmCommandChild (wParam, lParam); +} + +LRESULT WM_CONTEXTMENU (int /*long*/ wParam, int /*long*/ lParam) { + return wmContextMenu (handle, wParam, lParam); +} + +LRESULT WM_CTLCOLOR (int /*long*/ wParam, int /*long*/ lParam) { + int /*long*/ hPalette = display.hPalette; + if (hPalette != 0) { + OS.SelectPalette (wParam, hPalette, false); + OS.RealizePalette (wParam); + } + Control control = display.getControl (lParam); + if (control == null) return null; + return control.wmColorChild (wParam, lParam); +} + +LRESULT WM_CUT (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_DESTROY (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_DRAWITEM (int /*long*/ wParam, int /*long*/ lParam) { + DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT (); + OS.MoveMemory (struct, lParam, DRAWITEMSTRUCT.sizeof); + if (struct.CtlType == OS.ODT_MENU) { + MenuItem item = display.getMenuItem (struct.itemID); + if (item == null) return null; + return item.wmDrawChild (wParam, lParam); + } + Control control = display.getControl (struct.hwndItem); + if (control == null) return null; + return control.wmDrawChild (wParam, lParam); +} + +LRESULT WM_ENDSESSION (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_ENTERIDLE (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_ERASEBKGND (int /*long*/ wParam, int /*long*/ lParam) { + if ((state & DRAW_BACKGROUND) != 0) { + if (findImageControl () != null) return LRESULT.ONE; + } + if ((state & THEME_BACKGROUND) != 0) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if (findThemeControl () != null) return LRESULT.ONE; + } + } + return null; +} + +LRESULT WM_GETDLGCODE (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_GETFONT (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_GETOBJECT (int /*long*/ wParam, int /*long*/ lParam) { + if (accessible != null) { + int /*long*/ result = accessible.internal_WM_GETOBJECT (wParam, lParam); + if (result != 0) return new LRESULT (result); + } + return null; +} + +LRESULT WM_GETMINMAXINFO (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_HOTKEY (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_HELP (int /*long*/ wParam, int /*long*/ lParam) { + if (OS.IsWinCE) return null; + HELPINFO lphi = new HELPINFO (); + OS.MoveMemory (lphi, lParam, HELPINFO.sizeof); + Decorations shell = menuShell (); + if (!shell.isEnabled ()) return null; + if (lphi.iContextType == OS.HELPINFO_MENUITEM) { + MenuItem item = display.getMenuItem (lphi.iCtrlId); + if (item != null && item.isEnabled ()) { + Widget widget = null; + if (item.hooks (SWT.Help)) { + widget = item; + } else { + Menu menu = item.parent; + if (menu.hooks (SWT.Help)) widget = menu; + } + if (widget != null) { + int /*long*/ hwndShell = shell.handle; + OS.SendMessage (hwndShell, OS.WM_CANCELMODE, 0, 0); + widget.postEvent (SWT.Help); + return LRESULT.ONE; + } + } + return null; + } + if (hooks (SWT.Help)) { + postEvent (SWT.Help); + return LRESULT.ONE; + } + return null; +} + +LRESULT WM_HSCROLL (int /*long*/ wParam, int /*long*/ lParam) { + Control control = display.getControl (lParam); + if (control == null) return null; + return control.wmScrollChild (wParam, lParam); +} + +LRESULT WM_IME_CHAR (int /*long*/ wParam, int /*long*/ lParam) { + return wmIMEChar (handle, wParam, lParam); +} + +LRESULT WM_IME_COMPOSITION (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_IME_COMPOSITION_START (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_IME_ENDCOMPOSITION (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_INITMENUPOPUP (int /*long*/ wParam, int /*long*/ lParam) { + + /* Ignore WM_INITMENUPOPUP for an accelerator */ + if (display.accelKeyHit) return null; + + /* + * If the high order word of LPARAM is non-zero, + * the menu is the system menu and we can ignore + * WPARAM. Otherwise, use WPARAM to find the menu. + */ + Shell shell = getShell (); + Menu oldMenu = shell.activeMenu, newMenu = null; + if (OS.HIWORD (lParam) == 0) { + newMenu = menuShell ().findMenu (wParam); + if (newMenu != null) newMenu.update (); + } + Menu menu = newMenu; + while (menu != null && menu != oldMenu) { + menu = menu.getParentMenu (); + } + if (menu == null) { + menu = shell.activeMenu; + while (menu != null) { + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the hide + * event. If this happens, stop searching up the + * ancestor list because there is no longer a link + * to follow. + */ + menu.sendEvent (SWT.Hide); + if (menu.isDisposed ()) break; + menu = menu.getParentMenu (); + Menu ancestor = newMenu; + while (ancestor != null && ancestor != menu) { + ancestor = ancestor.getParentMenu (); + } + if (ancestor != null) break; + } + } + + /* + * The shell and the new menu may be disposed because of + * sending the hide event to the ancestor menus but setting + * a field to null in a disposed shell is not harmful. + */ + if (newMenu != null && newMenu.isDisposed ()) newMenu = null; + shell.activeMenu = newMenu; + + /* Send the show event */ + if (newMenu != null && newMenu != oldMenu) { + newMenu.sendEvent (SWT.Show); + // widget could be disposed at this point + } + return null; +} + +LRESULT WM_INPUTLANGCHANGE (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_KEYDOWN (int /*long*/ wParam, int /*long*/ lParam) { + return wmKeyDown (handle, wParam, lParam); +} + +LRESULT WM_KEYUP (int /*long*/ wParam, int /*long*/ lParam) { + return wmKeyUp (handle, wParam, lParam); +} + +LRESULT WM_KILLFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + return wmKillFocus (handle, wParam, lParam); +} + +LRESULT WM_LBUTTONDBLCLK (int /*long*/ wParam, int /*long*/ lParam) { + return wmLButtonDblClk (handle, wParam, lParam); +} + +LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + return wmLButtonDown (handle, wParam, lParam); +} + +LRESULT WM_LBUTTONUP (int /*long*/ wParam, int /*long*/ lParam) { + return wmLButtonUp (handle, wParam, lParam); +} + +LRESULT WM_MBUTTONDBLCLK (int /*long*/ wParam, int /*long*/ lParam) { + return wmMButtonDblClk (handle, wParam, lParam); +} + +LRESULT WM_MBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + return wmMButtonDown (handle, wParam, lParam); +} + +LRESULT WM_MBUTTONUP (int /*long*/ wParam, int /*long*/ lParam) { + return wmMButtonUp (handle, wParam, lParam); +} + +LRESULT WM_MEASUREITEM (int /*long*/ wParam, int /*long*/ lParam) { + MEASUREITEMSTRUCT struct = new MEASUREITEMSTRUCT (); + OS.MoveMemory (struct, lParam, MEASUREITEMSTRUCT.sizeof); + if (struct.CtlType == OS.ODT_MENU) { + MenuItem item = display.getMenuItem (struct.itemID); + if (item == null) return null; + return item.wmMeasureChild (wParam, lParam); + } + int /*long*/ hwnd = OS.GetDlgItem (handle, struct.CtlID); + Control control = display.getControl (hwnd); + if (control == null) return null; + return control.wmMeasureChild (wParam, lParam); +} + +LRESULT WM_MENUCHAR (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. When the user types Alt+<key> + * and <key> does not match a mnemonic in the System + * menu or the menu bar, Windows beeps. This beep is + * unexpected and unwanted by applications that look + * for Alt+<key>. The fix is to detect the case and + * stop Windows from beeping by closing the menu. + */ + int type = OS.HIWORD (wParam); + if (type == 0 || type == OS.MF_SYSMENU) { + display.mnemonicKeyHit = false; + return new LRESULT (OS.MAKELRESULT (0, OS.MNC_CLOSE)); + } + return null; +} + +LRESULT WM_MENUSELECT (int /*long*/ wParam, int /*long*/ lParam) { + int code = OS.HIWORD (wParam); + Shell shell = getShell (); + if (code == 0xFFFF && lParam == 0) { + Menu menu = shell.activeMenu; + while (menu != null) { + /* + * When the user cancels any menu that is not the + * menu bar, assume a mnemonic key was pressed to open + * the menu from WM_SYSCHAR. When the menu was invoked + * using the mouse, this assumption is wrong but not + * harmful. This variable is only used in WM_SYSCHAR + * and WM_SYSCHAR is only sent after the user has pressed + * a mnemonic. + */ + display.mnemonicKeyHit = true; + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the hide + * event. If this happens, stop searching up the + * parent list because there is no longer a link + * to follow. + */ + menu.sendEvent (SWT.Hide); + if (menu.isDisposed ()) break; + menu = menu.getParentMenu (); + } + /* + * The shell may be disposed because of sending the hide + * event to the last active menu menu but setting a field + * to null in a destroyed widget is not harmful. + */ + shell.activeMenu = null; + return null; + } + if ((code & OS.MF_SYSMENU) != 0) return null; + if ((code & OS.MF_HILITE) != 0) { + MenuItem item = null; + Decorations menuShell = menuShell (); + if ((code & OS.MF_POPUP) != 0) { + int index = OS.LOWORD (wParam); + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_SUBMENU; + if (OS.GetMenuItemInfo (lParam, index, true, info)) { + Menu newMenu = menuShell.findMenu (info.hSubMenu); + if (newMenu != null) item = newMenu.cascade; + } + } else { + Menu newMenu = menuShell.findMenu (lParam); + if (newMenu != null) { + int id = OS.LOWORD (wParam); + item = display.getMenuItem (id); + } + Menu oldMenu = shell.activeMenu; + if (oldMenu != null) { + Menu ancestor = oldMenu; + while (ancestor != null && ancestor != newMenu) { + ancestor = ancestor.getParentMenu (); + } + if (ancestor == newMenu) { + ancestor = oldMenu; + while (ancestor != newMenu) { + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the hide + * event or the item about to be armed. If this + * happens, stop searching up the ancestor list + * because there is no longer a link to follow. + */ + ancestor.sendEvent (SWT.Hide); + if (ancestor.isDisposed ()) break; + ancestor = ancestor.getParentMenu (); + if (ancestor == null) break; + } + /* + * The shell and/or the item could be disposed when + * processing hide events from above. If this happens, + * ensure that the shell is not accessed and that no + * arm event is sent to the item. + */ + if (!shell.isDisposed ()) { + if (newMenu != null && newMenu.isDisposed ()) { + newMenu = null; + } + shell.activeMenu = newMenu; + } + if (item != null && item.isDisposed ()) item = null; + } + } + } + if (item != null) item.sendEvent (SWT.Arm); + } + return null; +} + +LRESULT WM_MOUSEACTIVATE (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_MOUSEHOVER (int /*long*/ wParam, int /*long*/ lParam) { + return wmMouseHover (handle, wParam, lParam); +} + +LRESULT WM_MOUSELEAVE (int /*long*/ wParam, int /*long*/ lParam) { + if (OS.COMCTL32_MAJOR >= 6) getShell ().fixToolTip (); + return wmMouseLeave (handle, wParam, lParam); +} + +LRESULT WM_MOUSEMOVE (int /*long*/ wParam, int /*long*/ lParam) { + return wmMouseMove (handle, wParam, lParam); +} + +LRESULT WM_MOUSEWHEEL (int /*long*/ wParam, int /*long*/ lParam) { + return wmMouseWheel (handle, wParam, lParam); +} + +LRESULT WM_MOVE (int /*long*/ wParam, int /*long*/ lParam) { + state |= MOVE_OCCURRED; + if (findImageControl () != null) { + if (this != getShell ()) redrawChildren (); + } else { + if ((state & THEME_BACKGROUND) != 0) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if (OS.IsWindowVisible (handle)) { + if (findThemeControl () != null) redrawChildren (); + } + } + } + } + if ((state & MOVE_DEFERRED) == 0) sendEvent (SWT.Move); + // widget could be disposed at this point + return null; +} + +LRESULT WM_NCACTIVATE (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_NCCALCSIZE (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_NCHITTEST (int /*long*/ wParam, int /*long*/ lParam) { + if (!OS.IsWindowEnabled (handle)) return null; + if (!isActive ()) return new LRESULT (OS.HTTRANSPARENT); + return null; +} + +LRESULT WM_NCLBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_NCPAINT (int /*long*/ wParam, int /*long*/ lParam) { + return wmNCPaint (handle, wParam, lParam); +} + +LRESULT WM_NOTIFY (int /*long*/ wParam, int /*long*/ lParam) { + NMHDR hdr = new NMHDR (); + OS.MoveMemory (hdr, lParam, NMHDR.sizeof); + return wmNotify (hdr, wParam, lParam); +} + +LRESULT WM_PAINT (int /*long*/ wParam, int /*long*/ lParam) { + return wmPaint (handle, wParam, lParam); +} + +LRESULT WM_PALETTECHANGED (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_PARENTNOTIFY (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_PASTE (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_PRINT (int /*long*/ wParam, int /*long*/ lParam) { + return wmPrint (handle, wParam, lParam); +} + +LRESULT WM_PRINTCLIENT (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_QUERYENDSESSION (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_QUERYNEWPALETTE (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_QUERYOPEN (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_RBUTTONDBLCLK (int /*long*/ wParam, int /*long*/ lParam) { + return wmRButtonDblClk (handle, wParam, lParam); +} + +LRESULT WM_RBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + return wmRButtonDown (handle, wParam, lParam); +} + +LRESULT WM_RBUTTONUP (int /*long*/ wParam, int /*long*/ lParam) { + return wmRButtonUp (handle, wParam, lParam); +} + +LRESULT WM_SETCURSOR (int /*long*/ wParam, int /*long*/ lParam) { + int hitTest = (short) OS.LOWORD (lParam); + if (hitTest == OS.HTCLIENT) { + Control control = display.getControl (wParam); + if (control == null) return null; + Cursor cursor = control.findCursor (); + if (cursor != null) { + OS.SetCursor (cursor.handle); + return LRESULT.ONE; + } + } + return null; +} + +LRESULT WM_SETFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + return wmSetFocus (handle, wParam, lParam); +} + +LRESULT WM_SETTINGCHANGE (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_SETFONT (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_SETREDRAW (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_SHOWWINDOW (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + state |= RESIZE_OCCURRED; + if ((state & RESIZE_DEFERRED) == 0) sendEvent (SWT.Resize); + // widget could be disposed at this point + return null; +} + +LRESULT WM_SYSCHAR (int /*long*/ wParam, int /*long*/ lParam) { + return wmSysChar (handle, wParam, lParam); +} + +LRESULT WM_SYSCOLORCHANGE (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_SYSCOMMAND (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Check to see if the command is a system command or + * a user menu item that was added to the System menu. + * When a user item is added to the System menu, + * WM_SYSCOMMAND must always return zero. + * + * NOTE: This is undocumented. + */ + if ((wParam & 0xF000) == 0) { + Decorations shell = menuShell (); + if (shell.isEnabled ()) { + MenuItem item = display.getMenuItem (OS.LOWORD (wParam)); + if (item != null) item.wmCommandChild (wParam, lParam); + } + return LRESULT.ZERO; + } + + /* Process the System Command */ + int cmd = (int)/*64*/wParam & 0xFFF0; + switch (cmd) { + case OS.SC_CLOSE: + int /*long*/ hwndShell = menuShell ().handle; + int bits = OS.GetWindowLong (hwndShell, OS.GWL_STYLE); + if ((bits & OS.WS_SYSMENU) == 0) return LRESULT.ZERO; + break; + case OS.SC_KEYMENU: + /* + * When lParam is zero, one of F10, Shift+F10, Ctrl+F10 or + * Ctrl+Shift+F10 was pressed. If there is no menu bar and + * the focus control is interested in keystrokes, give the + * key to the focus control. Normally, F10 with no menu bar + * moves focus to the System menu but this can be achieved + * using Alt+Space. To allow the application to see F10, + * avoid running the default window proc. + * + * NOTE: When F10 is pressed, WM_SYSCOMMAND is sent to the + * shell, not the focus control. This is undocumented Windows + * behavior. + */ + if (lParam == 0) { + Decorations shell = menuShell (); + Menu menu = shell.getMenuBar (); + if (menu == null) { + Control control = display._getFocusControl (); + if (control != null) { + if (control.hooks (SWT.KeyDown) || control.hooks (SWT.KeyUp)) { + display.mnemonicKeyHit = false; + return LRESULT.ZERO; + } + } + } + } else { + /* + * When lParam is not zero, Alt+<key> was pressed. If the + * application is interested in keystrokes and there is a + * menu bar, check to see whether the key that was pressed + * matches a mnemonic on the menu bar. Normally, Windows + * matches the first character of a menu item as well as + * matching the mnemonic character. To allow the application + * to see the keystrokes in this case, avoid running the default + * window proc. + * + * NOTE: When the user types Alt+Space, the System menu is + * activated. In this case the application should not see + * the keystroke. + */ + if (hooks (SWT.KeyDown) || hooks (SWT.KeyUp)) { + if (lParam != ' ') { + Decorations shell = menuShell (); + Menu menu = shell.getMenuBar (); + if (menu != null) { + char key = Display.mbcsToWcs ((int)/*64*/lParam); + if (key != 0) { + key = Character.toUpperCase (key); + MenuItem [] items = menu.getItems (); + for (int i=0; i<items.length; i++) { + MenuItem item = items [i]; + String text = item.getText (); + char mnemonic = findMnemonic (text); + if (text.length () > 0 && mnemonic == 0) { + char ch = text.charAt (0); + if (Character.toUpperCase (ch) == key) { + display.mnemonicKeyHit = false; + return LRESULT.ZERO; + } + } + } + } + } else { + display.mnemonicKeyHit = false; + } + } + } + } + // FALL THROUGH + case OS.SC_HSCROLL: + case OS.SC_VSCROLL: + /* + * Do not allow keyboard traversal of the menu bar + * or scrolling when the shell is not enabled. + */ + Decorations shell = menuShell (); + if (!shell.isEnabled () || !shell.isActive ()) { + return LRESULT.ZERO; + } + break; + case OS.SC_MINIMIZE: + /* Save the focus widget when the shell is minimized */ + menuShell ().saveFocus (); + break; + } + return null; +} + +LRESULT WM_SYSKEYDOWN (int /*long*/ wParam, int /*long*/ lParam) { + return wmSysKeyDown (handle, wParam, lParam); +} + +LRESULT WM_SYSKEYUP (int /*long*/ wParam, int /*long*/ lParam) { + return wmSysKeyUp (handle, wParam, lParam); +} + +LRESULT WM_TIMER (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_UNDO (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_UPDATEUISTATE (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_VSCROLL (int /*long*/ wParam, int /*long*/ lParam) { + Control control = display.getControl (lParam); + if (control == null) return null; + return control.wmScrollChild (wParam, lParam); +} + +LRESULT WM_WINDOWPOSCHANGED (int /*long*/ wParam, int /*long*/ lParam) { + try { + display.resizeCount++; + int /*long*/ code = callWindowProc (handle, OS.WM_WINDOWPOSCHANGED, wParam, lParam); + return code == 0 ? LRESULT.ZERO : new LRESULT (code); + } finally { + --display.resizeCount; + } +} + +LRESULT WM_WINDOWPOSCHANGING (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Bug in Windows. When WM_SETREDRAW is used to turn off drawing + * for a control and the control is moved or resized, Windows does + * not redraw the area where the control once was in the parent. + * The fix is to detect this case and redraw the area. + */ + if (!getDrawing()) { + Shell shell = getShell (); + if (shell != this) { + WINDOWPOS lpwp = new WINDOWPOS (); + OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof); + if ((lpwp.flags & OS.SWP_NOMOVE) == 0 || (lpwp.flags & OS.SWP_NOSIZE) == 0) { + RECT rect = new RECT (); + OS.GetWindowRect (topHandle (), rect); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + if (width != 0 && height != 0) { + int /*long*/ hwndParent = parent == null ? 0 : parent.handle; + OS.MapWindowPoints (0, hwndParent, rect, 2); + if (OS.IsWinCE) { + OS.InvalidateRect (hwndParent, rect, true); + } else { + int /*long*/ rgn1 = OS.CreateRectRgn (rect.left, rect.top, rect.right, rect.bottom); + int /*long*/ rgn2 = OS.CreateRectRgn (lpwp.x, lpwp.y, lpwp.x + lpwp.cx, lpwp.y + lpwp.cy); + OS.CombineRgn (rgn1, rgn1, rgn2, OS.RGN_DIFF); + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (hwndParent, null, rgn1, flags); + OS.DeleteObject (rgn1); + OS.DeleteObject (rgn2); + } + } + } + } + } + return null; +} + +LRESULT WM_XBUTTONDBLCLK (int /*long*/ wParam, int /*long*/ lParam) { + return wmXButtonDblClk (handle, wParam, lParam); +} + +LRESULT WM_XBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + return wmXButtonDown (handle, wParam, lParam); +} + +LRESULT WM_XBUTTONUP (int /*long*/ wParam, int /*long*/ lParam) { + return wmXButtonUp (handle, wParam, lParam); +} + +LRESULT wmColorChild (int /*long*/ wParam, int /*long*/ lParam) { + Control control = findBackgroundControl (); + if (control == null) { + if ((state & THEME_BACKGROUND) != 0) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + control = findThemeControl (); + if (control != null) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + OS.SetTextColor (wParam, getForegroundPixel ()); + OS.SetBkColor (wParam, getBackgroundPixel ()); + fillThemeBackground (wParam, control, rect); + OS.SetBkMode (wParam, OS.TRANSPARENT); + return new LRESULT (OS.GetStockObject (OS.NULL_BRUSH)); + } + } + } + if (foreground == -1) return null; + } + if (control == null) control = this; + int forePixel = getForegroundPixel (); + int backPixel = control.getBackgroundPixel (); + OS.SetTextColor (wParam, forePixel); + OS.SetBkColor (wParam, backPixel); + if (control.backgroundImage != null) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + int /*long*/ hwnd = control.handle; + int /*long*/ hBitmap = control.backgroundImage.handle; + OS.MapWindowPoints (handle, hwnd, rect, 2); + POINT lpPoint = new POINT (); + OS.GetWindowOrgEx (wParam, lpPoint); + OS.SetBrushOrgEx (wParam, -rect.left - lpPoint.x, -rect.top - lpPoint.y, lpPoint); + int /*long*/ hBrush = findBrush (hBitmap, OS.BS_PATTERN); + if ((state & DRAW_BACKGROUND) != 0) { + int /*long*/ hOldBrush = OS.SelectObject (wParam, hBrush); + OS.MapWindowPoints (hwnd, handle, rect, 2); + OS.PatBlt (wParam, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY); + OS.SelectObject (wParam, hOldBrush); + } + OS.SetBkMode (wParam, OS.TRANSPARENT); + return new LRESULT (hBrush); + } + int /*long*/ hBrush = findBrush (backPixel, OS.BS_SOLID); + if ((state & DRAW_BACKGROUND) != 0) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + int /*long*/ hOldBrush = OS.SelectObject (wParam, hBrush); + OS.PatBlt (wParam, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY); + OS.SelectObject (wParam, hOldBrush); + } + return new LRESULT (hBrush); +} + +LRESULT wmCommandChild (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT wmDrawChild (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT wmMeasureChild (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT wmNotify (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + Control control = display.getControl (hdr.hwndFrom); + if (control == null) return null; + return control.wmNotifyChild (hdr, wParam, lParam); +} + +LRESULT wmNotifyChild (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT wmScrollChild (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +} + diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/CoolBar.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/CoolBar.java new file mode 100755 index 0000000000..9e22db925b --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/CoolBar.java @@ -0,0 +1,1195 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * Instances of this class provide an area for dynamically + * positioning the items they contain. + * <p> + * The item children that may be added to instances of this class + * must be of type <code>CoolItem</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, HORIZONTAL, VERTICAL</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * </p><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/#coolbar">CoolBar 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 CoolBar extends Composite { + CoolItem [] items; + CoolItem [] originalItems; + boolean locked; + boolean ignoreResize; + static final int /*long*/ ReBarProc; + static final TCHAR ReBarClass = new TCHAR (0, OS.REBARCLASSNAME, true); + static { + INITCOMMONCONTROLSEX icex = new INITCOMMONCONTROLSEX (); + icex.dwSize = INITCOMMONCONTROLSEX.sizeof; + icex.dwICC = OS.ICC_COOL_CLASSES; + OS.InitCommonControlsEx (icex); + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, ReBarClass, lpWndClass); + ReBarProc = lpWndClass.lpfnWndProc; + } + static final int SEPARATOR_WIDTH = 2; + static final int MAX_WIDTH = 0x7FFF; + static final int DEFAULT_COOLBAR_WIDTH = 0; + static final int DEFAULT_COOLBAR_HEIGHT = 0; + +/** + * 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#FLAT + * @see SWT#HORIZONTAL + * @see SWT#VERTICAL + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public CoolBar (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. + * + * NOTE: The CCS_VERT style cannot be applied when the + * widget is created because of this conflict. + */ + if ((style & SWT.VERTICAL) != 0) { + this.style |= SWT.VERTICAL; + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.CCS_VERT); + } else { + this.style |= SWT.HORIZONTAL; + } +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + return OS.CallWindowProc (ReBarProc, hwnd, msg, wParam, lParam); +} + +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 Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + int width = 0, height = 0; + int border = getBorderWidth (); + int newWidth = wHint == SWT.DEFAULT ? 0x3FFF : wHint + (border * 2); + int newHeight = hHint == SWT.DEFAULT ? 0x3FFF : hHint + (border * 2); + int count = (int)/*64*/OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0); + if (count != 0) { + ignoreResize = true; + boolean redraw = false; + if (OS.IsWindowVisible (handle)) { + if (OS.COMCTL32_MAJOR >= 6) { + redraw = true; + OS.UpdateWindow (handle); + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + } else { + redraw = getDrawing(); + if (redraw) { + OS.UpdateWindow (handle); + OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); + } + } + } + RECT oldRect = new RECT (); + OS.GetWindowRect (handle, oldRect); + int oldWidth = oldRect.right - oldRect.left; + int oldHeight = oldRect.bottom - oldRect.top; + int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOREDRAW | OS.SWP_NOZORDER; + SetWindowPos (handle, 0, 0, 0, newWidth, newHeight, flags); + RECT rect = new RECT (); + OS.SendMessage (handle, OS.RB_GETRECT, count - 1, rect); + height = Math.max (height, rect.bottom); + SetWindowPos (handle, 0, 0, 0, oldWidth, oldHeight, flags); + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_IDEALSIZE | OS.RBBIM_STYLE; + int rowWidth = 0; + for (int i = 0; i < count; i++) { + OS.SendMessage(handle, OS.RB_GETBANDINFO, i, rbBand); + if ((rbBand.fStyle & OS.RBBS_BREAK) != 0) { + width = Math.max(width, rowWidth); + rowWidth = 0; + } + rowWidth += rbBand.cxIdeal + getMargin (i); + } + width = Math.max(width, rowWidth); + if (redraw) { + if (OS.COMCTL32_MAJOR >= 6) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + } else { + OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0); + } + } + ignoreResize = false; + } + if (width == 0) width = DEFAULT_COOLBAR_WIDTH; + if (height == 0) height = DEFAULT_COOLBAR_HEIGHT; + if ((style & SWT.VERTICAL) != 0) { + int tmp = width; + width = height; + height = tmp; + } + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + height += border * 2; + width += border * 2; + return new Point (width, height); +} + +void createHandle () { + super.createHandle (); + state &= ~(CANVAS | THEME_BACKGROUND); + + /* + * Feature in Windows. When the control is created, + * it does not use the default system font. A new HFONT + * is created and destroyed when the control is destroyed. + * This means that a program that queries the font from + * this control, uses the font in another control and then + * destroys this control will have the font unexpectedly + * destroyed in the other control. The fix is to assign + * the font ourselves each time the control is created. + * The control will not destroy a font that it did not + * create. + */ + int /*long*/ hFont = OS.GetStockObject (OS.SYSTEM_FONT); + OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0); +} + +void createItem (CoolItem item, int index) { + int count = (int)/*64*/OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0); + if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE); + int id = 0; + while (id < items.length && items [id] != null) id++; + if (id == items.length) { + CoolItem [] newItems = new CoolItem [items.length + 4]; + System.arraycopy (items, 0, newItems, 0, items.length); + items = newItems; + } + int /*long*/ hHeap = OS.GetProcessHeap (); + int /*long*/ lpText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof); + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_TEXT | OS.RBBIM_STYLE | OS.RBBIM_ID; + rbBand.fStyle = OS.RBBS_VARIABLEHEIGHT | OS.RBBS_GRIPPERALWAYS; + if ((item.style & SWT.DROP_DOWN) != 0) { + rbBand.fStyle |= OS.RBBS_USECHEVRON; + } + rbBand.lpText = lpText; + rbBand.wID = id; + + /* + * Feature in Windows. When inserting an item at end of a row, + * sometimes, Windows will begin to place the item on the right + * side of the cool bar. The fix is to resize the new items to + * the maximum size and then resize the next to last item to the + * ideal size. + */ + int lastIndex = getLastIndexOfRow (index - 1); + boolean fixLast = index == lastIndex + 1; + if (fixLast) { + rbBand.fMask |= OS.RBBIM_SIZE; + rbBand.cx = MAX_WIDTH; + } + + /* + * Feature in Windows. Is possible that the item at index zero + * has the RBBS_BREAK flag set. When a new item is inserted at + * position zero, the previous item at position zero moves to + * a new line. The fix is to detect this case and clear the + * RBBS_BREAK flag on the previous item before inserting the + * new item. + */ + if (index == 0 && count > 0) { + getItem (0).setWrap (false); + } + + /* Insert the item */ + if (OS.SendMessage (handle, OS.RB_INSERTBAND, index, rbBand) == 0) { + error (SWT.ERROR_ITEM_NOT_ADDED); + } + + /* Resize the next to last item to the ideal size */ + if (fixLast) { + resizeToPreferredWidth (lastIndex); + } + + OS.HeapFree (hHeap, 0, lpText); + items [item.id = id] = item; + int length = originalItems.length; + CoolItem [] newOriginals = new CoolItem [length + 1]; + System.arraycopy (originalItems, 0, newOriginals, 0, index); + System.arraycopy (originalItems, index, newOriginals, index + 1, length - index); + newOriginals [index] = item; + originalItems = newOriginals; +} + +void createWidget () { + super.createWidget (); + items = new CoolItem [4]; + originalItems = new CoolItem [0]; +} + +void destroyItem (CoolItem item) { + int index = (int)/*64*/OS.SendMessage (handle, OS.RB_IDTOINDEX, item.id, 0); + int count = (int)/*64*/OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0); + if (count != 0) { + int lastIndex = getLastIndexOfRow (index); + if (index == lastIndex) { + /* + * Feature in Windows. If the last item in a row is + * given its ideal size, it will be placed at the far + * right hand edge of the coolbar. It is preferred + * that the last item appear next to the second last + * item. The fix is to size the last item of each row + * so that it occupies all the available space to the + * right in the row. + */ + resizeToMaximumWidth (lastIndex - 1); + } + } + + /* + * Feature in Windows. When Windows removed a rebar + * band, it makes the band child invisible. The fix + * is to show the child. + */ + Control control = item.control; + boolean wasVisible = control != null && !control.isDisposed() && control.getVisible (); + + /* + * When a wrapped item is being deleted, make the next + * item in the row wrapped in order to preserve the row. + * In order to avoid an unnecessary layout, temporarily + * ignore WM_SIZE. If the next item is wrapped then a + * row will be deleted and the WM_SIZE is necessary. + */ + CoolItem nextItem = null; + if (item.getWrap ()) { + if (index + 1 < count) { + nextItem = getItem (index + 1); + ignoreResize = !nextItem.getWrap (); + } + } + if (OS.SendMessage (handle, OS.RB_DELETEBAND, index, 0) == 0) { + error (SWT.ERROR_ITEM_NOT_REMOVED); + } + items [item.id] = null; + item.id = -1; + if (ignoreResize) { + nextItem.setWrap (true); + ignoreResize = false; + } + + /* Restore the visible state of the control */ + if (wasVisible) control.setVisible (true); + + index = 0; + while (index < originalItems.length) { + if (originalItems [index] == item) break; + index++; + } + int length = originalItems.length - 1; + CoolItem [] newOriginals = new CoolItem [length]; + System.arraycopy (originalItems, 0, newOriginals, 0, index); + System.arraycopy (originalItems, index + 1, newOriginals, index, length - index); + originalItems = newOriginals; +} + +void drawThemeBackground (int /*long*/ hDC, int /*long*/ hwnd, RECT rect) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if (background == -1 && (style & SWT.FLAT) != 0) { + Control control = findBackgroundControl (); + if (control != null && control.backgroundImage != null) { + fillBackground (hDC, control.getBackgroundPixel (), rect); + return; + } + } + } + RECT rect2 = new RECT (); + OS.GetClientRect (handle, rect2); + OS.MapWindowPoints (handle, hwnd, rect2, 2); + POINT lpPoint = new POINT (); + OS.SetWindowOrgEx (hDC, -rect2.left, -rect2.top, lpPoint); + OS.SendMessage (handle, OS.WM_PRINT, hDC, OS.PRF_CLIENT | OS.PRF_ERASEBKGND); + OS.SetWindowOrgEx (hDC, lpPoint.x, lpPoint.y, null); +} + +Control findThemeControl () { + if ((style & SWT.FLAT) != 0) return this; + return background == -1 && backgroundImage == null ? this : super.findThemeControl (); +} + +int getMargin (int index) { + int margin = 0; + if (OS.COMCTL32_MAJOR >= 6) { + MARGINS margins = new MARGINS (); + OS.SendMessage (handle, OS.RB_GETBANDMARGINS, 0, margins); + margin += margins.cxLeftWidth + margins.cxRightWidth; + } + RECT rect = new RECT (); + OS.SendMessage (handle, OS.RB_GETBANDBORDERS, index, rect); + if ((style & SWT.FLAT) != 0) { + /* + * Bug in Windows. When the style bit RBS_BANDBORDERS is not set + * the rectangle returned by RBS_BANDBORDERS is four pixels too small. + * The fix is to add four pixels to the result. + */ + if ((style & SWT.VERTICAL) != 0) { + margin += rect.top + 4; + } else { + margin += rect.left + 4; + } + } else { + if ((style & SWT.VERTICAL) != 0) { + margin += rect.top + rect.bottom; + } else { + margin += rect.left + rect.right; + } + } + if ((style & SWT.FLAT) == 0) { + if (!isLastItemOfRow (index)) { + margin += CoolBar.SEPARATOR_WIDTH; + } + } + return margin; +} + +/** + * Returns the item that is currently displayed at the given, + * zero-relative index. Throws an exception if the index is + * out of range. + * + * @param index the visual index of the item to return + * @return the item at the given visual 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 CoolItem getItem (int index) { + checkWidget (); + int count = (int)/*64*/OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0); + if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE); + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_ID; + OS.SendMessage (handle, OS.RB_GETBANDINFO, index, rbBand); + return items [rbBand.wID]; +} + +/** + * 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 (int)/*64*/OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0); +} + +/** + * Returns an array of zero-relative ints 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> + */ +public int [] getItemOrder () { + checkWidget (); + int count = (int)/*64*/OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0); + int [] indices = new int [count]; + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_ID; + for (int i=0; i<count; i++) { + OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand); + CoolItem item = items [rbBand.wID]; + int index = 0; + while (index<originalItems.length) { + if (originalItems [index] == item) break; + index++; + } + if (index == originalItems.length) error (SWT.ERROR_CANNOT_GET_ITEM); + indices [i] = index; + } + return indices; +} + +/** + * Returns an array of <code>CoolItem</code>s in the order + * in which they are currently being displayed. + * <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 in their current visual 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> + */ +public CoolItem [] getItems () { + checkWidget (); + int count = (int)/*64*/OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0); + CoolItem [] result = new CoolItem [count]; + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_ID; + for (int i=0; i<count; i++) { + OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand); + result [i] = items [rbBand.wID]; + } + return result; +} + +/** + * Returns an array of points whose x and y coordinates describe + * the widths and heights (respectively) of the items in the receiver + * in the order in which they are currently being displayed. + * + * @return the receiver's item sizes in their current visual 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> + */ +public Point [] getItemSizes () { + checkWidget (); + int count = (int)/*64*/OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0); + Point [] sizes = new Point [count]; + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_CHILDSIZE; + int separator = (style & SWT.FLAT) == 0 ? SEPARATOR_WIDTH : 0; + MARGINS margins = new MARGINS (); + for (int i=0; i<count; i++) { + RECT rect = new RECT (); + OS.SendMessage (handle, OS.RB_GETRECT, i, rect); + OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand); + if (OS.COMCTL32_MAJOR >= 6) { + OS.SendMessage (handle, OS.RB_GETBANDMARGINS, 0, margins); + rect.left -= margins.cxLeftWidth; + rect.right += margins.cxRightWidth; + } + if (!isLastItemOfRow(i)) rect.right += separator; + if ((style & SWT.VERTICAL) != 0) { + sizes [i] = new Point (rbBand.cyChild, rect.right - rect.left); + } else { + sizes [i] = new Point (rect.right - rect.left, rbBand.cyChild); + } + } + return sizes; +} + +int getLastIndexOfRow (int index) { + int count = (int)/*64*/OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0); + if (count == 0) return -1; + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_STYLE; + for (int i=index + 1; i<count; i++) { + OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand); + if ((rbBand.fStyle & OS.RBBS_BREAK) != 0) { + return i - 1; + } + } + return count - 1; +} + +boolean isLastItemOfRow (int index) { + int count = (int)/*64*/OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0); + if (index + 1 == count) return true; + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_STYLE; + OS.SendMessage (handle, OS.RB_GETBANDINFO, index + 1, rbBand); + return (rbBand.fStyle & OS.RBBS_BREAK) != 0; +} + +/** + * Returns whether or not the receiver is 'locked'. When a coolbar + * is locked, its items cannot be repositioned. + * + * @return true if the coolbar is locked, false 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 2.0 + */ +public boolean getLocked () { + checkWidget (); + return locked; +} + +/** + * Returns an array of ints that describe the zero-relative + * indices of any item(s) in the receiver that will begin on + * a new row. The 0th visible item always begins the first row, + * therefore it does not count as a wrap index. + * + * @return an array containing the receiver's wrap indices, or an empty array if all items are in one row + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if 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 [] getWrapIndices () { + checkWidget (); + CoolItem [] items = getItems (); + int [] indices = new int [items.length]; + int count = 0; + for (int i=0; i<items.length; i++) { + if (items [i].getWrap ()) indices [count++] = i; + } + int [] result = new int [count]; + System.arraycopy (indices, 0, result, 0, count); + return result; +} + +/** + * Searches the receiver's items in the order they are currently + * being displayed, 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 visual order index of the search item, or -1 if the item is not found + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the item is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the item 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> + */ +public int indexOf (CoolItem item) { + checkWidget (); + if (item == null) error (SWT.ERROR_NULL_ARGUMENT); + if (item.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT); + return (int)/*64*/OS.SendMessage (handle, OS.RB_IDTOINDEX, item.id, 0); +} + +void resizeToPreferredWidth (int index) { + /* + * Bug in Windows. When RB_GETBANDBORDERS is sent + * with an index out of range, Windows GP's. The + * fix is to ensure the index is in range. + */ + int count = (int)/*64*/OS.SendMessage(handle, OS.RB_GETBANDCOUNT, 0, 0); + if (0 <= index && index < count) { + REBARBANDINFO rbBand = new REBARBANDINFO(); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_IDEALSIZE; + OS.SendMessage (handle, OS.RB_GETBANDINFO, index, rbBand); + RECT rect = new RECT (); + OS.SendMessage (handle, OS.RB_GETBANDBORDERS, index, rect); + rbBand.cx = rbBand.cxIdeal + rect.left; + if ((style & SWT.FLAT) == 0) rbBand.cx += rect.right; + rbBand.fMask = OS.RBBIM_SIZE; + OS.SendMessage (handle, OS.RB_SETBANDINFO, index, rbBand); + } +} + +void resizeToMaximumWidth (int index) { + REBARBANDINFO rbBand = new REBARBANDINFO(); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_SIZE; + rbBand.cx = MAX_WIDTH; + OS.SendMessage (handle, OS.RB_SETBANDINFO, index, rbBand); +} + +void releaseChildren (boolean destroy) { + if (items != null) { + for (int i=0; i<items.length; i++) { + CoolItem item = items [i]; + if (item != null && !item.isDisposed ()) { + item.release (false); + } + } + items = null; + } + super.releaseChildren (destroy); +} + +void removeControl (Control control) { + super.removeControl (control); + for (int i=0; i<items.length; i++) { + CoolItem item = items [i]; + if (item != null && item.control == control) { + item.setControl (null); + } + } +} + +void setBackgroundPixel (int pixel) { + if (pixel == -1) pixel = defaultBackground (); + OS.SendMessage (handle, OS.RB_SETBKCOLOR, 0, pixel); + setItemColors ((int)/*64*/OS.SendMessage (handle, OS.RB_GETTEXTCOLOR, 0, 0), pixel); + /* + * Feature in Windows. For some reason, Windows + * does not fully erase the coolbar area and coolbar + * items when you set the background. The fix is + * to invalidate the coolbar area. + */ + if (!OS.IsWindowVisible (handle)) return; + if (OS.IsWinCE) { + OS.InvalidateRect (handle, null, true); + } else { + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, null, 0, flags); + } +} + +void setForegroundPixel (int pixel) { + if (pixel == -1) pixel = defaultForeground (); + OS.SendMessage (handle, OS.RB_SETTEXTCOLOR, 0, pixel); + setItemColors (pixel, (int)/*64*/OS.SendMessage (handle, OS.RB_GETBKCOLOR, 0, 0)); +} + +void setItemColors (int foreColor, int backColor) { + int count = (int)/*64*/OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0); + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_COLORS; + rbBand.clrFore = foreColor; + rbBand.clrBack = backColor; + for (int i=0; i<count; i++) { + OS.SendMessage (handle, OS.RB_SETBANDINFO, i, rbBand); + } +} + +/** + * Sets the receiver's item order, wrap indices, and item sizes + * all at once. This method is typically used to restore the + * displayed state of the receiver to a previously stored state. + * <p> + * The item order is the order in which the items in the receiver + * should be displayed, given in terms of the zero-relative ordering + * of when the items were added. + * </p><p> + * The wrap indices are the indices of all item(s) in the receiver + * that will begin on a new row. The indices are given in the order + * specified by the item order. The 0th item always begins the first + * row, therefore it does not count as a wrap index. If wrap indices + * is null or empty, the items will be placed on one line. + * </p><p> + * The sizes are specified in an array of points whose x and y + * coordinates describe the new widths and heights (respectively) + * of the receiver's items in the order specified by the item order. + * </p> + * + * @param itemOrder an array of indices that describe the new order to display the items in + * @param wrapIndices an array of wrap indices, or null + * @param sizes an array containing the new sizes for each of the receiver's items in visual 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> + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if item order or sizes is null</li> + * <li>ERROR_INVALID_ARGUMENT - if item order or sizes is not the same length as the number of items</li> + * </ul> + */ +public void setItemLayout (int [] itemOrder, int [] wrapIndices, Point [] sizes) { + checkWidget (); + setRedraw (false); + setItemOrder (itemOrder); + setWrapIndices (wrapIndices); + setItemSizes (sizes); + setRedraw (true); +} + +/* + * 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 itemOrder the new order to display the items in + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - 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> + */ +void setItemOrder (int [] itemOrder) { + if (itemOrder == null) error (SWT.ERROR_NULL_ARGUMENT); + int itemCount = (int)/*64*/OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0); + if (itemOrder.length != itemCount) error (SWT.ERROR_INVALID_ARGUMENT); + + /* Ensure that itemOrder does not contain any duplicates. */ + boolean [] set = new boolean [itemCount]; + for (int i=0; i<itemOrder.length; i++) { + int index = itemOrder [i]; + if (index < 0 || index >= itemCount) error (SWT.ERROR_INVALID_RANGE); + if (set [index]) error (SWT.ERROR_INVALID_ARGUMENT); + set [index] = true; + } + + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + for (int i=0; i<itemOrder.length; i++) { + int id = originalItems [itemOrder [i]].id; + int index = (int)/*64*/OS.SendMessage (handle, OS.RB_IDTOINDEX, id, 0); + if (index != i) { + int lastItemSrcRow = getLastIndexOfRow (index); + int lastItemDstRow = getLastIndexOfRow (i); + if (index == lastItemSrcRow) { + resizeToPreferredWidth (index); + } + if (i == lastItemDstRow) { + resizeToPreferredWidth (i); + } + + /* Move the item */ + OS.SendMessage (handle, OS.RB_MOVEBAND, index, i); + + if (index == lastItemSrcRow && index - 1 >= 0) { + resizeToMaximumWidth (index - 1); + } + if (i == lastItemDstRow) { + resizeToMaximumWidth (i); + } + } + } +} + +/* + * Sets the width and height of the receiver's items to the ones + * specified by the argument, which is an array of points whose x + * and y coordinates describe the widths and heights (respectively) + * in the order in which the items are currently being displayed. + * + * @param sizes an array containing the new sizes for each of the receiver's items in visual order + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the array of sizes is null</li> + * <li>ERROR_INVALID_ARGUMENT - if the array of sizes is not the same length as the number of items</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> + */ +void setItemSizes (Point [] sizes) { + if (sizes == null) error (SWT.ERROR_NULL_ARGUMENT); + int count = (int)/*64*/OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0); + if (sizes.length != count) error (SWT.ERROR_INVALID_ARGUMENT); + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_ID; + for (int i=0; i<count; i++) { + OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand); + items [rbBand.wID].setSize (sizes [i].x, sizes [i].y); + } +} + +/** + * Sets whether or not the receiver is 'locked'. When a coolbar + * is locked, its items cannot be repositioned. + * + * @param locked lock the coolbar if true, otherwise unlock the coolbar + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - 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 setLocked (boolean locked) { + checkWidget (); + this.locked = locked; + int count = (int)/*64*/OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0); + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_STYLE; + for (int i=0; i<count; i++) { + OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand); + if (locked) { + rbBand.fStyle |= OS.RBBS_NOGRIPPER; + } else { + rbBand.fStyle &= ~OS.RBBS_NOGRIPPER; + } + OS.SendMessage (handle, OS.RB_SETBANDINFO, i, rbBand); + } +} + +/** + * Sets the indices of all item(s) in the receiver that will + * begin on a new row. The indices are given in the order in + * which they are currently being displayed. The 0th item + * always begins the first row, therefore it does not count + * as a wrap index. If indices is null or empty, the items + * will be placed on one line. + * + * @param indices an array of wrap indices, 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 setWrapIndices (int [] indices) { + checkWidget (); + if (indices == null) indices = new int [0]; + int count = getItemCount (); + for (int i=0; i<indices.length; i++) { + if (indices [i] < 0 || indices [i] >= count) { + error (SWT.ERROR_INVALID_RANGE); + } + } + setRedraw (false); + CoolItem [] items = getItems (); + for (int i=0; i<items.length; i++) { + CoolItem item = items [i]; + if (item.getWrap ()) { + resizeToPreferredWidth (i - 1); + item.setWrap (false); + } + } + resizeToMaximumWidth (count - 1); + for (int i=0; i<indices.length; i++) { + int index = indices [i]; + if (0 <= index && index < items.length) { + CoolItem item = items [index]; + item.setWrap (true); + resizeToMaximumWidth (index - 1); + } + } + setRedraw (true); +} + +int widgetStyle () { + int bits = super.widgetStyle () | OS.CCS_NODIVIDER | OS.CCS_NORESIZE; + bits |= OS.RBS_VARHEIGHT | OS.RBS_DBLCLKTOGGLE; + if ((style & SWT.FLAT) == 0) bits |= OS.RBS_BANDBORDERS; + return bits; +} + +TCHAR windowClass () { + return ReBarClass; +} + +int /*long*/ windowProc () { + return ReBarProc; +} + +LRESULT WM_COMMAND (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. When the coolbar window + * proc processes WM_COMMAND, it forwards this + * message to its parent. This is done so that + * children of this control that send this message + * type to their parent will notify not only + * this control but also the parent of this control, + * which is typically the application window and + * the window that is looking for the message. + * If the control did not forward the message, + * applications would have to subclass the control + * window to see the message. Because the control + * window is subclassed by SWT, the message + * is delivered twice, once by SWT and once when + * the message is forwarded by the window proc. + * The fix is to avoid calling the window proc + * for this control. + */ + LRESULT result = super.WM_COMMAND (wParam, lParam); + if (result != null) return result; + return LRESULT.ZERO; +} + +LRESULT WM_ERASEBKGND (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_ERASEBKGND (wParam, lParam); + /* + * Feature in Windows. For some reason, Windows + * does not fully erase the area that the cool bar + * occupies when the size of the cool bar is larger + * than the space occupied by the cool bar items. + * The fix is to erase the cool bar background. + * + * NOTE: On versions of Windows prior to XP, for + * some reason, the cool bar draws separators in + * WM_ERASEBKGND. Therefore it is essential to run + * the cool bar window proc after the background has + * been erased. + */ + if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) { + drawBackground (wParam); + return null; + } + return result; +} + +LRESULT WM_NOTIFY (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. When the cool bar window + * proc processes WM_NOTIFY, it forwards this + * message to its parent. This is done so that + * children of this control that send this message + * type to their parent will notify not only + * this control but also the parent of this control, + * which is typically the application window and + * the window that is looking for the message. + * If the control did not forward the message, + * applications would have to subclass the control + * window to see the message. Because the control + * window is subclassed by SWT, the message + * is delivered twice, once by SWT and once when + * the message is forwarded by the window proc. + * The fix is to avoid calling the window proc + * for this control. + */ + LRESULT result = super.WM_NOTIFY (wParam, lParam); + if (result != null) return result; + return LRESULT.ZERO; +} + +LRESULT WM_SETREDRAW (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETREDRAW (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. When redraw is turned off, the rebar + * control does not call the default window proc. This means + * that the rebar will redraw and children of the rebar will + * also redraw. The fix is to call both the rebar window proc + * and the default window proc. + * + * NOTE: The rebar control can resize itself in WM_SETREDRAW. + * When redraw is turned off by the default window proc, this + * can leave pixel corruption in the parent. The fix is to + * detect the size change and damage the previous area in the + * parent. + * + * NOTE: In version 6.00 of COMCTL32.DLL, when WM_SETREDRAW + * is off, we cannot detect that the size has changed causing + * pixel corruption. The fix is to disallow WM_SETREDRAW by + * not running the default window proc or the rebar window + * proc. + */ + if (OS.COMCTL32_MAJOR >= 6) return LRESULT.ZERO; + Rectangle rect = getBounds (); + int /*long*/ code = callWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam); + OS.DefWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam); + if (!rect.equals (getBounds ())) { + parent.redraw (rect.x, rect.y, rect.width, rect.height, true); + } + return new LRESULT (code); +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreResize) { + int /*long*/ code = callWindowProc (handle, OS.WM_SIZE, wParam, lParam); + if (code == 0) return LRESULT.ZERO; + return new LRESULT (code); + } + //TEMPORARY CODE +// if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { +// if (background == -1 && (style & SWT.FLAT) == 0) { +// OS.InvalidateRect (handle, null, true); +// } +// } + return super.WM_SIZE (wParam, lParam); +} + +LRESULT wmNotifyChild (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + switch (hdr.code) { + case OS.RBN_BEGINDRAG: { + int pos = OS.GetMessagePos (); + POINT pt = new POINT (); + OS.POINTSTOPOINT (pt, pos); + OS.ScreenToClient (handle, pt); + int button = display.lastButton != 0 ? display.lastButton : 1; + if (!sendDragEvent (button, pt.x, pt.y)) return LRESULT.ONE; + break; + } + case OS.RBN_CHILDSIZE: { + /* + * Bug in Windows. When Windows sets the size of the rebar band + * child and the child is a combo box, the size of the drop down + * portion of the combo box is resized to zero. The fix is to set + * the size of the control to the current size after the rebar has + * already resized it. If the control is not a combo, this does + * nothing. If the control is a combo, the drop down portion is + * recalculated. + */ + NMREBARCHILDSIZE lprbcs = new NMREBARCHILDSIZE (); + OS.MoveMemory (lprbcs, lParam, NMREBARCHILDSIZE.sizeof); + if (lprbcs.uBand != -1) { + CoolItem item = items [lprbcs.wID]; + Control control = item.control; + if (control != null) { + int width = lprbcs.rcChild_right - lprbcs.rcChild_left; + int height = lprbcs.rcChild_bottom - lprbcs.rcChild_top; + control.setBounds (lprbcs.rcChild_left, lprbcs.rcChild_top, width, height); + } + } + break; + } + case OS.RBN_HEIGHTCHANGE: { + if (!ignoreResize) { + Point size = getSize (); + int border = getBorderWidth (); + int barHeight = (int)/*64*/OS.SendMessage (handle, OS.RB_GETBARHEIGHT, 0, 0); + if ((style & SWT.VERTICAL) != 0) { + setSize (barHeight + 2 * border, size.y); + } else { + setSize (size.x, barHeight + 2 * border); + } + } + break; + } + case OS.RBN_CHEVRONPUSHED: { + NMREBARCHEVRON lpnm = new NMREBARCHEVRON (); + OS.MoveMemory (lpnm, lParam, NMREBARCHEVRON.sizeof); + CoolItem item = items [lpnm.wID]; + if (item != null) { + Event event = new Event(); + event.detail = SWT.ARROW; + if ((style & SWT.VERTICAL) != 0) { + event.x = lpnm.right; + event.y = lpnm.top; + } else { + event.x = lpnm.left; + event.y = lpnm.bottom; + } + item.postEvent (SWT.Selection, event); + } + break; + } + case OS.NM_CUSTOMDRAW: { + /* + * Bug in Windows. On versions of Windows prior to XP, + * drawing the background color in NM_CUSTOMDRAW erases + * the separators. The fix is to draw the background + * in WM_ERASEBKGND. + */ + if (OS.COMCTL32_MAJOR < 6) break; + if (findBackgroundControl () != null || (style & SWT.FLAT) != 0) { + NMCUSTOMDRAW nmcd = new NMCUSTOMDRAW (); + OS.MoveMemory (nmcd, lParam, NMCUSTOMDRAW.sizeof); + switch (nmcd.dwDrawStage) { + case OS.CDDS_PREERASE: + return new LRESULT (OS.CDRF_SKIPDEFAULT | OS.CDRF_NOTIFYPOSTERASE); + case OS.CDDS_POSTERASE: + drawBackground (nmcd.hdc); + break; + } + } + break; + } + } + return super.wmNotifyChild (hdr, wParam, lParam); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/CoolItem.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/CoolItem.java new file mode 100755 index 0000000000..3b21e1ab53 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/CoolItem.java @@ -0,0 +1,717 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * Instances of this class are selectable user interface + * objects that represent the dynamically positionable + * areas of a <code>CoolBar</code>. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>DROP_DOWN</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/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ + +public class CoolItem extends Item { + CoolBar parent; + Control control; + int id; + boolean ideal, minimum; + +/** + * Constructs a new instance of this class given its parent + * (which must be a <code>CoolBar</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#DROP_DOWN + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public CoolItem (CoolBar 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>CoolBar</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 at which 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#DROP_DOWN + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public CoolItem (CoolBar parent, int style, int index) { + super (parent, style); + this.parent = parent; + parent.createItem (this, index); +} + +/** + * Adds the listener to the collection of listeners that 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> + * If <code>widgetSelected</code> is called when the mouse is over + * the drop-down arrow (or 'chevron') portion of the cool item, + * the event object detail field contains the value <code>SWT.ARROW</code>, + * and the x and y fields in the event object represent the point at + * the bottom left of the chevron, where the menu should be popped up. + * <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 + * + * @since 2.0 + */ +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); +} + +/** + * Returns the preferred size of the receiver. + * <p> + * The <em>preferred size</em> of a <code>CoolItem</code> is the size that + * it would best be displayed at. The width hint and height hint arguments + * allow the caller to ask the instance questions such as "Given a particular + * width, how high does it 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 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - 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 #getBounds + * @see #getSize + * @see Control#getBorderWidth + * @see Scrollable#computeTrim + * @see Scrollable#getClientArea + */ +public Point computeSize (int wHint, int hHint) { + checkWidget (); + int index = parent.indexOf (this); + if (index == -1) return new Point (0, 0); + int width = wHint, height = hHint; + if (wHint == SWT.DEFAULT) width = 32; + if (hHint == SWT.DEFAULT) height = 32; + if ((parent.style & SWT.VERTICAL) != 0) { + height += parent.getMargin (index); + } else { + width += parent.getMargin (index); + } + return new Point (width, height); +} + +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> + */ +public Rectangle getBounds () { + checkWidget (); + int index = parent.indexOf (this); + if (index == -1) return new Rectangle (0, 0, 0, 0); + int /*long*/ hwnd = parent.handle; + RECT rect = new RECT (); + OS.SendMessage (hwnd, OS.RB_GETRECT, index, rect); + if (OS.COMCTL32_MAJOR >= 6) { + MARGINS margins = new MARGINS (); + OS.SendMessage (hwnd, OS.RB_GETBANDMARGINS, 0, margins); + rect.left -= margins.cxLeftWidth; + rect.right += margins.cxRightWidth; + } + if (!parent.isLastItemOfRow (index)) { + rect.right += (parent.style & SWT.FLAT) == 0 ? CoolBar.SEPARATOR_WIDTH : 0; + } + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + if ((parent.style & SWT.VERTICAL) != 0) { + return new Rectangle (rect.top, rect.left, height, width); + } + return new Rectangle (rect.left, rect.top, width, height); +} + +Rectangle getClientArea () { + checkWidget (); + int index = parent.indexOf (this); + if (index == -1) return new Rectangle (0, 0, 0, 0); + int /*long*/ hwnd = parent.handle; + RECT insetRect = new RECT (); + OS.SendMessage (hwnd, OS.RB_GETBANDBORDERS, index, insetRect); + RECT rect = new RECT (); + OS.SendMessage (hwnd, OS.RB_GETRECT, index, rect); + int x = rect.left + insetRect.left; + int y = rect.top; + int width = rect.right - rect.left - insetRect.left; + int height = rect.bottom - rect.top; + if ((parent.style & SWT.FLAT) == 0) { + y += insetRect.top; + width -= insetRect.right; + height -= insetRect.top + insetRect.bottom; + } + if (index == 0) { + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_HEADERSIZE; + OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand); + width = width - rbBand.cxHeader + 1; + } + return new Rectangle (x, y, Math.max (0, width), Math.max (0, height)); +} + +/** + * Returns the control that is associated with the receiver. + * + * @return the control that is contained by 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 Control getControl () { + checkWidget (); + return control; +} + +/** + * Returns the receiver's parent, which must be a <code>CoolBar</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 CoolBar getParent () { + checkWidget (); + return parent; +} + +void releaseHandle () { + super.releaseHandle (); + parent = null; + id = -1; + control = null; +} + +/** + * Sets the control that is associated with the receiver + * to the argument. + * + * @param control the new control that will be contained by the receiver + * + * @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); + } + int index = parent.indexOf (this); + if (index == -1) return; + if (this.control != null && this.control.isDisposed ()) { + this.control = null; + } + Control oldControl = this.control, newControl = control; + int /*long*/ hwnd = parent.handle; + int /*long*/ hwndChild = newControl != null ? control.topHandle () : 0; + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_CHILD; + rbBand.hwndChild = hwndChild; + this.control = newControl; + + /* + * Feature in Windows. When Windows sets the rebar band child, + * it makes the new child visible and hides the old child and + * moves the new child to the top of the Z-order. The fix is + * to save and restore the visibility and Z-order. + */ + int /*long*/ hwndAbove = 0; + if (newControl != null) { + hwndAbove = OS.GetWindow (hwndChild, OS.GW_HWNDPREV); + } + boolean hideNew = newControl != null && !newControl.getVisible (); + boolean showOld = oldControl != null && oldControl.getVisible (); + OS.SendMessage (hwnd, OS.RB_SETBANDINFO, index, rbBand); + if (hideNew) newControl.setVisible (false); + if (showOld) oldControl.setVisible (true); + if (hwndAbove != 0 && hwndAbove != hwndChild) { + int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE; + SetWindowPos (hwndChild, hwndAbove, 0, 0, 0, 0, flags); + } +} + +/** + * Returns a point describing the receiver's ideal size. + * The x coordinate of the result is the ideal width of the receiver. + * The y coordinate of the result is the ideal height of the receiver. + * + * @return the receiver's ideal 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 getPreferredSize () { + checkWidget (); + int index = parent.indexOf (this); + if (index == -1) return new Point (0, 0); + int /*long*/ hwnd = parent.handle; + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_CHILDSIZE | OS.RBBIM_IDEALSIZE; + OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand); + int width = rbBand.cxIdeal + parent.getMargin (index); + if ((parent.style & SWT.VERTICAL) != 0) { + return new Point (rbBand.cyMaxChild, width); + } + return new Point (width, rbBand.cyMaxChild); +} + +/** + * Sets the receiver's ideal size to the point specified by the arguments. + * + * @param width the new ideal width for the receiver + * @param height the new ideal 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 setPreferredSize (int width, int height) { + checkWidget (); + int index = parent.indexOf (this); + if (index == -1) return; + width = Math.max (0, width); + height = Math.max (0, height); + ideal = true; + int /*long*/ hwnd = parent.handle; + int cxIdeal, cyMaxChild; + if ((parent.style & SWT.VERTICAL) != 0) { + cxIdeal = Math.max (0, height - parent.getMargin (index)); + cyMaxChild = width; + } else { + cxIdeal = Math.max (0, width - parent.getMargin (index)); + cyMaxChild = height; + } + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + + /* Get the child size fields first so we don't overwrite them. */ + rbBand.fMask = OS.RBBIM_CHILDSIZE; + OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand); + + /* Set the size fields we are currently modifying. */ + rbBand.fMask = OS.RBBIM_CHILDSIZE | OS.RBBIM_IDEALSIZE; + rbBand.cxIdeal = cxIdeal; + rbBand.cyMaxChild = cyMaxChild; + if (!minimum) rbBand.cyMinChild = cyMaxChild; + OS.SendMessage (hwnd, OS.RB_SETBANDINFO, index, rbBand); +} + +/** + * Sets the receiver's ideal size to the point specified by the argument. + * + * @param size the new ideal 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 setPreferredSize (Point size) { + checkWidget (); + if (size == null) error(SWT.ERROR_NULL_ARGUMENT); + setPreferredSize (size.x, size.y); +} + +/** + * 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 (); + int index = parent.indexOf (this); + if (index == -1) new Point (0, 0); + int /*long*/ hwnd = parent.handle; + RECT rect = new RECT (); + OS.SendMessage (hwnd, OS.RB_GETRECT, index, rect); + if (OS.COMCTL32_MAJOR >= 6) { + MARGINS margins = new MARGINS (); + OS.SendMessage (hwnd, OS.RB_GETBANDMARGINS, 0, margins); + rect.left -= margins.cxLeftWidth; + rect.right += margins.cxRightWidth; + } + if (!parent.isLastItemOfRow (index)) { + rect.right += (parent.style & SWT.FLAT) == 0 ? CoolBar.SEPARATOR_WIDTH : 0; + } + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + if ((parent.style & SWT.VERTICAL) != 0) { + return new Point (height, width); + } + return new Point (width, height); +} + +/** + * 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 (); + int index = parent.indexOf (this); + if (index == -1) return; + width = Math.max (0, width); + height = Math.max (0, height); + int /*long*/ hwnd = parent.handle; + int cx, cyChild, cxIdeal; + if ((parent.style & SWT.VERTICAL) != 0) { + cx = height; + cyChild = width; + cxIdeal = Math.max (0, height - parent.getMargin (index)); + } else { + cx = width; + cyChild = height; + cxIdeal = Math.max (0, width - parent.getMargin (index)); + } + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + + /* Get the child size fields first so we don't overwrite them. */ + rbBand.fMask = OS.RBBIM_CHILDSIZE | OS.RBBIM_IDEALSIZE; + OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand); + + /* Set the size fields we are currently modifying. */ + if (!ideal) rbBand.cxIdeal = cxIdeal; + if (!minimum) rbBand.cyMinChild = cyChild; + rbBand.cyChild = cyChild; + + /* + * Do not set the size for the last item on the row. + */ + if (!parent.isLastItemOfRow (index)) { + if (OS.COMCTL32_MAJOR >= 6) { + MARGINS margins = new MARGINS (); + OS.SendMessage (hwnd, OS.RB_GETBANDMARGINS, 0, margins); + cx -= margins.cxLeftWidth + margins.cxRightWidth; + } + int separator = (parent.style & SWT.FLAT) == 0 ? CoolBar.SEPARATOR_WIDTH : 0; + rbBand.cx = cx - separator; + rbBand.fMask |= OS.RBBIM_SIZE; + } + OS.SendMessage (hwnd, OS.RB_SETBANDINFO, index, rbBand); +} + +/** + * 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) { + if (size == null) error(SWT.ERROR_NULL_ARGUMENT); + setSize (size.x, size.y); +} + +/** + * Returns the minimum size that the cool item can + * be resized to using the cool item's gripper. + * + * @return a point containing the minimum width and height of the cool item, 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 2.0 + */ +public Point getMinimumSize () { + checkWidget (); + int index = parent.indexOf (this); + if (index == -1) return new Point (0, 0); + int /*long*/ hwnd = parent.handle; + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_CHILDSIZE; + OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand); + if ((parent.style & SWT.VERTICAL) != 0) { + return new Point (rbBand.cyMinChild, rbBand.cxMinChild); + } + return new Point (rbBand.cxMinChild, rbBand.cyMinChild); +} + +/** + * Sets the minimum size that the cool item can be resized to + * using the cool item's gripper, to the point specified by the arguments. + * + * @param width the minimum width of the cool item, in pixels + * @param height the minimum height of the cool item, 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 2.0 + */ +public void setMinimumSize (int width, int height) { + checkWidget (); + int index = parent.indexOf (this); + if (index == -1) return; + width = Math.max (0, width); + height = Math.max (0, height); + minimum = true; + int /*long*/ hwnd = parent.handle; + int cxMinChild, cyMinChild; + if ((parent.style & SWT.VERTICAL) != 0) { + cxMinChild = height; + cyMinChild = width; + } else { + cxMinChild = width; + cyMinChild = height; + } + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + + /* Get the child size fields first so we don't overwrite them. */ + rbBand.fMask = OS.RBBIM_CHILDSIZE; + OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand); + + /* Set the size fields we are currently modifying. */ + rbBand.cxMinChild = cxMinChild; + rbBand.cyMinChild = cyMinChild; + OS.SendMessage (hwnd, OS.RB_SETBANDINFO, index, rbBand); +} + +/** + * Sets the minimum size that the cool item can be resized to + * using the cool item's gripper, to the point specified by the argument. + * + * @param size a point representing the minimum width and height of the cool item, in pixels + * + * @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.0 + */ +public void setMinimumSize (Point size) { + checkWidget (); + if (size == null) error (SWT.ERROR_NULL_ARGUMENT); + setMinimumSize (size.x, size.y); +} + +boolean getWrap() { + int index = parent.indexOf (this); + int /*long*/ hwnd = parent.handle; + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_STYLE; + OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand); + return (rbBand.fStyle & OS.RBBS_BREAK) != 0; +} + +void setWrap(boolean wrap) { + int index = parent.indexOf (this); + int /*long*/ hwnd = parent.handle; + REBARBANDINFO rbBand = new REBARBANDINFO (); + rbBand.cbSize = REBARBANDINFO.sizeof; + rbBand.fMask = OS.RBBIM_STYLE; + OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand); + if (wrap) { + rbBand.fStyle |= OS.RBBS_BREAK; + } else { + rbBand.fStyle &= ~OS.RBBS_BREAK; + } + OS.SendMessage (hwnd, OS.RB_SETBANDINFO, index, rbBand); +} + +/** + * Removes the listener from the collection of listeners that + * 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 + * + * @since 2.0 + */ +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); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/DateTime.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/DateTime.java new file mode 100644 index 0000000000..541ef00dfd --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/DateTime.java @@ -0,0 +1,1048 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; + +/** + * 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 { + boolean doubleClick, ignoreSelection; + SYSTEMTIME lastSystemTime; + SYSTEMTIME time = new SYSTEMTIME (); // only used in calendar mode + static final int /*long*/ DateTimeProc; + static final TCHAR DateTimeClass = new TCHAR (0, OS.DATETIMEPICK_CLASS, true); + static final int /*long*/ CalendarProc; + static final TCHAR CalendarClass = new TCHAR (0, OS.MONTHCAL_CLASS, true); + static { + INITCOMMONCONTROLSEX icex = new INITCOMMONCONTROLSEX (); + icex.dwSize = INITCOMMONCONTROLSEX.sizeof; + icex.dwICC = OS.ICC_DATE_CLASSES; + OS.InitCommonControlsEx (icex); + } + static { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, DateTimeClass, lpWndClass); + DateTimeProc = lpWndClass.lpfnWndProc; + /* + * Feature in Windows. The date time window class + * does not include CS_DBLCLKS. This means that these + * controls will not get double click messages such as + * WM_LBUTTONDBLCLK. The fix is to register a new + * window class with CS_DBLCLKS. + * + * NOTE: Screen readers look for the exact class name + * of the control in order to provide the correct kind + * of assistance. Therefore, it is critical that the + * new window class have the same name. It is possible + * to register a local window class with the same name + * as a global class. Since bits that affect the class + * are being changed, it is possible that other native + * code, other than SWT, could create a control with + * this class name, and fail unexpectedly. + */ + int /*long*/ hInstance = OS.GetModuleHandle (null); + int /*long*/ hHeap = OS.GetProcessHeap (); + lpWndClass.hInstance = hInstance; + lpWndClass.style &= ~OS.CS_GLOBALCLASS; + lpWndClass.style |= OS.CS_DBLCLKS; + int byteCount = DateTimeClass.length () * TCHAR.sizeof; + int /*long*/ lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (lpszClassName, DateTimeClass, byteCount); + lpWndClass.lpszClassName = lpszClassName; + OS.RegisterClass (lpWndClass); + OS.HeapFree (hHeap, 0, lpszClassName); + } + static { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, CalendarClass, lpWndClass); + CalendarProc = lpWndClass.lpfnWndProc; + /* + * Feature in Windows. The date time window class + * does not include CS_DBLCLKS. This means that these + * controls will not get double click messages such as + * WM_LBUTTONDBLCLK. The fix is to register a new + * window class with CS_DBLCLKS. + * + * NOTE: Screen readers look for the exact class name + * of the control in order to provide the correct kind + * of assistance. Therefore, it is critical that the + * new window class have the same name. It is possible + * to register a local window class with the same name + * as a global class. Since bits that affect the class + * are being changed, it is possible that other native + * code, other than SWT, could create a control with + * this class name, and fail unexpectedly. + */ + int /*long*/ hInstance = OS.GetModuleHandle (null); + int /*long*/ hHeap = OS.GetProcessHeap (); + lpWndClass.hInstance = hInstance; + lpWndClass.style &= ~OS.CS_GLOBALCLASS; + lpWndClass.style |= OS.CS_DBLCLKS; + int byteCount = CalendarClass.length () * TCHAR.sizeof; + int /*long*/ lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (lpszClassName, CalendarClass, byteCount); + lpWndClass.lpszClassName = lpszClassName; + OS.RegisterClass (lpWndClass); + OS.HeapFree (hHeap, 0, lpszClassName); + } + static final int MARGIN = 4; + static final int MAX_DIGIT = 9; + static final int MAX_DAY = 31; + static final int MAX_12HOUR = 12; + static final int MAX_24HOUR = 24; + static final int MAX_MINUTE = 60; + static final int MONTH_DAY_YEAR = 0; + static final int DAY_MONTH_YEAR = 1; + static final int YEAR_MONTH_DAY = 2; + static final char SINGLE_QUOTE = '\''; //$NON-NLS-1$ short date format may include quoted text + static final char DAY_FORMAT_CONSTANT = 'd'; //$NON-NLS-1$ 1-4 lowercase 'd's represent day + static final char MONTH_FORMAT_CONSTANT = 'M'; //$NON-NLS-1$ 1-4 uppercase 'M's represent month + static final char YEAR_FORMAT_CONSTANT = 'y'; //$NON-NLS-1$ 1-5 lowercase 'y's represent year + static final char HOURS_FORMAT_CONSTANT = 'h'; //$NON-NLS-1$ 1-2 upper or lowercase 'h's represent hours + static final char MINUTES_FORMAT_CONSTANT = 'm'; //$NON-NLS-1$ 1-2 lowercase 'm's represent minutes + static final char SECONDS_FORMAT_CONSTANT = 's'; //$NON-NLS-1$ 1-2 lowercase 's's represent seconds + static final char AMPM_FORMAT_CONSTANT = 't'; //$NON-NLS-1$ 1-2 lowercase 't's represent am/pm + static final int[] MONTH_NAMES = new int[] {OS.LOCALE_SMONTHNAME1, OS.LOCALE_SMONTHNAME2, OS.LOCALE_SMONTHNAME3, OS.LOCALE_SMONTHNAME4, OS.LOCALE_SMONTHNAME5, OS.LOCALE_SMONTHNAME6, OS.LOCALE_SMONTHNAME7, OS.LOCALE_SMONTHNAME8, OS.LOCALE_SMONTHNAME9, OS.LOCALE_SMONTHNAME10, OS.LOCALE_SMONTHNAME11, OS.LOCALE_SMONTHNAME12}; + + +/** + * 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)); + if ((this.style & SWT.SHORT) != 0) { + String buffer = ((this.style & SWT.DATE) != 0) ? getCustomShortDateFormat() : getCustomShortTimeFormat(); + TCHAR lpszFormat = new TCHAR (0, buffer, true); + OS.SendMessage (handle, OS.DTM_SETFORMAT, 0, lpszFormat); + } +} + +/** + * 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); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + return OS.CallWindowProc (windowProc (), hwnd, msg, wParam, lParam); +} + +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.DATE, SWT.TIME, SWT.CALENDAR, 0, 0, 0); + style = checkBits (style, SWT.MEDIUM, SWT.SHORT, SWT.LONG, 0, 0, 0); + if ((style & SWT.DATE) == 0) style &=~ SWT.DROP_DOWN; + return style; +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + int width = 0, height = 0; + if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) { + if ((style & SWT.CALENDAR) != 0) { + RECT rect = new RECT (); + OS.SendMessage(handle, OS.MCM_GETMINREQRECT, 0, rect); + width = rect.right; + height = rect.bottom; + } else { + TCHAR buffer = new TCHAR (getCodePage (), 128); + int /*long*/ newFont, oldFont = 0; + int /*long*/ hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + RECT rect = new RECT (); + int flags = OS.DT_CALCRECT | OS.DT_EDITCONTROL | OS.DT_NOPREFIX; + SYSTEMTIME systime = new SYSTEMTIME (); + if ((style & SWT.DATE) != 0) { + /* Determine the widest/tallest year string. */ + systime.wMonth = 1; + systime.wDay = 1; + int widest = 0, secondWidest = 0, thirdWidest = 0; + for (int i = 0; i <= MAX_DIGIT; i++) { + systime.wYear = (short) (2000 + i); // year 2000 + i is guaranteed to exist + int size = OS.GetDateFormat(OS.LOCALE_USER_DEFAULT, OS.DATE_SHORTDATE, systime, null, buffer, buffer.length ()); + if (size == 0) { + buffer = new TCHAR (getCodePage (), size); + OS.GetDateFormat(OS.LOCALE_USER_DEFAULT, OS.DATE_SHORTDATE, systime, null, buffer, buffer.length ()); + } + rect.left = rect.top = rect.right = rect.bottom = 0; + OS.DrawText (hDC, buffer, size, rect, flags); + if (rect.right - rect.left >= width) { + width = rect.right - rect.left; + thirdWidest = secondWidest; + secondWidest = widest; + widest = i; + } + height = Math.max(height, rect.bottom - rect.top); + } + if (widest > 1) widest = widest * 1000 + widest * 100 + widest * 10 + widest; + else if (secondWidest > 1) widest = secondWidest * 1000 + widest * 100 + widest * 10 + widest; + else widest = thirdWidest * 1000 + widest * 100 + widest * 10 + widest; + systime.wYear = (short) widest; + + /* Determine the widest/tallest month name string. */ + width = widest = 0; + for (short i = 0; i < MONTH_NAMES.length; i++) { + int name = MONTH_NAMES [i]; + int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, name, buffer, buffer.length ()); + if (size == 0) { + buffer = new TCHAR (getCodePage (), size); + OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, name, buffer, buffer.length ()); + } + rect.left = rect.top = rect.right = rect.bottom = 0; + OS.DrawText (hDC, buffer, size, rect, flags); + if (rect.right - rect.left > width) { + width = rect.right - rect.left; + widest = i; + } + height = Math.max(height, rect.bottom - rect.top); + } + systime.wMonth = (short) (widest + 1); + + /* Determine the widest/tallest date string in the widest month of the widest year. */ + int dwFlags = ((style & SWT.MEDIUM) != 0) ? OS.DATE_SHORTDATE : ((style & SWT.SHORT) != 0) ? OS.DATE_YEARMONTH : OS.DATE_LONGDATE; + width = 0; + for (short i = 1; i <= MAX_DAY; i++) { + systime.wDay = i; + int size = OS.GetDateFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ()); + if (size == 0) { + buffer = new TCHAR (getCodePage (), size); + OS.GetDateFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ()); + } + rect.left = rect.top = rect.right = rect.bottom = 0; + OS.DrawText (hDC, buffer, size, rect, flags); + width = Math.max(width, rect.right - rect.left); + height = Math.max(height, rect.bottom - rect.top); + if ((style & SWT.SHORT) != 0) break; + } + } else if ((style & SWT.TIME) != 0) { + /* Determine the widest/tallest hour string. This code allows for the possibility of ligatures. */ + int dwFlags = ((style & SWT.SHORT) != 0) ? OS.TIME_NOSECONDS : 0; + short widest = 0; + int max = is24HourTime () ? MAX_24HOUR : MAX_12HOUR; + for (short i = 0; i < max; i++) { + systime.wHour = i; + int size = OS.GetTimeFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ()); + if (size == 0) { + buffer = new TCHAR (getCodePage (), size); + OS.GetTimeFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ()); + } + rect.left = rect.top = rect.right = rect.bottom = 0; + OS.DrawText (hDC, buffer, size, rect, flags); + if (rect.right - rect.left > width) { + width = rect.right - rect.left; + widest = i; + } + height = Math.max(height, rect.bottom - rect.top); + } + systime.wHour = widest; + + /* Determine the widest/tallest minute and second string. */ + width = widest = 0; + for (short i = 0; i < MAX_MINUTE; i++) { + systime.wMinute = i; + int size = OS.GetTimeFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ()); + if (size == 0) { + buffer = new TCHAR (getCodePage (), size); + OS.GetTimeFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ()); + } + rect.left = rect.top = rect.right = rect.bottom = 0; + OS.DrawText (hDC, buffer, size, rect, flags); + if (rect.right - rect.left > width) { + width = rect.right - rect.left; + widest = i; + } + height = Math.max(height, rect.bottom - rect.top); + } + systime.wMinute = widest; + systime.wSecond = widest; + + /* Determine the widest/tallest time string for the widest hour, widest minute, and if applicable, widest second. */ + int size = OS.GetTimeFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ()); + if (size == 0) { + buffer = new TCHAR (getCodePage (), size); + OS.GetTimeFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ()); + } + rect.left = rect.top = rect.right = rect.bottom = 0; + OS.DrawText (hDC, buffer, size, rect, flags); + width = rect.right - rect.left; + height = Math.max(height, rect.bottom - rect.top); + } + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + int upDownWidth = OS.GetSystemMetrics (OS.SM_CXVSCROLL); + int upDownHeight = OS.GetSystemMetrics (OS.SM_CYVSCROLL); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + // TODO: On Vista, can send DTM_GETDATETIMEPICKERINFO to ask the Edit control what its margins are + upDownHeight += 7; + if ((style & SWT.DROP_DOWN) != 0) upDownWidth += 16; + } + width += upDownWidth + MARGIN; + height = Math.max (height, upDownHeight); + } + } + 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 () { + super.createHandle (); + state &= ~(CANVAS | THEME_BACKGROUND); + + if ((style & SWT.BORDER) == 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); + bits &= ~(OS.WS_EX_CLIENTEDGE | OS.WS_EX_STATICEDGE); + OS.SetWindowLong (handle, OS.GWL_EXSTYLE, bits); + } +} + +int defaultBackground () { + return OS.GetSysColor (OS.COLOR_WINDOW); +} + +String getComputeSizeString () { + // TODO: Not currently used but might need for WinCE + if ((style & SWT.DATE) != 0) { + if ((style & SWT.SHORT) != 0) return getCustomShortDateFormat (); + if ((style & SWT.MEDIUM) != 0) return getShortDateFormat (); + if ((style & SWT.LONG) != 0) return getLongDateFormat (); + } + if ((style & SWT.TIME) != 0) { + if ((style & SWT.SHORT) != 0) return getCustomShortTimeFormat (); + return getTimeFormat (); + } + return ""; +} + +String getCustomShortDateFormat () { + TCHAR tchar = new TCHAR (getCodePage (), 80); + int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_SYEARMONTH, tchar, 80); + return size != 0 ? tchar.toString (0, size - 1) : "M/yyyy"; //$NON-NLS-1$ + + //TODO: Not currently used, but may need for WinCE (or if numeric short date is required) +// StringBuffer buffer = new StringBuffer (getShortDateFormat ()); +// int length = buffer.length (); +// boolean inQuotes = false; +// int start = 0, end = 0; +// while (start < length) { +// char ch = buffer.charAt (start); +// if (ch == SINGLE_QUOTE) inQuotes = !inQuotes; +// else if (ch == DAY_FORMAT_CONSTANT && !inQuotes) { +// end = start + 1; +// while (end < length && buffer.charAt (end) == DAY_FORMAT_CONSTANT) end++; +// int ordering = getShortDateFormatOrdering (); +// switch (ordering) { +// case MONTH_DAY_YEAR: +// // skip the following separator +// while (end < length && buffer.charAt (end) != YEAR_FORMAT_CONSTANT) end++; +// break; +// case DAY_MONTH_YEAR: +// // skip the following separator +// while (end < length && buffer.charAt (end) != MONTH_FORMAT_CONSTANT) end++; +// break; +// case YEAR_MONTH_DAY: +// // skip the preceding separator +// while (start > 0 && buffer.charAt (start) != MONTH_FORMAT_CONSTANT) start--; +// break; +// } +// break; +// } +// start++; +// } +// if (start < end) buffer.delete (start, end); +// return buffer.toString (); +} + +String getCustomShortTimeFormat () { + StringBuffer buffer = new StringBuffer (getTimeFormat ()); + int length = buffer.length (); + boolean inQuotes = false; + int start = 0, end = 0; + while (start < length) { + char ch = buffer.charAt (start); + if (ch == SINGLE_QUOTE) inQuotes = !inQuotes; + else if (ch == SECONDS_FORMAT_CONSTANT && !inQuotes) { + end = start + 1; + while (end < length && buffer.charAt (end) == SECONDS_FORMAT_CONSTANT) end++; + // skip the preceding separator + while (start > 0 && buffer.charAt (start) != MINUTES_FORMAT_CONSTANT) start--; + start++; + break; + } + start++; + } + if (start < end) buffer.delete (start, end); + return buffer.toString (); +} + +String getLongDateFormat () { + //TODO: Not currently used, but may need for WinCE + TCHAR tchar = new TCHAR (getCodePage (), 80); + int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_SLONGDATE, tchar, 80); + return size > 0 ? tchar.toString (0, size - 1) : "dddd, MMMM dd, yyyy"; //$NON-NLS-1$ +} + +String getShortDateFormat () { + //TODO: Not currently used, but may need for WinCE + TCHAR tchar = new TCHAR (getCodePage (), 80); + //TODO: May need to OR with LOCALE_ICENTURY + int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_SSHORTDATE, tchar, 80); + return size > 0 ? tchar.toString (0, size - 1) : "M/d/yyyy"; //$NON-NLS-1$ +} + +int getShortDateFormatOrdering () { + //TODO: Not currently used, but may need for WinCE + TCHAR tchar = new TCHAR (getCodePage (), 4); + int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_IDATE, tchar, 4); + if (size > 0) { + String number = tchar.toString (0, size - 1); + return Integer.parseInt (number); + } + return 0; +} + +String getTimeFormat () { + TCHAR tchar = new TCHAR (getCodePage (), 80); + int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_STIMEFORMAT, tchar, 80); + return size > 0 ? tchar.toString (0, size - 1) : "h:mm:ss tt"; //$NON-NLS-1$ +} + +boolean is24HourTime () { + TCHAR tchar = new TCHAR (getCodePage (), 4); + int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_ITIME, tchar, 4); + if (size > 0) { + String number = tchar.toString (0, size - 1); + return Integer.parseInt (number) != 0; + } + return true; +} + +/** + * 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 (); + SYSTEMTIME systime = new SYSTEMTIME (); + int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME; + OS.SendMessage (handle, msg, 0, systime); + return systime.wDay; +} + +/** + * 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 (); + if ((style & SWT.CALENDAR) != 0) return time.wHour; + SYSTEMTIME systime = new SYSTEMTIME (); + int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME; + OS.SendMessage (handle, msg, 0, systime); + return systime.wHour; +} + +/** + * 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 (); + if ((style & SWT.CALENDAR) != 0) return time.wMinute; + SYSTEMTIME systime = new SYSTEMTIME (); + int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME; + OS.SendMessage (handle, msg, 0, systime); + return systime.wMinute; +} + +/** + * 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 (); + SYSTEMTIME systime = new SYSTEMTIME (); + int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME; + OS.SendMessage (handle, msg, 0, systime); + return systime.wMonth - 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 (); + if ((style & SWT.CALENDAR) != 0) return time.wSecond; + SYSTEMTIME systime = new SYSTEMTIME (); + int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME; + OS.SendMessage (handle, msg, 0, systime); + return systime.wSecond; +} + +/** + * 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 (); + SYSTEMTIME systime = new SYSTEMTIME (); + int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME; + OS.SendMessage (handle, msg, 0, systime); + return systime.wYear; +} + +void releaseWidget () { + super.releaseWidget (); + lastSystemTime = 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); +} + +/** + * 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 (); + SYSTEMTIME systime = new SYSTEMTIME (); + int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME; + OS.SendMessage (handle, msg, 0, systime); + msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME; + systime.wYear = (short)year; + systime.wMonth = (short)(month + 1); + systime.wDay = (short)day; + OS.SendMessage (handle, msg, 0, systime); + lastSystemTime = null; +} + +/** + * 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 (); + SYSTEMTIME systime = new SYSTEMTIME (); + int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME; + OS.SendMessage (handle, msg, 0, systime); + msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME; + systime.wDay = (short)day; + OS.SendMessage (handle, msg, 0, systime); + lastSystemTime = null; +} + +/** + * 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 (); + SYSTEMTIME systime = new SYSTEMTIME (); + int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME; + OS.SendMessage (handle, msg, 0, systime); + msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME; + systime.wHour = (short)hours; + OS.SendMessage (handle, msg, 0, systime); + if ((style & SWT.CALENDAR) != 0 && hours >= 0 && hours <= 23) time.wHour = (short)hours; +} + +/** + * 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 (); + SYSTEMTIME systime = new SYSTEMTIME (); + int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME; + OS.SendMessage (handle, msg, 0, systime); + msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME; + systime.wMinute = (short)minutes; + OS.SendMessage (handle, msg, 0, systime); + if ((style & SWT.CALENDAR) != 0 && minutes >= 0 && minutes <= 59) time.wMinute = (short)minutes; +} + +/** + * 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 (); + SYSTEMTIME systime = new SYSTEMTIME (); + int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME; + OS.SendMessage (handle, msg, 0, systime); + msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME; + systime.wMonth = (short)(month + 1); + OS.SendMessage (handle, msg, 0, systime); + lastSystemTime = null; +} + +/** + * 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 (); + SYSTEMTIME systime = new SYSTEMTIME (); + int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME; + OS.SendMessage (handle, msg, 0, systime); + msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME; + systime.wSecond = (short)seconds; + OS.SendMessage (handle, msg, 0, systime); + if ((style & SWT.CALENDAR) != 0 && seconds >= 0 && seconds <= 59) time.wSecond = (short)seconds; +} + +/** + * 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 (); + SYSTEMTIME systime = new SYSTEMTIME (); + int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME; + OS.SendMessage (handle, msg, 0, systime); + msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME; + systime.wHour = (short)hours; + systime.wMinute = (short)minutes; + systime.wSecond = (short)seconds; + OS.SendMessage (handle, msg, 0, systime); + if ((style & SWT.CALENDAR) != 0 + && hours >= 0 && hours <= 23 + && minutes >= 0 && minutes <= 59 + && seconds >= 0 && seconds <= 59) { + time.wHour = (short)hours; + time.wMinute = (short)minutes; + time.wSecond = (short)seconds; + } +} + +/** + * 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 (); + SYSTEMTIME systime = new SYSTEMTIME (); + int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME; + OS.SendMessage (handle, msg, 0, systime); + msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME; + systime.wYear = (short)year; + OS.SendMessage (handle, msg, 0, systime); + lastSystemTime = null; +} + +int widgetStyle () { + int bits = super.widgetStyle () | OS.WS_TABSTOP; + if ((style & SWT.CALENDAR) != 0) return bits | OS.MCS_NOTODAY; + /* + * Bug in Windows: When WS_CLIPCHILDREN is set in a + * Date and Time Picker, the widget draws on top of + * the updown control. The fix is to clear the bits. + */ + bits &= ~OS.WS_CLIPCHILDREN; + if ((style & SWT.TIME) != 0) bits |= OS.DTS_TIMEFORMAT; + if ((style & SWT.DATE) != 0) { + bits |= ((style & SWT.MEDIUM) != 0 ? OS.DTS_SHORTDATECENTURYFORMAT : OS.DTS_LONGDATEFORMAT); + if ((style & SWT.DROP_DOWN) == 0) bits |= OS.DTS_UPDOWN; + } + return bits; +} + +TCHAR windowClass () { + return (style & SWT.CALENDAR) != 0 ? CalendarClass : DateTimeClass; +} + +int /*long*/ windowProc () { + return (style & SWT.CALENDAR) != 0 ? CalendarProc : DateTimeProc; +} + +LRESULT wmNotifyChild (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + switch (hdr.code) { + case OS.DTN_CLOSEUP: { + /* + * Feature in Windows. When the user selects the drop-down button, + * the DateTimePicker runs a modal loop and consumes WM_LBUTTONUP. + * This is done without adding a mouse capture. Since WM_LBUTTONUP + * is not delivered, the normal mechanism where a mouse capture is + * added on mouse down and removed when the mouse is released + * is broken, leaving an unwanted capture. The fix is to avoid + * setting capture on mouse down right after WM_LBUTTONUP is consumed. + */ + display.captureChanged = true; + break; + } + case OS.MCN_SELCHANGE: { + if (ignoreSelection) break; + SYSTEMTIME systime = new SYSTEMTIME (); + OS.SendMessage (handle, OS.MCM_GETCURSEL, 0, systime); + postEvent (SWT.Selection); + break; + } + case OS.DTN_DATETIMECHANGE: { + SYSTEMTIME systime = new SYSTEMTIME (); + OS.SendMessage (handle, OS.DTM_GETSYSTEMTIME, 0, systime); + if (lastSystemTime == null || systime.wDay != lastSystemTime.wDay || systime.wMonth != lastSystemTime.wMonth || systime.wYear != lastSystemTime.wYear) { + postEvent (SWT.Selection); + if ((style & SWT.TIME) == 0) lastSystemTime = systime; + } + break; + } + } + return super.wmNotifyChild (hdr, wParam, lParam); +} + +LRESULT WM_CHAR (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_CHAR (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. For some reason, when the + * user presses tab, return or escape, Windows beeps. + * The fix is to look for these keys and not call + * the window proc. + */ + switch ((int)/*64*/wParam) { + case SWT.CR: + postEvent (SWT.DefaultSelection); + // FALL THROUGH + case SWT.TAB: + case SWT.ESC: return LRESULT.ZERO; + } + return result; +} + +LRESULT WM_LBUTTONDBLCLK (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_LBUTTONDBLCLK (wParam, lParam); + if (isDisposed ()) return LRESULT.ZERO; + if ((style & SWT.CALENDAR) != 0) { + MCHITTESTINFO pMCHitTest = new MCHITTESTINFO (); + pMCHitTest.cbSize = MCHITTESTINFO.sizeof; + POINT pt = new POINT (); + pt.x = OS.GET_X_LPARAM (lParam); + pt.y = OS.GET_Y_LPARAM (lParam); + pMCHitTest.pt = pt; + int /*long*/ code = OS.SendMessage (handle, OS.MCM_HITTEST, 0, pMCHitTest); + if ((code & OS.MCHT_CALENDARDATE) == OS.MCHT_CALENDARDATE) doubleClick = true; + } + return result; +} + +LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam); + if (result == LRESULT.ZERO) return result; + doubleClick = false; + /* + * Feature in Windows. For some reason, the calendar control + * does not take focus on WM_LBUTTONDOWN. The fix is to + * explicitly set focus. + */ + if ((style & SWT.CALENDAR) != 0) { + if ((style & SWT.NO_FOCUS) == 0) OS.SetFocus (handle); + } + return result; +} + +LRESULT WM_LBUTTONUP (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_LBUTTONUP (wParam, lParam); + if (isDisposed ()) return LRESULT.ZERO; + if (doubleClick) postEvent (SWT.DefaultSelection); + doubleClick = false; + return result; +} + +LRESULT WM_TIMER (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_TIMER (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. For some reason, Windows sends WM_NOTIFY with + * MCN_SELCHANGE at regular intervals. This is unexpected. The fix is + * to ignore MCN_SELCHANGE during WM_TIMER. + */ + ignoreSelection = true; + int /*long*/ code = callWindowProc(handle, OS.WM_TIMER, wParam, lParam); + ignoreSelection = false; + return code == 0 ? LRESULT.ZERO : new LRESULT(code); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Decorations.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Decorations.java new file mode 100755 index 0000000000..c0eed0de0e --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Decorations.java @@ -0,0 +1,1798 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * 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, smallImage, largeImage; + Image [] images; + Menu menuBar; + Menu [] menus; + Control savedFocus; + Button defaultButton, saveDefault; + int swFlags, nAccel; + int /*long*/ hAccel; + boolean moved, resized, opened; + int oldX = OS.CW_USEDEFAULT, oldY = OS.CW_USEDEFAULT; + int oldWidth = OS.CW_USEDEFAULT, oldHeight = OS.CW_USEDEFAULT; + +/** + * Prevents uninitialized instances from being created outside the package. + */ +Decorations () { +} + +/** + * 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)); +} + +void _setMaximized (boolean maximized) { + swFlags = maximized ? OS.SW_SHOWMAXIMIZED : OS.SW_RESTORE; + if (OS.IsWinCE) { + /* + * Note: WinCE does not support SW_SHOWMAXIMIZED and SW_RESTORE. The + * workaround is to resize the window to fit the parent client area. + */ + if (maximized) { + RECT rect = new RECT (); + OS.SystemParametersInfo (OS.SPI_GETWORKAREA, 0, rect, 0); + int width = rect.right - rect.left, height = rect.bottom - rect.top; + if (OS.IsPPC) { + /* Leave space for the menu bar */ + if (menuBar != null) { + int /*long*/ hwndCB = menuBar.hwndCB; + RECT rectCB = new RECT (); + OS.GetWindowRect (hwndCB, rectCB); + height -= rectCB.bottom - rectCB.top; + } + } + int flags = OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE; + SetWindowPos (handle, 0, rect.left, rect.top, width, height, flags); + } + } else { + if (!OS.IsWindowVisible (handle)) return; + if (maximized == OS.IsZoomed (handle)) return; + OS.ShowWindow (handle, swFlags); + OS.UpdateWindow (handle); + } +} + +void _setMinimized (boolean minimized) { + if (OS.IsWinCE) return; + swFlags = minimized ? OS.SW_SHOWMINNOACTIVE : OS.SW_RESTORE; + if (!OS.IsWindowVisible (handle)) return; + if (minimized == OS.IsIconic (handle)) return; + int flags = swFlags; + if (flags == OS.SW_SHOWMINNOACTIVE && handle == OS.GetActiveWindow ()) { + flags = OS.SW_MINIMIZE; + } + OS.ShowWindow (handle, flags); + OS.UpdateWindow (handle); +} + +void addMenu (Menu menu) { + if (menus == null) menus = new Menu [4]; + for (int i=0; i<menus.length; i++) { + if (menus [i] == null) { + menus [i] = menu; + return; + } + } + Menu [] newMenus = new Menu [menus.length + 4]; + newMenus [menus.length] = menu; + System.arraycopy (menus, 0, newMenus, 0, menus.length); + menus = newMenus; +} + +void bringToTop () { + /* + * This code is intentionally commented. On some platforms, + * the ON_TOP style creates a shell that will stay on top + * of every other shell on the desktop. Using SetWindowPos () + * with HWND_TOP caused problems on Windows 98 so this code is + * commented out until this functionality is specified and + * the problems are fixed. + */ +// if ((style & SWT.ON_TOP) != 0) { +// int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE; +// OS.SetWindowPos (handle, OS.HWND_TOP, 0, 0, 0, 0, flags); +// } else { + OS.BringWindowToTop (handle); + // widget could be disposed at this point +// } +} + +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 (OS.IsWinCE) { + /* + * Feature in WinCE PPC. WS_MINIMIZEBOX or WS_MAXIMIZEBOX + * are not supposed to be used. If they are, the result + * is a button which does not repaint correctly. The fix + * is to remove this style. + */ + if ((style & SWT.MIN) != 0) style &= ~SWT.MIN; + if ((style & SWT.MAX) != 0) style &= ~SWT.MAX; + return style; + } + if ((style & (SWT.MENU | SWT.MIN | SWT.MAX | SWT.CLOSE)) != 0) { + style |= SWT.TITLE; + } + + /* + * If either WS_MINIMIZEBOX or WS_MAXIMIZEBOX are set, + * we must also set WS_SYSMENU or the buttons will not + * appear. + */ + if ((style & (SWT.MIN | SWT.MAX)) != 0) style |= SWT.CLOSE; + + /* + * Both WS_SYSMENU and WS_CAPTION must be set in order + * to for the system menu to appear. + */ + if ((style & SWT.CLOSE) != 0) style |= SWT.TITLE; + + /* + * Bug in Windows. The WS_CAPTION style must be + * set when the window is resizable or it does not + * draw properly. + */ + /* + * This code is intentionally commented. It seems + * that this problem originally in Windows 3.11, + * has been fixed in later versions. Because the + * exact nature of the drawing problem is unknown, + * keep the commented code around in case it comes + * back. + */ +// if ((style & SWT.RESIZE) != 0) style |= SWT.TITLE; + + return style; +} + +void checkBorder () { + /* Do nothing */ +} + +void checkComposited (Composite parent) { + /* Do nothing */ +} + +void checkOpened () { + if (!opened) resized = false; +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + return OS.DefMDIChildProc (hwnd, msg, wParam, lParam); +} + +void closeWidget () { + Event event = new Event (); + sendEvent (SWT.Close, event); + if (event.doit && !isDisposed ()) dispose (); +} + +int compare (ImageData data1, ImageData data2, int width, int height, int depth) { + int value1 = Math.abs (data1.width - width), value2 = Math.abs (data2.width - width); + if (value1 == value2) { + int transparent1 = data1.getTransparencyType (); + int transparent2 = data2.getTransparencyType (); + if (transparent1 == transparent2) { + if (data1.depth == data2.depth) return 0; + return data1.depth > data2.depth && data1.depth <= depth ? -1 : 1; + } + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) { + 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 value1 < value2 ? -1 : 1; +} + +Widget computeTabGroup () { + return this; +} + +Control computeTabRoot () { + return this; +} + +public Rectangle computeTrim (int x, int y, int width, int height) { + checkWidget (); + + /* Get the size of the trimmings */ + RECT rect = new RECT (); + OS.SetRect (rect, x, y, x + width, y + height); + int bits1 = OS.GetWindowLong (handle, OS.GWL_STYLE); + int bits2 = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); + boolean hasMenu = OS.IsWinCE ? false : OS.GetMenu (handle) != 0; + OS.AdjustWindowRectEx (rect, bits1, hasMenu, bits2); + + /* Get the size of the scroll bars */ + if (horizontalBar != null) rect.bottom += OS.GetSystemMetrics (OS.SM_CYHSCROLL); + if (verticalBar != null) rect.right += OS.GetSystemMetrics (OS.SM_CXVSCROLL); + + /* Compute the height of the menu bar */ + if (hasMenu) { + RECT testRect = new RECT (); + OS.SetRect (testRect, 0, 0, rect.right - rect.left, rect.bottom - rect.top); + OS.SendMessage (handle, OS.WM_NCCALCSIZE, 0, testRect); + while ((testRect.bottom - testRect.top) < height) { + if (testRect.bottom - testRect.top == 0) break; + rect.top -= OS.GetSystemMetrics (OS.SM_CYMENU) - OS.GetSystemMetrics (OS.SM_CYBORDER); + OS.SetRect (testRect, 0, 0, rect.right - rect.left, rect.bottom - rect.top); + OS.SendMessage (handle, OS.WM_NCCALCSIZE, 0, testRect); + } + } + return new Rectangle (rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); +} + +void createAccelerators () { + hAccel = nAccel = 0; + int maxAccel = 0; + MenuItem [] items = display.items; + if (menuBar == null || items == null) { + if (!OS.IsPPC) return; + maxAccel = 1; + } else { + maxAccel = OS.IsPPC ? items.length + 1 : items.length; + } + ACCEL accel = new ACCEL (); + byte [] buffer1 = new byte [ACCEL.sizeof]; + byte [] buffer2 = new byte [maxAccel * ACCEL.sizeof]; + if (menuBar != null && items != null) { + for (int i=0; i<items.length; i++) { + MenuItem item = items [i]; + if (item != null && item.accelerator != 0) { + Menu menu = item.parent; + if (menu.parent == this) { + while (menu != null && menu != menuBar) { + menu = menu.getParentMenu (); + } + if (menu == menuBar && item.fillAccel (accel)) { + OS.MoveMemory (buffer1, accel, ACCEL.sizeof); + System.arraycopy (buffer1, 0, buffer2, nAccel * ACCEL.sizeof, ACCEL.sizeof); + nAccel++; + } + } + } + } + } + if (OS.IsPPC) { + /* + * Note on WinCE PPC. Close the shell when user taps CTRL-Q. + * IDOK represents the "Done Button" which also closes the shell. + */ + accel.fVirt = (byte) (OS.FVIRTKEY | OS.FCONTROL); + accel.key = (short) 'Q'; + accel.cmd = (short) OS.IDOK; + OS.MoveMemory (buffer1, accel, ACCEL.sizeof); + System.arraycopy (buffer1, 0, buffer2, nAccel * ACCEL.sizeof, ACCEL.sizeof); + nAccel++; + } + if (nAccel != 0) hAccel = OS.CreateAcceleratorTable (buffer2, nAccel); +} + +void createHandle () { + super.createHandle (); + if (parent != null || ((style & SWT.TOOL) != 0)) { + setParent (); + setSystemMenu (); + } +} + +void createWidget () { + super.createWidget (); + swFlags = OS.IsWinCE ? OS.SW_SHOWMAXIMIZED : OS.SW_SHOWNOACTIVATE; + hAccel = -1; +} + +void destroyAccelerators () { + if (hAccel != 0 && hAccel != -1) OS.DestroyAcceleratorTable (hAccel); + hAccel = -1; +} + +public void dispose () { + if (isDisposed()) return; + if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS); + if (!(this instanceof Shell)) { + if (!traverseDecorations (true)) { + Shell shell = getShell (); + shell.setFocus (); + } + setVisible (false); + } + super.dispose (); +} + +Menu findMenu (int /*long*/ hMenu) { + if (menus == null) return null; + for (int i=0; i<menus.length; i++) { + Menu menu = menus [i]; + if (menu != null && hMenu == menu.handle) return menu; + } + return null; +} + +void fixDecorations (Decorations newDecorations, Control control, Menu [] menus) { + if (this == newDecorations) return; + if (control == savedFocus) savedFocus = null; + if (control == defaultButton) defaultButton = null; + if (control == saveDefault) saveDefault = 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); + destroyAccelerators (); + newDecorations.destroyAccelerators (); + } +} + +public Rectangle getBounds () { + checkWidget (); + if (!OS.IsWinCE) { + if (OS.IsIconic (handle)) { + WINDOWPLACEMENT lpwndpl = new WINDOWPLACEMENT (); + lpwndpl.length = WINDOWPLACEMENT.sizeof; + OS.GetWindowPlacement (handle, lpwndpl); + int width = lpwndpl.right - lpwndpl.left; + int height = lpwndpl.bottom - lpwndpl.top; + return new Rectangle (lpwndpl.left, lpwndpl.top, width, height); + } + } + return super.getBounds (); +} + +public Rectangle getClientArea () { + checkWidget (); + /* + * Note: The CommandBar is part of the client area, + * not the trim. Applications don't expect this so + * subtract the height of the CommandBar. + */ + if (OS.IsHPC) { + Rectangle rect = super.getClientArea (); + if (menuBar != null) { + int /*long*/ hwndCB = menuBar.hwndCB; + int height = OS.CommandBar_Height (hwndCB); + rect.y += height; + rect.height = Math.max (0, rect.height - height); + } + return rect; + } + if (!OS.IsWinCE) { + if (OS.IsIconic (handle)) { + WINDOWPLACEMENT lpwndpl = new WINDOWPLACEMENT (); + lpwndpl.length = WINDOWPLACEMENT.sizeof; + OS.GetWindowPlacement (handle, lpwndpl); + int width = lpwndpl.right - lpwndpl.left; + int height = lpwndpl.bottom - lpwndpl.top; + /* + * Feature in Windows. For some reason WM_NCCALCSIZE does + * not compute the client area when the window is minimized. + * The fix is to compute it using AdjustWindowRectEx() and + * GetSystemMetrics(). + * + * NOTE: This code fails to compute the correct client area + * for a minimized window where the menu bar would wrap were + * the window restored. There is no fix for this problem at + * this time. + */ + if (horizontalBar != null) width -= OS.GetSystemMetrics (OS.SM_CYHSCROLL); + if (verticalBar != null) height -= OS.GetSystemMetrics (OS.SM_CXVSCROLL); + RECT rect = new RECT (); + int bits1 = OS.GetWindowLong (handle, OS.GWL_STYLE); + int bits2 = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); + boolean hasMenu = OS.IsWinCE ? false : OS.GetMenu (handle) != 0; + OS.AdjustWindowRectEx (rect, bits1, hasMenu, bits2); + width = Math.max (0, width - (rect.right - rect.left)); + height = Math.max (0, height - (rect.bottom - rect.top)); + return new Rectangle (0, 0, width, height); + } + } + return super.getClientArea (); +} + +/** + * 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; +} + +public Point getLocation () { + checkWidget (); + if (!OS.IsWinCE) { + if (OS.IsIconic (handle)) { + WINDOWPLACEMENT lpwndpl = new WINDOWPLACEMENT (); + lpwndpl.length = WINDOWPLACEMENT.sizeof; + OS.GetWindowPlacement (handle, lpwndpl); + return new Point (lpwndpl.left, lpwndpl.top); + } + } + return super.getLocation (); +} + +/** + * 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 (); + if (OS.IsWinCE) return swFlags == OS.SW_SHOWMAXIMIZED; + if (OS.IsWindowVisible (handle)) return OS.IsZoomed (handle); + return swFlags == OS.SW_SHOWMAXIMIZED; +} + +/** + * 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 (); + if (OS.IsWinCE) return false; + if (OS.IsWindowVisible (handle)) return OS.IsIconic (handle); + return swFlags == OS.SW_SHOWMINNOACTIVE; +} + +String getNameText () { + return getText (); +} + +public Point getSize () { + checkWidget (); + if (!OS.IsWinCE) { + if (OS.IsIconic (handle)) { + WINDOWPLACEMENT lpwndpl = new WINDOWPLACEMENT (); + lpwndpl.length = WINDOWPLACEMENT.sizeof; + OS.GetWindowPlacement (handle, lpwndpl); + int width = lpwndpl.right - lpwndpl.left; + int height = lpwndpl.bottom - lpwndpl.top; + return new Point (width, height); + } + } + return super.getSize (); +} + +/** + * 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 (); + int length = OS.GetWindowTextLength (handle); + if (length == 0) return ""; + /* Use the character encoding for the default locale */ + TCHAR buffer = new TCHAR (0, length + 1); + OS.GetWindowText (handle, buffer, length + 1); + return buffer.toString (0, length); +} + +public boolean isReparentable () { + checkWidget (); + /* + * Feature in Windows. Calling SetParent() for a shell causes + * a kind of fake MDI to happen. It doesn't work well on Windows + * and is not supported on the other platforms. The fix is to + * disallow the SetParent(). + */ + return false; +} + +boolean isTabGroup () { + /* + * Can't test WS_TAB bits because they are the same as WS_MAXIMIZEBOX. + */ + return true; +} + +boolean isTabItem () { + /* + * Can't test WS_TAB bits because they are the same as WS_MAXIMIZEBOX. + */ + return false; +} + +Decorations menuShell () { + return this; +} + +void releaseChildren (boolean destroy) { + if (menuBar != null) { + menuBar.release (false); + menuBar = null; + } + super.releaseChildren (destroy); + 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 (); + if (smallImage != null) smallImage.dispose (); + if (largeImage != null) largeImage.dispose (); + smallImage = largeImage = image = null; + images = null; + savedFocus = null; + defaultButton = saveDefault = null; + if (hAccel != 0 && hAccel != -1) OS.DestroyAcceleratorTable (hAccel); + hAccel = -1; +} + +void removeMenu (Menu menu) { + if (menus == null) return; + for (int i=0; i<menus.length; i++) { + if (menus [i] == menu) { + menus [i] = null; + return; + } + } +} + +boolean restoreFocus () { + if (display.ignoreRestoreFocus) return true; + if (savedFocus != null && savedFocus.isDisposed ()) savedFocus = null; + if (savedFocus != null && savedFocus.setSavedFocus ()) return true; + /* + * This code is intentionally commented. When no widget + * has been given focus, some platforms give focus to the + * default button. Windows doesn't do this. + */ +// if (defaultButton != null && !defaultButton.isDisposed ()) { +// if (defaultButton.setFocus ()) return true; +// } + return false; +} + +void saveFocus () { + Control control = display._getFocusControl (); + if (control != null && control != this && this == control.menuShell ()) { + setSavedFocus (control); + } +} + +void setBounds (int x, int y, int width, int height, int flags, boolean defer) { + swFlags = OS.SW_SHOWNOACTIVATE; + if (OS.IsWinCE) { + swFlags = OS.SW_RESTORE; + } else { + if (OS.IsIconic (handle)) { + setPlacement (x, y, width, height, flags); + return; + } + } + forceResize (); + RECT rect = new RECT (); + OS.GetWindowRect (handle, rect); + boolean sameOrigin = true; + if ((OS.SWP_NOMOVE & flags) == 0) { + sameOrigin = rect.left == x && rect.top == y; + if (!sameOrigin) moved = true; + } + boolean sameExtent = true; + if ((OS.SWP_NOSIZE & flags) == 0) { + sameExtent = rect.right - rect.left == width && rect.bottom - rect.top == height; + if (!sameExtent) resized = true; + } + if (!OS.IsWinCE) { + if (OS.IsZoomed (handle)) { + if (sameOrigin && sameExtent) return; + setPlacement (x, y, width, height, flags); + _setMaximized (false); + return; + } + } + super.setBounds (x, y, width, height, flags, defer); +} + +/** + * 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); + } + setDefaultButton (button, true); +} + +void setDefaultButton (Button button, boolean save) { + if (button == null) { + if (defaultButton == saveDefault) { + if (save) saveDefault = null; + return; + } + } else { + if ((button.style & SWT.PUSH) == 0) return; + if (button == defaultButton) return; + } + if (defaultButton != null) { + if (!defaultButton.isDisposed ()) defaultButton.setDefault (false); + } + if ((defaultButton = button) == null) defaultButton = saveDefault; + if (defaultButton != null) { + if (!defaultButton.isDisposed ()) defaultButton.setDefault (true); + } + if (save) saveDefault = defaultButton; + if (saveDefault != null && saveDefault.isDisposed ()) saveDefault = null; +} + +/** + * 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; + setImages (image, null); +} + +void setImages (Image image, Image [] images) { + /* + * Feature in WinCE. WM_SETICON and WM_GETICON set the icon + * for the window class, not the window instance. This means + * that it is possible to set an icon into a window and then + * later free the icon, thus freeing the icon for every window. + * The fix is to avoid the API. + * + * On WinCE PPC, icons in windows are not displayed. + */ + if (OS.IsWinCE) return; + if (smallImage != null) smallImage.dispose (); + if (largeImage != null) largeImage.dispose (); + smallImage = largeImage = null; + int /*long*/ hSmallIcon = 0, hLargeIcon = 0; + Image smallIcon = null, largeIcon = null; + if (image != null) { + smallIcon = largeIcon = image; + } else { + if (images != null && images.length > 0) { + int depth = display.getIconDepth (); + ImageData [] datas = null; + if (images.length > 1) { + Image [] bestImages = new Image [images.length]; + System.arraycopy (images, 0, bestImages, 0, images.length); + datas = new ImageData [images.length]; + for (int i=0; i<datas.length; i++) { + datas [i] = images [i].getImageData (); + } + images = bestImages; + sort (images, datas, OS.GetSystemMetrics (OS.SM_CXSMICON), OS.GetSystemMetrics (OS.SM_CYSMICON), depth); + } + smallIcon = images [0]; + if (images.length > 1) { + sort (images, datas, OS.GetSystemMetrics (OS.SM_CXICON), OS.GetSystemMetrics (OS.SM_CYICON), depth); + } + largeIcon = images [0]; + } + } + if (smallIcon != null) { + switch (smallIcon.type) { + case SWT.BITMAP: + smallImage = Display.createIcon (smallIcon); + hSmallIcon = smallImage.handle; + break; + case SWT.ICON: + hSmallIcon = smallIcon.handle; + break; + } + } + OS.SendMessage (handle, OS.WM_SETICON, OS.ICON_SMALL, hSmallIcon); + if (largeIcon != null) { + switch (largeIcon.type) { + case SWT.BITMAP: + largeImage = Display.createIcon (largeIcon); + hLargeIcon = largeImage.handle; + break; + case SWT.ICON: + hLargeIcon = largeIcon.handle; + break; + } + } + OS.SendMessage (handle, OS.WM_SETICON, OS.ICON_BIG, hLargeIcon); + + /* + * Bug in Windows. When WM_SETICON is used to remove an + * icon from the window trimmings for a window with the + * extended style bits WS_EX_DLGMODALFRAME, the window + * trimmings do not redraw to hide the previous icon. + * The fix is to force a redraw. + */ + if (!OS.IsWinCE) { + if (hSmallIcon == 0 && hLargeIcon == 0 && (style & SWT.BORDER) != 0) { + int flags = OS.RDW_FRAME | OS.RDW_INVALIDATE; + OS.RedrawWindow (handle, null, 0, flags); + } + } +} + +/** + * 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; + setImages (null, images); +} + +/** + * 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 (); + Display.lpStartupInfo = null; + _setMaximized (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); + } + if (OS.IsWinCE) { + if (OS.IsHPC) { + boolean resize = menuBar != menu; + if (menuBar != null) OS.CommandBar_Show (menuBar.hwndCB, false); + menuBar = menu; + if (menuBar != null) OS.CommandBar_Show (menuBar.hwndCB, true); + if (resize) { + sendEvent (SWT.Resize); + if (isDisposed ()) return; + if (layout != null) { + markLayout (false, false); + updateLayout (true, false); + } + } + } else { + if (OS.IsPPC) { + /* + * Note in WinCE PPC. The menu bar is a separate popup window. + * If the shell is full screen, resize its window to leave + * space for the menu bar. + */ + boolean resize = getMaximized () && menuBar != menu; + if (menuBar != null) OS.ShowWindow (menuBar.hwndCB, OS.SW_HIDE); + menuBar = menu; + if (menuBar != null) OS.ShowWindow (menuBar.hwndCB, OS.SW_SHOW); + if (resize) _setMaximized (true); + } + if (OS.IsSP) { + if (menuBar != null) OS.ShowWindow (menuBar.hwndCB, OS.SW_HIDE); + menuBar = menu; + if (menuBar != null) OS.ShowWindow (menuBar.hwndCB, OS.SW_SHOW); + } + } + } else { + if (menu != null) display.removeBar (menu); + menuBar = menu; + int /*long*/ hMenu = menuBar != null ? menuBar.handle: 0; + OS.SetMenu (handle, hMenu); + } + destroyAccelerators (); +} + +/** + * 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 (); + Display.lpStartupInfo = null; + _setMinimized (minimized); +} + +void setParent () { + /* + * In order for an MDI child window to support + * a menu bar, setParent () is needed to reset + * the parent. Otherwise, the MDI child window + * will appear as a separate shell. This is an + * undocumented and possibly dangerous Windows + * feature. + */ + int /*long*/ hwndParent = parent.handle; + display.lockActiveWindow = true; + OS.SetParent (handle, hwndParent); + if (!OS.IsWindowVisible (hwndParent)) { + OS.ShowWindow (handle, OS.SW_SHOWNA); + } + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + bits &= ~OS.WS_CHILD; + OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.WS_POPUP); + OS.SetWindowLongPtr (handle, OS.GWLP_ID, 0); + int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE; + SetWindowPos (handle, OS.HWND_BOTTOM, 0, 0, 0, 0, flags); + display.lockActiveWindow = false; +} + +void setPlacement (int x, int y, int width, int height, int flags) { + WINDOWPLACEMENT lpwndpl = new WINDOWPLACEMENT (); + lpwndpl.length = WINDOWPLACEMENT.sizeof; + OS.GetWindowPlacement (handle, lpwndpl); + lpwndpl.showCmd = OS.SW_SHOWNA; + if (OS.IsIconic (handle)) { + lpwndpl.showCmd = OS.SW_SHOWMINNOACTIVE; + } else { + if (OS.IsZoomed (handle)) { + lpwndpl.showCmd = OS.SW_SHOWMAXIMIZED; + } + } + boolean sameOrigin = true; + if ((flags & OS.SWP_NOMOVE) == 0) { + sameOrigin = lpwndpl.left != x || lpwndpl.top != y; + lpwndpl.right = x + (lpwndpl.right - lpwndpl.left); + lpwndpl.bottom = y + (lpwndpl.bottom - lpwndpl.top); + lpwndpl.left = x; + lpwndpl.top = y; + } + boolean sameExtent = true; + if ((flags & OS.SWP_NOSIZE) == 0) { + sameExtent = lpwndpl.right - lpwndpl.left != width || lpwndpl.bottom - lpwndpl.top != height; + lpwndpl.right = lpwndpl.left + width; + lpwndpl.bottom = lpwndpl.top + height; + } + OS.SetWindowPlacement (handle, lpwndpl); + if (OS.IsIconic (handle)) { + if (sameOrigin) { + moved = true; + Point location = getLocation (); + oldX = location.x; + oldY = location.y; + sendEvent (SWT.Move); + if (isDisposed ()) return; + } + if (sameExtent) { + resized = true; + Rectangle rect = getClientArea (); + oldWidth = rect.width; + oldHeight = rect.height; + sendEvent (SWT.Resize); + if (isDisposed ()) return; + if (layout != null) { + markLayout (false, false); + updateLayout (true, false); + } + } + } +} + +void setSavedFocus (Control control) { + savedFocus = control; +} + +void setSystemMenu () { + if (OS.IsWinCE) return; + int /*long*/ hMenu = OS.GetSystemMenu (handle, false); + if (hMenu == 0) return; + int oldCount = OS.GetMenuItemCount (hMenu); + if ((style & SWT.RESIZE) == 0) { + OS.DeleteMenu (hMenu, OS.SC_SIZE, OS.MF_BYCOMMAND); + } + if ((style & SWT.MIN) == 0) { + OS.DeleteMenu (hMenu, OS.SC_MINIMIZE, OS.MF_BYCOMMAND); + } + if ((style & SWT.MAX) == 0) { + OS.DeleteMenu (hMenu, OS.SC_MAXIMIZE, OS.MF_BYCOMMAND); + } + if ((style & (SWT.MIN | SWT.MAX)) == 0) { + OS.DeleteMenu (hMenu, OS.SC_RESTORE, OS.MF_BYCOMMAND); + } + int newCount = OS.GetMenuItemCount (hMenu); + if ((style & SWT.CLOSE) == 0 || newCount != oldCount) { + OS.DeleteMenu (hMenu, OS.SC_TASKLIST, OS.MF_BYCOMMAND); + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_ID; + int index = 0; + while (index < newCount) { + if (OS.GetMenuItemInfo (hMenu, index, true, info)) { + if (info.wID == OS.SC_CLOSE) break; + } + index++; + } + if (index != newCount) { + OS.DeleteMenu (hMenu, index - 1, OS.MF_BYPOSITION); + if ((style & SWT.CLOSE) == 0) { + OS.DeleteMenu (hMenu, OS.SC_CLOSE, OS.MF_BYCOMMAND); + } + } + } +} + +/** + * 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); + /* Use the character encoding for the default locale */ + TCHAR buffer = new TCHAR (0, string, true); + /* Ensure that the title appears in the task bar.*/ + if ((state & FOREIGN_HANDLE) != 0) { + int /*long*/ hHeap = OS.GetProcessHeap (); + int byteCount = buffer.length () * TCHAR.sizeof; + int /*long*/ pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (pszText, buffer, byteCount); + OS.DefWindowProc (handle, OS.WM_SETTEXT, 0, pszText); + if (pszText != 0) OS.HeapFree (hHeap, 0, pszText); + } else { + OS.SetWindowText (handle, buffer); + } +} + +public void setVisible (boolean visible) { + checkWidget (); + if (!getDrawing()) { + if (((state & HIDDEN) == 0) == visible) return; + } else { + if (visible == OS.IsWindowVisible (handle)) return; + } + 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; + if (OS.IsHPC) { + if (menuBar != null) { + int /*long*/ hwndCB = menuBar.hwndCB; + OS.CommandBar_DrawMenuBar (hwndCB, 0); + } + } + if (!getDrawing()) { + state &= ~HIDDEN; + } else { + if (OS.IsWinCE) { + OS.ShowWindow (handle, OS.SW_SHOW); + } else { + if (menuBar != null) { + display.removeBar (menuBar); + OS.DrawMenuBar (handle); + } + STARTUPINFO lpStartUpInfo = Display.lpStartupInfo; + if (lpStartUpInfo != null && (lpStartUpInfo.dwFlags & OS.STARTF_USESHOWWINDOW) != 0) { + OS.ShowWindow (handle, lpStartUpInfo.wShowWindow); + } else { + OS.ShowWindow (handle, swFlags); + } + } + if (isDisposed ()) return; + opened = true; + if (!moved) { + moved = true; + Point location = getLocation (); + oldX = location.x; + oldY = location.y; + } + if (!resized) { + resized = true; + Rectangle rect = getClientArea (); + oldWidth = rect.width; + oldHeight = rect.height; + } + /* + * Bug in Windows. On Vista using the Classic theme, + * when the window is hung and UpdateWindow() is called, + * nothing is drawn, and outstanding WM_PAINTs are cleared. + * This causes pixel corruption. The fix is to avoid calling + * update on hung windows. + */ + boolean update = true; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0) && !OS.IsAppThemed ()) { + update = !OS.IsHungAppWindow (handle); + } + if (update) OS.UpdateWindow (handle); + } + } else { + if (!OS.IsWinCE) { + if (OS.IsIconic (handle)) { + swFlags = OS.SW_SHOWMINNOACTIVE; + } else { + if (OS.IsZoomed (handle)) { + swFlags = OS.SW_SHOWMAXIMIZED; + } else { + swFlags = OS.SW_SHOWNOACTIVATE; + } + } + } + if (!getDrawing()) { + state |= HIDDEN; + } else { + OS.ShowWindow (handle, OS.SW_HIDE); + } + if (isDisposed ()) return; + sendEvent (SWT.Hide); + } +} + +void sort (Image [] images, ImageData [] datas, int width, int height, int depth) { + /* Shell Sort from K&R, pg 108 */ + int length = images.length; + if (length <= 1) return; + 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], width, height, depth) >= 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 translateAccelerator (MSG msg) { + if (!isEnabled () || !isActive ()) return false; + if (menuBar != null && !menuBar.isEnabled ()) return false; + if (translateMDIAccelerator (msg) || translateMenuAccelerator (msg)) return true; + Decorations decorations = parent.menuShell (); + return decorations.translateAccelerator (msg); +} + +boolean translateMenuAccelerator (MSG msg) { + if (hAccel == -1) createAccelerators (); + return hAccel != 0 && OS.TranslateAccelerator (handle, hAccel, msg) != 0; +} + +boolean translateMDIAccelerator (MSG msg) { + if (!(this instanceof Shell)) { + Shell shell = getShell (); + int /*long*/ hwndMDIClient = shell.hwndMDIClient; + if (hwndMDIClient != 0 && OS.TranslateMDISysAccel (hwndMDIClient, msg)) { + return true; + } + if (msg.message == OS.WM_KEYDOWN) { + if (OS.GetKeyState (OS.VK_CONTROL) >= 0) return false; + switch ((int)/*64*/(msg.wParam)) { + case OS.VK_F4: + OS.PostMessage (handle, OS.WM_CLOSE, 0, 0); + return true; + case OS.VK_F6: + if (traverseDecorations (true)) return true; + } + return false; + } + if (msg.message == OS.WM_SYSKEYDOWN) { + switch ((int)/*64*/(msg.wParam)) { + case OS.VK_F4: + OS.PostMessage (shell.handle, OS.WM_CLOSE, 0, 0); + return true; + } + return false; + } + } + return false; +} + +boolean traverseDecorations (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. + */ + int start = index, offset = (next) ? 1 : -1; + while ((index = (index + offset + length) % length) != start) { + Control child = children [index]; + if (!child.isDisposed () && child instanceof Decorations) { + if (child.setFocus ()) return true; + } + } + return false; +} + +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; +} + +CREATESTRUCT widgetCreateStruct () { + return new CREATESTRUCT (); +} + +int widgetExtStyle () { + int bits = super.widgetExtStyle () | OS.WS_EX_MDICHILD; + bits &= ~OS.WS_EX_CLIENTEDGE; + if ((style & SWT.NO_TRIM) != 0) return bits; + if (OS.IsPPC) { + if ((style & SWT.CLOSE) != 0) bits |= OS.WS_EX_CAPTIONOKBTN; + } + if ((style & SWT.RESIZE) != 0) return bits; + if ((style & SWT.BORDER) != 0) bits |= OS.WS_EX_DLGMODALFRAME; + return bits; +} + +int /*long*/ widgetParent () { + Shell shell = getShell (); + return shell.hwndMDIClient (); +} + +int widgetStyle () { + /* + * Clear WS_VISIBLE and WS_TABSTOP. NOTE: In Windows, WS_TABSTOP + * has the same value as WS_MAXIMIZEBOX so these bits cannot be + * used to control tabbing. + */ + int bits = super.widgetStyle () & ~(OS.WS_TABSTOP | OS.WS_VISIBLE); + + /* Set the title bits and no-trim bits */ + bits &= ~OS.WS_BORDER; + if ((style & SWT.NO_TRIM) != 0) return bits; + if ((style & SWT.TITLE) != 0) bits |= OS.WS_CAPTION; + + /* Set the min and max button bits */ + if ((style & SWT.MIN) != 0) bits |= OS.WS_MINIMIZEBOX; + if ((style & SWT.MAX) != 0) bits |= OS.WS_MAXIMIZEBOX; + + /* Set the resize, dialog border or border bits */ + if ((style & SWT.RESIZE) != 0) { + /* + * Note on WinCE PPC. SWT.RESIZE is used to resize + * the Shell according to the state of the IME. + * It does not set the WS_THICKFRAME style. + */ + if (!OS.IsPPC) bits |= OS.WS_THICKFRAME; + } else { + if ((style & SWT.BORDER) == 0) bits |= OS.WS_BORDER; + } + + /* Set the system menu and close box bits */ + if (!OS.IsPPC && !OS.IsSP) { + if ((style & SWT.CLOSE) != 0) bits |= OS.WS_SYSMENU; + } + + return bits; +} + +int /*long*/ windowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + switch (msg) { + case Display.SWT_GETACCEL: + case Display.SWT_GETACCELCOUNT: + if (hAccel == -1) createAccelerators (); + return msg == Display.SWT_GETACCELCOUNT ? nAccel : hAccel; + } + return super.windowProc (hwnd, msg, wParam, lParam); +} + +LRESULT WM_ACTIVATE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_ACTIVATE (wParam, lParam); + if (result != null) return result; + /* + * Feature in AWT. When an AWT Window is activated, + * for some reason, it seems to forward the WM_ACTIVATE + * message to the parent. Normally, the parent is an + * AWT Frame. When AWT is embedded in SWT, the SWT + * shell gets the WM_ACTIVATE and assumes that it came + * from Windows. When an SWT shell is activated it + * restores focus to the last control that had focus. + * If this control is an embedded composite, it takes + * focus from the AWT Window. The fix is to ignore + * WM_ACTIVATE messages that come from AWT Windows. + */ + if (OS.GetParent (lParam) == handle) { + TCHAR buffer = new TCHAR (0, 128); + OS.GetClassName (lParam, buffer, buffer.length ()); + String className = buffer.toString (0, buffer.strlen ()); + if (className.equals (Display.AWT_WINDOW_CLASS)) { + return LRESULT.ZERO; + } + } + if (OS.LOWORD (wParam) != 0) { + /* + * When the high word of wParam is non-zero, the activation + * state of the window is being changed while the window is + * minimized. If this is the case, do not report activation + * events or restore the focus. + */ + if (OS.HIWORD (wParam) != 0) return result; + Control control = display.findControl (lParam); + if (control == null || control instanceof Shell) { + if (this instanceof Shell) { + sendEvent (SWT.Activate); + if (isDisposed ()) return LRESULT.ZERO; + } + } + if (restoreFocus ()) return LRESULT.ZERO; + } else { + Display display = this.display; + boolean lockWindow = display.isXMouseActive (); + if (lockWindow) display.lockActiveWindow = true; + Control control = display.findControl (lParam); + if (control == null || control instanceof Shell) { + if (this instanceof Shell) { + sendEvent (SWT.Deactivate); + if (!isDisposed ()) { + Shell shell = getShell (); + shell.setActiveControl (null); + // widget could be disposed at this point + } + } + } + if (lockWindow) display.lockActiveWindow = false; + if (isDisposed ()) return LRESULT.ZERO; + saveFocus (); + } + return result; +} + +LRESULT WM_CLOSE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_CLOSE (wParam, lParam); + if (result != null) return result; + if (isEnabled () && isActive ()) closeWidget (); + return LRESULT.ZERO; +} + +LRESULT WM_HOTKEY (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_HOTKEY (wParam, lParam); + if (result != null) return result; + if (OS.IsSP) { + /* + * Feature on WinCE SP. The Back key is either used to close + * the foreground Dialog or used as a regular Back key in an EDIT + * control. The article 'Back Key' in MSDN for Smartphone + * describes how an application should handle it. The + * workaround is to override the Back key when creating + * the menubar and handle it based on the style of the Shell. + * If the Shell has the SWT.CLOSE style, close the Shell. + * Otherwise, send the Back key to the window with focus. + */ + if (OS.HIWORD (lParam) == OS.VK_ESCAPE) { + if ((style & SWT.CLOSE) != 0) { + OS.PostMessage (handle, OS.WM_CLOSE, 0, 0); + } else { + OS.SHSendBackToFocusWindow (OS.WM_HOTKEY, wParam, lParam); + } + return LRESULT.ZERO; + } + } + return result; +} + +LRESULT WM_KILLFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_KILLFOCUS (wParam, lParam); + saveFocus (); + return result; +} + +LRESULT WM_MOVE (int /*long*/ wParam, int /*long*/ lParam) { + if (moved) { + Point location = getLocation (); + if (location.x == oldX && location.y == oldY) { + return null; + } + oldX = location.x; + oldY = location.y; + } + return super.WM_MOVE (wParam, lParam); +} + +LRESULT WM_NCACTIVATE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_NCACTIVATE (wParam, lParam); + if (result != null) return result; + if (wParam == 0) { + if (display.lockActiveWindow) return LRESULT.ZERO; + Control control = display.findControl (lParam); + if (control != null) { + Shell shell = getShell (); + Decorations decorations = control.menuShell (); + if (decorations.getShell () == shell) { + if (this instanceof Shell) return LRESULT.ONE; + if (display.ignoreRestoreFocus) { + if (display.lastHittest != OS.HTCLIENT) { + result = LRESULT.ONE; + } + } + } + } + } + if (!(this instanceof Shell)) { + int /*long*/ hwndShell = getShell().handle; + OS.SendMessage (hwndShell, OS.WM_NCACTIVATE, wParam, lParam); + } + return result; +} + +LRESULT WM_QUERYOPEN (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_QUERYOPEN (wParam, lParam); + if (result != null) return result; + sendEvent (SWT.Deiconify); + // widget could be disposed at this point + return result; +} + +LRESULT WM_SETFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETFOCUS (wParam, lParam); + if (savedFocus != this) restoreFocus (); + return result; +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = null; + boolean changed = true; + if (resized) { + int newWidth = 0, newHeight = 0; + switch ((int)/*64*/wParam) { + case OS.SIZE_RESTORED: + case OS.SIZE_MAXIMIZED: + newWidth = OS.LOWORD (lParam); + newHeight = OS.HIWORD (lParam); + break; + case OS.SIZE_MINIMIZED: + Rectangle rect = getClientArea (); + newWidth = rect.width; + newHeight = rect.height; + break; + } + changed = newWidth != oldWidth || newHeight != oldHeight; + if (changed) { + oldWidth = newWidth; + oldHeight = newHeight; + } + } + if (changed) { + result = super.WM_SIZE (wParam, lParam); + if (isDisposed ()) return result; + } + if (wParam == OS.SIZE_MINIMIZED) { + sendEvent (SWT.Iconify); + // widget could be disposed at this point + } + return result; +} + +LRESULT WM_SYSCOMMAND (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SYSCOMMAND (wParam, lParam); + if (result != null) return result; + if (!(this instanceof Shell)) { + int cmd = (int)/*64*/wParam & 0xFFF0; + switch (cmd) { + case OS.SC_CLOSE: { + OS.PostMessage (handle, OS.WM_CLOSE, 0, 0); + return LRESULT.ZERO; + } + case OS.SC_NEXTWINDOW: { + traverseDecorations (true); + return LRESULT.ZERO; + } + } + } + return result; +} + +LRESULT WM_WINDOWPOSCHANGING (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_WINDOWPOSCHANGING (wParam, lParam); + if (result != null) return result; + if (display.lockActiveWindow) { + WINDOWPOS lpwp = new WINDOWPOS (); + OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof); + lpwp.flags |= OS.SWP_NOZORDER; + OS.MoveMemory (lParam, lpwp, WINDOWPOS.sizeof); + } + return result; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/DirectoryDialog.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/DirectoryDialog.java new file mode 100755 index 0000000000..2c77b650dd --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/DirectoryDialog.java @@ -0,0 +1,310 @@ +/******************************************************************************* + * 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.win32.*; +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 = ""; //$NON-NLS-1$//$NON-NLS-2$ + String directoryPath; + +/** + * 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)); + checkSubclass (); +} + +int /*long*/ BrowseCallbackProc (int /*long*/ hwnd, int /*long*/ uMsg, int /*long*/ lParam, int /*long*/ lpData) { + switch ((int)/*64*/uMsg) { + case OS.BFFM_INITIALIZED: + if (filterPath != null && filterPath.length () != 0) { + /* Use the character encoding for the default locale */ + TCHAR buffer = new TCHAR (0, filterPath.replace ('/', '\\'), true); + OS.SendMessage (hwnd, OS.BFFM_SETSELECTION, 1, buffer); + } + if (title != null && title.length () != 0) { + /* Use the character encoding for the default locale */ + TCHAR buffer = new TCHAR (0, title, true); + OS.SetWindowText (hwnd, buffer); + } + break; + case OS.BFFM_VALIDATEFAILEDA: + case OS.BFFM_VALIDATEFAILEDW: + /* Use the character encoding for the default locale */ + int length = OS.IsUnicode ? OS.wcslen (lParam) : OS.strlen (lParam); + TCHAR buffer = new TCHAR (0, length); + int byteCount = buffer.length () * TCHAR.sizeof; + OS.MoveMemory (buffer, lParam, byteCount); + directoryPath = buffer.toString (0, length); + break; + } + return 0; +} + +/** + * 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 () { + if (OS.IsWinCE) SWT.error (SWT.ERROR_NOT_IMPLEMENTED); + + int /*long*/ hHeap = OS.GetProcessHeap (); + + /* Get the owner HWND for the dialog */ + int /*long*/ hwndOwner = 0; + if (parent != null) hwndOwner = parent.handle; + + /* Copy the message to OS memory */ + int /*long*/ lpszTitle = 0; + if (message.length () != 0) { + String string = message; + if (string.indexOf ('&') != -1) { + int length = string.length (); + char [] buffer = new char [length * 2]; + int index = 0; + for (int i=0; i<length; i++) { + char ch = string.charAt (i); + if (ch == '&') buffer [index++] = '&'; + buffer [index++] = ch; + } + string = new String (buffer, 0, index); + } + /* Use the character encoding for the default locale */ + TCHAR buffer = new TCHAR (0, string, true); + int byteCount = buffer.length () * TCHAR.sizeof; + lpszTitle = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (lpszTitle, buffer, byteCount); + } + + /* Create the BrowseCallbackProc */ + Callback callback = new Callback (this, "BrowseCallbackProc", 4); //$NON-NLS-1$ + int /*long*/ lpfn = callback.getAddress (); + if (lpfn == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + + /* Make the parent shell be temporary modal */ + Dialog oldModal = null; + Display display = parent.getDisplay (); + if ((style & (SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL)) != 0) { + oldModal = display.getModalDialog (); + display.setModalDialog (this); + } + + directoryPath = null; + BROWSEINFO lpbi = new BROWSEINFO (); + lpbi.hwndOwner = hwndOwner; + lpbi.lpszTitle = lpszTitle; + lpbi.ulFlags = OS.BIF_NEWDIALOGSTYLE | OS.BIF_RETURNONLYFSDIRS | OS.BIF_EDITBOX | OS.BIF_VALIDATE; + lpbi.lpfn = lpfn; + /* + * Bug in Windows. On some hardware configurations, SHBrowseForFolder() + * causes warning dialogs with the message "There is no disk in the drive + * Please insert a disk into \Device\Harddisk0\DR0". This is possibly + * caused by SHBrowseForFolder() calling internally GetVolumeInformation(). + * MSDN for GetVolumeInformation() says: + * + * "If you are attempting to obtain information about a floppy drive + * that does not have a floppy disk or a CD-ROM drive that does not + * have a compact disc, the system displays a message box asking the + * user to insert a floppy disk or a compact disc, respectively. + * To prevent the system from displaying this message box, call the + * SetErrorMode function with SEM_FAILCRITICALERRORS." + * + * The fix is to save and restore the error mode using SetErrorMode() + * with the SEM_FAILCRITICALERRORS flag around SHBrowseForFolder(). + */ + int oldErrorMode = OS.SetErrorMode (OS.SEM_FAILCRITICALERRORS); + + /* + * Bug in Windows. When a WH_MSGFILTER hook is used to run code + * during the message loop for SHBrowseForFolder(), running code + * in the hook can cause a GP. Specifically, SetWindowText() + * for static controls seemed to make the problem happen. + * The fix is to disable async messages while the directory + * dialog is open. + * + * NOTE: This only happens in versions of the comctl32.dll + * earlier than 6.0. + */ + boolean oldRunMessages = display.runMessages; + if (OS.COMCTL32_MAJOR < 6) display.runMessages = false; + int /*long*/ lpItemIdList = OS.SHBrowseForFolder (lpbi); + if (OS.COMCTL32_MAJOR < 6) display.runMessages = oldRunMessages; + OS.SetErrorMode (oldErrorMode); + + /* Clear the temporary dialog modal parent */ + if ((style & (SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL)) != 0) { + display.setModalDialog (oldModal); + } + + boolean success = lpItemIdList != 0; + if (success) { + /* Use the character encoding for the default locale */ + TCHAR buffer = new TCHAR (0, OS.MAX_PATH); + if (OS.SHGetPathFromIDList (lpItemIdList, buffer)) { + directoryPath = buffer.toString (0, buffer.strlen ()); + filterPath = directoryPath; + } + } + + /* Free the BrowseCallbackProc */ + callback.dispose (); + + /* Free the OS memory */ + if (lpszTitle != 0) OS.HeapFree (hHeap, 0, lpszTitle); + + /* Free the pointer to the ITEMIDLIST */ + int /*long*/ [] ppMalloc = new int /*long*/ [1]; + if (OS.SHGetMalloc (ppMalloc) == OS.S_OK) { + /* void Free (struct IMalloc *this, void *pv); */ + OS.VtblCall (5, ppMalloc [0], lpItemIdList); + } + + /* + * This code is intentionally commented. On some + * platforms, the owner window is repainted right + * away when a dialog window exits. This behavior + * is currently unspecified. + */ +// if (hwndOwner != 0) OS.UpdateWindow (hwndOwner); + + /* Return the directory path */ + if (!success) return null; + return directoryPath; +} + +/** + * 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; +} + +/** + * 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/win32/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java new file mode 100755 index 0000000000..e3a2fee7b8 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java @@ -0,0 +1,4653 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * 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 { + + /** + * the handle to the OS message queue + * (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 MSG msg = new MSG (); + + /* Windows and Events */ + Event [] eventQueue; + Callback windowCallback; + int /*long*/ windowProc; + int threadId; + TCHAR windowClass, windowShadowClass, windowOwnDCClass; + static int WindowClassCount; + static final String WindowName = "SWT_Window"; //$NON-NLS-1$ + static final String WindowShadowName = "SWT_WindowShadow"; //$NON-NLS-1$ + static final String WindowOwnDCName = "SWT_WindowOwnDC"; //$NON-NLS-1$ + EventTable eventTable, filterTable; + boolean useOwnDC; + + /* Widget Table */ + int freeSlot; + int [] indexTable; + Control lastControl, lastGetControl; + int /*long*/ lastHwnd, lastGetHwnd; + Control [] controlTable; + static final int GROW_SIZE = 1024; + static final int SWT_OBJECT_INDEX; + static final boolean USE_PROPERTY = !OS.IsWinCE; + static { + if (USE_PROPERTY) { + SWT_OBJECT_INDEX = OS.GlobalAddAtom (new TCHAR (0, "SWT_OBJECT_INDEX", true)); //$NON-NLS-1$ + } else { + SWT_OBJECT_INDEX = 0; + } + } + + /* Startup info */ + static STARTUPINFO lpStartupInfo; + static { + if (!OS.IsWinCE) { + lpStartupInfo = new STARTUPINFO (); + lpStartupInfo.cb = STARTUPINFO.sizeof; + OS.GetStartupInfo (lpStartupInfo); + } + } + + /* XP Themes */ + int /*long*/ hButtonTheme, hEditTheme, hExplorerBarTheme, hScrollBarTheme, hTabTheme; + static final char [] BUTTON = new char [] {'B', 'U', 'T', 'T', 'O', 'N', 0}; + static final char [] EDIT = new char [] {'E', 'D', 'I', 'T', 0}; + static final char [] EXPLORER = new char [] {'E', 'X', 'P', 'L', 'O', 'R', 'E', 'R', 0}; + static final char [] EXPLORERBAR = new char [] {'E', 'X', 'P', 'L', 'O', 'R', 'E', 'R', 'B', 'A', 'R', 0}; + static final char [] SCROLLBAR = new char [] {'S', 'C', 'R', 'O', 'L', 'L', 'B', 'A', 'R', 0}; + static final char [] LISTVIEW = new char [] {'L', 'I', 'S', 'T', 'V', 'I', 'E', 'W', 0}; + static final char [] TAB = new char [] {'T', 'A', 'B', 0}; + static final char [] TREEVIEW = new char [] {'T', 'R', 'E', 'E', 'V', 'I', 'E', 'W', 0}; + + /* Focus */ + int focusEvent; + Control focusControl; + + /* Menus */ + Menu [] bars, popups; + MenuItem [] items; + + /* + * The start value for WM_COMMAND id's. + * Windows reserves the values 0..100. + * + * The SmartPhone SWT resource file reserves + * the values 101..107. + */ + static final int ID_START = 108; + + /* Filter Hook */ + Callback msgFilterCallback; + int /*long*/ msgFilterProc, filterHook; + MSG hookMsg = new MSG (); + boolean runDragDrop = true, dragCancelled = false; + + /* Idle Hook */ + Callback foregroundIdleCallback; + int /*long*/ foregroundIdleProc, idleHook; + + /* Message Hook and Embedding */ + boolean ignoreNextKey; + Callback getMsgCallback, embeddedCallback; + int /*long*/ getMsgProc, msgHook, embeddedHwnd, embeddedProc; + static final String AWT_WINDOW_CLASS = "SunAwtWindow"; //$NON-NLS-1$ + static final short [] ACCENTS = new short [] {'~', '`', '\'', '^', '"'}; + + /* Sync/Async Widget Communication */ + Synchronizer synchronizer = new Synchronizer (this); + boolean runMessages = true, runMessagesInIdle = false, runMessagesInMessageProc = true; + static final String RUN_MESSAGES_IN_IDLE_KEY = "org.eclipse.swt.internal.win32.runMessagesInIdle"; //$NON-NLS-1$ + static final String RUN_MESSAGES_IN_MESSAGE_PROC_KEY = "org.eclipse.swt.internal.win32.runMessagesInMessageProc"; //$NON-NLS-1$ + static final String USE_OWNDC_KEY = "org.eclipse.swt.internal.win32.useOwnDC"; //$NON-NLS-1$ + Thread thread; + + /* Display Shutdown */ + Runnable [] disposeList; + + /* System Tray */ + Tray tray; + int nextTrayId; + + /* Timers */ + int /*long*/ [] timerIds; + Runnable [] timerList; + int /*long*/ nextTimerId = SETTINGS_ID + 1; + + /* Settings */ + static final int /*long*/ SETTINGS_ID = 100; + static final int SETTINGS_DELAY = 2000; + boolean lastHighContrast, sendSettings; + + /* Keyboard and Mouse */ + RECT clickRect; + int clickCount, lastTime, lastButton; + int /*long*/ lastClickHwnd; + int scrollRemainder; + int lastKey, lastAscii, lastMouse; + boolean lastVirtual, lastNull, lastDead; + byte [] keyboard = new byte [256]; + boolean accelKeyHit, mnemonicKeyHit; + boolean lockActiveWindow, captureChanged, xMouse; + + /* Tool Tips */ + int nextToolTipId; + + /* MDI */ + boolean ignoreRestoreFocus; + Control lastHittestControl; + int lastHittest; + + /* Message Only Window */ + Callback messageCallback; + int /*long*/ hwndMessage, messageProc; + + /* System Resources */ + LOGFONT lfSystemFont; + Font systemFont; + Image errorImage, infoImage, questionImage, warningIcon; + Cursor [] cursors = new Cursor [SWT.CURSOR_HAND + 1]; + Resource [] resources; + static final int RESOURCE_SIZE = 1 + 4 + SWT.CURSOR_HAND + 1; + + /* ImageList Cache */ + ImageList[] imageList, toolImageList, toolHotImageList, toolDisabledImageList; + + /* Custom Colors for ChooseColor */ + int /*long*/ lpCustColors; + + /* Sort Indicators */ + Image upArrow, downArrow; + + /* Table */ + char [] tableBuffer; + NMHDR hdr = new NMHDR (); + NMLVDISPINFO plvfi = new NMLVDISPINFO (); + int /*long*/ hwndParent; + int columnCount; + boolean [] columnVisible; + + /* Resize and move recursion */ + int resizeCount; + static final int RESIZE_LIMIT = 4; + + /* Display Data */ + Object data; + String [] keys; + Object [] values; + + /* Key Mappings */ + static final int [] [] KeyTable = { + + /* Keyboard and Mouse Masks */ + {OS.VK_MENU, SWT.ALT}, + {OS.VK_SHIFT, SWT.SHIFT}, + {OS.VK_CONTROL, SWT.CONTROL}, +// {OS.VK_????, SWT.COMMAND}, + + /* NOT CURRENTLY USED */ +// {OS.VK_LBUTTON, SWT.BUTTON1}, +// {OS.VK_MBUTTON, SWT.BUTTON3}, +// {OS.VK_RBUTTON, SWT.BUTTON2}, + + /* Non-Numeric Keypad Keys */ + {OS.VK_UP, SWT.ARROW_UP}, + {OS.VK_DOWN, SWT.ARROW_DOWN}, + {OS.VK_LEFT, SWT.ARROW_LEFT}, + {OS.VK_RIGHT, SWT.ARROW_RIGHT}, + {OS.VK_PRIOR, SWT.PAGE_UP}, + {OS.VK_NEXT, SWT.PAGE_DOWN}, + {OS.VK_HOME, SWT.HOME}, + {OS.VK_END, SWT.END}, + {OS.VK_INSERT, SWT.INSERT}, + + /* Virtual and Ascii Keys */ + {OS.VK_BACK, SWT.BS}, + {OS.VK_RETURN, SWT.CR}, + {OS.VK_DELETE, SWT.DEL}, + {OS.VK_ESCAPE, SWT.ESC}, + {OS.VK_RETURN, SWT.LF}, + {OS.VK_TAB, SWT.TAB}, + + /* Functions Keys */ + {OS.VK_F1, SWT.F1}, + {OS.VK_F2, SWT.F2}, + {OS.VK_F3, SWT.F3}, + {OS.VK_F4, SWT.F4}, + {OS.VK_F5, SWT.F5}, + {OS.VK_F6, SWT.F6}, + {OS.VK_F7, SWT.F7}, + {OS.VK_F8, SWT.F8}, + {OS.VK_F9, SWT.F9}, + {OS.VK_F10, SWT.F10}, + {OS.VK_F11, SWT.F11}, + {OS.VK_F12, SWT.F12}, + {OS.VK_F13, SWT.F13}, + {OS.VK_F14, SWT.F14}, + {OS.VK_F15, SWT.F15}, + + /* Numeric Keypad Keys */ + {OS.VK_MULTIPLY, SWT.KEYPAD_MULTIPLY}, + {OS.VK_ADD, SWT.KEYPAD_ADD}, + {OS.VK_RETURN, SWT.KEYPAD_CR}, + {OS.VK_SUBTRACT, SWT.KEYPAD_SUBTRACT}, + {OS.VK_DECIMAL, SWT.KEYPAD_DECIMAL}, + {OS.VK_DIVIDE, SWT.KEYPAD_DIVIDE}, + {OS.VK_NUMPAD0, SWT.KEYPAD_0}, + {OS.VK_NUMPAD1, SWT.KEYPAD_1}, + {OS.VK_NUMPAD2, SWT.KEYPAD_2}, + {OS.VK_NUMPAD3, SWT.KEYPAD_3}, + {OS.VK_NUMPAD4, SWT.KEYPAD_4}, + {OS.VK_NUMPAD5, SWT.KEYPAD_5}, + {OS.VK_NUMPAD6, SWT.KEYPAD_6}, + {OS.VK_NUMPAD7, SWT.KEYPAD_7}, + {OS.VK_NUMPAD8, SWT.KEYPAD_8}, + {OS.VK_NUMPAD9, SWT.KEYPAD_9}, +// {OS.VK_????, SWT.KEYPAD_EQUAL}, + + /* Other keys */ + {OS.VK_CAPITAL, SWT.CAPS_LOCK}, + {OS.VK_NUMLOCK, SWT.NUM_LOCK}, + {OS.VK_SCROLL, SWT.SCROLL_LOCK}, + {OS.VK_PAUSE, SWT.PAUSE}, + {OS.VK_CANCEL, SWT.BREAK}, + {OS.VK_SNAPSHOT, SWT.PRINT_SCREEN}, +// {OS.VK_????, SWT.HELP}, + + }; + + /* Multiple Displays */ + static Display Default; + static Display [] Displays = new Display [4]; + + /* Multiple Monitors */ + Monitor[] monitors = null; + int monitorCount = 0; + + /* Modality */ + Shell [] modalShells; + Dialog modalDialog; + static boolean TrimEnabled = false; + + /* Private SWT Window Messages */ + static final int SWT_GETACCELCOUNT = OS.WM_APP; + static final int SWT_GETACCEL = OS.WM_APP + 1; + static final int SWT_KEYMSG = OS.WM_APP + 2; + static final int SWT_DESTROY = OS.WM_APP + 3; + static final int SWT_TRAYICONMSG = OS.WM_APP + 4; + static final int SWT_NULL = OS.WM_APP + 5; + static final int SWT_RUNASYNC = OS.WM_APP + 6; + static int SWT_TASKBARCREATED; + static int SWT_RESTORECARET; + static int DI_GETDRAGIMAGE; + + /* Workaround for Adobe Reader 7.0 */ + int hitCount; + + /* Package Name */ + static final String PACKAGE_PREFIX = "org.eclipse.swt.widgets."; //$NON-NLS-1$ + /* + * This code is intentionally commented. In order + * to support CLDC, .class cannot be used because + * it does not compile on some Java compilers when + * they are targeted for CLDC. + */ +// static { +// String name = Display.class.getName (); +// int index = name.lastIndexOf ('.'); +// PACKAGE_PREFIX = name.substring (0, index + 1); +// } + + /* + * 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; +} + +/** + * 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); +} + +Control _getFocusControl () { + return findControl (OS.GetFocus ()); +} + +void addBar (Menu menu) { + if (bars == null) bars = new Menu [4]; + int length = bars.length; + for (int i=0; i<length; i++) { + if (bars [i] == menu) return; + } + int index = 0; + while (index < length) { + if (bars [index] == null) break; + index++; + } + if (index == length) { + Menu [] newBars = new Menu [length + 4]; + System.arraycopy (bars, 0, newBars, 0, length); + bars = newBars; + } + bars [index] = menu; +} + +void addControl (int /*long*/ handle, Control control) { + if (handle == 0) return; + if (freeSlot == -1) { + int length = (freeSlot = indexTable.length) + GROW_SIZE; + int [] newIndexTable = new int [length]; + Control [] newControlTable = new Control [length]; + System.arraycopy (indexTable, 0, newIndexTable, 0, freeSlot); + System.arraycopy (controlTable, 0, newControlTable, 0, freeSlot); + for (int i=freeSlot; i<length-1; i++) newIndexTable [i] = i + 1; + newIndexTable [length - 1] = -1; + indexTable = newIndexTable; + controlTable = newControlTable; + } + if (USE_PROPERTY) { + OS.SetProp (handle, SWT_OBJECT_INDEX, freeSlot + 1); + } else { + OS.SetWindowLongPtr (handle, OS.GWLP_USERDATA, freeSlot + 1); + } + int oldSlot = freeSlot; + freeSlot = indexTable [oldSlot]; + indexTable [oldSlot] = -2; + controlTable [oldSlot] = control; +} + +/** + * 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 addMenuItem (MenuItem item) { + if (items == null) items = new MenuItem [64]; + for (int i=0; i<items.length; i++) { + if (items [i] == null) { + item.id = i + ID_START; + items [i] = item; + return; + } + } + item.id = items.length + ID_START; + MenuItem [] newItems = new MenuItem [items.length + 64]; + newItems [items.length] = item; + System.arraycopy (items, 0, newItems, 0, items.length); + items = newItems; +} + +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; +} + +int asciiKey (int key) { + if (OS.IsWinCE) return 0; + + /* Get the current keyboard. */ + for (int i=0; i<keyboard.length; i++) keyboard [i] = 0; + if (!OS.GetKeyboardState (keyboard)) return 0; + + /* Translate the key to ASCII or UNICODE using the virtual keyboard */ + if (OS.IsUnicode) { + char [] result = new char [1]; + if (OS.ToUnicode (key, key, keyboard, result, 1, 0) == 1) return result [0]; + } else { + short [] result = new short [1]; + if (OS.ToAscii (key, key, keyboard, result, 0) == 1) return result [0]; + } + 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 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.MessageBeep (OS.MB_OK); +} + +/** + * 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 (!isValidClass (getClass ())) error (SWT.ERROR_INVALID_SUBCLASS); +} + +protected void checkDevice () { + if (thread == null) error (SWT.ERROR_WIDGET_DISPOSED); + if (thread != Thread.currentThread ()) { + /* + * Bug in IBM JVM 1.6. For some reason, under + * conditions that are yet to be full understood, + * Thread.currentThread() is either returning null + * or a different instance from the one that was + * saved when the Display was created. This is + * possibly a JIT problem because modifying this + * method to print logging information when the + * error happens seems to fix the problem. The + * fix is to use operating system calls to verify + * that the current thread is not the Display thread. + * + * NOTE: Despite the fact that Thread.currentThread() + * is used in other places, the failure has not been + * observed in all places where it is called. + */ + if (threadId != OS.GetCurrentThreadId ()) { + error (SWT.ERROR_THREAD_INVALID_ACCESS); + } + } + if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED); +} + +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]"); //$NON-NLS-1$ + if (Displays [i].thread == thread) SWT.error (SWT.ERROR_THREAD_INVALID_ACCESS); + } + } + } +} + +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 (); +} + +int controlKey (int key) { + int upper = (int)/*64*/OS.CharUpper ((short) key); + if (64 <= upper && upper <= 95) return upper & 0xBF; + return key; +} + +/** + * 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 (), true); + createDisplay (data); + register (this); + if (Default == null) Default = this; +} + +void createDisplay (DeviceData data) { +} + +static int /*long*/ create32bitDIB (Image image) { + int transparentPixel = -1, alpha = -1; + int /*long*/ hMask = 0, hBitmap = 0; + byte[] alphaData = null; + switch (image.type) { + case SWT.ICON: + ICONINFO info = new ICONINFO (); + OS.GetIconInfo (image.handle, info); + hBitmap = info.hbmColor; + hMask = info.hbmMask; + break; + case SWT.BITMAP: + ImageData data = image.getImageData (); + hBitmap = image.handle; + alpha = data.alpha; + alphaData = data.alphaData; + transparentPixel = data.transparentPixel; + break; + } + BITMAP bm = new BITMAP (); + OS.GetObject (hBitmap, BITMAP.sizeof, bm); + int imgWidth = bm.bmWidth; + int imgHeight = bm.bmHeight; + int /*long*/ hDC = OS.GetDC (0); + int /*long*/ srcHdc = OS.CreateCompatibleDC (hDC); + int /*long*/ oldSrcBitmap = OS.SelectObject (srcHdc, hBitmap); + int /*long*/ memHdc = OS.CreateCompatibleDC (hDC); + BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER (); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biWidth = imgWidth; + bmiHeader.biHeight = -imgHeight; + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = (short)32; + bmiHeader.biCompression = OS.BI_RGB; + byte [] bmi = new byte [BITMAPINFOHEADER.sizeof]; + OS.MoveMemory (bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + int /*long*/ [] pBits = new int /*long*/ [1]; + int /*long*/ memDib = OS.CreateDIBSection (0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0); + if (memDib == 0) SWT.error (SWT.ERROR_NO_HANDLES); + int /*long*/ oldMemBitmap = OS.SelectObject (memHdc, memDib); + BITMAP dibBM = new BITMAP (); + OS.GetObject (memDib, BITMAP.sizeof, dibBM); + int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight; + OS.BitBlt (memHdc, 0, 0, imgWidth, imgHeight, srcHdc, 0, 0, OS.SRCCOPY); + byte red = 0, green = 0, blue = 0; + if (transparentPixel != -1) { + if (bm.bmBitsPixel <= 8) { + byte [] color = new byte [4]; + OS.GetDIBColorTable (srcHdc, transparentPixel, 1, color); + blue = color [0]; + green = color [1]; + red = color [2]; + } else { + switch (bm.bmBitsPixel) { + case 16: + blue = (byte)((transparentPixel & 0x1F) << 3); + green = (byte)((transparentPixel & 0x3E0) >> 2); + red = (byte)((transparentPixel & 0x7C00) >> 7); + break; + case 24: + blue = (byte)((transparentPixel & 0xFF0000) >> 16); + green = (byte)((transparentPixel & 0xFF00) >> 8); + red = (byte)(transparentPixel & 0xFF); + break; + case 32: + blue = (byte)((transparentPixel & 0xFF000000) >>> 24); + green = (byte)((transparentPixel & 0xFF0000) >> 16); + red = (byte)((transparentPixel & 0xFF00) >> 8); + break; + } + } + } + byte [] srcData = new byte [sizeInBytes]; + OS.MoveMemory (srcData, pBits [0], sizeInBytes); + if (hMask != 0) { + OS.SelectObject(srcHdc, hMask); + for (int y = 0, dp = 0; y < imgHeight; ++y) { + for (int x = 0; x < imgWidth; ++x) { + if (OS.GetPixel(srcHdc, x, y) != 0) { + srcData [dp + 0] = srcData [dp + 1] = srcData [dp + 2] = srcData[dp + 3] = (byte)0; + } else { + srcData[dp + 3] = (byte)0xFF; + } + dp += 4; + } + } + } else if (alpha != -1) { + for (int y = 0, dp = 0; y < imgHeight; ++y) { + for (int x = 0; x < imgWidth; ++x) { + srcData [dp + 3] = (byte)alpha; + if (srcData [dp + 3] == 0) srcData [dp + 0] = srcData [dp + 1] = srcData [dp + 2] = 0; + dp += 4; + } + } + } else if (alphaData != null) { + for (int y = 0, dp = 0, ap = 0; y < imgHeight; ++y) { + for (int x = 0; x < imgWidth; ++x) { + srcData [dp + 3] = alphaData [ap++]; + if (srcData [dp + 3] == 0) srcData [dp + 0] = srcData [dp + 1] = srcData [dp + 2] = 0; + dp += 4; + } + } + } else if (transparentPixel != -1) { + for (int y = 0, dp = 0; y < imgHeight; ++y) { + for (int x = 0; x < imgWidth; ++x) { + if (srcData [dp] == blue && srcData [dp + 1] == green && srcData [dp + 2] == red) { + srcData [dp + 0] = srcData [dp + 1] = srcData [dp + 2] = srcData [dp + 3] = (byte)0; + } else { + srcData [dp + 3] = (byte)0xFF; + } + dp += 4; + } + } + } else { + for (int y = 0, dp = 0; y < imgHeight; ++y) { + for (int x = 0; x < imgWidth; ++x) { + srcData [dp + 3] = (byte)0xFF; + dp += 4; + } + } + } + OS.MoveMemory (pBits [0], srcData, sizeInBytes); + OS.SelectObject (srcHdc, oldSrcBitmap); + OS.SelectObject (memHdc, oldMemBitmap); + OS.DeleteObject (srcHdc); + OS.DeleteObject (memHdc); + OS.ReleaseDC (0, hDC); + if (hBitmap != image.handle && hBitmap != 0) OS.DeleteObject (hBitmap); + if (hMask != 0) OS.DeleteObject (hMask); + return memDib; +} +static int /*long*/ create32bitDIB (int /*long*/ hBitmap, int alpha, byte [] alphaData, int transparentPixel) { + BITMAP bm = new BITMAP (); + OS.GetObject (hBitmap, BITMAP.sizeof, bm); + int imgWidth = bm.bmWidth; + int imgHeight = bm.bmHeight; + int /*long*/ hDC = OS.GetDC (0); + int /*long*/ srcHdc = OS.CreateCompatibleDC (hDC); + int /*long*/ oldSrcBitmap = OS.SelectObject (srcHdc, hBitmap); + int /*long*/ memHdc = OS.CreateCompatibleDC (hDC); + BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER (); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biWidth = imgWidth; + bmiHeader.biHeight = -imgHeight; + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = (short)32; + bmiHeader.biCompression = OS.BI_RGB; + byte [] bmi = new byte [BITMAPINFOHEADER.sizeof]; + OS.MoveMemory (bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + int /*long*/ [] pBits = new int /*long*/ [1]; + int /*long*/ memDib = OS.CreateDIBSection (0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0); + if (memDib == 0) SWT.error (SWT.ERROR_NO_HANDLES); + int /*long*/ oldMemBitmap = OS.SelectObject (memHdc, memDib); + BITMAP dibBM = new BITMAP (); + OS.GetObject (memDib, BITMAP.sizeof, dibBM); + int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight; + OS.BitBlt (memHdc, 0, 0, imgWidth, imgHeight, srcHdc, 0, 0, OS.SRCCOPY); + byte red = 0, green = 0, blue = 0; + if (transparentPixel != -1) { + if (bm.bmBitsPixel <= 8) { + byte [] color = new byte [4]; + OS.GetDIBColorTable (srcHdc, transparentPixel, 1, color); + blue = color [0]; + green = color [1]; + red = color [2]; + } else { + switch (bm.bmBitsPixel) { + case 16: + blue = (byte)((transparentPixel & 0x1F) << 3); + green = (byte)((transparentPixel & 0x3E0) >> 2); + red = (byte)((transparentPixel & 0x7C00) >> 7); + break; + case 24: + blue = (byte)((transparentPixel & 0xFF0000) >> 16); + green = (byte)((transparentPixel & 0xFF00) >> 8); + red = (byte)(transparentPixel & 0xFF); + break; + case 32: + blue = (byte)((transparentPixel & 0xFF000000) >>> 24); + green = (byte)((transparentPixel & 0xFF0000) >> 16); + red = (byte)((transparentPixel & 0xFF00) >> 8); + break; + } + } + } + OS.SelectObject (srcHdc, oldSrcBitmap); + OS.SelectObject (memHdc, oldMemBitmap); + OS.DeleteObject (srcHdc); + OS.DeleteObject (memHdc); + OS.ReleaseDC (0, hDC); + byte [] srcData = new byte [sizeInBytes]; + OS.MoveMemory (srcData, pBits [0], sizeInBytes); + if (alpha != -1) { + for (int y = 0, dp = 0; y < imgHeight; ++y) { + for (int x = 0; x < imgWidth; ++x) { + srcData [dp + 3] = (byte)alpha; + dp += 4; + } + } + } else if (alphaData != null) { + for (int y = 0, dp = 0, ap = 0; y < imgHeight; ++y) { + for (int x = 0; x < imgWidth; ++x) { + srcData [dp + 3] = alphaData [ap++]; + dp += 4; + } + } + } else if (transparentPixel != -1) { + for (int y = 0, dp = 0; y < imgHeight; ++y) { + for (int x = 0; x < imgWidth; ++x) { + if (srcData [dp] == blue && srcData [dp + 1] == green && srcData [dp + 2] == red) { + srcData [dp + 3] = (byte)0; + } else { + srcData [dp + 3] = (byte)0xFF; + } + dp += 4; + } + } + } + OS.MoveMemory (pBits [0], srcData, sizeInBytes); + return memDib; +} + +static Image createIcon (Image image) { + Device device = image.getDevice (); + ImageData data = image.getImageData (); + if (data.alpha == -1 && data.alphaData == null) { + ImageData mask = data.getTransparencyMask (); + return new Image (device, data, mask); + } + int width = data.width, height = data.height; + int /*long*/ hMask, hBitmap; + int /*long*/ hDC = device.internal_new_GC (null); + int /*long*/ dstHdc = OS.CreateCompatibleDC (hDC), oldDstBitmap; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) { + hBitmap = Display.create32bitDIB (image.handle, data.alpha, data.alphaData, data.transparentPixel); + hMask = OS.CreateBitmap (width, height, 1, 1, null); + oldDstBitmap = OS.SelectObject (dstHdc, hMask); + OS.PatBlt (dstHdc, 0, 0, width, height, OS.BLACKNESS); + } else { + hMask = Display.createMaskFromAlpha (data, width, height); + /* Icons need black pixels where the mask is transparent */ + hBitmap = OS.CreateCompatibleBitmap (hDC, width, height); + oldDstBitmap = OS.SelectObject (dstHdc, hBitmap); + int /*long*/ srcHdc = OS.CreateCompatibleDC (hDC); + int /*long*/ oldSrcBitmap = OS.SelectObject (srcHdc, image.handle); + OS.PatBlt (dstHdc, 0, 0, width, height, OS.BLACKNESS); + OS.BitBlt (dstHdc, 0, 0, width, height, srcHdc, 0, 0, OS.SRCINVERT); + OS.SelectObject (srcHdc, hMask); + OS.BitBlt (dstHdc, 0, 0, width, height, srcHdc, 0, 0, OS.SRCAND); + OS.SelectObject (srcHdc, image.handle); + OS.BitBlt (dstHdc, 0, 0, width, height, srcHdc, 0, 0, OS.SRCINVERT); + OS.SelectObject (srcHdc, oldSrcBitmap); + OS.DeleteDC (srcHdc); + } + OS.SelectObject (dstHdc, oldDstBitmap); + OS.DeleteDC (dstHdc); + device.internal_dispose_GC (hDC, null); + ICONINFO info = new ICONINFO (); + info.fIcon = true; + info.hbmColor = hBitmap; + info.hbmMask = hMask; + int /*long*/ hIcon = OS.CreateIconIndirect (info); + if (hIcon == 0) SWT.error(SWT.ERROR_NO_HANDLES); + OS.DeleteObject (hBitmap); + OS.DeleteObject (hMask); + return Image.win32_new (device, SWT.ICON, hIcon); +} + +static int /*long*/ createMaskFromAlpha (ImageData data, int destWidth, int destHeight) { + int srcWidth = data.width; + int srcHeight = data.height; + ImageData mask = ImageData.internal_new (srcWidth, srcHeight, 1, + new PaletteData(new RGB [] {new RGB (0, 0, 0), new RGB (0xff, 0xff, 0xff)}), + 2, null, 1, null, null, -1, -1, -1, 0, 0, 0, 0); + int ap = 0; + for (int y = 0; y < mask.height; y++) { + for (int x = 0; x < mask.width; x++) { + mask.setPixel (x, y, (data.alphaData [ap++] & 0xff) <= 127 ? 1 : 0); + } + } + int /*long*/ hMask = OS.CreateBitmap (srcWidth, srcHeight, 1, 1, mask.data); + if (srcWidth != destWidth || srcHeight != destHeight) { + int /*long*/ hdc = OS.GetDC (0); + int /*long*/ hdc1 = OS.CreateCompatibleDC (hdc); + OS.SelectObject (hdc1, hMask); + int /*long*/ hdc2 = OS.CreateCompatibleDC (hdc); + int /*long*/ hMask2 = OS.CreateBitmap (destWidth, destHeight, 1, 1, null); + OS.SelectObject (hdc2, hMask2); + if (!OS.IsWinCE) OS.SetStretchBltMode(hdc2, OS.COLORONCOLOR); + OS.StretchBlt (hdc2, 0, 0, destWidth, destHeight, hdc1, 0, 0, srcWidth, srcHeight, OS.SRCCOPY); + OS.DeleteDC (hdc1); + OS.DeleteDC (hdc2); + OS.ReleaseDC (0, hdc); + OS.DeleteObject(hMask); + hMask = hMask2; + } + return hMask; +} + +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 () { +} + +/** + * 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 drawMenuBars () { + if (bars == null) return; + for (int i=0; i<bars.length; i++) { + Menu menu = bars [i]; + if (menu != null && !menu.isDisposed ()) menu.update (); + } + bars = null; +} + +int /*long*/ embeddedProc (int /*long*/ hwnd, int /*long*/ msg, int /*long*/ wParam, int /*long*/ lParam) { + switch ((int)/*64*/msg) { + case SWT_KEYMSG: { + MSG keyMsg = new MSG (); + OS.MoveMemory (keyMsg, lParam, MSG.sizeof); + OS.TranslateMessage (keyMsg); + OS.DispatchMessage (keyMsg); + int /*long*/ hHeap = OS.GetProcessHeap (); + OS.HeapFree (hHeap, 0, lParam); + break; + } + case SWT_DESTROY: { + OS.DestroyWindow (hwnd); + if (embeddedCallback != null) embeddedCallback.dispose (); + if (getMsgCallback != null) getMsgCallback.dispose (); + embeddedCallback = getMsgCallback = null; + embeddedProc = getMsgProc = 0; + break; + } + } + return OS.DefWindowProc (hwnd, (int)/*64*/msg, wParam, lParam); +} + +/** + * Does whatever display specific cleanup is required, and then + * uses the code in <code>SWTError.error</code> to handle the error. + * + * @param code the descriptive error code + * + * @see SWT#error(int) + */ +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); +} + +boolean filterMessage (MSG msg) { + int message = msg.message; + if (OS.WM_KEYFIRST <= message && message <= OS.WM_KEYLAST) { + Control control = findControl (msg.hwnd); + if (control != null) { + if (translateAccelerator (msg, control) || translateMnemonic (msg, control) || translateTraversal (msg, control)) { + lastAscii = lastKey = 0; + lastVirtual = lastNull = lastDead = false; + return true; + } + } + } + return false; +} + +Control findControl (int /*long*/ handle) { + if (handle == 0) return null; + int /*long*/ hwndOwner = 0; + do { + Control control = getControl (handle); + if (control != null) return control; + hwndOwner = OS.GetWindow (handle, OS.GW_OWNER); + handle = OS.GetParent (handle); + } while (handle != 0 && handle != hwndOwner); + return null; +} + +/** + * 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 getControl (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 /*long*/ id) { + checkDevice (); + //TODO - should ids be long + Control control = getControl (handle); + return control != null ? control.findItem (id) : null; +} + +/** + * 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 /*long*/ id) { + checkDevice (); + //TODO - should ids be long + if (widget instanceof Control) { + return findWidget (((Control) widget).handle, id); + } + return null; +} + +int /*long*/ foregroundIdleProc (int /*long*/ code, int /*long*/ wParam, int /*long*/ lParam) { + if (code >= 0) { + if (runMessages && getMessageCount () != 0) { + if (runMessagesInIdle) { + if (runMessagesInMessageProc) { + OS.PostMessage (hwndMessage, SWT_RUNASYNC, 0, 0); + } else { + runAsyncMessages (false); + } + } + MSG msg = new MSG(); + int flags = OS.PM_NOREMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE; + if (!OS.PeekMessage (msg, 0, 0, 0, flags)) wakeThread (); + } + } + return OS.CallNextHookEx (idleHook, (int)/*64*/code, wParam, lParam); +} + +/** + * 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 (); + Control control = findControl (OS.GetActiveWindow ()); + return control != null ? control.getShell () : 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 (); + if (OS.GetSystemMetrics (OS.SM_CMONITORS) < 2) { + int width = OS.GetSystemMetrics (OS.SM_CXSCREEN); + int height = OS.GetSystemMetrics (OS.SM_CYSCREEN); + return new Rectangle (0, 0, width, height); + } + int x = OS.GetSystemMetrics (OS.SM_XVIRTUALSCREEN); + int y = OS.GetSystemMetrics (OS.SM_YVIRTUALSCREEN); + int width = OS.GetSystemMetrics (OS.SM_CXVIRTUALSCREEN); + int height = OS.GetSystemMetrics (OS.SM_CYVIRTUALSCREEN); + return new Rectangle (x, y, width, height); +} + +/** + * 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 getClickCount (int type, int button, int /*long*/ hwnd, int /*long*/ lParam) { + switch (type) { + case SWT.MouseDown: + int doubleClick = OS.GetDoubleClickTime (); + if (clickRect == null) clickRect = new RECT (); + int deltaTime = Math.abs (lastTime - getLastEventTime ()); + POINT pt = new POINT (); + OS.POINTSTOPOINT (pt, lParam); + if (lastClickHwnd == hwnd && lastButton == button && (deltaTime <= doubleClick) && OS.PtInRect (clickRect, pt)) { + clickCount++; + } else { + clickCount = 1; + } + //FALL THROUGH + case SWT.MouseDoubleClick: + lastButton = button; + lastClickHwnd = hwnd; + lastTime = getLastEventTime (); + int xInset = OS.GetSystemMetrics (OS.SM_CXDOUBLECLK) / 2; + int yInset = OS.GetSystemMetrics (OS.SM_CYDOUBLECLK) / 2; + int x = OS.GET_X_LPARAM (lParam), y = OS.GET_Y_LPARAM (lParam); + OS.SetRect (clickRect, x - xInset, y - yInset, x + xInset, y + yInset); + //FALL THROUGH + case SWT.MouseUp: + return clickCount; + } + return 0; +} + +/** + * 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 (); + if (OS.GetSystemMetrics (OS.SM_CMONITORS) < 2) { + RECT rect = new RECT (); + OS.SystemParametersInfo (OS.SPI_GETWORKAREA, 0, rect, 0); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + return new Rectangle (rect.left, rect.top, width, height); + } + int x = OS.GetSystemMetrics (OS.SM_XVIRTUALSCREEN); + int y = OS.GetSystemMetrics (OS.SM_YVIRTUALSCREEN); + int width = OS.GetSystemMetrics (OS.SM_CXVIRTUALSCREEN); + int height = OS.GetSystemMetrics (OS.SM_CYVIRTUALSCREEN); + return new Rectangle (x, y, width, height); +} + +Control getControl (int /*long*/ handle) { + if (handle == 0) return null; + if (lastControl != null && lastHwnd == handle) { + return lastControl; + } + if (lastGetControl != null && lastGetHwnd == handle) { + return lastGetControl; + } + int index; + if (USE_PROPERTY) { + index = (int)/*64*/OS.GetProp (handle, SWT_OBJECT_INDEX) - 1; + } else { + index = (int)/*64*/OS.GetWindowLongPtr (handle, OS.GWLP_USERDATA) - 1; + } + if (0 <= index && index < controlTable.length) { + Control control = controlTable [index]; + /* + * Because GWL_USERDATA can be used by native widgets that + * do not belong to SWT, it is possible that GWL_USERDATA + * could return an index that is in the range of the table, + * but was not put there by SWT. Therefore, it is necessary + * to check the handle of the control that is in the table + * against the handle that provided the GWL_USERDATA. + */ + if (control != null && control.checkHandle (handle)) { + lastGetHwnd = handle; + lastGetControl = control; + return control; + } + } + return null; +} + +/** + * 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 (); + POINT pt = new POINT (); + if (!OS.GetCursorPos (pt)) return null; + return findControl (OS.WindowFromPoint (pt)); +} + +/** + * 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 (); + POINT pt = new POINT (); + OS.GetCursorPos (pt); + return new Point (pt.x, pt.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 (OS.GetSystemMetrics (OS.SM_CXCURSOR), OS.GetSystemMetrics (OS.SM_CYCURSOR))}; +} + +/** + * 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; + } +} + +static boolean isValidClass (Class clazz) { + String name = clazz.getName (); + int index = name.lastIndexOf ('.'); + return name.substring (0, index + 1).equals (PACKAGE_PREFIX); +} + +/** + * 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 (key.equals (RUN_MESSAGES_IN_IDLE_KEY)) { + return new Boolean (runMessagesInIdle); + } + if (key.equals (RUN_MESSAGES_IN_MESSAGE_PROC_KEY)) { + return new Boolean (runMessagesInMessageProc); + } + if (key.equals (USE_OWNDC_KEY)) { + return new Boolean (useOwnDC); + } + 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.LEFT; +} + + +/** + * 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.GetDoubleClickTime (); +} + +/** + * 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; + } + return _getFocusControl (); +} + +String getFontName (LOGFONT logFont) { + char[] chars; + if (OS.IsUnicode) { + chars = ((LOGFONTW)logFont).lfFaceName; + } else { + chars = new char[OS.LF_FACESIZE]; + byte[] bytes = ((LOGFONTA)logFont).lfFaceName; + OS.MultiByteToWideChar (OS.CP_ACP, OS.MB_PRECOMPOSED, bytes, bytes.length, chars, chars.length); + } + int index = 0; + while (index < chars.length) { + if (chars [index] == 0) break; + index++; + } + return new String (chars, 0, index); +} + +/** + * 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 (); + if (OS.IsWinCE) return false; + HIGHCONTRAST pvParam = new HIGHCONTRAST (); + pvParam.cbSize = HIGHCONTRAST.sizeof; + OS.SystemParametersInfo (OS.SPI_GETHIGHCONTRAST, 0, pvParam, 0); + return (pvParam.dwFlags & OS.HCF_HIGHCONTRASTON) != 0; +} + +/** + * 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 () { + checkDevice (); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) { + if (getDepth () >= 24) return 32; + } + + /* Use the character encoding for the default locale */ + TCHAR buffer1 = new TCHAR (0, "Control Panel\\Desktop\\WindowMetrics", true); //$NON-NLS-1$ + + int /*long*/ [] phkResult = new int /*long*/ [1]; + int result = OS.RegOpenKeyEx (OS.HKEY_CURRENT_USER, buffer1, 0, OS.KEY_READ, phkResult); + if (result != 0) return 4; + int depth = 4; + int [] lpcbData = new int [1]; + + /* Use the character encoding for the default locale */ + TCHAR buffer2 = new TCHAR (0, "Shell Icon BPP", true); //$NON-NLS-1$ + result = OS.RegQueryValueEx (phkResult [0], buffer2, 0, null, (TCHAR) null, lpcbData); + if (result == 0) { + TCHAR lpData = new TCHAR (0, lpcbData [0] / TCHAR.sizeof); + result = OS.RegQueryValueEx (phkResult [0], buffer2, 0, null, lpData, lpcbData); + if (result == 0) { + try { + depth = Integer.parseInt (lpData.toString (0, lpData.strlen ())); + } catch (NumberFormatException e) {} + } + } + OS.RegCloseKey (phkResult [0]); + return depth; +} + +/** + * 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 (OS.GetSystemMetrics (OS.SM_CXSMICON), OS.GetSystemMetrics (OS.SM_CYSMICON)), + new Point (OS.GetSystemMetrics (OS.SM_CXICON), OS.GetSystemMetrics (OS.SM_CYICON)), + }; +} + +ImageList getImageList (int style, int width, int height) { + if (imageList == null) imageList = new ImageList [4]; + + int i = 0; + int length = imageList.length; + while (i < length) { + ImageList list = imageList [i]; + if (list == null) break; + Point size = list.getImageSize(); + if (size.x == width && size.y == height) { + if (list.getStyle () == style) { + list.addRef(); + return list; + } + } + i++; + } + + if (i == length) { + ImageList [] newList = new ImageList [length + 4]; + System.arraycopy (imageList, 0, newList, 0, length); + imageList = newList; + } + + ImageList list = new ImageList (style); + imageList [i] = list; + list.addRef(); + return list; +} + +ImageList getImageListToolBar (int style, int width, int height) { + if (toolImageList == null) toolImageList = new ImageList [4]; + + int i = 0; + int length = toolImageList.length; + while (i < length) { + ImageList list = toolImageList [i]; + if (list == null) break; + Point size = list.getImageSize(); + if (size.x == width && size.y == height) { + if (list.getStyle () == style) { + list.addRef(); + return list; + } + } + i++; + } + + if (i == length) { + ImageList [] newList = new ImageList [length + 4]; + System.arraycopy (toolImageList, 0, newList, 0, length); + toolImageList = newList; + } + + ImageList list = new ImageList (style); + toolImageList [i] = list; + list.addRef(); + return list; +} + +ImageList getImageListToolBarDisabled (int style, int width, int height) { + if (toolDisabledImageList == null) toolDisabledImageList = new ImageList [4]; + + int i = 0; + int length = toolDisabledImageList.length; + while (i < length) { + ImageList list = toolDisabledImageList [i]; + if (list == null) break; + Point size = list.getImageSize(); + if (size.x == width && size.y == height) { + if (list.getStyle () == style) { + list.addRef(); + return list; + } + } + i++; + } + + if (i == length) { + ImageList [] newList = new ImageList [length + 4]; + System.arraycopy (toolDisabledImageList, 0, newList, 0, length); + toolDisabledImageList = newList; + } + + ImageList list = new ImageList (style); + toolDisabledImageList [i] = list; + list.addRef(); + return list; +} + +ImageList getImageListToolBarHot (int style, int width, int height) { + if (toolHotImageList == null) toolHotImageList = new ImageList [4]; + + int i = 0; + int length = toolHotImageList.length; + while (i < length) { + ImageList list = toolHotImageList [i]; + if (list == null) break; + Point size = list.getImageSize(); + if (size.x == width && size.y == height) { + if (list.getStyle () == style) { + list.addRef(); + return list; + } + } + i++; + } + + if (i == length) { + ImageList [] newList = new ImageList [length + 4]; + System.arraycopy (toolHotImageList, 0, newList, 0, length); + toolHotImageList = newList; + } + + ImageList list = new ImageList (style); + toolHotImageList [i] = list; + list.addRef(); + return list; +} + +int getLastEventTime () { + return OS.IsWinCE ? OS.GetTickCount () : OS.GetMessageTime (); +} + +MenuItem getMenuItem (int id) { + if (items == null) return null; + id = id - ID_START; + if (0 <= id && id < items.length) return items [id]; + return null; +} + +int getMessageCount () { + return synchronizer.getMessageCount (); +} + + +Shell getModalShell () { + if (modalShells == null) return null; + int index = modalShells.length; + while (--index >= 0) { + Shell shell = modalShells [index]; + if (shell != null) return shell; + } + return null; +} + +Dialog getModalDialog () { + return modalDialog; +} + +/** + * Returns an array of monitors attached to the device. + * + * @return the array of monitors + * + * @since 3.0 + */ +public Monitor [] getMonitors () { + checkDevice (); + if (OS.IsWinCE || OS.WIN32_VERSION < OS.VERSION (4, 10)) { + return new Monitor [] {getPrimaryMonitor ()}; + } + monitors = new Monitor [4]; + Callback callback = new Callback (this, "monitorEnumProc", 4); //$NON-NLS-1$ + int /*long*/ lpfnEnum = callback.getAddress (); + if (lpfnEnum == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + OS.EnumDisplayMonitors (0, null, lpfnEnum, 0); + callback.dispose (); + Monitor [] result = new Monitor [monitorCount]; + System.arraycopy (monitors, 0, result, 0, monitorCount); + monitors = null; + monitorCount = 0; + return result; +} + +int /*long*/ getMsgProc (int /*long*/ code, int /*long*/ wParam, int /*long*/ lParam) { + if (embeddedHwnd == 0) { + int /*long*/ hInstance = OS.GetModuleHandle (null); + embeddedHwnd = OS.CreateWindowEx (0, + windowClass, + null, + OS.WS_OVERLAPPED, + 0, 0, 0, 0, + 0, + 0, + hInstance, + null); + embeddedCallback = new Callback (this, "embeddedProc", 4); //$NON-NLS-1$ + embeddedProc = embeddedCallback.getAddress (); + if (embeddedProc == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + OS.SetWindowLongPtr (embeddedHwnd, OS.GWLP_WNDPROC, embeddedProc); + } + if (code >= 0 && (wParam & OS.PM_REMOVE) != 0) { + MSG msg = new MSG (); + OS.MoveMemory (msg, lParam, MSG.sizeof); + switch (msg.message) { + case OS.WM_KEYDOWN: + case OS.WM_KEYUP: + case OS.WM_SYSKEYDOWN: + case OS.WM_SYSKEYUP: { + Control control = findControl (msg.hwnd); + if (control != null) { + int /*long*/ hHeap = OS.GetProcessHeap (); + int /*long*/ keyMsg = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, MSG.sizeof); + OS.MoveMemory (keyMsg, msg, MSG.sizeof); + OS.PostMessage (hwndMessage, SWT_KEYMSG, wParam, keyMsg); + switch ((int)/*64*/msg.wParam) { + case OS.VK_SHIFT: + case OS.VK_MENU: + case OS.VK_CONTROL: + case OS.VK_CAPITAL: + case OS.VK_NUMLOCK: + case OS.VK_SCROLL: + break; + default: + msg.message = OS.WM_NULL; + OS.MoveMemory (lParam, msg, MSG.sizeof); + } + } + } + } + } + return OS.CallNextHookEx (msgHook, (int)/*64*/code, wParam, lParam); +} + +/** + * Returns the primary monitor for that device. + * + * @return the primary monitor + * + * @since 3.0 + */ +public Monitor getPrimaryMonitor () { + checkDevice (); + if (OS.IsWinCE || OS.WIN32_VERSION < OS.VERSION (4, 10)) { + Monitor monitor = new Monitor(); + int width = OS.GetSystemMetrics (OS.SM_CXSCREEN); + int height = OS.GetSystemMetrics (OS.SM_CYSCREEN); + monitor.width = width; + monitor.height = height; + RECT rect = new RECT (); + OS.SystemParametersInfo (OS.SPI_GETWORKAREA, 0, rect, 0); + monitor.clientX = rect.left; + monitor.clientY = rect.top; + monitor.clientWidth = rect.right - rect.left; + monitor.clientHeight = rect.bottom - rect.top; + return monitor; + } + monitors = new Monitor [4]; + Callback callback = new Callback (this, "monitorEnumProc", 4); //$NON-NLS-1$ + int /*long*/ lpfnEnum = callback.getAddress (); + if (lpfnEnum == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + OS.EnumDisplayMonitors (0, null, lpfnEnum, 0); + callback.dispose (); + Monitor result = null; + MONITORINFO lpmi = new MONITORINFO (); + lpmi.cbSize = MONITORINFO.sizeof; + for (int i = 0; i < monitorCount; i++) { + Monitor monitor = monitors [i]; + OS.GetMonitorInfo (monitors [i].handle, lpmi); + if ((lpmi.dwFlags & OS.MONITORINFOF_PRIMARY) != 0) { + result = monitor; + break; + } + } + monitors = null; + monitorCount = 0; + return result; +} + +/** + * 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 (); + int index = 0; + Shell [] result = new Shell [16]; + for (int i = 0; i < controlTable.length; i++) { + Control control = controlTable [i]; + if (control != null && control instanceof Shell) { + int j = 0; + while (j < index) { + if (result [j] == control) break; + j++; + } + if (j == index) { + if (index == result.length) { + Shell [] newResult = new Shell [index + 16]; + System.arraycopy (result, 0, newResult, 0, index); + result = newResult; + } + result [index++] = (Shell) control; + } + } + } + if (index == result.length) return result; + Shell [] newResult = new Shell [index]; + System.arraycopy (result, 0, newResult, 0, index); + return newResult; +} + +Image getSortImage (int direction) { + switch (direction) { + case SWT.UP: { + if (upArrow != null) return upArrow; + Color c1 = getSystemColor (SWT.COLOR_WIDGET_NORMAL_SHADOW); + Color c2 = getSystemColor (SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW); + Color c3 = getSystemColor (SWT.COLOR_WIDGET_BACKGROUND); + PaletteData palette = new PaletteData(new RGB [] {c1.getRGB (), c2.getRGB (), c3.getRGB ()}); + ImageData imageData = new ImageData (8, 8, 4, palette); + imageData.transparentPixel = 2; + upArrow = new Image (this, imageData); + GC gc = new GC (upArrow); + gc.setBackground (c3); + gc.fillRectangle (0, 0, 8, 8); + gc.setForeground (c1); + int [] line1 = new int [] {0,6, 1,6, 1,4, 2,4, 2,2, 3,2, 3,1}; + gc.drawPolyline (line1); + gc.setForeground (c2); + int [] line2 = new int [] {0,7, 7,7, 7,6, 6,6, 6,4, 5,4, 5,2, 4,2, 4,1}; + gc.drawPolyline (line2); + gc.dispose (); + return upArrow; + } + case SWT.DOWN: { + if (downArrow != null) return downArrow; + Color c1 = getSystemColor (SWT.COLOR_WIDGET_NORMAL_SHADOW); + Color c2 = getSystemColor (SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW); + Color c3 = getSystemColor (SWT.COLOR_WIDGET_BACKGROUND); + PaletteData palette = new PaletteData (new RGB [] {c1.getRGB (), c2.getRGB (), c3.getRGB ()}); + ImageData imageData = new ImageData (8, 8, 4, palette); + imageData.transparentPixel = 2; + downArrow = new Image (this, imageData); + GC gc = new GC (downArrow); + gc.setBackground (c3); + gc.fillRectangle (0, 0, 8, 8); + gc.setForeground (c1); + int [] line1 = new int [] {7,0, 0,0, 0,1, 1,1, 1,3, 2,3, 2,5, 3,5, 3,6}; + gc.drawPolyline (line1); + gc.setForeground (c2); + int [] line2 = new int [] {4,6, 4,5, 5,5, 5,3, 6,3, 6,1, 7,1}; + gc.drawPolyline (line2); + gc.dispose (); + return downArrow; + } + } + return null; +} + +/** + * 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 (); + int pixel = 0x00000000; + switch (id) { + case SWT.COLOR_WIDGET_DARK_SHADOW: pixel = OS.GetSysColor (OS.COLOR_3DDKSHADOW); break; + case SWT.COLOR_WIDGET_NORMAL_SHADOW: pixel = OS.GetSysColor (OS.COLOR_3DSHADOW); break; + case SWT.COLOR_WIDGET_LIGHT_SHADOW: pixel = OS.GetSysColor (OS.COLOR_3DLIGHT); break; + case SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW: pixel = OS.GetSysColor (OS.COLOR_3DHIGHLIGHT); break; + case SWT.COLOR_WIDGET_BACKGROUND: pixel = OS.GetSysColor (OS.COLOR_3DFACE); break; + case SWT.COLOR_WIDGET_BORDER: pixel = OS.GetSysColor (OS.COLOR_WINDOWFRAME); break; + case SWT.COLOR_WIDGET_FOREGROUND: + case SWT.COLOR_LIST_FOREGROUND: pixel = OS.GetSysColor (OS.COLOR_WINDOWTEXT); break; + case SWT.COLOR_LIST_BACKGROUND: pixel = OS.GetSysColor (OS.COLOR_WINDOW); break; + case SWT.COLOR_LIST_SELECTION: pixel = OS.GetSysColor (OS.COLOR_HIGHLIGHT); break; + case SWT.COLOR_LIST_SELECTION_TEXT: pixel = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);break; + case SWT.COLOR_INFO_FOREGROUND: pixel = OS.GetSysColor (OS.COLOR_INFOTEXT); break; + case SWT.COLOR_INFO_BACKGROUND: pixel = OS.GetSysColor (OS.COLOR_INFOBK); break; + case SWT.COLOR_TITLE_FOREGROUND: pixel = OS.GetSysColor (OS.COLOR_CAPTIONTEXT); break; + case SWT.COLOR_TITLE_BACKGROUND: pixel = OS.GetSysColor (OS.COLOR_ACTIVECAPTION); break; + case SWT.COLOR_TITLE_BACKGROUND_GRADIENT: + pixel = OS.GetSysColor (OS.COLOR_GRADIENTACTIVECAPTION); + if (pixel == 0) pixel = OS.GetSysColor (OS.COLOR_ACTIVECAPTION); + break; + case SWT.COLOR_TITLE_INACTIVE_FOREGROUND: pixel = OS.GetSysColor (OS.COLOR_INACTIVECAPTIONTEXT); break; + case SWT.COLOR_TITLE_INACTIVE_BACKGROUND: pixel = OS.GetSysColor (OS.COLOR_INACTIVECAPTION); break; + case SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT: + pixel = OS.GetSysColor (OS.COLOR_GRADIENTINACTIVECAPTION); + if (pixel == 0) pixel = OS.GetSysColor (OS.COLOR_INACTIVECAPTION); + break; + default: + return super.getSystemColor (id); + } + return Color.win32_new (this, pixel); +} + +/** + * 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 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 free'd 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_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 Font getSystemFont () { + checkDevice (); + if (systemFont != null) return systemFont; + int /*long*/ hFont = 0; + if (!OS.IsWinCE) { + NONCLIENTMETRICS info = OS.IsUnicode ? (NONCLIENTMETRICS) new NONCLIENTMETRICSW () : new NONCLIENTMETRICSA (); + info.cbSize = NONCLIENTMETRICS.sizeof; + if (OS.SystemParametersInfo (OS.SPI_GETNONCLIENTMETRICS, 0, info, 0)) { + LOGFONT logFont = OS.IsUnicode ? (LOGFONT) ((NONCLIENTMETRICSW)info).lfMessageFont : ((NONCLIENTMETRICSA)info).lfMessageFont; + hFont = OS.CreateFontIndirect (logFont); + lfSystemFont = hFont != 0 ? logFont : null; + } + } + if (hFont == 0) hFont = OS.GetStockObject (OS.DEFAULT_GUI_FONT); + if (hFont == 0) hFont = OS.GetStockObject (OS.SYSTEM_FONT); + return systemFont = Font.win32_new (this, hFont); +} + +/** + * 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; + int /*long*/ hIcon = OS.LoadImage (0, OS.OIC_HAND, OS.IMAGE_ICON, 0, 0, OS.LR_SHARED); + return errorImage = Image.win32_new (this, SWT.ICON, hIcon); + } + case SWT.ICON_WORKING: + case SWT.ICON_INFORMATION: { + if (infoImage != null) return infoImage; + int /*long*/ hIcon = OS.LoadImage (0, OS.OIC_INFORMATION, OS.IMAGE_ICON, 0, 0, OS.LR_SHARED); + return infoImage = Image.win32_new (this, SWT.ICON, hIcon); + } + case SWT.ICON_QUESTION: { + if (questionImage != null) return questionImage; + int /*long*/ hIcon = OS.LoadImage (0, OS.OIC_QUES, OS.IMAGE_ICON, 0, 0, OS.LR_SHARED); + return questionImage = Image.win32_new (this, SWT.ICON, hIcon); + } + case SWT.ICON_WARNING: { + if (warningIcon != null) return warningIcon; + int /*long*/ hIcon = OS.LoadImage (0, OS.OIC_BANG, OS.IMAGE_ICON, 0, 0, OS.LR_SHARED); + return warningIcon = Image.win32_new (this, SWT.ICON, hIcon); + } + } + 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; + if (!OS.IsWinCE) tray = new Tray (this, SWT.NONE); + return tray; +} + +/** + * 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 /*long*/ hButtonTheme () { + if (hButtonTheme != 0) return hButtonTheme; + return hButtonTheme = OS.OpenThemeData (hwndMessage, BUTTON); +} + +int /*long*/ hEditTheme () { + if (hEditTheme != 0) return hEditTheme; + return hEditTheme = OS.OpenThemeData (hwndMessage, EDIT); +} + +int /*long*/ hExplorerBarTheme () { + if (hExplorerBarTheme != 0) return hExplorerBarTheme; + return hExplorerBarTheme = OS.OpenThemeData (hwndMessage, EXPLORERBAR); +} + +int /*long*/ hScrollBarTheme () { + if (hScrollBarTheme != 0) return hScrollBarTheme; + return hScrollBarTheme = OS.OpenThemeData (hwndMessage, SCROLLBAR); +} + +int /*long*/ hTabTheme () { + if (hTabTheme != 0) return hTabTheme; + return hTabTheme = OS.OpenThemeData (hwndMessage, TAB); +} + +/** + * 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); + int /*long*/ hDC = OS.GetDC (0); + if (hDC == 0) SWT.error (SWT.ERROR_NO_HANDLES); + if (data != null) { + int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT; + if ((data.style & mask) != 0) { + data.layout = (data.style & SWT.RIGHT_TO_LEFT) != 0 ? OS.LAYOUT_RTL : 0; + } else { + data.style |= SWT.LEFT_TO_RIGHT; + } + data.device = this; + data.font = getSystemFont (); + } + return hDC; +} + +/** + * 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 (); + + /* Create the callbacks */ + windowCallback = new Callback (this, "windowProc", 4); //$NON-NLS-1$ + windowProc = windowCallback.getAddress (); + if (windowProc == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + + /* Remember the current thread id */ + threadId = OS.GetCurrentThreadId (); + + /* Use the character encoding for the default locale */ + windowClass = new TCHAR (0, WindowName + WindowClassCount, true); + windowShadowClass = new TCHAR (0, WindowShadowName + WindowClassCount, true); + windowOwnDCClass = new TCHAR (0, WindowOwnDCName + WindowClassCount, true); + WindowClassCount++; + + /* Register the SWT window class */ + int /*long*/ hHeap = OS.GetProcessHeap (); + int /*long*/ hInstance = OS.GetModuleHandle (null); + WNDCLASS lpWndClass = new WNDCLASS (); + lpWndClass.hInstance = hInstance; + lpWndClass.lpfnWndProc = windowProc; + lpWndClass.style = OS.CS_BYTEALIGNWINDOW | OS.CS_DBLCLKS; + lpWndClass.hCursor = OS.LoadCursor (0, OS.IDC_ARROW); + /* + * Set the default icon for the window class to IDI_APPLICATION. + * This is not necessary for native Windows applications but + * versions of Java starting at JDK 1.6 set the icon in the + * executable instead of leaving the default. + */ + if (!OS.IsWinCE && Library.JAVA_VERSION >= Library.JAVA_VERSION (1, 6, 0)) { + TCHAR lpszFile = new TCHAR (0, OS.MAX_PATH); + while (OS.GetModuleFileName (0, lpszFile, lpszFile.length ()) == lpszFile.length ()) { + lpszFile = new TCHAR (0, lpszFile.length () + OS.MAX_PATH); + } + if (OS.ExtractIconEx (lpszFile, -1, null, null, 1) != 0) { + String fileName = lpszFile.toString (0, lpszFile.strlen ()); + if (fileName.endsWith ("java.exe") || fileName.endsWith ("javaw.exe")) { //$NON-NLS-1$ //$NON-NLS-2$ + lpWndClass.hIcon = OS.LoadIcon (0, OS.IDI_APPLICATION); + } + } + } + int byteCount = windowClass.length () * TCHAR.sizeof; + lpWndClass.lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (lpWndClass.lpszClassName, windowClass, byteCount); + OS.RegisterClass (lpWndClass); + OS.HeapFree (hHeap, 0, lpWndClass.lpszClassName); + + /* Register the SWT drop shadow window class */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) { + lpWndClass.style |= OS.CS_DROPSHADOW; + } + byteCount = windowShadowClass.length () * TCHAR.sizeof; + lpWndClass.lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (lpWndClass.lpszClassName, windowShadowClass, byteCount); + OS.RegisterClass (lpWndClass); + OS.HeapFree (hHeap, 0, lpWndClass.lpszClassName); + + /* Register the CS_OWNDC window class */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) { + lpWndClass.style |= OS.CS_OWNDC; + } + byteCount = windowOwnDCClass.length () * TCHAR.sizeof; + lpWndClass.lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (lpWndClass.lpszClassName, windowOwnDCClass, byteCount); + OS.RegisterClass (lpWndClass); + OS.HeapFree (hHeap, 0, lpWndClass.lpszClassName); + + /* Create the message only HWND */ + hwndMessage = OS.CreateWindowEx (0, + windowClass, + null, + OS.WS_OVERLAPPED, + 0, 0, 0, 0, + 0, + 0, + hInstance, + null); + messageCallback = new Callback (this, "messageProc", 4); //$NON-NLS-1$ + messageProc = messageCallback.getAddress (); + if (messageProc == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + OS.SetWindowLongPtr (hwndMessage, OS.GWLP_WNDPROC, messageProc); + + /* Create the filter hook */ + if (!OS.IsWinCE) { + msgFilterCallback = new Callback (this, "msgFilterProc", 3); //$NON-NLS-1$ + msgFilterProc = msgFilterCallback.getAddress (); + if (msgFilterProc == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + filterHook = OS.SetWindowsHookEx (OS.WH_MSGFILTER, msgFilterProc, 0, threadId); + } + + /* Create the idle hook */ + if (!OS.IsWinCE) { + foregroundIdleCallback = new Callback (this, "foregroundIdleProc", 3); //$NON-NLS-1$ + foregroundIdleProc = foregroundIdleCallback.getAddress (); + if (foregroundIdleProc == 0) error (SWT.ERROR_NO_MORE_CALLBACKS); + idleHook = OS.SetWindowsHookEx (OS.WH_FOREGROUNDIDLE, foregroundIdleProc, 0, threadId); + } + + /* Register custom messages message */ + SWT_TASKBARCREATED = OS.RegisterWindowMessage (new TCHAR (0, "TaskbarCreated", true)); //$NON-NLS-1$ + SWT_RESTORECARET = OS.RegisterWindowMessage (new TCHAR (0, "SWT_RESTORECARET", true)); //$NON-NLS-1$ + DI_GETDRAGIMAGE = OS.RegisterWindowMessage (new TCHAR (0, "ShellGetDragImage", true)); //$NON-NLS-1$ + + /* Initialize OLE */ + if (!OS.IsWinCE) OS.OleInitialize (0); + + /* Initialize buffered painting */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)){ + OS.BufferedPaintInit (); + } + + /* Initialize the Widget Table */ + indexTable = new int [GROW_SIZE]; + controlTable = new Control [GROW_SIZE]; + for (int i=0; i<GROW_SIZE-1; i++) indexTable [i] = i + 1; + indexTable [GROW_SIZE - 1] = -1; + + /* Remember the last high contrast state */ + lastHighContrast = getHighContrast (); +} + +/** + * 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*/ hDC, GCData data) { + OS.ReleaseDC (0, hDC); +} + +boolean isXMouseActive () { + /* + * NOTE: X-Mouse is active when bit 1 of the UserPreferencesMask is set. + */ + boolean xMouseActive = false; + TCHAR key = new TCHAR (0, "Control Panel\\Desktop", true); //$NON-NLS-1$ + int /*long*/ [] phKey = new int /*long*/ [1]; + int result = OS.RegOpenKeyEx (OS.HKEY_CURRENT_USER, key, 0, OS.KEY_READ, phKey); + if (result == 0) { + TCHAR lpValueName = new TCHAR (0, "UserPreferencesMask", true); //$NON-NLS-1$ + int [] lpcbData = new int [] {4}, lpData = new int [1]; + result = OS.RegQueryValueEx (phKey [0], lpValueName, 0, null, lpData, lpcbData); + if (result == 0) xMouseActive = (lpData [0] & 0x01) != 0; + OS.RegCloseKey (phKey [0]); + } + return xMouseActive; +} + +boolean isValidThread () { + return thread == Thread.currentThread (); +} + +/** + * 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); + if (from == to) return new Point (x, y); + int /*long*/ hwndFrom = from != null ? from.handle : 0; + int /*long*/ hwndTo = to != null ? to.handle : 0; + POINT point = new POINT (); + point.x = x; + point.y = y; + OS.MapWindowPoints (hwndFrom, hwndTo, point, 1); + return new Point (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 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); + if (from == to) return new Rectangle (x, y, width, height); + int /*long*/ hwndFrom = from != null ? from.handle : 0; + int /*long*/ hwndTo = to != null ? to.handle : 0; + RECT rect = new RECT (); + rect.left = x; + rect.top = y; + rect.right = x + width; + rect.bottom = y + height; + OS.MapWindowPoints (hwndFrom, hwndTo, rect, 2); + return new Rectangle (rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); +} + +/* + * Returns a single character, converted from the default + * multi-byte character set (MBCS) used by the operating + * system widgets to a wide character set (WCS) used by Java. + * + * @param ch the MBCS character + * @return the WCS character + */ +static char mbcsToWcs (int ch) { + return mbcsToWcs (ch, 0); +} + +/* + * Returns a single character, converted from the specified + * multi-byte character set (MBCS) used by the operating + * system widgets to a wide character set (WCS) used by Java. + * + * @param ch the MBCS character + * @param codePage the code page used to convert the character + * @return the WCS character + */ +static char mbcsToWcs (int ch, int codePage) { + if (OS.IsUnicode) return (char) ch; + int key = ch & 0xFFFF; + if (key <= 0x7F) return (char) ch; + byte [] buffer; + if (key <= 0xFF) { + buffer = new byte [1]; + buffer [0] = (byte) key; + } else { + buffer = new byte [2]; + buffer [0] = (byte) ((key >> 8) & 0xFF); + buffer [1] = (byte) (key & 0xFF); + } + char [] unicode = new char [1]; + int cp = codePage != 0 ? codePage : OS.CP_ACP; + int count = OS.MultiByteToWideChar (cp, OS.MB_PRECOMPOSED, buffer, buffer.length, unicode, 1); + if (count == 0) return 0; + return unicode [0]; +} + +int /*long*/ messageProc (int /*long*/ hwnd, int /*long*/ msg, int /*long*/ wParam, int /*long*/ lParam) { + switch ((int)/*64*/msg) { + case SWT_RUNASYNC: { + if (runMessagesInIdle) runAsyncMessages (false); + break; + } + case SWT_KEYMSG: { + boolean consumed = false; + MSG keyMsg = new MSG (); + OS.MoveMemory (keyMsg, lParam, MSG.sizeof); + Control control = findControl (keyMsg.hwnd); + if (control != null) { + /* + * Feature in Windows. When the user types an accent key such + * as ^ in order to get an accented character on a German keyboard, + * calling TranslateMessage(), ToUnicode() or ToAscii() consumes + * the key. This means that a subsequent call to TranslateMessage() + * will see a regular key rather than the accented key. The fix + * is to use MapVirtualKey() and VkKeyScan () to detect an accent + * and avoid calls to TranslateMessage(). + */ + boolean accentKey = false; + switch (keyMsg.message) { + case OS.WM_KEYDOWN: + case OS.WM_SYSKEYDOWN: { + if (!OS.IsWinCE) { + switch ((int)/*64*/keyMsg.wParam) { + case OS.VK_SHIFT: + case OS.VK_MENU: + case OS.VK_CONTROL: + case OS.VK_CAPITAL: + case OS.VK_NUMLOCK: + case OS.VK_SCROLL: + break; + default: { + /* + * Bug in Windows. The high bit in the result of MapVirtualKey() on + * Windows NT is bit 32 while the high bit on Windows 95 is bit 16. + * They should both be bit 32. The fix is to test the right bit. + */ + int mapKey = OS.MapVirtualKey ((int)/*64*/keyMsg.wParam, 2); + if (mapKey != 0) { + accentKey = (mapKey & (OS.IsWinNT ? 0x80000000 : 0x8000)) != 0; + if (!accentKey) { + for (int i=0; i<ACCENTS.length; i++) { + int value = OS.VkKeyScan (ACCENTS [i]); + if (value != -1 && (value & 0xFF) == keyMsg.wParam) { + int state = value >> 8; + if ((OS.GetKeyState (OS.VK_SHIFT) < 0) == ((state & 0x1) != 0) && + (OS.GetKeyState (OS.VK_CONTROL) < 0) == ((state & 0x2) != 0) && + (OS.GetKeyState (OS.VK_MENU) < 0) == ((state & 0x4) != 0)) { + if ((state & 0x7) != 0) accentKey = true; + break; + } + } + } + } + } + break; + } + } + } + break; + } + } + if (!accentKey && !ignoreNextKey) { + keyMsg.hwnd = control.handle; + int flags = OS.PM_REMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE; + do { + if (!(consumed |= filterMessage (keyMsg))) { + OS.TranslateMessage (keyMsg); + consumed |= OS.DispatchMessage (keyMsg) == 1; + } + } while (OS.PeekMessage (keyMsg, keyMsg.hwnd, OS.WM_KEYFIRST, OS.WM_KEYLAST, flags)); + } + switch (keyMsg.message) { + case OS.WM_KEYDOWN: + case OS.WM_SYSKEYDOWN: { + switch ((int)/*64*/keyMsg.wParam) { + case OS.VK_SHIFT: + case OS.VK_MENU: + case OS.VK_CONTROL: + case OS.VK_CAPITAL: + case OS.VK_NUMLOCK: + case OS.VK_SCROLL: + break; + default: { + ignoreNextKey = accentKey; + break; + } + } + } + } + } + switch ((int)/*64*/keyMsg.wParam) { + case OS.VK_SHIFT: + case OS.VK_MENU: + case OS.VK_CONTROL: + case OS.VK_CAPITAL: + case OS.VK_NUMLOCK: + case OS.VK_SCROLL: + consumed = true; + } + if (consumed) { + int /*long*/ hHeap = OS.GetProcessHeap (); + OS.HeapFree (hHeap, 0, lParam); + } else { + OS.PostMessage (embeddedHwnd, SWT_KEYMSG, wParam, lParam); + } + return 0; + } + case SWT_TRAYICONMSG: { + if (tray != null) { + TrayItem [] items = tray.items; + for (int i=0; i<items.length; i++) { + TrayItem item = items [i]; + if (item != null && item.id == wParam) { + return item.messageProc (hwnd, (int)/*64*/msg, wParam, lParam); + } + } + } + return 0; + } + case OS.WM_ACTIVATEAPP: { + /* + * Feature in Windows. When multiple shells are + * disabled and one of the shells has an enabled + * dialog child and the user selects a disabled + * shell that does not have the enabled dialog + * child using the Task bar, Windows brings the + * disabled shell to the front. As soon as the + * user clicks on the disabled shell, the enabled + * dialog child comes to the front. This behavior + * is unspecified and seems strange. Normally, a + * disabled shell is frozen on the screen and the + * user cannot change the z-order by clicking with + * the mouse. The fix is to look for WM_ACTIVATEAPP + * and force the enabled dialog child to the front. + * This is typically what the user is expecting. + * + * NOTE: If the modal shell is disabled for any + * reason, it should not be brought to the front. + */ + if (wParam != 0) { + if (!isXMouseActive ()) { + int /*long*/ hwndActive = OS.GetActiveWindow (); + if (hwndActive != 0 && OS.IsWindowEnabled (hwndActive)) break; + Shell modal = modalDialog != null ? modalDialog.parent : getModalShell (); + if (modal != null && !modal.isDisposed ()) { + int /*long*/ hwndModal = modal.handle; + if (OS.IsWindowEnabled (hwndModal)) { + modal.bringToTop (); + if (modal.isDisposed ()) break; + } + int /*long*/ hwndPopup = OS.GetLastActivePopup (hwndModal); + if (hwndPopup != 0 && hwndPopup != modal.handle) { + if (getControl (hwndPopup) == null) { + if (OS.IsWindowEnabled (hwndPopup)) { + OS.SetActiveWindow (hwndPopup); + } + } + } + } + } + } + break; + } + case OS.WM_ENDSESSION: { + if (wParam != 0) { + dispose (); + /* + * When the session is ending, no SWT program can continue + * to run. In order to avoid running code after the display + * has been disposed, exit from Java. + */ + /* This code is intentionally commented */ +// System.exit (0); + } + break; + } + case OS.WM_QUERYENDSESSION: { + Event event = new Event (); + sendEvent (SWT.Close, event); + if (!event.doit) return 0; + break; + } + case OS.WM_DWMCOLORIZATIONCOLORCHANGED: { + sendSettings = true; + //FALL THROUGH + } + case OS.WM_SETTINGCHANGE: { + /* + * Bug in Windows. When high contrast is cleared using + * the key sequence, Alt + Left Shift + Print Screen, the + * system parameter is set to false, but WM_SETTINGCHANGE + * is not sent with SPI_SETHIGHCONTRAST. The fix is to + * detect the change when any WM_SETTINGCHANGE message + * is sent. + */ + if (lastHighContrast != getHighContrast ()) { + sendSettings = true; + lastHighContrast = getHighContrast (); + } + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + sendSettings = true; + } + switch ((int)/*64*/wParam) { + case 0: + case 1: + case OS.SPI_SETHIGHCONTRAST: { + sendSettings = true; + lastHighContrast = getHighContrast (); + } + } + /* Set the initial timer or push the time out period forward */ + if (sendSettings) { + OS.SetTimer (hwndMessage, SETTINGS_ID, SETTINGS_DELAY, 0); + } + break; + } + case OS.WM_THEMECHANGED: { + if (OS.COMCTL32_MAJOR >= 6) { + if (hButtonTheme != 0) OS.CloseThemeData (hButtonTheme); + if (hEditTheme != 0) OS.CloseThemeData (hEditTheme); + if (hExplorerBarTheme != 0) OS.CloseThemeData (hExplorerBarTheme); + if (hScrollBarTheme != 0) OS.CloseThemeData (hScrollBarTheme); + if (hTabTheme != 0) OS.CloseThemeData (hTabTheme); + hButtonTheme = hEditTheme = hExplorerBarTheme = hScrollBarTheme = hTabTheme = 0; + } + break; + } + case OS.WM_TIMER: { + if (wParam == SETTINGS_ID) { + sendSettings = false; + OS.KillTimer (hwndMessage, SETTINGS_ID); + runSettings (); + } else { + runTimer (wParam); + } + break; + } + default: { + if ((int)/*64*/msg == SWT_TASKBARCREATED) { + if (tray != null) { + TrayItem [] items = tray.items; + for (int i=0; i<items.length; i++) { + TrayItem item = items [i]; + if (item != null) item.recreate (); + } + } + } + } + } + return OS.DefWindowProc (hwnd, (int)/*64*/msg, wParam, lParam); +} + +int /*long*/ monitorEnumProc (int /*long*/ hmonitor, int /*long*/ hdc, int /*long*/ lprcMonitor, int /*long*/ dwData) { + if (monitorCount >= monitors.length) { + Monitor[] newMonitors = new Monitor [monitors.length + 4]; + System.arraycopy (monitors, 0, newMonitors, 0, monitors.length); + monitors = newMonitors; + } + MONITORINFO lpmi = new MONITORINFO (); + lpmi.cbSize = MONITORINFO.sizeof; + OS.GetMonitorInfo (hmonitor, lpmi); + Monitor monitor = new Monitor (); + monitor.handle = hmonitor; + monitor.x = lpmi.rcMonitor_left; + monitor.y = lpmi.rcMonitor_top; + monitor.width = lpmi.rcMonitor_right - lpmi.rcMonitor_left; + monitor.height = lpmi.rcMonitor_bottom - lpmi.rcMonitor_top; + monitor.clientX = lpmi.rcWork_left; + monitor.clientY = lpmi.rcWork_top; + monitor.clientWidth = lpmi.rcWork_right - lpmi.rcWork_left; + monitor.clientHeight = lpmi.rcWork_bottom - lpmi.rcWork_top; + monitors [monitorCount++] = monitor; + return 1; +} + +int /*long*/ msgFilterProc (int /*long*/ code, int /*long*/ wParam, int /*long*/ lParam) { + switch ((int)/*64*/code) { + case OS.MSGF_COMMCTRL_BEGINDRAG: { + if (!runDragDrop && !dragCancelled) { + OS.MoveMemory (hookMsg, lParam, MSG.sizeof); + if (hookMsg.message == OS.WM_MOUSEMOVE) { + dragCancelled = true; + OS.SendMessage (hookMsg.hwnd, OS.WM_CANCELMODE, 0, 0); + } + } + break; + } + /* + * Feature in Windows. For some reason, when the user clicks + * a table or tree, the Windows hook WH_MSGFILTER is sent when + * an input event from a dialog box, message box, menu, or scroll + * bar did not occur, causing async messages to run at the wrong + * time. The fix is to check the message filter code. + */ + case OS.MSGF_DIALOGBOX: + case OS.MSGF_MAINLOOP: + case OS.MSGF_MENU: + case OS.MSGF_MOVE: + case OS.MSGF_MESSAGEBOX: + case OS.MSGF_NEXTWINDOW: + case OS.MSGF_SCROLLBAR: + case OS.MSGF_SIZE: { + if (runMessages) { + OS.MoveMemory (hookMsg, lParam, MSG.sizeof); + if (hookMsg.message == OS.WM_NULL) { + MSG msg = new MSG (); + int flags = OS.PM_NOREMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE; + if (!OS.PeekMessage (msg, 0, 0, 0, flags)) { + if (runAsyncMessages (false)) wakeThread (); + } + } + } + break; + } + } + return OS.CallNextHookEx (filterHook, (int)/*64*/code, wParam, lParam); +} + +int numpadKey (int key) { + switch (key) { + case OS.VK_NUMPAD0: return '0'; + case OS.VK_NUMPAD1: return '1'; + case OS.VK_NUMPAD2: return '2'; + case OS.VK_NUMPAD3: return '3'; + case OS.VK_NUMPAD4: return '4'; + case OS.VK_NUMPAD5: return '5'; + case OS.VK_NUMPAD6: return '6'; + case OS.VK_NUMPAD7: return '7'; + case OS.VK_NUMPAD8: return '8'; + case OS.VK_NUMPAD9: return '9'; + case OS.VK_MULTIPLY: return '*'; + case OS.VK_ADD: return '+'; + case OS.VK_SEPARATOR: return '\0'; + case OS.VK_SUBTRACT: return '-'; + case OS.VK_DECIMAL: return '.'; + case OS.VK_DIVIDE: return '/'; + } + return 0; +} + +/** + * 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); + int type = event.type; + switch (type){ + case SWT.KeyDown: + case SWT.KeyUp: { + KEYBDINPUT inputs = new KEYBDINPUT (); + inputs.wVk = (short) untranslateKey (event.keyCode); + if (inputs.wVk == 0) { + char key = event.character; + switch (key) { + case SWT.BS: inputs.wVk = (short) OS.VK_BACK; break; + case SWT.CR: inputs.wVk = (short) OS.VK_RETURN; break; + case SWT.DEL: inputs.wVk = (short) OS.VK_DELETE; break; + case SWT.ESC: inputs.wVk = (short) OS.VK_ESCAPE; break; + case SWT.TAB: inputs.wVk = (short) OS.VK_TAB; break; + /* + * Since there is no LF key on the keyboard, do not attempt + * to map LF to CR or attempt to post an LF key. + */ +// case SWT.LF: inputs.wVk = (short) OS.VK_RETURN; break; + case SWT.LF: return false; + default: { + if (OS.IsWinCE) { + inputs.wVk = (short)/*64*/OS.CharUpper ((short) key); + } else { + inputs.wVk = OS.VkKeyScan ((short) wcsToMbcs (key, 0)); + if (inputs.wVk == -1) return false; + inputs.wVk &= 0xFF; + } + } + } + } + inputs.dwFlags = type == SWT.KeyUp ? OS.KEYEVENTF_KEYUP : 0; + int /*long*/ hHeap = OS.GetProcessHeap (); + int /*long*/ pInputs = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, INPUT.sizeof); + OS.MoveMemory(pInputs, new int[] {OS.INPUT_KEYBOARD}, 4); + //TODO - DWORD type of INPUT structure aligned to 8 bytes on 64 bit + OS.MoveMemory (pInputs + OS.PTR_SIZEOF, inputs, KEYBDINPUT.sizeof); + boolean result = OS.SendInput (1, pInputs, INPUT.sizeof) != 0; + OS.HeapFree (hHeap, 0, pInputs); + return result; + } + case SWT.MouseDown: + case SWT.MouseMove: + case SWT.MouseUp: + case SWT.MouseWheel: { + MOUSEINPUT inputs = new MOUSEINPUT (); + if (type == SWT.MouseMove){ + inputs.dwFlags = OS.MOUSEEVENTF_MOVE | OS.MOUSEEVENTF_ABSOLUTE; + int x= 0, y = 0, width = 0, height = 0; + if (OS.WIN32_VERSION >= OS.VERSION (5, 0)) { + inputs.dwFlags |= OS.MOUSEEVENTF_VIRTUALDESK; + x = OS.GetSystemMetrics (OS.SM_XVIRTUALSCREEN); + y = OS.GetSystemMetrics (OS.SM_YVIRTUALSCREEN); + width = OS.GetSystemMetrics (OS.SM_CXVIRTUALSCREEN); + height = OS.GetSystemMetrics (OS.SM_CYVIRTUALSCREEN); + } else { + width = OS.GetSystemMetrics (OS.SM_CXSCREEN); + height = OS.GetSystemMetrics (OS.SM_CYSCREEN); + } + inputs.dx = ((event.x - x) * 65535 + width - 2) / (width - 1); + inputs.dy = ((event.y - y) * 65535 + height - 2) / (height - 1); + } else { + if (type == SWT.MouseWheel) { + if (OS.WIN32_VERSION < OS.VERSION (5, 0)) return false; + inputs.dwFlags = OS.MOUSEEVENTF_WHEEL; + switch (event.detail) { + case SWT.SCROLL_PAGE: + inputs.mouseData = event.count * OS.WHEEL_DELTA; + break; + case SWT.SCROLL_LINE: + int [] value = new int [1]; + OS.SystemParametersInfo (OS.SPI_GETWHEELSCROLLLINES, 0, value, 0); + inputs.mouseData = event.count * OS.WHEEL_DELTA / value [0]; + break; + default: return false; + } + } else { + switch (event.button) { + case 1: inputs.dwFlags = type == SWT.MouseDown ? OS.MOUSEEVENTF_LEFTDOWN : OS.MOUSEEVENTF_LEFTUP; break; + case 2: inputs.dwFlags = type == SWT.MouseDown ? OS.MOUSEEVENTF_MIDDLEDOWN : OS.MOUSEEVENTF_MIDDLEUP; break; + case 3: inputs.dwFlags = type == SWT.MouseDown ? OS.MOUSEEVENTF_RIGHTDOWN : OS.MOUSEEVENTF_RIGHTUP; break; + case 4: { + if (OS.WIN32_VERSION < OS.VERSION (5, 0)) return false; + inputs.dwFlags = type == SWT.MouseDown ? OS.MOUSEEVENTF_XDOWN : OS.MOUSEEVENTF_XUP; + inputs.mouseData = OS.XBUTTON1; + break; + } + case 5: { + if (OS.WIN32_VERSION < OS.VERSION (5, 0)) return false; + inputs.dwFlags = type == SWT.MouseDown ? OS.MOUSEEVENTF_XDOWN : OS.MOUSEEVENTF_XUP; + inputs.mouseData = OS.XBUTTON2; + break; + } + default: return false; + } + } + } + int /*long*/ hHeap = OS.GetProcessHeap (); + int /*long*/ pInputs = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, INPUT.sizeof); + OS.MoveMemory(pInputs, new int[] {OS.INPUT_MOUSE}, 4); + //TODO - DWORD type of INPUT structure aligned to 8 bytes on 64 bit + OS.MoveMemory (pInputs + OS.PTR_SIZEOF, inputs, MOUSEINPUT.sizeof); + boolean result = OS.SendInput (1, pInputs, INPUT.sizeof) != 0; + OS.HeapFree (hHeap, 0, pInputs); + return result; + } + } + 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; +} + +/** + * 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 (); + lpStartupInfo = null; + drawMenuBars (); + runPopups (); + if (OS.PeekMessage (msg, 0, 0, 0, OS.PM_REMOVE)) { + if (!filterMessage (msg)) { + OS.TranslateMessage (msg); + OS.DispatchMessage (msg); + } + runDeferredEvents (); + return true; + } + return isDisposed () || (runMessages && runAsyncMessages (false)); +} + +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 () { + 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 () { + if (embeddedHwnd != 0) { + OS.PostMessage (embeddedHwnd, SWT_DESTROY, 0, 0); + } + + /* Release XP Themes */ + if (OS.COMCTL32_MAJOR >= 6) { + if (hButtonTheme != 0) OS.CloseThemeData (hButtonTheme); + if (hEditTheme != 0) OS.CloseThemeData (hEditTheme); + if (hExplorerBarTheme != 0) OS.CloseThemeData (hExplorerBarTheme); + if (hScrollBarTheme != 0) OS.CloseThemeData (hScrollBarTheme); + if (hTabTheme != 0) OS.CloseThemeData (hTabTheme); + hButtonTheme = hEditTheme = hExplorerBarTheme = hScrollBarTheme = hTabTheme = 0; + } + + /* Unhook the message hook */ + if (!OS.IsWinCE) { + if (msgHook != 0) OS.UnhookWindowsHookEx (msgHook); + msgHook = 0; + } + + /* Unhook the filter hook */ + if (!OS.IsWinCE) { + if (filterHook != 0) OS.UnhookWindowsHookEx (filterHook); + filterHook = 0; + msgFilterCallback.dispose (); + msgFilterCallback = null; + msgFilterProc = 0; + } + + /* Unhook the idle hook */ + if (!OS.IsWinCE) { + if (idleHook != 0) OS.UnhookWindowsHookEx (idleHook); + idleHook = 0; + foregroundIdleCallback.dispose (); + foregroundIdleCallback = null; + foregroundIdleProc = 0; + } + + /* Stop the settings timer */ + OS.KillTimer (hwndMessage, SETTINGS_ID); + + /* Destroy the message only HWND */ + if (hwndMessage != 0) OS.DestroyWindow (hwndMessage); + hwndMessage = 0; + messageCallback.dispose (); + messageCallback = null; + messageProc = 0; + + /* Unregister the SWT window class */ + int /*long*/ hHeap = OS.GetProcessHeap (); + int /*long*/ hInstance = OS.GetModuleHandle (null); + OS.UnregisterClass (windowClass, hInstance); + + /* Unregister the SWT drop shadow and CS_OWNDC window class */ + OS.UnregisterClass (windowShadowClass, hInstance); + OS.UnregisterClass (windowOwnDCClass, hInstance); + windowClass = windowShadowClass = windowOwnDCClass = null; + windowCallback.dispose (); + windowCallback = null; + windowProc = 0; + + /* Release the System fonts */ + if (systemFont != null) systemFont.dispose (); + systemFont = null; + lfSystemFont = null; + + /* Release the System Images */ + if (errorImage != null) errorImage.dispose (); + if (infoImage != null) infoImage.dispose (); + if (questionImage != null) questionImage.dispose (); + if (warningIcon != null) warningIcon.dispose (); + errorImage = infoImage = questionImage = warningIcon = null; + + /* Release Sort Indicators */ + if (upArrow != null) upArrow.dispose (); + if (downArrow != null) downArrow.dispose (); + upArrow = downArrow = null; + + /* Release the System Cursors */ + for (int i = 0; i < cursors.length; i++) { + if (cursors [i] != null) cursors [i].dispose (); + } + cursors = null; + + /* Release Acquired Resources */ + if (resources != null) { + for (int i=0; i<resources.length; i++) { + if (resources [i] != null) resources [i].dispose (); + } + resources = null; + } + + /* Release Custom Colors for ChooseColor */ + if (lpCustColors != 0) OS.HeapFree (hHeap, 0, lpCustColors); + lpCustColors = 0; + + /* Uninitialize OLE */ + if (!OS.IsWinCE) OS.OleUninitialize (); + + /* Uninitialize buffered painting */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + OS.BufferedPaintUnInit (); + } + + /* Release references */ + thread = null; + msg = hookMsg = null; + keyboard = null; + modalDialog = null; + modalShells = null; + data = null; + keys = null; + values = null; + bars = popups = null; + indexTable = null; + timerIds = null; + controlTable = null; + lastControl = lastGetControl = lastHittestControl = null; + imageList = toolImageList = toolHotImageList = toolDisabledImageList = null; + timerList = null; + tableBuffer = null; + columnVisible = null; + eventTable = filterTable = null; + items = null; + clickRect = null; + hdr = null; + plvfi = null; + + /* Release handles */ + threadId = 0; +} + +void releaseImageList (ImageList list) { + int i = 0; + int length = imageList.length; + while (i < length) { + if (imageList [i] == list) { + if (list.removeRef () > 0) return; + list.dispose (); + System.arraycopy (imageList, i + 1, imageList, i, --length - i); + imageList [length] = null; + for (int j=0; j<length; j++) { + if (imageList [j] != null) return; + } + imageList = null; + return; + } + i++; + } +} + +void releaseToolImageList (ImageList list) { + int i = 0; + int length = toolImageList.length; + while (i < length) { + if (toolImageList [i] == list) { + if (list.removeRef () > 0) return; + list.dispose (); + System.arraycopy (toolImageList, i + 1, toolImageList, i, --length - i); + toolImageList [length] = null; + for (int j=0; j<length; j++) { + if (toolImageList [j] != null) return; + } + toolImageList = null; + return; + } + i++; + } +} + +void releaseToolHotImageList (ImageList list) { + int i = 0; + int length = toolHotImageList.length; + while (i < length) { + if (toolHotImageList [i] == list) { + if (list.removeRef () > 0) return; + list.dispose (); + System.arraycopy (toolHotImageList, i + 1, toolHotImageList, i, --length - i); + toolHotImageList [length] = null; + for (int j=0; j<length; j++) { + if (toolHotImageList [j] != null) return; + } + toolHotImageList = null; + return; + } + i++; + } +} + +void releaseToolDisabledImageList (ImageList list) { + int i = 0; + int length = toolDisabledImageList.length; + while (i < length) { + if (toolDisabledImageList [i] == list) { + if (list.removeRef () > 0) return; + list.dispose (); + System.arraycopy (toolDisabledImageList, i + 1, toolDisabledImageList, i, --length - i); + toolDisabledImageList [length] = null; + for (int j=0; j<length; j++) { + if (toolDisabledImageList [j] != null) return; + } + toolDisabledImageList = null; + return; + } + i++; + } +} + +/** + * 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); +} + +void removeBar (Menu menu) { + if (bars == null) return; + for (int i=0; i<bars.length; i++) { + if (bars [i] == menu) { + bars [i] = null; + return; + } + } +} + +Control removeControl (int /*long*/ handle) { + if (handle == 0) return null; + lastControl = lastGetControl = null; + Control control = null; + int index; + if (USE_PROPERTY) { + index = (int)/*64*/OS.RemoveProp (handle, SWT_OBJECT_INDEX) - 1; + } else { + index = (int)/*64*/OS.GetWindowLongPtr (handle, OS.GWLP_USERDATA) - 1; + OS.SetWindowLongPtr (handle, OS.GWLP_USERDATA, 0); + } + if (0 <= index && index < controlTable.length) { + control = controlTable [index]; + controlTable [index] = null; + indexTable [index] = freeSlot; + freeSlot = index; + } + return control; +} + +void removeMenuItem (MenuItem item) { + if (items == null) return; + items [item.id - ID_START] = null; +} + +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 runDeferredEvents () { + boolean run = false; + /* + * Run deferred events. This code is always + * called in the Display's thread so it must + * be re-enterant but 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.sendEvent (event); + } + } + + /* + * At this point, the event queue could + * be null due to a recursive invocation + * when running the event. + */ + } + + /* Clear the queue */ + eventQueue = null; + return run; +} + +boolean runPopups () { + if (popups == null) return false; + boolean result = false; + while (popups != null) { + Menu menu = popups [0]; + if (menu == null) break; + int length = popups.length; + System.arraycopy (popups, 1, popups, 0, --length); + popups [length] = null; + runDeferredEvents (); + if (!menu.isDisposed ()) menu._setVisible (true); + result = true; + } + popups = null; + return result; +} + +void runSettings () { + Font oldFont = getSystemFont (); + saveResources (); + updateImages (); + sendEvent (SWT.Settings, null); + Font newFont = getSystemFont (); + boolean sameFont = oldFont.equals (newFont); + Shell [] shells = getShells (); + for (int i=0; i<shells.length; i++) { + Shell shell = shells [i]; + if (!shell.isDisposed ()) { + if (!sameFont) { + shell.updateFont (oldFont, newFont); + } + /* This code is intentionally commented */ + //shell.redraw (true); + shell.layout (true, true); + } + } +} + +boolean runTimer (int /*long*/ id) { + if (timerList != null && timerIds != null) { + int index = 0; + while (index <timerIds.length) { + if (timerIds [index] == id) { + OS.KillTimer (hwndMessage, timerIds [index]); + timerIds [index] = 0; + Runnable runnable = timerList [index]; + timerList [index] = null; + if (runnable != null) runnable.run (); + return true; + } + index++; + } + } + return false; +} + +void saveResources () { + int resourceCount = 0; + if (resources == null) { + resources = new Resource [RESOURCE_SIZE]; + } else { + resourceCount = resources.length; + Resource [] newResources = new Resource [resourceCount + RESOURCE_SIZE]; + System.arraycopy (resources, 0, newResources, 0, resourceCount); + resources = newResources; + } + if (systemFont != null) { + if (!OS.IsWinCE) { + NONCLIENTMETRICS info = OS.IsUnicode ? (NONCLIENTMETRICS) new NONCLIENTMETRICSW () : new NONCLIENTMETRICSA (); + info.cbSize = NONCLIENTMETRICS.sizeof; + if (OS.SystemParametersInfo (OS.SPI_GETNONCLIENTMETRICS, 0, info, 0)) { + LOGFONT logFont = OS.IsUnicode ? (LOGFONT) ((NONCLIENTMETRICSW)info).lfMessageFont : ((NONCLIENTMETRICSA)info).lfMessageFont; + if (lfSystemFont == null || + logFont.lfCharSet != lfSystemFont.lfCharSet || + logFont.lfHeight != lfSystemFont.lfHeight || + logFont.lfWidth != lfSystemFont.lfWidth || + logFont.lfEscapement != lfSystemFont.lfEscapement || + logFont.lfOrientation != lfSystemFont.lfOrientation || + logFont.lfWeight != lfSystemFont.lfWeight || + logFont.lfItalic != lfSystemFont.lfItalic || + logFont.lfUnderline != lfSystemFont.lfUnderline || + logFont.lfStrikeOut != lfSystemFont.lfStrikeOut || + logFont.lfCharSet != lfSystemFont.lfCharSet || + logFont.lfOutPrecision != lfSystemFont.lfOutPrecision || + logFont.lfClipPrecision != lfSystemFont.lfClipPrecision || + logFont.lfQuality != lfSystemFont.lfQuality || + logFont.lfPitchAndFamily != lfSystemFont.lfPitchAndFamily || + !getFontName (logFont).equals (getFontName (lfSystemFont))) { + resources [resourceCount++] = systemFont; + lfSystemFont = logFont; + systemFont = null; + } + } + } + } + if (errorImage != null) resources [resourceCount++] = errorImage; + if (infoImage != null) resources [resourceCount++] = infoImage; + if (questionImage != null) resources [resourceCount++] = questionImage; + if (warningIcon != null) resources [resourceCount++] = warningIcon; + errorImage = infoImage = questionImage = warningIcon = null; + for (int i=0; i<cursors.length; i++) { + if (cursors [i] != null) resources [resourceCount++] = cursors [i]; + cursors [i] = null; + } + if (resourceCount < RESOURCE_SIZE) { + Resource [] newResources = new Resource [resourceCount]; + System.arraycopy (resources, 0, newResources, 0, resourceCount); + resources = newResources; + } +} + +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 (); + if (!filterEvent (event)) { + if (eventTable != null) eventTable.sendEvent (event); + } +} + +/** + * 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 (); + OS.SetCursorPos (x, y); +} + +/** + * 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 (RUN_MESSAGES_IN_IDLE_KEY)) { + Boolean data = (Boolean) value; + runMessagesInIdle = data != null && data.booleanValue (); + return; + } + if (key.equals (RUN_MESSAGES_IN_MESSAGE_PROC_KEY)) { + Boolean data = (Boolean) value; + runMessagesInMessageProc = data != null && data.booleanValue (); + return; + } + if (key.equals (USE_OWNDC_KEY)) { + Boolean data = (Boolean) value; + useOwnDC = data != null && data.booleanValue (); + return; + } + /* 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; +} + +/** + * 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; +} + +/** + * 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) { + /* Do nothing */ +} + +void setModalDialog (Dialog modalDailog) { + this.modalDialog = modalDailog; + Shell [] shells = getShells (); + for (int i=0; i<shells.length; i++) shells [i].updateModal (); +} + +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 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); + } +} + +int shiftedKey (int key) { + if (OS.IsWinCE) return 0; + + /* Clear the virtual keyboard and press the shift key */ + for (int i=0; i<keyboard.length; i++) keyboard [i] = 0; + keyboard [OS.VK_SHIFT] |= 0x80; + + /* Translate the key to ASCII or UNICODE using the virtual keyboard */ + if (OS.IsUnicode) { + char [] result = new char [1]; + if (OS.ToUnicode (key, key, keyboard, result, 1, 0) == 1) return result [0]; + } else { + short [] result = new short [1]; + if (OS.ToAscii (key, key, keyboard, result, 0) == 1) return result [0]; + } + return 0; +} + +/** + * 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 (runMessages && getMessageCount () != 0) return true; + if (OS.IsWinCE) { + OS.MsgWaitForMultipleObjectsEx (0, 0, OS.INFINITE, OS.QS_ALLINPUT, OS.MWMO_INPUTAVAILABLE); + return true; + } + return OS.WaitMessage (); +} + +/** + * 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 (); + if (runnable == null) error (SWT.ERROR_NULL_ARGUMENT); + if (timerList == null) timerList = new Runnable [4]; + if (timerIds == null) timerIds = new int /*long*/ [4]; + int index = 0; + while (index < timerList.length) { + if (timerList [index] == runnable) break; + index++; + } + int /*long*/ timerId = 0; + if (index != timerList.length) { + timerId = timerIds [index]; + if (milliseconds < 0) { + OS.KillTimer (hwndMessage, timerId); + timerList [index] = null; + timerIds [index] = 0; + return; + } + } else { + if (milliseconds < 0) return; + index = 0; + while (index < timerList.length) { + if (timerList [index] == null) break; + index++; + } + timerId = nextTimerId++; + if (index == timerList.length) { + Runnable [] newTimerList = new Runnable [timerList.length + 4]; + System.arraycopy (timerList, 0, newTimerList, 0, timerList.length); + timerList = newTimerList; + int /*long*/ [] newTimerIds = new int /*long*/ [timerIds.length + 4]; + System.arraycopy (timerIds, 0, newTimerIds, 0, timerIds.length); + timerIds = newTimerIds; + } + } + int /*long*/ newTimerID = OS.SetTimer (hwndMessage, timerId, milliseconds, 0); + if (newTimerID != 0) { + timerList [index] = runnable; + timerIds [index] = newTimerID; + } +} + +boolean translateAccelerator (MSG msg, Control control) { + accelKeyHit = true; + boolean result = control.translateAccelerator (msg); + accelKeyHit = false; + return result; +} + +static int translateKey (int key) { + for (int i=0; i<KeyTable.length; i++) { + if (KeyTable [i] [0] == key) return KeyTable [i] [1]; + } + return 0; +} + +boolean translateMnemonic (MSG msg, Control control) { + switch (msg.message) { + case OS.WM_CHAR: + case OS.WM_SYSCHAR: + return control.translateMnemonic (msg); + } + return false; +} + +boolean translateTraversal (MSG msg, Control control) { + switch (msg.message) { + case OS.WM_KEYDOWN: + switch ((int)/*64*/msg.wParam) { + case OS.VK_RETURN: + case OS.VK_ESCAPE: + case OS.VK_TAB: + case OS.VK_UP: + case OS.VK_DOWN: + case OS.VK_LEFT: + case OS.VK_RIGHT: + case OS.VK_PRIOR: + case OS.VK_NEXT: + return control.translateTraversal (msg); + } + break; + case OS.WM_SYSKEYDOWN: + switch ((int)/*64*/msg.wParam) { + case OS.VK_MENU: + return control.translateTraversal (msg); + } + break; + } + return false; +} + +static int untranslateKey (int key) { + for (int i=0; i<KeyTable.length; i++) { + if (KeyTable [i] [1] == key) return KeyTable [i] [0]; + } + 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 (); + /* + * Feature in Windows. When an application does not remove + * events from the event queue for some time, Windows assumes + * the application is not responding and no longer sends paint + * events to the application. The fix is to detect that the + * application is not responding and call PeekMessage() with + * PM_REMOVE to tell Windows that the application is ready + * to dispatch events. Note that the message does not have + * to be found or dispatched in order to wake Windows up. + * + * NOTE: This allows other cross thread messages to be delivered, + * most notably WM_ACTIVATE. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) { + if (OS.IsHungAppWindow (hwndMessage)) { + MSG msg = new MSG (); + int flags = OS.PM_REMOVE | OS.PM_NOYIELD; + OS.PeekMessage (msg, hwndMessage, SWT_NULL, SWT_NULL, flags); + } + } + Shell[] shells = getShells (); + for (int i=0; i<shells.length; i++) { + Shell shell = shells [i]; + if (!shell.isDisposed ()) shell.update (true); + } +} + +void updateImages () { + if (upArrow != null) upArrow.dispose (); + if (downArrow != null) downArrow.dispose (); + upArrow = downArrow = null; + for (int i=0; i<controlTable.length; i++) { + Control control = controlTable [i]; + if (control != null) control.updateImages (); + } +} + +/** + * 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 () { + if (OS.IsWinCE) { + OS.PostMessage (hwndMessage, OS.WM_NULL, 0, 0); + } else { + OS.PostThreadMessage (threadId, OS.WM_NULL, 0, 0); + } +} + +/* + * Returns a single character, converted from the wide + * character set (WCS) used by Java to the specified + * multi-byte character set used by the operating system + * widgets. + * + * @param ch the WCS character + * @param codePage the code page used to convert the character + * @return the MBCS character + */ +static int wcsToMbcs (char ch, int codePage) { + if (OS.IsUnicode) return ch; + if (ch <= 0x7F) return ch; + TCHAR buffer = new TCHAR (codePage, ch, false); + return buffer.tcharAt (0); +} + +/* + * Returns a single character, converted from the wide + * character set (WCS) used by Java to the default + * multi-byte character set used by the operating system + * widgets. + * + * @param ch the WCS character + * @return the MBCS character + */ +static int wcsToMbcs (char ch) { + return wcsToMbcs (ch, 0); +} + +int /*long*/ windowProc (int /*long*/ hwnd, int /*long*/ msg, int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. On Vista only, it is faster to + * compute and answer the data for the visible columns + * of a table when scrolling, rather than just return + * the data for each column when asked. + */ + if (columnVisible != null) { + if (msg == OS.WM_NOTIFY && hwndParent == hwnd) { + OS.MoveMemory (hdr, lParam, NMHDR.sizeof); + switch (hdr.code) { + case OS.LVN_GETDISPINFOA: + case OS.LVN_GETDISPINFOW: { + OS.MoveMemory (plvfi, lParam, NMLVDISPINFO.sizeof); + if (0 <= plvfi.iSubItem && plvfi.iSubItem < columnCount) { + if (!columnVisible [plvfi.iSubItem]) return 0; + } + break; + } + } + } + } + /* + * Bug in Adobe Reader 7.0. For some reason, when Adobe + * Reader 7.0 is deactivated from within Internet Explorer, + * it sends thousands of consecutive WM_NCHITTEST messages + * to the control that is under the cursor. It seems that + * if the control takes some time to respond to the message, + * Adobe stops sending them. The fix is to detect this case + * and sleep. + * + * NOTE: Under normal circumstances, Windows will never send + * consecutive WM_NCHITTEST messages to the same control without + * another message (normally WM_SETCURSOR) in between. + */ + if ((int)/*64*/msg == OS.WM_NCHITTEST) { + if (hitCount++ >= 1024) { + try {Thread.sleep (1);} catch (Throwable t) {} + } + } else { + hitCount = 0; + } + if (lastControl != null && lastHwnd == hwnd) { + return lastControl.windowProc (hwnd, (int)/*64*/msg, wParam, lParam); + } + int index; + if (USE_PROPERTY) { + index = (int)/*64*/OS.GetProp (hwnd, SWT_OBJECT_INDEX) - 1; + } else { + index = (int)/*64*/OS.GetWindowLongPtr (hwnd, OS.GWLP_USERDATA) - 1; + } + if (0 <= index && index < controlTable.length) { + Control control = controlTable [index]; + if (control != null) { + lastHwnd = hwnd; + lastControl = control; + return control.windowProc (hwnd, (int)/*64*/msg, wParam, lParam); + } + } + return OS.DefWindowProc (hwnd, (int)/*64*/msg, wParam, lParam); +} + +static String withCrLf (String string) { + + /* If the string is empty, return the string. */ + int length = string.length (); + if (length == 0) return string; + + /* + * Check for an LF or CR/LF and assume the rest of + * the string is formated that way. This will not + * work if the string contains mixed delimiters. + */ + int i = string.indexOf ('\n', 0); + if (i == -1) return string; + if (i > 0 && string.charAt (i - 1) == '\r') { + return string; + } + + /* + * The string is formatted with LF. Compute the + * number of lines and the size of the buffer + * needed to hold the result + */ + i++; + int count = 1; + while (i < length) { + if ((i = string.indexOf ('\n', i)) == -1) break; + count++; i++; + } + count += length; + + /* Create a new string with the CR/LF line terminator. */ + i = 0; + StringBuffer result = new StringBuffer (count); + while (i < length) { + int j = string.indexOf ('\n', i); + if (j == -1) j = length; + result.append (string.substring (i, j)); + if ((i = j) < length) { + result.append ("\r\n"); //$NON-NLS-1$ + i++; + } + } + return result.toString (); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ExpandBar.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ExpandBar.java new file mode 100644 index 0000000000..ca9c0bb0be --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ExpandBar.java @@ -0,0 +1,824 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * Instances of this class support the layout of selectable + * expand bar items. + * <p> + * The item children that may be added to instances of this class + * must be of type <code>ExpandItem</code>. + * </p><p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>V_SCROLL</dd> + * <dt><b>Events:</b></dt> + * <dd>Expand, Collapse</dd> + * </dl> + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see ExpandItem + * @see ExpandEvent + * @see ExpandListener + * @see ExpandAdapter + * @see <a href="http://www.eclipse.org/swt/snippets/#expandbar">ExpandBar 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.2 + * @noextend This class is not intended to be subclassed by clients. + */ +public class ExpandBar extends Composite { + ExpandItem[] items; + int itemCount; + ExpandItem focusItem; + int spacing = 4; + int yCurrentScroll; + int /*long*/ hFont; + + +/** + * 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#V_SCROLL + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public ExpandBar (Composite parent, int style) { + super (parent, checkStyle (style)); +} + +/** + * 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>ExpandListener</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 ExpandListener + * @see #removeExpandListener + */ +public void addExpandListener (ExpandListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Expand, typedListener); + addListener (SWT.Collapse, typedListener); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + return OS.DefWindowProc (hwnd, msg, wParam, lParam); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +static int checkStyle (int style) { + style &= ~SWT.H_SCROLL; + return style | SWT.NO_BACKGROUND; +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + int height = 0, width = 0; + if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) { + if (itemCount > 0) { + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ hTheme = 0; + if (isAppThemed ()) { + hTheme = display.hExplorerBarTheme (); + } + int /*long*/ hCurrentFont = 0, oldFont = 0; + if (hTheme == 0) { + if (hFont != 0) { + hCurrentFont = hFont; + } else { + if (!OS.IsWinCE) { + NONCLIENTMETRICS info = OS.IsUnicode ? (NONCLIENTMETRICS) new NONCLIENTMETRICSW () : new NONCLIENTMETRICSA (); + info.cbSize = NONCLIENTMETRICS.sizeof; + if (OS.SystemParametersInfo (OS.SPI_GETNONCLIENTMETRICS, 0, info, 0)) { + LOGFONT logFont = OS.IsUnicode ? (LOGFONT) ((NONCLIENTMETRICSW)info).lfCaptionFont : ((NONCLIENTMETRICSA)info).lfCaptionFont; + hCurrentFont = OS.CreateFontIndirect (logFont); + } + } + } + if (hCurrentFont != 0) { + oldFont = OS.SelectObject (hDC, hCurrentFont); + } + } + height += spacing; + for (int i = 0; i < itemCount; i++) { + ExpandItem item = items [i]; + height += item.getHeaderHeight (); + if (item.expanded) height += item.height; + height += spacing; + width = Math.max (width, item.getPreferredWidth (hTheme, hDC)); + } + if (hCurrentFont != 0) { + OS.SelectObject (hDC, oldFont); + if (hCurrentFont != hFont) OS.DeleteObject (hCurrentFont); + } + OS.ReleaseDC (handle, hDC); + } + } + 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); + return new Point (trim.width, trim.height); +} + +void createHandle () { + super.createHandle (); + state &= ~CANVAS; + state |= TRACK_MOUSE; +} + +void createItem (ExpandItem item, int style, int index) { + if (!(0 <= index && index <= itemCount)) error (SWT.ERROR_INVALID_RANGE); + if (itemCount == items.length) { + ExpandItem [] newItems = new ExpandItem [itemCount + 4]; + System.arraycopy (items, 0, newItems, 0, items.length); + items = newItems; + } + System.arraycopy (items, index, items, index + 1, itemCount - index); + items [index] = item; + itemCount++; + if (focusItem == null) focusItem = item; + + RECT rect = new RECT (); + OS.GetWindowRect (handle, rect); + item.width = Math.max (0, rect.right - rect.left - spacing * 2); + layoutItems (index, true); +} + +void createWidget () { + super.createWidget (); + items = new ExpandItem [4]; + if (!isAppThemed ()) { + backgroundMode = SWT.INHERIT_DEFAULT; + } +} + +int defaultBackground() { + if (!isAppThemed ()) { + return OS.GetSysColor (OS.COLOR_WINDOW); + } + return super.defaultBackground(); +} + +void destroyItem (ExpandItem item) { + int index = 0; + while (index < itemCount) { + if (items [index] == item) break; + index++; + } + if (index == itemCount) return; + if (item == focusItem) { + int focusIndex = index > 0 ? index - 1 : 1; + if (focusIndex < itemCount) { + focusItem = items [focusIndex]; + focusItem.redraw (true); + } else { + focusItem = null; + } + } + System.arraycopy (items, index + 1, items, index, --itemCount - index); + items [itemCount] = null; + item.redraw (true); + layoutItems (index, true); +} + +void drawThemeBackground (int /*long*/ hDC, int /*long*/ hwnd, RECT rect) { + RECT rect2 = new RECT (); + OS.GetClientRect (handle, rect2); + OS.MapWindowPoints (handle, hwnd, rect2, 2); + OS.DrawThemeBackground (display.hExplorerBarTheme (), hDC, OS.EBP_NORMALGROUPBACKGROUND, 0, rect2, null); +} + +void drawWidget (GC gc, RECT clipRect) { + int /*long*/ hTheme = 0; + if (isAppThemed ()) { + hTheme = display.hExplorerBarTheme (); + } + if (hTheme != 0) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + OS.DrawThemeBackground (hTheme, gc.handle, OS.EBP_HEADERBACKGROUND, 0, rect, clipRect); + } else { + drawBackground (gc.handle); + } + boolean drawFocus = false; + if (handle == OS.GetFocus ()) { + int uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + drawFocus = (uiState & OS.UISF_HIDEFOCUS) == 0; + } + int /*long*/ hCurrentFont = 0, oldFont = 0; + if (hTheme == 0) { + if (hFont != 0) { + hCurrentFont = hFont; + } else { + if (!OS.IsWinCE) { + NONCLIENTMETRICS info = OS.IsUnicode ? (NONCLIENTMETRICS) new NONCLIENTMETRICSW () : new NONCLIENTMETRICSA (); + info.cbSize = NONCLIENTMETRICS.sizeof; + if (OS.SystemParametersInfo (OS.SPI_GETNONCLIENTMETRICS, 0, info, 0)) { + LOGFONT logFont = OS.IsUnicode ? (LOGFONT) ((NONCLIENTMETRICSW)info).lfCaptionFont : ((NONCLIENTMETRICSA)info).lfCaptionFont; + hCurrentFont = OS.CreateFontIndirect (logFont); + } + } + } + if (hCurrentFont != 0) { + oldFont = OS.SelectObject (gc.handle, hCurrentFont); + } + if (foreground != -1) { + OS.SetTextColor (gc.handle, foreground); + } + } + for (int i = 0; i < itemCount; i++) { + ExpandItem item = items[i]; + item.drawItem (gc, hTheme, clipRect, item == focusItem && drawFocus); + } + if (hCurrentFont != 0) { + OS.SelectObject (gc.handle, oldFont); + if (hCurrentFont != hFont) OS.DeleteObject (hCurrentFont); + } +} + +Control findBackgroundControl () { + Control control = super.findBackgroundControl (); + if (!isAppThemed ()) { + if (control == null) control = this; + } + return control; +} + +Control findThemeControl () { + return isAppThemed () ? this : super.findThemeControl (); +} + +int getBandHeight () { + if (hFont == 0) return ExpandItem.CHEVRON_SIZE; + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ oldHFont = OS.SelectObject (hDC, hFont); + TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA(); + OS.GetTextMetrics (hDC, lptm); + OS.SelectObject (hDC, oldHFont); + OS.ReleaseDC (handle, hDC); + return Math.max (ExpandItem.CHEVRON_SIZE, lptm.tmHeight + 4); +} + +/** + * 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 ExpandItem 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 an array of <code>ExpandItem</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 ExpandItem [] getItems () { + checkWidget (); + ExpandItem [] result = new ExpandItem [itemCount]; + System.arraycopy (items, 0, result, 0, itemCount); + return result; +} + +/** + * Returns the receiver's spacing. + * + * @return the spacing + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if 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 getSpacing () { + checkWidget (); + return spacing; +} + +/** + * 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> + */ +public int indexOf (ExpandItem 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; +} + +boolean isAppThemed () { + if (background != -1) return false; + if (foreground != -1) return false; + if (hFont != 0) return false; + return OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed (); +} + +void layoutItems (int index, boolean setScrollbar) { + if (index < itemCount) { + int y = spacing - yCurrentScroll; + for (int i = 0; i < index; i++) { + ExpandItem item = items [i]; + if (item.expanded) y += item.height; + y += item.getHeaderHeight () + spacing; + } + for (int i = index; i < itemCount; i++) { + ExpandItem item = items [i]; + item.setBounds (spacing, y, 0, 0, true, false); + if (item.expanded) y += item.height; + y += item.getHeaderHeight () + spacing; + } + } + if (setScrollbar) setScrollbar (); +} + +void releaseChildren (boolean destroy) { + if (items != null) { + for (int i=0; i<items.length; i++) { + ExpandItem item = items [i]; + if (item != null && !item.isDisposed ()) { + item.release (false); + } + } + items = null; + } + focusItem = null; + super.releaseChildren (destroy); +} + +/** + * 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 ExpandListener + * @see #addExpandListener + */ +public void removeExpandListener (ExpandListener 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 setBackgroundPixel (int pixel) { + super.setBackgroundPixel (pixel); + if (!OS.IsWinCE) { + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, null, 0, flags); + } +} + +public void setFont (Font font) { + super.setFont (font); + hFont = font != null ? font.handle : 0; + layoutItems (0, true); +} + +void setForegroundPixel (int pixel) { + super.setForegroundPixel (pixel); + if (!OS.IsWinCE) { + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, null, 0, flags); + } +} + +void setScrollbar () { + if (itemCount == 0) return; + if ((style & SWT.V_SCROLL) == 0) return; + RECT rect = new RECT(); + OS.GetClientRect (handle, rect); + int height = rect.bottom - rect.top; + ExpandItem item = items [itemCount - 1]; + int maxHeight = item.y + getBandHeight () + spacing; + if (item.expanded) maxHeight += item.height; + + //claim bottom free space + if (yCurrentScroll > 0 && height > maxHeight) { + yCurrentScroll = Math.max (0, yCurrentScroll + maxHeight - height); + layoutItems (0, false); + } + maxHeight += yCurrentScroll; + + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_RANGE | OS.SIF_PAGE | OS.SIF_POS; + info.nMin = 0; + info.nMax = maxHeight; + info.nPage = height; + info.nPos = Math.min (yCurrentScroll, info.nMax); + if (info.nPage != 0) info.nPage++; + OS.SetScrollInfo (handle, OS.SB_VERT, info, true); +} + +/** + * Sets the receiver's spacing. Spacing specifies the number of pixels allocated around + * each item. + * + * @param spacing the spacing around each 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 setSpacing (int spacing) { + checkWidget (); + if (spacing < 0) return; + if (spacing == this.spacing) return; + this.spacing = spacing; + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + int width = Math.max (0, (rect.right - rect.left) - spacing * 2); + for (int i = 0; i < itemCount; i++) { + ExpandItem item = items[i]; + if (item.width != width) item.setBounds (0, 0, width, item.height, false, true); + } + layoutItems (0, true); + OS.InvalidateRect (handle, null, true); +} + +void showItem (ExpandItem item) { + Control control = item.control; + if (control != null && !control.isDisposed ()) { + control.setVisible (item.expanded); + } + item.redraw (true); + int index = indexOf (item); + layoutItems (index + 1, true); +} + +void showFocus (boolean up) { + RECT rect = new RECT(); + OS.GetClientRect (handle, rect); + int height = rect.bottom - rect.top; + int updateY = 0; + if (up) { + if (focusItem.y < 0) { + updateY = Math.min (yCurrentScroll, -focusItem.y); + } + } else { + int itemHeight = focusItem.y + getBandHeight (); + if (focusItem.expanded) { + if (height >= getBandHeight () + focusItem.height) { + itemHeight += focusItem.height; + } + } + if (itemHeight > height) { + updateY = height - itemHeight; + } + } + if (updateY != 0) { + yCurrentScroll = Math.max (0, yCurrentScroll - updateY); + if ((style & SWT.V_SCROLL) != 0) { + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_POS; + info.nPos = yCurrentScroll; + OS.SetScrollInfo (handle, OS.SB_VERT, info, true); + } + OS.ScrollWindowEx (handle, 0, updateY, null, null, 0, null, OS.SW_SCROLLCHILDREN | OS.SW_INVALIDATE); + for (int i = 0; i < itemCount; i++) { + items [i].y += updateY; + } + } +} + +TCHAR windowClass () { + return display.windowClass; +} + +int /*long*/ windowProc () { + return display.windowProc; +} + +LRESULT WM_KEYDOWN (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_KEYDOWN (wParam, lParam); + if (result != null) return result; + if (focusItem == null) return result; + switch ((int)/*64*/wParam) { + case OS.VK_SPACE: + case OS.VK_RETURN: + Event event = new Event (); + event.item = focusItem; + sendEvent (focusItem.expanded ? SWT.Collapse : SWT.Expand, event); + focusItem.expanded = !focusItem.expanded; + showItem (focusItem); + return LRESULT.ZERO; + case OS.VK_UP: { + int focusIndex = indexOf (focusItem); + if (focusIndex > 0) { + focusItem.redraw (true); + focusItem = items [focusIndex - 1]; + focusItem.redraw (true); + showFocus (true); + return LRESULT.ZERO; + } + break; + } + case OS.VK_DOWN: { + int focusIndex = indexOf (focusItem); + if (focusIndex < itemCount - 1) { + focusItem.redraw (true); + focusItem = items [focusIndex + 1]; + focusItem.redraw (true); + showFocus (false); + return LRESULT.ZERO; + } + break; + } + } + return result; +} + +LRESULT WM_KILLFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_KILLFOCUS (wParam, lParam); + if (focusItem != null) focusItem.redraw (true); + return result; +} + +LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam); + if (result == LRESULT.ZERO) return result; + int x = OS.GET_X_LPARAM (lParam); + int y = OS.GET_Y_LPARAM (lParam); + for (int i = 0; i < itemCount; i++) { + ExpandItem item = items[i]; + boolean hover = item.isHover (x, y); + if (hover && focusItem != item) { + focusItem.redraw (true); + focusItem = item; + focusItem.redraw (true); + forceFocus (); + break; + } + } + return result; +} + +LRESULT WM_LBUTTONUP (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_LBUTTONUP (wParam, lParam); + if (result == LRESULT.ZERO) return result; + if (focusItem == null) return result; + int x = OS.GET_X_LPARAM (lParam); + int y = OS.GET_Y_LPARAM (lParam); + boolean hover = focusItem.isHover (x, y); + if (hover) { + Event event = new Event (); + event.item = focusItem; + sendEvent (focusItem.expanded ? SWT.Collapse : SWT.Expand, event); + focusItem.expanded = !focusItem.expanded; + showItem (focusItem); + } + return result; +} + +LRESULT WM_MOUSELEAVE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_MOUSELEAVE (wParam, lParam); + if (result != null) return result; + for (int i = 0; i < itemCount; i++) { + ExpandItem item = items [i]; + if (item.hover) { + item.hover = false; + item.redraw (false); + break; + } + } + return result; +} + +LRESULT WM_MOUSEMOVE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_MOUSEMOVE (wParam, lParam); + if (result == LRESULT.ZERO) return result; + int x = OS.GET_X_LPARAM (lParam); + int y = OS.GET_Y_LPARAM (lParam); + for (int i = 0; i < itemCount; i++) { + ExpandItem item = items [i]; + boolean hover = item.isHover (x, y); + if (item.hover != hover) { + item.hover = hover; + item.redraw (false); + } + } + return result; +} + +LRESULT WM_MOUSEWHEEL (int /*long*/ wParam, int /*long*/ lParam) { + return wmScrollWheel (true, wParam, lParam); +} + +LRESULT WM_PAINT (int /*long*/ wParam, int /*long*/ lParam) { + PAINTSTRUCT ps = new PAINTSTRUCT (); + GCData data = new GCData (); + data.ps = ps; + data.hwnd = handle; + GC gc = new_GC (data); + if (gc != null) { + int width = ps.right - ps.left; + int height = ps.bottom - ps.top; + if (width != 0 && height != 0) { + RECT rect = new RECT (); + OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom); + drawWidget (gc, rect); + if (hooks (SWT.Paint) || filters (SWT.Paint)) { + Event event = new Event (); + event.gc = gc; + event.x = rect.left; + event.y = rect.top; + event.width = width; + event.height = height; + sendEvent (SWT.Paint, event); + event.gc = null; + } + } + gc.dispose (); + } + return LRESULT.ZERO; +} + +LRESULT WM_PRINTCLIENT (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_PRINTCLIENT (wParam, lParam); + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + GCData data = new GCData (); + data.device = display; + data.foreground = getForegroundPixel (); + GC gc = GC.win32_new (wParam, data); + drawWidget (gc, rect); + gc.dispose (); + return result; +} + +LRESULT WM_SETCURSOR (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETCURSOR (wParam, lParam); + if (result != null) return result; + int hitTest = (short) OS.LOWORD (lParam); + if (hitTest == OS.HTCLIENT) { + for (int i = 0; i < itemCount; i++) { + ExpandItem item = items [i]; + if (item.hover) { + int /*long*/ hCursor = OS.LoadCursor (0, OS.IDC_HAND); + OS.SetCursor (hCursor); + return LRESULT.ONE; + } + } + } + return result; +} + +LRESULT WM_SETFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETFOCUS (wParam, lParam); + if (focusItem != null) focusItem.redraw (true); + return result; +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SIZE (wParam, lParam); + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + int width = Math.max (0, (rect.right - rect.left) - spacing * 2); + for (int i = 0; i < itemCount; i++) { + ExpandItem item = items[i]; + if (item.width != width) item.setBounds (0, 0, width, item.height, false, true); + } + setScrollbar (); + OS.InvalidateRect (handle, null, true); + return result; +} + +LRESULT wmScroll (ScrollBar bar, boolean update, int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.wmScroll (bar, true, hwnd, msg, wParam, lParam); + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_POS; + OS.GetScrollInfo (handle, OS.SB_VERT, info); + int updateY = yCurrentScroll - info.nPos; + OS.ScrollWindowEx (handle, 0, updateY, null, null, 0, null, OS.SW_SCROLLCHILDREN | OS.SW_INVALIDATE); + yCurrentScroll = info.nPos; + if (updateY != 0) { + for (int i = 0; i < itemCount; i++) { + items [i].y += updateY; + } + } + return result; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ExpandItem.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ExpandItem.java new file mode 100644 index 0000000000..51ba1ea3f9 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ExpandItem.java @@ -0,0 +1,489 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; + +/** + * Instances of this class represent a selectable user interface object + * that represents a expandable item in a expand bar. + * <p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>(none)</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * </p><p> + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * </p> + * + * @see ExpandBar + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.2 + * @noextend This class is not intended to be subclassed by clients. + */ +public class ExpandItem extends Item { + ExpandBar parent; + Control control; + boolean expanded, hover; + int x, y, width, height; + int imageHeight, imageWidth; + static final int TEXT_INSET = 6; + static final int BORDER = 1; + static final int CHEVRON_SIZE = 24; + +/** + * 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 ExpandItem (ExpandBar parent, int style) { + this (parent, style, checkNull (parent).getItemCount ()); +} + +/** + * Constructs a new instance of this class given its parent, 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 Widget#checkSubclass + * @see Widget#getStyle + */ +public ExpandItem (ExpandBar parent, int style, int index) { + super (parent, style); + this.parent = parent; + parent.createItem (this, style, index); +} + +static ExpandBar checkNull (ExpandBar control) { + if (control == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return control; +} + +private void drawChevron (int /*long*/ hDC, RECT rect) { + int /*long*/ oldBrush = OS.SelectObject (hDC, OS.GetSysColorBrush (OS.COLOR_BTNFACE)); + OS.PatBlt (hDC, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY); + OS.SelectObject (hDC, oldBrush); + rect.left += 4; + rect.top += 4; + rect.right -= 4; + rect.bottom -= 4; + int /*long*/ hPen = OS.CreatePen (OS.PS_SOLID, 1, parent.getForegroundPixel ()); + int /*long*/ oldPen = OS.SelectObject (hDC, hPen); + int [] polyline1, polyline2; + if (expanded) { + int px = rect.left + 5; + int py = rect.top + 7; + polyline1 = new int [] { + px,py, px+1,py, px+1,py-1, px+2,py-1, px+2,py-2, px+3,py-2, px+3,py-3, + px+3,py-2, px+4,py-2, px+4,py-1, px+5,py-1, px+5,py, px+7,py}; + py += 4; + polyline2 = new int [] { + px,py, px+1,py, px+1,py-1, px+2,py-1, px+2,py-2, px+3,py-2, px+3,py-3, + px+3,py-2, px+4,py-2, px+4,py-1, px+5,py-1, px+5,py, px+7,py}; + } else { + int px = rect.left + 5; + int py = rect.top + 4; + polyline1 = new int[] { + px,py, px+1,py, px+1,py+1, px+2,py+1, px+2,py+2, px+3,py+2, px+3,py+3, + px+3,py+2, px+4,py+2, px+4,py+1, px+5,py+1, px+5,py, px+7,py}; + py += 4; + polyline2 = new int [] { + px,py, px+1,py, px+1,py+1, px+2,py+1, px+2,py+2, px+3,py+2, px+3,py+3, + px+3,py+2, px+4,py+2, px+4,py+1, px+5,py+1, px+5,py, px+7,py}; + } + OS.Polyline (hDC, polyline1, polyline1.length / 2); + OS.Polyline (hDC, polyline2, polyline2.length / 2); + if (hover) { + int /*long*/ whitePen = OS.CreatePen (OS.PS_SOLID, 1, OS.GetSysColor (OS.COLOR_3DHILIGHT)); + int /*long*/ darkGrayPen = OS.CreatePen (OS.PS_SOLID, 1, OS.GetSysColor (OS.COLOR_3DSHADOW)); + OS.SelectObject (hDC, whitePen); + int [] points1 = { + rect.left, rect.bottom, + rect.left, rect.top, + rect.right, rect.top}; + OS.Polyline (hDC, points1, points1.length / 2); + OS.SelectObject (hDC, darkGrayPen); + int [] points2 = { + rect.right, rect.top, + rect.right, rect.bottom, + rect.left, rect.bottom}; + OS.Polyline (hDC, points2, points2.length / 2); + OS.SelectObject (hDC, oldPen); + OS.DeleteObject (whitePen); + OS.DeleteObject (darkGrayPen); + } else { + OS.SelectObject (hDC, oldPen); + } + OS.DeleteObject (hPen); +} + +void drawItem (GC gc, int /*long*/ hTheme, RECT clipRect, boolean drawFocus) { + int /*long*/ hDC = gc.handle; + int headerHeight = parent.getBandHeight (); + RECT rect = new RECT (); + OS.SetRect (rect, x, y, x + width, y + headerHeight); + if (hTheme != 0) { + OS.DrawThemeBackground (hTheme, hDC, OS.EBP_NORMALGROUPHEAD, 0, rect, clipRect); + } else { + int /*long*/ oldBrush = OS.SelectObject (hDC, OS.GetSysColorBrush (OS.COLOR_BTNFACE)); + OS.PatBlt (hDC, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY); + OS.SelectObject (hDC, oldBrush); + } + if (image != null) { + rect.left += ExpandItem.TEXT_INSET; + if (imageHeight > headerHeight) { + gc.drawImage (image, rect.left, rect.top + headerHeight - imageHeight); + } else { + gc.drawImage (image, rect.left, rect.top + (headerHeight - imageHeight) / 2); + } + rect.left += imageWidth; + } + if (text.length () > 0) { + rect.left += ExpandItem.TEXT_INSET; + TCHAR buffer = new TCHAR (parent.getCodePage (), text, false); + if (hTheme != 0) { + OS.DrawThemeText (hTheme, hDC, OS.EBP_NORMALGROUPHEAD, 0, buffer.chars, buffer.length(), OS.DT_VCENTER | OS.DT_SINGLELINE, 0, rect); + } else { + int oldBkMode = OS.SetBkMode (hDC, OS.TRANSPARENT); + OS.DrawText (hDC, buffer, buffer.length (), rect, OS.DT_VCENTER | OS.DT_SINGLELINE); + OS.SetBkMode (hDC, oldBkMode); + } + } + int chevronSize = ExpandItem.CHEVRON_SIZE; + rect.left = rect.right - chevronSize; + rect.top = y + (headerHeight - chevronSize) / 2; + rect.bottom = rect.top + chevronSize; + if (hTheme != 0) { + int partID = expanded ? OS.EBP_NORMALGROUPCOLLAPSE : OS.EBP_NORMALGROUPEXPAND; + int stateID = hover ? OS.EBNGC_HOT : OS.EBNGC_NORMAL; + OS.DrawThemeBackground (hTheme, hDC, partID, stateID, rect, clipRect); + } else { + drawChevron (hDC, rect); + } + if (drawFocus) { + OS.SetRect (rect, x + 1, y + 1, x + width - 2, y + headerHeight - 2); + OS.DrawFocusRect (hDC, rect); + } + if (expanded) { + if (!parent.isAppThemed ()) { + int /*long*/ pen = OS.CreatePen (OS.PS_SOLID, 1, OS.GetSysColor (OS.COLOR_BTNFACE)); + int /*long*/ oldPen = OS.SelectObject (hDC, pen); + int [] points = { + x, y + headerHeight, + x, y + headerHeight + height, + x + width - 1, y + headerHeight + height, + x + width - 1, y + headerHeight - 1}; + OS.Polyline (hDC, points, points.length / 2); + OS.SelectObject (hDC, oldPen); + OS.DeleteObject (pen); + } + } +} + +void destroyWidget () { + parent.destroyItem (this); + releaseHandle (); +} + +/** + * Returns the control that is shown when the item is expanded. + * If no control has been set, return <code>null</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 <code>true</code> if the receiver is expanded, + * and false otherwise. + * + * @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 height of the receiver's header + * + * @return the height of the header + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if 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 getHeaderHeight () { + checkWidget (); + return Math.max (parent.getBandHeight (), imageHeight); +} + +/** + * Gets the height of the receiver. + * + * @return the 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 getHeight () { + checkWidget (); + return height; +} + +/** + * Returns the receiver's parent, which must be a <code>ExpandBar</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 ExpandBar getParent () { + checkWidget (); + return parent; +} + +int getPreferredWidth (int /*long*/ hTheme, int /*long*/ hDC) { + int width = ExpandItem.TEXT_INSET * 2 + ExpandItem.CHEVRON_SIZE; + if (image != null) { + width += ExpandItem.TEXT_INSET + imageWidth; + } + if (text.length() > 0) { + RECT rect = new RECT (); + TCHAR buffer = new TCHAR (parent.getCodePage (), text, false); + if (hTheme != 0) { + OS.GetThemeTextExtent (hTheme, hDC, OS.EBP_NORMALGROUPHEAD, 0, buffer.chars, buffer.length(), OS.DT_SINGLELINE, null, rect); + } else { + OS.DrawText (hDC, buffer, buffer.length (), rect, OS.DT_CALCRECT); + } + width += (rect.right - rect.left); + } + return width; +} + +boolean isHover (int x, int y) { + int bandHeight = parent.getBandHeight (); + return this.x < x && x < (this.x + width) && this.y < y && y < (this.y + bandHeight); +} + +void redraw (boolean all) { + int /*long*/ parentHandle = parent.handle; + int headerHeight = parent.getBandHeight (); + RECT rect = new RECT (); + int left = all ? x : x + width - headerHeight; + OS.SetRect (rect, left, y, x + width, y + headerHeight); + OS.InvalidateRect (parentHandle, rect, true); + if (imageHeight > headerHeight) { + OS.SetRect (rect, x + ExpandItem.TEXT_INSET, y + headerHeight - imageHeight, x + ExpandItem.TEXT_INSET + imageWidth, y); + OS.InvalidateRect (parentHandle, rect, true); + } + if (!parent.isAppThemed ()) { + OS.SetRect (rect, x, y + headerHeight, x + width, y + headerHeight + height + 1); + OS.InvalidateRect (parentHandle, rect, true); + } +} + +void releaseHandle () { + super.releaseHandle (); + parent = null; +} + +void releaseWidget () { + super.releaseWidget (); + control = null; +} + +void setBounds (int x, int y, int width, int height, boolean move, boolean size) { + redraw (true); + int headerHeight = parent.getBandHeight (); + if (move) { + if (imageHeight > headerHeight) { + y += (imageHeight - headerHeight); + } + this.x = x; + this.y = y; + redraw (true); + } + if (size) { + this.width = width; + this.height = height; + redraw (true); + } + if (control != null && !control.isDisposed ()) { + if (!parent.isAppThemed ()) { + x += BORDER; + width = Math.max (0, width - BORDER * 2); + height = Math.max (0, height - BORDER); + } + if (move && size) control.setBounds (x, y + headerHeight, width, height); + if (move && !size) control.setLocation (x, y + headerHeight); + if (!move && size) control.setSize (width, height); + } +} + +/** + * Sets the control that is shown when the item is expanded. + * + * @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); + } + this.control = control; + if (control != null) { + int headerHeight = parent.getBandHeight (); + control.setVisible (expanded); + if (!parent.isAppThemed ()) { + int width = Math.max (0, this.width - BORDER * 2); + int height = Math.max (0, this.height - BORDER); + control.setBounds (x + BORDER, y + headerHeight, width, height); + } else { + control.setBounds (x, y + headerHeight, width, height); + } + } +} + +/** + * Sets the expanded state of the receiver. + * + * @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 (); + this.expanded = expanded; + parent.showItem (this); +} + +/** + * Sets the height of the receiver. This is height of the item when it is expanded, + * excluding the height of the header. + * + * @param height the new 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 void setHeight (int height) { + checkWidget (); + if (height < 0) return; + setBounds (0, 0, width, height, false, true); + if (expanded) parent.layoutItems (parent.indexOf (this) + 1, true); +} + +public void setImage (Image image) { + super.setImage (image); + int oldImageHeight = imageHeight; + if (image != null) { + Rectangle bounds = image.getBounds (); + imageHeight = bounds.height; + imageWidth = bounds.width; + } else { + imageHeight = imageWidth = 0; + } + if (oldImageHeight != imageHeight) { + parent.layoutItems (parent.indexOf (this), true); + } else { + redraw (true); + } +} + +public void setText (String string) { + super.setText (string); + redraw (true); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/FileDialog.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/FileDialog.java new file mode 100755 index 0000000000..c84a13a51c --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/FileDialog.java @@ -0,0 +1,618 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; + +/** + * 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 { + String [] filterNames = new String [0]; + String [] filterExtensions = new String [0]; + String [] fileNames = new String [0]; + String filterPath = "", fileName = ""; + int filterIndex = 0; + boolean overwrite = false; + static final String FILTER = "*.*"; + static int BUFFER_SIZE = 1024 * 32; + static boolean USE_HOOK = true; + static { + /* + * Feature in Vista. When OFN_ENABLEHOOK is set in the + * save or open file dialog, Vista uses the old XP look + * and feel. OFN_ENABLEHOOK is used to grow the file + * name buffer in a multi-select file dialog. The fix + * is to only use OFN_ENABLEHOOK when the buffer has + * overrun. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + USE_HOOK = false; + } + } + +/** + * 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)); + 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; +} + +int /*long*/ OFNHookProc (int /*long*/ hdlg, int /*long*/ uiMsg, int /*long*/ wParam, int /*long*/ lParam) { + switch ((int)/*64*/uiMsg) { + case OS.WM_NOTIFY: + OFNOTIFY ofn = new OFNOTIFY (); + OS.MoveMemory (ofn, lParam, OFNOTIFY.sizeof); + if (ofn.code == OS.CDN_SELCHANGE) { + int lResult = (int)/*64*/OS.SendMessage (ofn.hwndFrom, OS.CDM_GETSPEC, 0, 0); + if (lResult > 0) { + lResult += OS.MAX_PATH; + OPENFILENAME lpofn = new OPENFILENAME (); + OS.MoveMemory (lpofn, ofn.lpOFN, OPENFILENAME.sizeof); + if (lpofn.nMaxFile < lResult) { + int /*long*/ hHeap = OS.GetProcessHeap (); + int /*long*/ lpstrFile = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, lResult * TCHAR.sizeof); + if (lpstrFile != 0) { + if (lpofn.lpstrFile != 0) OS.HeapFree (hHeap, 0, lpofn.lpstrFile); + lpofn.lpstrFile = lpstrFile; + lpofn.nMaxFile = lResult; + OS.MoveMemory (ofn.lpOFN, lpofn, OPENFILENAME.sizeof); + } + } + } + } + break; + } + return 0; +} + +/** + * 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 () { + int /*long*/ hHeap = OS.GetProcessHeap (); + + /* Get the owner HWND for the dialog */ + int /*long*/ hwndOwner = parent.handle; + int /*long*/ hwndParent = parent.handle; + + /* + * Feature in Windows. There is no API to set the orientation of a + * file dialog. It is always inherited from the parent. The fix is + * to create a hidden parent and set the orientation in the hidden + * parent for the dialog to inherit. + */ + boolean enabled = false; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) { + int dialogOrientation = style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT); + int parentOrientation = parent.style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT); + if (dialogOrientation != parentOrientation) { + int exStyle = OS.WS_EX_NOINHERITLAYOUT; + if (dialogOrientation == SWT.RIGHT_TO_LEFT) exStyle |= OS.WS_EX_LAYOUTRTL; + hwndOwner = OS.CreateWindowEx ( + exStyle, + Shell.DialogClass, + null, + 0, + OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0, + hwndParent, + 0, + OS.GetModuleHandle (null), + null); + enabled = OS.IsWindowEnabled (hwndParent); + if (enabled) OS.EnableWindow (hwndParent, false); + } + } + + /* Convert the title and copy it into lpstrTitle */ + if (title == null) title = ""; + /* Use the character encoding for the default locale */ + TCHAR buffer3 = new TCHAR (0, title, true); + int byteCount3 = buffer3.length () * TCHAR.sizeof; + int /*long*/ lpstrTitle = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount3); + OS.MoveMemory (lpstrTitle, buffer3, byteCount3); + + /* Compute filters and copy into lpstrFilter */ + String strFilter = ""; + if (filterNames == null) filterNames = new String [0]; + if (filterExtensions == null) filterExtensions = new String [0]; + for (int i=0; i<filterExtensions.length; i++) { + String filterName = filterExtensions [i]; + if (i < filterNames.length) filterName = filterNames [i]; + strFilter = strFilter + filterName + '\0' + filterExtensions [i] + '\0'; + } + if (filterExtensions.length == 0) { + strFilter = strFilter + FILTER + '\0' + FILTER + '\0'; + } + /* Use the character encoding for the default locale */ + TCHAR buffer4 = new TCHAR (0, strFilter, true); + int byteCount4 = buffer4.length () * TCHAR.sizeof; + int /*long*/ lpstrFilter = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount4); + OS.MoveMemory (lpstrFilter, buffer4, byteCount4); + + /* Convert the fileName and filterName to C strings */ + if (fileName == null) fileName = ""; + /* Use the character encoding for the default locale */ + TCHAR name = new TCHAR (0, fileName, true); + + /* + * Copy the name into lpstrFile and ensure that the + * last byte is NULL and the buffer does not overrun. + */ + int nMaxFile = OS.MAX_PATH; + if ((style & SWT.MULTI) != 0) nMaxFile = Math.max (nMaxFile, BUFFER_SIZE); + int byteCount = nMaxFile * TCHAR.sizeof; + int /*long*/ lpstrFile = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + int byteCountFile = Math.min (name.length () * TCHAR.sizeof, byteCount - TCHAR.sizeof); + OS.MoveMemory (lpstrFile, name, byteCountFile); + + /* + * Copy the path into lpstrInitialDir and ensure that + * the last byte is NULL and the buffer does not overrun. + */ + if (filterPath == null) filterPath = ""; + /* Use the character encoding for the default locale */ + TCHAR path = new TCHAR (0, filterPath.replace ('/', '\\'), true); + int byteCount5 = OS.MAX_PATH * TCHAR.sizeof; + int /*long*/ lpstrInitialDir = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount5); + int byteCountDir = Math.min (path.length () * TCHAR.sizeof, byteCount5 - TCHAR.sizeof); + OS.MoveMemory (lpstrInitialDir, path, byteCountDir); + + /* Create the file dialog struct */ + OPENFILENAME struct = new OPENFILENAME (); + struct.lStructSize = OPENFILENAME.sizeof; + struct.Flags = OS.OFN_HIDEREADONLY | OS.OFN_NOCHANGEDIR; + boolean save = (style & SWT.SAVE) != 0; + if (save && overwrite) struct.Flags |= OS.OFN_OVERWRITEPROMPT; + Callback callback = null; + if ((style & SWT.MULTI) != 0) { + struct.Flags |= OS.OFN_ALLOWMULTISELECT | OS.OFN_EXPLORER | OS.OFN_ENABLESIZING; + if (!OS.IsWinCE && USE_HOOK) { + callback = new Callback (this, "OFNHookProc", 4); //$NON-NLS-1$ + int /*long*/ lpfnHook = callback.getAddress (); + if (lpfnHook == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + struct.lpfnHook = lpfnHook; + struct.Flags |= OS.OFN_ENABLEHOOK; + } + } + struct.hwndOwner = hwndOwner; + struct.lpstrTitle = lpstrTitle; + struct.lpstrFile = lpstrFile; + struct.nMaxFile = nMaxFile; + struct.lpstrInitialDir = lpstrInitialDir; + struct.lpstrFilter = lpstrFilter; + struct.nFilterIndex = filterIndex == 0 ? filterIndex : filterIndex + 1; + + /* + * Set the default extension to an empty string. If the + * user fails to type an extension and this extension is + * empty, Windows uses the current value of the filter + * extension at the time that the dialog is closed. + */ + int /*long*/ lpstrDefExt = 0; + if (save) { + lpstrDefExt = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof); + struct.lpstrDefExt = lpstrDefExt; + } + + /* Make the parent shell be temporary modal */ + Dialog oldModal = null; + Display display = parent.getDisplay (); + if ((style & (SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL)) != 0) { + oldModal = display.getModalDialog (); + display.setModalDialog (this); + } + + /* + * Feature in Windows. For some reason, the WH_MSGFILTER filter + * does not run for GetSaveFileName() or GetOpenFileName(). The + * fix is to allow async messages to run in the WH_FOREGROUNDIDLE + * hook instead. + * + * Bug in Windows 98. For some reason, when certain operating + * system calls such as Shell_NotifyIcon(), GetOpenFileName() + * and GetSaveFileName() are made during the WH_FOREGROUNDIDLE + * hook, Windows hangs. The fix is to disallow async messages + * during WH_FOREGROUNDIDLE. + */ + boolean oldRunMessagesInIdle = display.runMessagesInIdle; + display.runMessagesInIdle = !OS.IsWin95; + /* + * Open the dialog. If the open fails due to an invalid + * file name, use an empty file name and open it again. + */ + boolean success = (save) ? OS.GetSaveFileName (struct) : OS.GetOpenFileName (struct); + switch (OS.CommDlgExtendedError ()) { + case OS.FNERR_INVALIDFILENAME: + OS.MoveMemory (lpstrFile, new TCHAR (0, "", true), TCHAR.sizeof); + success = (save) ? OS.GetSaveFileName (struct) : OS.GetOpenFileName (struct); + break; + case OS.FNERR_BUFFERTOOSMALL: + USE_HOOK = true; + break; + } + display.runMessagesInIdle = oldRunMessagesInIdle; + + /* Clear the temporary dialog modal parent */ + if ((style & (SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL)) != 0) { + display.setModalDialog (oldModal); + } + + /* Dispose the callback and reassign the buffer */ + if (callback != null) callback.dispose (); + lpstrFile = struct.lpstrFile; + + /* Set the new path, file name and filter */ + fileNames = new String [0]; + String fullPath = null; + if (success) { + + /* Use the character encoding for the default locale */ + TCHAR buffer = new TCHAR (0, struct.nMaxFile); + int byteCount1 = buffer.length () * TCHAR.sizeof; + OS.MoveMemory (buffer, lpstrFile, byteCount1); + + /* + * Bug in WinCE. For some reason, nFileOffset and nFileExtension + * are always zero on WinCE HPC. nFileOffset is always zero on + * WinCE PPC when using GetSaveFileName(). nFileOffset is correctly + * set on WinCE PPC when using OpenFileName(). The fix is to parse + * lpstrFile to calculate nFileOffset. + * + * Note: WinCE does not support multi-select file dialogs. + */ + int nFileOffset = struct.nFileOffset; + if (OS.IsWinCE && nFileOffset == 0) { + int index = 0; + while (index < buffer.length ()) { + int ch = buffer.tcharAt (index); + if (ch == 0) break; + if (ch == '\\') nFileOffset = index + 1; + index++; + } + } + if (nFileOffset > 0) { + + /* Use the character encoding for the default locale */ + TCHAR prefix = new TCHAR (0, nFileOffset - 1); + int byteCount2 = prefix.length () * TCHAR.sizeof; + OS.MoveMemory (prefix, lpstrFile, byteCount2); + filterPath = prefix.toString (0, prefix.length ()); + + /* + * Get each file from the buffer. Files are delimited + * by a NULL character with 2 NULL characters at the end. + */ + int count = 0; + fileNames = new String [(style & SWT.MULTI) != 0 ? 4 : 1]; + int start = nFileOffset; + do { + int end = start; + while (end < buffer.length () && buffer.tcharAt (end) != 0) end++; + String string = buffer.toString (start, end - start); + start = end; + if (count == fileNames.length) { + String [] newFileNames = new String [fileNames.length + 4]; + System.arraycopy (fileNames, 0, newFileNames, 0, fileNames.length); + fileNames = newFileNames; + } + fileNames [count++] = string; + if ((style & SWT.MULTI) == 0) break; + start++; + } while (start < buffer.length () && buffer.tcharAt (start) != 0); + + if (fileNames.length > 0) fileName = fileNames [0]; + String separator = ""; + int length = filterPath.length (); + if (length > 0 && filterPath.charAt (length - 1) != '\\') { + separator = "\\"; + } + fullPath = filterPath + separator + fileName; + if (count < fileNames.length) { + String [] newFileNames = new String [count]; + System.arraycopy (fileNames, 0, newFileNames, 0, count); + fileNames = newFileNames; + } + } + filterIndex = struct.nFilterIndex - 1; + } + + /* Free the memory that was allocated. */ + OS.HeapFree (hHeap, 0, lpstrFile); + OS.HeapFree (hHeap, 0, lpstrFilter); + OS.HeapFree (hHeap, 0, lpstrInitialDir); + OS.HeapFree (hHeap, 0, lpstrTitle); + if (lpstrDefExt != 0) OS.HeapFree (hHeap, 0, lpstrDefExt); + + /* Destroy the BIDI orientation window */ + if (hwndParent != hwndOwner) { + if (enabled) OS.EnableWindow (hwndParent, true); + OS.SetActiveWindow (hwndParent); + OS.DestroyWindow (hwndOwner); + } + + /* + * This code is intentionally commented. On some + * platforms, the owner window is repainted right + * away when a dialog window exits. This behavior + * is currently unspecified. + */ +// if (hwndOwner != 0) OS.UpdateWindow (hwndOwner); + + /* Answer the full path or null */ + return fullPath; +} + +/** + * 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/win32/org/eclipse/swt/widgets/FontDialog.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/FontDialog.java new file mode 100755 index 0000000000..aa35f803ee --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/FontDialog.java @@ -0,0 +1,325 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * 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; + +/** + * 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 (); +} + +/** + * 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 () { + if (OS.IsWinCE) SWT.error (SWT.ERROR_NOT_IMPLEMENTED); + + /* Get the owner HWND for the dialog */ + int /*long*/ hwndOwner = parent.handle; + int /*long*/ hwndParent = parent.handle; + + /* + * Feature in Windows. There is no API to set the orientation of a + * font dialog. It is always inherited from the parent. The fix is + * to create a hidden parent and set the orientation in the hidden + * parent for the dialog to inherit. + */ + boolean enabled = false; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) { + int dialogOrientation = style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT); + int parentOrientation = parent.style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT); + if (dialogOrientation != parentOrientation) { + int exStyle = OS.WS_EX_NOINHERITLAYOUT; + if (dialogOrientation == SWT.RIGHT_TO_LEFT) exStyle |= OS.WS_EX_LAYOUTRTL; + hwndOwner = OS.CreateWindowEx ( + exStyle, + Shell.DialogClass, + null, + 0, + OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0, + hwndParent, + 0, + OS.GetModuleHandle (null), + null); + enabled = OS.IsWindowEnabled (hwndParent); + if (enabled) OS.EnableWindow (hwndParent, false); + } + } + + /* Open the dialog */ + int /*long*/ hHeap = OS.GetProcessHeap (); + CHOOSEFONT lpcf = new CHOOSEFONT (); + lpcf.lStructSize = CHOOSEFONT.sizeof; + lpcf.hwndOwner = hwndOwner; + lpcf.Flags = OS.CF_SCREENFONTS | OS.CF_EFFECTS; + int /*long*/ lpLogFont = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, LOGFONT.sizeof); + if (fontData != null && fontData.data != null) { + LOGFONT logFont = fontData.data; + int lfHeight = logFont.lfHeight; + int /*long*/ hDC = OS.GetDC (0); + int pixels = -(int)(0.5f + (fontData.height * OS.GetDeviceCaps(hDC, OS.LOGPIXELSY) / 72)); + OS.ReleaseDC (0, hDC); + logFont.lfHeight = pixels; + lpcf.Flags |= OS.CF_INITTOLOGFONTSTRUCT; + OS.MoveMemory (lpLogFont, logFont, LOGFONT.sizeof); + logFont.lfHeight = lfHeight; + } + lpcf.lpLogFont = lpLogFont; + if (rgb != null) { + int red = rgb.red & 0xFF; + int green = (rgb.green << 8) & 0xFF00; + int blue = (rgb.blue << 16) & 0xFF0000; + lpcf.rgbColors = red | green | blue; + } + + /* Make the parent shell be temporary modal */ + Dialog oldModal = null; + Display display = null; + if ((style & (SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL)) != 0) { + display = parent.getDisplay (); + oldModal = display.getModalDialog (); + display.setModalDialog (this); + } + + /* Open the dialog */ + boolean success = OS.ChooseFont (lpcf); + + /* Clear the temporary dialog modal parent */ + if ((style & (SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL)) != 0) { + display.setModalDialog (oldModal); + } + + /* Compute the result */ + if (success) { + LOGFONT logFont = OS.IsUnicode ? (LOGFONT) new LOGFONTW () : new LOGFONTA (); + OS.MoveMemory (logFont, lpLogFont, LOGFONT.sizeof); + + /* + * This will not work on multiple screens or + * for printing. Should use DC for the proper device. + */ + int /*long*/ hDC = OS.GetDC(0); + int logPixelsY = OS.GetDeviceCaps(hDC, OS.LOGPIXELSY); + int pixels = 0; + if (logFont.lfHeight > 0) { + /* + * Feature in Windows. If the lfHeight of the LOGFONT structure + * is positive, the lfHeight measures the height of the entire + * cell, including internal leading, in logical units. Since the + * height of a font in points does not include the internal leading, + * we must subtract the internal leading, which requires a TEXTMETRIC, + * which in turn requires font creation. + */ + int /*long*/ hFont = OS.CreateFontIndirect(logFont); + int /*long*/ oldFont = OS.SelectObject(hDC, hFont); + TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA (); + OS.GetTextMetrics(hDC, lptm); + OS.SelectObject(hDC, oldFont); + OS.DeleteObject(hFont); + pixels = logFont.lfHeight - lptm.tmInternalLeading; + } else { + pixels = -logFont.lfHeight; + } + OS.ReleaseDC(0, hDC); + + float points = pixels * 72f /logPixelsY; + fontData = FontData.win32_new (logFont, points); + int red = lpcf.rgbColors & 0xFF; + int green = (lpcf.rgbColors >> 8) & 0xFF; + int blue = (lpcf.rgbColors >> 16) & 0xFF; + rgb = new RGB (red, green, blue); + } + + /* Free the OS memory */ + if (lpLogFont != 0) OS.HeapFree (hHeap, 0, lpLogFont); + + /* Destroy the BIDI orientation window */ + if (hwndParent != hwndOwner) { + if (enabled) OS.EnableWindow (hwndParent, true); + OS.SetActiveWindow (hwndParent); + OS.DestroyWindow (hwndOwner); + } + + /* + * This code is intentionally commented. On some + * platforms, the owner window is repainted right + * away when a dialog window exits. This behavior + * is currently unspecified. + */ +// if (hwndOwner != 0) OS.UpdateWindow (hwndOwner); + + if (!success) return null; + 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; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Group.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Group.java new file mode 100755 index 0000000000..317dfa452e --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Group.java @@ -0,0 +1,569 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * 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 { + String text = ""; + static final int CLIENT_INSET = 3; + static final int /*long*/ GroupProc; + static final TCHAR GroupClass = new TCHAR (0, OS.IsWinCE ? "BUTTON" : "SWT_GROUP", true); + static { + /* + * Feature in Windows. The group box window class + * uses the CS_HREDRAW and CS_VREDRAW style bits to + * force a full redraw of the control and all children + * when resized. This causes flashing. The fix is to + * register a new window class without these bits and + * implement special code that damages only the control. + * + * Feature in WinCE. On certain devices, defining + * a new window class which looks like BUTTON causes + * CreateWindowEx() to crash. The workaround is to use + * the class Button directly. + */ + WNDCLASS lpWndClass = new WNDCLASS (); + if (OS.IsWinCE) { + OS.GetClassInfo (0, GroupClass, lpWndClass); + GroupProc = lpWndClass.lpfnWndProc; + } else { + TCHAR WC_BUTTON = new TCHAR (0, "BUTTON", true); + OS.GetClassInfo (0, WC_BUTTON, lpWndClass); + GroupProc = lpWndClass.lpfnWndProc; + int /*long*/ hInstance = OS.GetModuleHandle (null); + if (!OS.GetClassInfo (hInstance, GroupClass, lpWndClass)) { + int /*long*/ hHeap = OS.GetProcessHeap (); + lpWndClass.hInstance = hInstance; + lpWndClass.style &= ~(OS.CS_HREDRAW | OS.CS_VREDRAW); + int byteCount = GroupClass.length () * TCHAR.sizeof; + int /*long*/ lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (lpszClassName, GroupClass, byteCount); + lpWndClass.lpszClassName = lpszClassName; + OS.RegisterClass (lpWndClass); + OS.HeapFree (hHeap, 0, lpszClassName); + } + } + } + +/** + * 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)); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + /* + * Feature in Windows. When the user clicks on the group + * box label, the group box takes focus. This is unwanted. + * The fix is to avoid calling the group box window proc. + */ + switch (msg) { + case OS.WM_LBUTTONDOWN: + case OS.WM_LBUTTONDBLCLK: + return OS.DefWindowProc (hwnd, msg, wParam, lParam); + } + return OS.CallWindowProc (GroupProc, hwnd, msg, wParam, lParam); +} + +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 Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + Point size = super.computeSize (wHint, hHint, changed); + int length = text.length (); + if (length != 0) { + /* + * Bug in Windows. When a group control is right-to-left and + * is disabled, the first pixel of the text is clipped. The + * fix is to add a space to both sides of the text. Note that + * the work around must run all the time to stop the preferred + * size from changing when a group is enabled and disabled. + */ + String string = text; + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) { + string = " " + string + " "; + } + } + /* + * If the group has text, and the text is wider than the + * client area, pad the width so the text is not clipped. + */ + TCHAR buffer = new TCHAR (getCodePage (), string, true); + int /*long*/ newFont, oldFont = 0; + int /*long*/ hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + RECT rect = new RECT (); + int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE; + OS.DrawText (hDC, buffer, -1, rect, flags); + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + int offsetY = OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed () ? 0 : 1; + size.x = Math.max (size.x, rect.right - rect.left + CLIENT_INSET * 6 + offsetY); + } + return size; +} + +public Rectangle computeTrim (int x, int y, int width, int height) { + checkWidget (); + Rectangle trim = super.computeTrim (x, y, width, height); + int /*long*/ newFont, oldFont = 0; + int /*long*/ hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA (); + OS.GetTextMetrics (hDC, tm); + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + int offsetY = OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed () ? 0 : 1; + trim.x -= CLIENT_INSET; + trim.y -= tm.tmHeight + offsetY; + trim.width += CLIENT_INSET * 2; + trim.height += tm.tmHeight + CLIENT_INSET + offsetY; + return trim; +} + +void createHandle () { + /* + * Feature in Windows. When a button is created, + * it clears the UI state for all controls in the + * shell by sending WM_CHANGEUISTATE with UIS_SET, + * UISF_HIDEACCEL and UISF_HIDEFOCUS to the parent. + * This is undocumented and unexpected. The fix + * is to ignore the WM_CHANGEUISTATE, when sent + * from CreateWindowEx(). + */ + parent.state |= IGNORE_WM_CHANGEUISTATE; + super.createHandle (); + parent.state &= ~IGNORE_WM_CHANGEUISTATE; + state |= DRAW_BACKGROUND; + state &= ~CANVAS; +} + +void enableWidget (boolean enabled) { + super.enableWidget (enabled); + /* + * Bug in Windows. When a group control is right-to-left and + * is disabled, the first pixel of the text is clipped. The + * fix is to add a space to both sides of the text. + */ + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) { + String string = enabled || text.length() == 0 ? text : " " + text + " "; + TCHAR buffer = new TCHAR (getCodePage (), string, true); + OS.SetWindowText (handle, buffer); + } + } +} + +public Rectangle getClientArea () { + checkWidget (); + forceResize (); + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + int /*long*/ newFont, oldFont = 0; + int /*long*/ hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA (); + OS.GetTextMetrics (hDC, tm); + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + int offsetY = OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed () ? 0 : 1; + int x = CLIENT_INSET, y = tm.tmHeight + offsetY; + int width = Math.max (0, rect.right - CLIENT_INSET * 2); + int height = Math.max (0, rect.bottom - y - CLIENT_INSET); + return new Rectangle (x, y, width, 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; +} + +boolean mnemonicHit (char key) { + return setFocus (); +} + +boolean mnemonicMatch (char key) { + char mnemonic = findMnemonic (getText ()); + if (mnemonic == '\0') return false; + return Character.toUpperCase (key) == Character.toUpperCase (mnemonic); +} + +void printWidget (int /*long*/ hwnd, int /*long*/ hdc, GC gc) { + /* + * Bug in Windows. For some reason, PrintWindow() + * returns success but does nothing when it is called + * on a printer. The fix is to just go directly to + * WM_PRINT in this case. + */ + boolean success = false; + if (!(OS.GetDeviceCaps(gc.handle, OS.TECHNOLOGY) == OS.DT_RASPRINTER)) { + int bits = OS.GetWindowLong (hwnd, OS.GWL_STYLE); + if ((bits & OS.WS_VISIBLE) == 0) { + OS.DefWindowProc (hwnd, OS.WM_SETREDRAW, 1, 0); + } + success = OS.PrintWindow (hwnd, hdc, 0); + if ((bits & OS.WS_VISIBLE) == 0) { + OS.DefWindowProc (hwnd, OS.WM_SETREDRAW, 0, 0); + } + } + + /* + * Bug in Windows. For some reason, PrintWindow() fails + * when it is called on a push button. The fix is to + * detect the failure and use WM_PRINT instead. Note + * that WM_PRINT cannot be used all the time because it + * fails for browser controls when the browser has focus. + */ + if (!success) { + /* + * Bug in Windows. For some reason, WM_PRINT when called + * with PRF_CHILDREN will not draw the tool bar divider + * for tool bar children that do not have CCS_NODIVIDER. + * The fix is to draw the group box and iterate through + * the children, drawing each one. + */ + int flags = OS.PRF_CLIENT | OS.PRF_NONCLIENT | OS.PRF_ERASEBKGND; + OS.SendMessage (hwnd, OS.WM_PRINT, hdc, flags); + int nSavedDC = OS.SaveDC (hdc); + Control [] children = _getChildren (); + Rectangle rect = getBounds (); + OS.IntersectClipRect (hdc, 0, 0, rect.width, rect.height); + for (int i=children.length - 1; i>=0; --i) { + Point location = children [i].getLocation (); + int graphicsMode = OS.GetGraphicsMode(hdc); + if (graphicsMode == OS.GM_ADVANCED) { + float [] lpXform = {1, 0, 0, 1, location.x, location.y}; + OS.ModifyWorldTransform(hdc, lpXform, OS.MWT_LEFTMULTIPLY); + } else { + OS.SetWindowOrgEx (hdc, -location.x, -location.y, null); + } + int /*long*/ topHandle = children [i].topHandle(); + int bits = OS.GetWindowLong (topHandle, OS.GWL_STYLE); + if ((bits & OS.WS_VISIBLE) != 0) { + children [i].printWidget (topHandle, hdc, gc); + } + if (graphicsMode == OS.GM_ADVANCED) { + float [] lpXform = {1, 0, 0, 1, -location.x, -location.y}; + OS.ModifyWorldTransform(hdc, lpXform, OS.MWT_LEFTMULTIPLY); + } + } + OS.RestoreDC (hdc, nSavedDC); + } +} + +void releaseWidget () { + super.releaseWidget (); + text = null; +} + +public void setFont (Font font) { + checkWidget (); + Rectangle oldRect = getClientArea (); + super.setFont (font); + Rectangle newRect = getClientArea (); + if (!oldRect.equals (newRect)) sendResize (); +} + +/** + * 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; + /* + * Bug in Windows. When a group control is right-to-left and + * is disabled, the first pixel of the text is clipped. The + * fix is to add a space to both sides of the text. + */ + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) { + if (!OS.IsWindowEnabled (handle)) { + if (string.length() != 0) string = " " + string + " "; + } + } + } + TCHAR buffer = new TCHAR (getCodePage (), string, true); + OS.SetWindowText (handle, buffer); +} + +int widgetStyle () { + /* + * Bug in Windows. When GetDCEx() is called with DCX_INTERSECTUPDATE, + * the HDC that is returned does not include the current update region. + * This was confirmed under DEBUG Windows when GetDCEx() complained about + * invalid flags. Therefore, it is not easily possible to get an HDC from + * outside of WM_PAINT that includes the current damage and clips children. + * Because the receiver has children and draws a frame and label, it is + * necessary that the receiver always draw clipped, in the current damaged + * area. The fix is to force the receiver to be fully clipped by including + * WS_CLIPCHILDREN and WS_CLIPSIBLINGS in the default style bits. + */ + return super.widgetStyle () | OS.BS_GROUPBOX | OS.WS_CLIPCHILDREN | OS.WS_CLIPSIBLINGS; +} + +TCHAR windowClass () { + return GroupClass; +} + +int /*long*/ windowProc () { + return GroupProc; +} + +LRESULT WM_ERASEBKGND (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_ERASEBKGND (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. Group boxes do not erase + * the background before drawing. The fix is to + * fill the background. + */ + drawBackground (wParam); + return LRESULT.ONE; +} + +LRESULT WM_NCHITTEST (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_NCHITTEST (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. The window proc for the group box + * returns HTTRANSPARENT indicating that mouse messages + * should not be delivered to the receiver and any children. + * Normally, group boxes in Windows do not have children and + * this is the correct behavior for this case. Because we + * allow children, answer HTCLIENT to allow mouse messages + * to be delivered to the children. + */ + int /*long*/ code = callWindowProc (handle, OS.WM_NCHITTEST, wParam, lParam); + if (code == OS.HTTRANSPARENT) code = OS.HTCLIENT; + return new LRESULT (code); +} + +LRESULT WM_MOUSEMOVE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_MOUSEMOVE (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. In version 6.00 of COMCTL32.DLL, + * every time the mouse moves, the group title redraws. + * This only happens when WM_NCHITTEST returns HTCLIENT. + * The fix is to avoid calling the group window proc. + */ + return LRESULT.ZERO; +} + +LRESULT WM_PRINTCLIENT (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_PRINTCLIENT (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. In version 6.00 of COMCTL32.DLL, + * when WM_PRINTCLIENT is sent from a child BS_GROUP + * control to a parent BS_GROUP, the parent BS_GROUP + * clears the font from the HDC. Normally, group boxes + * in Windows do not have children so this behavior is + * undefined. When the parent of a BS_GROUP is not a + * BS_GROUP, there is no problem. The fix is to save + * and restore the current font. + */ + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + int nSavedDC = OS.SaveDC (wParam); + int /*long*/ code = callWindowProc (handle, OS.WM_PRINTCLIENT, wParam, lParam); + OS.RestoreDC (wParam, nSavedDC); + return new LRESULT (code); + } + return result; +} + +LRESULT WM_UPDATEUISTATE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_UPDATEUISTATE (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. When WM_UPDATEUISTATE is sent to + * a group, it sends WM_CTLCOLORBTN to get the foreground + * and background. If drawing happens in WM_CTLCOLORBTN, + * it will overwrite the contents of the control. The + * fix is draw the group without drawing the background + * and avoid the group window proc. + */ + boolean redraw = findImageControl () != null; + if (!redraw) { + if ((state & THEME_BACKGROUND) != 0) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + redraw = findThemeControl () != null; + } + } + if (!redraw) redraw = findBackgroundControl () != null; + } + if (redraw) { + OS.InvalidateRect (handle, null, false); + int /*long*/ code = OS.DefWindowProc (handle, OS.WM_UPDATEUISTATE, wParam, lParam); + return new LRESULT (code); + } + return result; +} + +LRESULT WM_WINDOWPOSCHANGING (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_WINDOWPOSCHANGING (wParam, lParam); + if (result != null) return result; + /* + * Invalidate the portion of the group widget that needs to + * be redrawn. Note that for some reason, invalidating the + * group from inside WM_SIZE causes pixel corruption for + * radio button children. + */ + if (OS.IsWinCE) return result; + if (!OS.IsWindowVisible (handle)) return result; + WINDOWPOS lpwp = new WINDOWPOS (); + OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof); + if ((lpwp.flags & (OS.SWP_NOSIZE | OS.SWP_NOREDRAW)) != 0) { + return result; + } + RECT rect = new RECT (); + OS.SetRect (rect, 0, 0, lpwp.cx, lpwp.cy); + OS.SendMessage (handle, OS.WM_NCCALCSIZE, 0, rect); + int newWidth = rect.right - rect.left; + int newHeight = rect.bottom - rect.top; + OS.GetClientRect (handle, rect); + int oldWidth = rect.right - rect.left; + int oldHeight = rect.bottom - rect.top; + if (newWidth == oldWidth && newHeight == oldHeight) { + return result; + } + if (newWidth != oldWidth) { + int left = oldWidth; + if (newWidth < oldWidth) left = newWidth; + OS.SetRect (rect, left - CLIENT_INSET, 0, newWidth, newHeight); + OS.InvalidateRect (handle, rect, true); + } + if (newHeight != oldHeight) { + int bottom = oldHeight; + if (newHeight < oldHeight) bottom = newHeight; + if (newWidth < oldWidth) oldWidth -= CLIENT_INSET; + OS.SetRect (rect, 0, bottom - CLIENT_INSET, oldWidth, newHeight); + OS.InvalidateRect (handle, rect, true); + } + return result; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/IME.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/IME.java new file mode 100644 index 0000000000..bfd9425355 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/IME.java @@ -0,0 +1,566 @@ +/******************************************************************************* + * 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.internal.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * 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 WM_MSIME_MOUSE = OS.RegisterWindowMessage (new TCHAR (0, "MSIMEMouseOperation", true)); //$NON-NLS-1$ + + static final byte [] IID_ITfInputProcessorProfiles = new byte [16]; + static final byte [] IID_ITfDisplayAttributeProvider = new byte [16]; + static final byte [] CLSID_TF_InputProcessorProfiles = new byte [16]; + static final byte [] GUID_TFCAT_TIP_KEYBOARD = new byte [16]; + static { + OS.IIDFromString ("{1F02B6C5-7842-4EE6-8A0B-9A24183A95CA}\0".toCharArray (), IID_ITfInputProcessorProfiles); //$NON-NLS-1$ + OS.IIDFromString ("{fee47777-163c-4769-996a-6e9c50ad8f54}\0".toCharArray (), IID_ITfDisplayAttributeProvider); //$NON-NLS-1$ + OS.IIDFromString ("{33C53A50-F456-4884-B049-85FD643ECFED}\0".toCharArray (), CLSID_TF_InputProcessorProfiles); //$NON-NLS-1$ + OS.IIDFromString ("{34745C63-B2F0-4784-8B67-5E12C8701A31}\0".toCharArray (), GUID_TFCAT_TIP_KEYBOARD); //$NON-NLS-1$ + } + + /* TextLayout has a copy of these constants */ + static final int UNDERLINE_IME_DOT = 1 << 16; + static final int UNDERLINE_IME_DASH = 2 << 16; + static final int UNDERLINE_IME_THICK = 3 << 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 (); +} + +void createWidget () { + text = ""; //$NON-NLS-1$ + startOffset = -1; + if (parent.getIME () == null) { + parent.setIME (this); + } +} + +/** + * 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; +} + +TF_DISPLAYATTRIBUTE getDisplayAttribute (short langid, int attInfo) { + int /*long*/ [] pProfiles = new int /*long*/ [1]; + int hr = OS.CoCreateInstance (CLSID_TF_InputProcessorProfiles, 0, OS.CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfiles, pProfiles); + TF_DISPLAYATTRIBUTE pda = null; + if (hr == OS.S_OK) { + byte [] pclsid = new byte [16]; + byte [] pguidProfile = new byte [16]; + /* pProfiles.GetDefaultLanguageProfile () */ + hr = OS.VtblCall (8, pProfiles [0], langid, GUID_TFCAT_TIP_KEYBOARD, pclsid, pguidProfile); + if (hr == OS.S_OK) { + int /*long*/ [] pProvider = new int /*long*/ [1]; + hr = OS.CoCreateInstance (pclsid, 0, OS.CLSCTX_INPROC_SERVER, IID_ITfDisplayAttributeProvider, pProvider); + if (hr == OS.S_OK) { + int /*long*/ [] pEnum = new int /*long*/ [1]; + /* pProvider.EnumDisplayAttributeInfo () */ + hr = OS.VtblCall (3, pProvider [0], pEnum); + if (hr == OS.S_OK) { + int /*long*/ [] pDispInfo = new int /*long*/ [1]; + TF_DISPLAYATTRIBUTE tempPda = new TF_DISPLAYATTRIBUTE (); + /* pEnum.Next () */ + while ((hr = OS.VtblCall (4, pEnum [0], 1, pDispInfo, null)) == OS.S_OK) { + /* pDispInfo.GetAttributeInfo(); */ + OS.VtblCall (5, pDispInfo [0], tempPda); + /* pDispInfo.Release () */ + OS.VtblCall (2, pDispInfo [0]); + if (tempPda.bAttr == attInfo) { + pda = tempPda; + break; + } + } + /* pEnum.Release () */ + hr = OS.VtblCall (2, pEnum [0]); + } + /* pProvider.Release () */ + hr = OS.VtblCall (2, pProvider [0]); + } + } + /* pProfiles.Release () */ + hr = OS.VtblCall (2, pProfiles [0]); + } + if (pda == null) { + pda = new TF_DISPLAYATTRIBUTE (); + switch (attInfo) { + case OS.TF_ATTR_INPUT: + pda.lsStyle = OS.TF_LS_SQUIGGLE; + break; + case OS.TF_ATTR_CONVERTED: + case OS.TF_ATTR_TARGET_CONVERTED: + pda.lsStyle = OS.TF_LS_SOLID; + pda.fBoldLine = attInfo == OS.TF_ATTR_TARGET_CONVERTED; + break; + } + } + return pda; +} + +/** + * 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; +} + +/** + * 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() { + checkWidget (); + int /*long*/ layout = OS.GetKeyboardLayout (0); + short langID = (short)OS.LOWORD (layout); + return OS.PRIMARYLANGID (langID) == OS.LANG_KOREAN; +} + +boolean isInlineEnabled () { + if (OS.IsWinCE || OS.WIN32_VERSION < OS.VERSION (5, 1)) return false; + return OS.IsDBLocale && hooks (SWT.ImeComposition); +} + +void releaseParent () { + super.releaseParent (); + if (this == parent.getIME ()) parent.setIME (null); +} + +void releaseWidget () { + super.releaseWidget (); + parent = null; + text = null; + styles = null; + ranges = null; +} + +/** + * 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; + } +} + +LRESULT WM_IME_COMPOSITION (int /*long*/ wParam, int /*long*/ lParam) { + if (!isInlineEnabled ()) return null; + ranges = null; + styles = null; + caretOffset = commitCount = 0; + int /*long*/ hwnd = parent.handle; + int /*long*/ hIMC = OS.ImmGetContext (hwnd); + int codePage = parent.getCodePage (); + if (hIMC != 0) { + TCHAR buffer = null; + if ((lParam & OS.GCS_RESULTSTR) != 0) { + int length = OS.ImmGetCompositionString (hIMC, OS.GCS_RESULTSTR, (TCHAR)null, 0); + if (length > 0) { + buffer = new TCHAR (codePage, length / TCHAR.sizeof); + OS.ImmGetCompositionString (hIMC, OS.GCS_RESULTSTR, buffer, length); + if (startOffset == -1) { + Event event = new Event (); + event.detail = SWT.COMPOSITION_SELECTION; + sendEvent (SWT.ImeComposition, event); + startOffset = event.start; + } + Event event = new Event (); + event.detail = SWT.COMPOSITION_CHANGED; + event.start = startOffset; + event.end = startOffset + text.length(); + event.text = text = buffer != null ? buffer.toString () : ""; //$NON-NLS-1$ + commitCount = text.length (); + sendEvent (SWT.ImeComposition, event); + String chars = text; + text = ""; //$NON-NLS-1$ + startOffset = -1; + commitCount = 0; + if (event.doit) { + Display display = this.display; + display.lastKey = 0; + display.lastVirtual = display.lastNull = display.lastDead = false; + length = chars.length (); + for (int i = 0; i < length; i++) { + char c = chars.charAt (i); + display.lastAscii = c; + event = new Event (); + event.character = c; + parent.sendEvent (SWT.KeyDown, event); + } + } + } + if ((lParam & OS.GCS_COMPSTR) == 0) return LRESULT.ONE; + } + buffer = null; + if ((lParam & OS.GCS_COMPSTR) != 0) { + int length = OS.ImmGetCompositionString (hIMC, OS.GCS_COMPSTR, (TCHAR)null, 0); + if (length > 0) { + buffer = new TCHAR (codePage, length / TCHAR.sizeof); + OS.ImmGetCompositionString (hIMC, OS.GCS_COMPSTR, buffer, length); + if ((lParam & OS.GCS_CURSORPOS) != 0) { + caretOffset = OS.ImmGetCompositionString (hIMC, OS.GCS_CURSORPOS, (TCHAR) null, 0); + } + int [] clauses = null; + if ((lParam & OS.GCS_COMPCLAUSE) != 0) { + length = OS.ImmGetCompositionString (hIMC, OS.GCS_COMPCLAUSE, (int [])null, 0); + if (length > 0) { + clauses = new int [length / 4]; + OS.ImmGetCompositionString (hIMC, OS.GCS_COMPCLAUSE, clauses, length); + } + } + if ((lParam & OS.GCS_COMPATTR) != 0 && clauses != null) { + length = OS.ImmGetCompositionString (hIMC, OS.GCS_COMPATTR, (byte [])null, 0); + if (length > 0) { + byte [] attrs = new byte [length]; + OS.ImmGetCompositionString (hIMC, OS.GCS_COMPATTR, attrs, length); + length = clauses.length - 1; + ranges = new int [length * 2]; + styles = new TextStyle [length]; + int /*long*/ layout = OS.GetKeyboardLayout (0); + short langID = (short)OS.LOWORD (layout); + TF_DISPLAYATTRIBUTE attr = null; + TextStyle style = null; + for (int i = 0; i < length; i++) { + ranges [i * 2] = clauses [i]; + ranges [i * 2 + 1] = clauses [i + 1] - 1; + styles [i] = style = new TextStyle (); + attr = getDisplayAttribute (langID, attrs [clauses [i]]); + if (attr != null) { + switch (attr.crText.type) { + case OS.TF_CT_COLORREF: + style.foreground = Color.win32_new (display, attr.crText.cr); + break; + case OS.TF_CT_SYSCOLOR: + int colorRef = OS.GetSysColor (attr.crText.cr); + style.foreground = Color.win32_new (display, colorRef); + break; + } + switch (attr.crBk.type) { + case OS.TF_CT_COLORREF: + style.background = Color.win32_new (display, attr.crBk.cr); + break; + case OS.TF_CT_SYSCOLOR: + int colorRef = OS.GetSysColor (attr.crBk.cr); + style.background = Color.win32_new (display, colorRef); + break; + } + switch (attr.crLine.type) { + case OS.TF_CT_COLORREF: + style.underlineColor = Color.win32_new (display, attr.crLine.cr); + break; + case OS.TF_CT_SYSCOLOR: + int colorRef = OS.GetSysColor (attr.crLine.cr); + style.underlineColor = Color.win32_new (display, colorRef); + break; + } + style.underline = attr.lsStyle != OS.TF_LS_NONE; + switch (attr.lsStyle) { + case OS.TF_LS_SQUIGGLE: + style.underlineStyle = SWT.UNDERLINE_SQUIGGLE; + break; + case OS.TF_LS_DASH: + style.underlineStyle = UNDERLINE_IME_DASH; + break; + case OS.TF_LS_DOT: + style.underlineStyle = UNDERLINE_IME_DOT; + break; + case OS.TF_LS_SOLID: + style.underlineStyle = attr.fBoldLine ? UNDERLINE_IME_THICK : SWT.UNDERLINE_SINGLE; + break; + } + } + } + } + } + } + OS.ImmReleaseContext (hwnd, hIMC); + } + 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; + } + Event event = new Event (); + event.detail = SWT.COMPOSITION_CHANGED; + event.start = startOffset; + event.end = end; + event.text = text = buffer != null ? buffer.toString () : ""; //$NON-NLS-1$ + sendEvent (SWT.ImeComposition, event); + if (text.length() == 0) { + startOffset = -1; + ranges = null; + styles = null; + } + } + return LRESULT.ONE; +} + +LRESULT WM_IME_COMPOSITION_START (int /*long*/ wParam, int /*long*/ lParam) { + return isInlineEnabled () ? LRESULT.ONE : null; +} + +LRESULT WM_IME_ENDCOMPOSITION (int /*long*/ wParam, int /*long*/ lParam) { + return isInlineEnabled () ? LRESULT.ONE : null; +} + +LRESULT WM_KILLFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + if (!isInlineEnabled ()) return null; + int /*long*/ hwnd = parent.handle; + int /*long*/ hIMC = OS.ImmGetContext (hwnd); + if (hIMC != 0) { + if (OS.ImmGetOpenStatus (hIMC)) { + OS.ImmNotifyIME (hIMC, OS.NI_COMPOSITIONSTR, OS.CPS_COMPLETE, 0); + } + OS.ImmReleaseContext (hwnd, hIMC); + } + return null; +} + +LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + if (!isInlineEnabled ()) return null; + int /*long*/ hwnd = parent.handle; + int /*long*/ hIMC = OS.ImmGetContext (hwnd); + if (hIMC != 0) { + if (OS.ImmGetOpenStatus (hIMC)) { + if (OS.ImmGetCompositionString (hIMC, OS.GCS_COMPSTR, (TCHAR)null, 0) > 0) { + Event event = new Event (); + event.detail = SWT.COMPOSITION_OFFSET; + event.x = OS.GET_X_LPARAM (lParam); + event.y = OS.GET_Y_LPARAM (lParam); + sendEvent (SWT.ImeComposition, event); + int offset = event.index; + int length = text.length(); + if (offset != -1 && startOffset != -1 && startOffset <= offset && offset < startOffset + length) { + int /*long*/ imeWnd = OS.ImmGetDefaultIMEWnd (hwnd); + offset = event.index + event.count - startOffset; + int trailing = event.count > 0 ? 1 : 2; + int /*long*/ param = OS.MAKEWPARAM (OS.MAKEWORD (OS.IMEMOUSE_LDOWN, trailing), offset); + OS.SendMessage (imeWnd, WM_MSIME_MOUSE, param, hIMC); + } else { + OS.ImmNotifyIME (hIMC, OS.NI_COMPOSITIONSTR, OS.CPS_COMPLETE, 0); + } + } + } + OS.ImmReleaseContext (hwnd, hIMC); + } + return null; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Label.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Label.java new file mode 100755 index 0000000000..5d23c9515a --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Label.java @@ -0,0 +1,686 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * 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; + static final int MARGIN = 4; + static /*final*/ boolean IMAGE_AND_TEXT = false; + static final int /*long*/ LabelProc; + static final TCHAR LabelClass = new TCHAR (0, "STATIC", true); + static { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, LabelClass, lpWndClass); + LabelProc = lpWndClass.lpfnWndProc; + } + +/** + * 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*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + return OS.CallWindowProc (LabelProc, hwnd, msg, wParam, lParam); +} + +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 = 0, height = 0, border = getBorderWidth (); + if ((style & SWT.SEPARATOR) != 0) { + int lineWidth = OS.GetSystemMetrics (OS.SM_CXBORDER); + if ((style & SWT.HORIZONTAL) != 0) { + width = DEFAULT_WIDTH; height = lineWidth * 2; + } else { + width = lineWidth * 2; height = DEFAULT_HEIGHT; + } + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + width += border * 2; height += border * 2; + return new Point (width, height); + } + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + boolean drawText = true; + boolean drawImage = (bits & OS.SS_OWNERDRAW) == OS.SS_OWNERDRAW; + if (drawImage) { + if (image != null) { + Rectangle rect = image.getBounds(); + width += rect.width; + height += rect.height; + if (IMAGE_AND_TEXT) { + if (text.length () != 0) width += MARGIN; + } else { + drawText = false; + } + } + } + if (drawText) { + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + int /*long*/ oldFont = OS.SelectObject (hDC, newFont); + int length = OS.GetWindowTextLength (handle); + if (length == 0) { + TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA (); + OS.GetTextMetrics (hDC, tm); + height = Math.max (height, tm.tmHeight); + } else { + RECT rect = new RECT (); + int flags = OS.DT_CALCRECT | OS.DT_EDITCONTROL | OS.DT_EXPANDTABS; + if ((style & SWT.WRAP) != 0 && wHint != SWT.DEFAULT) { + flags |= OS.DT_WORDBREAK; + rect.right = Math.max (0, wHint - width); + } + TCHAR buffer = new TCHAR (getCodePage (), length + 1); + OS.GetWindowText (handle, buffer, length + 1); + OS.DrawText (hDC, buffer, length, rect, flags); + width += rect.right - rect.left; + height = Math.max (height, rect.bottom - rect.top); + } + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + } + if (wHint != SWT.DEFAULT) width = wHint; + if (hHint != SWT.DEFAULT) height = hHint; + width += border * 2; + height += border * 2; + /* + * Feature in WinCE PPC. Text labels have a trim + * of one pixel wide on the right and left side. + * The fix is to increase the width to include + * this trim. + */ + if (OS.IsWinCE && !drawImage) width += 2; + return new Point (width, height); +} + +void createHandle () { + super.createHandle (); + state |= THEME_BACKGROUND; +} + +/** + * 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 0; + 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 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 mnemonicHit (char key) { + Composite control = this.parent; + while (control != null) { + Control [] children = control._getChildren (); + int index = 0; + while (index < children.length) { + if (children [index] == this) break; + index++; + } + index++; + if (index < children.length) { + if (children [index].setFocus ()) return true; + } + control = control.parent; + } + return false; +} + +boolean mnemonicMatch (char key) { + char mnemonic = findMnemonic (getText ()); + if (mnemonic == '\0') return false; + return Character.toUpperCase (key) == Character.toUpperCase (mnemonic); +} + +void releaseWidget () { + super.releaseWidget (); + text = null; + image = null; +} + +/** + * 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); + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.SS_OWNERDRAW) != OS.SS_OWNERDRAW) { + bits &= ~(OS.SS_LEFTNOWORDWRAP | OS.SS_CENTER | OS.SS_RIGHT); + if ((style & SWT.LEFT) != 0) { + if ((style & SWT.WRAP) != 0) { + bits |= OS.SS_LEFT; + } else { + bits |= OS.SS_LEFTNOWORDWRAP; + } + } + if ((style & SWT.CENTER) != 0) bits |= OS.SS_CENTER; + if ((style & SWT.RIGHT) != 0) bits |= OS.SS_RIGHT; + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); + } + OS.InvalidateRect (handle, null, true); +} + +/** + * 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; + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.SS_OWNERDRAW) != OS.SS_OWNERDRAW) { + bits &= ~(OS.SS_LEFTNOWORDWRAP | OS.SS_CENTER | OS.SS_RIGHT); + bits |= OS.SS_OWNERDRAW; + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); + } + OS.InvalidateRect (handle, null, true); +} + +/** + * 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; + /* + * Feature in Windows. For some reason, SetWindowText() for + * static controls redraws the control, even when the text has + * has not changed. The fix is to check for this case and do + * nothing. + */ + if (string.equals (text)) return; + text = string; + if (image == null || !IMAGE_AND_TEXT) { + int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits; + newBits &= ~OS.SS_OWNERDRAW; + if ((style & SWT.LEFT) != 0) { + if ((style & SWT.WRAP) != 0) { + newBits |= OS.SS_LEFT; + } else { + newBits |= OS.SS_LEFTNOWORDWRAP; + } + } + if ((style & SWT.CENTER) != 0) newBits |= OS.SS_CENTER; + if ((style & SWT.RIGHT) != 0) newBits |= OS.SS_RIGHT; + if (oldBits != newBits) OS.SetWindowLong (handle, OS.GWL_STYLE, newBits); + } + string = Display.withCrLf (string); + TCHAR buffer = new TCHAR (getCodePage (), string, true); + OS.SetWindowText (handle, buffer); + /* + * Bug in Windows. For some reason, the HBRUSH that + * is returned from WM_CTRLCOLOR is misaligned when + * the label uses it to draw. If the brush is a solid + * color, this does not matter. However, if the brush + * contains an image, the image is misaligned. The + * fix is to draw the background in WM_ERASEBKGND. + */ + if (OS.COMCTL32_MAJOR < 6) { + if (findImageControl () != null) OS.InvalidateRect (handle, null, true); + } +} + +int widgetExtStyle () { + int bits = super.widgetExtStyle () & ~OS.WS_EX_CLIENTEDGE; + if ((style & SWT.BORDER) != 0) return bits | OS.WS_EX_STATICEDGE; + return bits; +} + +int widgetStyle () { + int bits = super.widgetStyle () | OS.SS_NOTIFY; + if ((style & SWT.SEPARATOR) != 0) return bits | OS.SS_OWNERDRAW; + if (OS.WIN32_VERSION >= OS.VERSION (5, 0)) { + if ((style & SWT.WRAP) != 0) bits |= OS.SS_EDITCONTROL; + } + if ((style & SWT.CENTER) != 0) return bits | OS.SS_CENTER; + if ((style & SWT.RIGHT) != 0) return bits | OS.SS_RIGHT; + if ((style & SWT.WRAP) != 0) return bits | OS.SS_LEFT; + return bits | OS.SS_LEFTNOWORDWRAP; +} + +TCHAR windowClass () { + return LabelClass; +} + +int /*long*/ windowProc () { + return LabelProc; +} + +LRESULT WM_ERASEBKGND (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_ERASEBKGND (wParam, lParam); + if (result != null) return result; + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.SS_OWNERDRAW) == OS.SS_OWNERDRAW) { + return LRESULT.ONE; + } + /* + * Bug in Windows. For some reason, the HBRUSH that + * is returned from WM_CTRLCOLOR is misaligned when + * the label uses it to draw. If the brush is a solid + * color, this does not matter. However, if the brush + * contains an image, the image is misaligned. The + * fix is to draw the background in WM_ERASEBKGND. + */ + if (OS.COMCTL32_MAJOR < 6) { + if (findImageControl () != null) { + drawBackground (wParam); + return LRESULT.ONE; + } + } + return result; +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SIZE (wParam, lParam); + if (isDisposed ()) return result; + if ((style & SWT.SEPARATOR) != 0) { + OS.InvalidateRect (handle, null, true); + return result; + } + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.SS_OWNERDRAW) == OS.SS_OWNERDRAW) { + OS.InvalidateRect (handle, null, true); + return result; + } + /* + * Bug in Windows. For some reason, a label with + * style SS_LEFT, SS_CENTER or SS_RIGHT does not + * redraw the text in the new position when resized. + * Note that SS_LEFTNOWORDWRAP does not have the + * problem. The fix is to force the redraw. + */ + if ((bits & OS.SS_LEFTNOWORDWRAP) != OS.SS_LEFTNOWORDWRAP) { + OS.InvalidateRect (handle, null, true); + return result; + } + return result; +} + +LRESULT WM_UPDATEUISTATE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_UPDATEUISTATE (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. When WM_UPDATEUISTATE is sent to + * a static control, it sends WM_CTLCOLORSTATIC to get the + * foreground and background. If any drawing happens in + * WM_CTLCOLORSTATIC, it overwrites the contents of the control. + * The fix is draw the static without drawing the background + * and avoid the static window proc. + */ + boolean redraw = findImageControl () != null; + if (!redraw) { + if ((state & THEME_BACKGROUND) != 0) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + redraw = findThemeControl () != null; + } + } + } + if (redraw) { + OS.InvalidateRect (handle, null, false); + int /*long*/ code = OS.DefWindowProc (handle, OS.WM_UPDATEUISTATE, wParam, lParam); + return new LRESULT (code); + } + return result; +} + +LRESULT wmColorChild (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Bug in Windows. For some reason, the HBRUSH that + * is returned from WM_CTRLCOLOR is misaligned when + * the label uses it to draw. If the brush is a solid + * color, this does not matter. However, if the brush + * contains an image, the image is misaligned. The + * fix is to draw the background in WM_ERASEBKGND. + */ + LRESULT result = super.wmColorChild (wParam, lParam); + if (OS.COMCTL32_MAJOR < 6) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.SS_OWNERDRAW) != OS.SS_OWNERDRAW) { + if (findImageControl () != null) { + OS.SetBkMode (wParam, OS.TRANSPARENT); + return new LRESULT (OS.GetStockObject (OS.NULL_BRUSH)); + } + } + } + return result; +} + +LRESULT WM_PAINT (int /*long*/ wParam, int /*long*/ lParam) { + if (OS.IsWinCE) { + boolean drawImage = image != null; + boolean drawSeparator = (style & SWT.SEPARATOR) != 0 && (style & SWT.SHADOW_NONE) == 0; + if (drawImage || drawSeparator) { + LRESULT result = null; + PAINTSTRUCT ps = new PAINTSTRUCT (); + GCData data = new GCData (); + data.ps = ps; + data.hwnd = handle; + GC gc = new_GC (data); + if (gc != null) { + drawBackground (gc.handle); + RECT clientRect = new RECT(); + OS.GetClientRect (handle, clientRect); + if (drawSeparator) { + RECT rect = new RECT (); + int lineWidth = OS.GetSystemMetrics (OS.SM_CXBORDER); + int flags = (style & SWT.SHADOW_IN) != 0 ? OS.EDGE_SUNKEN : OS.EDGE_ETCHED; + if ((style & SWT.HORIZONTAL) != 0) { + int bottom = clientRect.top + Math.max (lineWidth * 2, (clientRect.bottom - clientRect.top) / 2); + OS.SetRect (rect, clientRect.left, clientRect.top, clientRect.right, bottom); + OS.DrawEdge (gc.handle, rect, flags, OS.BF_BOTTOM); + } else { + int right = clientRect.left + Math.max (lineWidth * 2, (clientRect.right - clientRect.left) / 2); + OS.SetRect (rect, clientRect.left, clientRect.top, right, clientRect.bottom); + OS.DrawEdge (gc.handle, rect, flags, OS.BF_RIGHT); + } + result = LRESULT.ONE; + } + if (drawImage) { + Rectangle imageBounds = image.getBounds (); + int x = 0; + if ((style & SWT.CENTER) != 0) { + x = Math.max (0, (clientRect.right - imageBounds.width) / 2); + } else { + if ((style & SWT.RIGHT) != 0) { + x = Math.max (0, (clientRect.right - imageBounds.width)); + } + } + gc.drawImage (image, x, Math.max (0, (clientRect.bottom - imageBounds.height) / 2)); + result = LRESULT.ONE; + } + int width = ps.right - ps.left; + int height = ps.bottom - ps.top; + if (width != 0 && height != 0) { + Event event = new Event (); + event.gc = gc; + event.x = ps.left; + event.y = ps.top; + event.width = width; + event.height = height; + sendEvent (SWT.Paint, event); + // widget could be disposed at this point + event.gc = null; + } + gc.dispose (); + } + return result; + } + } + return super.WM_PAINT(wParam, lParam); +} + +LRESULT wmDrawChild (int /*long*/ wParam, int /*long*/ lParam) { + DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT (); + OS.MoveMemory (struct, lParam, DRAWITEMSTRUCT.sizeof); + drawBackground (struct.hDC); + if ((style & SWT.SEPARATOR) != 0) { + if ((style & SWT.SHADOW_NONE) != 0) return null; + RECT rect = new RECT (); + int lineWidth = OS.GetSystemMetrics (OS.SM_CXBORDER); + int flags = (style & SWT.SHADOW_IN) != 0 ? OS.EDGE_SUNKEN : OS.EDGE_ETCHED; + if ((style & SWT.HORIZONTAL) != 0) { + int bottom = struct.top + Math.max (lineWidth * 2, (struct.bottom - struct.top) / 2); + OS.SetRect (rect, struct.left, struct.top, struct.right, bottom); + OS.DrawEdge (struct.hDC, rect, flags, OS.BF_BOTTOM); + } else { + int right = struct.left + Math.max (lineWidth * 2, (struct.right - struct.left) / 2); + OS.SetRect (rect, struct.left, struct.top, right, struct.bottom); + OS.DrawEdge (struct.hDC, rect, flags, OS.BF_RIGHT); + } + } else { + int width = struct.right - struct.left; + int height = struct.bottom - struct.top; + if (width != 0 && height != 0) { + boolean drawImage = image != null; + boolean drawText = IMAGE_AND_TEXT && text.length () != 0; + int margin = drawText && drawImage ? MARGIN : 0; + int imageWidth = 0, imageHeight = 0; + if (drawImage) { + Rectangle rect = image.getBounds (); + imageWidth = rect.width; + imageHeight = rect.height; + } + RECT rect = null; + TCHAR buffer = null; + int textWidth = 0, textHeight = 0, flags = 0; + if (drawText) { + rect = new RECT (); + flags = OS.DT_CALCRECT | OS.DT_EDITCONTROL | OS.DT_EXPANDTABS; + if ((style & SWT.LEFT) != 0) flags |= OS.DT_LEFT; + if ((style & SWT.CENTER) != 0) flags |= OS.DT_CENTER; + if ((style & SWT.RIGHT) != 0) flags |= OS.DT_RIGHT; + if ((style & SWT.WRAP) != 0) { + flags |= OS.DT_WORDBREAK; + rect.right = Math.max (0, width - imageWidth - margin); + } + buffer = new TCHAR (getCodePage (), text, true); + OS.DrawText (struct.hDC, buffer, -1, rect, flags); + textWidth = rect.right - rect.left; + textHeight = rect.bottom - rect.top; + } + int x = 0; + if ((style & SWT.CENTER) != 0) { + x = Math.max (0, (width - imageWidth - textWidth - margin) / 2); + } else { + if ((style & SWT.RIGHT) != 0) { + x = width - imageWidth - textWidth - margin; + } + } + if (drawImage) { + GCData data = new GCData(); + data.device = display; + GC gc = GC.win32_new (struct.hDC, data); + Image image = getEnabled () ? this.image : new Image (display, this.image, SWT.IMAGE_DISABLE); + gc.drawImage (image, x, Math.max (0, (height - imageHeight) / 2)); + if (image != this.image) image.dispose (); + gc.dispose (); + x += imageWidth + margin; + } + if (drawText) { + flags &= ~OS.DT_CALCRECT; + rect.left = x; + rect.right += rect.left; + rect.top = Math.max (0, (height - textHeight) / 2); + rect.bottom += rect.top; + OS.DrawText (struct.hDC, buffer, -1, rect, flags); + } + } + } + return null; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Link.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Link.java new file mode 100644 index 0000000000..bf0d12ea71 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Link.java @@ -0,0 +1,1010 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.accessibility.*; + +/** + * 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 { + String text; + TextLayout layout; + Color linkColor, disabledColor; + Point [] offsets; + Point selection; + String [] ids; + int [] mnemonics; + int focusIndex, mouseDownIndex; + int /*long*/ font; + static final RGB LINK_FOREGROUND = new RGB (0, 51, 153); + static final int /*long*/ LinkProc; + static final TCHAR LinkClass = new TCHAR (0, OS.WC_LINK, true); + static { + if (OS.COMCTL32_MAJOR >= 6) { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, LinkClass, lpWndClass); + LinkProc = lpWndClass.lpfnWndProc; + /* + * Feature in Windows. The SysLink window class + * does not include CS_DBLCLKS. This means that these + * controls will not get double click messages such as + * WM_LBUTTONDBLCLK. The fix is to register a new + * window class with CS_DBLCLKS. + * + * NOTE: Screen readers look for the exact class name + * of the control in order to provide the correct kind + * of assistance. Therefore, it is critical that the + * new window class have the same name. It is possible + * to register a local window class with the same name + * as a global class. Since bits that affect the class + * are being changed, it is possible that other native + * code, other than SWT, could create a control with + * this class name, and fail unexpectedly. + */ + int /*long*/ hInstance = OS.GetModuleHandle (null); + int /*long*/ hHeap = OS.GetProcessHeap (); + lpWndClass.hInstance = hInstance; + lpWndClass.style &= ~OS.CS_GLOBALCLASS; + lpWndClass.style |= OS.CS_DBLCLKS; + int byteCount = LinkClass.length () * TCHAR.sizeof; + int /*long*/ lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (lpszClassName, LinkClass, byteCount); + lpWndClass.lpszClassName = lpszClassName; + OS.RegisterClass (lpWndClass); + OS.HeapFree (hHeap, 0, lpszClassName); + } else { + LinkProc = 0; + } + } + +/** + * 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); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + if (LinkProc != 0) { + /* + * Feature in Windows. By convention, native Windows controls + * check for a non-NULL wParam, assume that it is an HDC and + * paint using that device. The SysLink control does not. + * The fix is to check for an HDC and use WM_PRINTCLIENT. + */ + switch (msg) { + case OS.WM_PAINT: + if (wParam != 0) { + OS.SendMessage (hwnd, OS.WM_PRINTCLIENT, wParam, 0); + return 0; + } + break; + } + return OS.CallWindowProc (LinkProc, hwnd, msg, wParam, lParam); + } + return OS.DefWindowProc (hwnd, msg, wParam, lParam); +} + +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; + if (OS.COMCTL32_MAJOR >= 6) { + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + int /*long*/ oldFont = OS.SelectObject (hDC, newFont); + if (text.length () > 0) { + TCHAR buffer = new TCHAR (getCodePage (), parse (text), false); + RECT rect = new RECT (); + int flags = OS.DT_CALCRECT | OS.DT_NOPREFIX; + if (wHint != SWT.DEFAULT) { + flags |= OS.DT_WORDBREAK; + rect.right = wHint; + } + OS.DrawText (hDC, buffer, buffer.length (), rect, flags); + width = rect.right - rect.left; + height = rect.bottom; + } else { + TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW () : new TEXTMETRICA (); + OS.GetTextMetrics (hDC, lptm); + width = 0; + height = lptm.tmHeight; + } + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + } else { + int layoutWidth = layout.getWidth (); + //TEMPORARY CODE + if (wHint == 0) { + layout.setWidth (1); + Rectangle rect = layout.getBounds (); + width = 0; + height = rect.height; + } else { + layout.setWidth (wHint); + Rectangle rect = layout.getBounds (); + width = rect.width; + height = rect.height; + } + layout.setWidth (layoutWidth); + } + 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 () { + super.createHandle (); + state |= THEME_BACKGROUND; + if (OS.COMCTL32_MAJOR < 6) { + layout = new TextLayout (display); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) { + linkColor = Color.win32_new (display, OS.GetSysColor (OS.COLOR_HOTLIGHT)); + } else { + linkColor = new Color (display, LINK_FOREGROUND); + } + disabledColor = Color.win32_new (display, OS.GetSysColor (OS.COLOR_GRAYTEXT)); + offsets = new Point [0]; + ids = new String [0]; + mnemonics = new int [0]; + selection = new Point (-1, -1); + focusIndex = mouseDownIndex = -1; + } +} + +void createWidget () { + super.createWidget (); + text = ""; + if (OS.COMCTL32_MAJOR < 6) { + if ((style & SWT.MIRRORED) != 0) { + layout.setOrientation (SWT.RIGHT_TO_LEFT); + } + initAccessible (); + } +} + +void drawWidget (GC gc, RECT rect) { + drawBackground (gc.handle, rect); + int selStart = selection.x; + int selEnd = selection.y; + if (selStart > selEnd) { + selStart = selection.y; + selEnd = selection.x; + } + // temporary code to disable text selection + selStart = selEnd = -1; + if (!OS.IsWindowEnabled (handle)) gc.setForeground (disabledColor); + layout.draw (gc, 0, 0, selStart, selEnd, null, null); + if (hasFocus () && focusIndex != -1) { + Rectangle [] rects = getRectangles (focusIndex); + for (int i = 0; i < rects.length; i++) { + Rectangle rectangle = rects [i]; + gc.drawFocus (rectangle.x, rectangle.y, rectangle.width, rectangle.height); + } + } + if (hooks (SWT.Paint) || filters (SWT.Paint)) { + Event event = new Event (); + event.gc = gc; + event.x = rect.left; + event.y = rect.top; + event.width = rect.right - rect.left; + event.height = rect.bottom - rect.top; + sendEvent (SWT.Paint, event); + event.gc = null; + } +} + +void enableWidget (boolean enabled) { + if (OS.COMCTL32_MAJOR >= 6) { + LITEM item = new LITEM (); + item.mask = OS.LIF_ITEMINDEX | OS.LIF_STATE; + item.stateMask = OS.LIS_ENABLED; + item.state = enabled ? OS.LIS_ENABLED : 0; + while (OS.SendMessage (handle, OS.LM_SETITEM, 0, item) != 0) { + item.iLink++; + } + } else { + TextStyle linkStyle = new TextStyle (null, enabled ? linkColor : disabledColor, null); + linkStyle.underline = true; + for (int i = 0; i < offsets.length; i++) { + Point point = offsets [i]; + layout.setStyle (linkStyle, point.x, point.y); + } + redraw (); + } + /* + * Feature in Windows. For some reason, setting + * LIS_ENABLED state using LM_SETITEM causes the + * SysLink to become enabled. To be specific, + * calling IsWindowEnabled() returns true. The + * fix is disable the SysLink after LM_SETITEM. + */ + super.enableWidget (enabled); +} + +void initAccessible () { + Accessible accessible = getAccessible (); + accessible.addAccessibleListener (new AccessibleAdapter () { + public void getName (AccessibleEvent e) { + e.result = parse (text); + } + }); + + accessible.addAccessibleControlListener (new AccessibleControlAdapter () { + public void getChildAtPoint (AccessibleControlEvent e) { + e.childID = ACC.CHILDID_SELF; + } + + public void getLocation (AccessibleControlEvent e) { + Rectangle rect = display.map (getParent (), null, getBounds ()); + e.x = rect.x; + e.y = rect.y; + e.width = rect.width; + e.height = rect.height; + } + + public void getChildCount (AccessibleControlEvent e) { + e.detail = 0; + } + + public void getRole (AccessibleControlEvent e) { + e.detail = ACC.ROLE_LINK; + } + + public void getState (AccessibleControlEvent e) { + e.detail = ACC.STATE_FOCUSABLE; + if (hasFocus ()) e.detail |= ACC.STATE_FOCUSED; + } + + public void getDefaultAction (AccessibleControlEvent e) { + e.result = SWT.getMessage ("SWT_Press"); //$NON-NLS-1$ + } + + public void getSelection (AccessibleControlEvent e) { + if (hasFocus ()) e.childID = ACC.CHILDID_SELF; + } + + public void getFocus (AccessibleControlEvent e) { + if (hasFocus ()) e.childID = ACC.CHILDID_SELF; + } + }); +} + +String getNameText () { + return getText (); +} + +Rectangle [] getRectangles (int linkIndex) { + int lineCount = layout.getLineCount (); + Rectangle [] rects = new Rectangle [lineCount]; + int [] lineOffsets = layout.getLineOffsets (); + Point point = offsets [linkIndex]; + int lineStart = 1; + while (point.x > lineOffsets [lineStart]) lineStart++; + int lineEnd = 1; + while (point.y > lineOffsets [lineEnd]) lineEnd++; + int index = 0; + if (lineStart == lineEnd) { + rects [index++] = layout.getBounds (point.x, point.y); + } else { + rects [index++] = layout.getBounds (point.x, lineOffsets [lineStart]-1); + rects [index++] = layout.getBounds (lineOffsets [lineEnd-1], point.y); + if (lineEnd - lineStart > 1) { + for (int i = lineStart; i < lineEnd - 1; i++) { + rects [index++] = layout.getLineBounds (i); + } + } + } + if (rects.length != index) { + Rectangle [] tmp = new Rectangle [index]; + System.arraycopy (rects, 0, tmp, 0, index); + rects = tmp; + } + return rects; +} + +/** + * 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; +} + +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 releaseWidget () { + super.releaseWidget (); + if (layout != null) layout.dispose (); + layout = null; + if (linkColor != null) linkColor.dispose (); + linkColor = null; + disabledColor = null; + offsets = null; + ids = null; + mnemonics = 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); +} + +/** + * 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; + if (OS.COMCTL32_MAJOR >= 6) { + boolean enabled = OS.IsWindowEnabled (handle); + /* + * Bug in Windows. For some reason, when SetWindowText() + * is used to set the text of a link control to the empty + * string, the old text remains. The fix is to set the + * text to a space instead. + */ + if (string.length () == 0) string = " "; //$NON-NLS-1$ + TCHAR buffer = new TCHAR (getCodePage (), string, true); + OS.SetWindowText (handle, buffer); + parse (text); + enableWidget (enabled); + } else { + layout.setText (parse (text)); + focusIndex = offsets.length > 0 ? 0 : -1; + selection.x = selection.y = -1; + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if (offsets.length > 0) { + bits |= OS.WS_TABSTOP; + } else { + bits &= ~OS.WS_TABSTOP; + } + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); + boolean enabled = OS.IsWindowEnabled (handle); + TextStyle linkStyle = new TextStyle (null, enabled ? linkColor : disabledColor, null); + linkStyle.underline = true; + for (int i = 0; i < offsets.length; i++) { + Point point = offsets [i]; + layout.setStyle (linkStyle, point.x, point.y); + } + TextStyle mnemonicStyle = new TextStyle (null, null, null); + mnemonicStyle.underline = true; + for (int i = 0; i < mnemonics.length; i++) { + int mnemonic = mnemonics [i]; + if (mnemonic != -1) { + layout.setStyle (mnemonicStyle, mnemonic, mnemonic); + } + } + redraw (); + } +} + +int widgetStyle () { + int bits = super.widgetStyle (); + return bits | OS.WS_TABSTOP; +} + +TCHAR windowClass () { + return OS.COMCTL32_MAJOR >= 6 ? LinkClass : display.windowClass; +} + +int /*long*/ windowProc () { + return LinkProc != 0 ? LinkProc : display.windowProc; +} + +LRESULT WM_CHAR (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_CHAR (wParam, lParam); + if (result != null) return result; + if (OS.COMCTL32_MAJOR < 6) { + if (focusIndex == -1) return result; + switch ((int)/*64*/wParam) { + case ' ': + case SWT.CR: + Event event = new Event (); + event.text = ids [focusIndex]; + sendEvent (SWT.Selection, event); + break; + case SWT.TAB: + boolean next = OS.GetKeyState (OS.VK_SHIFT) >= 0; + if (next) { + if (focusIndex < offsets.length - 1) { + focusIndex++; + redraw (); + } + } else { + if (focusIndex > 0) { + focusIndex--; + redraw (); + } + } + break; + } + } else { + switch ((int)/*64*/wParam) { + case ' ': + case SWT.CR: + case SWT.TAB: + /* + * NOTE: Call the window proc with WM_KEYDOWN rather than WM_CHAR + * so that the key that was ignored during WM_KEYDOWN is processed. + * This allows the application to cancel an operation that is normally + * performed in WM_KEYDOWN from WM_CHAR. + */ + int /*long*/ code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam); + return new LRESULT (code); + } + + } + return result; +} + +LRESULT WM_GETDLGCODE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_GETDLGCODE (wParam, lParam); + if (result != null) return result; + int index, count; + int /*long*/ code = 0; + if (OS.COMCTL32_MAJOR >= 6) { + LITEM item = new LITEM (); + item.mask = OS.LIF_ITEMINDEX | OS.LIF_STATE; + item.stateMask = OS.LIS_FOCUSED; + index = 0; + while (OS.SendMessage (handle, OS.LM_GETITEM, 0, item) != 0) { + if ((item.state & OS.LIS_FOCUSED) != 0) { + index = item.iLink; + } + item.iLink++; + } + count = item.iLink; + code = callWindowProc (handle, OS.WM_GETDLGCODE, wParam, lParam); + } else { + index = focusIndex; + count = offsets.length; + } + if (count == 0) { + return new LRESULT (code | OS.DLGC_STATIC); + } + boolean next = OS.GetKeyState (OS.VK_SHIFT) >= 0; + if (next && index < count - 1) { + return new LRESULT (code | OS.DLGC_WANTTAB); + } + if (!next && index > 0) { + return new LRESULT (code | OS.DLGC_WANTTAB); + } + return result; +} + +LRESULT WM_GETFONT (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_GETFONT (wParam, lParam); + if (result != null) return result; + int /*long*/ code = callWindowProc (handle, OS.WM_GETFONT, wParam, lParam); + if (code != 0) return new LRESULT (code); + if (font == 0) font = defaultFont (); + return new LRESULT (font); +} + +LRESULT WM_KEYDOWN (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_KEYDOWN (wParam, lParam); + if (result != null) return result; + if (OS.COMCTL32_MAJOR >= 6) { + switch ((int)/*64*/wParam) { + case OS.VK_SPACE: + case OS.VK_RETURN: + case OS.VK_TAB: + /* + * Ensure that the window proc does not process VK_SPACE, + * VK_RETURN or VK_TAB so that it can be handled in WM_CHAR. + * This allows the application to cancel an operation that + * is normally performed in WM_KEYDOWN from WM_CHAR. + */ + return LRESULT.ZERO; + } + } + return result; +} + +LRESULT WM_KILLFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_KILLFOCUS (wParam, lParam); + if (OS.COMCTL32_MAJOR < 6) redraw (); + return result; +} + +LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam); + if (result == LRESULT.ZERO) return result; + if (OS.COMCTL32_MAJOR < 6) { + if (focusIndex != -1) setFocus (); + int x = OS.GET_X_LPARAM (lParam); + int y = OS.GET_Y_LPARAM (lParam); + int offset = layout.getOffset (x, y, null); + int oldSelectionX = selection.x; + int oldSelectionY = selection.y; + selection.x = offset; + selection.y = -1; + if (oldSelectionX != -1 && oldSelectionY != -1) { + if (oldSelectionX > oldSelectionY) { + int temp = oldSelectionX; + oldSelectionX = oldSelectionY; + oldSelectionY = temp; + } + Rectangle rect = layout.getBounds (oldSelectionX, oldSelectionY); + redraw (rect.x, rect.y, rect.width, rect.height, false); + } + for (int j = 0; j < offsets.length; j++) { + Rectangle [] rects = getRectangles (j); + for (int i = 0; i < rects.length; i++) { + Rectangle rect = rects [i]; + if (rect.contains (x, y)) { + if (j != focusIndex) { + redraw (); + } + focusIndex = mouseDownIndex = j; + return result; + } + } + } + } + return result; +} + +LRESULT WM_LBUTTONUP (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_LBUTTONUP (wParam, lParam); + if (result == LRESULT.ZERO) return result; + if (OS.COMCTL32_MAJOR < 6) { + if (mouseDownIndex == -1) return result; + int x = OS.GET_X_LPARAM (lParam); + int y = OS.GET_Y_LPARAM (lParam); + Rectangle [] rects = getRectangles (mouseDownIndex); + for (int i = 0; i < rects.length; i++) { + Rectangle rect = rects [i]; + if (rect.contains (x, y)) { + Event event = new Event (); + event.text = ids [mouseDownIndex]; + sendEvent (SWT.Selection, event); + break; + } + } + } + mouseDownIndex = -1; + return result; +} + +LRESULT WM_NCHITTEST (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_NCHITTEST (wParam, lParam); + if (result != null) return result; + + /* + * Feature in Windows. For WM_NCHITTEST, the Syslink window proc + * returns HTTRANSPARENT when mouse is over plain text. The fix is + * to always return HTCLIENT. + */ + if (OS.COMCTL32_MAJOR >= 6) return new LRESULT (OS.HTCLIENT); + + return result; +} + +LRESULT WM_MOUSEMOVE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_MOUSEMOVE (wParam, lParam); + if (OS.COMCTL32_MAJOR < 6) { + int x = OS.GET_X_LPARAM (lParam); + int y = OS.GET_Y_LPARAM (lParam); + if (OS.GetKeyState (OS.VK_LBUTTON) < 0) { + int oldSelection = selection.y; + selection.y = layout.getOffset (x, y, null); + if (selection.y != oldSelection) { + int newSelection = selection.y; + if (oldSelection > newSelection) { + int temp = oldSelection; + oldSelection = newSelection; + newSelection = temp; + } + Rectangle rect = layout.getBounds (oldSelection, newSelection); + redraw (rect.x, rect.y, rect.width, rect.height, false); + } + } else { + for (int j = 0; j < offsets.length; j++) { + Rectangle [] rects = getRectangles (j); + for (int i = 0; i < rects.length; i++) { + Rectangle rect = rects [i]; + if (rect.contains (x, y)) { + setCursor (display.getSystemCursor (SWT.CURSOR_HAND)); + return result; + } + } + } + setCursor (null); + } + } + return result; +} + +LRESULT WM_PAINT (int /*long*/ wParam, int /*long*/ lParam) { + if (OS.COMCTL32_MAJOR >= 6) { + return super.WM_PAINT (wParam, lParam); + } + PAINTSTRUCT ps = new PAINTSTRUCT (); + GCData data = new GCData (); + data.ps = ps; + data.hwnd = handle; + GC gc = new_GC (data); + if (gc != null) { + int width = ps.right - ps.left; + int height = ps.bottom - ps.top; + if (width != 0 && height != 0) { + RECT rect = new RECT (); + OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom); + drawWidget (gc, rect); + } + gc.dispose (); + } + return LRESULT.ZERO; +} + +LRESULT WM_PRINTCLIENT (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_PRINTCLIENT (wParam, lParam); + if (OS.COMCTL32_MAJOR < 6) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + GCData data = new GCData (); + data.device = display; + data.foreground = getForegroundPixel (); + GC gc = GC.win32_new (wParam, data); + drawWidget (gc, rect); + gc.dispose (); + } + return result; +} + +LRESULT WM_SETFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETFOCUS (wParam, lParam); + if (OS.COMCTL32_MAJOR < 6) redraw (); + return result; +} + +LRESULT WM_SETFONT (int /*long*/ wParam, int /*long*/ lParam) { + if (OS.COMCTL32_MAJOR < 6) { + layout.setFont (Font.win32_new (display, wParam)); + } + if (lParam != 0) OS.InvalidateRect (handle, null, true); + return super.WM_SETFONT (font = wParam, lParam); +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SIZE (wParam, lParam); + if (OS.COMCTL32_MAJOR < 6) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + layout.setWidth (rect.right > 0 ? rect.right : -1); + redraw (); + } + return result; +} + +LRESULT wmColorChild (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.wmColorChild (wParam, lParam); + /* + * Feature in Windows. When a SysLink is disabled, it does + * not gray out the non-link portion of the text. The fix + * is to set the text color to the system gray color. + */ + if (OS.COMCTL32_MAJOR >= 6) { + if (!OS.IsWindowEnabled (handle)) { + OS.SetTextColor (wParam, OS.GetSysColor (OS.COLOR_GRAYTEXT)); + if (result == null) { + int backPixel = getBackgroundPixel (); + OS.SetBkColor (wParam, backPixel); + int /*long*/ hBrush = findBrush (backPixel, OS.BS_SOLID); + return new LRESULT (hBrush); + } + } + } + return result; +} + +LRESULT wmNotifyChild (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + if (OS.COMCTL32_MAJOR >= 6) { + switch (hdr.code) { + case OS.NM_RETURN: + case OS.NM_CLICK: + NMLINK item = new NMLINK (); + OS.MoveMemory (item, lParam, NMLINK.sizeof); + Event event = new Event (); + event.text = ids [item.iLink]; + sendEvent (SWT.Selection, event); + break; + } + } + return super.wmNotifyChild (hdr, wParam, lParam); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/List.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/List.java new file mode 100755 index 0000000000..aecad23367 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/List.java @@ -0,0 +1,1716 @@ +/******************************************************************************* + * 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.win32.*; +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 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 { + static final int INSET = 3; + static final int /*long*/ ListProc; + static final TCHAR ListClass = new TCHAR (0, "LISTBOX", true); + static { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, ListClass, lpWndClass); + ListProc = lpWndClass.lpfnWndProc; + } + +/** + * 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)); +} +/** + * 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); + TCHAR buffer = new TCHAR (getCodePage (), string, true); + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_ADDSTRING, 0, buffer); + if (result == OS.LB_ERR) error (SWT.ERROR_ITEM_NOT_ADDED); + if (result == OS.LB_ERRSPACE) error (SWT.ERROR_ITEM_NOT_ADDED); + if ((style & SWT.H_SCROLL) != 0) setScrollWidth (buffer, true); +} +/** + * 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 (index == -1) error (SWT.ERROR_INVALID_RANGE); + TCHAR buffer = new TCHAR (getCodePage (), string, true); + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_INSERTSTRING, index, buffer); + if (result == OS.LB_ERRSPACE) error (SWT.ERROR_ITEM_NOT_ADDED); + if (result == OS.LB_ERR) { + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (0 <= index && index <= count) { + error (SWT.ERROR_ITEM_NOT_ADDED); + } else { + error (SWT.ERROR_INVALID_RANGE); + } + } + if ((style & SWT.H_SCROLL) != 0) setScrollWidth (buffer, true); +} + +/** + * 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); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + boolean redraw = false; + switch (msg) { + case OS.WM_HSCROLL: + case OS.WM_VSCROLL: { + redraw = findImageControl () != null && getDrawing() && OS.IsWindowVisible (handle); + if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + break; + } + } + int /*long*/ code = OS.CallWindowProc (ListProc, hwnd, msg, wParam, lParam); + switch (msg) { + case OS.WM_HSCROLL: + case OS.WM_VSCROLL: { + if (redraw) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.InvalidateRect (handle, null, true); + } + break; + } + } + return code; +} + +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, height = 0; + if (wHint == SWT.DEFAULT) { + if ((style & SWT.H_SCROLL) != 0) { + width = (int)/*64*/OS.SendMessage (handle, OS.LB_GETHORIZONTALEXTENT, 0, 0); + width -= INSET; + } else { + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + int /*long*/ newFont, oldFont = 0; + int /*long*/ hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + RECT rect = new RECT (); + int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX; + int cp = getCodePage (); + TCHAR buffer = new TCHAR (cp, 64 + 1); + for (int i=0; i<count; i++) { + int length = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTEXTLEN, i, 0); + if (length != OS.LB_ERR) { + if (length + 1 > buffer.length ()) { + buffer = new TCHAR (cp, length + 1); + } + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTEXT, i, buffer); + if (result != OS.LB_ERR) { + OS.DrawText (hDC, buffer, length, rect, flags); + width = Math.max (width, rect.right - rect.left); + } + } + } + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + } + } + if (hHint == SWT.DEFAULT) { + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + int itemHeight = (int)/*64*/OS.SendMessage (handle, OS.LB_GETITEMHEIGHT, 0, 0); + height = count * itemHeight; + } + 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 + INSET; + height += border * 2; + if ((style & SWT.V_SCROLL) != 0) { + width += OS.GetSystemMetrics (OS.SM_CXVSCROLL); + } + if ((style & SWT.H_SCROLL) != 0) { + height += OS.GetSystemMetrics (OS.SM_CYHSCROLL); + } + return new Point (width, height); +} + +int defaultBackground () { + return OS.GetSysColor (OS.COLOR_WINDOW); +} + +/** + * 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); + if (indices.length == 0) return; + if ((style & SWT.SINGLE) != 0) { + int oldIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0); + if (oldIndex == OS.LB_ERR) return; + for (int i=0; i<indices.length; i++) { + if (oldIndex == indices [i]) { + OS.SendMessage (handle, OS.LB_SETCURSEL, -1, 0); + return; + } + } + return; + } + for (int i=0; i<indices.length; i++) { + int index = indices [i]; + if (index != -1) { + OS.SendMessage (handle, OS.LB_SETSEL, 0, index); + } + } +} + +/** + * 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 (index == -1) return; + if ((style & SWT.SINGLE) != 0) { + int oldIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0); + if (oldIndex == OS.LB_ERR) return; + if (oldIndex == index) OS.SendMessage (handle, OS.LB_SETCURSEL, -1, 0); + return; + } + OS.SendMessage (handle, OS.LB_SETSEL, 0, index); +} + +/** + * 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 ((style & SWT.SINGLE) != 0) { + int oldIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0); + if (oldIndex == OS.LB_ERR) return; + if (start <= oldIndex && oldIndex <= end) { + OS.SendMessage (handle, OS.LB_SETCURSEL, -1, 0); + } + return; + } + /* + * Ensure that at least one item is contained in + * the range from start to end. Note that when + * start = end, LB_SELITEMRANGEEX deselects the + * item. + */ + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (start < 0 && end < 0) return; + if (start >= count && end >= count) return; + start = Math.min (count - 1, Math.max (0, start)); + end = Math.min (count - 1, Math.max (0, end)); + OS.SendMessage (handle, OS.LB_SELITEMRANGEEX, end, start); +} + +/** + * 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 (); + if ((style & SWT.SINGLE) != 0) { + OS.SendMessage (handle, OS.LB_SETCURSEL, -1, 0); + } else { + OS.SendMessage (handle, OS.LB_SETSEL, 0, -1); + } +} + +/** + * 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 (); + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCARETINDEX, 0, 0); + if (result == 0) { + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (count == 0) return -1; + } + return result; +} + +/** + * 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 (); + int length = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTEXTLEN, index, 0); + if (length != OS.LB_ERR) { + TCHAR buffer = new TCHAR (getCodePage (), length + 1); + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTEXT, index, buffer); + if (result != OS.LB_ERR) return buffer.toString (0, length); + } + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (0 <= index && index < count) error (SWT.ERROR_CANNOT_GET_ITEM); + error (SWT.ERROR_INVALID_RANGE); + return ""; +} + +/** + * 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 (); + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (result == OS.LB_ERR) error (SWT.ERROR_CANNOT_GET_COUNT); + return result; +} + +/** + * 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 (); + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETITEMHEIGHT, 0, 0); + if (result == OS.LB_ERR) error (SWT.ERROR_CANNOT_GET_ITEM_HEIGHT); + return result; +} + +/** + * 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 (); + int count = getItemCount (); + String [] result = new String [count]; + for (int i=0; i<count; i++) result [i] = getItem (i); + 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 (); + int [] indices = getSelectionIndices (); + String [] result = new String [indices.length]; + for (int i=0; i<indices.length; i++) { + result [i] = getItem (indices [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 (); + if ((style & SWT.SINGLE) != 0) { + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0); + if (result == OS.LB_ERR) return 0; + return 1; + } + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETSELCOUNT, 0, 0); + if (result == OS.LB_ERR) error (SWT.ERROR_CANNOT_GET_COUNT); + return result; +} + +/** + * 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 (); + if ((style & SWT.SINGLE) != 0) { + return (int)/*64*/OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0); + } + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETSELCOUNT, 0, 0); + if (count == OS.LB_ERR) error (SWT.ERROR_CANNOT_GET_SELECTION); + if (count == 0) return -1; + int index = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCARETINDEX, 0, 0); + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETSEL, index, 0); + if (result == OS.LB_ERR) error (SWT.ERROR_CANNOT_GET_SELECTION); + if (result != 0) return index; + int [] buffer = new int[1]; + result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETSELITEMS, 1, buffer); + if (result != 1) error (SWT.ERROR_CANNOT_GET_SELECTION); + return buffer [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 (); + if ((style & SWT.SINGLE) != 0) { + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0); + if (result == OS.LB_ERR) return new int [0]; + return new int [] {result}; + } + int length = (int)/*64*/OS.SendMessage (handle, OS.LB_GETSELCOUNT, 0, 0); + if (length == OS.LB_ERR) error (SWT.ERROR_CANNOT_GET_SELECTION); + int [] indices = new int [length]; + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETSELITEMS, length, indices); + if (result != length) error (SWT.ERROR_CANNOT_GET_SELECTION); + return indices; +} + +/** + * 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 (); + return (int)/*64*/OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0); +} + +/** + * 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 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 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); + + /* + * Bug in Windows. For some reason, LB_FINDSTRINGEXACT + * will not find empty strings even though it is legal + * to insert an empty string into a list. The fix is + * to search the list, an item at a time. + */ + if (string.length () == 0) { + int count = getItemCount (); + for (int i=start; i<count; i++) { + if (string.equals (getItem (i))) return i; + } + return -1; + } + + /* Use LB_FINDSTRINGEXACT to search for the item */ + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (!(0 <= start && start < count)) return -1; + int index = start - 1, last; + TCHAR buffer = new TCHAR (getCodePage (), string, true); + do { + index = (int)/*64*/OS.SendMessage (handle, OS.LB_FINDSTRINGEXACT, last = index, buffer); + if (index == OS.LB_ERR || index <= last) return -1; + } while (!string.equals (getItem (index))); + return index; +} + +/** + * 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 (); + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETSEL, index, 0); + return (result != 0) && (result != OS.LB_ERR); +} + +/** + * 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 = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (!(0 <= start && start <= end && end < count)) { + error (SWT.ERROR_INVALID_RANGE); + } + int topIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0); + RECT rect = null; + int /*long*/ hDC = 0, oldFont = 0, newFont = 0; + int newWidth = 0; + if ((style & SWT.H_SCROLL) != 0) { + rect = new RECT (); + hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + } + int cp = getCodePage (); + int i = 0, topCount = 0, last = -1; + while (i < newIndices.length) { + int index = newIndices [i]; + if (index != last) { + TCHAR buffer = null; + if ((style & SWT.H_SCROLL) != 0) { + int length = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTEXTLEN, index, 0); + if (length == OS.LB_ERR) break; + buffer = new TCHAR (cp, length + 1); + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTEXT, index, buffer); + if (result == OS.LB_ERR) break; + } + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_DELETESTRING, index, 0); + if (result == OS.LB_ERR) break; + if ((style & SWT.H_SCROLL) != 0) { + int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX; + OS.DrawText (hDC, buffer, -1, rect, flags); + newWidth = Math.max (newWidth, rect.right - rect.left); + } + if (index < topIndex) topCount++; + last = index; + } + i++; + } + if ((style & SWT.H_SCROLL) != 0) { + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + setScrollWidth (newWidth, false); + } + if (topCount > 0) { + topIndex -= topCount; + OS.SendMessage (handle, OS.LB_SETTOPINDEX, topIndex, 0); + } + if (i < newIndices.length) error (SWT.ERROR_ITEM_NOT_REMOVED); +} + +/** + * 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 (); + TCHAR buffer = null; + if ((style & SWT.H_SCROLL) != 0) { + int length = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTEXTLEN, index, 0); + if (length == OS.LB_ERR) { + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (0 <= index && index < count) error (SWT.ERROR_ITEM_NOT_REMOVED); + error (SWT.ERROR_INVALID_RANGE); + } + buffer = new TCHAR (getCodePage (), length + 1); + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTEXT, index, buffer); + if (result == OS.LB_ERR) { + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (0 <= index && index < count) error (SWT.ERROR_ITEM_NOT_REMOVED); + error (SWT.ERROR_INVALID_RANGE); + } + } + int topIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0); + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_DELETESTRING, index, 0); + if (result == OS.LB_ERR) { + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (0 <= index && index < count) error (SWT.ERROR_ITEM_NOT_REMOVED); + error (SWT.ERROR_INVALID_RANGE); + } + if ((style & SWT.H_SCROLL) != 0) setScrollWidth (buffer, false); + if (index < topIndex) { + OS.SendMessage (handle, OS.LB_SETTOPINDEX, topIndex - 1, 0); + } +} + +/** + * 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; + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (!(0 <= start && start <= end && end < count)) { + error (SWT.ERROR_INVALID_RANGE); + } + if (start == 0 && end == count - 1) { + removeAll (); + return; + } + int topIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0); + RECT rect = null; + int /*long*/ hDC = 0, oldFont = 0, newFont = 0; + int newWidth = 0; + if ((style & SWT.H_SCROLL) != 0) { + rect = new RECT (); + hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + } + int cp = getCodePage (); + int index = start; + int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX; + while (index <= end) { + TCHAR buffer = null; + if ((style & SWT.H_SCROLL) != 0) { + int length = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTEXTLEN, start, 0); + if (length == OS.LB_ERR) break; + buffer = new TCHAR (cp, length + 1); + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTEXT, start, buffer); + if (result == OS.LB_ERR) break; + } + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_DELETESTRING, start, 0); + if (result == OS.LB_ERR) break; + if ((style & SWT.H_SCROLL) != 0) { + OS.DrawText (hDC, buffer, -1, rect, flags); + newWidth = Math.max (newWidth, rect.right - rect.left); + } + index++; + } + if ((style & SWT.H_SCROLL) != 0) { + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + setScrollWidth (newWidth, false); + } + if (end < topIndex) { + topIndex -= end - start + 1; + OS.SendMessage (handle, OS.LB_SETTOPINDEX, topIndex, 0); + } + if (index <= end) error (SWT.ERROR_ITEM_NOT_REMOVED); +} + +/** + * 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. + * <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 (); + OS.SendMessage (handle, OS.LB_RESETCONTENT, 0, 0); + if ((style & SWT.H_SCROLL) != 0) { + OS.SendMessage (handle, OS.LB_SETHORIZONTALEXTENT, 0, 0); + } +} + +/** + * 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 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; + select (indices, false); +} + +void select (int [] indices, boolean scroll) { + int i = 0; + while (i < indices.length) { + int index = indices [i]; + if (index != -1) { + select (index, false); + } + i++; + } + if (scroll) showSelection (); +} + +/** + * 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 (); + select (index, false); +} + +void select (int index, boolean scroll) { + if (index < 0) return; + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (index >= count) return; + if (scroll) { + if ((style & SWT.SINGLE) != 0) { + OS.SendMessage (handle, OS.LB_SETCURSEL, index, 0); + } else { + OS.SendMessage (handle, OS.LB_SETSEL, 1, index); + } + return; + } + int topIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0); + RECT itemRect = new RECT (), selectedRect = null; + OS.SendMessage (handle, OS.LB_GETITEMRECT, index, itemRect); + boolean redraw = getDrawing () && OS.IsWindowVisible (handle); + if (redraw) { + OS.UpdateWindow (handle); + OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); + } + int focusIndex = -1; + if ((style & SWT.SINGLE) != 0) { + int oldIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0); + if (oldIndex != -1) { + selectedRect = new RECT (); + OS.SendMessage (handle, OS.LB_GETITEMRECT, oldIndex, selectedRect); + } + OS.SendMessage (handle, OS.LB_SETCURSEL, index, 0); + } else { + focusIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCARETINDEX, 0, 0); + OS.SendMessage (handle, OS.LB_SETSEL, 1, index); + } + if ((style & SWT.MULTI) != 0) { + if (focusIndex != -1) { + OS.SendMessage (handle, OS.LB_SETCARETINDEX, focusIndex, 0); + } + } + OS.SendMessage (handle, OS.LB_SETTOPINDEX, topIndex, 0); + if (redraw) { + OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0); + OS.ValidateRect (handle, null); + OS.InvalidateRect (handle, itemRect, true); + if (selectedRect != null) { + OS.InvalidateRect (handle, selectedRect, true); + } + } +} + +/** + * 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; + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (count == 0 || start >= count) return; + start = Math.max (0, start); + end = Math.min (end, count - 1); + if ((style & SWT.SINGLE) != 0) { + select (start, false); + } else { + select (start, end, false); + } +} + +void select (int start, int end, boolean scroll) { + /* + * Note that when start = end, LB_SELITEMRANGEEX + * deselects the item. + */ + if (start == end) { + select (start, scroll); + return; + } + OS.SendMessage (handle, OS.LB_SELITEMRANGEEX, start, end); + if (scroll) showSelection (); +} + +/** + * 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; + OS.SendMessage (handle, OS.LB_SETSEL, 1, -1); +} + +void setFocusIndex (int index) { +// checkWidget (); + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (!(0 <= index && index < count)) return; + OS.SendMessage (handle, OS.LB_SETCARETINDEX, index, 0); +} + +public void setFont (Font font) { + checkWidget (); + super.setFont (font); + if ((style & SWT.H_SCROLL) != 0) 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); + int topIndex = getTopIndex (); + boolean isSelected = isSelected (index); + remove (index); + add (string, index); + if (isSelected) select (index, false); + setTopIndex (topIndex); +} + +/** + * 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); + } + int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC); + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, ListProc); + boolean redraw = getDrawing () && OS.IsWindowVisible (handle); + if (redraw) { + OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); + } + RECT rect = null; + int /*long*/ hDC = 0, oldFont = 0, newFont = 0; + int newWidth = 0; + if ((style & SWT.H_SCROLL) != 0) { + rect = new RECT (); + hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + OS.SendMessage (handle, OS.LB_SETHORIZONTALEXTENT, 0, 0); + } + int length = items.length; + OS.SendMessage (handle, OS.LB_RESETCONTENT, 0, 0); + OS.SendMessage (handle, OS.LB_INITSTORAGE, length, length * 32); + int index = 0; + int cp = getCodePage (); + while (index < length) { + String string = items [index]; + TCHAR buffer = new TCHAR (cp, string, true); + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_ADDSTRING, 0, buffer); + if (result == OS.LB_ERR || result == OS.LB_ERRSPACE) break; + if ((style & SWT.H_SCROLL) != 0) { + int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX; + OS.DrawText (hDC, buffer, -1, rect, flags); + newWidth = Math.max (newWidth, rect.right - rect.left); + } + index++; + } + if ((style & SWT.H_SCROLL) != 0) { + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + OS.SendMessage (handle, OS.LB_SETHORIZONTALEXTENT, newWidth + INSET, 0); + } + if (redraw) { + OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0); + /* + * This code is intentionally commented. The window proc + * for the list box implements WM_SETREDRAW to invalidate + * and erase the widget. This is undocumented behavior. + * The commented code below shows what is actually happening + * and reminds us that we are relying on this undocumented + * behavior. + */ +// int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE; +// OS.RedrawWindow (handle, null, 0, flags); + } + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); + if (index < items.length) error (SWT.ERROR_ITEM_NOT_ADDED); +} + +void setScrollWidth () { + int newWidth = 0; + RECT rect = new RECT (); + int /*long*/ newFont, oldFont = 0; + int /*long*/ hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + int cp = getCodePage (); + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX; + for (int i=0; i<count; i++) { + int length = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTEXTLEN, i, 0); + if (length != OS.LB_ERR) { + TCHAR buffer = new TCHAR (cp, length + 1); + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTEXT, i, buffer); + if (result != OS.LB_ERR) { + OS.DrawText (hDC, buffer, -1, rect, flags); + newWidth = Math.max (newWidth, rect.right - rect.left); + } + } + } + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + OS.SendMessage (handle, OS.LB_SETHORIZONTALEXTENT, newWidth + INSET, 0); +} + +void setScrollWidth (TCHAR buffer, boolean grow) { + RECT rect = new RECT (); + int /*long*/ newFont, oldFont = 0; + int /*long*/ hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX; + OS.DrawText (hDC, buffer, -1, rect, flags); + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + setScrollWidth (rect.right - rect.left, grow); +} + +void setScrollWidth (int newWidth, boolean grow) { + newWidth += INSET; + int width = (int)/*64*/OS.SendMessage (handle, OS.LB_GETHORIZONTALEXTENT, 0, 0); + if (grow) { + if (newWidth <= width) return; + OS.SendMessage (handle, OS.LB_SETHORIZONTALEXTENT, newWidth, 0); + } else { + if (newWidth < width) return; + setScrollWidth (); + } +} + +/** + * 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; + select (indices, true); + if ((style & SWT.MULTI) != 0) { + int focusIndex = indices [0]; + if (focusIndex >= 0) setFocusIndex (focusIndex); + } +} + +/** + * 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 focusIndex = -1; + for (int i=length-1; i>=0; --i) { + String string = items [i]; + int index = 0; + if (string != null) { + int localFocus = -1; + while ((index = indexOf (string, index)) != -1) { + if (localFocus == -1) localFocus = index; + select (index, false); + if ((style & SWT.SINGLE) != 0 && isSelected (index)) { + showSelection (); + return; + } + index++; + } + if (localFocus != -1) focusIndex = localFocus; + } + } + if ((style & SWT.MULTI) != 0) { + if (focusIndex >= 0) setFocusIndex (focusIndex); + } +} + +/** + * 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 (); + select (index, true); + if ((style & SWT.MULTI) != 0) { + if (index >= 0) setFocusIndex (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; + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (count == 0 || start >= count) return; + start = Math.max (0, start); + end = Math.min (end, count - 1); + if ((style & SWT.SINGLE) != 0) { + select (start, true); + } else { + select (start, end, true); + setFocusIndex (start); + } +} + +/** + * 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 (); + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_SETTOPINDEX, index, 0); + if (result == OS.LB_ERR) { + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + index = Math.min (count - 1, Math.max (0, index)); + OS.SendMessage (handle, OS.LB_SETTOPINDEX, index, 0); + } +} + +/** + * 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; + if ((style & SWT.SINGLE) != 0) { + index = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0); + } else { + int [] indices = new int [1]; + int result = (int)/*64*/OS.SendMessage (handle, OS.LB_GETSELITEMS, 1, indices); + index = indices [0]; + if (result != 1) index = -1; + } + if (index == -1) return; + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (count == 0) return; + int height = (int)/*64*/OS.SendMessage (handle, OS.LB_GETITEMHEIGHT, 0, 0); + forceResize (); + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + int topIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0); + int visibleCount = Math.max (rect.bottom / height, 1); + int bottomIndex = Math.min (topIndex + visibleCount, count) - 1; + if (topIndex <= index && index <= bottomIndex) return; + int newTop = Math.min (Math.max (index - (visibleCount / 2), 0), count - 1); + OS.SendMessage (handle, OS.LB_SETTOPINDEX, newTop, 0); +} + +int widgetStyle () { + int bits = super.widgetStyle () | OS.LBS_NOTIFY | OS.LBS_NOINTEGRALHEIGHT; + if ((style & SWT.SINGLE) != 0) return bits; + if ((style & SWT.MULTI) != 0) { + if ((style & SWT.SIMPLE) != 0) return bits | OS.LBS_MULTIPLESEL; + return bits | OS.LBS_EXTENDEDSEL; + } + return bits; +} + +TCHAR windowClass () { + return ListClass; +} + +int /*long*/ windowProc () { + return ListProc; +} + +LRESULT WM_CHAR (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_CHAR (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. The Windows list box does not implement + * the control key interface for multi-select list boxes, making + * it inaccessible from the keyboard. The fix is to implement + * the key processing. + */ + if (OS.GetKeyState (OS.VK_CONTROL) < 0 && OS.GetKeyState (OS.VK_SHIFT) >= 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.LBS_EXTENDEDSEL) != 0) { + switch ((int)/*64*/wParam) { + case OS.VK_SPACE: { + int index = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCARETINDEX, 0, 0); + int code = (int)/*64*/OS.SendMessage (handle, OS.LB_GETSEL, index, 0); + if (code == OS.LB_ERR) break; + OS.SendMessage (handle, OS.LB_SETSEL, code != 0 ? 0 : 1, index); + OS.SendMessage (handle, OS.LB_SETANCHORINDEX, index, 0); + postEvent (SWT.Selection); + return LRESULT.ZERO; + } + } + } + } + return result; +} + +LRESULT WM_KEYDOWN (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_KEYDOWN (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. The Windows list box does not implement + * the control key interface for multi-select list boxes, making + * it inaccessible from the keyboard. The fix is to implement + * the key processing. + */ + if (OS.GetKeyState (OS.VK_CONTROL) < 0 && OS.GetKeyState (OS.VK_SHIFT) >= 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.LBS_EXTENDEDSEL) != 0) { + int newIndex = -1; + switch ((int)/*64*/wParam) { + case OS.VK_SPACE: { + /* + * Ensure that the window proc does not process VK_SPACE + * so that it can be handled in WM_CHAR. This allows the + * application to cancel an operation that is normally + * performed in WM_KEYDOWN from WM_CHAR. + */ + return LRESULT.ZERO; + } + case OS.VK_UP: + case OS.VK_DOWN: { + int oldIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCARETINDEX, 0, 0); + newIndex = Math.max (0, oldIndex + (((int)/*64*/wParam) == OS.VK_UP ? -1 : 1)); + break; + } + case OS.VK_PRIOR: { + int topIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0); + int oldIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCARETINDEX, 0, 0); + if (oldIndex != topIndex) { + newIndex = topIndex; + } else { + forceResize (); + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + int itemHeight = (int)/*64*/OS.SendMessage (handle, OS.LB_GETITEMHEIGHT, 0, 0); + int pageSize = Math.max (2, (rect.bottom / itemHeight)); + newIndex = Math.max (0, topIndex - (pageSize - 1)); + } + break; + } + case OS.VK_NEXT: { + int topIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0); + int oldIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCARETINDEX, 0, 0); + forceResize (); + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + int itemHeight = (int)/*64*/OS.SendMessage (handle, OS.LB_GETITEMHEIGHT, 0, 0); + int pageSize = Math.max (2, (rect.bottom / itemHeight)); + int bottomIndex = topIndex + pageSize - 1; + if (oldIndex != bottomIndex) { + newIndex = bottomIndex; + } else { + newIndex = bottomIndex + pageSize - 1; + } + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (count != OS.LB_ERR) newIndex = Math.min (count - 1, newIndex); + break; + } + case OS.VK_HOME: { + newIndex = 0; + break; + } + case OS.VK_END: { + int count = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0); + if (count == OS.LB_ERR) break; + newIndex = count - 1; + break; + } + } + if (newIndex != -1) { + /* + * Feature in Windows. When the user changes focus using + * the keyboard, the focus indicator does not draw. The + * fix is to update the UI state for the control whenever + * the focus indicator changes as a result of something + * the user types. + */ + int uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + if ((uiState & OS.UISF_HIDEFOCUS) != 0) { + OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0); + /* + * Bug in Windows. When the WM_CHANGEUISTATE is used + * to update the UI state for a list that has been + * selected using Shift+Arrow, the focus indicator + * has pixel corruption. The fix is to redraw the + * control. + */ + RECT itemRect = new RECT (); + int oldIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETCARETINDEX, 0, 0); + OS.SendMessage (handle, OS.LB_GETITEMRECT, oldIndex, itemRect); + OS.InvalidateRect (handle, itemRect, true); + } + OS.SendMessage (handle, OS.LB_SETCARETINDEX, newIndex, 0); + return LRESULT.ZERO; + } + } + } + return result; +} + +LRESULT WM_SETREDRAW (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETREDRAW (wParam, lParam); + if (result != null) return result; + /* + * Bug in Windows. When WM_SETREDRAW is used to turn off + * redraw for a list, table or tree, the background of the + * control is drawn. The fix is to call DefWindowProc(), + * which stops all graphics output to the control. + */ + OS.DefWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam); + return result; +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Bug in Windows. If the top index is changed while the + * list is being resized, Windows does not redraw properly + * when their is white space at the bottom of the control. + * The fix is to detect when the top index has changed and + * redraw the control. + * + * Bug in Windows. If the receiver is scrolled horizontally + * and is resized, the list does not redraw properly. The fix + * is to redraw the control when the horizontal scroll bar is + * not at the beginning. + */ + int oldIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0); + LRESULT result = super.WM_SIZE (wParam, lParam); + if (!isDisposed ()) { + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_POS; + if (OS.GetScrollInfo (handle, OS.SB_HORZ, info)) { + if (info.nPos != 0) OS.InvalidateRect (handle, null, true); + } + int newIndex = (int)/*64*/OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0); + if (oldIndex != newIndex) OS.InvalidateRect (handle, null, true); + } + return result; +} + +LRESULT wmCommandChild (int /*long*/ wParam, int /*long*/ lParam) { + int code = OS.HIWORD (wParam); + switch (code) { + case OS.LBN_SELCHANGE: + postEvent (SWT.Selection); + break; + case OS.LBN_DBLCLK: + postEvent (SWT.DefaultSelection); + break; + } + return super.wmCommandChild (wParam, lParam); +} + + + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Menu.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Menu.java new file mode 100755 index 0000000000..183a593045 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Menu.java @@ -0,0 +1,1566 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * 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> + */ + public int /*long*/ handle; + + int x, y; + int /*long*/ hBrush, hwndCB; + int id0, id1; + int foreground = -1, background = -1; + Image backgroundImage; + boolean hasLocation; + MenuItem cascade; + Decorations parent; + ImageList imageList; + + /* Resource ID for SHMENUBARINFO */ + static final int ID_PPC = 100; + + /* SmartPhone SoftKeyBar resource ids */ + static final int ID_SPMM = 102; + static final int ID_SPBM = 103; + static final int ID_SPMB = 104; + static final int ID_SPBB = 105; + static final int ID_SPSOFTKEY0 = 106; + static final int ID_SPSOFTKEY1 = 107; + +/** + * 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) { + this (parent, checkStyle (style), 0); +} + +/** + * 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); +} + +Menu (Decorations parent, int style, int /*long*/ handle) { + super (parent, checkStyle (style)); + this.parent = parent; + this.handle = handle; + /* + * Bug in IBM JVM 1.3.1. For some reason, when the checkOrientation() is + * called from createWidget(), the JVM issues this error: + * + * JVM Exception 0x2 (subcode 0x0) occurred in thread "main" (TID:0x9F19D8) + * + * In addition, on Windows XP, a dialog appears with following error message, + * indicating that the problem may be in the JIT: + * + * AppName: java.exe AppVer: 0.0.0.0 ModName: jitc.dll + * ModVer: 0.0.0.0 Offset: 000b6912 + * + * The fix is to call checkOrientation() from here. + */ + checkOrientation (parent); + createWidget (); +} + +void _setVisible (boolean visible) { + if ((style & (SWT.BAR | SWT.DROP_DOWN)) != 0) return; + int /*long*/ hwndParent = parent.handle; + if (visible) { + int flags = OS.TPM_LEFTBUTTON; + if (OS.GetKeyState (OS.VK_LBUTTON) >= 0) flags |= OS.TPM_RIGHTBUTTON; + if ((style & SWT.RIGHT_TO_LEFT) != 0) flags |= OS.TPM_RIGHTALIGN; + if ((parent.style & SWT.MIRRORED) != 0) { + flags &= ~OS.TPM_RIGHTALIGN; + if ((style & SWT.LEFT_TO_RIGHT) != 0) flags |= OS.TPM_RIGHTALIGN; + } + int nX = x, nY = y; + if (!hasLocation) { + int pos = OS.GetMessagePos (); + nX = OS.GET_X_LPARAM (pos); + nY = OS.GET_Y_LPARAM (pos); + } + /* + * Feature in Windows. It is legal use TrackPopupMenu() + * to display an empty menu as long as menu items are added + * inside of WM_INITPOPUPMENU. If no items are added, then + * TrackPopupMenu() fails and does not send an indication + * that the menu has been closed. This is not strictly a + * bug but leads to unwanted behavior when application code + * assumes that every WM_INITPOPUPMENU will eventually result + * in a WM_MENUSELECT, wParam=MAKEWPARAM (0, 0xFFFF), lParam=0 to + * indicate that the menu has been closed. The fix is to detect + * the case when TrackPopupMenu() fails and the number of items in + * the menu is zero and issue a fake WM_MENUSELECT. + */ + boolean success = OS.TrackPopupMenu (handle, flags, nX, nY, 0, hwndParent, null); + if (!success && GetMenuItemCount (handle) == 0) { + OS.SendMessage (hwndParent, OS.WM_MENUSELECT, OS.MAKEWPARAM (0, 0xFFFF), 0); + } + } else { + OS.SendMessage (hwndParent, OS.WM_CANCELMODE, 0, 0); + } +} + +/** + * 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); +} + +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 createHandle () { + if (handle != 0) return; + if ((style & SWT.BAR) != 0) { + if (OS.IsPPC) { + int /*long*/ hwndShell = parent.handle; + SHMENUBARINFO mbi = new SHMENUBARINFO (); + mbi.cbSize = SHMENUBARINFO.sizeof; + mbi.hwndParent = hwndShell; + mbi.dwFlags = OS.SHCMBF_HIDDEN; + mbi.nToolBarId = ID_PPC; + mbi.hInstRes = OS.GetLibraryHandle (); + boolean success = OS.SHCreateMenuBar (mbi); + hwndCB = mbi.hwndMB; + if (!success) error (SWT.ERROR_NO_HANDLES); + /* Remove the item from the resource file */ + OS.SendMessage (hwndCB, OS.TB_DELETEBUTTON, 0, 0); + return; + } + /* + * Note in WinCE SmartPhone. The SoftBar contains only 2 items. + * An item can either be a menu or a button. + * SWT.BAR: creates a SoftBar with 2 menus + * SWT.BAR | SWT.BUTTON1: creates a SoftBar with 1 button + * for button1, and a menu for button2 + * SWT.BAR | SWT.BUTTON1 | SWT.BUTTON2: creates a SoftBar with + * 2 buttons + */ + if (OS.IsSP) { + /* Determine type of menubar */ + int nToolBarId; + if ((style & SWT.BUTTON1) != 0) { + nToolBarId = ((style & SWT.BUTTON2) != 0) ? ID_SPBB : ID_SPBM; + } else { + nToolBarId = ((style & SWT.BUTTON2) != 0) ? ID_SPMB : ID_SPMM; + } + + /* Create SHMENUBAR */ + SHMENUBARINFO mbi = new SHMENUBARINFO (); + mbi.cbSize = SHMENUBARINFO.sizeof; + mbi.hwndParent = parent.handle; + mbi.dwFlags = OS.SHCMBF_HIDDEN; + mbi.nToolBarId = nToolBarId; /* as defined in .rc file */ + mbi.hInstRes = OS.GetLibraryHandle (); + if (!OS.SHCreateMenuBar (mbi)) error (SWT.ERROR_NO_HANDLES); + hwndCB = mbi.hwndMB; + + /* + * Feature on WinCE SmartPhone. The SHCMBF_HIDDEN flag causes the + * SHMENUBAR to not be drawn. However the keyboard events still go + * through it. The workaround is to also hide the SHMENUBAR with + * ShowWindow (). + */ + OS.ShowWindow (hwndCB, OS.SW_HIDE); + + TBBUTTONINFO info = new TBBUTTONINFO (); + info.cbSize = TBBUTTONINFO.sizeof; + info.dwMask = OS.TBIF_COMMAND; + MenuItem item; + + /* Set first item */ + if (nToolBarId == ID_SPMM || nToolBarId == ID_SPMB) { + int /*long*/ hMenu = OS.SendMessage (hwndCB, OS.SHCMBM_GETSUBMENU, 0, ID_SPSOFTKEY0); + /* Remove the item from the resource file */ + OS.RemoveMenu (hMenu, 0, OS.MF_BYPOSITION); + Menu menu = new Menu (parent, SWT.DROP_DOWN, hMenu); + item = new MenuItem (this, menu, SWT.CASCADE, 0); + } else { + item = new MenuItem (this, null, SWT.PUSH, 0); + } + info.idCommand = id0 = item.id; + OS.SendMessage (hwndCB, OS.TB_SETBUTTONINFO, ID_SPSOFTKEY0, info); + + /* Set second item */ + if (nToolBarId == ID_SPMM || nToolBarId == ID_SPBM) { + int /*long*/ hMenu = OS.SendMessage (hwndCB, OS.SHCMBM_GETSUBMENU, 0, ID_SPSOFTKEY1); + OS.RemoveMenu (hMenu, 0, OS.MF_BYPOSITION); + Menu menu = new Menu (parent, SWT.DROP_DOWN, hMenu); + item = new MenuItem (this, menu, SWT.CASCADE, 1); + } else { + item = new MenuItem (this, null, SWT.PUSH, 1); + } + info.idCommand = id1 = item.id; + OS.SendMessage (hwndCB, OS.TB_SETBUTTONINFO, ID_SPSOFTKEY1, info); + + /* + * Override the Back key. For some reason, the owner of the menubar + * must be a Dialog or it won't receive the WM_HOTKEY message. As + * a result, Shell on WinCE SP must use the class Dialog. + */ + int dwMask = OS.SHMBOF_NODEFAULT | OS.SHMBOF_NOTIFY; + int /*long*/ lParam = OS.MAKELPARAM (dwMask, dwMask); + OS.SendMessage (hwndCB, OS.SHCMBM_OVERRIDEKEY, OS.VK_ESCAPE, lParam); + return; + } + handle = OS.CreateMenu (); + if (handle == 0) error (SWT.ERROR_NO_HANDLES); + if (OS.IsHPC) { + int /*long*/ hwndShell = parent.handle; + hwndCB = OS.CommandBar_Create (OS.GetModuleHandle (null), hwndShell, 1); + if (hwndCB == 0) error (SWT.ERROR_NO_HANDLES); + OS.CommandBar_Show (hwndCB, false); + OS.CommandBar_InsertMenubarEx (hwndCB, 0, handle, 0); + /* + * The command bar hosts the 'close' button when the window does not + * have a caption. + */ + if ((parent.style & SWT.CLOSE) != 0 && (parent.style & SWT.TITLE) == 0) { + OS.CommandBar_AddAdornments (hwndCB, 0, 0); + } + } + } else { + handle = OS.CreatePopupMenu (); + if (handle == 0) error (SWT.ERROR_NO_HANDLES); + } +} + +void createItem (MenuItem item, int index) { + int count = GetMenuItemCount (handle); + if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE); + display.addMenuItem (item); + boolean success = false; + if ((OS.IsPPC || OS.IsSP) && hwndCB != 0) { + if (OS.IsSP) return; + TBBUTTON lpButton = new TBBUTTON (); + lpButton.idCommand = item.id; + lpButton.fsStyle = (byte) OS.TBSTYLE_AUTOSIZE; + if ((item.style & SWT.CASCADE) != 0) lpButton.fsStyle |= OS.TBSTYLE_DROPDOWN | 0x80; + if ((item.style & SWT.SEPARATOR) != 0) lpButton.fsStyle = (byte) OS.BTNS_SEP; + lpButton.fsState = (byte) OS.TBSTATE_ENABLED; + lpButton.iBitmap = OS.I_IMAGENONE; + success = OS.SendMessage (hwndCB, OS.TB_INSERTBUTTON, index, lpButton) != 0; + } else { + if (OS.IsWinCE) { + int uFlags = OS.MF_BYPOSITION; + TCHAR lpNewItem = null; + if ((item.style & SWT.SEPARATOR) != 0) { + uFlags |= OS.MF_SEPARATOR; + } else { + lpNewItem = new TCHAR (0, " ", true); + } + success = OS.InsertMenu (handle, index, uFlags, item.id, lpNewItem); + if (success) { + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_DATA; + info.dwItemData = item.id; + success = OS.SetMenuItemInfo (handle, index, true, info); + } + } else { + /* + * Bug in Windows. For some reason, when InsertMenuItem() + * is used to insert an item without text, it is not possible + * to use SetMenuItemInfo() to set the text at a later time. + * The fix is to insert the item with some text. + * + * Feature in Windows. When an empty string is used instead + * of a space and InsertMenuItem() is used to set a submenu + * before setting text to a non-empty string, the menu item + * becomes unexpectedly disabled. The fix is to insert a + * space. + */ + int /*long*/ hHeap = OS.GetProcessHeap (); + TCHAR buffer = new TCHAR (0, " ", true); + int byteCount = buffer.length () * TCHAR.sizeof; + int /*long*/ pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (pszText, buffer, byteCount); + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_ID | OS.MIIM_TYPE | OS.MIIM_DATA; + info.wID = item.id; + info.dwItemData = item.id; + info.fType = item.widgetStyle (); + info.dwTypeData = pszText; + success = OS.InsertMenuItem (handle, index, true, info); + if (pszText != 0) OS.HeapFree (hHeap, 0, pszText); + } + } + if (!success) { + display.removeMenuItem (item); + error (SWT.ERROR_ITEM_NOT_ADDED); + } + redraw (); +} + +void createWidget () { + /* + * Bug in IBM JVM 1.3.1. For some reason, when the following code is called + * from this method, the JVM issues this error: + * + * JVM Exception 0x2 (subcode 0x0) occurred in thread "main" (TID:0x9F19D8) + * + * In addition, on Windows XP, a dialog appears with following error message, + * indicating that the problem may be in the JIT: + * + * AppName: java.exe AppVer: 0.0.0.0 ModName: jitc.dll + * ModVer: 0.0.0.0 Offset: 000b6912 + * + * The fix is to move the code to the caller of this method. + */ +// checkOrientation (parent); + createHandle (); + parent.addMenu (this); +} + +int defaultBackground () { + return OS.GetSysColor (OS.COLOR_MENU); +} + +int defaultForeground () { + return OS.GetSysColor (OS.COLOR_MENUTEXT); +} + +void destroyAccelerators () { + parent.destroyAccelerators (); +} + +void destroyItem (MenuItem item) { + if (OS.IsWinCE) { + if ((OS.IsPPC || OS.IsSP) && hwndCB != 0) { + if (OS.IsSP) { + redraw(); + return; + } + int index = (int)/*64*/OS.SendMessage (hwndCB, OS.TB_COMMANDTOINDEX, item.id, 0); + if (OS.SendMessage (hwndCB, OS.TB_DELETEBUTTON, index, 0) == 0) { + error (SWT.ERROR_ITEM_NOT_REMOVED); + } + int count = (int)/*64*/OS.SendMessage (hwndCB, OS.TB_BUTTONCOUNT, 0, 0); + if (count == 0) { + if (imageList != null) { + OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, 0); + display.releaseImageList (imageList); + imageList = null; + } + } + } else { + int index = 0; + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_DATA; + while (OS.GetMenuItemInfo (handle, index, true, info)) { + if (info.dwItemData == item.id) break; + index++; + } + if (info.dwItemData != item.id) { + error (SWT.ERROR_ITEM_NOT_REMOVED); + } + if (!OS.DeleteMenu (handle, index, OS.MF_BYPOSITION)) { + error (SWT.ERROR_ITEM_NOT_REMOVED); + } + } + } else { + if (!OS.DeleteMenu (handle, item.id, OS.MF_BYCOMMAND)) { + error (SWT.ERROR_ITEM_NOT_REMOVED); + } + } + redraw (); +} + +void destroyWidget () { + MenuItem cascade = this.cascade; + int /*long*/ hMenu = handle, hCB = hwndCB; + releaseHandle (); + if (OS.IsWinCE && hCB != 0) { + OS.CommandBar_Destroy (hCB); + } else { + if (cascade != null) { + if (!OS.IsSP) cascade.setMenu (null, true); + } else { + if (hMenu != 0) OS.DestroyMenu (hMenu); + } + } +} + +void fixMenus (Decorations newParent) { + MenuItem [] items = getItems (); + for (int i=0; i<items.length; i++) { + items [i].fixMenus (newParent); + } + parent.removeMenu (this); + newParent.addMenu (this); + this.parent = newParent; +} + +/** + * 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 3.3 + */ +/*public*/ Color getBackground () { + checkWidget (); + return Color.win32_new (display, background != -1 ? 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.3 + */ +/*public*/ Image getBackgroundImage () { + checkWidget (); + return backgroundImage; +} + +/** + * 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 menu or a shell. In this case, the + * location is relative to the display. + * <p> + * Note that the bounds of a menu or menu item are undefined when + * the menu is not visible. This is because most platforms compute + * the bounds of a menu dynamically just before it is displayed. + * </p> + * + * @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.1 + */ +/*public*/ Rectangle getBounds () { + checkWidget (); + if (OS.IsWinCE) return new Rectangle (0, 0, 0, 0); + if ((style & SWT.BAR) != 0) { + if (parent.menuBar != this) { + return new Rectangle (0, 0, 0, 0); + } + int /*long*/ hwndShell = parent.handle; + MENUBARINFO info = new MENUBARINFO (); + info.cbSize = MENUBARINFO.sizeof; + if (OS.GetMenuBarInfo (hwndShell, OS.OBJID_MENU, 0, info)) { + int width = info.right - info.left; + int height = info.bottom - info.top; + return new Rectangle (info.left, info.top, width, height); + } + } else { + int count = GetMenuItemCount (handle); + if (count != 0) { + RECT rect1 = new RECT (); + if (OS.GetMenuItemRect (0, handle, 0, rect1)) { + RECT rect2 = new RECT (); + if (OS.GetMenuItemRect (0, handle, count - 1, rect2)) { + int x = rect1.left - 2, y = rect1.top - 2; + int width = (rect2.right - rect2.left) + 4; + int height = (rect2.bottom - rect1.top) + 4; + return new Rectangle (x, y, width, height); + } + } + } + } + return new Rectangle (0, 0, 0, 0); +} + +/** + * 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 (); + if (OS.IsWinCE) return null; + int id = OS.GetMenuDefaultItem (handle, OS.MF_BYCOMMAND, OS.GMDI_USEDISABLED); + if (id == -1) return null; + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_ID; + if (OS.GetMenuItemInfo (handle, id, false, info)) { + return display.getMenuItem (info.wID); + } + return null; +} + +/** + * 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 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 Color.win32_new (display, foreground != -1 ? foreground : defaultForeground ()); +} + +/** + * 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 (); + int id = 0; + if ((OS.IsPPC || OS.IsSP) && hwndCB != 0) { + if (OS.IsPPC) { + TBBUTTON lpButton = new TBBUTTON (); + int /*long*/ result = OS.SendMessage (hwndCB, OS.TB_GETBUTTON, index, lpButton); + if (result == 0) error (SWT.ERROR_CANNOT_GET_ITEM); + id = lpButton.idCommand; + } + if (OS.IsSP) { + if (!(0 <= index && index <= 1)) error (SWT.ERROR_CANNOT_GET_ITEM); + id = index == 0 ? id0 : id1; + } + } else { + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_DATA; + if (!OS.GetMenuItemInfo (handle, index, true, info)) { + error (SWT.ERROR_INVALID_RANGE); + } + id = (int)/*64*/info.dwItemData; + } + return display.getMenuItem (id); +} + +/** + * 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 GetMenuItemCount (handle); +} + +/** + * 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 (); + if ((OS.IsPPC || OS.IsSP) && hwndCB != 0) { + if (OS.IsSP) { + MenuItem [] result = new MenuItem [2]; + result[0] = display.getMenuItem (id0); + result[1] = display.getMenuItem (id1); + return result; + } + int count = (int)/*64*/OS.SendMessage (hwndCB, OS.TB_BUTTONCOUNT, 0, 0); + TBBUTTON lpButton = new TBBUTTON (); + MenuItem [] result = new MenuItem [count]; + for (int i=0; i<count; i++) { + OS.SendMessage (hwndCB, OS.TB_GETBUTTON, i, lpButton); + result [i] = display.getMenuItem (lpButton.idCommand); + } + return result; + } + int index = 0, count = 0; + int length = OS.IsWinCE ? 4 : OS.GetMenuItemCount (handle); + MenuItem [] items = new MenuItem [length]; + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_DATA; + while (OS.GetMenuItemInfo (handle, index, true, info)) { + if (count == items.length) { + MenuItem [] newItems = new MenuItem [count + 4]; + System.arraycopy (items, 0, newItems, 0, count); + items = newItems; + } + MenuItem item = display.getMenuItem ((int)/*64*/info.dwItemData); + if (item != null) items [count++] = item; + index++; + } + if (count == items.length) return items; + MenuItem [] result = new MenuItem [count]; + System.arraycopy (items, 0, result, 0, count); + return result; +} + +int GetMenuItemCount (int /*long*/ handle) { + if (OS.IsWinCE) { + if ((OS.IsPPC || OS.IsSP) && hwndCB != 0) { + return OS.IsSP ? 2 : (int)/*64*/OS.SendMessage (hwndCB, OS.TB_BUTTONCOUNT, 0, 0); + } + int count = 0; + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + while (OS.GetMenuItemInfo (handle, count, true, info)) count++; + return count; + } + return OS.GetMenuItemCount (handle); +} + +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; + } + } + Shell shell = getShell (); + Menu menu = shell.activeMenu; + while (menu != null && menu != this) { + menu = menu.getParentMenu (); + } + return this == menu; +} + +int imageIndex (Image image) { + if (hwndCB == 0 || image == null) return OS.I_IMAGENONE; + if (imageList == null) { + Rectangle bounds = image.getBounds (); + imageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height); + int index = imageList.add (image); + int /*long*/ hImageList = imageList.getHandle (); + OS.SendMessage (hwndCB, OS.TB_SETIMAGELIST, 0, hImageList); + return index; + } + int index = imageList.indexOf (image); + if (index == -1) { + index = imageList.add (image); + } else { + imageList.put (index, image); + } + return index; +} + +/** + * 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); + if (item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + if (item.parent != this) return -1; + if ((OS.IsPPC || OS.IsSP) && hwndCB != 0) { + if (OS.IsPPC) { + return (int)/*64*/OS.SendMessage (hwndCB, OS.TB_COMMANDTOINDEX, item.id, 0); + } + if (OS.IsSP) { + if (item.id == id0) return 0; + if (item.id == id1) return 1; + return -1; + } + } + int index = 0; + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_DATA; + while (OS.GetMenuItemInfo (handle, index, true, info)) { + if (info.dwItemData == item.id) return index; + index++; + } + 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 redraw () { + if (!isVisible ()) return; + if ((style & SWT.BAR) != 0) { + display.addBar (this); + } else { + update (); + } +} + +void releaseHandle () { + super.releaseHandle (); + handle = hwndCB = 0; + cascade = null; +} + +void releaseChildren (boolean destroy) { + MenuItem [] items = getItems (); + for (int i=0; i<items.length; i++) { + MenuItem item = items [i]; + if (item != null && !item.isDisposed ()) { + if (OS.IsPPC && hwndCB != 0) { + item.dispose (); + } else { + item.release (false); + } + } + } + super.releaseChildren (destroy); +} + +void releaseParent () { + super.releaseParent (); + if ((style & SWT.BAR) != 0) { + display.removeBar (this); + if (this == parent.menuBar) { + parent.setMenuBar (null); + } + } else { + if ((style & SWT.POP_UP) != 0) { + display.removePopup (this); + } + } +} + +void releaseWidget () { + super.releaseWidget (); + backgroundImage = null; + if (hBrush == 0) OS.DeleteObject (hBrush); + hBrush = 0; + if (OS.IsPPC && hwndCB != 0) { + if (imageList != null) { + OS.SendMessage (hwndCB, OS.TB_SETIMAGELIST, 0, 0); + display.releaseToolImageList (imageList); + imageList = null; + } + } + if (parent != null) parent.removeMenu (this); + parent = 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 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. + * + * @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.3 + */ +/*public*/ void setBackground (Color color) { + checkWidget (); + int pixel = -1; + if (color != null) { + if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + pixel = color.handle; + } + if (pixel == background) return; + background = pixel; + updateBackground (); +} + +/** + * 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. + * + * @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.3 + */ +/*public*/ void setBackgroundImage (Image image) { + checkWidget (); + if (image != null) { + if (image.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + if (image.type != SWT.BITMAP) error (SWT.ERROR_INVALID_ARGUMENT); + } + if (backgroundImage == image) return; + backgroundImage = image; + updateBackground (); +} + +/** + * 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. + * + * @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.3 + */ +/*public*/ void setForeground (Color color) { + checkWidget (); + int pixel = -1; + if (color != null) { + if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + pixel = color.handle; + } + if (pixel == foreground) return; + foreground = pixel; + updateForeground (); +} + +/** + * 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 (); + int newID = -1; + if (item != null) { + if (item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + if (item.parent != this) return; + newID = item.id; + } + if (OS.IsWinCE) return; + int oldID = OS.GetMenuDefaultItem (handle, OS.MF_BYCOMMAND, OS.GMDI_USEDISABLED); + if (newID == oldID) return; + OS.SetMenuDefaultItem (handle, newID, OS.MF_BYCOMMAND); + redraw (); +} + +/** + * 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 (); + state &= ~DISABLED; + if (!enabled) state |= DISABLED; +} + +/** + * 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 (); + if ((style & (SWT.BAR | SWT.DROP_DOWN)) != 0) return; + 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) SWT.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); + _setVisible (false); + } +} + +void update () { + if (OS.IsPPC || OS.IsSP) return; + if (OS.IsHPC) { + /* + * Each time a menu has been modified, the command menu bar + * must be redrawn or it won't update properly. For example, + * a submenu will not drop down. + */ + Menu menuBar = parent.menuBar; + if (menuBar != null) { + Menu menu = this; + while (menu != null && menu != menuBar) { + menu = menu.getParentMenu (); + } + if (menu == menuBar) { + OS.CommandBar_DrawMenuBar (menuBar.hwndCB, 0); + OS.CommandBar_Show (menuBar.hwndCB, true); + } + } + return; + } + if (OS.IsWinCE) return; + if ((style & SWT.BAR) != 0) { + if (this == parent.menuBar) OS.DrawMenuBar (parent.handle); + return; + } + if (OS.WIN32_VERSION < OS.VERSION (4, 10)) { + return; + } + boolean hasCheck = false, hasImage = false; + MenuItem [] items = getItems (); + for (int i=0; i<items.length; i++) { + MenuItem item = items [i]; + if (item.image != null) { + if ((hasImage = true) && hasCheck) break; + } + if ((item.style & (SWT.CHECK | SWT.RADIO)) != 0) { + if ((hasCheck = true) && hasImage) break; + } + } + + /* + * Bug in Windows. If a menu contains items that have + * images and can be checked, Windows does not include + * the width of the image and the width of the check when + * computing the width of the menu. When the longest item + * does not have an image, the label and the accelerator + * text can overlap. The fix is to use SetMenuItemInfo() + * to indicate that all items have a bitmap and then include + * the width of the widest bitmap in WM_MEASURECHILD. + * + * NOTE: This work around causes problems on Windows 98. + * Under certain circumstances that have yet to be isolated, + * some menus can become huge and blank. For now, do not + * run the code on Windows 98. + * + * NOTE: This work around doesn't run on Vista because + * WM_MEASURECHILD and WM_DRAWITEM cause Vista to lose + * the menu theme. + */ + if (!OS.IsWin95) { + if (OS.WIN32_VERSION < OS.VERSION (6, 0)) { + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_BITMAP; + for (int i=0; i<items.length; i++) { + MenuItem item = items [i]; + if ((style & SWT.SEPARATOR) == 0) { + if (item.image == null || foreground != -1) { + info.hbmpItem = hasImage || foreground != -1 ? OS.HBMMENU_CALLBACK : 0; + OS.SetMenuItemInfo (handle, item.id, false, info); + } + } + } + } + } + + /* Update the menu to hide or show the space for bitmaps */ + MENUINFO lpcmi = new MENUINFO (); + lpcmi.cbSize = MENUINFO.sizeof; + lpcmi.fMask = OS.MIM_STYLE; + OS.GetMenuInfo (handle, lpcmi); + if (hasImage && !hasCheck) { + lpcmi.dwStyle |= OS.MNS_CHECKORBMP; + } else { + lpcmi.dwStyle &= ~OS.MNS_CHECKORBMP; + } + OS.SetMenuInfo (handle, lpcmi); +} + +void updateBackground () { + if (hBrush == 0) OS.DeleteObject (hBrush); + hBrush = 0; + if (backgroundImage != null) { + hBrush = OS.CreatePatternBrush (backgroundImage.handle); + } else { + if (background != -1) hBrush = OS.CreateSolidBrush (background); + } + MENUINFO lpcmi = new MENUINFO (); + lpcmi.cbSize = MENUINFO.sizeof; + lpcmi.fMask = OS.MIM_BACKGROUND; + lpcmi.hbrBack = hBrush; + OS.SetMenuInfo (handle, lpcmi); +} + +void updateForeground () { + if (OS.WIN32_VERSION < OS.VERSION (4, 10)) return; + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + int index = 0; + while (OS.GetMenuItemInfo (handle, index, true, info)) { + info.fMask = OS.MIIM_BITMAP; + info.hbmpItem = OS.HBMMENU_CALLBACK; + OS.SetMenuItemInfo (handle, index, true, info); + index++; + } + redraw (); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/MenuItem.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/MenuItem.java new file mode 100755 index 0000000000..3950d5cebd --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/MenuItem.java @@ -0,0 +1,1174 @@ +/******************************************************************************* + * 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.win32.*; +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 { + Menu parent, menu; + int /*long*/ hBitmap; + int id, accelerator; + /* + * Feature in Windows. On Windows 98, it is necessary + * to add 4 pixels to the width of the image or the image + * and text are too close. On other Windows platforms, + * this causes the text of the longest item to touch the + * accelerator text. The fix is to use smaller margins + * everywhere but on Windows 98. + */ + final static int MARGIN_WIDTH = OS.IsWin95 ? 2 : 1; + final static int MARGIN_HEIGHT = OS.IsWin95 ? 2 : 1; + +/** + * 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); +} + +MenuItem (Menu parent, Menu menu, int style, int index) { + super (parent, checkStyle (style)); + this.parent = parent; + this.menu = menu; + if (menu != null) menu.cascade = this; + display.addMenuItem (this); +} + +/** + * 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); +} + +void destroyWidget () { + parent.destroyItem (this); + releaseHandle (); +} + +boolean fillAccel (ACCEL accel) { + accel.cmd = accel.key = accel.fVirt = 0; + if (accelerator == 0 || !getEnabled ()) return false; + if ((accelerator & SWT.COMMAND) != 0) return false; + int fVirt = OS.FVIRTKEY; + int key = accelerator & SWT.KEY_MASK; + int vKey = Display.untranslateKey (key); + if (vKey != 0) { + key = vKey; + } else { + switch (key) { + /* + * Bug in Windows. For some reason, VkKeyScan + * fails to map ESC to VK_ESCAPE and DEL to + * VK_DELETE. The fix is to map these keys + * as a special case. + */ + case 27: key = OS.VK_ESCAPE; break; + case 127: key = OS.VK_DELETE; break; + default: { + key = Display.wcsToMbcs ((char) key); + if (key == 0) return false; + if (OS.IsWinCE) { + key = (int)/*64*/OS.CharUpper ((short) key); + } else { + vKey = OS.VkKeyScan ((short) key) & 0xFF; + if (vKey == -1) { + fVirt = 0; + } else { + key = vKey; + } + } + } + } + } + accel.key = (short) key; + accel.cmd = (short) id; + accel.fVirt = (byte) fVirt; + if ((accelerator & SWT.ALT) != 0) accel.fVirt |= OS.FALT; + if ((accelerator & SWT.SHIFT) != 0) accel.fVirt |= OS.FSHIFT; + if ((accelerator & SWT.CONTROL) != 0) accel.fVirt |= OS.FCONTROL; + return true; +} + +void fixMenus (Decorations newParent) { + if (menu != null) menu.fixMenus (newParent); +} + +/** + * 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 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> + * + * @since 3.1 + */ +/*public*/ Rectangle getBounds () { + checkWidget (); + if (OS.IsWinCE) return new Rectangle (0, 0, 0, 0); + int index = parent.indexOf (this); + if (index == -1) return new Rectangle (0, 0, 0, 0); + if ((parent.style & SWT.BAR) != 0) { + Decorations shell = parent.parent; + if (shell.menuBar != parent) { + return new Rectangle (0, 0, 0, 0); + } + int /*long*/ hwndShell = shell.handle; + MENUBARINFO info1 = new MENUBARINFO (); + info1.cbSize = MENUBARINFO.sizeof; + if (!OS.GetMenuBarInfo (hwndShell, OS.OBJID_MENU, 1, info1)) { + return new Rectangle (0, 0, 0, 0); + } + MENUBARINFO info2 = new MENUBARINFO (); + info2.cbSize = MENUBARINFO.sizeof; + if (!OS.GetMenuBarInfo (hwndShell, OS.OBJID_MENU, index + 1, info2)) { + return new Rectangle (0, 0, 0, 0); + } + int x = info2.left - info1.left; + int y = info2.top - info1.top; + int width = info2.right - info2.left; + int height = info2.bottom - info2.top; + return new Rectangle (x, y, width, height); + } else { + int /*long*/ hMenu = parent.handle; + RECT rect1 = new RECT (); + if (!OS.GetMenuItemRect (0, hMenu, 0, rect1)) { + return new Rectangle (0, 0, 0, 0); + } + RECT rect2 = new RECT (); + if (!OS.GetMenuItemRect (0, hMenu, index, rect2)) { + return new Rectangle (0, 0, 0, 0); + } + int x = rect2.left - rect1.left + 2; + int y = rect2.top - rect1.top + 2; + int width = rect2.right - rect2.left; + int height = rect2.bottom - rect2.top; + return new Rectangle (x, y, width, height); + } +} + +/** + * 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 (); + if ((OS.IsPPC || OS.IsSP) && parent.hwndCB != 0) { + int /*long*/ hwndCB = parent.hwndCB; + TBBUTTONINFO info = new TBBUTTONINFO (); + info.cbSize = TBBUTTONINFO.sizeof; + info.dwMask = OS.TBIF_STATE; + OS.SendMessage (hwndCB, OS.TB_GETBUTTONINFO, id, info); + return (info.fsState & OS.TBSTATE_ENABLED) != 0; + } + /* + * Feature in Windows. For some reason, when the menu item + * is a separator, GetMenuItemInfo() always indicates that + * the item is not enabled. The fix is to track the enabled + * state for separators. + */ + if ((style & SWT.SEPARATOR) != 0) { + return (state & DISABLED) == 0; + } + int /*long*/ hMenu = parent.handle; + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_STATE; + boolean success; + if (OS.IsWinCE) { + int index = parent.indexOf (this); + if (index == -1) error (SWT.ERROR_CANNOT_GET_ENABLED); + success = OS.GetMenuItemInfo (hMenu, index, true, info); + } else { + success = OS.GetMenuItemInfo (hMenu, id, false, info); + } + if (!success) error (SWT.ERROR_CANNOT_GET_ENABLED); + return (info.fState & (OS.MFS_DISABLED | OS.MFS_GRAYED)) == 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; + if ((OS.IsPPC || OS.IsSP) && parent.hwndCB != 0) return false; + int /*long*/ hMenu = parent.handle; + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_STATE; + boolean success = OS.GetMenuItemInfo (hMenu, id, false, info); + if (!success) error (SWT.ERROR_CANNOT_GET_SELECTION); + return (info.fState & OS.MFS_CHECKED) !=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 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 (); +} + +void releaseChildren (boolean destroy) { + if (menu != null) { + menu.release (false); + menu = null; + } + super.releaseChildren (destroy); +} + +void releaseHandle () { + super.releaseHandle (); + parent = null; + id = -1; +} + +void releaseParent () { + super.releaseParent (); + if (menu != null) menu.dispose (); + menu = null; +} + +void releaseWidget () { + super.releaseWidget (); + if (hBitmap != 0) OS.DeleteObject (hBitmap); + hBitmap = 0; + if (accelerator != 0) { + parent.destroyAccelerators (); + } + accelerator = 0; + display.removeMenuItem (this); +} + +/** + * 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); +} + +/** + * 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; + parent.destroyAccelerators (); +} + +/** + * 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 ((OS.IsPPC || OS.IsSP) && parent.hwndCB != 0) { + int /*long*/ hwndCB = parent.hwndCB; + TBBUTTONINFO info = new TBBUTTONINFO (); + info.cbSize = TBBUTTONINFO.sizeof; + info.dwMask = OS.TBIF_STATE; + OS.SendMessage (hwndCB, OS.TB_GETBUTTONINFO, id, info); + info.fsState &= ~OS.TBSTATE_ENABLED; + if (enabled) info.fsState |= OS.TBSTATE_ENABLED; + OS.SendMessage (hwndCB, OS.TB_SETBUTTONINFO, id, info); + } else { + /* + * Feature in Windows. For some reason, when the menu item + * is a separator, GetMenuItemInfo() always indicates that + * the item is not enabled. The fix is to track the enabled + * state for separators. + */ + if ((style & SWT.SEPARATOR) != 0) { + if (enabled) { + state &= ~DISABLED; + } else { + state |= DISABLED; + } + } + int /*long*/ hMenu = parent.handle; + if (OS.IsWinCE) { + int index = parent.indexOf (this); + if (index == -1) return; + int uEnable = OS.MF_BYPOSITION | (enabled ? OS.MF_ENABLED : OS.MF_GRAYED); + OS.EnableMenuItem (hMenu, index, uEnable); + } else { + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_STATE; + boolean success = OS.GetMenuItemInfo (hMenu, id, false, info); + if (!success) error (SWT.ERROR_CANNOT_SET_ENABLED); + int bits = OS.MFS_DISABLED | OS.MFS_GRAYED; + if (enabled) { + if ((info.fState & bits) == 0) return; + info.fState &= ~bits; + } else { + if ((info.fState & bits) == bits) return; + info.fState |= bits; + } + success = OS.SetMenuItemInfo (hMenu, id, false, info); + if (!success) { + /* + * Bug in Windows. For some reason SetMenuItemInfo(), + * returns a fail code when setting the enabled or + * selected state of a default item, but sets the + * state anyway. The fix is to ignore the error. + * + * NOTE: This only happens on Vista. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + success = id == OS.GetMenuDefaultItem (hMenu, OS.MF_BYCOMMAND, OS.GMDI_USEDISABLED); + } + if (!success) error (SWT.ERROR_CANNOT_SET_ENABLED); + } + } + } + parent.destroyAccelerators (); + parent.redraw (); +} + +/** + * 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); + if (OS.IsWinCE) { + if ((OS.IsPPC || OS.IsSP) && parent.hwndCB != 0) { + int /*long*/ hwndCB = parent.hwndCB; + TBBUTTONINFO info = new TBBUTTONINFO (); + info.cbSize = TBBUTTONINFO.sizeof; + info.dwMask = OS.TBIF_IMAGE; + info.iImage = parent.imageIndex (image); + OS.SendMessage (hwndCB, OS.TB_SETBUTTONINFO, id, info); + } + return; + } + if (OS.WIN32_VERSION < OS.VERSION (4, 10)) return; + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_BITMAP; + if (parent.foreground != -1) { + info.hbmpItem = OS.HBMMENU_CALLBACK; + } else { + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + if (hBitmap != 0) OS.DeleteObject (hBitmap); + info.hbmpItem = hBitmap = image != null ? Display.create32bitDIB (image) : 0; + } else { + info.hbmpItem = image != null ? OS.HBMMENU_CALLBACK : 0; + } + } + int /*long*/ hMenu = parent.handle; + OS.SetMenuItemInfo (hMenu, id, false, info); + parent.redraw (); +} + +/** + * 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); + } + } + setMenu (menu, false); +} + +void setMenu (Menu menu, boolean dispose) { + + /* Assign the new menu */ + Menu oldMenu = this.menu; + if (oldMenu == menu) return; + if (oldMenu != null) oldMenu.cascade = null; + this.menu = menu; + + /* Assign the new menu in the OS */ + if ((OS.IsPPC || OS.IsSP) && parent.hwndCB != 0) { + if (OS.IsPPC) { + int /*long*/ hwndCB = parent.hwndCB; + int /*long*/ hMenu = menu == null ? 0 : menu.handle; + OS.SendMessage (hwndCB, OS.SHCMBM_SETSUBMENU, id, hMenu); + } + if (OS.IsSP) error (SWT.ERROR_CANNOT_SET_MENU); + } else { + int /*long*/ hMenu = parent.handle; + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_DATA; + int index = 0; + while (OS.GetMenuItemInfo (hMenu, index, true, info)) { + if (info.dwItemData == id) break; + index++; + } + if (info.dwItemData != id) return; + int cch = 128; + int /*long*/ hHeap = OS.GetProcessHeap (); + int byteCount = cch * TCHAR.sizeof; + int /*long*/ pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + info.fMask = OS.MIIM_STATE | OS.MIIM_ID | OS.MIIM_DATA; + /* + * Bug in Windows. When GetMenuItemInfo() is used to get the text, + * for an item that has a bitmap set using MIIM_BITMAP, the text is + * not returned. This means that when SetMenuItemInfo() is used to + * set the submenu and the current menu state, the text is lost. + * The fix is use MIIM_BITMAP and MIIM_STRING. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) { + info.fMask |= OS.MIIM_BITMAP | OS.MIIM_STRING; + } else { + info.fMask |= OS.MIIM_TYPE; + } + info.dwTypeData = pszText; + info.cch = cch; + boolean success = OS.GetMenuItemInfo (hMenu, index, true, info); + if (menu != null) { + menu.cascade = this; + info.fMask |= OS.MIIM_SUBMENU; + info.hSubMenu = menu.handle; + } + if (OS.IsWinCE) { + OS.RemoveMenu (hMenu, index, OS.MF_BYPOSITION); + /* + * On WinCE, InsertMenuItem() is not available. The fix is to + * use SetMenuItemInfo() but this call does not set the menu item + * state and submenu. The fix is to use InsertMenu() to insert + * the item, SetMenuItemInfo() to set the string and EnableMenuItem() + * and CheckMenuItem() to set the state. + */ + int /*long*/ uIDNewItem = id; + int uFlags = OS.MF_BYPOSITION; + if (menu != null) { + uFlags |= OS.MF_POPUP; + uIDNewItem = menu.handle; + } + TCHAR lpNewItem = new TCHAR (0, " ", true); + success = OS.InsertMenu (hMenu, index, uFlags, uIDNewItem, lpNewItem); + if (success) { + info.fMask = OS.MIIM_DATA | OS.MIIM_TYPE; + success = OS.SetMenuItemInfo (hMenu, index, true, info); + if ((info.fState & (OS.MFS_DISABLED | OS.MFS_GRAYED)) != 0) { + OS.EnableMenuItem (hMenu, index, OS.MF_BYPOSITION | OS.MF_GRAYED); + } + if ((info.fState & OS.MFS_CHECKED) != 0) { + OS.CheckMenuItem (hMenu, index, OS.MF_BYPOSITION | OS.MF_CHECKED); + } + } + } else { + if (dispose || oldMenu == null) { + success = OS.SetMenuItemInfo (hMenu, index, true, info); + } else { + /* + * Feature in Windows. When SetMenuItemInfo () is used to + * set a submenu and the menu item already has a submenu, + * Windows destroys the previous menu. This is undocumented + * and unexpected but not necessarily wrong. The fix is to + * remove the item with RemoveMenu () which does not destroy + * the submenu and then insert the item with InsertMenuItem (). + */ + OS.RemoveMenu (hMenu, index, OS.MF_BYPOSITION); + success = OS.InsertMenuItem (hMenu, index, true, info); + } + } + if (pszText != 0) OS.HeapFree (hHeap, 0, pszText); + if (!success) error (SWT.ERROR_CANNOT_SET_MENU); + } + parent.destroyAccelerators (); +} + +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; + if ((OS.IsPPC || OS.IsSP) && parent.hwndCB != 0) return; + int /*long*/ hMenu = parent.handle; + if (OS.IsWinCE) { + int index = parent.indexOf (this); + if (index == -1) return; + int uCheck = OS.MF_BYPOSITION | (selected ? OS.MF_CHECKED : OS.MF_UNCHECKED); + OS.CheckMenuItem (hMenu, index, uCheck); + } else { + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + info.fMask = OS.MIIM_STATE; + boolean success = OS.GetMenuItemInfo (hMenu, id, false, info); + if (!success) error (SWT.ERROR_CANNOT_SET_SELECTION); + info.fState &= ~OS.MFS_CHECKED; + if (selected) info.fState |= OS.MFS_CHECKED; + success = OS.SetMenuItemInfo (hMenu, id, false, info); + if (!success) { + /* + * Bug in Windows. For some reason SetMenuItemInfo(), + * returns a fail code when setting the enabled or + * selected state of a default item, but sets the + * state anyway. The fix is to ignore the error. + * + * NOTE: This only happens on Vista. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + success = id == OS.GetMenuDefaultItem (hMenu, OS.MF_BYCOMMAND, OS.GMDI_USEDISABLED); + } + if (!success) error (SWT.ERROR_CANNOT_SET_SELECTION); + } + } + parent.redraw (); +} +/** + * 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); + int /*long*/ hHeap = OS.GetProcessHeap (); + int /*long*/ pszText = 0; + boolean success = false; + if ((OS.IsPPC || OS.IsSP) && parent.hwndCB != 0) { + /* + * Bug in WinCE PPC. Tool items on the menubar don't resize + * correctly when the character '&' is used (even when it + * is a sequence '&&'). The fix is to remove all '&' from + * the string. + */ + if (string.indexOf ('&') != -1) { + int length = string.length (); + char[] text = new char [length]; + string.getChars( 0, length, text, 0); + int i = 0, j = 0; + for (i=0; i<length; i++) { + if (text[i] != '&') text [j++] = text [i]; + } + if (j < i) string = new String (text, 0, j); + } + /* Use the character encoding for the default locale */ + TCHAR buffer = new TCHAR (0, string, true); + int byteCount = buffer.length () * TCHAR.sizeof; + pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (pszText, buffer, byteCount); + int /*long*/ hwndCB = parent.hwndCB; + TBBUTTONINFO info2 = new TBBUTTONINFO (); + info2.cbSize = TBBUTTONINFO.sizeof; + info2.dwMask = OS.TBIF_TEXT; + info2.pszText = pszText; + success = OS.SendMessage (hwndCB, OS.TB_SETBUTTONINFO, id, info2) != 0; + } else { + MENUITEMINFO info = new MENUITEMINFO (); + info.cbSize = MENUITEMINFO.sizeof; + int /*long*/ hMenu = parent.handle; + + /* Use the character encoding for the default locale */ + TCHAR buffer = new TCHAR (0, string, true); + int byteCount = buffer.length () * TCHAR.sizeof; + pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (pszText, buffer, byteCount); + /* + * Bug in Windows 2000. For some reason, when MIIM_TYPE is set + * on a menu item that also has MIIM_BITMAP, the MIIM_TYPE clears + * the MIIM_BITMAP style. The fix is to use MIIM_STRING. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) { + info.fMask = OS.MIIM_STRING; + } else { + info.fMask = OS.MIIM_TYPE; + info.fType = widgetStyle (); + } + info.dwTypeData = pszText; + success = OS.SetMenuItemInfo (hMenu, id, false, info); + } + if (pszText != 0) OS.HeapFree (hHeap, 0, pszText); + if (!success) error (SWT.ERROR_CANNOT_SET_TEXT); + parent.redraw (); +} + +int widgetStyle () { + int bits = 0; + Decorations shell = parent.parent; + if ((shell.style & SWT.MIRRORED) != 0) { + if ((parent.style & SWT.LEFT_TO_RIGHT) != 0) { + bits |= OS.MFT_RIGHTJUSTIFY | OS.MFT_RIGHTORDER; + } + } else { + if ((parent.style & SWT.RIGHT_TO_LEFT) != 0) { + bits |= OS.MFT_RIGHTJUSTIFY | OS.MFT_RIGHTORDER; + } + } + if ((style & SWT.SEPARATOR) != 0) return bits | OS.MFT_SEPARATOR; + if ((style & SWT.RADIO) != 0) return bits | OS.MFT_RADIOCHECK; + return bits | OS.MFT_STRING; +} + +LRESULT wmCommandChild (int /*long*/ wParam, int /*long*/ lParam) { + 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 (); + setInputState (event, SWT.Selection); + postEvent (SWT.Selection, event); + return null; +} + +LRESULT wmDrawChild (int /*long*/ wParam, int /*long*/ lParam) { + DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT (); + OS.MoveMemory (struct, lParam, DRAWITEMSTRUCT.sizeof); + if (image != null) { + GCData data = new GCData(); + data.device = display; + GC gc = GC.win32_new (struct.hDC, data); + /* + * Bug in Windows. When a bitmap is included in the + * menu bar, the HDC seems to already include the left + * coordinate. The fix is to ignore this value when + * the item is in a menu bar. + */ + int x = (parent.style & SWT.BAR) != 0 ? MARGIN_WIDTH * 2 : struct.left; + Image image = getEnabled () ? this.image : new Image (display, this.image, SWT.IMAGE_DISABLE); + gc.drawImage (image, x, struct.top + MARGIN_HEIGHT); + if (this.image != image) image.dispose (); + gc.dispose (); + } + if (parent.foreground != -1) OS.SetTextColor (struct.hDC, parent.foreground); + return null; +} + +LRESULT wmMeasureChild (int /*long*/ wParam, int /*long*/ lParam) { + MEASUREITEMSTRUCT struct = new MEASUREITEMSTRUCT (); + OS.MoveMemory (struct, lParam, MEASUREITEMSTRUCT.sizeof); + int width = 0, height = 0; + if (image != null) { + Rectangle rect = image.getBounds (); + width = rect.width; + height = rect.height; + } else { + /* + * Bug in Windows. If a menu contains items that have + * images and can be checked, Windows does not include + * the width of the image and the width of the check when + * computing the width of the menu. When the longest item + * does not have an image, the label and the accelerator + * text can overlap. The fix is to use SetMenuItemInfo() + * to indicate that all items have a bitmap and then include + * the width of the widest bitmap in WM_MEASURECHILD. + */ + MENUINFO lpcmi = new MENUINFO (); + lpcmi.cbSize = MENUINFO.sizeof; + lpcmi.fMask = OS.MIM_STYLE; + int /*long*/ hMenu = parent.handle; + OS.GetMenuInfo (hMenu, lpcmi); + if ((lpcmi.dwStyle & OS.MNS_CHECKORBMP) == 0) { + MenuItem [] items = parent.getItems (); + for (int i=0; i<items.length; i++) { + MenuItem item = items [i]; + if (item.image != null) { + Rectangle rect = item.image.getBounds (); + width = Math.max (width, rect.width); + } + } + } + } + if (width != 0 || height != 0) { + struct.itemWidth = width + MARGIN_WIDTH * 2; + struct.itemHeight = height + MARGIN_HEIGHT * 2; + OS.MoveMemory (lParam, struct, MEASUREITEMSTRUCT.sizeof); + } + return null; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/MessageBox.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/MessageBox.java new file mode 100755 index 0000000000..b151ed404b --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/MessageBox.java @@ -0,0 +1,265 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; + +/** + * 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 = ""; + +/** + * 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))); + 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 () { + + /* Compute the MessageBox style */ + int buttonBits = 0; + if ((style & SWT.OK) == SWT.OK) buttonBits = OS.MB_OK; + if ((style & (SWT.OK | SWT.CANCEL)) == (SWT.OK | SWT.CANCEL)) buttonBits = OS.MB_OKCANCEL; + if ((style & (SWT.YES | SWT.NO)) == (SWT.YES | SWT.NO)) buttonBits = OS.MB_YESNO; + if ((style & (SWT.YES | SWT.NO | SWT.CANCEL)) == (SWT.YES | SWT.NO | SWT.CANCEL)) buttonBits = OS.MB_YESNOCANCEL; + if ((style & (SWT.RETRY | SWT.CANCEL)) == (SWT.RETRY | SWT.CANCEL)) buttonBits = OS.MB_RETRYCANCEL; + if ((style & (SWT.ABORT | SWT.RETRY | SWT.IGNORE)) == (SWT.ABORT | SWT.RETRY | SWT.IGNORE)) buttonBits = OS.MB_ABORTRETRYIGNORE; + if (buttonBits == 0) buttonBits = OS.MB_OK; + + int iconBits = 0; + if ((style & SWT.ICON_ERROR) != 0) iconBits = OS.MB_ICONERROR; + if ((style & SWT.ICON_INFORMATION) != 0) iconBits = OS.MB_ICONINFORMATION; + if ((style & SWT.ICON_QUESTION) != 0) iconBits = OS.MB_ICONQUESTION; + if ((style & SWT.ICON_WARNING) != 0) iconBits = OS.MB_ICONWARNING; + if ((style & SWT.ICON_WORKING) != 0) iconBits = OS.MB_ICONINFORMATION; + + /* Only MB_APPLMODAL is supported on WinCE */ + int modalBits = 0; + if (OS.IsWinCE) { + if ((style & (SWT.PRIMARY_MODAL | SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL)) != 0) { + modalBits = OS.MB_APPLMODAL; + } + } else { + if ((style & SWT.PRIMARY_MODAL) != 0) modalBits = OS.MB_APPLMODAL; + if ((style & SWT.APPLICATION_MODAL) != 0) modalBits = OS.MB_TASKMODAL; + if ((style & SWT.SYSTEM_MODAL) != 0) modalBits = OS.MB_SYSTEMMODAL; + } + + int bits = buttonBits | iconBits | modalBits; + if ((style & SWT.RIGHT_TO_LEFT) != 0) bits |= OS.MB_RTLREADING | OS.MB_RIGHT; + if ((style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT)) == 0) { + if (parent != null && (parent.style & SWT.MIRRORED) != 0) { + bits |= OS.MB_RTLREADING | OS.MB_RIGHT; + } + } + + /* + * Feature in Windows. System modal is not supported + * on Windows 95 and NT. The fix is to convert system + * modal to task modal. + */ + if ((bits & OS.MB_SYSTEMMODAL) != 0) { + bits |= OS.MB_TASKMODAL; + bits &= ~OS.MB_SYSTEMMODAL; + /* Force a system modal message box to the front */ + bits |= OS.MB_TOPMOST; + } + + /* + * Feature in Windows. In order for MB_TASKMODAL to work, + * the parent HWND of the MessageBox () call must be NULL. + * If the parent is not NULL, MB_TASKMODAL behaves the + * same as MB_APPLMODAL. The fix to set the parent HWND + * anyway and not rely on MB_MODAL to work by making the + * parent be temporarily modal. + */ + int /*long*/ hwndOwner = parent != null ? parent.handle : 0; + Dialog oldModal = null; + Display display = null; + if ((bits & OS.MB_TASKMODAL) != 0) { + display = parent.getDisplay (); + oldModal = display.getModalDialog (); + display.setModalDialog (this); + } + + /* Open the message box */ + /* Use the character encoding for the default locale */ + TCHAR buffer1 = new TCHAR (0, message, true); + TCHAR buffer2 = new TCHAR (0, title, true); + int code = OS.MessageBox (hwndOwner, buffer1, buffer2, bits); + + /* Clear the temporarily dialog modal parent */ + if ((bits & OS.MB_TASKMODAL) != 0) { + display.setModalDialog (oldModal); + } + + /* + * This code is intentionally commented. On some + * platforms, the owner window is repainted right + * away when a dialog window exits. This behavior + * is currently unspecified. + */ +// if (hwndOwner != 0) OS.UpdateWindow (hwndOwner); + + /* Compute and return the result */ + if (code != 0) { + int type = bits & 0x0F; + if (type == OS.MB_OK) return SWT.OK; + if (type == OS.MB_OKCANCEL) { + return (code == OS.IDOK) ? SWT.OK : SWT.CANCEL; + } + if (type == OS.MB_YESNO) { + return (code == OS.IDYES) ? SWT.YES : SWT.NO; + } + if (type == OS.MB_YESNOCANCEL) { + if (code == OS.IDYES) return SWT.YES; + if (code == OS.IDNO) return SWT.NO; + return SWT.CANCEL; + } + if (type == OS.MB_RETRYCANCEL) { + return (code == OS.IDRETRY) ? SWT.RETRY : SWT.CANCEL; + } + if (type == OS.MB_ABORTRETRYIGNORE) { + if (code == OS.IDRETRY) return SWT.RETRY; + if (code == OS.IDABORT) return SWT.ABORT; + return SWT.IGNORE; + } + } + return SWT.CANCEL; +} + +/** + * 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/win32/org/eclipse/swt/widgets/ProgressBar.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ProgressBar.java new file mode 100755 index 0000000000..ea29f26be4 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ProgressBar.java @@ -0,0 +1,458 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * 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 { + static final int DELAY = 100; + static final int TIMER_ID = 100; + static final int MINIMUM_WIDTH = 100; + static final int /*long*/ ProgressBarProc; + static final TCHAR ProgressBarClass = new TCHAR (0, OS.PROGRESS_CLASS, true); + static { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, ProgressBarClass, lpWndClass); + ProgressBarProc = lpWndClass.lpfnWndProc; + /* + * Feature in Windows. The progress bar window class + * does not include CS_DBLCLKS. This means that these + * controls will not get double click messages such as + * WM_LBUTTONDBLCLK. The fix is to register a new + * window class with CS_DBLCLKS. + * + * NOTE: Screen readers look for the exact class name + * of the control in order to provide the correct kind + * of assistance. Therefore, it is critical that the + * new window class have the same name. It is possible + * to register a local window class with the same name + * as a global class. Since bits that affect the class + * are being changed, it is possible that other native + * code, other than SWT, could create a control with + * this class name, and fail unexpectedly. + */ + int /*long*/ hInstance = OS.GetModuleHandle (null); + int /*long*/ hHeap = OS.GetProcessHeap (); + lpWndClass.hInstance = hInstance; + lpWndClass.style &= ~OS.CS_GLOBALCLASS; + lpWndClass.style |= OS.CS_DBLCLKS; + int byteCount = ProgressBarClass.length () * TCHAR.sizeof; + int /*long*/ lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (lpszClassName, ProgressBarClass, byteCount); + lpWndClass.lpszClassName = lpszClassName; + OS.RegisterClass (lpWndClass); + OS.HeapFree (hHeap, 0, lpszClassName); + } + +/** + * 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)); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + return OS.CallWindowProc (ProgressBarProc, hwnd, msg, wParam, lParam); +} + +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 border = getBorderWidth (); + int width = border * 2, height = border * 2; + if ((style & SWT.HORIZONTAL) != 0) { + width += OS.GetSystemMetrics (OS.SM_CXHSCROLL) * 10; + height += OS.GetSystemMetrics (OS.SM_CYHSCROLL); + } else { + width += OS.GetSystemMetrics (OS.SM_CXVSCROLL); + height += OS.GetSystemMetrics (OS.SM_CYVSCROLL) * 10; + } + if (wHint != SWT.DEFAULT) width = wHint + (border * 2); + if (hHint != SWT.DEFAULT) height = hHint + (border * 2); + return new Point (width, height); +} + +void createHandle () { + super.createHandle (); + startTimer (); +} + +int defaultForeground () { + return OS.GetSysColor (OS.COLOR_HIGHLIGHT); +} + +/** + * 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)/*64*/OS.SendMessage (handle, OS.PBM_GETRANGE, 0, 0); +} + +/** + * 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)/*64*/OS.SendMessage (handle, OS.PBM_GETRANGE, 1, 0); +} + +/** + * 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)/*64*/OS.SendMessage (handle, OS.PBM_GETPOS, 0, 0); +} + +/** + * 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 (); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + int state = (int)/*64*/OS.SendMessage (handle, OS.PBM_GETSTATE, 0, 0); + switch (state) { + case OS.PBST_NORMAL: return SWT.NORMAL; + case OS.PBST_ERROR: return SWT.ERROR; + case OS.PBST_PAUSED: return SWT.PAUSED; + } + } + return SWT.NORMAL; +} + +void releaseWidget () { + super.releaseWidget (); + stopTimer (); +} + +void startTimer () { + if ((style & SWT.INDETERMINATE) != 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if (OS.COMCTL32_MAJOR < 6 || (bits & OS.PBS_MARQUEE) == 0) { + OS.SetTimer (handle, TIMER_ID, DELAY, 0); + } else { + OS.SendMessage (handle, OS.PBM_SETMARQUEE, 1, DELAY); + } + } +} + +void stopTimer () { + if ((style & SWT.INDETERMINATE) != 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if (OS.COMCTL32_MAJOR < 6 || (bits & OS.PBS_MARQUEE) == 0) { + OS.KillTimer (handle, TIMER_ID); + } else { + OS.SendMessage (handle, OS.PBM_SETMARQUEE, 0, 0); + } + } +} + +void setBackgroundPixel (int pixel) { + if (pixel == -1) pixel = OS.CLR_DEFAULT; + OS.SendMessage (handle, OS.PBM_SETBKCOLOR, 0, pixel); +} + +void setForegroundPixel (int pixel) { + if (pixel == -1) pixel = OS.CLR_DEFAULT; + OS.SendMessage (handle, OS.PBM_SETBARCOLOR, 0, pixel); +} + +/** + * 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)/*64*/OS.SendMessage (handle, OS.PBM_GETRANGE, 1, 0); + if (0 <= minimum && minimum < value) { + OS.SendMessage (handle, OS.PBM_SETRANGE32, minimum, 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)/*64*/OS.SendMessage (handle, OS.PBM_GETRANGE, 0, 0); + if (0 <= value && value < maximum) { + OS.SendMessage (handle, OS.PBM_SETRANGE32, value, maximum); + } +} + +/** + * 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 (); + /* + * Feature in Vista. When the progress bar is not in + * a normal state, PBM_SETPOS does not set the position + * of the bar when the selection is equal to the minimum. + * This is undocumented. The fix is to temporarily + * set the state to PBST_NORMAL, set the position, then + * reset the state. + */ + int /*long*/ state = 0; + boolean fixSelection = false; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + int minumum = (int)/*64*/OS.SendMessage (handle, OS.PBM_GETRANGE, 1, 0); + int selection = (int)/*64*/OS.SendMessage (handle, OS.PBM_GETPOS, 0, 0); + if (selection == minumum) { + fixSelection = true; + state = OS.SendMessage (handle, OS.PBM_GETSTATE, 0, 0); + OS.SendMessage (handle, OS.PBM_SETSTATE, OS.PBST_NORMAL, 0); + } + } + OS.SendMessage (handle, OS.PBM_SETPOS, value, 0); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + if (fixSelection) OS.SendMessage (handle, OS.PBM_SETSTATE, state, 0); + } +} + +/** + * 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 (); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + switch (state) { + case SWT.NORMAL: + OS.SendMessage (handle, OS.PBM_SETSTATE, OS.PBST_NORMAL, 0); + break; + case SWT.ERROR: + OS.SendMessage (handle, OS.PBM_SETSTATE, OS.PBST_ERROR, 0); + break; + case SWT.PAUSED: + OS.SendMessage (handle, OS.PBM_SETSTATE, OS.PBST_PAUSED, 0); + break; + } + } +} + +int widgetStyle () { + int bits = super.widgetStyle (); + if ((style & SWT.SMOOTH) != 0) bits |= OS.PBS_SMOOTH; + if ((style & SWT.VERTICAL) != 0) bits |= OS.PBS_VERTICAL; + if ((style & SWT.INDETERMINATE) != 0) bits |= OS.PBS_MARQUEE; + return bits; +} + +TCHAR windowClass () { + return ProgressBarClass; +} + +int /*long*/ windowProc () { + return ProgressBarProc; +} + +LRESULT WM_GETDLGCODE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_GETDLGCODE (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. The progress bar does + * not implement WM_GETDLGCODE. As a result, + * a progress bar takes focus and takes part + * in tab traversal. This behavior, while + * unspecified, is unwanted. The fix is to + * implement WM_GETDLGCODE to behave like a + * STATIC control. + */ + return new LRESULT (OS.DLGC_STATIC); +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SIZE (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. When a progress bar with the style + * PBS_MARQUEE becomes too small, the animation (currently + * a small bar moving from right to left) does not have + * enough space to draw. The result is that the progress + * bar does not appear to be moving. The fix is to detect + * this case, clear the PBS_MARQUEE style and emulate the + * animation using PBM_STEPIT. + * + * NOTE: This only happens on Window XP. + */ + if ((style & SWT.INDETERMINATE) != 0) { + if (OS.COMCTL32_MAJOR >= 6) { + forceResize (); + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE); + int newBits = oldBits; + if (rect.right - rect.left < MINIMUM_WIDTH) { + newBits &= ~OS.PBS_MARQUEE; + } else { + newBits |= OS.PBS_MARQUEE; + } + if (newBits != oldBits) { + stopTimer (); + OS.SetWindowLong (handle, OS.GWL_STYLE, newBits); + startTimer (); + } + } + } + return result; +} + +LRESULT WM_TIMER (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_TIMER (wParam, lParam); + if (result != null) return result; + if ((style & SWT.INDETERMINATE) != 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if (OS.COMCTL32_MAJOR < 6 || (bits & OS.PBS_MARQUEE) == 0) { + if (wParam == TIMER_ID) { + OS.SendMessage (handle, OS.PBM_STEPIT, 0, 0); + } + } + } + return result; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Sash.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Sash.java new file mode 100755 index 0000000000..8df5d6bf1b --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Sash.java @@ -0,0 +1,421 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * 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 { + boolean dragging; + int startX, startY, lastX, lastY; + final static int INCREMENT = 1; + final static int PAGE_INCREMENT = 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 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)); +} + +/** + * 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); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + return OS.DefWindowProc (hwnd, msg, wParam, lParam); +} + +void createHandle () { + super.createHandle (); + state |= THEME_BACKGROUND; +} + +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 border = getBorderWidth (); + int width = border * 2, height = border * 2; + if ((style & SWT.HORIZONTAL) != 0) { + width += DEFAULT_WIDTH; height += 3; + } else { + width += 3; height += DEFAULT_HEIGHT; + } + if (wHint != SWT.DEFAULT) width = wHint + (border * 2); + if (hHint != SWT.DEFAULT) height = hHint + (border * 2); + return new Point (width, height); +} + +void drawBand (int x, int y, int width, int height) { + if ((style & SWT.SMOOTH) != 0) return; + int /*long*/ hwndTrack = parent.handle; + byte [] bits = {-86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0}; + int /*long*/ stippleBitmap = OS.CreateBitmap (8, 8, 1, 1, bits); + int /*long*/ stippleBrush = OS.CreatePatternBrush (stippleBitmap); + int /*long*/ hDC = OS.GetDCEx (hwndTrack, 0, OS.DCX_CACHE); + int /*long*/ oldBrush = OS.SelectObject (hDC, stippleBrush); + OS.PatBlt (hDC, x, y, width, height, OS.PATINVERT); + OS.SelectObject (hDC, oldBrush); + OS.ReleaseDC (hwndTrack, hDC); + OS.DeleteObject (stippleBrush); + OS.DeleteObject (stippleBitmap); +} + +/** + * 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); +} + +TCHAR windowClass () { + return display.windowClass; +} + +int /*long*/ windowProc () { + return display.windowProc; +} + +LRESULT WM_ERASEBKGND (int /*long*/ wParam, int /*long*/ lParam) { + super.WM_ERASEBKGND (wParam, lParam); + drawBackground (wParam); + return LRESULT.ONE; +} + +LRESULT WM_KEYDOWN (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_KEYDOWN (wParam, lParam); + if (result != null) return result; + switch ((int)/*64*/wParam) { + case OS.VK_LEFT: + case OS.VK_RIGHT: + case OS.VK_UP: + case OS.VK_DOWN: + + /* Calculate the new x or y position */ + if (OS.GetKeyState (OS.VK_LBUTTON) < 0) return result; + int step = OS.GetKeyState (OS.VK_CONTROL) < 0 ? INCREMENT : PAGE_INCREMENT; + POINT pt = new POINT (); + if ((style & SWT.VERTICAL) != 0) { + if (wParam == OS.VK_UP || wParam == OS.VK_DOWN) break; + pt.x = wParam == OS.VK_LEFT ? -step : step; + } else { + if (wParam == OS.VK_LEFT || wParam == OS.VK_RIGHT) break; + pt.y = wParam == OS.VK_UP ? -step : step; + } + int /*long*/ hwndTrack = parent.handle; + OS.MapWindowPoints (handle, hwndTrack, pt, 1); + RECT rect = new RECT (), clientRect = new RECT (); + OS.GetWindowRect (handle, rect); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + OS.GetClientRect (hwndTrack, clientRect); + int clientWidth = clientRect.right - clientRect.left; + int clientHeight = clientRect.bottom - clientRect.top; + int newX = lastX, newY = lastY; + if ((style & SWT.VERTICAL) != 0) { + newX = Math.min (Math.max (0, pt.x - startX), clientWidth - width); + } else { + newY = Math.min (Math.max (0, pt.y - startY), clientHeight - height); + } + if (newX == lastX && newY == lastY) return result; + + /* Update the pointer position */ + POINT cursorPt = new POINT (); + cursorPt.x = pt.x; cursorPt.y = pt.y; + OS.ClientToScreen (hwndTrack, cursorPt); + if ((style & SWT.VERTICAL) != 0) { + cursorPt.y += height / 2; + } + else { + cursorPt.x += width / 2; + } + OS.SetCursorPos (cursorPt.x, cursorPt.y); + + Event event = new Event (); + event.x = newX; + event.y = newY; + event.width = width; + event.height = height; + sendEvent (SWT.Selection, event); + if (isDisposed ()) return LRESULT.ZERO; + if (event.doit) { + if ((style & SWT.SMOOTH) != 0) { + setBounds (event.x, event.y, width, height); + } + } + return result; + } + return result; +} + +LRESULT WM_GETDLGCODE (int /*long*/ wParam, int /*long*/ lParam) { + return new LRESULT (OS.DLGC_STATIC); +} + +LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam); + if (result == LRESULT.ZERO) return result; + + /* Compute the banding rectangle */ + int /*long*/ hwndTrack = parent.handle; + POINT pt = new POINT (); + OS.POINTSTOPOINT (pt, lParam); + RECT rect = new RECT (); + OS.GetWindowRect (handle, rect); + OS.MapWindowPoints (handle, 0, pt, 1); + startX = pt.x - rect.left; + startY = pt.y - rect.top; + OS.MapWindowPoints (0, hwndTrack, rect, 2); + lastX = rect.left; + lastY = rect.top; + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + + /* The event must be sent because doit flag is used */ + Event event = new Event (); + event.x = lastX; + event.y = lastY; + event.width = width; + event.height = height; + if ((style & SWT.SMOOTH) == 0) { + event.detail = SWT.DRAG; + } + sendEvent (SWT.Selection, event); + if (isDisposed ()) return LRESULT.ZERO; + + /* Draw the banding rectangle */ + if (event.doit) { + dragging = true; + lastX = event.x; + lastY = event.y; + menuShell ().bringToTop (); + if (isDisposed ()) return LRESULT.ZERO; + if (OS.IsWinCE) { + OS.UpdateWindow (hwndTrack); + } else { + int flags = OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (hwndTrack, null, 0, flags); + } + drawBand (event.x, event.y, width, height); + if ((style & SWT.SMOOTH) != 0) { + setBounds (event.x, event.y, width, height); + // widget could be disposed at this point + } + } + return result; +} + +LRESULT WM_LBUTTONUP (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_LBUTTONUP (wParam, lParam); + if (result == LRESULT.ZERO) return result; + + /* Compute the banding rectangle */ + if (!dragging) return result; + dragging = false; + RECT rect = new RECT (); + OS.GetWindowRect (handle, rect); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + + /* The event must be sent because doit flag is used */ + Event event = new Event (); + event.x = lastX; + event.y = lastY; + event.width = width; + event.height = height; + drawBand (event.x, event.y, width, height); + sendEvent (SWT.Selection, event); + if (isDisposed ()) return result; + if (event.doit) { + if ((style & SWT.SMOOTH) != 0) { + setBounds (event.x, event.y, width, height); + // widget could be disposed at this point + } + } + return result; +} + +LRESULT WM_MOUSEMOVE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_MOUSEMOVE (wParam, lParam); + if (result != null) return result; + if (!dragging || (wParam & OS.MK_LBUTTON) == 0) return result; + + /* Compute the banding rectangle */ + POINT pt = new POINT (); + OS.POINTSTOPOINT (pt, lParam); + int /*long*/ hwndTrack = parent.handle; + OS.MapWindowPoints (handle, hwndTrack, pt, 1); + RECT rect = new RECT (), clientRect = new RECT (); + OS.GetWindowRect (handle, rect); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + OS.GetClientRect (hwndTrack, clientRect); + int newX = lastX, newY = lastY; + if ((style & SWT.VERTICAL) != 0) { + int clientWidth = clientRect.right - clientRect.left; + newX = Math.min (Math.max (0, pt.x - startX), clientWidth - width); + } else { + int clientHeight = clientRect.bottom - clientRect.top; + newY = Math.min (Math.max (0, pt.y - startY), clientHeight - height); + } + if (newX == lastX && newY == lastY) return result; + drawBand (lastX, lastY, width, height); + + /* The event must be sent because doit flag is used */ + Event event = new Event (); + event.x = newX; + event.y = newY; + event.width = width; + event.height = height; + if ((style & SWT.SMOOTH) == 0) { + event.detail = SWT.DRAG; + } + sendEvent (SWT.Selection, event); + if (isDisposed ()) return LRESULT.ZERO; + if (event.doit) { + lastX = event.x; + lastY = event.y; + } + if (OS.IsWinCE) { + OS.UpdateWindow (hwndTrack); + } else { + int flags = OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (hwndTrack, null, 0, flags); + } + drawBand (lastX, lastY, width, height); + if ((style & SWT.SMOOTH) != 0) { + setBounds (lastX, lastY, width, height); + // widget could be disposed at this point + } + return result; +} + +LRESULT WM_SETCURSOR (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETCURSOR (wParam, lParam); + if (result != null) return result; + int hitTest = (short) OS.LOWORD (lParam); + if (hitTest == OS.HTCLIENT) { + int /*long*/ hCursor = 0; + if ((style & SWT.HORIZONTAL) != 0) { + hCursor = OS.LoadCursor (0, OS.IDC_SIZENS); + } else { + hCursor = OS.LoadCursor (0, OS.IDC_SIZEWE); + } + OS.SetCursor (hCursor); + return LRESULT.ONE; + } + return result; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Scale.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Scale.java new file mode 100755 index 0000000000..ce6b0119dc --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Scale.java @@ -0,0 +1,561 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * 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 { + boolean ignoreResize, ignoreSelection; + static final int /*long*/ TrackBarProc; + static final TCHAR TrackBarClass = new TCHAR (0, OS.TRACKBAR_CLASS, true); + static { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, TrackBarClass, lpWndClass); + TrackBarProc = lpWndClass.lpfnWndProc; + /* + * Feature in Windows. The track bar window class + * does not include CS_DBLCLKS. This means that these + * controls will not get double click messages such as + * WM_LBUTTONDBLCLK. The fix is to register a new + * window class with CS_DBLCLKS. + * + * NOTE: Screen readers look for the exact class name + * of the control in order to provide the correct kind + * of assistance. Therefore, it is critical that the + * new window class have the same name. It is possible + * to register a local window class with the same name + * as a global class. Since bits that affect the class + * are being changed, it is possible that other native + * code, other than SWT, could create a control with + * this class name, and fail unexpectedly. + */ + int /*long*/ hInstance = OS.GetModuleHandle (null); + int /*long*/ hHeap = OS.GetProcessHeap (); + lpWndClass.hInstance = hInstance; + lpWndClass.style &= ~OS.CS_GLOBALCLASS; + lpWndClass.style |= OS.CS_DBLCLKS; + int byteCount = TrackBarClass.length () * TCHAR.sizeof; + int /*long*/ lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (lpszClassName, TrackBarClass, byteCount); + lpWndClass.lpszClassName = lpszClassName; + OS.RegisterClass (lpWndClass); + OS.HeapFree (hHeap, 0, lpszClassName); + } + +/** + * 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); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + return OS.CallWindowProc (TrackBarProc, hwnd, msg, wParam, lParam); +} + +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 border = getBorderWidth (); + int width = border * 2, height = border * 2; + RECT rect = new RECT (); + OS.SendMessage (handle, OS.TBM_GETTHUMBRECT, 0, rect); + if ((style & SWT.HORIZONTAL) != 0) { + width += OS.GetSystemMetrics (OS.SM_CXHSCROLL) * 10; + int scrollY = OS.GetSystemMetrics (OS.SM_CYHSCROLL); + height += (rect.top * 2) + scrollY + (scrollY / 3); + } else { + int scrollX = OS.GetSystemMetrics (OS.SM_CXVSCROLL); + width += (rect.left * 2) + scrollX + (scrollX / 3); + height += OS.GetSystemMetrics (OS.SM_CYVSCROLL) * 10; + } + if (wHint != SWT.DEFAULT) width = wHint + (border * 2); + if (hHint != SWT.DEFAULT) height = hHint + (border * 2); + return new Point (width, height); +} + +void createHandle () { + super.createHandle (); + state |= THEME_BACKGROUND | DRAW_BACKGROUND; + OS.SendMessage (handle, OS.TBM_SETRANGEMAX, 0, 100); + OS.SendMessage (handle, OS.TBM_SETPAGESIZE, 0, 10); + OS.SendMessage (handle, OS.TBM_SETTICFREQ, 10, 0); +} + +int defaultForeground () { + return OS.GetSysColor (OS.COLOR_BTNFACE); +} + +/** + * 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 (int)/*64*/OS.SendMessage (handle, OS.TBM_GETLINESIZE, 0, 0); +} + +/** + * 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)/*64*/OS.SendMessage (handle, OS.TBM_GETRANGEMAX, 0, 0); +} + +/** + * 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)OS.SendMessage (handle, OS.TBM_GETRANGEMIN, 0, 0); +} + +/** + * 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 (int)/*64*/OS.SendMessage (handle, OS.TBM_GETPAGESIZE, 0, 0); +} + +/** + * 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)/*64*/OS.SendMessage (handle, OS.TBM_GETPOS, 0, 0); +} + +/** + * 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 setBackgroundImage (int /*long*/ hImage) { + super.setBackgroundImage (hImage); + /* + * Bug in Windows. Changing the background color of the Scale + * widget and calling InvalidateRect() still draws with the old + * color. The fix is to send a fake WM_SIZE event to cause + * it to redraw with the new background color. + */ + ignoreResize = true; + OS.SendMessage (handle, OS.WM_SIZE, 0, 0); + ignoreResize = false; +} + +void setBackgroundPixel (int pixel) { + super.setBackgroundPixel (pixel); + /* + * Bug in Windows. Changing the background color of the Scale + * widget and calling InvalidateRect() still draws with the old + * color. The fix is to send a fake WM_SIZE event to cause + * it to redraw with the new background color. + */ + ignoreResize = true; + OS.SendMessage (handle, OS.WM_SIZE, 0, 0); + ignoreResize = false; +} + +void setBounds (int x, int y, int width, int height, int flags, boolean defer) { + /* + * Bug in Windows. If SetWindowPos() is called on a + * track bar with either SWP_DRAWFRAME, a new size, + * or both during mouse down, the track bar posts a + * WM_MOUSEMOVE message when the mouse has not moved. + * The window proc for the track bar uses WM_MOUSEMOVE + * to issue WM_HSCROLL or WM_SCROLL events to notify + * the application that the slider has changed. The + * end result is that when the user requests a page + * scroll and the application resizes the track bar + * during the change notification, continuous stream + * of WM_MOUSEMOVE messages are generated and the + * thumb moves to the mouse position rather than + * scrolling by a page. The fix is to clear the + * SWP_DRAWFRAME flag. + * + * NOTE: There is no fix for the WM_MOUSEMOVE that + * is generated by a new size. Clearing SWP_DRAWFRAME + * does not fix the problem. However, it is unlikely + * that the programmer will resize the control during + * mouse down. + */ + flags &= ~OS.SWP_DRAWFRAME; + super.setBounds (x, y, width, height, flags, true); +} + +/** + * 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 increment) { + checkWidget (); + if (increment < 1) return; + int minimum = (int)/*64*/OS.SendMessage (handle, OS.TBM_GETRANGEMIN, 0, 0); + int maximum = (int)/*64*/OS.SendMessage (handle, OS.TBM_GETRANGEMAX, 0, 0); + if (increment > maximum - minimum) return; + OS.SendMessage (handle, OS.TBM_SETLINESIZE, 0, increment); +} + +/** + * 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)/*64*/OS.SendMessage (handle, OS.TBM_GETRANGEMIN, 0, 0); + if (0 <= minimum && minimum < value) { + OS.SendMessage (handle, OS.TBM_SETRANGEMAX, 1, 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)/*64*/OS.SendMessage (handle, OS.TBM_GETRANGEMAX, 0, 0); + if (0 <= value && value < maximum) { + OS.SendMessage (handle, OS.TBM_SETRANGEMIN, 1, 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 pageIncrement) { + checkWidget (); + if (pageIncrement < 1) return; + int minimum = (int)/*64*/OS.SendMessage (handle, OS.TBM_GETRANGEMIN, 0, 0); + int maximum = (int)/*64*/OS.SendMessage (handle, OS.TBM_GETRANGEMAX, 0, 0); + if (pageIncrement > maximum - minimum) return; + OS.SendMessage (handle, OS.TBM_SETPAGESIZE, 0, pageIncrement); + OS.SendMessage (handle, OS.TBM_SETTICFREQ, pageIncrement, 0); +} + +/** + * 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 (); + OS.SendMessage (handle, OS.TBM_SETPOS, 1, value); +} + +int widgetStyle () { + int bits = super.widgetStyle () | OS.WS_TABSTOP | OS.TBS_BOTH | OS.TBS_AUTOTICKS; + if ((style & SWT.HORIZONTAL) != 0) return bits | OS.TBS_HORZ | OS.TBS_DOWNISLEFT; + return bits | OS.TBS_VERT; +} + +TCHAR windowClass () { + return TrackBarClass; +} + +int /*long*/ windowProc () { + return TrackBarProc; +} + +LRESULT WM_MOUSEWHEEL (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_MOUSEWHEEL (wParam, lParam); + if (result != null) return result; + /* + * Bug in Windows. When a track bar slider is changed + * from WM_MOUSEWHEEL, it does not always send either + * a WM_VSCROLL or M_HSCROLL to notify the application + * of the change. The fix is to detect that the selection + * has changed and that notification has not been issued + * and send the selection event. + */ + int oldPosition = (int)/*64*/OS.SendMessage (handle, OS.TBM_GETPOS, 0, 0); + ignoreSelection = true; + int /*long*/ code = callWindowProc (handle, OS.WM_MOUSEWHEEL, wParam, lParam); + ignoreSelection = false; + int newPosition = (int)/*64*/OS.SendMessage (handle, OS.TBM_GETPOS, 0, 0); + if (oldPosition != newPosition) { + /* + * Send the event because WM_HSCROLL and WM_VSCROLL + * are sent from a modal message loop in windows that + * is active when the user is scrolling. + */ + sendEvent (SWT.Selection); + // widget could be disposed at this point + } + return new LRESULT (code); +} + +LRESULT WM_PAINT (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Bug in Windows. For some reason, when WM_CTLCOLORSTATIC + * is used to implement transparency and returns a NULL brush, + * Windows doesn't always draw the track bar. It seems that + * it is drawn correctly the first time. It is possible that + * Windows double buffers the control and the double buffer + * strategy fails when WM_CTLCOLORSTATIC returns unexpected + * results. The fix is to send a fake WM_SIZE to force it + * to redraw every time there is a WM_PAINT. + */ + boolean fixPaint = findBackgroundControl () != null; + if (!fixPaint) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + Control control = findThemeControl (); + fixPaint = control != null; + } + } + if (fixPaint) { + boolean redraw = getDrawing () && OS.IsWindowVisible (handle); + if (redraw) OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); + ignoreResize = true; + OS.SendMessage (handle, OS.WM_SIZE, 0, 0); + ignoreResize = false; + if (redraw) { + OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0); + OS.InvalidateRect (handle, null, false); + } + } + return super.WM_PAINT (wParam, lParam); +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreResize) return null; + return super.WM_SIZE (wParam, lParam); +} + +LRESULT wmScrollChild (int /*long*/ wParam, int /*long*/ lParam) { + + /* Do nothing when scrolling is ending */ + int code = OS.LOWORD (wParam); + switch (code) { + case OS.TB_ENDTRACK: + case OS.TB_THUMBPOSITION: + return null; + } + + if (!ignoreSelection) { + Event event = new Event (); + /* + * This code is intentionally commented. The event + * detail field is not currently supported on all + * platforms. + */ +// switch (code) { +// case OS.TB_TOP: event.detail = SWT.HOME; break; +// case OS.TB_BOTTOM: event.detail = SWT.END; break; +// case OS.TB_LINEDOWN: event.detail = SWT.ARROW_DOWN; break; +// case OS.TB_LINEUP: event.detail = SWT.ARROW_UP; break; +// case OS.TB_PAGEDOWN: event.detail = SWT.PAGE_DOWN; break; +// case OS.TB_PAGEUP: event.detail = SWT.PAGE_UP; break; +// } + /* + * Send the event because WM_HSCROLL and WM_VSCROLL + * are sent from a modal message loop in windows that + * is active when the user is scrolling. + */ + sendEvent (SWT.Selection, event); + // widget could be disposed at this point + } + return null; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ScrollBar.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ScrollBar.java new file mode 100755 index 0000000000..80e8ca68af --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ScrollBar.java @@ -0,0 +1,952 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * 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 { + Scrollable parent; + int increment, pageIncrement; + +/** + * 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 + */ +ScrollBar (Scrollable parent, int style) { + super (parent, checkStyle (style)); + this.parent = parent; + createWidget (); +} + +/** + * 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 createWidget () { + increment = 1; + pageIncrement = 10; + /* + * Do not set the initial values of the maximum + * or the thumb. These values normally default + * to 100 and 10 but may have been set already + * by the widget that owns the scroll bar. For + * example, a scroll bar that is created for a + * list widget, setting these defaults would + * override the initial values provided by the + * list widget. + */ +} + +void destroyWidget () { + int /*long*/ hwnd = hwndScrollBar (); + int type = scrollBarType (); + if (OS.IsWinCE) { + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_RANGE | OS.SIF_PAGE; + info.nPage = 101; + info.nMax = 100; + info.nMin = 0; + OS.SetScrollInfo (hwnd, type, info, true); + } else { + OS.ShowScrollBar (hwnd, type, false); + } + parent.destroyScrollBar (style); + releaseHandle (); + //This code is intentionally commented + //parent.sendEvent (SWT.Resize); +} + +Rectangle getBounds () { +// checkWidget (); + parent.forceResize (); + RECT rect = new RECT (); + OS.GetClientRect (parent.scrolledHandle (), rect); + int x = 0, y = 0, width, height; + if ((style & SWT.HORIZONTAL) != 0) { + y = rect.bottom - rect.top; + width = rect.right - rect.left; + height = OS.GetSystemMetrics (OS.SM_CYHSCROLL); + } else { + x = rect.right - rect.left; + width = OS.GetSystemMetrics (OS.SM_CXVSCROLL); + height = rect.bottom - rect.top; + } + return new Rectangle (x, y, width, height); +} + +/** + * 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(); + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_RANGE; + int /*long*/ hwnd = hwndScrollBar (); + int type = scrollBarType (); + OS.GetScrollInfo (hwnd, type, info); + return info.nMax; +} + +/** + * 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(); + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_RANGE; + int /*long*/ hwnd = hwndScrollBar (); + int type = scrollBarType (); + OS.GetScrollInfo (hwnd, type, info); + return info.nMin; +} + +/** + * 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(); + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_POS; + int /*long*/ hwnd = hwndScrollBar (); + int type = scrollBarType (); + OS.GetScrollInfo (hwnd, type, info); + return info.nPos; +} + +/** + * 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(); + parent.forceResize (); + RECT rect = new RECT (); + OS.GetClientRect (parent.scrolledHandle (), rect); + int width, height; + if ((style & SWT.HORIZONTAL) != 0) { + width = rect.right - rect.left; + height = OS.GetSystemMetrics (OS.SM_CYHSCROLL); + } else { + width = OS.GetSystemMetrics (OS.SM_CXVSCROLL); + height = rect.bottom - rect.top; + } + return new Point (width, 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(); + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_PAGE; + int /*long*/ hwnd = hwndScrollBar (); + int type = scrollBarType (); + OS.GetScrollInfo (hwnd, type, info); + if (info.nPage != 0) --info.nPage; + return info.nPage; +} + +/** + * 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 (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) { + SCROLLBARINFO psbi = new SCROLLBARINFO (); + psbi.cbSize = SCROLLBARINFO.sizeof; + int idObject = (style & SWT.VERTICAL) != 0 ? OS.OBJID_VSCROLL : OS.OBJID_HSCROLL; + OS.GetScrollBarInfo (hwndScrollBar (), idObject, psbi); + return (psbi.rgstate [0] & OS.STATE_SYSTEM_INVISIBLE) == 0; + } + return (state & HIDDEN) == 0; +} + +int /*long*/ hwndScrollBar () { + return parent.scrolledHandle (); +} + +/** + * 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 (); +} + +/** + * 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 (); +} + +void releaseHandle () { + super.releaseHandle (); + parent = null; +} + +void releaseParent () { + super.releaseParent (); + if (parent.horizontalBar == this) parent.horizontalBar = null; + if (parent.verticalBar == this) parent.verticalBar = null; +} + +/** + * 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); +} + +int scrollBarType () { + return (style & SWT.VERTICAL) != 0 ? OS.SB_VERT : OS.SB_HORZ; +} + +/** + * 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(); + /* + * This line is intentionally commented. Currently + * always show scrollbar as being enabled and visible. + */ +// if (OS.IsWinCE) error (SWT.ERROR_NOT_IMPLEMENTED); + if (!OS.IsWinCE) { + int /*long*/ hwnd = hwndScrollBar (); + int type = scrollBarType (); + int flags = enabled ? OS.ESB_ENABLE_BOTH : OS.ESB_DISABLE_BOTH; + OS.EnableScrollBar (hwnd, type, flags); + if (enabled) { + state &= ~DISABLED; + } else { + state |= DISABLED; + } + } +} + +/** + * 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 + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if 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; + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + int /*long*/ hwnd = hwndScrollBar (); + int type = scrollBarType (); + info.fMask = OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL; + OS.GetScrollInfo (hwnd, type, info); + if (value - info.nMin - info.nPage < 1) return; + info.nMax = value; + SetScrollInfo (hwnd, type, info, true); +} + +/** + * 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; + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + int /*long*/ hwnd = hwndScrollBar (); + int type = scrollBarType (); + info.fMask = OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL; + OS.GetScrollInfo (hwnd, type, info); + if (info.nMax - value - info.nPage < 1) return; + info.nMin = value; + SetScrollInfo (hwnd, type, info, true); +} + +/** + * 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; +} + +boolean SetScrollInfo (int /*long*/ hwnd, int flags, SCROLLINFO info, boolean fRedraw) { + /* + * Bug in Windows. For some reason, when SetScrollInfo() + * is used with SIF_POS and the scroll bar is hidden, + * the opposite scroll bar is incorrectly made visible + * so that the next time the parent is resized (or another + * scroll bar operation is performed), the opposite scroll + * bar draws. The fix is to hide both scroll bars. + */ + boolean barVisible = false; + boolean visible = getVisible (); + + /* + * This line is intentionally commented. Currently + * always show scrollbar as being enabled and visible. + */ +// if (OS.IsWinCE) error (SWT.ERROR_NOT_IMPLEMENTED); + ScrollBar bar = null; + if (!OS.IsWinCE) { + switch (flags) { + case OS.SB_HORZ: + bar = parent.getVerticalBar (); + break; + case OS.SB_VERT: + bar = parent.getHorizontalBar (); + break; + } + barVisible = bar != null && bar.getVisible (); + } + if (!visible || (state & DISABLED) != 0) fRedraw = false; + boolean result = OS.SetScrollInfo (hwnd, flags, info, fRedraw); + + /* + * Bug in Windows. For some reason, when the widget + * is a standard scroll bar, and SetScrollInfo() is + * called with SIF_RANGE or SIF_PAGE, the widget is + * incorrectly made visible so that the next time the + * parent is resized (or another scroll bar operation + * is performed), the scroll bar draws. The fix is + * to hide the scroll bar (again) when already hidden. + */ + if (!visible) { + /* + * This line is intentionally commented. Currently + * always show scrollbar as being enabled and visible. + */ +// if (OS.IsWinCE) error (SWT.ERROR_NOT_IMPLEMENTED); + if (!OS.IsWinCE) { + OS.ShowScrollBar (hwnd, !barVisible ? OS.SB_BOTH : flags, false); + } + } + + /* + * Bug in Windows. When only one scroll bar is visible, + * and the thumb changes using SIF_RANGE or SIF_PAGE + * from being visible to hidden, the opposite scroll + * bar is incorrectly made visible. The next time the + * parent is resized (or another scroll bar operation + * is performed), the opposite scroll bar draws. The + * fix is to hide the opposite scroll bar again. + * + * NOTE: This problem only happens on Vista + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + if (visible && bar != null && !barVisible) { + OS.ShowScrollBar (hwnd, flags == OS.SB_HORZ ? OS.SB_VERT : OS.SB_HORZ, false); + } + } + + /* + * Feature in Windows. Using SIF_DISABLENOSCROLL, + * SetScrollInfo () can change enabled and disabled + * state of the scroll bar causing a scroll bar that + * was disabled by the application to become enabled. + * The fix is to disable the scroll bar (again) when + * the application has disabled the scroll bar. + */ + if ((state & DISABLED) != 0) { + /* + * This line is intentionally commented. Currently + * always show scrollbar as being enabled and visible. + */ +// if (OS.IsWinCE) error (SWT.ERROR_NOT_IMPLEMENTED); + if (!OS.IsWinCE) { + OS.EnableScrollBar (hwnd, flags, OS.ESB_DISABLE_BOTH); + } + } + return result; +} + +/** + * 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 selection) { + checkWidget(); + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + int /*long*/ hwnd = hwndScrollBar (); + int type = scrollBarType (); + info.fMask = OS.SIF_POS; + info.nPos = selection; + SetScrollInfo (hwnd, type, info, true); +} + +/** + * 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; + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + int /*long*/ hwnd = hwndScrollBar (); + int type = scrollBarType (); + info.fMask = OS.SIF_PAGE | OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL; + OS.GetScrollInfo (hwnd, type, info); + info.nPage = value; + if (info.nPage != 0) info.nPage++; + SetScrollInfo (hwnd, type, info, true); +} + +/** + * 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.increment = increment; + this.pageIncrement = pageIncrement; + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_POS | OS.SIF_PAGE | OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL; + info.nPos = selection; + info.nMin = minimum; + info.nMax = maximum; + info.nPage = thumb; + if (info.nPage != 0) info.nPage++; + int /*long*/ hwnd = hwndScrollBar (); + int type = scrollBarType (); + SetScrollInfo (hwnd, type, info, true); +} + +/** + * 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 == getVisible ()) return; + + /* + * On Windows CE, use SIF_DISABLENOSCROLL to show and + * hide the scroll bar when the page size is equal to + * the range. + */ + if (OS.IsWinCE) { + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + int /*long*/ hwnd = hwndScrollBar (); + int type = scrollBarType (); + info.fMask = OS.SIF_RANGE | OS.SIF_PAGE; + if (visible) info.fMask |= OS.SIF_DISABLENOSCROLL; + OS.GetScrollInfo (hwnd, type, info); + if (info.nPage == info.nMax - info.nMin + 1) { + /* + * Bug in Windows. When the only changed flag to + * SetScrollInfo () is OS.SIF_DISABLENOSCROLL, + * Windows does not update the scroll bar state. + * The fix is to increase and then decrease the + * maximum, causing Windows to honour the flag. + */ + int max = info.nMax; + info.nMax++; + OS.SetScrollInfo (hwnd, type, info, false); + info.nMax = max; + OS.SetScrollInfo (hwnd, type, info, true); + } else { + /* + * This line is intentionally commented. Currently + * always show scrollbar as being enabled and visible. + */ +// if (OS.IsWinCE) error (SWT.ERROR_NOT_IMPLEMENTED); + } + return; + } + + /* + * Set the state bits before calling ShowScrollBar () + * because hiding and showing the scroll bar can cause + * WM_SIZE messages when the client area is resized. + * Setting the state before the call means that code + * that runs during WM_SIZE that queries the visibility + * of the scroll bar will get the correct value. + */ + state = visible ? state & ~HIDDEN : state | HIDDEN; + int /*long*/ hwnd = hwndScrollBar (); + int type = scrollBarType (); + if (OS.ShowScrollBar (hwnd, type, visible)) { + /* + * Bug in Windows. For some reason, when the widget + * is a standard scroll bar, and SetScrollInfo () is + * called with SIF_RANGE or SIF_PAGE while the widget + * is not visible, the widget is incorrectly disabled + * even though the values for SIF_RANGE and SIF_PAGE, + * when set for a visible scroll bar would not disable + * the scroll bar. The fix is to enable the scroll bar + * when not disabled by the application and the current + * scroll bar ranges would cause the scroll bar to be + * enabled had they been set when the scroll bar was + * visible. + */ + if ((state & DISABLED) == 0) { + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_RANGE | OS.SIF_PAGE; + OS.GetScrollInfo (hwnd, type, info); + if (info.nMax - info.nMin - info.nPage >= 0) { + OS.EnableScrollBar (hwnd, type, OS.ESB_ENABLE_BOTH); + } + } + sendEvent (visible ? SWT.Show : SWT.Hide); + // widget could be disposed at this point + } +} + +LRESULT wmScrollChild (int /*long*/ wParam, int /*long*/ lParam) { + + /* Do nothing when scrolling is ending */ + int code = OS.LOWORD (wParam); + if (code == OS.SB_ENDSCROLL) return null; + + /* + * Send the event because WM_HSCROLL and + * WM_VSCROLL are sent from a modal message + * loop in Windows that is active when the + * user is scrolling. + */ + Event event = new Event (); + switch (code) { + case OS.SB_THUMBPOSITION: event.detail = SWT.NONE; break; + case OS.SB_THUMBTRACK: event.detail = SWT.DRAG; break; + case OS.SB_TOP: event.detail = SWT.HOME; break; + case OS.SB_BOTTOM: event.detail = SWT.END; break; + case OS.SB_LINEDOWN: event.detail = SWT.ARROW_DOWN; break; + case OS.SB_LINEUP: event.detail = SWT.ARROW_UP; break; + case OS.SB_PAGEDOWN: event.detail = SWT.PAGE_DOWN; break; + case OS.SB_PAGEUP: event.detail = SWT.PAGE_UP; break; + } + sendEvent (SWT.Selection, event); + // the widget could be destroyed at this point + return null; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Scrollable.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Scrollable.java new file mode 100755 index 0000000000..c4ac04add5 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Scrollable.java @@ -0,0 +1,495 @@ +/******************************************************************************* + * 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.win32.*; +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 { + ScrollBar horizontalBar, verticalBar; + +/** + * Prevents uninitialized instances from being created outside the package. + */ +Scrollable () { +} + +/** + * 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); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + return OS.DefWindowProc (hwnd, msg, wParam, lParam); +} + +/** + * 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 (); + int /*long*/ scrolledHandle = scrolledHandle (); + RECT rect = new RECT (); + OS.SetRect (rect, x, y, x + width, y + height); + int bits1 = OS.GetWindowLong (scrolledHandle, OS.GWL_STYLE); + int bits2 = OS.GetWindowLong (scrolledHandle, OS.GWL_EXSTYLE); + OS.AdjustWindowRectEx (rect, bits1, false, bits2); + if (horizontalBar != null) rect.bottom += OS.GetSystemMetrics (OS.SM_CYHSCROLL); + if (verticalBar != null) rect.right += OS.GetSystemMetrics (OS.SM_CXVSCROLL); + int nWidth = rect.right - rect.left, nHeight = rect.bottom - rect.top; + return new Rectangle (rect.left, rect.top, nWidth, nHeight); +} + +ScrollBar createScrollBar (int type) { + ScrollBar bar = new ScrollBar (this, type); + if ((state & CANVAS) != 0) { + bar.setMaximum (100); + bar.setThumb (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 destroyScrollBar (int type) { + int /*long*/ hwnd = scrolledHandle (); + int bits = OS.GetWindowLong (hwnd, OS.GWL_STYLE); + if ((type & SWT.HORIZONTAL) != 0) { + style &= ~SWT.H_SCROLL; + bits &= ~OS.WS_HSCROLL; + } + if ((type & SWT.VERTICAL) != 0) { + style &= ~SWT.V_SCROLL; + bits &= ~OS.WS_VSCROLL; + } + OS.SetWindowLong (hwnd, OS.GWL_STYLE, bits); +} + +/** + * 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 (); + forceResize (); + RECT rect = new RECT (); + int /*long*/ scrolledHandle = scrolledHandle (); + OS.GetClientRect (scrolledHandle, rect); + int x = rect.left, y = rect.top; + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + if (scrolledHandle != handle) { + OS.GetClientRect (handle, rect); + OS.MapWindowPoints(handle, scrolledHandle, rect, 2); + x = -rect.left; + y = -rect.top; + } + return new Rectangle (x, y, width, 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; +} + +void releaseChildren (boolean destroy) { + if (horizontalBar != null) { + horizontalBar.release (false); + horizontalBar = null; + } + if (verticalBar != null) { + verticalBar.release (false); + verticalBar = null; + } + super.releaseChildren (destroy); +} + +int /*long*/ scrolledHandle () { + return handle; +} + +int widgetExtStyle () { + return super.widgetExtStyle (); + /* + * This code is intentionally commented. In future, + * we may wish to support different standard Windows + * edge styles. The issue here is that not all of + * these styles are available on the other platforms + * this would need to be a hint. + */ +// if ((style & SWT.BORDER) != 0) return OS.WS_EX_CLIENTEDGE; +// if ((style & SWT.SHADOW_IN) != 0) return OS.WS_EX_STATICEDGE; +// return super.widgetExtStyle (); +} + +int widgetStyle () { + int bits = super.widgetStyle () | OS.WS_TABSTOP; + if ((style & SWT.H_SCROLL) != 0) bits |= OS.WS_HSCROLL; + if ((style & SWT.V_SCROLL) != 0) bits |= OS.WS_VSCROLL; + return bits; +} + +TCHAR windowClass () { + return display.windowClass; +} + +int /*long*/ windowProc () { + return display.windowProc; +} + +LRESULT WM_HSCROLL (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_HSCROLL (wParam, lParam); + if (result != null) return result; + + /* + * Bug on WinCE. lParam should be NULL when the message is not sent + * by a scroll bar control, but it contains the handle to the window. + * When the message is sent by a scroll bar control, it correctly + * contains the handle to the scroll bar. The fix is to check for + * both. + */ + if (horizontalBar != null && (lParam == 0 || lParam == handle)) { + return wmScroll (horizontalBar, (state & CANVAS) != 0, handle, OS.WM_HSCROLL, wParam, lParam); + } + return result; +} + +LRESULT WM_MOUSEWHEEL (int /*long*/ wParam, int /*long*/ lParam) { + return wmScrollWheel ((state & CANVAS) != 0, wParam, lParam); +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + int /*long*/ code = callWindowProc (handle, OS.WM_SIZE, wParam, lParam); + super.WM_SIZE (wParam, lParam); + // widget may be disposed at this point + if (code == 0) return LRESULT.ZERO; + return new LRESULT (code); +} + +LRESULT WM_VSCROLL (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_VSCROLL (wParam, lParam); + if (result != null) return result; + /* + * Bug on WinCE. lParam should be NULL when the message is not sent + * by a scroll bar control, but it contains the handle to the window. + * When the message is sent by a scroll bar control, it correctly + * contains the handle to the scroll bar. The fix is to check for + * both. + */ + if (verticalBar != null && (lParam == 0 || lParam == handle)) { + return wmScroll (verticalBar, (state & CANVAS) != 0, handle, OS.WM_VSCROLL, wParam, lParam); + } + return result; +} + +LRESULT wmNCPaint (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.wmNCPaint (hwnd, wParam, lParam); + if (result != null) return result; + /* + * Bug in Windows. On XP only (not Vista), Windows sometimes + * does not redraw the bottom right corner of a window that + * has scroll bars, causing pixel corruption. The fix is to + * always draw the corner. + */ + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if (!OS.IsWinCE && OS.WIN32_VERSION < OS.VERSION (6, 0)) { + int bits1 = OS.GetWindowLong (hwnd, OS.GWL_STYLE); + if ((bits1 & (OS.WS_HSCROLL | OS.WS_VSCROLL)) != 0) { + RECT windowRect = new RECT (); + OS.GetWindowRect (hwnd, windowRect); + RECT trimRect = new RECT (); + int bits2 = OS.GetWindowLong (hwnd, OS.GWL_EXSTYLE); + OS.AdjustWindowRectEx (trimRect, bits1, false, bits2); + boolean hVisible = false, vVisible = false; + SCROLLBARINFO psbi = new SCROLLBARINFO (); + psbi.cbSize = SCROLLBARINFO.sizeof; + if (OS.GetScrollBarInfo (hwnd, OS.OBJID_HSCROLL, psbi)) { + hVisible = (psbi.rgstate [0] & OS.STATE_SYSTEM_INVISIBLE) == 0; + } + if (OS.GetScrollBarInfo (hwnd, OS.OBJID_VSCROLL, psbi)) { + vVisible = (psbi.rgstate [0] & OS.STATE_SYSTEM_INVISIBLE) == 0; + } + RECT cornerRect = new RECT (); + cornerRect.right = windowRect.right - windowRect.left - trimRect.right; + cornerRect.bottom = windowRect.bottom - windowRect.top - trimRect.bottom; + cornerRect.left = cornerRect.right - (hVisible ? OS.GetSystemMetrics (OS.SM_CXVSCROLL) : 0); + cornerRect.top = cornerRect.bottom - (vVisible ? OS.GetSystemMetrics (OS.SM_CYHSCROLL) : 0); + if (cornerRect.left != cornerRect.right && cornerRect.top != cornerRect.bottom) { + int /*long*/ hDC = OS.GetWindowDC (hwnd); + OS.FillRect (hDC, cornerRect, OS.COLOR_BTNFACE + 1); + Decorations shell = menuShell (); + if ((shell.style & SWT.RESIZE) != 0) { + int /*long*/ hwndScroll = shell.scrolledHandle (); + boolean drawGripper = hwnd == hwndScroll; + if (!drawGripper) { + RECT shellRect = new RECT (); + OS.GetClientRect (hwndScroll, shellRect); + OS.MapWindowPoints (hwndScroll, 0, shellRect, 2); + drawGripper = shellRect.right == windowRect.right && shellRect.bottom == windowRect.bottom; + } + if (drawGripper) { + OS.DrawThemeBackground (display.hScrollBarTheme(), hDC, OS.SBP_SIZEBOX, 0, cornerRect, null); + } + } + OS.ReleaseDC (hwnd, hDC); + } + } + } + } + return result; +} + +LRESULT wmScrollWheel (boolean update, int /*long*/ wParam, int /*long*/ lParam) { + int scrollRemainder = display.scrollRemainder; + LRESULT result = super.WM_MOUSEWHEEL (wParam, lParam); + if (result != null) return result; + /* + * Translate WM_MOUSEWHEEL to WM_VSCROLL or WM_HSCROLL. + */ + if (update) { + if ((wParam & (OS.MK_SHIFT | OS.MK_CONTROL)) != 0) return result; + boolean vertical = verticalBar != null && verticalBar.getEnabled (); + boolean horizontal = horizontalBar != null && horizontalBar.getEnabled (); + int msg = vertical ? OS.WM_VSCROLL : horizontal ? OS.WM_HSCROLL : 0; + if (msg == 0) return result; + int [] linesToScroll = new int [1]; + OS.SystemParametersInfo (OS.SPI_GETWHEELSCROLLLINES, 0, linesToScroll, 0); + int delta = OS.GET_WHEEL_DELTA_WPARAM (wParam); + boolean pageScroll = linesToScroll [0] == OS.WHEEL_PAGESCROLL; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) { + ScrollBar bar = vertical ? verticalBar : horizontalBar; + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_POS; + OS.GetScrollInfo (handle, bar.scrollBarType (), info); + if (vertical && !pageScroll) delta *= linesToScroll [0]; + int increment = pageScroll ? bar.getPageIncrement () : bar.getIncrement (); + info.nPos -= increment * delta / OS.WHEEL_DELTA; + OS.SetScrollInfo (handle, bar.scrollBarType (), info, true); + OS.SendMessage (handle, msg, OS.SB_THUMBPOSITION, 0); + } else { + int code = 0; + if (pageScroll) { + code = delta < 0 ? OS.SB_PAGEDOWN : OS.SB_PAGEUP; + } else { + code = delta < 0 ? OS.SB_LINEDOWN : OS.SB_LINEUP; + if (msg == OS.WM_VSCROLL) delta *= linesToScroll [0]; + } + /* Check if the delta and the remainder have the same direction (sign) */ + if ((delta ^ scrollRemainder) >= 0) delta += scrollRemainder; + int count = Math.abs (delta) / OS.WHEEL_DELTA; + for (int i=0; i<count; i++) { + OS.SendMessage (handle, msg, code, 0); + } + } + return LRESULT.ZERO; + } + + /* + * When the native widget scrolls inside WM_MOUSEWHEEL, it + * may or may not send a WM_VSCROLL or WM_HSCROLL to do the + * actual scrolling. This depends on the implementation of + * each native widget. In order to ensure that application + * code is notified when the scroll bar moves, compare the + * scroll bar position before and after the WM_MOUSEWHEEL. + * If the native control sends a WM_VSCROLL or WM_HSCROLL, + * then the application has already been notified. If not + * explicitly send the event. + */ + int vPosition = verticalBar == null ? 0 : verticalBar.getSelection (); + int hPosition = horizontalBar == null ? 0 : horizontalBar.getSelection (); + int /*long*/ code = callWindowProc (handle, OS.WM_MOUSEWHEEL, wParam, lParam); + if (verticalBar != null) { + int position = verticalBar.getSelection (); + if (position != vPosition) { + Event event = new Event (); + event.detail = position < vPosition ? SWT.PAGE_UP : SWT.PAGE_DOWN; + verticalBar.sendEvent (SWT.Selection, event); + } + } + if (horizontalBar != null) { + int position = horizontalBar.getSelection (); + if (position != hPosition) { + Event event = new Event (); + event.detail = position < hPosition ? SWT.PAGE_UP : SWT.PAGE_DOWN; + horizontalBar.sendEvent (SWT.Selection, event); + } + } + return new LRESULT (code); +} + +LRESULT wmScroll (ScrollBar bar, boolean update, int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = null; + if (update) { + int type = msg == OS.WM_HSCROLL ? OS.SB_HORZ : OS.SB_VERT; + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_TRACKPOS | OS.SIF_POS | OS.SIF_RANGE; + OS.GetScrollInfo (hwnd, type, info); + info.fMask = OS.SIF_POS; + int code = OS.LOWORD (wParam); + switch (code) { + case OS.SB_ENDSCROLL: return null; + case OS.SB_THUMBPOSITION: + case OS.SB_THUMBTRACK: + /* + * Note: On WinCE, the value in SB_THUMBPOSITION is relative to nMin. + * Same for SB_THUMBPOSITION 'except' for the very first thumb track + * message which has the actual value of nMin. This is a problem when + * nMin is not zero. + */ + info.nPos = info.nTrackPos; + break; + case OS.SB_TOP: + info.nPos = info.nMin; + break; + case OS.SB_BOTTOM: + info.nPos = info.nMax; + break; + case OS.SB_LINEDOWN: + info.nPos += bar.getIncrement (); + break; + case OS.SB_LINEUP: + int increment = bar.getIncrement (); + info.nPos = Math.max (info.nMin, info.nPos - increment); + break; + case OS.SB_PAGEDOWN: + info.nPos += bar.getPageIncrement (); + break; + case OS.SB_PAGEUP: + int pageIncrement = bar.getPageIncrement (); + info.nPos = Math.max (info.nMin, info.nPos - pageIncrement); + break; + } + OS.SetScrollInfo (hwnd, type, info, true); + } else { + int /*long*/ code = callWindowProc (hwnd, msg, wParam, lParam); + result = code == 0 ? LRESULT.ZERO : new LRESULT (code); + } + bar.wmScrollChild (wParam, lParam); + return result; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Shell.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Shell.java new file mode 100755 index 0000000000..307e203db9 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Shell.java @@ -0,0 +1,2518 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * 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 { + Menu activeMenu; + ToolTip [] toolTips; + int /*long*/ hIMC, hwndMDIClient, lpstrTip, toolTipHandle, balloonTipHandle; + int minWidth = SWT.DEFAULT, minHeight = SWT.DEFAULT; + int /*long*/ [] brushes; + boolean showWithParent, fullScreen, wasMaximized, modified, center; + String toolTitle, balloonTitle; + int /*long*/ toolIcon, balloonIcon; + int /*long*/ windowProc; + Control lastActive; + SHACTIVATEINFO psai; + static /*final*/ int /*long*/ ToolTipProc; + static final int /*long*/ DialogProc; + static final TCHAR DialogClass = new TCHAR (0, OS.IsWinCE ? "Dialog" : "#32770", true); + final static int [] SYSTEM_COLORS = { + OS.COLOR_BTNFACE, + OS.COLOR_WINDOW, + OS.COLOR_BTNTEXT, + OS.COLOR_WINDOWTEXT, + OS.COLOR_HIGHLIGHT, + OS.COLOR_SCROLLBAR, + }; + final static int BRUSHES_SIZE = 32; + static { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, DialogClass, lpWndClass); + DialogProc = lpWndClass.lpfnWndProc; + } + +/** + * 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, OS.IsWinCE ? SWT.NONE : 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); + } + this.center = parent != null && (style & SWT.SHEET) != 0; + this.style = checkStyle (parent, style); + this.parent = parent; + this.display = display; + this.handle = handle; + if (handle != 0 && !embedded) { + 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, OS.IsWinCE ? SWT.NONE : 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 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 + */ +public static Shell win32_new (Display display, int /*long*/ handle) { + return new Shell (display, null, SWT.NO_TRIM, handle, true); +} + +/** + * 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); +} + +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) { + 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; +} + +/** + * 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.Close,typedListener); + addListener (SWT.Iconify,typedListener); + addListener (SWT.Deiconify,typedListener); + addListener (SWT.Activate, typedListener); + addListener (SWT.Deactivate, typedListener); +} + +int /*long*/ balloonTipHandle () { + if (balloonTipHandle == 0) createBalloonTipHandle (); + return balloonTipHandle; +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + if (hwnd == toolTipHandle || hwnd == balloonTipHandle) { + return OS.CallWindowProc (ToolTipProc, hwnd, msg, wParam, lParam); + } + if (hwndMDIClient != 0) { + return OS.DefFrameProc (hwnd, hwndMDIClient, msg, wParam, lParam); + } + if (windowProc != 0) { + return OS.CallWindowProc (windowProc, hwnd, msg, wParam, lParam); + } + if ((style & SWT.TOOL) != 0) { + int trim = SWT.TITLE | SWT.CLOSE | SWT.MIN | SWT.MAX | SWT.BORDER | SWT.RESIZE; + if ((style & trim) == 0) return OS.DefWindowProc (hwnd, msg, wParam, lParam); + } + if (parent != null) { + switch (msg) { + case OS.WM_KILLFOCUS: + case OS.WM_SETFOCUS: + return OS.DefWindowProc (hwnd, msg, wParam, lParam); + } + return OS.CallWindowProc (DialogProc, hwnd, msg, wParam, lParam); + } + return OS.DefWindowProc (hwnd, msg, wParam, lParam); +} + +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 createBalloonTipHandle () { + balloonTipHandle = OS.CreateWindowEx ( + 0, + new TCHAR (0, OS.TOOLTIPS_CLASS, true), + null, + OS.TTS_ALWAYSTIP | OS.TTS_NOPREFIX | OS.TTS_BALLOON, + OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0, + handle, + 0, + OS.GetModuleHandle (null), + null); + if (balloonTipHandle == 0) error (SWT.ERROR_NO_HANDLES); + if (ToolTipProc == 0) { + ToolTipProc = OS.GetWindowLongPtr (balloonTipHandle, OS.GWLP_WNDPROC); + } + /* + * Feature in Windows. Despite the fact that the + * tool tip text contains \r\n, the tooltip will + * not honour the new line unless TTM_SETMAXTIPWIDTH + * is set. The fix is to set TTM_SETMAXTIPWIDTH to + * a large value. + */ + OS.SendMessage (balloonTipHandle, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF); + display.addControl (balloonTipHandle, this); + OS.SetWindowLongPtr (balloonTipHandle, OS.GWLP_WNDPROC, display.windowProc); +} + +void createHandle () { + boolean embedded = handle != 0 && (state & FOREIGN_HANDLE) == 0; + + /* + * On Windows 98 and NT, setting a window to be the + * top most window using HWND_TOPMOST can result in a + * parent dialog shell being moved behind its parent + * if the dialog has a sibling that is currently on top + * This only occurs using SetWindowPos (), not when the + * handle is created. + */ + /* + * The following code is intentionally commented. + */ +// if ((style & SWT.ON_TOP) != 0) display.lockActiveWindow = true; + if (handle == 0 || embedded) { + super.createHandle (); + } else { + state |= CANVAS; + if ((style & (SWT.H_SCROLL | SWT.V_SCROLL)) == 0) { + state |= THEME_BACKGROUND; + } + windowProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC); + } + + /* + * The following code is intentionally commented. + */ +// if ((style & SWT.ON_TOP) != 0) display.lockActiveWindow = false; + + if (!embedded) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + bits &= ~(OS.WS_OVERLAPPED | OS.WS_CAPTION); + if (!OS.IsWinCE) bits |= OS.WS_POPUP; + if ((style & SWT.TITLE) != 0) bits |= OS.WS_CAPTION; + if ((style & SWT.NO_TRIM) == 0) { + if ((style & (SWT.BORDER | SWT.RESIZE)) == 0) bits |= OS.WS_BORDER; + } + /* + * Bug in Windows. When the WS_CAPTION bits are cleared using + * SetWindowLong(), Windows does not resize the client area of + * the window to get rid of the caption until the first resize. + * The fix is to use SetWindowPos() with SWP_DRAWFRAME to force + * the frame to be redrawn and resized. + */ + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); + int flags = OS.SWP_DRAWFRAME | OS.SWP_NOMOVE | OS.SWP_NOSIZE | OS.SWP_NOZORDER | OS.SWP_NOACTIVATE; + SetWindowPos (handle, 0, 0, 0, 0, 0, flags); + if (OS.IsWinCE) _setMaximized (true); + if (OS.IsPPC) { + psai = new SHACTIVATEINFO (); + psai.cbSize = SHACTIVATEINFO.sizeof; + } + } + if (OS.IsDBLocale) { + hIMC = OS.ImmCreateContext (); + if (hIMC != 0) OS.ImmAssociateContext (handle, hIMC); + } +} + +void createToolTip (ToolTip toolTip) { + int id = 0; + if (toolTips == null) toolTips = new ToolTip [4]; + while (id < toolTips.length && toolTips [id] != null) id++; + if (id == toolTips.length) { + ToolTip [] newToolTips = new ToolTip [toolTips.length + 4]; + System.arraycopy (toolTips, 0, newToolTips, 0, toolTips.length); + toolTips = newToolTips; + } + toolTips [id] = toolTip; + toolTip.id = id + Display.ID_START; + if (OS.IsWinCE) return; + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + lpti.hwnd = handle; + lpti.uId = toolTip.id; + lpti.uFlags = OS.TTF_TRACK; + lpti.lpszText = OS.LPSTR_TEXTCALLBACK; + OS.SendMessage (toolTip.hwndToolTip (), OS.TTM_ADDTOOL, 0, lpti); +} + +void createToolTipHandle () { + toolTipHandle = OS.CreateWindowEx ( + 0, + new TCHAR (0, OS.TOOLTIPS_CLASS, true), + null, + OS.TTS_ALWAYSTIP | OS.TTS_NOPREFIX, + OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0, + handle, + 0, + OS.GetModuleHandle (null), + null); + if (toolTipHandle == 0) error (SWT.ERROR_NO_HANDLES); + if (ToolTipProc == 0) { + ToolTipProc = OS.GetWindowLongPtr (toolTipHandle, OS.GWLP_WNDPROC); + } + /* + * Feature in Windows. Despite the fact that the + * tool tip text contains \r\n, the tooltip will + * not honour the new line unless TTM_SETMAXTIPWIDTH + * is set. The fix is to set TTM_SETMAXTIPWIDTH to + * a large value. + */ + OS.SendMessage (toolTipHandle, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF); + display.addControl (toolTipHandle, this); + OS.SetWindowLongPtr (toolTipHandle, OS.GWLP_WNDPROC, display.windowProc); +} + +void deregister () { + super.deregister (); + if (toolTipHandle != 0) display.removeControl (toolTipHandle); + if (balloonTipHandle != 0) display.removeControl (balloonTipHandle); +} + +void destroyToolTip (ToolTip toolTip) { + if (toolTips == null) return; + toolTips [toolTip.id - Display.ID_START] = null; + if (OS.IsWinCE) return; + if (balloonTipHandle != 0) { + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + lpti.uId = toolTip.id; + lpti.hwnd = handle; + OS.SendMessage (balloonTipHandle, OS.TTM_DELTOOL, 0, lpti); + } + toolTip.id = -1; +} + +void destroyWidget () { + fixActiveShell (); + super.destroyWidget (); +} + +public void dispose () { + /* + * This code is intentionally commented. On some + * platforms, the owner window is repainted right + * away when a dialog window exits. This behavior + * is currently unspecified. + */ +// /* +// * Note: It is valid to attempt to dispose a widget +// * more than once. If this happens, fail silently. +// */ +// if (!isValidWidget ()) return; +// if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS); +// Display oldDisplay = display; + super.dispose (); + // widget is disposed at this point +// if (oldDisplay != null) oldDisplay.update (); +} + +void enableWidget (boolean enabled) { + if (enabled) { + state &= ~DISABLED; + } else { + state |= DISABLED; + } + if (Display.TrimEnabled) { + if (isActive ()) setItemEnabled (OS.SC_CLOSE, enabled); + } else { + OS.EnableWindow (handle, enabled); + } +} + +int /*long*/ findBrush (int /*long*/ value, int lbStyle) { + if (lbStyle == OS.BS_SOLID) { + for (int i=0; i<SYSTEM_COLORS.length; i++) { + if (value == OS.GetSysColor (SYSTEM_COLORS [i])) { + return OS.GetSysColorBrush (SYSTEM_COLORS [i]); + } + } + } + if (brushes == null) brushes = new int /*long*/ [BRUSHES_SIZE]; + LOGBRUSH logBrush = new LOGBRUSH (); + for (int i=0; i<brushes.length; i++) { + int /*long*/ hBrush = brushes [i]; + if (hBrush == 0) break; + OS.GetObject (hBrush, LOGBRUSH.sizeof, logBrush); + switch (logBrush.lbStyle) { + case OS.BS_SOLID: + if (lbStyle == OS.BS_SOLID) { + if (logBrush.lbColor == value) return hBrush; + } + break; + case OS.BS_PATTERN: + if (lbStyle == OS.BS_PATTERN) { + if (logBrush.lbHatch == value) return hBrush; + } + break; + } + } + int length = brushes.length; + int /*long*/ hBrush = brushes [--length]; + if (hBrush != 0) OS.DeleteObject (hBrush); + System.arraycopy (brushes, 0, brushes, 1, length); + switch (lbStyle) { + case OS.BS_SOLID: + hBrush = OS.CreateSolidBrush ((int)/*64*/value); + break; + case OS.BS_PATTERN: + hBrush = OS.CreatePatternBrush (value); + break; + } + return brushes [0] = hBrush; +} + +Control findBackgroundControl () { + return background != -1 || backgroundImage != null ? this : null; +} + +Cursor findCursor () { + return cursor; +} + +Control findThemeControl () { + return null; +} + +ToolTip findToolTip (int id) { + if (toolTips == null) return null; + id = id - Display.ID_START; + return 0 <= id && id < toolTips.length ? toolTips [id] : null; +} + +void fixActiveShell () { + /* + * Feature in Windows. When the active shell is disposed + * or hidden, Windows normally makes the parent shell active + * and assigns focus. This does not happen when the parent + * shell is disabled. Instead, Windows assigns focus to the + * next shell on the desktop (possibly a shell in another + * application). The fix is to activate the disabled parent + * shell before disposing or hiding the active shell. + */ + int /*long*/ hwndParent = OS.GetParent (handle); + if (hwndParent != 0 && handle == OS.GetActiveWindow ()) { + if (!OS.IsWindowEnabled (hwndParent) && OS.IsWindowVisible (hwndParent)) { + OS.SetActiveWindow (hwndParent); + } + } +} + +void fixShell (Shell newShell, Control control) { + if (this == newShell) return; + if (control == lastActive) setActiveControl (null); + String toolTipText = control.toolTipText; + if (toolTipText != null) { + control.setToolTipText (this, null); + control.setToolTipText (newShell, toolTipText); + } +} + +void fixToolTip () { + /* + * Bug in Windows. On XP, when a tooltip is + * hidden due to a time out or mouse press, + * the tooltip remains active although no + * longer visible and won't show again until + * another tooltip becomes active. If there + * is only one tooltip in the window, it will + * never show again. The fix is to remove the + * current tooltip and add it again every time + * the mouse leaves the control. + */ + if (OS.COMCTL32_MAJOR >= 6) { + if (toolTipHandle == 0) return; + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + if (OS.SendMessage (toolTipHandle, OS.TTM_GETCURRENTTOOL, 0, lpti) != 0) { + if ((lpti.uFlags & OS.TTF_IDISHWND) != 0) { + OS.SendMessage (toolTipHandle, OS.TTM_DELTOOL, 0, lpti); + OS.SendMessage (toolTipHandle, OS.TTM_ADDTOOL, 0, lpti); + } + } + } +} + +/** + * 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; + OS.SetForegroundWindow (handle); +} + +void forceResize () { + /* Do nothing */ +} + +/** + * 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 (); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) { + byte [] pbAlpha = new byte [1]; + if (OS.GetLayeredWindowAttributes (handle, null, pbAlpha, null)) { + return pbAlpha [0] & 0xFF; + } + } + return 0xFF; +} + +public Rectangle getBounds () { + checkWidget (); + if (!OS.IsWinCE) { + if (OS.IsIconic (handle)) return super.getBounds (); + } + RECT rect = new RECT (); + OS.GetWindowRect (handle, rect); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + return new Rectangle (rect.left, rect.top, width, height); +} + +ToolTip getCurrentToolTip () { + if (toolTipHandle != 0) { + ToolTip tip = getCurrentToolTip (toolTipHandle); + if (tip != null) return tip; + } + if (balloonTipHandle != 0) { + ToolTip tip = getCurrentToolTip (balloonTipHandle); + if (tip != null) return tip; + } + return null; +} + +ToolTip getCurrentToolTip (int /*long*/ hwndToolTip) { + if (hwndToolTip == 0) return null; + if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, 0) != 0) { + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) != 0) { + if ((lpti.uFlags & OS.TTF_IDISHWND) == 0) return findToolTip ((int)/*64*/lpti.uId); + } + } + return null; +} + +public boolean getEnabled () { + checkWidget (); + return (state & DISABLED) == 0; +} + +/** + * 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 (); + if (!OS.IsDBLocale) return 0; + int /*long*/ hIMC = OS.ImmGetContext (handle); + int [] lpfdwConversion = new int [1], lpfdwSentence = new int [1]; + boolean open = OS.ImmGetOpenStatus (hIMC); + if (open) open = OS.ImmGetConversionStatus (hIMC, lpfdwConversion, lpfdwSentence); + OS.ImmReleaseContext (handle, hIMC); + if (!open) return SWT.NONE; + int result = 0; + if ((lpfdwConversion [0] & OS.IME_CMODE_ROMAN) != 0) result |= SWT.ROMAN; + if ((lpfdwConversion [0] & OS.IME_CMODE_FULLSHAPE) != 0) result |= SWT.DBCS; + if ((lpfdwConversion [0] & OS.IME_CMODE_KATAKANA) != 0) return result | SWT.PHONETIC; + if ((lpfdwConversion [0] & OS.IME_CMODE_NATIVE) != 0) return result | SWT.NATIVE; + return result | SWT.ALPHA; +} + +public Point getLocation () { + checkWidget (); + if (!OS.IsWinCE) { + if (OS.IsIconic (handle)) { + return super.getLocation (); + } + } + RECT rect = new RECT (); + OS.GetWindowRect (handle, rect); + return new Point (rect.left, rect.top); +} + +public boolean getMaximized () { + checkWidget (); + return !fullScreen && super.getMaximized (); +} + +/** + * 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 (); + int width = Math.max (0, minWidth); + int trim = SWT.TITLE | SWT.CLOSE | SWT.MIN | SWT.MAX; + if ((style & SWT.NO_TRIM) == 0 && (style & trim) != 0) { + width = Math.max (width, OS.GetSystemMetrics (OS.SM_CXMINTRACK)); + } + int height = Math.max (0, minHeight); + if ((style & SWT.NO_TRIM) == 0 && (style & trim) != 0) { + if ((style & SWT.RESIZE) != 0) { + height = Math.max (height, OS.GetSystemMetrics (OS.SM_CYMINTRACK)); + } else { + RECT rect = new RECT (); + int bits1 = OS.GetWindowLong (handle, OS.GWL_STYLE); + int bits2 = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); + OS.AdjustWindowRectEx (rect, bits1, false, bits2); + height = Math.max (height, rect.bottom - rect.top); + } + } + return new Point (width, height); +} + +/** + * 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 modified; +} + +/** + * 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; +} + +public Point getSize () { + checkWidget (); + if (!OS.IsWinCE) { + if (OS.IsIconic (handle)) return super.getSize (); + } + RECT rect = new RECT (); + OS.GetWindowRect (handle, rect); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + return new Point (width, height); +} + +/** + * 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; +} + +Composite findDeferredControl () { + return layoutCount > 0 ? this : null; +} + +public boolean isEnabled () { + checkWidget (); + return getEnabled (); +} + +public boolean isVisible () { + checkWidget (); + return getVisible (); +} + +int /*long*/ hwndMDIClient () { + if (hwndMDIClient == 0) { + int widgetStyle = OS.MDIS_ALLCHILDSTYLES | OS.WS_CHILD | OS.WS_CLIPCHILDREN | OS.WS_CLIPSIBLINGS; + hwndMDIClient = OS.CreateWindowEx ( + 0, + new TCHAR (0, "MDICLIENT", true), + null, + widgetStyle, + 0, 0, 0, 0, + handle, + 0, + OS.GetModuleHandle (null), + new CREATESTRUCT ()); +// OS.ShowWindow (hwndMDIClient, OS.SW_SHOW); + } + return hwndMDIClient; +} + +/** + * 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 (); + STARTUPINFO lpStartUpInfo = Display.lpStartupInfo; + if (lpStartUpInfo == null || (lpStartUpInfo.dwFlags & OS.STARTF_USESHOWWINDOW) == 0) { + bringToTop (); + if (isDisposed ()) return; + } + /* + * Feature on WinCE PPC. A new application becomes + * the foreground application only if it has at least + * one visible window before the event loop is started. + * The workaround is to explicitly force the shell to + * be the foreground window. + */ + if (OS.IsWinCE) OS.SetForegroundWindow (handle); + OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0); + setVisible (true); + if (isDisposed ()) return; + /* + * Bug in Windows XP. Despite the fact that an icon has been + * set for a window, the task bar displays the wrong icon the + * first time the window is made visible with ShowWindow() after + * a call to BringToTop(), when a long time elapses between the + * ShowWindow() and the time the event queue is read. The icon + * in the window trimming is correct but the one in the task + * bar does not get updated. The fix is to call PeekMessage() + * with the flag PM_NOREMOVE and PM_QS_SENDMESSAGE to respond + * to a cross thread WM_GETICON. + * + * NOTE: This allows other cross thread messages to be delivered, + * most notably WM_ACTIVATE. + */ + MSG msg = new MSG (); + int flags = OS.PM_NOREMOVE | OS.PM_NOYIELD | OS.PM_QS_SENDMESSAGE; + OS.PeekMessage (msg, 0, 0, 0, flags); + if (!restoreFocus () && !traverseGroup (true)) 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 (toolTipHandle != 0) display.addControl (toolTipHandle, this); + if (balloonTipHandle != 0) display.addControl (balloonTipHandle, this); +} + +void releaseBrushes () { + if (brushes != null) { + for (int i=0; i<brushes.length; i++) { + if (brushes [i] != 0) OS.DeleteObject (brushes [i]); + } + } + brushes = null; +} + +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.release (false); + } + } + if (toolTips != null) { + for (int i=0; i<toolTips.length; i++) { + ToolTip toolTip = toolTips [i]; + if (toolTip != null && !toolTip.isDisposed ()) { + toolTip.release (false); + } + } + } + toolTips = null; + super.releaseChildren (destroy); +} + +void releaseHandle () { + super.releaseHandle (); + hwndMDIClient = 0; +} + +void releaseParent () { + /* Do nothing */ +} + +void releaseWidget () { + super.releaseWidget (); + releaseBrushes (); + activeMenu = null; + display.clearModal (this); + if (lpstrTip != 0) { + int /*long*/ hHeap = OS.GetProcessHeap (); + OS.HeapFree (hHeap, 0, lpstrTip); + } + lpstrTip = 0; + toolTipHandle = balloonTipHandle = 0; + if (OS.IsDBLocale) { + if (hIMC != 0) OS.ImmDestroyContext (hIMC); + } + lastActive = null; + toolTitle = balloonTitle = null; +} + +void removeMenu (Menu menu) { + super.removeMenu (menu); + if (menu == activeMenu) activeMenu = 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.Close, listener); + eventTable.unhook (SWT.Iconify,listener); + eventTable.unhook (SWT.Deiconify,listener); + eventTable.unhook (SWT.Activate, listener); + eventTable.unhook (SWT.Deactivate, listener); +} + +LRESULT selectPalette (int /*long*/ hPalette) { + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ hOld = OS.SelectPalette (hDC, hPalette, false); + int result = OS.RealizePalette (hDC); + if (result > 0) { + OS.InvalidateRect (handle, null, true); + } else { + OS.SelectPalette (hDC, hOld, true); + OS.RealizePalette (hDC); + } + OS.ReleaseDC (handle, hDC); + return (result > 0) ? LRESULT.ONE : LRESULT.ZERO; +} + +boolean sendKeyEvent (int type, int msg, int /*long*/ wParam, int /*long*/ lParam, Event event) { + if (!isEnabled () || !isActive ()) return false; + return super.sendKeyEvent (type, msg, wParam, lParam, event); +} + +/** + * 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 () { + checkWidget (); + if (!isVisible ()) return; + bringToTop (); + // widget could be disposed at this point +} + +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) { + checkWidget (); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) { + alpha &= 0xFF; + int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); + if (alpha == 0xFF) { + OS.SetWindowLong (handle, OS.GWL_EXSTYLE, bits & ~OS.WS_EX_LAYERED); + int flags = OS.RDW_ERASE | OS.RDW_INVALIDATE | OS.RDW_FRAME | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, null, 0, flags); + } else { + OS.SetWindowLong (handle, OS.GWL_EXSTYLE, bits | OS.WS_EX_LAYERED); + OS.SetLayeredWindowAttributes (handle, 0, (byte)alpha, OS.LWA_ALPHA); + } + } +} + +void setBounds (int x, int y, int width, int height, int flags, boolean defer) { + if (fullScreen) setFullScreen (false); + /* + * Bug in Windows. When a window has alpha and + * SetWindowPos() is called with SWP_DRAWFRAME, + * the contents of the window are copied rather + * than allowing the windows underneath to draw. + * This causes pixel corruption. The fix is to + * clear the SWP_DRAWFRAME bits. + */ + int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); + if ((bits & OS.WS_EX_LAYERED) != 0) { + flags &= ~OS.SWP_DRAWFRAME; + } + super.setBounds (x, y, width, height, flags, false); +} + +public void setEnabled (boolean enabled) { + checkWidget (); + if (((state & DISABLED) == 0) == enabled) return; + super.setEnabled (enabled); + if (enabled && handle == OS.GetActiveWindow ()) { + if (!restoreFocus ()) traverseGroup (true); + } +} + +/** + * 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; + int stateFlags = fullScreen ? OS.SW_SHOWMAXIMIZED : OS.SW_RESTORE; + int styleFlags = OS.GetWindowLong (handle, OS.GWL_STYLE); + int mask = SWT.TITLE | SWT.CLOSE | SWT.MIN | SWT.MAX; + if ((style & mask) != 0) { + if (fullScreen) { + styleFlags = styleFlags & ~OS.WS_CAPTION; + } else { + styleFlags = styleFlags | OS.WS_CAPTION; + } + } + if (fullScreen) wasMaximized = getMaximized (); + boolean visible = isVisible (); + OS.SetWindowLong (handle, OS.GWL_STYLE, styleFlags); + if (wasMaximized) { + OS.ShowWindow (handle, OS.SW_HIDE); + stateFlags = OS.SW_SHOWMAXIMIZED; + } + if (visible) OS.ShowWindow (handle, stateFlags); + OS.UpdateWindow (handle); + this.fullScreen = fullScreen; +} + +/** + * 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 (); + if (!OS.IsDBLocale) return; + boolean imeOn = mode != SWT.NONE; + int /*long*/ hIMC = OS.ImmGetContext (handle); + OS.ImmSetOpenStatus (hIMC, imeOn); + if (imeOn) { + int [] lpfdwConversion = new int [1], lpfdwSentence = new int [1]; + if (OS.ImmGetConversionStatus (hIMC, lpfdwConversion, lpfdwSentence)) { + int newBits = 0; + int oldBits = OS.IME_CMODE_NATIVE | OS.IME_CMODE_KATAKANA; + if ((mode & SWT.PHONETIC) != 0) { + newBits = OS.IME_CMODE_KATAKANA | OS.IME_CMODE_NATIVE; + oldBits = 0; + } else { + if ((mode & SWT.NATIVE) != 0) { + newBits = OS.IME_CMODE_NATIVE; + oldBits = OS.IME_CMODE_KATAKANA; + } + } + if ((mode & (SWT.DBCS | SWT.NATIVE)) != 0) { + newBits |= OS.IME_CMODE_FULLSHAPE; + } else { + oldBits |= OS.IME_CMODE_FULLSHAPE; + } + if ((mode & SWT.ROMAN) != 0) { + newBits |= OS.IME_CMODE_ROMAN; + } else { + oldBits |= OS.IME_CMODE_ROMAN; + } + lpfdwConversion [0] |= newBits; + lpfdwConversion [0] &= ~oldBits; + OS.ImmSetConversionStatus (hIMC, lpfdwConversion [0], lpfdwSentence [0]); + } + } + OS.ImmReleaseContext (handle, hIMC); +} + +/** + * 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 (); + int widthLimit = 0, heightLimit = 0; + int trim = SWT.TITLE | SWT.CLOSE | SWT.MIN | SWT.MAX; + if ((style & SWT.NO_TRIM) == 0 && (style & trim) != 0) { + widthLimit = OS.GetSystemMetrics (OS.SM_CXMINTRACK); + if ((style & SWT.RESIZE) != 0) { + heightLimit = OS.GetSystemMetrics (OS.SM_CYMINTRACK); + } else { + RECT rect = new RECT (); + int bits1 = OS.GetWindowLong (handle, OS.GWL_STYLE); + int bits2 = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); + OS.AdjustWindowRectEx (rect, bits1, false, bits2); + heightLimit = rect.bottom - rect.top; + } + } + minWidth = Math.max (widthLimit, width); + minHeight = Math.max (heightLimit, height); + Point size = getSize (); + int newWidth = Math.max (size.x, minWidth); + int newHeight = Math.max (size.y, minHeight); + if (minWidth <= widthLimit) minWidth = SWT.DEFAULT; + if (minHeight <= heightLimit) minHeight = SWT.DEFAULT; + if (newWidth != size.x || newHeight != size.y) setSize (newWidth, newHeight); +} + +/** + * 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 (); + this.modified = modified; +} + +void setItemEnabled (int cmd, boolean enabled) { + int /*long*/ hMenu = OS.GetSystemMenu (handle, false); + if (hMenu == 0) return; + int flags = OS.MF_ENABLED; + if (!enabled) flags = OS.MF_DISABLED | OS.MF_GRAYED; + OS.EnableMenuItem (hMenu, cmd, OS.MF_BYCOMMAND | flags); +} + +void setParent () { + /* Do nothing. Not necessary for Shells */ +} + +/** + * 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; + super.setRegion (region); +} + +void setToolTipText (int /*long*/ hwnd, String text) { + if (OS.IsWinCE) return; + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + lpti.hwnd = handle; + lpti.uId = hwnd; + int /*long*/ hwndToolTip = toolTipHandle (); + if (text == null) { + OS.SendMessage (hwndToolTip, OS.TTM_DELTOOL, 0, lpti); + } else { + if (OS.SendMessage (hwndToolTip, OS.TTM_GETTOOLINFO, 0, lpti) != 0) { + OS.SendMessage (hwndToolTip, OS.TTM_UPDATE, 0, 0); + } else { + lpti.uFlags = OS.TTF_IDISHWND | OS.TTF_SUBCLASS; + lpti.lpszText = OS.LPSTR_TEXTCALLBACK; + OS.SendMessage (hwndToolTip, OS.TTM_ADDTOOL, 0, lpti); + } + } +} + +void setToolTipText (NMTTDISPINFO lpnmtdi, byte [] buffer) { + /* + * Ensure that the current position of the mouse + * is inside the client area of the shell. This + * prevents tool tips from popping up over the + * shell trimmings. + */ + if (!hasCursor ()) return; + int /*long*/ hHeap = OS.GetProcessHeap (); + if (lpstrTip != 0) OS.HeapFree (hHeap, 0, lpstrTip); + int byteCount = buffer.length; + lpstrTip = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (lpstrTip, buffer, byteCount); + lpnmtdi.lpszText = lpstrTip; +} + +void setToolTipText (NMTTDISPINFO lpnmtdi, char [] buffer) { + /* + * Ensure that the current position of the mouse + * is inside the client area of the shell. This + * prevents tool tips from popping up over the + * shell trimmings. + */ + if (!hasCursor ()) return; + int /*long*/ hHeap = OS.GetProcessHeap (); + if (lpstrTip != 0) OS.HeapFree (hHeap, 0, lpstrTip); + int byteCount = buffer.length * 2; + lpstrTip = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (lpstrTip, buffer, byteCount); + lpnmtdi.lpszText = lpstrTip; +} + +void setToolTipTitle (int /*long*/ hwndToolTip, String text, int icon) { + /* + * Bug in Windows. For some reason, when TTM_SETTITLE + * is used to set the title of a tool tip, Windows leaks + * GDI objects. This happens even when TTM_SETTITLE is + * called with TTI_NONE and NULL. The documentation + * states that Windows copies the icon and that the + * programmer must free the copy but does not provide + * API to get the icon. For example, when TTM_SETTITLE + * is called with ICON_ERROR, when TTM_GETTITLE is used + * to query the title and the icon, the uTitleBitmap + * field in the TTGETTITLE struct is zero. The fix + * is to remember these values, only set them when then + * change and leak less. + * + * NOTE: This only happens on Vista. + */ + if (hwndToolTip != toolTipHandle && hwndToolTip != balloonTipHandle) { + return; + } + if (hwndToolTip == toolTipHandle) { + if (text == toolTitle || (toolTitle != null && toolTitle.equals (text))) { + if (icon == toolIcon) return; + } + toolTitle = text; + toolIcon = icon; + } else { + if (hwndToolTip == balloonTipHandle) { + if (text == balloonTitle || (balloonTitle != null && balloonTitle.equals (text))) { + if (icon == toolIcon) return; + } + balloonTitle = text; + balloonIcon = icon; + } + } + if (text != null) { + TCHAR pszTitle = new TCHAR (getCodePage (), text, true); + OS.SendMessage (hwndToolTip, OS.TTM_SETTITLE, icon, pszTitle); + } else { + OS.SendMessage (hwndToolTip, OS.TTM_SETTITLE, 0, 0); + } +} + +public void setVisible (boolean visible) { + checkWidget (); + /* + * Feature in Windows. When ShowWindow() is called used to hide + * a window, Windows attempts to give focus to the parent. If the + * parent is disabled by EnableWindow(), focus is assigned to + * another windows on the desktop. This means that if you hide + * a modal window before the parent is enabled, the parent will + * not come to the front. The fix is to change the modal state + * before hiding or showing a window so that this does not occur. + */ + int mask = SWT.PRIMARY_MODAL | SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL; + if ((style & mask) != 0) { + if (visible) { + display.setModalShell (this); + if ((style & (SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL)) != 0) { + display.setModalDialog (null); + } + Control control = display._getFocusControl (); + if (control != null && !control.isActive ()) { + bringToTop (); + if (isDisposed ()) return; + } + int /*long*/ hwndShell = OS.GetActiveWindow (); + if (hwndShell == 0) { + if (parent != null) hwndShell = parent.handle; + } + if (hwndShell != 0) { + OS.SendMessage (hwndShell, OS.WM_CANCELMODE, 0, 0); + } + OS.ReleaseCapture (); + } else { + display.clearModal (this); + } + } else { + updateModal (); + } + + /* + * Bug in Windows. Calling ShowOwnedPopups() to hide the + * child windows of a hidden window causes the application + * to be deactivated. The fix is to call ShowOwnedPopups() + * to hide children before hiding the parent. + */ + if (showWithParent && !visible) { + if (!OS.IsWinCE) OS.ShowOwnedPopups (handle, false); + } + if (!visible) fixActiveShell (); + if (visible && center && !moved) { + center (); + if (isDisposed ()) return; + } + super.setVisible (visible); + if (isDisposed ()) return; + if (showWithParent != visible) { + showWithParent = visible; + if (visible) { + if (!OS.IsWinCE) OS.ShowOwnedPopups (handle, true); + } + } + + /* Make the foreign window parent appear in the task bar */ + if (visible) { + if (parent != null && (parent.state & FOREIGN_HANDLE) != 0) { + int /*long*/ hwndParent = parent.handle; + int style = OS.GetWindowLong (hwndParent, OS.GWL_EXSTYLE); + if ((style & OS.WS_EX_TOOLWINDOW) != 0) { + OS.SetWindowLong (hwndParent, OS.GWL_EXSTYLE, style & ~OS.WS_EX_TOOLWINDOW); + /* + * Bug in Windows. The window does not show in the task bar when + * WS_EX_TOOLWINDOW is removed after the window has already been shown. + * The fix is to hide and shown the shell. + */ + OS.ShowWindow (hwndParent, OS.SW_HIDE); + OS.ShowWindow (hwndParent, OS.SW_RESTORE); + } + } + } +} + +void subclass () { + super.subclass (); + if (ToolTipProc != 0) { + int /*long*/ newProc = display.windowProc; + if (toolTipHandle != 0) { + OS.SetWindowLongPtr (toolTipHandle, OS.GWLP_WNDPROC, newProc); + } + if (balloonTipHandle != 0) { + OS.SetWindowLongPtr (balloonTipHandle, OS.GWLP_WNDPROC, newProc); + } + } +} + +int /*long*/ toolTipHandle () { + if (toolTipHandle == 0) createToolTipHandle (); + return toolTipHandle; +} + +boolean translateAccelerator (MSG msg) { + if (!isEnabled () || !isActive ()) return false; + if (menuBar != null && !menuBar.isEnabled ()) return false; + return translateMDIAccelerator (msg) || translateMenuAccelerator (msg); +} + +boolean traverseEscape () { + if (parent == null) return false; + if (!isVisible () || !isEnabled ()) return false; + close (); + return true; +} + +void unsubclass () { + super.unsubclass (); + if (ToolTipProc != 0) { + if (toolTipHandle != 0) { + OS.SetWindowLongPtr (toolTipHandle, OS.GWLP_WNDPROC, ToolTipProc); + } + if (toolTipHandle != 0) { + OS.SetWindowLongPtr (toolTipHandle, OS.GWLP_WNDPROC, ToolTipProc); + } + } +} + +void updateModal () { + if (Display.TrimEnabled) { + setItemEnabled (OS.SC_CLOSE, isActive ()); + } else { + OS.EnableWindow (handle, isActive ()); + } +} + +CREATESTRUCT widgetCreateStruct () { + return null; +} + +int /*long*/ widgetParent () { + if (handle != 0) return handle; + return parent != null ? parent.handle : 0; +} + +int widgetExtStyle () { + int bits = super.widgetExtStyle () & ~OS.WS_EX_MDICHILD; + if ((style & SWT.TOOL) != 0) bits |= OS.WS_EX_TOOLWINDOW; + + /* + * Feature in Windows. When a window that does not have a parent + * is created, it is automatically added to the Windows Task Bar, + * even when it has no title. The fix is to use WS_EX_TOOLWINDOW + * which does not cause the window to appear in the Task Bar. + */ + if (!OS.IsWinCE) { + if (parent == null) { + if ((style & SWT.ON_TOP) != 0) { + int trim = SWT.TITLE | SWT.CLOSE | SWT.MIN | SWT.MAX; + if ((style & SWT.NO_TRIM) != 0 || (style & trim) == 0) { + bits |= OS.WS_EX_TOOLWINDOW; + } + } + } + } + + /* + * Bug in Windows 98 and NT. Creating a window with the + * WS_EX_TOPMOST extended style can result in a dialog shell + * being moved behind its parent. The exact case where this + * happens is a shell with two dialog shell children where + * each dialog child has another hidden dialog child with + * the WS_EX_TOPMOST extended style. Clicking on either of + * the visible dialogs causes them to become active but move + * to the back, behind the parent shell. The fix is to + * disallow the WS_EX_TOPMOST extended style on Windows 98 + * and NT. + */ + if (parent != null) { + if (OS.IsWin95) return bits; + if (OS.WIN32_VERSION < OS.VERSION (4, 10)) { + return bits; + } + } + if ((style & SWT.ON_TOP) != 0) bits |= OS.WS_EX_TOPMOST; + return bits; +} + +TCHAR windowClass () { + if (OS.IsSP) return DialogClass; + if ((style & SWT.TOOL) != 0) { + int trim = SWT.TITLE | SWT.CLOSE | SWT.MIN | SWT.MAX | SWT.BORDER | SWT.RESIZE; + if ((style & trim) == 0) return display.windowShadowClass; + } + return parent != null ? DialogClass : super.windowClass (); +} + +int /*long*/ windowProc () { + if (windowProc != 0) return windowProc; + if (OS.IsSP) return DialogProc; + if ((style & SWT.TOOL) != 0) { + int trim = SWT.TITLE | SWT.CLOSE | SWT.MIN | SWT.MAX | SWT.BORDER | SWT.RESIZE; + if ((style & trim) == 0) return super.windowProc (); + } + return parent != null ? DialogProc : super.windowProc (); +} + +int /*long*/ windowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + if (hwnd == toolTipHandle || hwnd == balloonTipHandle) { + switch (msg) { + case OS.WM_TIMER: { + if (wParam != ToolTip.TIMER_ID) break; + ToolTip tip = getCurrentToolTip (hwnd); + if (tip != null && tip.autoHide) { + tip.setVisible (false); + } + break; + } + case OS.WM_LBUTTONDOWN: { + ToolTip tip = getCurrentToolTip (hwnd); + if (tip != null) { + tip.setVisible (false); + tip.postEvent (SWT.Selection); + } + break; + } + } + return callWindowProc (hwnd, msg, wParam, lParam); + } + return super.windowProc (hwnd, msg, wParam, lParam); +} + +int widgetStyle () { + int bits = super.widgetStyle (); + if (handle != 0) return bits | OS.WS_CHILD; + bits &= ~OS.WS_CHILD; + /* + * Feature in WinCE. Calling CreateWindowEx () with WS_OVERLAPPED + * and a parent window causes the new window to become a WS_CHILD of + * the parent instead of a dialog child. The fix is to use WS_POPUP + * for a window with a parent. + * + * Feature in WinCE PPC. A window without a parent with WS_POPUP + * always shows on top of the Pocket PC 'Today Screen'. The fix + * is to not set WS_POPUP for a window without a parent on WinCE + * devices. + * + * NOTE: WS_POPUP causes CreateWindowEx () to ignore CW_USEDEFAULT + * and causes the default window location and size to be zero. + */ + if (OS.IsWinCE) { + if (OS.IsSP) return bits | OS.WS_POPUP; + return parent == null ? bits : bits | OS.WS_POPUP; + } + + /* + * Use WS_OVERLAPPED for all windows, either dialog or top level + * so that CreateWindowEx () will respect CW_USEDEFAULT and set + * the default window location and size. + * + * NOTE: When a WS_OVERLAPPED window is created, Windows gives + * the new window WS_CAPTION style bits. These two constants are + * as follows: + * + * WS_OVERLAPPED = 0 + * WS_CAPTION = WS_BORDER | WS_DLGFRAME + * + */ + return bits | OS.WS_OVERLAPPED | OS.WS_CAPTION; +} + +LRESULT WM_ACTIVATE (int /*long*/ wParam, int /*long*/ lParam) { + if (OS.IsPPC) { + /* + * Note: this does not work when we get WM_ACTIVATE prior + * to adding a listener. + */ + if (hooks (SWT.HardKeyDown) || hooks (SWT.HardKeyUp)) { + int fActive = OS.LOWORD (wParam); + int /*long*/ hwnd = fActive != 0 ? handle : 0; + for (int bVk=OS.VK_APP1; bVk<=OS.VK_APP6; bVk++) { + OS.SHSetAppKeyWndAssoc ((byte) bVk, hwnd); + } + } + /* Restore SIP state when window is activated */ + if (OS.LOWORD (wParam) != 0) { + OS.SHSipPreference (handle, psai.fSipUp == 0 ? OS.SIP_DOWN : OS.SIP_UP); + } + } + + /* + * Bug in Windows XP. When a Shell is deactivated, the + * IME composition window does not go away. This causes + * repaint issues. The fix is to commit the composition + * string. + */ + if (OS.WIN32_VERSION >= OS.VERSION (5, 1)) { + if (OS.LOWORD (wParam) == 0 && OS.IsDBLocale && hIMC != 0) { + if (OS.ImmGetOpenStatus (hIMC)) { + OS.ImmNotifyIME (hIMC, OS.NI_COMPOSITIONSTR, OS.CPS_COMPLETE, 0); + } + } + } + + /* Process WM_ACTIVATE */ + LRESULT result = super.WM_ACTIVATE (wParam, lParam); + if (OS.LOWORD (wParam) == 0) { + if (lParam == 0 || (lParam != toolTipHandle && lParam != balloonTipHandle)) { + ToolTip tip = getCurrentToolTip (); + if (tip != null) tip.setVisible (false); + } + } + return parent != null ? LRESULT.ZERO : result; +} + +LRESULT WM_COMMAND (int /*long*/ wParam, int /*long*/ lParam) { + if (OS.IsPPC) { + /* + * Note in WinCE PPC: Close the Shell when the "Done Button" has + * been pressed. lParam is either 0 (PocketPC 2002) or the handle + * to the Shell (PocketPC). + */ + int loWord = OS.LOWORD (wParam); + if (loWord == OS.IDOK && (lParam == 0 || lParam == handle)) { + OS.PostMessage (handle, OS.WM_CLOSE, 0, 0); + return LRESULT.ZERO; + } + } + /* + * Feature in Windows. On PPC, the menu is not actually an HMENU. + * By observation, it is a tool bar that is configured to look like + * a menu. Therefore, when the PPC menu sends WM_COMMAND messages, + * lParam is not zero because the WM_COMMAND was not sent from a menu. + * Sub menu item events originate from the menu bar. Top menu items + * events originate from a tool bar. The fix is to detect the source + * of the WM_COMMAND and set lParam to zero to pretend that the message + * came from a real Windows menu, not a tool bar. + */ + if (OS.IsPPC || OS.IsSP) { + if (menuBar != null) { + int /*long*/ hwndCB = menuBar.hwndCB; + if (lParam != 0 && hwndCB != 0) { + if (lParam == hwndCB) { + return super.WM_COMMAND (wParam, 0); + } else { + int /*long*/ hwndChild = OS.GetWindow (hwndCB, OS.GW_CHILD); + if (lParam == hwndChild) return super.WM_COMMAND (wParam, 0); + } + } + } + } + return super.WM_COMMAND (wParam, lParam); +} + +LRESULT WM_DESTROY (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_DESTROY (wParam, lParam); + /* + * When the shell is a WS_CHILD window of a non-SWT + * window, the destroy code does not get called because + * the non-SWT window does not call dispose (). Instead, + * the destroy code is called here in WM_DESTROY. + */ + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.WS_CHILD) != 0) { + releaseParent (); + release (false); + } + return result; +} + +LRESULT WM_ERASEBKGND (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_ERASEBKGND (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. When a shell is resized by dragging + * the resize handles, Windows temporarily fills in black + * rectangles where the new contents of the shell should + * draw. The fix is to always draw the background of shells. + * + * NOTE: This only happens on Vista. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + drawBackground (wParam); + return LRESULT.ONE; + } + return result; +} + +LRESULT WM_ENTERIDLE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_ENTERIDLE (wParam, lParam); + if (result != null) return result; + if (display.runMessages) { + if (display.runAsyncMessages (false)) display.wakeThread (); + } + return result; +} + +LRESULT WM_GETMINMAXINFO (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_GETMINMAXINFO (wParam, lParam); + if (result != null) return result; + if (minWidth != SWT.DEFAULT || minHeight != SWT.DEFAULT) { + MINMAXINFO info = new MINMAXINFO (); + OS.MoveMemory (info, lParam, MINMAXINFO.sizeof); + if (minWidth != SWT.DEFAULT) info.ptMinTrackSize_x = minWidth; + if (minHeight != SWT.DEFAULT) info.ptMinTrackSize_y = minHeight; + OS.MoveMemory (lParam, info, MINMAXINFO.sizeof); + return LRESULT.ZERO; + } + return result; +} + +LRESULT WM_MOUSEACTIVATE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_MOUSEACTIVATE (wParam, lParam); + if (result != null) return result; + + /* + * Check for WM_MOUSEACTIVATE when an MDI shell is active + * and stop the normal shell activation but allow the mouse + * down to be delivered. + */ + int hittest = (short) OS.LOWORD (lParam); + switch (hittest) { + case OS.HTERROR: + case OS.HTTRANSPARENT: + case OS.HTNOWHERE: + break; + default: { + Control control = display._getFocusControl (); + if (control != null) { + Decorations decorations = control.menuShell (); + if (decorations.getShell () == this && decorations != this) { + display.ignoreRestoreFocus = true; + display.lastHittest = hittest; + display.lastHittestControl = null; + if (hittest == OS.HTMENU || hittest == OS.HTSYSMENU) { + display.lastHittestControl = control; + return null; + } + if (OS.IsWin95 && hittest == OS.HTCAPTION) { + display.lastHittestControl = control; + } + return new LRESULT (OS.MA_NOACTIVATE); + } + } + } + } + if (hittest == OS.HTMENU) return null; + + /* + * Get the current location of the cursor, + * not the location of the cursor when the + * WM_MOUSEACTIVATE was generated. This is + * strictly incorrect but is necessary in + * order to support Activate and Deactivate + * events for embedded widgets that have + * their own event loop. In that case, the + * cursor location reported by GetMessagePos() + * is the one for our event loop, not the + * embedded widget's event loop. + */ + POINT pt = new POINT (); + if (!OS.GetCursorPos (pt)) { + int pos = OS.GetMessagePos (); + OS.POINTSTOPOINT (pt, pos); + } + int /*long*/ hwnd = OS.WindowFromPoint (pt); + if (hwnd == 0) return null; + Control control = display.findControl (hwnd); + + /* + * When a shell is created with SWT.ON_TOP and SWT.NO_FOCUS, + * do not activate the shell when the user clicks on the + * the client area or on the border or a control within the + * shell that does not take focus. + */ + if (control != null && (control.state & CANVAS) != 0) { + if ((control.style & SWT.NO_FOCUS) != 0) { + int bits = SWT.ON_TOP | SWT.NO_FOCUS; + if ((style & bits) == bits) { + if (hittest == OS.HTBORDER || hittest == OS.HTCLIENT) { + return new LRESULT (OS.MA_NOACTIVATE); + } + } + } + } + + int /*long*/ code = callWindowProc (handle, OS.WM_MOUSEACTIVATE, wParam, lParam); + setActiveControl (control); + return new LRESULT (code); +} + +LRESULT WM_MOVE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_MOVE (wParam, lParam); + if (result != null) return result; + ToolTip tip = getCurrentToolTip (); + if (tip != null) tip.setVisible (false); + return result; +} + +LRESULT WM_NCHITTEST (int /*long*/ wParam, int /*long*/ lParam) { + if (!OS.IsWindowEnabled (handle)) return null; + if (!isEnabled () || !isActive ()) { + if (!Display.TrimEnabled) return new LRESULT (OS.HTNOWHERE); + int /*long*/ hittest = callWindowProc (handle, OS.WM_NCHITTEST, wParam, lParam); + if (hittest == OS.HTCLIENT || hittest == OS.HTMENU) hittest = OS.HTBORDER; + return new LRESULT (hittest); + } + if (menuBar != null && !menuBar.getEnabled ()) { + int /*long*/ hittest = callWindowProc (handle, OS.WM_NCHITTEST, wParam, lParam); + if (hittest == OS.HTMENU) hittest = OS.HTBORDER; + return new LRESULT (hittest); + } + return null; +} + +LRESULT WM_NCLBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_NCLBUTTONDOWN (wParam, lParam); + if (result != null) return result; + /* + * When the normal activation was interrupted in WM_MOUSEACTIVATE + * because the active shell was an MDI shell, set the active window + * to the top level shell but lock the active window and stop focus + * changes. This allows the user to interact the top level shell + * in the normal manner. + */ + if (!display.ignoreRestoreFocus) return result; + Display display = this.display; + int /*long*/ hwndActive = 0; + boolean fixActive = OS.IsWin95 && display.lastHittest == OS.HTCAPTION; + if (fixActive) hwndActive = OS.SetActiveWindow (handle); + display.lockActiveWindow = true; + int /*long*/ code = callWindowProc (handle, OS.WM_NCLBUTTONDOWN, wParam, lParam); + display.lockActiveWindow = false; + if (fixActive) OS.SetActiveWindow (hwndActive); + Control focusControl = display.lastHittestControl; + if (focusControl != null && !focusControl.isDisposed ()) { + focusControl.setFocus (); + } + display.lastHittestControl = null; + display.ignoreRestoreFocus = false; + return new LRESULT (code); +} + +LRESULT WM_PALETTECHANGED (int /*long*/ wParam, int /*long*/ lParam) { + if (wParam != handle) { + int /*long*/ hPalette = display.hPalette; + if (hPalette != 0) return selectPalette (hPalette); + } + return super.WM_PALETTECHANGED (wParam, lParam); +} + +LRESULT WM_QUERYNEWPALETTE (int /*long*/ wParam, int /*long*/ lParam) { + int /*long*/ hPalette = display.hPalette; + if (hPalette != 0) return selectPalette (hPalette); + return super.WM_QUERYNEWPALETTE (wParam, lParam); +} + +LRESULT WM_SETCURSOR (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. When the shell is disabled + * by a Windows standard dialog (like a MessageBox + * or FileDialog), clicking in the shell does not + * bring the shell or the dialog to the front. The + * fix is to detect this case and bring the shell + * forward. + */ + int msg = OS.HIWORD (lParam); + if (msg == OS.WM_LBUTTONDOWN) { + if (!Display.TrimEnabled) { + Shell modalShell = display.getModalShell (); + if (modalShell != null && !isActive ()) { + int /*long*/ hwndModal = modalShell.handle; + if (OS.IsWindowEnabled (hwndModal)) { + OS.SetActiveWindow (hwndModal); + } + } + } + if (!OS.IsWindowEnabled (handle)) { + if (!OS.IsWinCE) { + int /*long*/ hwndPopup = OS.GetLastActivePopup (handle); + if (hwndPopup != 0 && hwndPopup != handle) { + if (display.getControl (hwndPopup) == null) { + if (OS.IsWindowEnabled (hwndPopup)) { + OS.SetActiveWindow (hwndPopup); + } + } + } + } + } + } + /* + * When the shell that contains a cursor is disabled, + * WM_SETCURSOR is called with HTERROR. Normally, + * when a control is disabled, the parent will get + * mouse and cursor events. In the case of a disabled + * shell, there is no enabled parent. In order to + * show the cursor when a shell is disabled, it is + * necessary to override WM_SETCURSOR when called + * with HTERROR to set the cursor but only when the + * mouse is in the client area of the shell. + */ + int hitTest = (short) OS.LOWORD (lParam); + if (hitTest == OS.HTERROR) { + if (!getEnabled ()) { + Control control = display.getControl (wParam); + if (control == this && cursor != null) { + POINT pt = new POINT (); + int pos = OS.GetMessagePos (); + OS.POINTSTOPOINT (pt, pos); + OS.ScreenToClient (handle, pt); + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + if (OS.PtInRect (rect, pt)) { + OS.SetCursor (cursor.handle); + switch (msg) { + case OS.WM_LBUTTONDOWN: + case OS.WM_RBUTTONDOWN: + case OS.WM_MBUTTONDOWN: + case OS.WM_XBUTTONDOWN: + OS.MessageBeep (OS.MB_OK); + } + return LRESULT.ONE; + } + } + } + } + return super.WM_SETCURSOR (wParam, lParam); +} + +LRESULT WM_SETTINGCHANGE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETTINGCHANGE (wParam, lParam); + if (result != null) return result; + if (OS.IsPPC) { + if (wParam == OS.SPI_SETSIPINFO) { + /* + * The SIP is in a new state. Cache its new value. + * Resize the Shell if it has the style SWT.RESIZE. + * Note that SHHandleWMSettingChange resizes the + * Shell and also updates the cached state. + */ + if ((style & SWT.RESIZE) != 0) { + OS.SHHandleWMSettingChange (handle, wParam, lParam, psai); + return LRESULT.ZERO; + } else { + SIPINFO pSipInfo = new SIPINFO (); + pSipInfo.cbSize = SIPINFO.sizeof; + OS.SipGetInfo (pSipInfo); + psai.fSipUp = pSipInfo.fdwFlags & OS.SIPF_ON; + } + } + } + return result; +} + +LRESULT WM_SHOWWINDOW (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SHOWWINDOW (wParam, lParam); + if (result != null) return result; + /* + * Bug in Windows. If the shell is hidden while the parent + * is iconic, Windows shows the shell when the parent is + * deiconified. This does not happen if the shell is hidden + * while the parent is not an icon. The fix is to track + * visible state for the shell and refuse to show the shell + * when the parent is shown. + */ + if (lParam == OS.SW_PARENTOPENING) { + Control control = this; + while (control != null) { + Shell shell = control.getShell (); + if (!shell.showWithParent) return LRESULT.ZERO; + control = control.parent; + } + } + return result; +} + +LRESULT WM_SYSCOMMAND (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SYSCOMMAND (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. When the last visible window in + * a process minimized, Windows swaps out the memory for + * the process. The assumption is that the user can no + * longer interact with the window, so the memory can be + * released to other applications. However, for programs + * that use a lot of memory, swapping the memory back in + * can take a long time, sometimes minutes. The fix is + * to intercept WM_SYSCOMMAND looking for SC_MINIMIZE + * and use ShowWindow() with SW_SHOWMINIMIZED to minimize + * the window, rather than running the default window proc. + * + * NOTE: The default window proc activates the next + * top-level window in the Z-order while ShowWindow() + * with SW_SHOWMINIMIZED does not. There is no fix for + * this at this time. + */ + if (OS.IsWinNT) { + int cmd = (int)/*64*/wParam & 0xFFF0; + switch (cmd) { + case OS.SC_MINIMIZE: + long memory = Runtime.getRuntime ().totalMemory (); + if (memory >= 32 * 1024 * 1024) { + OS.ShowWindow (handle, OS.SW_SHOWMINIMIZED); + return LRESULT.ZERO; + } + } + } + return result; +} + +LRESULT WM_WINDOWPOSCHANGING (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_WINDOWPOSCHANGING (wParam,lParam); + if (result != null) return result; + WINDOWPOS lpwp = new WINDOWPOS (); + OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof); + if ((lpwp.flags & OS.SWP_NOSIZE) == 0) { + lpwp.cx = Math.max (lpwp.cx, minWidth); + int trim = SWT.TITLE | SWT.CLOSE | SWT.MIN | SWT.MAX; + if ((style & SWT.NO_TRIM) == 0 && (style & trim) != 0) { + lpwp.cx = Math.max (lpwp.cx, OS.GetSystemMetrics (OS.SM_CXMINTRACK)); + } + lpwp.cy = Math.max (lpwp.cy, minHeight); + if ((style & SWT.NO_TRIM) == 0 && (style & trim) != 0) { + if ((style & SWT.RESIZE) != 0) { + lpwp.cy = Math.max (lpwp.cy, OS.GetSystemMetrics (OS.SM_CYMINTRACK)); + } else { + RECT rect = new RECT (); + int bits1 = OS.GetWindowLong (handle, OS.GWL_STYLE); + int bits2 = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); + OS.AdjustWindowRectEx (rect, bits1, false, bits2); + lpwp.cy = Math.max (lpwp.cy, rect.bottom - rect.top); + } + } + OS.MoveMemory (lParam, lpwp, WINDOWPOS.sizeof); + } + return result; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Slider.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Slider.java new file mode 100755 index 0000000000..086a9d56be --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Slider.java @@ -0,0 +1,788 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * 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 { + int increment, pageIncrement; + boolean ignoreFocus; + static final int /*long*/ ScrollBarProc; + static final TCHAR ScrollBarClass = new TCHAR (0, "SCROLLBAR", true); + static { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, ScrollBarClass, lpWndClass); + ScrollBarProc = lpWndClass.lpfnWndProc; + } + +/** + * 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); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + /* + * Feature in Windows. Windows runs a modal message + * loop when the user drags a scroll bar. This means + * that mouse down events won't get delivered until + * after the loop finishes. The fix is to run any + * deferred messages, including mouse down messages + * before calling the scroll bar window proc. + */ + switch (msg) { + case OS.WM_LBUTTONDOWN: + case OS.WM_LBUTTONDBLCLK: + display.runDeferredEvents (); + } + return OS.CallWindowProc (ScrollBarProc, hwnd, msg, wParam, lParam); +} + +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 border = getBorderWidth (); + int width = border * 2, height = border * 2; + if ((style & SWT.HORIZONTAL) != 0) { + width += OS.GetSystemMetrics (OS.SM_CXHSCROLL) * 10; + height += OS.GetSystemMetrics (OS.SM_CYHSCROLL); + } else { + width += OS.GetSystemMetrics (OS.SM_CXVSCROLL); + height += OS.GetSystemMetrics (OS.SM_CYVSCROLL) * 10; + } + if (wHint != SWT.DEFAULT) width = wHint + (border * 2); + if (hHint != SWT.DEFAULT) height = hHint + (border * 2); + return new Point (width, height); +} + +void createWidget () { + super.createWidget (); + increment = 1; + pageIncrement = 10; + /* + * Set the initial values of the maximum + * to 100 and the thumb to 10. Note that + * info.nPage needs to be 11 in order to + * get a thumb that is 10. + */ + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_ALL; + info.nMax = 100; + info.nPage = 11; + OS.SetScrollInfo (handle, OS.SB_CTL, info, true); +} + +int defaultBackground () { + return OS.GetSysColor (OS.COLOR_SCROLLBAR); +} + +int defaultForeground () { + return OS.GetSysColor (OS.COLOR_BTNFACE); +} + +void enableWidget (boolean enabled) { + super.enableWidget (enabled); + if (!OS.IsWinCE) { + int flags = enabled ? OS.ESB_ENABLE_BOTH : OS.ESB_DISABLE_BOTH; + OS.EnableScrollBar (handle, OS.SB_CTL, flags); + } + if (enabled) { + state &= ~DISABLED; + } else { + state |= DISABLED; + } +} + +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 (); + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_RANGE; + OS.GetScrollInfo (handle, OS.SB_CTL, info); + return info.nMax; +} + +/** + * 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 (); + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_RANGE; + OS.GetScrollInfo (handle, OS.SB_CTL, info); + return info.nMin; +} + +/** + * 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 (); + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_POS; + OS.GetScrollInfo (handle, OS.SB_CTL, info); + return info.nPos; +} + +/** + * 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 (); + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_PAGE; + OS.GetScrollInfo (handle, OS.SB_CTL, info); + if (info.nPage != 0) --info.nPage; + return info.nPage; +} + +/** + * 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 setBounds (int x, int y, int width, int height, int flags) { + super.setBounds (x, y, width, height, flags); + /* + * Bug in Windows. If the scroll bar is resized when it has focus, + * the flashing cursor that is used to show that the scroll bar has + * focus is not moved. The fix is to send a fake WM_SETFOCUS to + * get the scroll bar to recompute the size of the flashing cursor. + */ + if (OS.GetFocus () == handle) { + ignoreFocus = true; + OS.SendMessage (handle, OS.WM_SETFOCUS, 0, 0); + ignoreFocus = false; + } +} + +/** + * 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; + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL; + OS.GetScrollInfo (handle, OS.SB_CTL, info); + if (value - info.nMin - info.nPage < 1) return; + info.nMax = value; + SetScrollInfo (handle, OS.SB_CTL, info, true); +} + +/** + * 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; + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL; + OS.GetScrollInfo (handle, OS.SB_CTL, info); + if (info.nMax - value - info.nPage < 1) return; + info.nMin = value; + SetScrollInfo (handle, OS.SB_CTL, info, true); +} + +/** + * 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; +} + +boolean SetScrollInfo (int /*long*/ hwnd, int flags, SCROLLINFO info, boolean fRedraw) { + /* + * Feature in Windows. Using SIF_DISABLENOSCROLL, + * SetScrollInfo () can change enabled and disabled + * state of the scroll bar causing a scroll bar that + * was disabled by the application to become enabled. + * The fix is to disable the scroll bar (again) when + * the application has disabled the scroll bar. + */ + if ((state & DISABLED) != 0) fRedraw = false; + boolean result = OS.SetScrollInfo (hwnd, flags, info, fRedraw); + if ((state & DISABLED) != 0) { + OS.EnableWindow (handle, false); + if (!OS.IsWinCE) { + OS.EnableScrollBar (handle, OS.SB_CTL, OS.ESB_DISABLE_BOTH); + } + } + + /* + * Bug in Windows. If the thumb is resized when it has focus, + * the flashing cursor that is used to show that the scroll bar + * has focus is not moved. The fix is to send a fake WM_SETFOCUS + * to get the scroll bar to recompute the size of the flashing + * cursor. + */ + if (OS.GetFocus () == handle) { + ignoreFocus = true; + OS.SendMessage (handle, OS.WM_SETFOCUS, 0, 0); + ignoreFocus = false; + } + return result; +} + +/** + * 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 (); + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_POS; + info.nPos = value; + SetScrollInfo (handle, OS.SB_CTL, info, true); +} + +/** + * 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; + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_PAGE | OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL; + OS.GetScrollInfo (handle, OS.SB_CTL, info); + info.nPage = value; + if (info.nPage != 0) info.nPage++; + SetScrollInfo (handle, OS.SB_CTL, info, true); +} + +/** + * 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.increment = increment; + this.pageIncrement = pageIncrement; + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_POS | OS.SIF_PAGE | OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL; + info.nPos = selection; + info.nMin = minimum; + info.nMax = maximum; + info.nPage = thumb; + if (info.nPage != 0) info.nPage++; + SetScrollInfo (handle, OS.SB_CTL, info, true); +} + +int widgetExtStyle () { + /* + * Bug in Windows. If a scroll bar control is given a border, + * dragging the scroll bar thumb eats away parts of the border + * while the thumb is dragged. The fix is to clear border for + * all scroll bars. + */ + int bits = super.widgetExtStyle (); + if ((style & SWT.BORDER) != 0) bits &= ~OS.WS_EX_CLIENTEDGE; + return bits; +} + +int widgetStyle () { + int bits = super.widgetStyle () | OS.WS_TABSTOP; + /* + * Bug in Windows. If a scroll bar control is given a border, + * dragging the scroll bar thumb eats away parts of the border + * while the thumb is dragged. The fix is to clear WS_BORDER. + */ + if ((style & SWT.BORDER) != 0) bits &= ~OS.WS_BORDER; + if ((style & SWT.HORIZONTAL) != 0) return bits | OS.SBS_HORZ; + return bits | OS.SBS_VERT; +} + +TCHAR windowClass () { + return ScrollBarClass; +} + +int /*long*/ windowProc () { + return ScrollBarProc; +} + +LRESULT WM_KEYDOWN (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_KEYDOWN (wParam, lParam); + if (result != null) return result; + if ((style & SWT.VERTICAL) != 0) return result; + /* + * Bug in Windows. When a horizontal scroll bar is mirrored, + * the native control does not correctly swap the arrow keys. + * The fix is to swap them before calling the scroll bar window + * proc. + * + * NOTE: This fix is not ideal. It breaks when the bug is fixed + * in the operating system. + */ + if ((style & SWT.MIRRORED) != 0) { + switch ((int)/*64*/wParam) { + case OS.VK_LEFT: + case OS.VK_RIGHT: { + int key = wParam == OS.VK_LEFT ? OS.VK_RIGHT : OS.VK_LEFT; + int /*long*/ code = callWindowProc (handle, OS.WM_KEYDOWN, key, lParam); + return new LRESULT (code); + } + } + } + return result; +} + +LRESULT WM_LBUTTONDBLCLK (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. Windows uses the WS_TABSTOP + * style for the scroll bar to decide that focus + * should be set during WM_LBUTTONDBLCLK. This is + * not the desired behavior. The fix is to clear + * and restore WS_TABSTOP so that Windows will not + * assign focus. + */ + int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE); + int newBits = oldBits & ~OS.WS_TABSTOP; + OS.SetWindowLong (handle, OS.GWL_STYLE, newBits); + LRESULT result = super.WM_LBUTTONDBLCLK (wParam, lParam); + if (isDisposed ()) return LRESULT.ZERO; + OS.SetWindowLong (handle, OS.GWL_STYLE, oldBits); + if (result == LRESULT.ZERO) return result; + + /* + * Feature in Windows. Windows runs a modal message loop + * when the user drags a scroll bar that terminates when + * it sees an WM_LBUTTONUP. Unfortunately the WM_LBUTTONUP + * is consumed. The fix is to send a fake mouse up and + * release the automatic capture. + */ + if (!OS.IsWinCE) { + if (OS.GetCapture () == handle) OS.ReleaseCapture (); + if (!sendMouseEvent (SWT.MouseUp, 1, handle, OS.WM_LBUTTONUP, wParam, lParam)) { + return LRESULT.ZERO; + } + } + return result; +} + +LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. Windows uses the WS_TABSTOP + * style for the scroll bar to decide that focus + * should be set during WM_LBUTTONDOWN. This is + * not the desired behavior. The fix is to clear + * and restore WS_TABSTOP so that Windows will not + * assign focus. + */ + int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE); + int newBits = oldBits & ~OS.WS_TABSTOP; + OS.SetWindowLong (handle, OS.GWL_STYLE, newBits); + LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam); + if (isDisposed ()) return LRESULT.ZERO; + OS.SetWindowLong (handle, OS.GWL_STYLE, oldBits); + if (result == LRESULT.ZERO) return result; + + /* + * Feature in Windows. Windows runs a modal message loop + * when the user drags a scroll bar that terminates when + * it sees an WM_LBUTTONUP. Unfortunately the WM_LBUTTONUP + * is consumed. The fix is to send a fake mouse up and + * release the automatic capture. + */ + if (!OS.IsWinCE) { + if (OS.GetCapture () == handle) OS.ReleaseCapture (); + if (!sendMouseEvent (SWT.MouseUp, 1, handle, OS.WM_LBUTTONUP, wParam, lParam)) { + return LRESULT.ONE; + } + } + return result; +} + +LRESULT WM_SETFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreFocus) return null; + return super.WM_SETFOCUS (wParam, lParam); +} + +LRESULT wmScrollChild (int /*long*/ wParam, int /*long*/ lParam) { + + /* Do nothing when scrolling is ending */ + int code = OS.LOWORD (wParam); + if (code == OS.SB_ENDSCROLL) return null; + + /* Move the thumb */ + Event event = new Event (); + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_TRACKPOS | OS.SIF_POS | OS.SIF_RANGE; + OS.GetScrollInfo (handle, OS.SB_CTL, info); + info.fMask = OS.SIF_POS; + switch (code) { + case OS.SB_THUMBPOSITION: + event.detail = SWT.NONE; + info.nPos = info.nTrackPos; + break; + case OS.SB_THUMBTRACK: + event.detail = SWT.DRAG; + info.nPos = info.nTrackPos; + break; + case OS.SB_TOP: + event.detail = SWT.HOME; + info.nPos = info.nMin; + break; + case OS.SB_BOTTOM: + event.detail = SWT.END; + info.nPos = info.nMax; + break; + case OS.SB_LINEDOWN: + event.detail = SWT.ARROW_DOWN; + info.nPos += increment; + break; + case OS.SB_LINEUP: + event.detail = SWT.ARROW_UP; + info.nPos = Math.max (info.nMin, info.nPos - increment); + break; + case OS.SB_PAGEDOWN: + event.detail = SWT.PAGE_DOWN; + info.nPos += pageIncrement; + break; + case OS.SB_PAGEUP: + event.detail = SWT.PAGE_UP; + info.nPos = Math.max (info.nMin, info.nPos - pageIncrement); + break; + } + OS.SetScrollInfo (handle, OS.SB_CTL, info, true); + + /* + * Feature in Windows. Windows runs a modal message + * loop when the user drags a scroll bar. This means + * that selection event must be sent because WM_HSCROLL + * and WM_VSCROLL are sent from the modal message loop + * so that they are delivered during inside the loop. + */ + sendEvent (SWT.Selection, event); + // the widget could be destroyed at this point + return null; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Spinner.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Spinner.java new file mode 100644 index 0000000000..8c7b19d696 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Spinner.java @@ -0,0 +1,1460 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * 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 { + int /*long*/ hwndText, hwndUpDown; + boolean ignoreModify; + int pageIncrement, digits; + static final int /*long*/ EditProc; + static final TCHAR EditClass = new TCHAR (0, "EDIT", true); + static final int /*long*/ UpDownProc; + static final TCHAR UpDownClass = new TCHAR (0, OS.UPDOWN_CLASS, true); + static { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, EditClass, lpWndClass); + EditProc = lpWndClass.lpfnWndProc; + OS.GetClassInfo (0, UpDownClass, lpWndClass); + UpDownProc = lpWndClass.lpfnWndProc; + } + + /** + * 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 = OS.IsWinNT ? 0x7FFFFFFF : 0x7FFF; + } + +/** + * 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)); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + if (hwnd == hwndText) { + return OS.CallWindowProc (EditProc, hwnd, msg, wParam, lParam); + } + if (hwnd == hwndUpDown) { + return OS.CallWindowProc (UpDownProc, hwnd, msg, wParam, lParam); + } + return OS.DefWindowProc (handle, msg, wParam, lParam); +} + +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); +} + +boolean checkHandle (int /*long*/ hwnd) { + return hwnd == handle || hwnd == hwndText || hwnd == hwndUpDown; +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +void createHandle () { + super.createHandle (); + state &= ~(CANVAS | THEME_BACKGROUND); + int /*long*/ hInstance = OS.GetModuleHandle (null); + int textExStyle = (style & SWT.BORDER) != 0 ? OS.WS_EX_CLIENTEDGE : 0; + int textStyle = OS.WS_CHILD | OS.WS_VISIBLE | OS.ES_AUTOHSCROLL | OS.WS_CLIPSIBLINGS; + if ((style & SWT.READ_ONLY) != 0) textStyle |= OS.ES_READONLY; + if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) { + if ((style & SWT.RIGHT_TO_LEFT) != 0) textExStyle |= OS.WS_EX_LAYOUTRTL; + } + hwndText = OS.CreateWindowEx ( + textExStyle, + EditClass, + null, + textStyle, + 0, 0, 0, 0, + handle, + 0, + hInstance, + null); + if (hwndText == 0) error (SWT.ERROR_NO_HANDLES); + OS.SetWindowLongPtr (hwndText, OS.GWLP_ID, hwndText); + int upDownStyle = OS.WS_CHILD | OS.WS_VISIBLE | OS.UDS_AUTOBUDDY; + if ((style & SWT.WRAP) != 0) upDownStyle |= OS.UDS_WRAP; + if ((style & SWT.BORDER) != 0) { + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + upDownStyle |= OS.UDS_ALIGNLEFT; + } else { + upDownStyle |= OS.UDS_ALIGNRIGHT; + } + } + hwndUpDown = OS.CreateWindowEx ( + 0, + UpDownClass, + null, + upDownStyle, + 0, 0, 0, 0, + handle, + 0, + hInstance, + null); + if (hwndUpDown == 0) error (SWT.ERROR_NO_HANDLES); + int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE; + SetWindowPos (hwndText, hwndUpDown, 0, 0, 0, 0, flags); + OS.SetWindowLongPtr (hwndUpDown, OS.GWLP_ID, hwndUpDown); + if (OS.IsDBLocale) { + int /*long*/ hIMC = OS.ImmGetContext (handle); + OS.ImmAssociateContext (hwndText, hIMC); + OS.ImmAssociateContext (hwndUpDown, hIMC); + OS.ImmReleaseContext (handle, hIMC); + } + OS.SendMessage (hwndUpDown, OS.UDM_SETRANGE32, 0, 100); + OS.SendMessage (hwndUpDown, OS.IsWinCE ? OS.UDM_SETPOS : OS.UDM_SETPOS32, 0, 0); + pageIncrement = 10; + digits = 0; + TCHAR buffer = new TCHAR (getCodePage (), "0", true); + OS.SetWindowText (hwndText, buffer); +} + +/** + * 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); +} + +int /*long*/ borderHandle () { + return hwndText; +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + int width = 0, height = 0; + if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) { + int /*long*/ newFont, oldFont = 0; + int /*long*/ hDC = OS.GetDC (hwndText); + newFont = OS.SendMessage (hwndText, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA (); + OS.GetTextMetrics (hDC, tm); + height = tm.tmHeight; + RECT rect = new RECT (); + int [] max = new int [1]; + OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, null, max); + String string = String.valueOf (max [0]); + if (digits > 0) { + StringBuffer buffer = new StringBuffer (); + buffer.append (string); + buffer.append (getDecimalSeparator ()); + int count = digits - string.length (); + while (count >= 0) { + buffer.append ("0"); + count--; + } + string = buffer.toString (); + } + TCHAR buffer = new TCHAR (getCodePage (), string, false); + int flags = OS.DT_CALCRECT | OS.DT_EDITCONTROL | OS.DT_NOPREFIX; + OS.DrawText (hDC, buffer, buffer.length (), rect, flags); + width = rect.right - rect.left; + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (hwndText, hDC); + } + 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); + if (hHint == SWT.DEFAULT) { + int upDownHeight = OS.GetSystemMetrics (OS.SM_CYVSCROLL) + 2 * getBorderWidth (); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + upDownHeight += (style & SWT.BORDER) != 0 ? 1 : 3; + } + trim.height = Math.max (trim.height, upDownHeight); + } + return new Point (trim.width, trim.height); +} + +public Rectangle computeTrim (int x, int y, int width, int height) { + checkWidget (); + + /* Get the trim of the text control */ + RECT rect = new RECT (); + OS.SetRect (rect, x, y, x + width, y + height); + int bits0 = OS.GetWindowLong (hwndText, OS.GWL_STYLE); + int bits1 = OS.GetWindowLong (hwndText, OS.GWL_EXSTYLE); + OS.AdjustWindowRectEx (rect, bits0, false, bits1); + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + /* + * The preferred height of a single-line text widget + * has been hand-crafted to be the same height as + * the single-line text widget in an editable combo + * box. + */ + int /*long*/ margins = OS.SendMessage (hwndText, OS.EM_GETMARGINS, 0, 0); + x -= OS.LOWORD (margins); + width += OS.LOWORD (margins) + OS.HIWORD (margins); + if ((style & SWT.BORDER) != 0) { + x -= 1; + y -= 1; + width += 2; + height += 2; + } + width += OS.GetSystemMetrics (OS.SM_CXVSCROLL); + return new Rectangle (x, y, 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> + */ +public void copy () { + checkWidget (); + OS.SendMessage (hwndText, OS.WM_COPY, 0, 0); +} + +/** + * 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; + OS.SendMessage (hwndText, OS.WM_CUT, 0, 0); +} + +int defaultBackground () { + return OS.GetSysColor (OS.COLOR_WINDOW); +} + +void enableWidget (boolean enabled) { + super.enableWidget (enabled); + OS.EnableWindow (hwndText, enabled); + OS.EnableWindow (hwndUpDown, enabled); +} + +void deregister () { + super.deregister (); + display.removeControl (hwndText); + display.removeControl (hwndUpDown); +} + +boolean hasFocus () { + int /*long*/ hwndFocus = OS.GetFocus (); + if (hwndFocus == handle) return true; + if (hwndFocus == hwndText) return true; + if (hwndFocus == hwndUpDown) return true; + return false; +} + +/** + * 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; +} + +String getDecimalSeparator () { + TCHAR tchar = new TCHAR (getCodePage (), 4); + int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_SDECIMAL, tchar, 4); + return size != 0 ? tchar.toString (0, size - 1) : "."; +} + +/** + * 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 (); + UDACCEL udaccel = new UDACCEL (); + OS.SendMessage (hwndUpDown, OS.UDM_GETACCEL, 1, udaccel); + return udaccel.nInc; +} + +/** + * 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 (); + int [] max = new int [1]; + OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, null, max); + return max [0]; +} + +/** + * 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 (); + int [] min = new int [1]; + OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, min, null); + return min [0]; +} + +/** + * 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 (); + if (OS.IsWinCE) { + return OS.LOWORD (OS.SendMessage (hwndUpDown, OS.UDM_GETPOS, 0, 0)); + } else { + return (int)/*64*/OS.SendMessage (hwndUpDown, OS.UDM_GETPOS32, 0, 0); + } +} + +int getSelectionText (boolean [] parseFail) { + int length = OS.GetWindowTextLength (hwndText); + TCHAR buffer = new TCHAR (getCodePage (), length + 1); + OS.GetWindowText (hwndText, buffer, length + 1); + String string = buffer.toString (0, length); + try { + int value; + if (digits > 0) { + String decimalSeparator = getDecimalSeparator (); + 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 = new int [1], min = new int [1]; + OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, min, max); + if (min [0] <= value && value <= max [0]) 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 (); + int length = OS.GetWindowTextLength (hwndText); + if (length == 0) return ""; + TCHAR buffer = new TCHAR (getCodePage (), length + 1); + OS.GetWindowText (hwndText, buffer, length + 1); + return buffer.toString (0, length); +} + +/** + * 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 (int)/*64*/OS.SendMessage (hwndText, OS.EM_GETLIMITTEXT, 0, 0) & 0x7FFFFFFF; +} + +int mbcsToWcsPos (int mbcsPos) { + if (mbcsPos <= 0) return 0; + if (OS.IsUnicode) return mbcsPos; + int mbcsSize = OS.GetWindowTextLengthA (hwndText); + if (mbcsSize == 0) return 0; + if (mbcsPos >= mbcsSize) return mbcsSize; + byte [] buffer = new byte [mbcsSize + 1]; + OS.GetWindowTextA (hwndText, buffer, mbcsSize + 1); + return OS.MultiByteToWideChar (getCodePage (), OS.MB_PRECOMPOSED, buffer, mbcsPos, null, 0); +} + +/** + * 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; + OS.SendMessage (hwndText, OS.WM_PASTE, 0, 0); +} + +void register () { + super.register (); + display.addControl (hwndText, this); + display.addControl (hwndUpDown, this); +} + +void releaseHandle () { + super.releaseHandle (); + hwndText = hwndUpDown = 0; +} + +/** + * 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 + */ +void removeVerifyListener (VerifyListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Verify, listener); +} + +boolean sendKeyEvent (int type, int msg, int /*long*/ wParam, int /*long*/ lParam, Event event) { + if (!super.sendKeyEvent (type, msg, wParam, lParam, event)) { + return false; + } + if ((style & SWT.READ_ONLY) != 0) return true; + if (type != SWT.KeyDown) return true; + if (msg != OS.WM_CHAR && msg != OS.WM_KEYDOWN && msg != OS.WM_IME_CHAR) { + return true; + } + if (event.character == 0) return true; +// if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return true; + char key = event.character; + int stateMask = event.stateMask; + + /* + * Disable all magic keys that could modify the text + * and don't send events when Alt, Shift or Ctrl is + * pressed. + */ + switch (msg) { + case OS.WM_CHAR: + if (key != 0x08 && key != 0x7F && key != '\r' && key != '\t' && key != '\n') break; + // FALL THROUGH + case OS.WM_KEYDOWN: + if ((stateMask & (SWT.ALT | SWT.SHIFT | SWT.CONTROL)) != 0) return false; + break; + } + + /* + * If the left button is down, the text widget refuses the character. + */ + if (OS.GetKeyState (OS.VK_LBUTTON) < 0) { + return true; + } + + /* Verify the character */ + String oldText = ""; + int [] start = new int [1], end = new int [1]; + OS.SendMessage (hwndText, OS.EM_GETSEL, start, end); + switch (key) { + case 0x08: /* Bs */ + if (start [0] == end [0]) { + if (start [0] == 0) return true; + start [0] = start [0] - 1; + if (!OS.IsUnicode && OS.IsDBLocale) { + int [] newStart = new int [1], newEnd = new int [1]; + OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]); + OS.SendMessage (hwndText, OS.EM_GETSEL, newStart, newEnd); + if (start [0] != newStart [0]) start [0] = start [0] - 1; + } + start [0] = Math.max (start [0], 0); + } + break; + case 0x7F: /* Del */ + if (start [0] == end [0]) { + int length = OS.GetWindowTextLength (hwndText); + if (start [0] == length) return true; + end [0] = end [0] + 1; + if (!OS.IsUnicode && OS.IsDBLocale) { + int [] newStart = new int [1], newEnd = new int [1]; + OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]); + OS.SendMessage (hwndText, OS.EM_GETSEL, newStart, newEnd); + if (end [0] != newEnd [0]) end [0] = end [0] + 1; + } + end [0] = Math.min (end [0], length); + } + break; + case '\r': /* Return */ + return true; + default: /* Tab and other characters */ + if (key != '\t' && key < 0x20) return true; + oldText = new String (new char [] {key}); + break; + } + String newText = verifyText (oldText, start [0], end [0], event); + if (newText == null) return false; + if (newText == oldText) return true; + TCHAR buffer = new TCHAR (getCodePage (), newText, true); + OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]); + OS.SendMessage (hwndText, OS.EM_REPLACESEL, 0, buffer); + return false; +} + +void setBackgroundImage (int /*long*/ hBitmap) { + super.setBackgroundImage (hBitmap); + OS.InvalidateRect (hwndText, null, true); +} + +void setBackgroundPixel (int pixel) { + super.setBackgroundPixel (pixel); + OS.InvalidateRect (hwndText, null, true); +} + +/** + * 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 == this.digits) return; + this.digits = value; + int pos; + if (OS.IsWinCE) { + pos = OS.LOWORD (OS.SendMessage (hwndUpDown, OS.UDM_GETPOS, 0, 0)); + } else { + pos = (int)/*64*/OS.SendMessage (hwndUpDown, OS.UDM_GETPOS32, 0, 0); + } + setSelection (pos, false, true, false); +} + +void setForegroundPixel (int pixel) { + super.setForegroundPixel (pixel); + OS.InvalidateRect (hwndText, null, true); +} + +/** + * 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; + int /*long*/ hHeap = OS.GetProcessHeap (); + int count = (int)/*64*/OS.SendMessage (hwndUpDown, OS.UDM_GETACCEL, 0, (UDACCEL)null); + int /*long*/ udaccels = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, UDACCEL.sizeof * count); + OS.SendMessage (hwndUpDown, OS.UDM_GETACCEL, count, udaccels); + int first = -1; + UDACCEL udaccel = new UDACCEL (); + for (int i = 0; i < count; i++) { + int /*long*/ offset = udaccels + (i * UDACCEL.sizeof); + OS.MoveMemory (udaccel, offset, UDACCEL.sizeof); + if (first == -1) first = udaccel.nInc; + udaccel.nInc = udaccel.nInc * value / first; + OS.MoveMemory (offset, udaccel, UDACCEL.sizeof); + } + OS.SendMessage (hwndUpDown, OS.UDM_SETACCEL, count, udaccels); + OS.HeapFree (hHeap, 0, udaccels); +} + +/** + * 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 = new int [1]; + OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, min, null); + if (value <= min [0]) return; + int pos; + if (OS.IsWinCE) { + pos = OS.LOWORD (OS.SendMessage (hwndUpDown, OS.UDM_GETPOS, 0, 0)); + } else { + pos = (int)/*64*/OS.SendMessage (hwndUpDown, OS.UDM_GETPOS32, 0, 0); + } + OS.SendMessage (hwndUpDown , OS.UDM_SETRANGE32, min [0], 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 = new int [1]; + OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, null, max); + if (value >= max [0]) return; + int pos; + if (OS.IsWinCE) { + pos = OS.LOWORD (OS.SendMessage (hwndUpDown, OS.UDM_GETPOS, 0, 0)); + } else { + pos = (int)/*64*/OS.SendMessage (hwndUpDown, OS.UDM_GETPOS32, 0, 0); + } + OS.SendMessage (hwndUpDown , OS.UDM_SETRANGE32, value, max [0]); + 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 [] max = new int [1], min = new int [1]; + OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, min, max); + value = Math.min (Math.max (min [0], value), max [0]); + setSelection (value, true, true, false); +} + +void setSelection (int value, boolean setPos, boolean setText, boolean notify) { + if (setPos) { + OS.SendMessage (hwndUpDown , OS.IsWinCE ? OS.UDM_SETPOS : OS.UDM_SETPOS32, 0, value); + } + if (setText) { + String string; + if (digits == 0) { + string = String.valueOf (value); + } else { + string = String.valueOf (Math.abs (value)); + String decimalSeparator = getDecimalSeparator (); + int index = string.length () - digits; + StringBuffer buffer = new StringBuffer (); + if (value < 0) buffer.append ("-"); + 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 (); + } + if (hooks (SWT.Verify) || filters (SWT.Verify)) { + int length = OS.GetWindowTextLength (hwndText); + string = verifyText (string, 0, length, null); + if (string == null) return; + } + TCHAR buffer = new TCHAR (getCodePage (), string, true); + OS.SetWindowText (hwndText, buffer); + OS.SendMessage (hwndText, OS.EM_SETSEL, 0, -1); + if (!OS.IsWinCE) { + OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, hwndText, OS.OBJID_CLIENT, 0); + } + } + if (notify) postEvent (SWT.Selection); +} + +/** + * 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); + OS.SendMessage (hwndText, OS.EM_SETLIMITTEXT, limit, 0); +} + +void setToolTipText (Shell shell, String string) { + shell.setToolTipText (hwndText, string); + shell.setToolTipText (hwndUpDown, string); +} + +/** + * 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); + setIncrement (increment); + this.pageIncrement = pageIncrement; + this.digits = digits; + OS.SendMessage (hwndUpDown , OS.UDM_SETRANGE32, minimum, maximum); + setSelection (selection, true, true, false); +} + +void subclass () { + super.subclass (); + int /*long*/ newProc = display.windowProc; + OS.SetWindowLongPtr (hwndText, OS.GWLP_WNDPROC, newProc); + OS.SetWindowLongPtr (hwndUpDown, OS.GWLP_WNDPROC, newProc); +} + +void unsubclass () { + super.unsubclass (); + OS.SetWindowLongPtr (hwndText, OS.GWLP_WNDPROC, EditProc); + OS.SetWindowLongPtr (hwndUpDown, OS.GWLP_WNDPROC, UpDownProc); +} + +String verifyText (String string, int start, int end, Event keyEvent) { + Event event = new Event (); + event.text = string; + event.start = start; + event.end = end; + if (keyEvent != null) { + event.character = keyEvent.character; + event.keyCode = keyEvent.keyCode; + event.stateMask = keyEvent.stateMask; + } + 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; + } + if (string.length() > 0) { + int [] min = new int [1]; + OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, min, null); + if (min [0] < 0 && string.charAt (0) == '-') index++; + } + while (index < string.length ()) { + if (!Character.isDigit (string.charAt (index))) break; + index++; + } + event.doit = index == string.length (); + if (!OS.IsUnicode && OS.IsDBLocale) { + event.start = mbcsToWcsPos (start); + event.end = mbcsToWcsPos (end); + } + sendEvent (SWT.Verify, event); + if (!event.doit || isDisposed ()) return null; + return event.text; +} + +int widgetExtStyle () { + return super.widgetExtStyle () & ~OS.WS_EX_CLIENTEDGE; +} + +int /*long*/ windowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (hwnd == hwndText || hwnd == hwndUpDown) { + LRESULT result = null; + switch (msg) { + /* Keyboard messages */ + case OS.WM_CHAR: result = wmChar (hwnd, wParam, lParam); break; + case OS.WM_IME_CHAR: result = wmIMEChar (hwnd, wParam, lParam); break; + case OS.WM_KEYDOWN: result = wmKeyDown (hwnd, wParam, lParam); break; + case OS.WM_KEYUP: result = wmKeyUp (hwnd, wParam, lParam); break; + case OS.WM_SYSCHAR: result = wmSysChar (hwnd, wParam, lParam); break; + case OS.WM_SYSKEYDOWN: result = wmSysKeyDown (hwnd, wParam, lParam); break; + case OS.WM_SYSKEYUP: result = wmSysKeyUp (hwnd, wParam, lParam); break; + + /* Mouse Messages */ + case OS.WM_CAPTURECHANGED: result = wmCaptureChanged (hwnd, wParam, lParam); break; + case OS.WM_LBUTTONDBLCLK: result = wmLButtonDblClk (hwnd, wParam, lParam); break; + case OS.WM_LBUTTONDOWN: result = wmLButtonDown (hwnd, wParam, lParam); break; + case OS.WM_LBUTTONUP: result = wmLButtonUp (hwnd, wParam, lParam); break; + case OS.WM_MBUTTONDBLCLK: result = wmMButtonDblClk (hwnd, wParam, lParam); break; + case OS.WM_MBUTTONDOWN: result = wmMButtonDown (hwnd, wParam, lParam); break; + case OS.WM_MBUTTONUP: result = wmMButtonUp (hwnd, wParam, lParam); break; + case OS.WM_MOUSEHOVER: result = wmMouseHover (hwnd, wParam, lParam); break; + case OS.WM_MOUSELEAVE: result = wmMouseLeave (hwnd, wParam, lParam); break; + case OS.WM_MOUSEMOVE: result = wmMouseMove (hwnd, wParam, lParam); break; +// case OS.WM_MOUSEWHEEL: result = wmMouseWheel (hwnd, wParam, lParam); break; + case OS.WM_RBUTTONDBLCLK: result = wmRButtonDblClk (hwnd, wParam, lParam); break; + case OS.WM_RBUTTONDOWN: result = wmRButtonDown (hwnd, wParam, lParam); break; + case OS.WM_RBUTTONUP: result = wmRButtonUp (hwnd, wParam, lParam); break; + case OS.WM_XBUTTONDBLCLK: result = wmXButtonDblClk (hwnd, wParam, lParam); break; + case OS.WM_XBUTTONDOWN: result = wmXButtonDown (hwnd, wParam, lParam); break; + case OS.WM_XBUTTONUP: result = wmXButtonUp (hwnd, wParam, lParam); break; + + /* Focus Messages */ + case OS.WM_SETFOCUS: result = wmSetFocus (hwnd, wParam, lParam); break; + case OS.WM_KILLFOCUS: result = wmKillFocus (hwnd, wParam, lParam); break; + + /* Paint messages */ + case OS.WM_PAINT: result = wmPaint (hwnd, wParam, lParam); break; + case OS.WM_PRINT: result = wmPrint (hwnd, wParam, lParam); break; + + /* Menu messages */ + case OS.WM_CONTEXTMENU: result = wmContextMenu (hwnd, wParam, lParam); break; + + /* Clipboard messages */ + case OS.WM_CLEAR: + case OS.WM_CUT: + case OS.WM_PASTE: + case OS.WM_UNDO: + case OS.EM_UNDO: + if (hwnd == hwndText) { + result = wmClipboard (hwnd, msg, wParam, lParam); + } + break; + } + if (result != null) return result.value; + return callWindowProc (hwnd, msg, wParam, lParam); + } + return super.windowProc (hwnd, msg, wParam, lParam); +} + +LRESULT WM_ERASEBKGND (int /*long*/ wParam, int /*long*/ lParam) { + super.WM_ERASEBKGND (wParam, lParam); + drawBackground (wParam); + return LRESULT.ONE; +} + +LRESULT WM_KILLFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT WM_SETFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + OS.SetFocus (hwndText); + OS.SendMessage (hwndText, OS.EM_SETSEL, 0, -1); + return null; +} + +LRESULT WM_SETFONT (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETFONT (wParam, lParam); + if (result != null) return result; + OS.SendMessage (hwndText, OS.WM_SETFONT, wParam, lParam); + return result; +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SIZE (wParam, lParam); + if (isDisposed ()) return result; + int width = OS.LOWORD (lParam), height = OS.HIWORD (lParam); + int upDownWidth = OS.GetSystemMetrics (OS.SM_CXVSCROLL); + int textWidth = width - upDownWidth; + int border = OS.GetSystemMetrics (OS.SM_CXEDGE); + int flags = OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE; + SetWindowPos (hwndText, 0, 0, 0, textWidth + border, height, flags); + SetWindowPos (hwndUpDown, 0, textWidth, 0, upDownWidth, height, flags); + return result; +} + +LRESULT wmChar (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.wmChar (hwnd, wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. For some reason, when the + * widget is a single line text widget, when the + * user presses tab, return or escape, Windows beeps. + * The fix is to look for these keys and not call + * the window proc. + */ + switch ((int)/*64*/wParam) { + case SWT.CR: + postEvent (SWT.DefaultSelection); + // FALL THROUGH + case SWT.TAB: + case SWT.ESC: return LRESULT.ZERO; + } + return result; +} + +LRESULT wmClipboard (int /*long*/ hwndText, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if ((style & SWT.READ_ONLY) != 0) return null; +// if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return null; + boolean call = false; + int [] start = new int [1], end = new int [1]; + String newText = null; + switch (msg) { + case OS.WM_CLEAR: + case OS.WM_CUT: + OS.SendMessage (hwndText, OS.EM_GETSEL, start, end); + if (start [0] != end [0]) { + newText = ""; + call = true; + } + break; + case OS.WM_PASTE: + OS.SendMessage (hwndText, OS.EM_GETSEL, start, end); + newText = getClipboardText (); + break; + case OS.EM_UNDO: + case OS.WM_UNDO: + if (OS.SendMessage (hwndText, OS.EM_CANUNDO, 0, 0) != 0) { + ignoreModify = true; + OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam); + int length = OS.GetWindowTextLength (hwndText); + int [] newStart = new int [1], newEnd = new int [1]; + OS.SendMessage (hwndText, OS.EM_GETSEL, newStart, newEnd); + if (length != 0 && newStart [0] != newEnd [0]) { + TCHAR buffer = new TCHAR (getCodePage (), length + 1); + OS.GetWindowText (hwndText, buffer, length + 1); + newText = buffer.toString (newStart [0], newEnd [0] - newStart [0]); + } else { + newText = ""; + } + OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam); + OS.SendMessage (hwndText, OS.EM_GETSEL, start, end); + ignoreModify = false; + } + break; + } + if (newText != null) { + String oldText = newText; + newText = verifyText (newText, start [0], end [0], null); + if (newText == null) return LRESULT.ZERO; + if (!newText.equals (oldText)) { + if (call) { + OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam); + } + TCHAR buffer = new TCHAR (getCodePage (), newText, true); + if (msg == OS.WM_SETTEXT) { + int /*long*/ hHeap = OS.GetProcessHeap (); + int byteCount = buffer.length () * TCHAR.sizeof; + int /*long*/ pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (pszText, buffer, byteCount); + int /*long*/ code = OS.CallWindowProc (EditProc, hwndText, msg, wParam, pszText); + OS.HeapFree (hHeap, 0, pszText); + return new LRESULT (code); + } else { + OS.SendMessage (hwndText, OS.EM_REPLACESEL, 0, buffer); + return LRESULT.ZERO; + } + } + } + return null; +} + +LRESULT wmCommandChild (int /*long*/ wParam, int /*long*/ lParam) { + int code = OS.HIWORD (wParam); + switch (code) { + case OS.EN_CHANGE: + if (ignoreModify) break; + boolean [] parseFail = new boolean [1]; + int value = getSelectionText (parseFail); + if (!parseFail [0]) { + int pos; + if (OS.IsWinCE) { + pos = OS.LOWORD (OS.SendMessage (hwndUpDown, OS.UDM_GETPOS, 0, 0)); + } else { + pos = (int)/*64*/OS.SendMessage (hwndUpDown, OS.UDM_GETPOS32, 0, 0); + } + if (pos != value) setSelection (value, true, false, true); + } + sendEvent (SWT.Modify); + if (isDisposed ()) return LRESULT.ZERO; + break; + } + return super.wmCommandChild (wParam, lParam); +} + +LRESULT wmKeyDown (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.wmKeyDown (hwnd, wParam, lParam); + if (result != null) return result; + + /* Increment the value */ + UDACCEL udaccel = new UDACCEL (); + OS.SendMessage (hwndUpDown, OS.UDM_GETACCEL, 1, udaccel); + int delta = 0; + switch ((int)/*64*/wParam) { + case OS.VK_UP: delta = udaccel.nInc; break; + case OS.VK_DOWN: delta = -udaccel.nInc; break; + case OS.VK_PRIOR: delta = pageIncrement; break; + case OS.VK_NEXT: delta = -pageIncrement; break; + } + if (delta != 0) { + boolean [] parseFail = new boolean [1]; + int value = getSelectionText (parseFail); + if (parseFail [0]) { + if (OS.IsWinCE) { + value = OS.LOWORD (OS.SendMessage (hwndUpDown, OS.UDM_GETPOS, 0, 0)); + } else { + value = (int)/*64*/OS.SendMessage (hwndUpDown, OS.UDM_GETPOS32, 0, 0); + } + } + int newValue = value + delta; + int [] max = new int [1], min = new int [1]; + OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, min, max); + if ((style & SWT.WRAP) != 0) { + if (newValue < min [0]) newValue = max [0]; + if (newValue > max [0]) newValue = min [0]; + } + newValue = Math.min (Math.max (min [0], newValue), max [0]); + if (value != newValue) setSelection (newValue, true, true, true); + } + + /* Stop the edit control from moving the caret */ + switch ((int)/*64*/wParam) { + case OS.VK_UP: + case OS.VK_DOWN: + return LRESULT.ZERO; + } + return result; +} + +LRESULT wmKillFocus (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + boolean [] parseFail = new boolean [1]; + int value = getSelectionText (parseFail); + if (parseFail [0]) { + if (OS.IsWinCE) { + value = OS.LOWORD (OS.SendMessage (hwndUpDown, OS.UDM_GETPOS, 0, 0)); + } else { + value = (int)/*64*/OS.SendMessage (hwndUpDown, OS.UDM_GETPOS32, 0, 0); + } + setSelection (value, false, true, false); + } + return super.wmKillFocus (hwnd, wParam, lParam); +} + +LRESULT wmNotifyChild (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + switch (hdr.code) { + case OS.UDN_DELTAPOS: + NMUPDOWN lpnmud = new NMUPDOWN (); + OS.MoveMemory (lpnmud, lParam, NMUPDOWN.sizeof); + int value = lpnmud.iPos + lpnmud.iDelta; + int [] max = new int [1], min = new int [1]; + OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, min, max); + if ((style & SWT.WRAP) != 0) { + if (value < min [0]) value = max [0]; + if (value > max [0]) value = min [0]; + } + /* + * The SWT.Modify event is sent after the widget has been + * updated with the new state. Rather than allowing + * the default updown window proc to set the value + * when the user clicks on the updown control, set + * the value explicitly and stop the window proc + * from running. + */ + value = Math.min (Math.max (min [0], value), max [0]); + if (value != lpnmud.iPos) { + setSelection (value, true, true, true); + } + return LRESULT.ONE; + } + return super.wmNotifyChild (hdr, wParam, lParam); +} + +LRESULT wmScrollChild (int /*long*/ wParam, int /*long*/ lParam) { + int code = OS.LOWORD (wParam); + switch (code) { + case OS.SB_THUMBPOSITION: + postEvent (SWT.Selection); + break; + } + return super.wmScrollChild (wParam, lParam); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TabFolder.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TabFolder.java new file mode 100755 index 0000000000..0583d9c5d4 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TabFolder.java @@ -0,0 +1,1010 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * 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; + ImageList imageList; + static final int /*long*/ TabFolderProc; + static final TCHAR TabFolderClass = new TCHAR (0, OS.WC_TABCONTROL, true); + + /* + * These are the undocumented control id's for the children of + * a tab control. Since there are no constants for these values, + * they may change with different versions of Windows. + */ + static final int ID_UPDOWN = 1; + + static { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, TabFolderClass, lpWndClass); + TabFolderProc = lpWndClass.lpfnWndProc; + /* + * Feature in Windows. The tab control window class + * uses the CS_HREDRAW and CS_VREDRAW style bits to + * force a full redraw of the control and all children + * when resized. This causes flashing. The fix is to + * register a new window class without these bits and + * implement special code that damages only the exposed + * area. + * + * NOTE: Screen readers look for the exact class name + * of the control in order to provide the correct kind + * of assistance. Therefore, it is critical that the + * new window class have the same name. It is possible + * to register a local window class with the same name + * as a global class. Since bits that affect the class + * are being changed, it is possible that other native + * code, other than SWT, could create a control with + * this class name, and fail unexpectedly. + */ + int /*long*/ hInstance = OS.GetModuleHandle (null); + int /*long*/ hHeap = OS.GetProcessHeap (); + lpWndClass.hInstance = hInstance; + lpWndClass.style &= ~(OS.CS_HREDRAW | OS.CS_VREDRAW | OS.CS_GLOBALCLASS); + int byteCount = TabFolderClass.length () * TCHAR.sizeof; + int /*long*/ lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (lpszClassName, TabFolderClass, byteCount); + lpWndClass.lpszClassName = lpszClassName; + OS.RegisterClass (lpWndClass); + OS.HeapFree (hHeap, 0, lpszClassName); + } + +/** + * 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); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + return OS.CallWindowProc (TabFolderProc, hwnd, msg, wParam, lParam); +} + +static int checkStyle (int style) { + /* + * When the SWT.TOP style has not been set, force the + * tabs to be on the bottom for tab folders on PPC. + */ + if (OS.IsPPC) { + if ((style & SWT.TOP) == 0) style |= SWT.BOTTOM; + } + 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) { + checkWidget (); + Point size = super.computeSize (wHint, hHint, changed); + RECT insetRect = new RECT (), itemRect = new RECT (); + OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 0, insetRect); + int width = insetRect.left - insetRect.right; + int count = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0); + if (count != 0) { + OS.SendMessage (handle, OS.TCM_GETITEMRECT, count - 1, itemRect); + width = Math.max (width, itemRect.right - insetRect.right); + } + RECT rect = new RECT (); + OS.SetRect (rect, 0, 0, width, size.y); + OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 1, rect); + int border = getBorderWidth (); + rect.left -= border; rect.right += border; + width = rect.right - rect.left; + size.x = Math.max (width, size.x); + return size; +} + +public Rectangle computeTrim (int x, int y, int width, int height) { + checkWidget (); + RECT rect = new RECT (); + OS.SetRect (rect, x, y, x + width, y + height); + OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 1, rect); + int border = getBorderWidth (); + rect.left -= border; rect.right += border; + rect.top -= border; rect.bottom += border; + int newWidth = rect.right - rect.left; + int newHeight = rect.bottom - rect.top; + return new Rectangle (rect.left, rect.top, newWidth, newHeight); +} + +void createItem (TabItem item, int index) { + int count = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0); + 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; + } + TCITEM tcItem = new TCITEM (); + if (OS.SendMessage (handle, OS.TCM_INSERTITEM, index, tcItem) == -1) { + error (SWT.ERROR_ITEM_NOT_ADDED); + } + System.arraycopy (items, index, items, index + 1, count - index); + items [index] = item; + + /* + * Send a selection event when the item that is added becomes + * the new selection. This only happens when the first item + * is added. + */ + if (count == 0) { + Event event = new Event (); + event.item = items [0]; + sendEvent (SWT.Selection, event); + // the widget could be destroyed at this point + } +} + +void createHandle () { + super.createHandle (); + state &= ~(CANVAS | THEME_BACKGROUND); + + /* Enable the flat look for tab folders on PPC */ + if (OS.IsPPC) { + OS.SendMessage (handle, OS.CCM_SETVERSION, 0x020c /*COMCTL32_VERSION*/, 0); + } + + /* + * Feature in Windows. Despite the fact that the + * tool tip text contains \r\n, the tooltip will + * not honour the new line unless TTM_SETMAXTIPWIDTH + * is set. The fix is to set TTM_SETMAXTIPWIDTH to + * a large value. + */ + int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.TCM_GETTOOLTIPS, 0, 0); + OS.SendMessage (hwndToolTip, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF); +} + +void createWidget () { + super.createWidget (); + items = new TabItem [4]; +} + +void destroyItem (TabItem item) { + int count = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0); + int index = 0; + while (index < count) { + if (items [index] == item) break; + index++; + } + if (index == count) return; + int selectionIndex = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0); + if (OS.SendMessage (handle, OS.TCM_DELETEITEM, index, 0) == 0) { + error (SWT.ERROR_ITEM_NOT_REMOVED); + } + System.arraycopy (items, index + 1, items, index, --count - index); + items [count] = null; + if (count == 0) { + if (imageList != null) { + OS.SendMessage (handle, OS.TCM_SETIMAGELIST, 0, 0); + display.releaseImageList (imageList); + } + imageList = null; + items = new TabItem [4]; + } + if (count > 0 && index == selectionIndex) { + setSelection (Math.max (0, selectionIndex - 1), true); + } +} + +void drawThemeBackground (int /*long*/ hDC, int /*long*/ hwnd, RECT rect) { + RECT rect2 = new RECT (); + OS.GetClientRect (handle, rect2); + OS.MapWindowPoints (handle, hwnd, rect2, 2); + if (OS.IntersectRect (new RECT (), rect2, rect)) { + OS.DrawThemeBackground (display.hTabTheme (), hDC, OS.TABP_BODY, 0, rect2, null); + } +} + +Control findThemeControl () { + /* It is not possible to change the background of this control */ + return this; +} + +public Rectangle getClientArea () { + checkWidget (); + forceResize (); + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 0, rect); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + return new Rectangle (rect.left, rect.top, 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 = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0); + 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); + TCHITTESTINFO pinfo = new TCHITTESTINFO (); + pinfo.x = point.x; + pinfo.y = point.y; + int index = (int)/*64*/OS.SendMessage (handle, OS.TCM_HITTEST, 0, pinfo); + if (index == -1) return null; + 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 (int)/*64*/OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0); +} + +/** + * 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 = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0); + 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 = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0); + 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 (); + return (int)/*64*/OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0); +} + +int imageIndex (Image image) { + if (image == null) return OS.I_IMAGENONE; + if (imageList == null) { + Rectangle bounds = image.getBounds (); + imageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height); + int index = imageList.add (image); + int /*long*/ hImageList = imageList.getHandle (); + OS.SendMessage (handle, OS.TCM_SETIMAGELIST, 0, hImageList); + return index; + } + int index = imageList.indexOf (image); + if (index == -1) { + index = imageList.add (image); + } else { + imageList.put (index, image); + } + return index; +} + +/** + * 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 = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0); + 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 = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0); + 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); +} + +boolean mnemonicHit (char key) { + for (int i=0; i<items.length; i++) { + TabItem item = items [i]; + if (item != null) { + char ch = findMnemonic (item.getText ()); + if (Character.toUpperCase (key) == Character.toUpperCase (ch)) { + if (forceFocus ()) { + if (i != getSelectionIndex ()) setSelection (i, true); + return true; + } + } + } + } + return false; +} + +boolean mnemonicMatch (char key) { + for (int i=0; i<items.length; i++) { + TabItem item = items [i]; + if (item != null) { + char ch = findMnemonic (item.getText ()); + if (Character.toUpperCase (key) == Character.toUpperCase (ch)) { + return true; + } + } + } + return false; +} + +void releaseChildren (boolean destroy) { + if (items != null) { + int count = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0); + for (int i=0; i<count; i++) { + TabItem item = items [i]; + if (item != null && !item.isDisposed ()) { + item.release (false); + } + } + items = null; + } + super.releaseChildren (destroy); +} + +void releaseWidget () { + super.releaseWidget (); + if (imageList != null) { + OS.SendMessage (handle, OS.TCM_SETIMAGELIST, 0, 0); + display.releaseImageList (imageList); + } + imageList = null; +} + +void removeControl (Control control) { + super.removeControl (control); + int count = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0); + 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); +} + +/** + * 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); + } else { + for (int i=items.length-1; i>=0; --i) { + int index = indexOf (items [i]); + if (index != -1) setSelection (index, false); + } + } +} + +public void setFont (Font font) { + checkWidget (); + Rectangle oldRect = getClientArea (); + super.setFont (font); + Rectangle newRect = getClientArea (); + if (!oldRect.equals (newRect)) { + sendResize (); + int index = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0); + if (index != -1) { + TabItem item = items [index]; + Control control = item.control; + if (control != null && !control.isDisposed ()) { + control.setBounds (getClientArea ()); + } + } + } +} + +/** + * 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 = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0); + if (!(0 <= index && index < count)) return; + setSelection (index, false); +} + +void setSelection (int index, boolean notify) { + int oldIndex = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0); + if (oldIndex == index) return; + if (oldIndex != -1) { + TabItem item = items [oldIndex]; + Control control = item.control; + if (control != null && !control.isDisposed ()) { + control.setVisible (false); + } + } + OS.SendMessage (handle, OS.TCM_SETCURSEL, index, 0); + int newIndex = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0); + if (newIndex != -1) { + TabItem item = items [newIndex]; + Control control = item.control; + if (control != null && !control.isDisposed ()) { + control.setBounds (getClientArea ()); + control.setVisible (true); + } + if (notify) { + Event event = new Event (); + event.item = item; + sendEvent (SWT.Selection, event); + } + } +} + +String toolTipText (NMTTDISPINFO hdr) { + if ((hdr.uFlags & OS.TTF_IDISHWND) != 0) { + return null; + } + int index = (int)/*64*/hdr.idFrom; + int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.TCM_GETTOOLTIPS, 0, 0); + if (hwndToolTip == hdr.hwndFrom) { + /* + * Bug in Windows. For some reason the reading order + * in NMTTDISPINFO is sometimes set incorrectly. The + * reading order seems to change every time the mouse + * enters the control from the top edge. The fix is + * to explicitly set TTF_RTLREADING. + */ + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + hdr.uFlags |= OS.TTF_RTLREADING; + } else { + hdr.uFlags &= ~OS.TTF_RTLREADING; + } + if (toolTipText != null) return ""; + if (0 <= index && index < items.length) { + TabItem item = items [index]; + if (item != null) return item.toolTipText; + } + } + return super.toolTipText (hdr); +} + +boolean traversePage (boolean next) { + int count = getItemCount (); + if (count <= 1) return false; + int index = getSelectionIndex (); + if (index == -1) { + index = 0; + } else { + int offset = (next) ? 1 : -1; + index = (index + offset + count) % count; + } + setSelection (index, true); + if (index == getSelectionIndex ()) { + OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0); + return true; + } + return false; +} + +int widgetStyle () { + /* + * Bug in Windows. Under certain circumstances, + * when TCM_SETITEM is used to change the text + * in a tab item, the tab folder draws on top + * of the client area. The fix is ensure that + * this cannot happen by setting WS_CLIPCHILDREN. + */ + int bits = super.widgetStyle () | OS.WS_CLIPCHILDREN; + if ((style & SWT.NO_FOCUS) != 0) bits |= OS.TCS_FOCUSNEVER; + if ((style & SWT.BOTTOM) != 0) bits |= OS.TCS_BOTTOM; + return bits | OS.TCS_TABS | OS.TCS_TOOLTIPS; +} + +TCHAR windowClass () { + return TabFolderClass; +} + +int /*long*/ windowProc () { + return TabFolderProc; +} + +LRESULT WM_GETDLGCODE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_GETDLGCODE (wParam, lParam); + /* + * Return DLGC_BUTTON so that mnemonics will be + * processed without needing to press the ALT key + * when the widget has focus. + */ + if (result != null) return result; + return new LRESULT (OS.DLGC_BUTTON | OS.DLGC_WANTARROWS); +} + +LRESULT WM_MOUSELEAVE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_MOUSELEAVE (wParam, lParam); + if (result != null) return result; + /* + * Bug in Windows. On XP, when a tooltip is + * hidden due to a time out or mouse press, + * the tooltip remains active although no + * longer visible and won't show again until + * another tooltip becomes active. If there + * is only one tooltip in the window, it will + * never show again. The fix is to remove the + * current tooltip and add it again every time + * the mouse leaves the control. + */ + if (OS.COMCTL32_MAJOR >= 6) { + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.TCM_GETTOOLTIPS, 0, 0); + if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) != 0) { + if ((lpti.uFlags & OS.TTF_IDISHWND) == 0) { + OS.SendMessage (hwndToolTip, OS.TTM_DELTOOL, 0, lpti); + OS.SendMessage (hwndToolTip, OS.TTM_ADDTOOL, 0, lpti); + } + } + } + return result; +} + +LRESULT WM_NCHITTEST (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_NCHITTEST (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. The tab control implements + * WM_NCHITTEST to return HTCLIENT when the cursor + * is inside the tab buttons. This causes mouse + * events like WM_MOUSEMOVE to be delivered to the + * parent. Also, tool tips for the tab control are + * never invoked because tool tips rely on mouse + * events to be delivered to the window that wants + * to display the tool tip. The fix is to call the + * default window proc that returns HTCLIENT when + * the mouse is in the client area. + */ + int /*long*/ hittest = OS.DefWindowProc (handle, OS.WM_NCHITTEST, wParam, lParam); + return new LRESULT (hittest); +} + +LRESULT WM_NOTIFY (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. When the tab folder window + * proc processes WM_NOTIFY, it forwards this + * message to its parent. This is done so that + * children of this control that send this message + * type to their parent will notify not only + * this control but also the parent of this control, + * which is typically the application window and + * the window that is looking for the message. + * If the control did not forward the message, + * applications would have to subclass the control + * window to see the message. Because the control + * window is subclassed by SWT, the message + * is delivered twice, once by SWT and once when + * the message is forwarded by the window proc. + * The fix is to avoid calling the window proc + * for this control. + */ + LRESULT result = super.WM_NOTIFY (wParam, lParam); + if (result != null) return result; + return LRESULT.ZERO; +} + +LRESULT WM_PARENTNOTIFY (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_PARENTNOTIFY (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. Windows does not explicitly set the orientation of + * the buddy control. Instead, the orientation is inherited when WS_EX_LAYOUTRTL + * is specified for the tab folder. This means that when both WS_EX_LAYOUTRTL + * and WS_EX_NOINHERITLAYOUT are specified for the tab folder, the buddy control + * will not be oriented correctly. The fix is to explicitly set the orientation + * for the buddy control. + * + * NOTE: WS_EX_LAYOUTRTL is not supported on Windows NT. + */ + if (OS.WIN32_VERSION < OS.VERSION (4, 10)) return result; + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + int code = OS.LOWORD (wParam); + switch (code) { + case OS.WM_CREATE: { + int id = OS.HIWORD (wParam); + int /*long*/ hwnd = lParam; + if (id == ID_UPDOWN) { + int bits = OS.GetWindowLong (hwnd, OS.GWL_EXSTYLE); + OS.SetWindowLong (hwnd, OS.GWL_EXSTYLE, bits | OS.WS_EX_LAYOUTRTL); + } + break; + } + } + } + return result; +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SIZE (wParam, lParam); + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the resize + * event. If this happens, end the processing of the + * Windows message by returning the result of the + * WM_SIZE message. + */ + if (isDisposed ()) return result; + int index = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0); + if (index != -1) { + TabItem item = items [index]; + Control control = item.control; + if (control != null && !control.isDisposed ()) { + control.setBounds (getClientArea ()); + } + } + return result; +} + +LRESULT WM_WINDOWPOSCHANGING (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_WINDOWPOSCHANGING (wParam, lParam); + if (result != null) return result; + if (!OS.IsWindowVisible (handle)) return result; + WINDOWPOS lpwp = new WINDOWPOS (); + OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof); + if ((lpwp.flags & (OS.SWP_NOSIZE | OS.SWP_NOREDRAW)) != 0) { + return result; + } + // TEMPORARY CODE +// if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { +// OS.InvalidateRect (handle, null, true); +// return result; +// } + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.TCS_MULTILINE) != 0) { + OS.InvalidateRect (handle, null, true); + return result; + } + RECT rect = new RECT (); + OS.SetRect (rect, 0, 0, lpwp.cx, lpwp.cy); + OS.SendMessage (handle, OS.WM_NCCALCSIZE, 0, rect); + int newWidth = rect.right - rect.left; + int newHeight = rect.bottom - rect.top; + OS.GetClientRect (handle, rect); + int oldWidth = rect.right - rect.left; + int oldHeight = rect.bottom - rect.top; + if (newWidth == oldWidth && newHeight == oldHeight) { + return result; + } + RECT inset = new RECT (); + OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 0, inset); + int marginX = -inset.right, marginY = -inset.bottom; + if (newWidth != oldWidth) { + int left = oldWidth; + if (newWidth < oldWidth) left = newWidth; + OS.SetRect (rect, left - marginX, 0, newWidth, newHeight); + OS.InvalidateRect (handle, rect, true); + } + if (newHeight != oldHeight) { + int bottom = oldHeight; + if (newHeight < oldHeight) bottom = newHeight; + if (newWidth < oldWidth) oldWidth -= marginX; + OS.SetRect (rect, 0, bottom - marginY, oldWidth, newHeight); + OS.InvalidateRect (handle, rect, true); + } + return result; +} + +LRESULT wmNotifyChild (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + int code = hdr.code; + switch (code) { + case OS.TCN_SELCHANGE: + case OS.TCN_SELCHANGING: + TabItem item = null; + int index = (int)/*64*/OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0); + if (index != -1) item = items [index]; + if (item != null) { + Control control = item.control; + if (control != null && !control.isDisposed ()) { + if (code == OS.TCN_SELCHANGE) { + control.setBounds (getClientArea ()); + } + control.setVisible (code == OS.TCN_SELCHANGE); + } + } + if (code == OS.TCN_SELCHANGE) { + Event event = new Event (); + event.item = item; + postEvent (SWT.Selection, event); + } + } + return super.wmNotifyChild (hdr, wParam, lParam); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TabItem.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TabItem.java new file mode 100755 index 0000000000..b3d441e71f --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TabItem.java @@ -0,0 +1,372 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; + +/** + * 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; + +/** + * 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); +} + +void _setText (int index, String string) { + /* + * Bug in Windows. In version 6.00 of COMCTL32.DLL, tab + * items with an image and a label that includes '&' cause + * the tab to draw incorrectly (even when doubled '&&'). + * The image overlaps the label. The fix is to remove + * all '&' characters from the string. + */ + if (OS.COMCTL32_MAJOR >= 6 && image != null) { + if (string.indexOf ('&') != -1) { + int length = string.length (); + char[] text = new char [length]; + string.getChars ( 0, length, text, 0); + int i = 0, j = 0; + for (i=0; i<length; i++) { + if (text[i] != '&') text [j++] = text [i]; + } + if (j < i) string = new String (text, 0, j); + } + } + int /*long*/ hwnd = parent.handle; + int /*long*/ hHeap = OS.GetProcessHeap (); + TCHAR buffer = new TCHAR (parent.getCodePage (), string, true); + int byteCount = buffer.length () * TCHAR.sizeof; + int /*long*/ pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (pszText, buffer, byteCount); + TCITEM tcItem = new TCITEM (); + tcItem.mask = OS.TCIF_TEXT; + tcItem.pszText = pszText; + OS.SendMessage (hwnd, OS.TCM_SETITEM, index, tcItem); + OS.HeapFree (hHeap, 0, pszText); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +void destroyWidget () { + parent.destroyItem (this); + releaseHandle (); +} + +/** + * 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 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(); + int index = parent.indexOf(this); + if (index == -1) return new Rectangle (0, 0, 0, 0); + RECT itemRect = new RECT (); + OS.SendMessage (parent.handle, OS.TCM_GETITEMRECT, index, itemRect); + return new Rectangle(itemRect.left, itemRect.top, itemRect.right - itemRect.left, itemRect.bottom - itemRect.top); +} + +/** + * 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 (); + 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) { + if (selectionIndex != -1) { + Control selectedControl = parent.getItem(selectionIndex).getControl(); + if (selectedControl == newControl) return; + } + newControl.setVisible(false); + return; + } + } + if (newControl != null) { + newControl.setBounds (parent.getClientArea ()); + newControl.setVisible (true); + } + if (oldControl != null) oldControl.setVisible (false); +} + +public void setImage (Image image) { + checkWidget(); + int index = parent.indexOf (this); + if (index == -1) return; + super.setImage (image); + /* + * Bug in Windows. In version 6.00 of COMCTL32.DLL, tab + * items with an image and a label that includes '&' cause + * the tab to draw incorrectly (even when doubled '&&'). + * The image overlaps the label. The fix is to remove + * all '&' characters from the string and set the text + * whenever the image or text is changed. + */ + if (OS.COMCTL32_MAJOR >= 6) { + if (text.indexOf ('&') != -1) _setText (index, text); + } + int /*long*/ hwnd = parent.handle; + TCITEM tcItem = new TCITEM (); + tcItem.mask = OS.TCIF_IMAGE; + tcItem.iImage = parent.imageIndex (image); + OS.SendMessage (hwnd, OS.TCM_SETITEM, index, tcItem); +} +/** + * 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 (string.equals (text)) return; + int index = parent.indexOf (this); + if (index == -1) return; + super.setText (string); + _setText (index, string); +} + +/** + * 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; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Table.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Table.java new file mode 100755 index 0000000000..48a1f8e0fb --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Table.java @@ -0,0 +1,6966 @@ +/******************************************************************************* + * 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 java.util.*; + +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * 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; + int columnCount, customCount; + ImageList imageList, headerImageList; + TableItem currentItem; + TableColumn sortColumn; + RECT focusRect; + int /*long*/ headerToolTipHandle; + boolean ignoreCustomDraw, ignoreDrawForeground, ignoreDrawBackground, ignoreDrawFocus, ignoreDrawSelection, ignoreDrawHot; + boolean customDraw, dragStarted, explorerTheme, firstColumnImage, fixScrollWidth, tipRequested, wasSelected, wasResized, painted; + boolean ignoreActivate, ignoreSelect, ignoreShrink, ignoreResize, ignoreColumnMove, ignoreColumnResize, fullRowSelect; + int itemHeight, lastIndexOf, lastWidth, sortDirection, resizeCount, selectionForeground, hotIndex; + static /*final*/ int /*long*/ HeaderProc; + static final int INSET = 4; + static final int GRID_WIDTH = 1; + static final int SORT_WIDTH = 10; + static final int HEADER_MARGIN = 12; + static final int HEADER_EXTRA = 3; + static final int VISTA_EXTRA = 2; + static final int EXPLORER_EXTRA = 2; + static final int H_SCROLL_LIMIT = 32; + static final int V_SCROLL_LIMIT = 16; + static final int DRAG_IMAGE_SIZE = 301; + static final boolean EXPLORER_THEME = true; + static final int /*long*/ TableProc; + static final TCHAR TableClass = new TCHAR (0, OS.WC_LISTVIEW, true); + static { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, TableClass, lpWndClass); + TableProc = lpWndClass.lpfnWndProc; + } + +/** + * 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)); +} + +void _addListener (int eventType, Listener listener) { + super._addListener (eventType, listener); + switch (eventType) { + case SWT.MeasureItem: + case SWT.EraseItem: + case SWT.PaintItem: + setCustomDraw (true); + setBackgroundTransparent (true); + if (OS.COMCTL32_MAJOR < 6) style |= SWT.DOUBLE_BUFFERED; + if (OS.IsWinCE) OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_LABELTIP, 0); + break; + } +} + +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.NONE, -1, false); +} + +/** + * 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); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + return callWindowProc (hwnd, msg, wParam, lParam, false); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam, boolean forceSelect) { + if (handle == 0) return 0; + if (handle != hwnd) { + return OS.CallWindowProc (HeaderProc, hwnd, msg, wParam, lParam); + } + int topIndex = 0; + boolean checkSelection = false, checkActivate = false, redraw = false; + switch (msg) { + /* Keyboard messages */ + /* + * Feature in Windows. Windows sends LVN_ITEMACTIVATE from WM_KEYDOWN + * instead of WM_CHAR. This means that application code that expects + * to consume the key press and therefore avoid a SWT.DefaultSelection + * event will fail. The fix is to ignore LVN_ITEMACTIVATE when it is + * caused by WM_KEYDOWN and send SWT.DefaultSelection from WM_CHAR. + */ + case OS.WM_KEYDOWN: + checkActivate = true; + //FALL THROUGH + case OS.WM_CHAR: + case OS.WM_IME_CHAR: + case OS.WM_KEYUP: + case OS.WM_SYSCHAR: + case OS.WM_SYSKEYDOWN: + case OS.WM_SYSKEYUP: + //FALL THROUGH + + /* Scroll messages */ + case OS.WM_HSCROLL: + case OS.WM_VSCROLL: + //FALL THROUGH + + /* Resize messages */ + case OS.WM_WINDOWPOSCHANGED: + redraw = findImageControl () != null && getDrawing () && OS.IsWindowVisible (handle); + if (redraw) { + /* + * Feature in Windows. When LVM_SETBKCOLOR is used with CLR_NONE + * to make the background of the table transparent, drawing becomes + * slow. The fix is to temporarily clear CLR_NONE when redraw is + * turned off. + */ + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, 0xFFFFFF); + } + //FALL THROUGH + + /* Mouse messages */ + case OS.WM_LBUTTONDBLCLK: + case OS.WM_LBUTTONDOWN: + case OS.WM_LBUTTONUP: + case OS.WM_MBUTTONDBLCLK: + case OS.WM_MBUTTONDOWN: + case OS.WM_MBUTTONUP: + case OS.WM_MOUSEHOVER: + case OS.WM_MOUSELEAVE: + case OS.WM_MOUSEMOVE: + case OS.WM_MOUSEWHEEL: + case OS.WM_RBUTTONDBLCLK: + case OS.WM_RBUTTONDOWN: + case OS.WM_RBUTTONUP: + case OS.WM_XBUTTONDBLCLK: + case OS.WM_XBUTTONDOWN: + case OS.WM_XBUTTONUP: + checkSelection = true; + //FALL THROUGH + + /* Other messages */ + case OS.WM_SETFONT: + case OS.WM_TIMER: { + if (findImageControl () != null) { + topIndex = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0); + } + } + } + boolean oldSelected = wasSelected; + if (checkSelection) wasSelected = false; + if (checkActivate) ignoreActivate = true; + + /* + * Bug in Windows. For some reason, when the WS_EX_COMPOSITED + * style is set in a parent of a table and the header is visible, + * Windows issues an endless stream of WM_PAINT messages. The + * fix is to call BeginPaint() and EndPaint() outside of WM_PAINT + * and pass the paint HDC in to the window proc. + */ + boolean fixPaint = false; + if (msg == OS.WM_PAINT) { + int bits0 = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits0 & OS.LVS_NOCOLUMNHEADER) == 0) { + int /*long*/ hwndParent = OS.GetParent (handle), hwndOwner = 0; + while (hwndParent != 0) { + int bits1 = OS.GetWindowLong (hwndParent, OS.GWL_EXSTYLE); + if ((bits1 & OS.WS_EX_COMPOSITED) != 0) { + fixPaint = true; + break; + } + hwndOwner = OS.GetWindow (hwndParent, OS.GW_OWNER); + if (hwndOwner != 0) break; + hwndParent = OS.GetParent (hwndParent); + } + } + } + + /* Remove the scroll bars that Windows keeps automatically adding */ + boolean fixScroll = false; + if ((style & SWT.H_SCROLL) == 0 || (style & SWT.V_SCROLL) == 0) { + switch (msg) { + case OS.WM_PAINT: + case OS.WM_NCPAINT: + case OS.WM_WINDOWPOSCHANGING: { + int bits = OS.GetWindowLong (hwnd, OS.GWL_STYLE); + if ((style & SWT.H_SCROLL) == 0 && (bits & OS.WS_HSCROLL) != 0) { + fixScroll = true; + bits &= ~OS.WS_HSCROLL; + } + if ((style & SWT.V_SCROLL) == 0 && (bits & OS.WS_VSCROLL) != 0) { + fixScroll = true; + bits &= ~OS.WS_VSCROLL; + } + if (fixScroll) OS.SetWindowLong (handle, OS.GWL_STYLE, bits); + } + } + } + int /*long*/ code = 0; + if (fixPaint) { + PAINTSTRUCT ps = new PAINTSTRUCT (); + int /*long*/ hDC = OS.BeginPaint (hwnd, ps); + code = OS.CallWindowProc (TableProc, hwnd, OS.WM_PAINT, hDC, lParam); + OS.EndPaint (hwnd, ps); + } else { + code = OS.CallWindowProc (TableProc, hwnd, msg, wParam, lParam); + } + if (fixScroll) { + int flags = OS.RDW_FRAME | OS.RDW_INVALIDATE; + OS.RedrawWindow (handle, null, 0, flags); + } + + if (checkActivate) ignoreActivate = false; + if (checkSelection) { + if (wasSelected || forceSelect) { + Event event = new Event (); + int index = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED); + if (index != -1) event.item = _getItem (index); + postEvent (SWT.Selection, event); + } + wasSelected = oldSelected; + } + switch (msg) { + /* Keyboard messages */ + case OS.WM_KEYDOWN: + case OS.WM_CHAR: + case OS.WM_IME_CHAR: + case OS.WM_KEYUP: + case OS.WM_SYSCHAR: + case OS.WM_SYSKEYDOWN: + case OS.WM_SYSKEYUP: + //FALL THROUGH + + /* Scroll messages */ + case OS.WM_HSCROLL: + case OS.WM_VSCROLL: + //FALL THROUGH + + /* Resize messages */ + case OS.WM_WINDOWPOSCHANGED: + if (redraw) { + OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, OS.CLR_NONE); + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.InvalidateRect (handle, null, true); + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + if (hwndHeader != 0) OS.InvalidateRect (hwndHeader, null, true); + } + //FALL THROUGH + + /* Mouse messages */ + case OS.WM_LBUTTONDBLCLK: + case OS.WM_LBUTTONDOWN: + case OS.WM_LBUTTONUP: + case OS.WM_MBUTTONDBLCLK: + case OS.WM_MBUTTONDOWN: + case OS.WM_MBUTTONUP: + case OS.WM_MOUSEHOVER: + case OS.WM_MOUSELEAVE: + case OS.WM_MOUSEMOVE: + case OS.WM_MOUSEWHEEL: + case OS.WM_RBUTTONDBLCLK: + case OS.WM_RBUTTONDOWN: + case OS.WM_RBUTTONUP: + case OS.WM_XBUTTONDBLCLK: + case OS.WM_XBUTTONDOWN: + case OS.WM_XBUTTONUP: + //FALL THROUGH + + /* Other messages */ + case OS.WM_SETFONT: + case OS.WM_TIMER: { + if (findImageControl () != null) { + if (topIndex != OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)) { + OS.InvalidateRect (handle, null, true); + } + } + break; + } + + case OS.WM_PAINT: + painted = true; + break; + } + return code; +} + +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; + } + return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0); +} + +LRESULT CDDS_ITEMPOSTPAINT (NMLVCUSTOMDRAW nmcd, int /*long*/ wParam, int /*long*/ lParam) { + int /*long*/ hDC = nmcd.hdc; + if (explorerTheme && !ignoreCustomDraw) { + hotIndex = -1; + if (hooks (SWT.EraseItem) && nmcd.left != nmcd.right) { + OS.RestoreDC (hDC, -1); + } + } + /* + * Bug in Windows. When the table has the extended style + * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with + * CLR_NONE to make the table transparent, Windows fills + * a black rectangle around any column that contains an + * image. The fix is clear LVS_EX_FULLROWSELECT during + * custom draw. + * + * NOTE: Since CDIS_FOCUS is cleared during custom draw, + * it is necessary to draw the focus rectangle after the + * item has been drawn. + */ + if (!ignoreCustomDraw && !ignoreDrawFocus && nmcd.left != nmcd.right) { + if (OS.IsWindowVisible (handle) && OS.IsWindowEnabled (handle)) { + if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) { + if ((int)/*64*/OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) { + int dwExStyle = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) == 0) { +// if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) { + if (OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED) == nmcd.dwItemSpec) { + if (handle == OS.GetFocus ()) { + int uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + if ((uiState & OS.UISF_HIDEFOCUS) == 0) { + RECT rect = new RECT (); + rect.left = OS.LVIR_BOUNDS; + boolean oldIgnore = ignoreCustomDraw; + ignoreCustomDraw = true; + OS.SendMessage (handle, OS. LVM_GETITEMRECT, nmcd.dwItemSpec, rect); + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + int index = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0); + RECT itemRect = new RECT (); + if (index == 0) { + itemRect.left = OS.LVIR_LABEL; + OS.SendMessage (handle, OS. LVM_GETITEMRECT, index, itemRect); + } else { + itemRect.top = index; + itemRect.left = OS.LVIR_ICON; + OS.SendMessage (handle, OS. LVM_GETSUBITEMRECT, nmcd.dwItemSpec, itemRect); + } + ignoreCustomDraw = oldIgnore; + rect.left = itemRect.left; + OS.DrawFocusRect (nmcd.hdc, rect); + } + } + } + } + } + } + } + } + return null; +} + +LRESULT CDDS_ITEMPREPAINT (NMLVCUSTOMDRAW nmcd, int /*long*/ wParam, int /*long*/ lParam) { + /* + * Bug in Windows. When the table has the extended style + * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with + * CLR_NONE to make the table transparent, Windows fills + * a black rectangle around any column that contains an + * image. The fix is clear LVS_EX_FULLROWSELECT during + * custom draw. + * + * NOTE: It is also necessary to clear CDIS_FOCUS to stop + * the table from drawing the focus rectangle around the + * first item instead of the full row. + */ + if (!ignoreCustomDraw) { + if (OS.IsWindowVisible (handle) && OS.IsWindowEnabled (handle)) { + if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) { + if ((int)/*64*/OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) { + int dwExStyle = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) == 0) { + if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) { + nmcd.uItemState &= ~OS.CDIS_FOCUS; + OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof); + } + } + } + } + } + } + if (explorerTheme && !ignoreCustomDraw) { + hotIndex = (nmcd.uItemState & OS.CDIS_HOT) != 0 ? (int)/*64*/nmcd.dwItemSpec : -1; + if (hooks (SWT.EraseItem) && nmcd.left != nmcd.right) { + OS.SaveDC (nmcd.hdc); + int /*long*/ hrgn = OS.CreateRectRgn (0, 0, 0, 0); + OS.SelectClipRgn (nmcd.hdc, hrgn); + OS.DeleteObject (hrgn); + } + } + return new LRESULT (OS.CDRF_NOTIFYSUBITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT); +} + +LRESULT CDDS_POSTPAINT (NMLVCUSTOMDRAW nmcd, int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreCustomDraw) return null; + /* + * Bug in Windows. When the table has the extended style + * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with + * CLR_NONE to make the table transparent, Windows fills + * a black rectangle around any column that contains an + * image. The fix is clear LVS_EX_FULLROWSELECT during + * custom draw. + */ + if (--customCount == 0 && OS.IsWindowVisible (handle)) { + if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) { + if ((int)/*64*/OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) { + int dwExStyle = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) == 0) { + int bits = OS.LVS_EX_FULLROWSELECT; + /* + * Feature in Windows. When LVM_SETEXTENDEDLISTVIEWSTYLE is + * used to set or clear the extended style bits and the table + * has a tooltip, the tooltip is hidden. The fix is to clear + * the tooltip before setting the bits and then reset it. + */ + int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, 0, 0); + if (OS.IsWinCE) { + RECT rect = new RECT (); + boolean damaged = OS.GetUpdateRect (handle, rect, true); + OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, bits); + OS.ValidateRect (handle, null); + if (damaged) OS.InvalidateRect (handle, rect, true); + } else { + int /*long*/ rgn = OS.CreateRectRgn (0, 0, 0, 0); + int result = OS.GetUpdateRgn (handle, rgn, true); + OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, bits); + OS.ValidateRect (handle, null); + if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true); + OS.DeleteObject (rgn); + } + /* + * Bug in Windows. Despite the documentation, LVM_SETTOOLTIPS + * uses WPARAM instead of LPARAM for the new tooltip The fix + * is to put the tooltip in both parameters. + */ + hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, hwndToolTip, hwndToolTip); + } + } + } + } + return null; +} + +LRESULT CDDS_PREPAINT (NMLVCUSTOMDRAW nmcd, int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreCustomDraw) { + return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT); + } + /* + * Bug in Windows. When the table has the extended style + * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with + * CLR_NONE to make the table transparent, Windows fills + * a black rectangle around any column that contains an + * image. The fix is clear LVS_EX_FULLROWSELECT during + * custom draw. + */ + if (customCount++ == 0 && OS.IsWindowVisible (handle)) { + if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) { + if ((int)/*64*/OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) { + int dwExStyle = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) != 0) { + int bits = OS.LVS_EX_FULLROWSELECT; + /* + * Feature in Windows. When LVM_SETEXTENDEDLISTVIEWSTYLE is + * used to set or clear the extended style bits and the table + * has a tooltip, the tooltip is hidden. The fix is to clear + * the tooltip before setting the bits and then reset it. + */ + int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, 0, 0); + if (OS.IsWinCE) { + RECT rect = new RECT (); + boolean damaged = OS.GetUpdateRect (handle, rect, true); + OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, 0); + OS.ValidateRect (handle, null); + if (damaged) OS.InvalidateRect (handle, rect, true); + } else { + int /*long*/ rgn = OS.CreateRectRgn (0, 0, 0, 0); + int result = OS.GetUpdateRgn (handle, rgn, true); + OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, 0); + OS.ValidateRect (handle, null); + if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true); + OS.DeleteObject (rgn); + } + /* + * Bug in Windows. Despite the documentation, LVM_SETTOOLTIPS + * uses WPARAM instead of LPARAM for the new tooltip The fix + * is to put the tooltip in both parameters. + */ + hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, hwndToolTip, hwndToolTip); + } + } + } + } + if (OS.IsWindowVisible (handle)) { + boolean draw = true; + /* + * Feature in Windows. On Vista using the explorer theme, + * Windows draws a vertical line to separate columns. When + * there is only a single column, the line looks strange. + * The fix is to draw the background using custom draw. + */ + if (explorerTheme && columnCount == 0) { + int /*long*/ hDC = nmcd.hdc; + RECT rect = new RECT (); + OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom); + if (OS.IsWindowEnabled (handle) || findImageControl () != null) { + drawBackground (hDC, rect); + } else { + fillBackground (hDC, OS.GetSysColor (OS.COLOR_3DFACE), rect); + } + draw = false; + } + if (draw) { + Control control = findBackgroundControl (); + if (control != null && control.backgroundImage != null) { + RECT rect = new RECT (); + OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom); + fillImageBackground (nmcd.hdc, control, rect); + } else { + if ((int)/*64*/OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) { + if (OS.IsWindowEnabled (handle)) { + RECT rect = new RECT (); + OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom); + if (control == null) control = this; + fillBackground (nmcd.hdc, control.getBackgroundPixel (), rect); + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if (sortColumn != null && sortDirection != SWT.NONE) { + int index = indexOf (sortColumn); + if (index != -1) { + parent.forceResize (); + int clrSortBk = getSortColumnPixel (); + RECT columnRect = new RECT (), headerRect = new RECT (); + OS.GetClientRect (handle, columnRect); + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect) != 0) { + OS.MapWindowPoints (hwndHeader, handle, headerRect, 2); + columnRect.left = headerRect.left; + columnRect.right = headerRect.right; + if (OS.IntersectRect(columnRect, columnRect, rect)) { + fillBackground (nmcd.hdc, clrSortBk, columnRect); + } + } + } + } + } + } + } + } + } + } + return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT); +} + +LRESULT CDDS_SUBITEMPOSTPAINT (NMLVCUSTOMDRAW nmcd, int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreCustomDraw) return null; + if (nmcd.left == nmcd.right) return new LRESULT (OS.CDRF_DODEFAULT); + int /*long*/ hDC = nmcd.hdc; + if (ignoreDrawForeground) OS.RestoreDC (hDC, -1); + if (OS.IsWindowVisible (handle)) { + /* + * Feature in Windows. When there is a sort column, the sort column + * color draws on top of the background color for an item. The fix + * is to clear the sort column in CDDS_SUBITEMPREPAINT, and reset it + * in CDDS_SUBITEMPOSTPAINT. + */ + if ((int)/*64*/OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) != OS.CLR_NONE) { + if ((sortDirection & (SWT.UP | SWT.DOWN)) != 0) { + if (sortColumn != null && !sortColumn.isDisposed ()) { + int oldColumn = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0); + if (oldColumn == -1) { + int newColumn = indexOf (sortColumn); + int result = 0; + int /*long*/ rgn = 0; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + rgn = OS.CreateRectRgn (0, 0, 0, 0); + result = OS.GetUpdateRgn (handle, rgn, true); + } + OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, newColumn, 0); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + OS.ValidateRect (handle, null); + if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true); + OS.DeleteObject (rgn); + } + } + } + } + } + if (hooks (SWT.PaintItem)) { + TableItem item = _getItem ((int)/*64*/nmcd.dwItemSpec); + sendPaintItemEvent (item, nmcd); + //widget could be disposed at this point + } + if (!ignoreDrawFocus && focusRect != null) { + OS.SetTextColor (nmcd.hdc, 0); + OS.SetBkColor (nmcd.hdc, 0xFFFFFF); + OS.DrawFocusRect (nmcd.hdc, focusRect); + focusRect = null; + } + } + return null; +} + +LRESULT CDDS_SUBITEMPREPAINT (NMLVCUSTOMDRAW nmcd, int /*long*/ wParam, int /*long*/ lParam) { + int /*long*/ hDC = nmcd.hdc; + if (explorerTheme && !ignoreCustomDraw && hooks (SWT.EraseItem) && (nmcd.left != nmcd.right)) { + OS.RestoreDC (hDC, -1); + } + /* + * Feature in Windows. When a new table item is inserted + * using LVM_INSERTITEM in a table that is transparent + * (ie. LVM_SETBKCOLOR has been called with CLR_NONE), + * TVM_INSERTITEM calls NM_CUSTOMDRAW before the new item + * has been added to the array. The fix is to check for + * null. + */ + TableItem item = _getItem ((int)/*64*/nmcd.dwItemSpec); + if (item == null || item.isDisposed ()) return null; + int /*long*/ hFont = item.fontHandle (nmcd.iSubItem); + if (hFont != -1) OS.SelectObject (hDC, hFont); + if (ignoreCustomDraw || (nmcd.left == nmcd.right)) { + return new LRESULT (hFont == -1 ? OS.CDRF_DODEFAULT : OS.CDRF_NEWFONT); + } + int code = OS.CDRF_DODEFAULT; + selectionForeground = -1; + ignoreDrawForeground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawBackground = false; + if (OS.IsWindowVisible (handle)) { + Event measureEvent = null; + if (hooks (SWT.MeasureItem)) { + measureEvent = sendMeasureItemEvent (item, (int)/*64*/nmcd.dwItemSpec, nmcd.iSubItem, nmcd.hdc); + if (isDisposed () || item.isDisposed ()) return null; + } + if (hooks (SWT.EraseItem)) { + sendEraseItemEvent (item, nmcd, lParam, measureEvent); + if (isDisposed () || item.isDisposed ()) return null; + code |= OS.CDRF_NOTIFYPOSTPAINT; + } + if (ignoreDrawForeground || hooks (SWT.PaintItem)) code |= OS.CDRF_NOTIFYPOSTPAINT; + } + int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1; + if (clrText == -1) clrText = item.foreground; + int clrTextBk = item.cellBackground != null ? item.cellBackground [nmcd.iSubItem] : -1; + if (clrTextBk == -1) clrTextBk = item.background; + if (selectionForeground != -1) clrText = selectionForeground; + /* + * Bug in Windows. When the table has the extended style + * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with + * CLR_NONE to make the table transparent, Windows draws + * a black rectangle around any column that contains an + * image. The fix is emulate LVS_EX_FULLROWSELECT by + * drawing the selection. + */ + if (OS.IsWindowVisible (handle) && OS.IsWindowEnabled (handle)) { + if (!explorerTheme && !ignoreDrawSelection && (style & SWT.FULL_SELECTION) != 0) { + int bits = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((bits & OS.LVS_EX_FULLROWSELECT) == 0) { + /* + * Bug in Windows. For some reason, CDIS_SELECTED always set, + * even for items that are not selected. The fix is to get + * the selection state from the item. + */ + LVITEM lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_STATE; + lvItem.stateMask = OS.LVIS_SELECTED; + lvItem.iItem = (int)/*64*/nmcd.dwItemSpec; + int /*long*/ result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem); + if ((result != 0 && (lvItem.state & OS.LVIS_SELECTED) != 0)) { + int clrSelection = -1; + if (nmcd.iSubItem == 0) { + if (OS.GetFocus () == handle || display.getHighContrast ()) { + clrSelection = OS.GetSysColor (OS.COLOR_HIGHLIGHT); + } else { + if ((style & SWT.HIDE_SELECTION) == 0) { + clrSelection = OS.GetSysColor (OS.COLOR_3DFACE); + } + } + } else { + if (OS.GetFocus () == handle || display.getHighContrast ()) { + clrText = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT); + clrTextBk = clrSelection = OS.GetSysColor (OS.COLOR_HIGHLIGHT); + } else { + if ((style & SWT.HIDE_SELECTION) == 0) { + clrTextBk = clrSelection = OS.GetSysColor (OS.COLOR_3DFACE); + } + } + } + if (clrSelection != -1) { + RECT rect = item.getBounds ((int)/*64*/nmcd.dwItemSpec, nmcd.iSubItem, true, nmcd.iSubItem != 0, true, false, hDC); + fillBackground (hDC, clrSelection, rect); + } + } + } + } + } + if (!ignoreDrawForeground) { + /* + * Bug in Windows. When the attributes are for one cell in a table, + * Windows does not reset them for the next cell. As a result, all + * subsequent cells are drawn using the previous font, foreground and + * background colors. The fix is to set the all attributes when any + * attribute could have changed. + */ + boolean hasAttributes = true; + if (hFont == -1 && clrText == -1 && clrTextBk == -1) { + if (item.cellForeground == null && item.cellBackground == null && item.cellFont == null) { + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0); + if (count == 1) hasAttributes = false; + } + } + if (hasAttributes) { + if (hFont == -1) hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + OS.SelectObject (hDC, hFont); + if (OS.IsWindowEnabled (handle)) { + nmcd.clrText = clrText == -1 ? getForegroundPixel () : clrText; + if (clrTextBk == -1) { + nmcd.clrTextBk = OS.CLR_NONE; + if (selectionForeground == -1) { + Control control = findBackgroundControl (); + if (control == null) control = this; + if (control.backgroundImage == null) { + if ((int)/*64*/OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) != OS.CLR_NONE) { + nmcd.clrTextBk = control.getBackgroundPixel (); + } + } + } + } else { + nmcd.clrTextBk = selectionForeground != -1 ? OS.CLR_NONE : clrTextBk; + } + OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof); + } + code |= OS.CDRF_NEWFONT; + } + } + if (OS.IsWindowEnabled (handle)) { + /* + * Feature in Windows. When there is a sort column, the sort column + * color draws on top of the background color for an item. The fix + * is to clear the sort column in CDDS_SUBITEMPREPAINT, and reset it + * in CDDS_SUBITEMPOSTPAINT. + */ + if (clrTextBk != -1) { + int oldColumn = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0); + if (oldColumn != -1 && oldColumn == nmcd.iSubItem) { + int result = 0; + int /*long*/ rgn = 0; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + rgn = OS.CreateRectRgn (0, 0, 0, 0); + result = OS.GetUpdateRgn (handle, rgn, true); + } + OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, -1, 0); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + OS.ValidateRect (handle, null); + if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true); + OS.DeleteObject (rgn); + } + code |= OS.CDRF_NOTIFYPOSTPAINT; + } + } + } else { + /* + * Feature in Windows. When the table is disabled, it draws + * with a gray background but does not gray the text. The fix + * is to explicitly gray the text. + */ + nmcd.clrText = OS.GetSysColor (OS.COLOR_GRAYTEXT); + if (findImageControl () != null) { + nmcd.clrTextBk = OS.CLR_NONE; + } else { + nmcd.clrTextBk = OS.GetSysColor (OS.COLOR_3DFACE); + } + nmcd.uItemState &= ~OS.CDIS_SELECTED; + OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof); + code |= OS.CDRF_NEWFONT; + } + return new LRESULT (code); +} + +void checkBuffered () { + super.checkBuffered (); + if (OS.COMCTL32_MAJOR >= 6) style |= SWT.DOUBLE_BUFFERED; + if ((style & SWT.VIRTUAL) != 0) style |= SWT.DOUBLE_BUFFERED; +} + +boolean checkData (TableItem item, boolean redraw) { + if ((style & SWT.VIRTUAL) == 0) return true; + return checkData (item, indexOf (item), redraw); +} + +boolean checkData (TableItem item, int index, boolean redraw) { + if ((style & SWT.VIRTUAL) == 0) return true; + if (!item.cached) { + item.cached = true; + Event event = new Event (); + event.item = item; + event.index = index; + currentItem = item; + sendEvent (SWT.SetData, event); + //widget could be disposed at this point + currentItem = null; + if (isDisposed () || item.isDisposed ()) return false; + if (redraw) { + if (!setScrollWidth (item, false)) { + item.redraw (); + } + } + } + return true; +} + +boolean checkHandle (int /*long*/ hwnd) { + if (hwnd == handle) return true; + return hwnd == OS.SendMessage (handle, OS.LVM_GETHEADER, 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 (); + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE); + TableItem item = items [index]; + if (item != null) { + if (item != currentItem) item.clear (); + /* + * Bug in Windows. Despite the fact that every item in the + * table always has LPSTR_TEXTCALLBACK, Windows caches the + * bounds for the selected items. This means that + * when you change the string to be something else, Windows + * correctly asks you for the new string but when the item + * is selected, the selection draws using the bounds of the + * previous item. The fix is to reset LPSTR_TEXTCALLBACK + * even though it has not changed, causing Windows to flush + * cached bounds. + */ + if ((style & SWT.VIRTUAL) == 0 && item.cached) { + LVITEM lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT; + lvItem.pszText = OS.LPSTR_TEXTCALLBACK; + lvItem.iItem = index; + OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem); + item.cached = false; + } + if (currentItem == null && getDrawing () && OS.IsWindowVisible (handle)) { + OS.SendMessage (handle, OS.LVM_REDRAWITEMS, index, index); + } + setScrollWidth (item, false); + } +} + +/** + * 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; + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (!(0 <= start && start <= end && end < count)) { + error (SWT.ERROR_INVALID_RANGE); + } + if (start == 0 && end == count - 1) { + clearAll (); + } else { + LVITEM lvItem = null; + boolean cleared = false; + for (int i=start; i<=end; i++) { + TableItem item = items [i]; + if (item != null) { + if (item != currentItem) { + cleared = true; + item.clear (); + } + /* + * Bug in Windows. Despite the fact that every item in the + * table always has LPSTR_TEXTCALLBACK, Windows caches the + * bounds for the selected items. This means that + * when you change the string to be something else, Windows + * correctly asks you for the new string but when the item + * is selected, the selection draws using the bounds of the + * previous item. The fix is to reset LPSTR_TEXTCALLBACK + * even though it has not changed, causing Windows to flush + * cached bounds. + */ + if ((style & SWT.VIRTUAL) == 0 && item.cached) { + if (lvItem == null) { + lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT; + lvItem.pszText = OS.LPSTR_TEXTCALLBACK; + } + lvItem.iItem = i; + OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem); + item.cached = false; + } + } + } + if (cleared) { + if (currentItem == null && getDrawing () && OS.IsWindowVisible (handle)) { + OS.SendMessage (handle, OS.LVM_REDRAWITEMS, start, end); + } + TableItem item = start == end ? items [start] : null; + setScrollWidth (item, false); + } + } +} + +/** + * 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; + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + for (int i=0; i<indices.length; i++) { + if (!(0 <= indices [i] && indices [i] < count)) { + error (SWT.ERROR_INVALID_RANGE); + } + } + LVITEM lvItem = null; + boolean cleared = false; + for (int i=0; i<indices.length; i++) { + int index = indices [i]; + TableItem item = items [index]; + if (item != null) { + if (item != currentItem) { + cleared = true; + item.clear (); + } + /* + * Bug in Windows. Despite the fact that every item in the + * table always has LPSTR_TEXTCALLBACK, Windows caches the + * bounds for the selected items. This means that + * when you change the string to be something else, Windows + * correctly asks you for the new string but when the item + * is selected, the selection draws using the bounds of the + * previous item. The fix is to reset LPSTR_TEXTCALLBACK + * even though it has not changed, causing Windows to flush + * cached bounds. + */ + if ((style & SWT.VIRTUAL) == 0 && item.cached) { + if (lvItem == null) { + lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT; + lvItem.pszText = OS.LPSTR_TEXTCALLBACK; + } + lvItem.iItem = i; + OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem); + item.cached = false; + } + if (currentItem == null && getDrawing () && OS.IsWindowVisible (handle)) { + OS.SendMessage (handle, OS.LVM_REDRAWITEMS, index, index); + } + } + } + if (cleared) setScrollWidth (null, false); +} + +/** + * 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 (); + LVITEM lvItem = null; + boolean cleared = false; + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + for (int i=0; i<count; i++) { + TableItem item = items [i]; + if (item != null) { + if (item != currentItem) { + cleared = true; + item.clear (); + } + /* + * Bug in Windows. Despite the fact that every item in the + * table always has LPSTR_TEXTCALLBACK, Windows caches the + * bounds for the selected items. This means that + * when you change the string to be something else, Windows + * correctly asks you for the new string but when the item + * is selected, the selection draws using the bounds of the + * previous item. The fix is to reset LPSTR_TEXTCALLBACK + * even though it has not changed, causing Windows to flush + * cached bounds. + */ + if ((style & SWT.VIRTUAL) == 0 && item.cached) { + if (lvItem == null) { + lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT; + lvItem.pszText = OS.LPSTR_TEXTCALLBACK; + } + lvItem.iItem = i; + OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem); + item.cached = false; + } + } + } + if (cleared) { + if (currentItem == null && getDrawing () && OS.IsWindowVisible (handle)) { + OS.SendMessage (handle, OS.LVM_REDRAWITEMS, 0, count - 1); + } + setScrollWidth (null, false); + } +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + if (fixScrollWidth) setScrollWidth (null, true); + //This code is intentionally commented +// if (itemHeight == -1 && hooks (SWT.MeasureItem)) { +// int i = 0; +// TableItem item = items [i]; +// if (item != null) { +// int hDC = OS.GetDC (handle); +// int oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); +// if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); +// int index = 0, count = Math.max (1, columnCount); +// while (index < count) { +// int hFont = item.cellFont != null ? item.cellFont [index] : -1; +// if (hFont == -1) hFont = item.font; +// if (hFont != -1) hFont = OS.SelectObject (hDC, hFont); +// sendMeasureItemEvent (item, i, index, hDC); +// if (hFont != -1) hFont = OS.SelectObject (hDC, hFont); +// if (isDisposed () || item.isDisposed ()) break; +// index++; +// } +// if (newFont != 0) OS.SelectObject (hDC, oldFont); +// OS.ReleaseDC (handle, hDC); +// } +// } + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + RECT rect = new RECT (); + OS.GetWindowRect (hwndHeader, rect); + int height = rect.bottom - rect.top; + int bits = 0; + if (wHint != SWT.DEFAULT) { + bits |= wHint & 0xFFFF; + } else { + int width = 0; + int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0); + for (int i=0; i<count; i++) { + width += OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, i, 0); + } + bits |= width & 0xFFFF; + } + int /*long*/ result = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, -1, OS.MAKELPARAM (bits, 0xFFFF)); + int width = OS.LOWORD (result); + int /*long*/ empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0); + int /*long*/ oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0); + int itemHeight = OS.HIWORD (oneItem) - OS.HIWORD (empty); + height += (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0) * itemHeight; + 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; + if ((style & SWT.V_SCROLL) != 0) { + width += OS.GetSystemMetrics (OS.SM_CXVSCROLL); + } + if ((style & SWT.H_SCROLL) != 0) { + height += OS.GetSystemMetrics (OS.SM_CYHSCROLL); + } + return new Point (width, height); +} + +void createHandle () { + super.createHandle (); + state &= ~(CANVAS | THEME_BACKGROUND); + + /* Use the Explorer theme */ + if (EXPLORER_THEME) { + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0) && OS.IsAppThemed ()) { + explorerTheme = true; + OS.SetWindowTheme (handle, Display.EXPLORER, null); + } + } + + /* Get the header window proc */ + if (HeaderProc == 0) { + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + HeaderProc = OS.GetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC); + } + + /* + * Feature in Windows. In version 5.8 of COMCTL32.DLL, + * if the font is changed for an item, the bounds for the + * item are not updated, causing the text to be clipped. + * The fix is to detect the version of COMCTL32.DLL, and + * if it is one of the versions with the problem, then + * use version 5.00 of the control (a version that does + * not have the problem). This is the recommended work + * around from the MSDN. + */ + if (!OS.IsWinCE) { + if (OS.COMCTL32_MAJOR < 6) { + OS.SendMessage (handle, OS.CCM_SETVERSION, 5, 0); + } + } + + /* + * This code is intentionally commented. According to + * the documentation, setting the default item size is + * supposed to improve performance. By experimentation, + * this does not seem to have much of an effect. + */ +// OS.SendMessage (handle, OS.LVM_SETITEMCOUNT, 1024 * 2, 0); + + /* Set the checkbox image list */ + if ((style & SWT.CHECK) != 0) { + int /*long*/ empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0); + int /*long*/ oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0); + int width = OS.HIWORD (oneItem) - OS.HIWORD (empty), height = width; + setCheckboxImageList (width, height, false); + OS.SendMessage (handle, OS. LVM_SETCALLBACKMASK, OS.LVIS_STATEIMAGEMASK, 0); + } + + /* + * Feature in Windows. When the control is created, + * it does not use the default system font. A new HFONT + * is created and destroyed when the control is destroyed. + * This means that a program that queries the font from + * this control, uses the font in another control and then + * destroys this control will have the font unexpectedly + * destroyed in the other control. The fix is to assign + * the font ourselves each time the control is created. + * The control will not destroy a font that it did not + * create. + */ + int /*long*/ hFont = OS.GetStockObject (OS.SYSTEM_FONT); + OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0); + + /* + * Bug in Windows. When the first column is inserted + * without setting the header text, Windows will never + * allow the header text for the first column to be set. + * The fix is to set the text to an empty string when + * the column is inserted. + */ + LVCOLUMN lvColumn = new LVCOLUMN (); + lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_WIDTH; + int /*long*/ hHeap = OS.GetProcessHeap (); + int /*long*/ pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof); + lvColumn.pszText = pszText; + OS.SendMessage (handle, OS.LVM_INSERTCOLUMN, 0, lvColumn); + OS.HeapFree (hHeap, 0, pszText); + + /* Set the extended style bits */ + int bits1 = OS.LVS_EX_LABELTIP; + if ((style & SWT.FULL_SELECTION) != 0) bits1 |= OS.LVS_EX_FULLROWSELECT; + if (OS.COMCTL32_MAJOR >= 6) bits1 |= OS.LVS_EX_DOUBLEBUFFER; + OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits1, bits1); + + /* + * Feature in Windows. Windows does not explicitly set the orientation of + * the header. Instead, the orientation is inherited when WS_EX_LAYOUTRTL + * is specified for the table. This means that when both WS_EX_LAYOUTRTL + * and WS_EX_NOINHERITLAYOUT are specified for the table, the header will + * not be oriented correctly. The fix is to explicitly set the orientation + * for the header. + * + * NOTE: WS_EX_LAYOUTRTL is not supported on Windows NT. + */ + if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) { + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + int bits2 = OS.GetWindowLong (hwndHeader, OS.GWL_EXSTYLE); + OS.SetWindowLong (hwndHeader, OS.GWL_EXSTYLE, bits2 | OS.WS_EX_LAYOUTRTL); + int /*long*/ hwndTooltop = OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0); + int bits3 = OS.GetWindowLong (hwndTooltop, OS.GWL_EXSTYLE); + OS.SetWindowLong (hwndTooltop, OS.GWL_EXSTYLE, bits3 | OS.WS_EX_LAYOUTRTL); + } + } +} + +void createHeaderToolTips () { + if (OS.IsWinCE) return; + if (headerToolTipHandle != 0) return; + int bits = 0; + if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) { + if ((style & SWT.RIGHT_TO_LEFT) != 0) bits |= OS.WS_EX_LAYOUTRTL; + } + headerToolTipHandle = OS.CreateWindowEx ( + bits, + new TCHAR (0, OS.TOOLTIPS_CLASS, true), + null, + OS.TTS_NOPREFIX, + OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0, + handle, + 0, + OS.GetModuleHandle (null), + null); + if (headerToolTipHandle == 0) error (SWT.ERROR_NO_HANDLES); + /* + * Feature in Windows. Despite the fact that the + * tool tip text contains \r\n, the tooltip will + * not honour the new line unless TTM_SETMAXTIPWIDTH + * is set. The fix is to set TTM_SETMAXTIPWIDTH to + * a large value. + */ + OS.SendMessage (headerToolTipHandle, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF); +} + +void createItem (TableColumn column, int index) { + if (!(0 <= index && index <= columnCount)) error (SWT.ERROR_INVALID_RANGE); + int oldColumn = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0); + if (oldColumn >= index) { + OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, oldColumn + 1, 0); + } + if (columnCount == columns.length) { + TableColumn [] newColumns = new TableColumn [columns.length + 4]; + System.arraycopy (columns, 0, newColumns, 0, columns.length); + columns = newColumns; + } + int itemCount = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + for (int i=0; i<itemCount; i++) { + TableItem item = items [i]; + if (item != null) { + String [] strings = item.strings; + if (strings != null) { + String [] temp = new String [columnCount + 1]; + System.arraycopy (strings, 0, temp, 0, index); + System.arraycopy (strings, index, temp, index + 1, columnCount - index); + item.strings = temp; + } + Image [] images = item.images; + if (images != null) { + Image [] temp = new Image [columnCount + 1]; + System.arraycopy (images, 0, temp, 0, index); + System.arraycopy (images, index, temp, index + 1, columnCount - index); + item.images = temp; + } + if (index == 0) { + if (columnCount != 0) { + if (strings == null) { + item.strings = new String [columnCount + 1]; + item.strings [1] = item.text; + } + item.text = ""; //$NON-NLS-1$ + if (images == null) { + item.images = new Image [columnCount + 1]; + item.images [1] = item.image; + } + item.image = null; + } + } + if (item.cellBackground != null) { + int [] cellBackground = item.cellBackground; + int [] temp = new int [columnCount + 1]; + System.arraycopy (cellBackground, 0, temp, 0, index); + System.arraycopy (cellBackground, index, temp, index + 1, columnCount - index); + temp [index] = -1; + item.cellBackground = temp; + } + if (item.cellForeground != null) { + int [] cellForeground = item.cellForeground; + int [] temp = new int [columnCount + 1]; + System.arraycopy (cellForeground, 0, temp, 0, index); + System.arraycopy (cellForeground, index, temp, index + 1, columnCount - index); + temp [index] = -1; + 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, temp, index + 1, columnCount - index); + item.cellFont = temp; + } + } + } + /* + * Insert the column into the columns array before inserting + * it into the widget so that the column will be present when + * any callbacks are issued as a result of LVM_INSERTCOLUMN + * or LVM_SETCOLUMN. + */ + System.arraycopy (columns, index, columns, index + 1, columnCount++ - index); + columns [index] = column; + + /* + * Ensure that resize listeners for the table and for columns + * within the table are not called. This can happen when the + * first column is inserted into a table or when a new column + * is inserted in the first position. + */ + ignoreColumnResize = true; + if (index == 0) { + if (columnCount > 1) { + LVCOLUMN lvColumn = new LVCOLUMN (); + lvColumn.mask = OS.LVCF_WIDTH; + OS.SendMessage (handle, OS.LVM_INSERTCOLUMN, 1, lvColumn); + OS.SendMessage (handle, OS.LVM_GETCOLUMN, 1, lvColumn); + int width = lvColumn.cx; + int cchTextMax = 1024; + int /*long*/ hHeap = OS.GetProcessHeap (); + int byteCount = cchTextMax * TCHAR.sizeof; + int /*long*/ pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_IMAGE | OS.LVCF_WIDTH | OS.LVCF_FMT; + lvColumn.pszText = pszText; + lvColumn.cchTextMax = cchTextMax; + OS.SendMessage (handle, OS.LVM_GETCOLUMN, 0, lvColumn); + OS.SendMessage (handle, OS.LVM_SETCOLUMN, 1, lvColumn); + lvColumn.fmt = OS.LVCFMT_IMAGE; + lvColumn.cx = width; + lvColumn.iImage = OS.I_IMAGENONE; + lvColumn.pszText = lvColumn.cchTextMax = 0; + OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn); + lvColumn.mask = OS.LVCF_FMT; + lvColumn.fmt = OS.LVCFMT_LEFT; + OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn); + if (pszText != 0) OS.HeapFree (hHeap, 0, pszText); + } else { + OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, 0); + } + /* + * Bug in Windows. Despite the fact that every item in the + * table always has LPSTR_TEXTCALLBACK, Windows caches the + * bounds for the selected items. This means that + * when you change the string to be something else, Windows + * correctly asks you for the new string but when the item + * is selected, the selection draws using the bounds of the + * previous item. The fix is to reset LPSTR_TEXTCALLBACK + * even though it has not changed, causing Windows to flush + * cached bounds. + */ + if ((style & SWT.VIRTUAL) == 0) { + LVITEM lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE; + lvItem.pszText = OS.LPSTR_TEXTCALLBACK; + lvItem.iImage = OS.I_IMAGECALLBACK; + for (int i=0; i<itemCount; i++) { + lvItem.iItem = i; + OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem); + } + } + } else { + int fmt = OS.LVCFMT_LEFT; + if ((column.style & SWT.CENTER) == SWT.CENTER) fmt = OS.LVCFMT_CENTER; + if ((column.style & SWT.RIGHT) == SWT.RIGHT) fmt = OS.LVCFMT_RIGHT; + LVCOLUMN lvColumn = new LVCOLUMN (); + lvColumn.mask = OS.LVCF_WIDTH | OS.LVCF_FMT; + lvColumn.fmt = fmt; + OS.SendMessage (handle, OS.LVM_INSERTCOLUMN, index, lvColumn); + } + ignoreColumnResize = false; + + /* Add the tool tip item for the header */ + if (headerToolTipHandle != 0) { + RECT rect = new RECT (); + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, rect) != 0) { + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + lpti.uFlags = OS.TTF_SUBCLASS; + lpti.hwnd = hwndHeader; + lpti.uId = column.id = display.nextToolTipId++; + lpti.left = rect.left; + lpti.top = rect.top; + lpti.right = rect.right; + lpti.bottom = rect.bottom; + lpti.lpszText = OS.LPSTR_TEXTCALLBACK; + OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, lpti); + } + } +} + +void createItem (TableItem item, int index) { + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE); + if (count == items.length) { + /* + * Grow the array faster when redraw is off or the + * table is not visible. When the table is painted, + * the items array is resized to be smaller to reduce + * memory usage. + */ + boolean small = getDrawing () && OS.IsWindowVisible (handle); + int length = small ? 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; + } + LVITEM lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE; + lvItem.iItem = index; + lvItem.pszText = OS.LPSTR_TEXTCALLBACK; + /* + * Bug in Windows. Despite the fact that the image list + * index has never been set for the item, Windows always + * assumes that the image index for the item is valid. + * When an item is inserted, the image index is zero. + * Therefore, when the first image is inserted and is + * assigned image index zero, every item draws with this + * image. The fix is to set the image index when the + * the item is created. + */ + lvItem.iImage = OS.I_IMAGECALLBACK; + + /* Insert the item */ + setDeferResize (true); + ignoreSelect = ignoreShrink = true; + int result = (int)/*64*/OS.SendMessage (handle, OS.LVM_INSERTITEM, 0, lvItem); + ignoreSelect = ignoreShrink = false; + if (result == -1) error (SWT.ERROR_ITEM_NOT_ADDED); + System.arraycopy (items, index, items, index + 1, count - index); + items [index] = item; + setDeferResize (false); + + /* Resize to show the first item */ + if (count == 0) setScrollWidth (item, false); +} + +void createWidget () { + super.createWidget (); + itemHeight = hotIndex = -1; + items = new TableItem [4]; + columns = new TableColumn [4]; +} + +int defaultBackground () { + return OS.GetSysColor (OS.COLOR_WINDOW); +} + +void deregister () { + super.deregister (); + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + if (hwndHeader != 0) display.removeControl (hwndHeader); +} + +/** + * 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); + if (indices.length == 0) return; + LVITEM lvItem = new LVITEM (); + lvItem.stateMask = OS.LVIS_SELECTED; + for (int i=0; i<indices.length; i++) { + /* + * An index of -1 will apply the change to all + * items. Ensure that indices are greater than -1. + */ + if (indices [i] >= 0) { + ignoreSelect = true; + OS.SendMessage (handle, OS.LVM_SETITEMSTATE, indices [i], lvItem); + ignoreSelect = false; + } + } +} + +/** + * 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 (); + /* + * An index of -1 will apply the change to all + * items. Ensure that index is greater than -1. + */ + if (index < 0) return; + LVITEM lvItem = new LVITEM (); + lvItem.stateMask = OS.LVIS_SELECTED; + ignoreSelect = true; + OS.SendMessage (handle, OS.LVM_SETITEMSTATE, index, lvItem); + 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 (); + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (start == 0 && end == count - 1) { + deselectAll (); + } else { + LVITEM lvItem = new LVITEM (); + lvItem.stateMask = OS.LVIS_SELECTED; + /* + * An index of -1 will apply the change to all + * items. Ensure that indices are greater than -1. + */ + start = Math.max (0, start); + for (int i=start; i<=end; i++) { + ignoreSelect = true; + OS.SendMessage (handle, OS.LVM_SETITEMSTATE, i, lvItem); + 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 (); + LVITEM lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_STATE; + lvItem.stateMask = OS.LVIS_SELECTED; + ignoreSelect = true; + OS.SendMessage (handle, OS.LVM_SETITEMSTATE, -1, lvItem); + ignoreSelect = false; +} + +void destroyItem (TableColumn column) { + int index = 0; + while (index < columnCount) { + if (columns [index] == column) break; + index++; + } + int oldColumn = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0); + if (oldColumn == index) { + OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, -1, 0); + } else { + if (oldColumn > index) { + OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, oldColumn - 1, 0); + } + } + int orderIndex = 0; + int [] oldOrder = new int [columnCount]; + OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, oldOrder); + while (orderIndex < columnCount) { + if (oldOrder [orderIndex] == index) break; + orderIndex++; + } + ignoreColumnResize = true; + boolean first = false; + if (index == 0) { + first = true; + if (columnCount > 1) { + index = 1; + int cchTextMax = 1024; + int /*long*/ hHeap = OS.GetProcessHeap (); + int byteCount = cchTextMax * TCHAR.sizeof; + int /*long*/ pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + LVCOLUMN lvColumn = new LVCOLUMN (); + lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_IMAGE | OS.LVCF_WIDTH | OS.LVCF_FMT; + lvColumn.pszText = pszText; + lvColumn.cchTextMax = cchTextMax; + OS.SendMessage (handle, OS.LVM_GETCOLUMN, 1, lvColumn); + lvColumn.fmt &= ~(OS.LVCFMT_CENTER | OS.LVCFMT_RIGHT); + lvColumn.fmt |= OS.LVCFMT_LEFT; + OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn); + if (pszText != 0) OS.HeapFree (hHeap, 0, pszText); + } else { + int /*long*/ hHeap = OS.GetProcessHeap (); + int /*long*/ pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof); + LVCOLUMN lvColumn = new LVCOLUMN (); + lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_IMAGE | OS.LVCF_WIDTH | OS.LVCF_FMT; + lvColumn.pszText = pszText; + lvColumn.iImage = OS.I_IMAGENONE; + lvColumn.fmt = OS.LVCFMT_LEFT; + OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn); + if (pszText != 0) OS.HeapFree (hHeap, 0, pszText); + if (OS.COMCTL32_MAJOR >= 6) { + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_FORMAT; + hdItem.fmt = OS.HDF_LEFT; + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem); + } + } + /* + * Bug in Windows. Despite the fact that every item in the + * table always has LPSTR_TEXTCALLBACK, Windows caches the + * bounds for the selected items. This means that + * when you change the string to be something else, Windows + * correctly asks you for the new string but when the item + * is selected, the selection draws using the bounds of the + * previous item. The fix is to reset LPSTR_TEXTCALLBACK + * even though it has not changed, causing Windows to flush + * cached bounds. + */ + if ((style & SWT.VIRTUAL) == 0) { + LVITEM lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE; + lvItem.pszText = OS.LPSTR_TEXTCALLBACK; + lvItem.iImage = OS.I_IMAGECALLBACK; + int itemCount = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + for (int i=0; i<itemCount; i++) { + lvItem.iItem = i; + OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem); + } + } + } + if (columnCount > 1) { + if (OS.SendMessage (handle, OS.LVM_DELETECOLUMN, index, 0) == 0) { + error (SWT.ERROR_ITEM_NOT_REMOVED); + } + } + if (first) index = 0; + System.arraycopy (columns, index + 1, columns, index, --columnCount - index); + columns [columnCount] = null; + int itemCount = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + for (int i=0; i<itemCount; i++) { + TableItem item = items [i]; + if (item != null) { + if (columnCount == 0) { + 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] : ""; //$NON-NLS-1$ + } + String [] temp = new String [columnCount]; + System.arraycopy (strings, 0, temp, 0, index); + System.arraycopy (strings, index + 1, temp, index, columnCount - index); + item.strings = temp; + } else { + if (index == 0) item.text = ""; //$NON-NLS-1$ + } + if (item.images != null) { + Image [] images = item.images; + if (index == 0) item.image = images [1]; + Image [] temp = new Image [columnCount]; + System.arraycopy (images, 0, temp, 0, index); + System.arraycopy (images, index + 1, temp, index, columnCount - index); + item.images = temp; + } else { + if (index == 0) item.image = null; + } + if (item.cellBackground != null) { + int [] cellBackground = item.cellBackground; + int [] temp = new int [columnCount]; + System.arraycopy (cellBackground, 0, temp, 0, index); + System.arraycopy (cellBackground, index + 1, temp, index, columnCount - index); + item.cellBackground = temp; + } + if (item.cellForeground != null) { + int [] cellForeground = item.cellForeground; + int [] temp = new int [columnCount]; + System.arraycopy (cellForeground, 0, temp, 0, index); + System.arraycopy (cellForeground, index + 1, temp, index, columnCount - index); + item.cellForeground = temp; + } + if (item.cellFont != null) { + Font [] cellFont = item.cellFont; + Font [] temp = new Font [columnCount]; + System.arraycopy (cellFont, 0, temp, 0, index); + System.arraycopy (cellFont, index + 1, temp, index, columnCount - index); + item.cellFont = temp; + } + } + } + } + if (columnCount == 0) setScrollWidth (null, true); + updateMoveable (); + ignoreColumnResize = false; + if (columnCount != 0) { + /* + * Bug in Windows. When LVM_DELETECOLUMN is used to delete a + * column zero when that column is both the first column in the + * table and the first column in the column order array, Windows + * incorrectly computes the new column order. For example, both + * the orders {0, 3, 1, 2} and {0, 3, 2, 1} give a new column + * order of {0, 2, 1}, while {0, 2, 1, 3} gives {0, 1, 2, 3}. + * The fix is to compute the new order and compare it with the + * order that Windows is using. If the two differ, the new order + * is used. + */ + int count = 0; + int oldIndex = oldOrder [orderIndex]; + int [] newOrder = new int [columnCount]; + for (int i=0; i<oldOrder.length; i++) { + if (oldOrder [i] != oldIndex) { + int newIndex = oldOrder [i] <= oldIndex ? oldOrder [i] : oldOrder [i] - 1; + newOrder [count++] = newIndex; + } + } + OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, oldOrder); + int j = 0; + while (j < newOrder.length) { + if (oldOrder [j] != newOrder [j]) break; + j++; + } + if (j != newOrder.length) { + OS.SendMessage (handle, OS.LVM_SETCOLUMNORDERARRAY, newOrder.length, newOrder); + /* + * Bug in Windows. When LVM_SETCOLUMNORDERARRAY is used to change + * the column order, the header redraws correctly but the table does + * not. The fix is to force a redraw. + */ + OS.InvalidateRect (handle, null, true); + } + TableColumn [] newColumns = new TableColumn [columnCount - orderIndex]; + for (int i=orderIndex; i<newOrder.length; i++) { + newColumns [i - orderIndex] = columns [newOrder [i]]; + newColumns [i - orderIndex].updateToolTip (newOrder [i]); + } + for (int i=0; i<newColumns.length; i++) { + if (!newColumns [i].isDisposed ()) { + newColumns [i].sendEvent (SWT.Move); + } + } + } + + /* Remove the tool tip item for the header */ + if (headerToolTipHandle != 0) { + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + lpti.uId = column.id; + lpti.hwnd = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + OS.SendMessage (headerToolTipHandle, OS.TTM_DELTOOL, 0, lpti); + } +} + +void destroyItem (TableItem item) { + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + int index = 0; + while (index < count) { + if (items [index] == item) break; + index++; + } + if (index == count) return; + setDeferResize (true); + ignoreSelect = ignoreShrink = true; + int /*long*/ code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0); + ignoreSelect = ignoreShrink = false; + if (code == 0) error (SWT.ERROR_ITEM_NOT_REMOVED); + System.arraycopy (items, index + 1, items, index, --count - index); + items [count] = null; + if (count == 0) setTableEmpty (); + setDeferResize (false); +} + +void fixCheckboxImageList (boolean fixScroll) { + /* + * Bug in Windows. When the state image list is larger than the + * image list, Windows incorrectly positions the state images. When + * the table is scrolled, Windows draws garbage. The fix is to force + * the state image list to be the same size as the image list. + */ + if ((style & SWT.CHECK) == 0) return; + int /*long*/ hImageList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0); + if (hImageList == 0) return; + int [] cx = new int [1], cy = new int [1]; + OS.ImageList_GetIconSize (hImageList, cx, cy); + int /*long*/ hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0); + if (hStateList == 0) return; + int [] stateCx = new int [1], stateCy = new int [1]; + OS.ImageList_GetIconSize (hStateList, stateCx, stateCy); + if (cx [0] == stateCx [0] && cy [0] == stateCy [0]) return; + setCheckboxImageList (cx [0], cy [0], fixScroll); +} + +void fixCheckboxImageListColor (boolean fixScroll) { + if ((style & SWT.CHECK) == 0) return; + int /*long*/ hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0); + if (hStateList == 0) return; + int [] cx = new int [1], cy = new int [1]; + OS.ImageList_GetIconSize (hStateList, cx, cy); + setCheckboxImageList (cx [0], cy [0], fixScroll); +} + +void fixItemHeight (boolean fixScroll) { + /* + * Bug in Windows. When both a header and grid lines are + * displayed, the grid lines do not take into account the + * height of the header and draw in the wrong place. The + * fix is to set the height of the table items to be the + * height of the header so that the lines draw in the right + * place. The height of a table item is the maximum of the + * height of the font or the height of image list. + * + * NOTE: In version 5.80 of COMCTL32.DLL, the bug is fixed. + */ + if (itemHeight != -1) return; + if (OS.COMCTL32_VERSION >= OS.VERSION (5, 80)) return; + int bits = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((bits & OS.LVS_EX_GRIDLINES) == 0) return; + bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.LVS_NOCOLUMNHEADER) != 0) return; + /* + * Bug in Windows. Making any change to an item that + * changes the item height of a table while the table + * is scrolled can cause the lines to draw incorrectly. + * This happens even when the lines are not currently + * visible and are shown afterwards. The fix is to + * save the top index, scroll to the top of the table + * and then restore the original top index. + */ + int topIndex = getTopIndex (); + if (fixScroll && topIndex != 0) { + setRedraw (false); + setTopIndex (0); + } + int /*long*/ hOldList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0); + if (hOldList != 0) return; + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + RECT rect = new RECT (); + OS.GetWindowRect (hwndHeader, rect); + int height = rect.bottom - rect.top - 1; + int /*long*/ hImageList = OS.ImageList_Create (1, height, 0, 0, 0); + OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList); + fixCheckboxImageList (false); + OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, 0); + if (headerImageList != null) { + int /*long*/ hHeaderImageList = headerImageList.getHandle (); + OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hHeaderImageList); + } + OS.ImageList_Destroy (hImageList); + if (fixScroll && topIndex != 0) { + setTopIndex (topIndex); + setRedraw (true); + } +} + +/** + * 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 (); + if (columnCount == 0) return new int [0]; + int [] order = new int [columnCount]; + OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order); + 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; +} + +int getFocusIndex () { +// checkWidget (); + return (int)/*64*/OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED); +} + +/** + * 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 GRID_WIDTH; +} + +/** + * 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 (); + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + if (hwndHeader == 0) return 0; + RECT rect = new RECT (); + OS.GetWindowRect (hwndHeader, rect); + return rect.bottom - rect.top; +} + +/** + * 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 (); + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + return (bits & OS.LVS_NOCOLUMNHEADER) == 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 TableItem getItem (int index) { + checkWidget (); + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (!(0 <= index && index < count)) 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 (); + if (point == null) error (SWT.ERROR_NULL_ARGUMENT); + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (count == 0) return null; + LVHITTESTINFO pinfo = new LVHITTESTINFO (); + pinfo.x = point.x; + pinfo.y = point.y; + if ((style & SWT.FULL_SELECTION) == 0) { + if (hooks (SWT.MeasureItem)) { + /* + * Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest + * a point that is above the table, instead of returning -1 to + * indicate that the hittest failed, a negative index is returned. + * The fix is to consider any value that is negative a failure. + */ + if (OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo) < 0) { + RECT rect = new RECT (); + rect.left = OS.LVIR_ICON; + ignoreCustomDraw = true; + int /*long*/ code = OS.SendMessage (handle, OS.LVM_GETITEMRECT, 0, rect); + ignoreCustomDraw = false; + if (code != 0) { + pinfo.x = rect.left; + /* + * Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest + * a point that is above the table, instead of returning -1 to + * indicate that the hittest failed, a negative index is returned. + * The fix is to consider any value that is negative a failure. + */ + OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo); + if (pinfo.iItem < 0) pinfo.iItem = -1; + } + } + if (pinfo.iItem != -1 && pinfo.iSubItem == 0) { + if (hitTestSelection (pinfo.iItem, pinfo.x, pinfo.y)) { + return _getItem (pinfo.iItem); + } + } + return null; + } + } + OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo); + if (pinfo.iItem != -1) { + /* + * Bug in Windows. When the point that is used by + * LVM_HITTEST is inside the header, Windows returns + * the first item in the table. The fix is to check + * when LVM_HITTEST returns the first item and make + * sure that when the point is within the header, + * the first item is not returned. + */ + if (pinfo.iItem == 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.LVS_NOCOLUMNHEADER) == 0) { + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + if (hwndHeader != 0) { + RECT rect = new RECT (); + OS.GetWindowRect (hwndHeader, rect); + POINT pt = new POINT (); + pt.x = pinfo.x; + pt.y = pinfo.y; + OS.MapWindowPoints (handle, 0, pt, 1); + if (OS.PtInRect (rect, pt)) return null; + } + } + } + return _getItem (pinfo.iItem); + } + 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 (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); +} + +/** + * 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 (); + if (!painted && hooks (SWT.MeasureItem)) hitTestSelection (0, 0, 0); + int /*long*/ empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0); + int /*long*/ oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0); + return OS.HIWORD (oneItem) - OS.HIWORD (empty); +} + +/** + * 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 (); + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + TableItem [] result = new TableItem [count]; + if ((style & SWT.VIRTUAL) != 0) { + for (int i=0; i<count; i++) { + result [i] = _getItem (i); + } + } else { + System.arraycopy (items, 0, result, 0, count); + } + 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 (); + int bits = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + return (bits & OS.LVS_EX_GRIDLINES) != 0; +} + +/** + * 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 (); + int i = -1, j = 0, count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0); + TableItem [] result = new TableItem [count]; + while ((i = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) != -1) { + result [j++] = _getItem (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*/OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0); +} + +/** + * 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 (); + int focusIndex = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED); + int selectedIndex = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_SELECTED); + if (focusIndex == selectedIndex) return selectedIndex; + int i = -1; + while ((i = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) != -1) { + if (i == focusIndex) return i; + } + return selectedIndex; +} + +/** + * 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 (); + int i = -1, j = 0, count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0); + int [] result = new int [count]; + while ((i = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) != -1) { + result [j++] = 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; +} + +int getSortColumnPixel () { + int pixel = OS.IsWindowEnabled (handle) ? getBackgroundPixel () : OS.GetSysColor (OS.COLOR_3DFACE); + int red = pixel & 0xFF; + int green = (pixel & 0xFF00) >> 8; + int blue = (pixel & 0xFF0000) >> 16; + if (red > 240 && green > 240 && blue > 240) { + red -= 8; + green -= 8; + blue -= 8; + } else { + red = Math.min (0xFF, (red / 10) + red); + green = Math.min (0xFF, (green / 10) + green); + blue = Math.min (0xFF, (blue / 10) + blue); + } + return (red & 0xFF) | ((green & 0xFF) << 8) | ((blue & 0xFF) << 16); +} + +/** + * 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 (); + /* + * Bug in Windows. Under rare circumstances, LVM_GETTOPINDEX + * can return a negative number. When this happens, the table + * is displaying blank lines at the top of the controls. The + * fix is to check for a negative number and return zero instead. + */ + return Math.max (0, (int)/*64*/OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)); +} + +boolean hasChildren () { + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + int /*long*/ hwndChild = OS.GetWindow (handle, OS.GW_CHILD); + while (hwndChild != 0) { + if (hwndChild != hwndHeader) return true; + hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT); + } + return false; +} + +boolean hitTestSelection (int index, int x, int y) { + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (count == 0) return false; + if (!hooks (SWT.MeasureItem)) return false; + boolean result = false; + if (0 <= index && index < count) { + TableItem item = _getItem (index); + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + int /*long*/ hFont = item.fontHandle (0); + if (hFont != -1) hFont = OS.SelectObject (hDC, hFont); + Event event = sendMeasureItemEvent (item, index, 0, hDC); + if (event.getBounds ().contains (x, y)) result = true; + if (hFont != -1) hFont = OS.SelectObject (hDC, hFont); + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); +// if (isDisposed () || item.isDisposed ()) return false; + } + return result; +} + +int imageIndex (Image image, int column) { + if (image == null) return OS.I_IMAGENONE; + if (column == 0) { + firstColumnImage = true; + } else { + setSubImagesVisible (true); + } + if (imageList == null) { + Rectangle bounds = image.getBounds (); + imageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height); + int index = imageList.indexOf (image); + if (index == -1) index = imageList.add (image); + int /*long*/ hImageList = imageList.getHandle (); + /* + * Bug in Windows. Making any change to an item that + * changes the item height of a table while the table + * is scrolled can cause the lines to draw incorrectly. + * This happens even when the lines are not currently + * visible and are shown afterwards. The fix is to + * save the top index, scroll to the top of the table + * and then restore the original top index. + */ + int topIndex = getTopIndex (); + if (topIndex != 0) { + setRedraw (false); + setTopIndex (0); + } + OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList); + if (headerImageList != null) { + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + int /*long*/ hHeaderImageList = headerImageList.getHandle (); + OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hHeaderImageList); + } + fixCheckboxImageList (false); + if (itemHeight != -1) setItemHeight (false); + if (topIndex != 0) { + setTopIndex (topIndex); + setRedraw (true); + } + return index; + } + int index = imageList.indexOf (image); + if (index != -1) return index; + return imageList.add (image); +} + +int imageIndexHeader (Image image) { + if (image == null) return OS.I_IMAGENONE; + if (headerImageList == null) { + Rectangle bounds = image.getBounds (); + headerImageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height); + int index = headerImageList.indexOf (image); + if (index == -1) index = headerImageList.add (image); + int /*long*/ hImageList = headerImageList.getHandle (); + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hImageList); + return index; + } + int index = headerImageList.indexOf (image); + if (index != -1) return index; + return headerImageList.add (image); +} + +/** + * 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); + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (1 <= lastIndexOf && lastIndexOf < count - 1) { + if (items [lastIndexOf] == item) return lastIndexOf; + if (items [lastIndexOf + 1] == item) return ++lastIndexOf; + if (items [lastIndexOf - 1] == item) return --lastIndexOf; + } + if (lastIndexOf < count / 2) { + for (int i=0; i<count; i++) { + if (items [i] == item) return lastIndexOf = i; + } + } else { + for (int i=count - 1; i>=0; --i) { + if (items [i] == item) return lastIndexOf = i; + } + } + return -1; +} + +boolean isCustomToolTip () { + return hooks (SWT.MeasureItem); +} + +boolean isOptimizedRedraw () { + if ((style & SWT.H_SCROLL) == 0 || (style & SWT.V_SCROLL) == 0) return false; + return !hasChildren () && !hooks (SWT.Paint) && !filters (SWT.Paint); +} + +/** + * 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 (); + LVITEM lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_STATE; + lvItem.stateMask = OS.LVIS_SELECTED; + lvItem.iItem = index; + int /*long*/ result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem); + return (result != 0) && ((lvItem.state & OS.LVIS_SELECTED) != 0); +} + +void register () { + super.register (); + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + if (hwndHeader != 0) display.addControl (hwndHeader, this); +} + +void releaseChildren (boolean destroy) { + if (items != null) { + int itemCount = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + /* + * Feature in Windows 98. When there are a large number + * of columns and items in a table (>1000) where each + * of the subitems in the table has a string, it is much + * faster to delete each item with LVM_DELETEITEM rather + * than using LVM_DELETEALLITEMS. The fix is to detect + * this case and delete the items, one by one. The fact + * that the fix is only necessary on Windows 98 was + * confirmed using version 5.81 of COMCTL32.DLL on both + * Windows 98 and NT. + * + * NOTE: LVM_DELETEALLITEMS is also sent by the table + * when the table is destroyed. + */ + if (OS.IsWin95 && columnCount > 1) { + /* Turn off redraw and resize events and leave them off */ + resizeCount = 1; + OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); + for (int i=itemCount-1; i>=0; --i) { + TableItem item = items [i]; + if (item != null && !item.isDisposed ()) item.release (false); + ignoreSelect = ignoreShrink = true; + OS.SendMessage (handle, OS.LVM_DELETEITEM, i, 0); + ignoreSelect = ignoreShrink = false; + } + } else { + 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.isDisposed ()) column.release (false); + } + columns = null; + } + super.releaseChildren (destroy); +} + +void releaseWidget () { + super.releaseWidget (); + customDraw = false; + currentItem = null; + if (imageList != null) { + OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, 0); + display.releaseImageList (imageList); + } + if (headerImageList != null) { + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, 0); + display.releaseImageList (headerImageList); + } + imageList = headerImageList = null; + int /*long*/ hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0); + OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_STATE, 0); + if (hStateList != 0) OS.ImageList_Destroy (hStateList); + if (headerToolTipHandle != 0) OS.DestroyWindow (headerToolTipHandle); + headerToolTipHandle = 0; +} + +/** + * 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]; + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (!(0 <= start && start <= end && end < count)) { + error (SWT.ERROR_INVALID_RANGE); + } + setDeferResize (true); + int last = -1; + for (int i=0; i<newIndices.length; i++) { + int index = newIndices [i]; + if (index != last) { + TableItem item = items [index]; + if (item != null && !item.isDisposed ()) item.release (false); + ignoreSelect = ignoreShrink = true; + int /*long*/ code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0); + ignoreSelect = ignoreShrink = false; + if (code == 0) error (SWT.ERROR_ITEM_NOT_REMOVED); + System.arraycopy (items, index + 1, items, index, --count - index); + items [count] = null; + last = index; + } + } + if (count == 0) setTableEmpty (); + setDeferResize (false); +} + +/** + * 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 (); + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE); + TableItem item = items [index]; + if (item != null && !item.isDisposed ()) item.release (false); + setDeferResize (true); + ignoreSelect = ignoreShrink = true; + int /*long*/ code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0); + ignoreSelect = ignoreShrink = false; + if (code == 0) error (SWT.ERROR_ITEM_NOT_REMOVED); + System.arraycopy (items, index + 1, items, index, --count - index); + items [count] = null; + if (count == 0) setTableEmpty (); + setDeferResize (false); +} + +/** + * 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; + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (!(0 <= start && start <= end && end < count)) { + error (SWT.ERROR_INVALID_RANGE); + } + if (start == 0 && end == count - 1) { + removeAll (); + } else { + setDeferResize (true); + int index = start; + while (index <= end) { + TableItem item = items [index]; + if (item != null && !item.isDisposed ()) item.release (false); + ignoreSelect = ignoreShrink = true; + int /*long*/ code = OS.SendMessage (handle, OS.LVM_DELETEITEM, start, 0); + ignoreSelect = ignoreShrink = false; + if (code == 0) break; + index++; + } + System.arraycopy (items, index, items, start, count - index); + for (int i=count-(index-start); i<count; i++) items [i] = null; + if (index <= end) error (SWT.ERROR_ITEM_NOT_REMOVED); + /* + * This code is intentionally commented. It is not necessary + * to check for an empty table because removeAll() was called + * when the start == 0 and end == count - 1. + */ + //if (count - index == 0) setTableEmpty (); + setDeferResize (false); + } +} + +/** + * 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 (); + int itemCount = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + for (int i=0; i<itemCount; i++) { + TableItem item = items [i]; + if (item != null && !item.isDisposed ()) item.release (false); + } + /* + * Feature in Windows 98. When there are a large number + * of columns and items in a table (>1000) where each + * of the subitems in the table has a string, it is much + * faster to delete each item with LVM_DELETEITEM rather + * than using LVM_DELETEALLITEMS. The fix is to detect + * this case and delete the items, one by one. The fact + * that the fix is only necessary on Windows 98 was + * confirmed using version 5.81 of COMCTL32.DLL on both + * Windows 98 and NT. + * + * NOTE: LVM_DELETEALLITEMS is also sent by the table + * when the table is destroyed. + */ + setDeferResize (true); + if (OS.IsWin95 && columnCount > 1) { + boolean redraw = getDrawing () && OS.IsWindowVisible (handle); + if (redraw) OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); + int index = itemCount - 1; + while (index >= 0) { + ignoreSelect = ignoreShrink = true; + int /*long*/ code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0); + ignoreSelect = ignoreShrink = false; + if (code == 0) break; + --index; + } + if (redraw) { + OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0); + /* + * This code is intentionally commented. The window proc + * for the table implements WM_SETREDRAW to invalidate + * and erase the table so it is not necessary to do this + * again. + */ +// int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE; +// OS.RedrawWindow (handle, null, 0, flags); + } + if (index != -1) error (SWT.ERROR_ITEM_NOT_REMOVED); + } else { + ignoreSelect = ignoreShrink = true; + int /*long*/ code = OS.SendMessage (handle, OS.LVM_DELETEALLITEMS, 0, 0); + ignoreSelect = ignoreShrink = false; + if (code == 0) error (SWT.ERROR_ITEM_NOT_REMOVED); + } + setTableEmpty (); + setDeferResize (false); +} + +/** + * 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 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; + LVITEM lvItem = new LVITEM (); + lvItem.state = OS.LVIS_SELECTED; + lvItem.stateMask = OS.LVIS_SELECTED; + for (int i=length-1; i>=0; --i) { + /* + * An index of -1 will apply the change to all + * items. Ensure that indices are greater than -1. + */ + if (indices [i] >= 0) { + ignoreSelect = true; + OS.SendMessage (handle, OS.LVM_SETITEMSTATE, indices [i], lvItem); + ignoreSelect = 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. 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 (); + /* + * An index of -1 will apply the change to all + * items. Ensure that index is greater than -1. + */ + if (index < 0) return; + LVITEM lvItem = new LVITEM (); + lvItem.state = OS.LVIS_SELECTED; + lvItem.stateMask = OS.LVIS_SELECTED; + ignoreSelect = true; + OS.SendMessage (handle, OS.LVM_SETITEMSTATE, index, lvItem); + ignoreSelect = false; +} + +/** + * 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; + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (count == 0 || start >= count) return; + start = Math.max (0, start); + end = Math.min (end, count - 1); + if (start == 0 && end == count - 1) { + selectAll (); + } else { + /* + * An index of -1 will apply the change to all + * items. Indices must be greater than -1. + */ + LVITEM lvItem = new LVITEM (); + lvItem.state = OS.LVIS_SELECTED; + lvItem.stateMask = OS.LVIS_SELECTED; + for (int i=start; i<=end; i++) { + ignoreSelect = true; + OS.SendMessage (handle, OS.LVM_SETITEMSTATE, i, lvItem); + ignoreSelect = false; + } + } +} + +/** + * 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; + LVITEM lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_STATE; + lvItem.state = OS.LVIS_SELECTED; + lvItem.stateMask = OS.LVIS_SELECTED; + ignoreSelect = true; + OS.SendMessage (handle, OS.LVM_SETITEMSTATE, -1, lvItem); + ignoreSelect = false; +} + +void sendEraseItemEvent (TableItem item, NMLVCUSTOMDRAW nmcd, int /*long*/ lParam, Event measureEvent) { + int /*long*/ hDC = nmcd.hdc; + int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1; + if (clrText == -1) clrText = item.foreground; + int clrTextBk = -1; + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if (sortColumn != null && sortDirection != SWT.NONE) { + if (findImageControl () == null) { + if (indexOf (sortColumn) == nmcd.iSubItem) { + clrTextBk = getSortColumnPixel (); + } + } + } + } + clrTextBk = item.cellBackground != null ? item.cellBackground [nmcd.iSubItem] : -1; + if (clrTextBk == -1) clrTextBk = item.background; + /* + * Bug in Windows. For some reason, CDIS_SELECTED always set, + * even for items that are not selected. The fix is to get + * the selection state from the item. + */ + LVITEM lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_STATE; + lvItem.stateMask = OS.LVIS_SELECTED; + lvItem.iItem = (int)/*64*/nmcd.dwItemSpec; + int /*long*/ result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem); + boolean selected = (result != 0 && (lvItem.state & OS.LVIS_SELECTED) != 0); + GCData data = new GCData (); + data.device = display; + int clrSelectionBk = -1; + boolean drawSelected = false, drawBackground = false, drawHot = false; + if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) { + drawHot = hotIndex == nmcd.dwItemSpec; + } + if (OS.IsWindowEnabled (handle)) { + if (selected && (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0)) { + if (OS.GetFocus () == handle || display.getHighContrast ()) { + drawSelected = true; + data.foreground = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT); + data.background = clrSelectionBk = OS.GetSysColor (OS.COLOR_HIGHLIGHT); + } else { + drawSelected = (style & SWT.HIDE_SELECTION) == 0; + data.foreground = OS.GetTextColor (hDC); + data.background = clrSelectionBk = OS.GetSysColor (OS.COLOR_3DFACE); + } + if (explorerTheme) { + data.foreground = clrText != -1 ? clrText : getForegroundPixel (); + } + } else { + drawBackground = clrTextBk != -1; + /* + * Bug in Windows. When LVM_SETTEXTBKCOLOR, LVM_SETBKCOLOR + * or LVM_SETTEXTCOLOR is used to set the background color of + * the the text or the control, the color is not set in the HDC + * that is provided in Custom Draw. The fix is to explicitly + * set the color. + */ + if (clrText == -1 || clrTextBk == -1) { + Control control = findBackgroundControl (); + if (control == null) control = this; + if (clrText == -1) clrText = control.getForegroundPixel (); + if (clrTextBk == -1) clrTextBk = control.getBackgroundPixel (); + } + data.foreground = clrText != -1 ? clrText : OS.GetTextColor (hDC); + data.background = clrTextBk != -1 ? clrTextBk : OS.GetBkColor (hDC); + } + } else { + data.foreground = OS.GetSysColor (OS.COLOR_GRAYTEXT); + data.background = OS.GetSysColor (OS.COLOR_3DFACE); + if (selected) clrSelectionBk = data.background; + } + data.font = item.getFont (nmcd.iSubItem); + data.uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + int nSavedDC = OS.SaveDC (hDC); + GC gc = GC.win32_new (hDC, data); + RECT cellRect = item.getBounds ((int)/*64*/nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, true, hDC); + Event event = new Event (); + event.item = item; + event.gc = gc; + event.index = nmcd.iSubItem; + event.detail |= SWT.FOREGROUND; +// if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) { + if (OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED) == nmcd.dwItemSpec) { + if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) { + if (handle == OS.GetFocus ()) { + int uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + if ((uiState & OS.UISF_HIDEFOCUS) == 0) event.detail |= SWT.FOCUSED; + } + } + } + boolean focused = (event.detail & SWT.FOCUSED) != 0; + if (drawHot) event.detail |= SWT.HOT; + if (drawSelected) event.detail |= SWT.SELECTED; + if (drawBackground) event.detail |= SWT.BACKGROUND; + event.x = cellRect.left; + event.y = cellRect.top; + event.width = cellRect.right - cellRect.left; + event.height = cellRect.bottom - cellRect.top; + gc.setClipping (event.x, event.y, event.width, event.height); + sendEvent (SWT.EraseItem, event); + event.gc = null; + int clrSelectionText = data.foreground; + gc.dispose (); + OS.RestoreDC (hDC, nSavedDC); + if (isDisposed () || item.isDisposed ()) return; + if (event.doit) { + ignoreDrawForeground = (event.detail & SWT.FOREGROUND) == 0; + ignoreDrawBackground = (event.detail & SWT.BACKGROUND) == 0; + ignoreDrawSelection = (event.detail & SWT.SELECTED) == 0; + ignoreDrawFocus = (event.detail & SWT.FOCUSED) == 0; + ignoreDrawHot = (event.detail & SWT.HOT) == 0; + } else { + ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = true; + } + if (drawSelected) { + if (ignoreDrawSelection) { + ignoreDrawHot = true; + if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) { + selectionForeground = clrSelectionText; + } + nmcd.uItemState &= ~OS.CDIS_SELECTED; + OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof); + } + } else { + if (ignoreDrawSelection) { + nmcd.uItemState |= OS.CDIS_SELECTED; + OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof); + } + } + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + boolean firstColumn = nmcd.iSubItem == OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0); + if (ignoreDrawForeground && ignoreDrawHot) { + if (!ignoreDrawBackground && drawBackground) { + RECT backgroundRect = item.getBounds ((int)/*64*/nmcd.dwItemSpec, nmcd.iSubItem, true, false, true, false, hDC); + fillBackground (hDC, clrTextBk, backgroundRect); + } + } + focusRect = null; + if (!ignoreDrawHot || !ignoreDrawSelection || !ignoreDrawFocus) { + boolean fullText = (style & SWT.FULL_SELECTION) != 0 || !firstColumn; + RECT textRect = item.getBounds ((int)/*64*/nmcd.dwItemSpec, nmcd.iSubItem, true, false, fullText, false, hDC); + if ((style & SWT.FULL_SELECTION) == 0) { + if (measureEvent != null) { + textRect.right = Math.min (cellRect.right, measureEvent.x + measureEvent.width); + } + if (!ignoreDrawFocus) { + nmcd.uItemState &= ~OS.CDIS_FOCUS; + OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof); + focusRect = textRect; + } + } + if (explorerTheme) { + if (!ignoreDrawHot || (!ignoreDrawSelection && clrSelectionBk != -1)) { + boolean hot = drawHot; + RECT pClipRect = new RECT (); + OS.SetRect (pClipRect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom); + RECT rect = new RECT (); + OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom); + if ((style & SWT.FULL_SELECTION) != 0) { + int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0); + int index = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, count - 1, 0); + RECT headerRect = new RECT (); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect); + OS.MapWindowPoints (hwndHeader, handle, headerRect, 2); + rect.left = 0; + rect.right = headerRect.right; + pClipRect.left = cellRect.left; + pClipRect.right += EXPLORER_EXTRA; + } else { + rect.right += EXPLORER_EXTRA; + pClipRect.right += EXPLORER_EXTRA; + } + int /*long*/ hTheme = OS.OpenThemeData (handle, Display.LISTVIEW); + int iStateId = selected ? OS.LISS_SELECTED : OS.LISS_HOT; + if (OS.GetFocus () != handle && selected && !hot) iStateId = OS.LISS_SELECTEDNOTFOCUS; + OS.DrawThemeBackground (hTheme, hDC, OS.LVP_LISTITEM, iStateId, rect, pClipRect); + OS.CloseThemeData (hTheme); + } + } else { + if (!ignoreDrawSelection && clrSelectionBk != -1) fillBackground (hDC, clrSelectionBk, textRect); + } + } + if (focused && ignoreDrawFocus) { + nmcd.uItemState &= ~OS.CDIS_FOCUS; + OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof); + } + if (ignoreDrawForeground) { + RECT clipRect = item.getBounds ((int)/*64*/nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, false, hDC); + OS.SaveDC (hDC); + OS.SelectClipRgn (hDC, 0); + OS.ExcludeClipRect (hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); + } +} + +Event sendEraseItemEvent (TableItem item, NMTTCUSTOMDRAW nmcd, int column, RECT cellRect) { + int nSavedDC = OS.SaveDC (nmcd.hdc); + RECT insetRect = toolTipInset (cellRect); + OS.SetWindowOrgEx (nmcd.hdc, insetRect.left, insetRect.top, null); + GCData data = new GCData (); + data.device = display; + data.foreground = OS.GetTextColor (nmcd.hdc); + data.background = OS.GetBkColor (nmcd.hdc); + data.font = item.getFont (column); + data.uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + GC gc = GC.win32_new (nmcd.hdc, data); + Event event = new Event (); + event.item = item; + event.index = column; + event.gc = gc; + event.detail |= SWT.FOREGROUND; + event.x = cellRect.left; + event.y = cellRect.top; + event.width = cellRect.right - cellRect.left; + event.height = cellRect.bottom - cellRect.top; + //gc.setClipping (event.x, event.y, event.width, event.height); + sendEvent (SWT.EraseItem, event); + event.gc = null; + //int newTextClr = data.foreground; + gc.dispose (); + OS.RestoreDC (nmcd.hdc, nSavedDC); + return event; +} + +Event sendMeasureItemEvent (TableItem item, int row, int column, int /*long*/ hDC) { + GCData data = new GCData (); + data.device = display; + data.font = item.getFont (column); + int nSavedDC = OS.SaveDC (hDC); + GC gc = GC.win32_new (hDC, data); + RECT itemRect = item.getBounds (row, column, true, true, false, false, hDC); + Event event = new Event (); + event.item = item; + event.gc = gc; + event.index = column; + event.x = itemRect.left; + event.y = itemRect.top; + event.width = itemRect.right - itemRect.left; + event.height = itemRect.bottom - itemRect.top; + sendEvent (SWT.MeasureItem, event); + event.gc = null; + gc.dispose (); + OS.RestoreDC (hDC, nSavedDC); + if (!isDisposed () && !item.isDisposed ()) { + if (columnCount == 0) { + int width = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0); + if (event.x + event.width > width) setScrollWidth (event.x + event.width); + } + int /*long*/ empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0); + int /*long*/ oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0); + int itemHeight = OS.HIWORD (oneItem) - OS.HIWORD (empty); + if (event.height > itemHeight) setItemHeight (event.height); + } + return event; +} + +LRESULT sendMouseDownEvent (int type, int button, int msg, int /*long*/ wParam, int /*long*/ lParam) { + Display display = this.display; + display.captureChanged = false; + if (!sendMouseEvent (type, button, handle, msg, wParam, lParam)) { + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + return LRESULT.ZERO; + } + + /* + * Feature in Windows. Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN, + * the widget starts a modal loop to determine if the user wants + * to begin a drag/drop operation or marque select. Unfortunately, + * this modal loop eats the corresponding mouse up. The fix is to + * detect the cases when the modal loop has eaten the mouse up and + * issue a fake mouse up. + * + * By observation, when the mouse is clicked anywhere but the check + * box, the widget eats the mouse up. When the mouse is dragged, + * the widget does not eat the mouse up. + */ + LVHITTESTINFO pinfo = new LVHITTESTINFO (); + pinfo.x = OS.GET_X_LPARAM (lParam); + pinfo.y = OS.GET_Y_LPARAM (lParam); + OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo); + if ((style & SWT.FULL_SELECTION) == 0) { + if (hooks (SWT.MeasureItem)) { + /* + * Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest + * a point that is above the table, instead of returning -1 to + * indicate that the hittest failed, a negative index is returned. + * The fix is to consider any value that is negative a failure. + */ + if (OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo) < 0) { + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (count != 0) { + RECT rect = new RECT (); + rect.left = OS.LVIR_ICON; + ignoreCustomDraw = true; + int /*long*/ code = OS.SendMessage (handle, OS.LVM_GETITEMRECT, 0, rect); + ignoreCustomDraw = false; + if (code != 0) { + pinfo.x = rect.left; + /* + * Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest + * a point that is above the table, instead of returning -1 to + * indicate that the hittest failed, a negative index is returned. + * The fix is to consider any value that is negative a failure. + */ + OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo); + if (pinfo.iItem < 0) pinfo.iItem = -1; + pinfo.flags &= ~(OS.LVHT_ONITEMICON | OS.LVHT_ONITEMLABEL); + } + } + } else { + if (pinfo.iSubItem != 0) pinfo.iItem = -1; + } + } + } + + /* + * Force the table to have focus so that when the user + * reselects the focus item, the LVIS_FOCUSED state bits + * for the item will be set. If the user did not click on + * an item, then set focus to the table so that it will + * come to the front and take focus in the work around + * below. + */ + OS.SetFocus (handle); + + /* + * Feature in Windows. When the user selects outside of + * a table item, Windows deselects all the items, even + * when the table is multi-select. While not strictly + * wrong, this is unexpected. The fix is to detect the + * case and avoid calling the window proc. + */ + if ((style & SWT.SINGLE) != 0 || hooks (SWT.MouseDown) || hooks (SWT.MouseUp)) { + if (pinfo.iItem == -1) { + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + return LRESULT.ZERO; + } + } + + /* + * Feature in Windows. When a table item is reselected + * in a single-select table, Windows does not issue a + * WM_NOTIFY because the item state has not changed. + * This is strictly correct but is inconsistent with the + * list widget and other widgets in Windows. The fix is + * to detect the case when an item is reselected and mark + * it as selected. + */ + boolean forceSelect = false; + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0); + if (count == 1 && pinfo.iItem != -1) { + LVITEM lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_STATE; + lvItem.stateMask = OS.LVIS_SELECTED; + lvItem.iItem = pinfo.iItem; + OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem); + if ((lvItem.state & OS.LVIS_SELECTED) != 0) { + forceSelect = true; + } + } + + /* Determine whether the user has selected an item based on SWT.MeasureItem */ + fullRowSelect = false; + if (pinfo.iItem != -1) { + if ((style & SWT.FULL_SELECTION) == 0) { + if (hooks (SWT.MeasureItem)) { + fullRowSelect = hitTestSelection (pinfo.iItem, pinfo.x, pinfo.y); + if (fullRowSelect) { + int flags = OS.LVHT_ONITEMICON | OS.LVHT_ONITEMLABEL; + if ((pinfo.flags & flags) != 0) fullRowSelect = false; + } + } + } + } + + /* + * Feature in Windows. Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN, + * the widget starts a modal loop to determine if the user wants + * to begin a drag/drop operation or marque select. This modal + * loop eats mouse events until a drag is detected. The fix is + * to avoid this behavior by only running the drag and drop when + * the event is hooked and the mouse is over an item. + */ + boolean dragDetect = (state & DRAG_DETECT) != 0 && hooks (SWT.DragDetect); + if (!dragDetect) { + int flags = OS.LVHT_ONITEMICON | OS.LVHT_ONITEMLABEL; + dragDetect = pinfo.iItem == -1 || (pinfo.flags & flags) == 0; + if (fullRowSelect) dragDetect = true; + } + + /* + * Temporarily set LVS_EX_FULLROWSELECT to allow drag and drop + * and the mouse to manipulate items based on the results of + * the SWT.MeasureItem event. + */ + if (fullRowSelect) { + OS.UpdateWindow (handle); + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_FULLROWSELECT, OS.LVS_EX_FULLROWSELECT); + } + dragStarted = false; + display.dragCancelled = false; + if (!dragDetect) display.runDragDrop = false; + int /*long*/ code = callWindowProc (handle, msg, wParam, lParam, forceSelect); + if (!dragDetect) display.runDragDrop = true; + if (fullRowSelect) { + fullRowSelect = false; + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_FULLROWSELECT, 0); + } + + if (dragStarted || display.dragCancelled) { + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + } else { + int flags = OS.LVHT_ONITEMLABEL | OS.LVHT_ONITEMICON; + boolean fakeMouseUp = (pinfo.flags & flags) != 0; + if (!fakeMouseUp && (style & SWT.MULTI) != 0) { + fakeMouseUp = (pinfo.flags & OS.LVHT_ONITEMSTATEICON) == 0; + } + if (fakeMouseUp) { + sendMouseEvent (SWT.MouseUp, button, handle, msg, wParam, lParam); + } + } + return new LRESULT (code); +} + +void sendPaintItemEvent (TableItem item, NMLVCUSTOMDRAW nmcd) { + int /*long*/ hDC = nmcd.hdc; + GCData data = new GCData (); + data.device = display; + data.font = item.getFont (nmcd.iSubItem); + /* + * Bug in Windows. For some reason, CDIS_SELECTED always set, + * even for items that are not selected. The fix is to get + * the selection state from the item. + */ + LVITEM lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_STATE; + lvItem.stateMask = OS.LVIS_SELECTED; + lvItem.iItem = (int)/*64*/nmcd.dwItemSpec; + int /*long*/ result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem); + boolean selected = result != 0 && (lvItem.state & OS.LVIS_SELECTED) != 0; + boolean drawSelected = false, drawBackground = false, drawHot = false; + if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) { + drawHot = hotIndex == nmcd.dwItemSpec; + } + if (OS.IsWindowEnabled (handle)) { + if (selected && (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0)) { + if (OS.GetFocus () == handle || display.getHighContrast ()) { + drawSelected = true; + if (selectionForeground != -1) { + data.foreground = selectionForeground; + } else { + data.foreground = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT); + } + data.background = OS.GetSysColor (OS.COLOR_HIGHLIGHT); + } else { + drawSelected = (style & SWT.HIDE_SELECTION) == 0; + data.foreground = OS.GetTextColor (hDC); + data.background = OS.GetSysColor (OS.COLOR_3DFACE); + } + if (explorerTheme && selectionForeground == -1) { + int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1; + if (clrText == -1) clrText = item.foreground; + data.foreground = clrText != -1 ? clrText : getForegroundPixel (); + } + } else { + int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1; + if (clrText == -1) clrText = item.foreground; + int clrTextBk = item.cellBackground != null ? item.cellBackground [nmcd.iSubItem] : -1; + if (clrTextBk == -1) clrTextBk = item.background; + drawBackground = clrTextBk != -1; + /* + * Bug in Windows. When LVM_SETTEXTBKCOLOR, LVM_SETBKCOLOR + * or LVM_SETTEXTCOLOR is used to set the background color of + * the the text or the control, the color is not set in the HDC + * that is provided in Custom Draw. The fix is to explicitly + * set the color. + */ + if (clrText == -1 || clrTextBk == -1) { + Control control = findBackgroundControl (); + if (control == null) control = this; + if (clrText == -1) clrText = control.getForegroundPixel (); + if (clrTextBk == -1) clrTextBk = control.getBackgroundPixel (); + } + data.foreground = clrText != -1 ? clrText : OS.GetTextColor (hDC); + data.background = clrTextBk != -1 ? clrTextBk : OS.GetBkColor (hDC); + } + } else { + data.foreground = OS.GetSysColor (OS.COLOR_GRAYTEXT); + data.background = OS.GetSysColor (OS.COLOR_3DFACE); + } + data.uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + int nSavedDC = OS.SaveDC (hDC); + GC gc = GC.win32_new (hDC, data); + RECT itemRect = item.getBounds ((int)/*64*/nmcd.dwItemSpec, nmcd.iSubItem, true, true, false, false, hDC); + Event event = new Event (); + event.item = item; + event.gc = gc; + event.index = nmcd.iSubItem; + event.detail |= SWT.FOREGROUND; +// if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) { + if (OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED) == nmcd.dwItemSpec) { + if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) { + if (handle == OS.GetFocus ()) { + int uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + if ((uiState & OS.UISF_HIDEFOCUS) == 0) event.detail |= SWT.FOCUSED; + } + } + } + if (drawHot) event.detail |= SWT.HOT; + if (drawSelected) event.detail |= SWT.SELECTED; + if (drawBackground) event.detail |= SWT.BACKGROUND; + event.x = itemRect.left; + event.y = itemRect.top; + event.width = itemRect.right - itemRect.left; + event.height = itemRect.bottom - itemRect.top; + RECT cellRect = item.getBounds ((int)/*64*/nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, true, hDC); + int cellWidth = cellRect.right - cellRect.left; + int cellHeight = cellRect.bottom - cellRect.top; + gc.setClipping (cellRect.left, cellRect.top, cellWidth, cellHeight); + sendEvent (SWT.PaintItem, event); + if (data.focusDrawn) focusRect = null; + event.gc = null; + gc.dispose (); + OS.RestoreDC (hDC, nSavedDC); +} + +Event sendPaintItemEvent (TableItem item, NMTTCUSTOMDRAW nmcd, int column, RECT itemRect) { + int nSavedDC = OS.SaveDC (nmcd.hdc); + RECT insetRect = toolTipInset (itemRect); + OS.SetWindowOrgEx (nmcd.hdc, insetRect.left, insetRect.top, null); + GCData data = new GCData (); + data.device = display; + data.font = item.getFont (column); + data.foreground = OS.GetTextColor (nmcd.hdc); + data.background = OS.GetBkColor (nmcd.hdc); + data.uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + GC gc = GC.win32_new (nmcd.hdc, data); + Event event = new Event (); + event.item = item; + event.index = column; + event.gc = gc; + event.detail |= SWT.FOREGROUND; + event.x = itemRect.left; + event.y = itemRect.top; + event.width = itemRect.right - itemRect.left; + event.height = itemRect.bottom - itemRect.top; + //gc.setClipping (cellRect.left, cellRect.top, cellWidth, cellHeight); + sendEvent (SWT.PaintItem, event); + event.gc = null; + gc.dispose (); + OS.RestoreDC (nmcd.hdc, nSavedDC); + return event; +} + +void setBackgroundImage (int /*long*/ hBitmap) { + super.setBackgroundImage (hBitmap); + if (hBitmap != 0) { + setBackgroundTransparent (true); + } else { + if (!hooks (SWT.MeasureItem) && !hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) { + setBackgroundTransparent (false); + } + } +} + +void setBackgroundPixel (int newPixel) { + int oldPixel = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0); + if (oldPixel != OS.CLR_NONE) { + if (findImageControl () != null) return; + if (newPixel == -1) newPixel = defaultBackground (); + if (oldPixel != newPixel) { + OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, newPixel); + OS.SendMessage (handle, OS.LVM_SETTEXTBKCOLOR, 0, newPixel); + if ((style & SWT.CHECK) != 0) fixCheckboxImageListColor (true); + } + } + /* + * Feature in Windows. When the background color is changed, + * the table does not redraw until the next WM_PAINT. The fix + * is to force a redraw. + */ + OS.InvalidateRect (handle, null, true); +} + +void setBackgroundTransparent (boolean transparent) { + /* + * Bug in Windows. When the table has the extended style + * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with + * CLR_NONE to make the table transparent, Windows draws + * a black rectangle around the first column. The fix is + * clear LVS_EX_FULLROWSELECT. + * + * Feature in Windows. When LVM_SETBKCOLOR is used with + * CLR_NONE and LVM_SETSELECTEDCOLUMN is used to select + * a column, Windows fills the column with the selection + * color, drawing on top of the background image and any + * other custom drawing. The fix is to clear the selected + * column. + */ + int oldPixel = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0); + if (transparent) { + if (oldPixel != OS.CLR_NONE) { + /* + * Bug in Windows. When the background color is changed, + * the table does not redraw until the next WM_PAINT. The + * fix is to force a redraw. + */ + OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, OS.CLR_NONE); + OS.SendMessage (handle, OS.LVM_SETTEXTBKCOLOR, 0, OS.CLR_NONE); + OS.InvalidateRect (handle, null, true); + + /* Clear LVS_EX_FULLROWSELECT */ + if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) { + int bits = OS.LVS_EX_FULLROWSELECT; + OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, 0); + } + + /* Clear LVM_SETSELECTEDCOLUMN */ + if ((sortDirection & (SWT.UP | SWT.DOWN)) != 0) { + if (sortColumn != null && !sortColumn.isDisposed ()) { + OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, -1, 0); + /* + * Bug in Windows. When LVM_SETSELECTEDCOLUMN is set, Windows + * does not redraw either the new or the previous selected column. + * The fix is to force a redraw. + */ + OS.InvalidateRect (handle, null, true); + } + } + } + } else { + if (oldPixel == OS.CLR_NONE) { + Control control = findBackgroundControl (); + if (control == null) control = this; + if (control.backgroundImage == null) { + int newPixel = control.getBackgroundPixel (); + OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, newPixel); + OS.SendMessage (handle, OS.LVM_SETTEXTBKCOLOR, 0, newPixel); + if ((style & SWT.CHECK) != 0) fixCheckboxImageListColor (true); + OS.InvalidateRect (handle, null, true); + } + + /* Set LVS_EX_FULLROWSELECT */ + if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) { + if (!hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) { + int bits = OS.LVS_EX_FULLROWSELECT; + OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, bits); + } + } + + /* Set LVM_SETSELECTEDCOLUMN */ + if ((sortDirection & (SWT.UP | SWT.DOWN)) != 0) { + if (sortColumn != null && !sortColumn.isDisposed ()) { + int column = indexOf (sortColumn); + if (column != -1) { + OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, column, 0); + /* + * Bug in Windows. When LVM_SETSELECTEDCOLUMN is set, Windows + * does not redraw either the new or the previous selected column. + * The fix is to force a redraw. + */ + OS.InvalidateRect (handle, null, true); + } + } + } + } + } +} + +void setBounds (int x, int y, int width, int height, int flags, boolean defer) { + /* + * Bug in Windows. If the table column widths are adjusted + * in WM_SIZE or WM_POSITIONCHANGED using LVM_SETCOLUMNWIDTH + * blank lines may be inserted at the top of the table. A + * call to LVM_GETTOPINDEX will return a negative number (this + * is an impossible result). Once the blank lines appear, + * there seems to be no way to get rid of them, other than + * destroying and recreating the table. The fix is to send + * the resize notification after the size has been changed in + * the operating system. + * + * NOTE: This does not fix the case when the user is resizing + * columns dynamically. There is no fix for this case at this + * time. + */ + setDeferResize (true); + super.setBounds (x, y, width, height, flags, false); + setDeferResize (false); +} + +/** + * 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); + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + if (columnCount == 0) { + if (order.length != 0) error (SWT.ERROR_INVALID_ARGUMENT); + return; + } + if (order.length != columnCount) error (SWT.ERROR_INVALID_ARGUMENT); + int [] oldOrder = new int [columnCount]; + OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, oldOrder); + 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_RANGE); + if (seen [index]) error (SWT.ERROR_INVALID_ARGUMENT); + seen [index] = true; + if (index != oldOrder [i]) reorder = true; + } + if (reorder) { + RECT [] oldRects = new RECT [columnCount]; + for (int i=0; i<columnCount; i++) { + oldRects [i] = new RECT (); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, oldRects [i]); + } + OS.SendMessage (handle, OS.LVM_SETCOLUMNORDERARRAY, order.length, order); + /* + * Bug in Windows. When LVM_SETCOLUMNORDERARRAY is used to change + * the column order, the header redraws correctly but the table does + * not. The fix is to force a redraw. + */ + OS.InvalidateRect (handle, null, true); + TableColumn[] newColumns = new TableColumn [columnCount]; + System.arraycopy (columns, 0, newColumns, 0, columnCount); + RECT newRect = new RECT (); + for (int i=0; i<columnCount; i++) { + TableColumn column = newColumns [i]; + if (!column.isDisposed ()) { + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, newRect); + if (newRect.left != oldRects [i].left) { + column.updateToolTip (i); + column.sendEvent (SWT.Move); + } + } + } + } +} + +void setCustomDraw (boolean customDraw) { + if (this.customDraw == customDraw) return; + if (!this.customDraw && customDraw && currentItem != null) { + OS.InvalidateRect (handle, null, true); + } + this.customDraw = customDraw; +} + +void setDeferResize (boolean defer) { + if (defer) { + if (resizeCount++ == 0) { + wasResized = false; + /* + * Feature in Windows. When LVM_SETBKCOLOR is used with CLR_NONE + * to make the background of the table transparent, drawing becomes + * slow. The fix is to temporarily clear CLR_NONE when redraw is + * turned off. + */ + if (hooks (SWT.MeasureItem) || hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) { + if (drawCount++ == 0 && OS.IsWindowVisible (handle)) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, 0xFFFFFF); + } + } + } + } else { + if (--resizeCount == 0) { + if (hooks (SWT.MeasureItem) || hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) { + if (--drawCount == 0 /*&& OS.IsWindowVisible (handle)*/) { + OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, OS.CLR_NONE); + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + if (OS.IsWinCE) { + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + if (hwndHeader != 0) OS.InvalidateRect (hwndHeader, null, true); + OS.InvalidateRect (handle, null, true); + } else { + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, null, 0, flags); + } + } + } + if (wasResized) { + wasResized = false; + setResizeChildren (false); + sendEvent (SWT.Resize); + if (isDisposed ()) return; + if (layout != null) { + markLayout (false, false); + updateLayout (false, false); + } + setResizeChildren (true); + } + } + } +} + +void setCheckboxImageList (int width, int height, boolean fixScroll) { + if ((style & SWT.CHECK) == 0) return; + int count = 4, flags = 0; + if (OS.IsWinCE) { + flags |= OS.ILC_COLOR; + } else { + int /*long*/ hDC = OS.GetDC (handle); + int bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL); + int planes = OS.GetDeviceCaps (hDC, OS.PLANES); + OS.ReleaseDC (handle, hDC); + int depth = bits * planes; + switch (depth) { + case 4: flags |= OS.ILC_COLOR4; break; + case 8: flags |= OS.ILC_COLOR8; break; + case 16: flags |= OS.ILC_COLOR16; break; + case 24: flags |= OS.ILC_COLOR24; break; + case 32: flags |= OS.ILC_COLOR32; break; + default: flags |= OS.ILC_COLOR; break; + } + } + if ((style & SWT.RIGHT_TO_LEFT) != 0) flags |= OS.ILC_MIRROR; + if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) flags |= OS.ILC_MASK; + int /*long*/ hStateList = OS.ImageList_Create (width, height, flags, count, count); + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ memDC = OS.CreateCompatibleDC (hDC); + int /*long*/ hBitmap = OS.CreateCompatibleBitmap (hDC, width * count, height); + int /*long*/ hOldBitmap = OS.SelectObject (memDC, hBitmap); + RECT rect = new RECT (); + OS.SetRect (rect, 0, 0, width * count, height); + int clrBackground; + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + Control control = findBackgroundControl (); + if (control == null) control = this; + clrBackground = control.getBackgroundPixel (); + } else { + clrBackground = 0x020000FF; + if ((clrBackground & 0xFFFFFF) == OS.GetSysColor (OS.COLOR_WINDOW)) { + clrBackground = 0x0200FF00; + } + } + int /*long*/ hBrush = OS.CreateSolidBrush (clrBackground); + OS.FillRect (memDC, rect, hBrush); + OS.DeleteObject (hBrush); + int /*long*/ oldFont = OS.SelectObject (hDC, defaultFont ()); + TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA (); + OS.GetTextMetrics (hDC, tm); + OS.SelectObject (hDC, oldFont); + int itemWidth = Math.min (tm.tmHeight, width); + int itemHeight = Math.min (tm.tmHeight, height); + int left = (width - itemWidth) / 2, top = (height - itemHeight) / 2 + 1; + OS.SetRect (rect, left, top, left + itemWidth, top + itemHeight); + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + int /*long*/ hTheme = display.hButtonTheme (); + OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, rect, null); + rect.left += width; rect.right += width; + OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_CHECKEDNORMAL, rect, null); + rect.left += width; rect.right += width; + OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, rect, null); + rect.left += width; rect.right += width; + OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_MIXEDNORMAL, rect, null); + } else { + OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_FLAT); + rect.left += width; rect.right += width; + OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_FLAT); + rect.left += width; rect.right += width; + OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_INACTIVE | OS.DFCS_FLAT); + rect.left += width; rect.right += width; + OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_INACTIVE | OS.DFCS_FLAT); + } + OS.SelectObject (memDC, hOldBitmap); + OS.DeleteDC (memDC); + OS.ReleaseDC (handle, hDC); + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + OS.ImageList_Add (hStateList, hBitmap, 0); + } else { + OS.ImageList_AddMasked (hStateList, hBitmap, clrBackground); + } + OS.DeleteObject (hBitmap); + /* + * Bug in Windows. Making any change to an item that + * changes the item height of a table while the table + * is scrolled can cause the lines to draw incorrectly. + * This happens even when the lines are not currently + * visible and are shown afterwards. The fix is to + * save the top index, scroll to the top of the table + * and then restore the original top index. + */ + int topIndex = getTopIndex (); + if (fixScroll && topIndex != 0) { + setRedraw (false); + setTopIndex (0); + } + int /*long*/ hOldStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0); + OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_STATE, hStateList); + if (hOldStateList != 0) OS.ImageList_Destroy (hOldStateList); + /* + * Bug in Windows. Setting the LVSIL_STATE state image list + * when the table already has a LVSIL_SMALL image list causes + * pixel corruption of the images. The fix is to reset the + * LVSIL_SMALL image list. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + int /*long*/ hImageList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0); + OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList); + } + if (fixScroll && topIndex != 0) { + setTopIndex (topIndex); + setRedraw (true); + } +} + +void setFocusIndex (int index) { +// checkWidget (); + /* + * An index of -1 will apply the change to all + * items. Ensure that index is greater than -1. + */ + if (index < 0) return; + LVITEM lvItem = new LVITEM (); + lvItem.state = OS.LVIS_FOCUSED; + lvItem.stateMask = OS.LVIS_FOCUSED; + ignoreSelect = true; + OS.SendMessage (handle, OS.LVM_SETITEMSTATE, index, lvItem); + ignoreSelect = false; + OS.SendMessage (handle, OS.LVM_SETSELECTIONMARK, 0, index); +} + +public void setFont (Font font) { + checkWidget (); + /* + * Bug in Windows. Making any change to an item that + * changes the item height of a table while the table + * is scrolled can cause the lines to draw incorrectly. + * This happens even when the lines are not currently + * visible and are shown afterwards. The fix is to + * save the top index, scroll to the top of the table + * and then restore the original top index. + */ + int topIndex = getTopIndex (); + if (topIndex != 0) { + setRedraw (false); + setTopIndex (0); + } + if (itemHeight != -1) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.LVS_OWNERDRAWFIXED); + } + super.setFont (font); + if (itemHeight != -1) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + OS.SetWindowLong (handle, OS.GWL_STYLE, bits & ~OS.LVS_OWNERDRAWFIXED); + } + setScrollWidth (null, true); + if (topIndex != 0) { + setTopIndex (topIndex); + setRedraw (true); + } + + /* + * Bug in Windows. Setting the font will cause the table + * to be redrawn but not the column headers. The fix is + * to force a redraw of the column headers. + */ + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + OS.InvalidateRect (hwndHeader, null, true); +} + +void setForegroundPixel (int pixel) { + /* + * The Windows table control uses CLR_DEFAULT to indicate + * that it is using the default foreground color. This + * is undocumented. + */ + if (pixel == -1) pixel = OS.CLR_DEFAULT; + OS.SendMessage (handle, OS.LVM_SETTEXTCOLOR, 0, pixel); + + /* + * Feature in Windows. When the foreground color is + * changed, the table does not redraw until the next + * WM_PAINT. The fix is to force a redraw. + */ + OS.InvalidateRect (handle, null, 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 (); + int newBits = OS.GetWindowLong (handle, OS.GWL_STYLE); + newBits &= ~OS.LVS_NOCOLUMNHEADER; + if (!show) newBits |= OS.LVS_NOCOLUMNHEADER; + /* + * Feature in Windows. Setting or clearing LVS_NOCOLUMNHEADER + * causes the table to scroll to the beginning. The fix is to + * save and restore the top index causing the table to scroll + * to the new location. + */ + int oldIndex = getTopIndex (); + OS.SetWindowLong (handle, OS.GWL_STYLE, newBits); + + /* + * Bug in Windows. Making any change to an item that + * changes the item height of a table while the table + * is scrolled can cause the lines to draw incorrectly. + * This happens even when the lines are not currently + * visible and are shown afterwards. The fix is to + * save the top index, scroll to the top of the table + * and then restore the original top index. + */ + int newIndex = getTopIndex (); + if (newIndex != 0) { + setRedraw (false); + setTopIndex (0); + } + if (show) { + int bits = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((bits & OS.LVS_EX_GRIDLINES) != 0) fixItemHeight (false); + } + setTopIndex (oldIndex); + if (newIndex != 0) { + setRedraw (true); + } + updateHeaderToolTips (); +} + +/** + * 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); + int itemCount = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (count == itemCount) return; + setDeferResize (true); + boolean isVirtual = (style & SWT.VIRTUAL) != 0; + if (!isVirtual) setRedraw (false); + int index = count; + while (index < itemCount) { + TableItem item = items [index]; + if (item != null && !item.isDisposed ()) item.release (false); + if (!isVirtual) { + ignoreSelect = ignoreShrink = true; + int /*long*/ code = OS.SendMessage (handle, OS.LVM_DELETEITEM, count, 0); + ignoreSelect = ignoreShrink = false; + if (code == 0) break; + } + index++; + } + if (index < itemCount) error (SWT.ERROR_ITEM_NOT_REMOVED); + int length = Math.max (4, (count + 3) / 4 * 4); + TableItem [] newItems = new TableItem [length]; + System.arraycopy (items, 0, newItems, 0, Math.min (count, itemCount)); + items = newItems; + if (isVirtual) { + int flags = OS.LVSICF_NOINVALIDATEALL | OS.LVSICF_NOSCROLL; + OS.SendMessage (handle, OS.LVM_SETITEMCOUNT, count, flags); + /* + * Bug in Windows. When a virtual table contains items and + * LVM_SETITEMCOUNT is used to set the new item count to zero, + * Windows does not redraw the table. Note that simply not + * specifying LVSICF_NOINVALIDATEALL or LVSICF_NOSCROLL does + * correct the problem. The fix is to force a redraw. + */ + if (count == 0 && itemCount != 0) { + OS.InvalidateRect (handle, null, true); + } + } else { + for (int i=itemCount; i<count; i++) { + items [i] = new TableItem (this, SWT.NONE, i, true); + } + } + if (!isVirtual) setRedraw (true); + if (itemCount == 0) setScrollWidth (null, false); + setDeferResize (false); +} + +void setItemHeight (boolean fixScroll) { + /* + * Bug in Windows. Making any change to an item that + * changes the item height of a table while the table + * is scrolled can cause the lines to draw incorrectly. + * This happens even when the lines are not currently + * visible and are shown afterwards. The fix is to + * save the top index, scroll to the top of the table + * and then restore the original top index. + */ + int topIndex = getTopIndex (); + if (fixScroll && topIndex != 0) { + setRedraw (false); + setTopIndex (0); + } + if (itemHeight == -1) { + /* + * Feature in Windows. Windows has no API to restore the + * defualt item height for a table. The fix is to use + * WM_SETFONT which recomputes and assigns the default item + * height. + */ + int /*long*/ hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0); + } else { + /* + * Feature in Windows. Window has no API to set the item + * height for a table. The fix is to set temporarily set + * LVS_OWNERDRAWFIXED then resize the table, causing a + * WM_MEASUREITEM to be sent, then clear LVS_OWNERDRAWFIXED. + */ + forceResize (); + RECT rect = new RECT (); + OS.GetWindowRect (handle, rect); + int width = rect.right - rect.left, height = rect.bottom - rect.top; + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.LVS_OWNERDRAWFIXED); + int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOREDRAW | OS.SWP_NOZORDER; + ignoreResize = true; + SetWindowPos (handle, 0 , 0, 0, width, height + 1, flags); + SetWindowPos (handle, 0 , 0, 0, width, height, flags); + ignoreResize = false; + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); + } + if (fixScroll && topIndex != 0) { + setTopIndex (topIndex); + setRedraw (true); + } +} + +/** + * Sets the height of the area which would be used to + * display <em>one</em> of the items in the table. + * + * @param itemHeight 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> + * + * @since 3.2 + */ +/*public*/ void setItemHeight (int itemHeight) { + checkWidget (); + if (itemHeight < -1) error (SWT.ERROR_INVALID_ARGUMENT); + this.itemHeight = itemHeight; + setItemHeight (true); + setScrollWidth (null, true); +} + +/** + * 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 (); + int newBits = show ? OS.LVS_EX_GRIDLINES : 0; + OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_GRIDLINES, newBits); + if (show) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.LVS_NOCOLUMNHEADER) == 0) fixItemHeight (true); + } +} + +public void setRedraw (boolean redraw) { + checkWidget (); + /* + * Feature in Windows. When WM_SETREDRAW is used to turn + * off drawing in a widget, it clears the WS_VISIBLE bits + * and then sets them when redraw is turned back on. This + * means that WM_SETREDRAW will make a widget unexpectedly + * visible. The fix is to track the visibility state while + * drawing is turned off and restore it when drawing is turned + * back on. + */ + if (drawCount == 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.WS_VISIBLE) == 0) state |= HIDDEN; + } + if (redraw) { + if (--drawCount == 0) { + /* + * When many items are added to a table, it is faster to + * temporarily unsubclass the window proc so that messages + * are dispatched directly to the table. + * + * NOTE: This is optimization somewhat dangerous because any + * operation can occur when redraw is turned off, even operations + * where the table must be subclassed in order to have the correct + * behavior or work around a Windows bug. + * + * This code is intentionally commented. + */ +// subclass (); + + /* Set the width of the horizontal scroll bar */ + setScrollWidth (null, true); + + /* + * Bug in Windows. For some reason, when WM_SETREDRAW is used + * to turn redraw back on this may result in a WM_SIZE. If the + * table column widths are adjusted in WM_SIZE, blank lines may + * be inserted at the top of the widget. A call to LVM_GETTOPINDEX + * will return a negative number (this is an impossible result). + * The fix is to send the resize notification after the size has + * been changed in the operating system. + */ + setDeferResize (true); + OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0); + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + if (hwndHeader != 0) OS.SendMessage (hwndHeader, OS.WM_SETREDRAW, 1, 0); + if ((state & HIDDEN) != 0) { + state &= ~HIDDEN; + OS.ShowWindow (handle, OS.SW_HIDE); + } else { + if (OS.IsWinCE) { + OS.InvalidateRect (handle, null, false); + if (hwndHeader != 0) { + OS.InvalidateRect (hwndHeader, null, false); + } + } else { + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, null, 0, flags); + } + } + setDeferResize (false); + } + } else { + if (drawCount++ == 0) { + OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + if (hwndHeader != 0) OS.SendMessage (hwndHeader, OS.WM_SETREDRAW, 0, 0); + + /* + * When many items are added to a table, it is faster to + * temporarily unsubclass the window proc so that messages + * are dispatched directly to the table. + * + * NOTE: This is optimization somewhat dangerous because any + * operation can occur when redraw is turned off, even operations + * where the table must be subclassed in order to have the correct + * behavior or work around a Windows bug. + * + * This code is intentionally commented. + */ +// unsubclass (); + } + } +} + +void setScrollWidth (int width) { + if (width != (int)/*64*/OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0)) { + /* + * Feature in Windows. When LVM_SETCOLUMNWIDTH is sent, + * Windows draws right away instead of queuing a WM_PAINT. + * This can cause recursive calls when called from paint + * or from messages that are retrieving the item data, + * such as WM_NOTIFY, causing a stack overflow. The fix + * is to turn off redraw and queue a repaint, collapsing + * the recursive calls. + */ + boolean redraw = false; + if (hooks (SWT.MeasureItem)) { + redraw = getDrawing () && OS.IsWindowVisible (handle); + } + if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, width); + if (redraw) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + if (OS.IsWinCE) { + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + if (hwndHeader != 0) OS.InvalidateRect (hwndHeader, null, true); + OS.InvalidateRect (handle, null, true); + } else { + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, null, 0, flags); + } + } + } +} + +boolean setScrollWidth (TableItem item, boolean force) { + if (currentItem != null) { + if (currentItem != item) fixScrollWidth = true; + return false; + } + if (!force && (!getDrawing () || !OS.IsWindowVisible (handle))) { + fixScrollWidth = true; + return false; + } + fixScrollWidth = false; + /* + * NOTE: It is much faster to measure the strings and compute the + * width of the scroll bar in non-virtual table rather than using + * LVM_SETCOLUMNWIDTH with LVSCW_AUTOSIZE. + */ + if (columnCount == 0) { + int newWidth = 0, imageIndent = 0, index = 0; + int itemCount = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + while (index < itemCount) { + String string = null; + int /*long*/ hFont = -1; + if (item != null) { + string = item.text; + imageIndent = Math.max (imageIndent, item.imageIndent); + hFont = item.fontHandle (0); + } else { + if (items [index] != null) { + TableItem tableItem = items [index]; + string = tableItem.text; + imageIndent = Math.max (imageIndent, tableItem.imageIndent); + hFont = tableItem.fontHandle (0); + } + } + if (string != null && string.length () != 0) { + if (hFont != -1) { + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ oldFont = OS.SelectObject (hDC, hFont); + int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX; + TCHAR buffer = new TCHAR (getCodePage (), string, false); + RECT rect = new RECT (); + OS.DrawText (hDC, buffer, buffer.length (), rect, flags); + OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + newWidth = Math.max (newWidth, rect.right - rect.left); + } else { + TCHAR buffer = new TCHAR (getCodePage (), string, true); + newWidth = Math.max (newWidth, (int)/*64*/OS.SendMessage (handle, OS.LVM_GETSTRINGWIDTH, 0, buffer)); + } + } + if (item != null) break; + index++; + } + /* + * Bug in Windows. When the width of the first column is + * small but not zero, Windows draws '...' outside of the + * bounds of the text. This is strange, but only causes + * problems when the item is selected. In this case, Windows + * clears the '...' but doesn't redraw it when the item is + * deselected, causing pixel corruption. The fix is to ensure + * that the column is at least wide enough to draw a single + * space. + */ + if (newWidth == 0) { + TCHAR buffer = new TCHAR (getCodePage (), " ", true); + newWidth = Math.max (newWidth, (int)/*64*/OS.SendMessage (handle, OS.LVM_GETSTRINGWIDTH, 0, buffer)); + } + int /*long*/ hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0); + if (hStateList != 0) { + int [] cx = new int [1], cy = new int [1]; + OS.ImageList_GetIconSize (hStateList, cx, cy); + newWidth += cx [0] + INSET; + } + int /*long*/ hImageList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0); + if (hImageList != 0) { + int [] cx = new int [1], cy = new int [1]; + OS.ImageList_GetIconSize (hImageList, cx, cy); + newWidth += (imageIndent + 1) * cx [0]; + } else { + /* + * Bug in Windows. When LVM_SETIMAGELIST is used to remove the + * image list by setting it to NULL, the item width and height + * is not changed and space is reserved for icons despite the + * fact that there are none. The fix is to set the image list + * to be very small before setting it to NULL. This causes + * Windows to reserve the smallest possible space when an image + * list is removed. In this case, the scroll width must be one + * pixel larger. + */ + newWidth++; + } + newWidth += INSET * 2; + int oldWidth = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + newWidth += VISTA_EXTRA; + } + if (newWidth > oldWidth) { + setScrollWidth (newWidth); + return true; + } + } + return false; +} + +/** + * 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); + deselectAll (); + int length = indices.length; + if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return; + select (indices); + int focusIndex = indices [0]; + if (focusIndex != -1) setFocusIndex (focusIndex); + showSelection (); +} + +/** + * 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); + deselectAll (); + int length = items.length; + if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return; + int focusIndex = -1; + for (int i=length-1; i>=0; --i) { + int index = indexOf (items [i]); + if (index != -1) { + select (focusIndex = index); + } + } + if (focusIndex != -1) setFocusIndex (focusIndex); + showSelection (); +} + +/** + * 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 (); + deselectAll (); + select (index); + if (index != -1) setFocusIndex (index); + showSelection (); +} + +/** + * 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 (); + deselectAll (); + if (end < 0 || start > end || ((style & SWT.SINGLE) != 0 && start != end)) return; + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (count == 0 || start >= count) return; + start = Math.max (0, start); + end = Math.min (end, count - 1); + select (start, end); + setFocusIndex (start); + showSelection (); +} + +/** + * 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 (sortColumn != null && !sortColumn.isDisposed ()) { + sortColumn.setSortDirection (SWT.NONE); + } + sortColumn = column; + if (sortColumn != null && sortDirection != SWT.NONE) { + sortColumn.setSortDirection (sortDirection); + } +} + +/** + * 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 | SWT.DOWN)) == 0 && direction != SWT.NONE) return; + sortDirection = direction; + if (sortColumn != null && !sortColumn.isDisposed ()) { + sortColumn.setSortDirection (direction); + } +} + +void setSubImagesVisible (boolean visible) { + int dwExStyle = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((dwExStyle & OS.LVS_EX_SUBITEMIMAGES) != 0 == visible) return; + int bits = visible ? OS.LVS_EX_SUBITEMIMAGES : 0; + OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_SUBITEMIMAGES, bits); +} + +void setTableEmpty () { + if (imageList != null) { + /* + * Bug in Windows. When LVM_SETIMAGELIST is used to remove the + * image list by setting it to NULL, the item width and height + * is not changed and space is reserved for icons despite the + * fact that there are none. The fix is to set the image list + * to be very small before setting it to NULL. This causes + * Windows to reserve the smallest possible space when an image + * list is removed. + */ + int /*long*/ hImageList = OS.ImageList_Create (1, 1, 0, 0, 0); + OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList); + OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, 0); + if (headerImageList != null) { + int /*long*/ hHeaderImageList = headerImageList.getHandle (); + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hHeaderImageList); + } + OS.ImageList_Destroy (hImageList); + display.releaseImageList (imageList); + imageList = null; + if (itemHeight != -1) setItemHeight (false); + } + if (!hooks (SWT.MeasureItem) && !hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) { + Control control = findBackgroundControl (); + if (control == null) control = this; + if (control.backgroundImage == null) { + setCustomDraw (false); + setBackgroundTransparent (false); + if (OS.COMCTL32_MAJOR < 6) style &= ~SWT.DOUBLE_BUFFERED; + } + } + items = new TableItem [4]; + if (columnCount == 0) { + OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, 0); + setScrollWidth (null, false); + } +} + +/** + * 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 (); + int topIndex = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0); + if (index == topIndex) return; + if (!painted && hooks (SWT.MeasureItem)) hitTestSelection (index, 0, 0); + + /* + * Bug in Windows. For some reason, LVM_SCROLL refuses to + * scroll a table vertically when the width and height of + * the table is smaller than a certain size. The values + * that seem to cause the problem are width=68 and height=6 + * but there is no guarantee that these values cause the + * failure on different machines or on different versions + * of Windows. It may depend on the font and any number + * of other factors. For example, setting the font to + * anything but the default sometimes fixes the problem. + * The fix is to use LVM_GETCOUNTPERPAGE to detect the + * case when the number of visible items is zero and + * use LVM_ENSUREVISIBLE to scroll the table to make the + * index visible. + */ + + /* + * Bug in Windows. When the table header is visible and + * there is not enough space to show a single table item, + * LVM_GETCOUNTPERPAGE can return a negative number instead + * of zero. The fix is to test for negative or zero. + */ + if (OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0) <= 0) { + /* + * Bug in Windows. For some reason, LVM_ENSUREVISIBLE can + * scroll one item more or one item less when there is not + * enough space to show a single table item. The fix is + * to detect the case and call LVM_ENSUREVISIBLE again with + * the same arguments. It seems that once LVM_ENSUREVISIBLE + * has scrolled into the general area, it is able to scroll + * to the exact item. + */ + OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1); + if (index != OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)) { + OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1); + } + return; + } + + /* Use LVM_SCROLL to scroll the table */ + RECT rect = new RECT (); + rect.left = OS.LVIR_BOUNDS; + ignoreCustomDraw = true; + OS.SendMessage (handle, OS.LVM_GETITEMRECT, 0, rect); + ignoreCustomDraw = false; + int dy = (index - topIndex) * (rect.bottom - rect.top); + OS.SendMessage (handle, OS.LVM_SCROLL, 0, dy); +} + +/** + * 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; + int index = indexOf (column); + if (!(0 <= index && index < columnCount)) return; + /* + * Feature in Windows. Calling LVM_GETSUBITEMRECT with -1 for the + * row number gives the bounds of the item that would be above the + * first row in the table. This is undocumented and does not work + * for the first column. In this case, to get the bounds of the + * first column, get the bounds of the second column and subtract + * the width of the first. The left edge of the second column is + * also used as the right edge of the first. + */ + RECT itemRect = new RECT (); + itemRect.left = OS.LVIR_BOUNDS; + if (index == 0) { + itemRect.top = 1; + ignoreCustomDraw = true; + OS.SendMessage (handle, OS.LVM_GETSUBITEMRECT, -1, itemRect); + ignoreCustomDraw = false; + itemRect.right = itemRect.left; + int width = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0); + itemRect.left = itemRect.right - width; + } else { + itemRect.top = index; + ignoreCustomDraw = true; + OS.SendMessage (handle, OS.LVM_GETSUBITEMRECT, -1, itemRect); + ignoreCustomDraw = false; + } + /* + * Bug in Windows. When a table that is drawing grid lines + * is slowly scrolled horizontally to the left, the table does + * not redraw the newly exposed vertical grid lines. The fix + * is to save the old scroll position, call the window proc, + * get the new scroll position and redraw the new area. + */ + int oldPos = 0; + int bits = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((bits & OS.LVS_EX_GRIDLINES) != 0) { + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_POS; + OS.GetScrollInfo (handle, OS.SB_HORZ, info); + oldPos = info.nPos; + } + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + if (itemRect.left < rect.left) { + int dx = itemRect.left - rect.left; + OS.SendMessage (handle, OS.LVM_SCROLL, dx, 0); + } else { + int width = Math.min (rect.right - rect.left, itemRect.right - itemRect.left); + if (itemRect.left + width > rect.right) { + int dx = itemRect.left + width - rect.right; + OS.SendMessage (handle, OS.LVM_SCROLL, dx, 0); + } + } + /* + * Bug in Windows. When a table that is drawing grid lines + * is slowly scrolled horizontally to the left, the table does + * not redraw the newly exposed vertical grid lines. The fix + * is to save the old scroll position, call the window proc, + * get the new scroll position and redraw the new area. + */ + if ((bits & OS.LVS_EX_GRIDLINES) != 0) { + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_POS; + OS.GetScrollInfo (handle, OS.SB_HORZ, info); + int newPos = info.nPos; + if (newPos < oldPos) { + rect.right = oldPos - newPos + GRID_WIDTH; + OS.InvalidateRect (handle, rect, true); + } + } +} + +void showItem (int index) { + if (!painted && hooks (SWT.MeasureItem)) hitTestSelection (index, 0, 0); + /* + * Bug in Windows. For some reason, when there is insufficient space + * to show an item, LVM_ENSUREVISIBLE causes blank lines to be + * inserted at the top of the widget. A call to LVM_GETTOPINDEX will + * return a negative number (this is an impossible result). The fix + * is to use LVM_GETCOUNTPERPAGE to detect the case when the number + * of visible items is zero and use LVM_ENSUREVISIBLE with the + * fPartialOK flag set to true to scroll the table. + */ + if (OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0) <= 0) { + /* + * Bug in Windows. For some reason, LVM_ENSUREVISIBLE can + * scroll one item more or one item less when there is not + * enough space to show a single table item. The fix is + * to detect the case and call LVM_ENSUREVISIBLE again with + * the same arguments. It seems that once LVM_ENSUREVISIBLE + * has scrolled into the general area, it is able to scroll + * to the exact item. + */ + OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1); + if (index != OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)) { + OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1); + } + } else { + OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 0); + } +} + +/** + * 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) showItem (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 = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_SELECTED); + if (index != -1) showItem (index); +} + +/*public*/ void sort () { + checkWidget (); +// if ((style & SWT.VIRTUAL) != 0) return; +// int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); +// if (itemCount == 0 || itemCount == 1) return; +// Comparator comparator = new Comparator () { +// int index = sortColumn == null ? 0 : indexOf (sortColumn); +// public int compare (Object object1, Object object2) { +// TableItem item1 = (TableItem) object1, item2 = (TableItem) object2; +// if (sortDirection == SWT.UP || sortDirection == SWT.NONE) { +// return item1.getText (index).compareTo (item2.getText (index)); +// } else { +// return item2.getText (index).compareTo (item1.getText (index)); +// } +// } +// }; +// Arrays.sort (items, 0, itemCount, comparator); +// redraw (); +} + +void subclass () { + super.subclass (); + if (HeaderProc != 0) { + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, display.windowProc); + } +} + +RECT toolTipInset (RECT rect) { + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + RECT insetRect = new RECT (); + OS.SetRect (insetRect, rect.left - 1, rect.top - 1, rect.right + 1, rect.bottom + 1); + return insetRect; + } + return rect; +} + +RECT toolTipRect (RECT rect) { + RECT toolRect = new RECT (); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + OS.SetRect (toolRect, rect.left - 1, rect.top - 1, rect.right + 1, rect.bottom + 1); + } else { + int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0); + OS.SetRect (toolRect, rect.left, rect.top, rect.right, rect.bottom); + int dwStyle = OS.GetWindowLong (hwndToolTip, OS.GWL_STYLE); + int dwExStyle = OS.GetWindowLong (hwndToolTip, OS.GWL_EXSTYLE); + OS.AdjustWindowRectEx (toolRect, dwStyle, false, dwExStyle); + } + return toolRect; +} + +String toolTipText (NMTTDISPINFO hdr) { + int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0); + if (hwndToolTip == hdr.hwndFrom && toolTipText != null) return ""; //$NON-NLS-1$ + if (headerToolTipHandle == hdr.hwndFrom) { + for (int i=0; i<columnCount; i++) { + TableColumn column = columns [i]; + if (column.id == hdr.idFrom) return column.toolTipText; + } + } + return super.toolTipText (hdr); +} + +void unsubclass () { + super.unsubclass (); + if (HeaderProc != 0) { + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc); + } +} + +void update (boolean all) { +// checkWidget (); + /* + * When there are many columns in a table, scrolling performance + * can be improved by temporarily unsubclassing the window proc + * so that internal messages are dispatched directly to the table. + * If the application expects to see a paint event or has a child + * whose font, foreground or background color might be needed, + * the window proc cannot be unsubclassed. + * + * NOTE: The header tooltip can subclass the header proc so the + * current proc must be restored or header tooltips stop working. + */ + int /*long*/ oldHeaderProc = 0, oldTableProc = 0; + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + boolean fixSubclass = isOptimizedRedraw (); + if (fixSubclass) { + oldTableProc = OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TableProc); + oldHeaderProc = OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc); + } + super.update (all); + if (fixSubclass) { + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldTableProc); + OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, oldHeaderProc); + } +} + +void updateHeaderToolTips () { + if (headerToolTipHandle == 0) return; + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + RECT rect = new RECT (); + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + lpti.uFlags = OS.TTF_SUBCLASS; + lpti.hwnd = hwndHeader; + lpti.lpszText = OS.LPSTR_TEXTCALLBACK; + for (int i=0; i<columnCount; i++) { + TableColumn column = columns [i]; + if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, rect) != 0) { + lpti.uId = column.id = display.nextToolTipId++; + lpti.left = rect.left; + lpti.top = rect.top; + lpti.right = rect.right; + lpti.bottom = rect.bottom; + OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, lpti); + } + } +} + +void updateImages () { + if (sortColumn != null && !sortColumn.isDisposed ()) { + if (OS.COMCTL32_MAJOR < 6) { + switch (sortDirection) { + case SWT.UP: + case SWT.DOWN: + sortColumn.setImage (display.getSortImage (sortDirection), true, true); + break; + } + } + } +} + +void updateMoveable () { + int index = 0; + while (index < columnCount) { + if (columns [index].moveable) break; + index++; + } + int newBits = index < columnCount ? OS.LVS_EX_HEADERDRAGDROP : 0; + OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_HEADERDRAGDROP, newBits); +} + +int widgetStyle () { + int bits = super.widgetStyle () | OS.LVS_SHAREIMAGELISTS; + if ((style & SWT.HIDE_SELECTION) == 0) bits |= OS.LVS_SHOWSELALWAYS; + if ((style & SWT.SINGLE) != 0) bits |= OS.LVS_SINGLESEL; + /* + * This code is intentionally commented. In the future, + * the FLAT bit may be used to make the header flat and + * unresponsive to mouse clicks. + */ +// if ((style & SWT.FLAT) != 0) bits |= OS.LVS_NOSORTHEADER; + bits |= OS.LVS_REPORT | OS.LVS_NOCOLUMNHEADER; + if ((style & SWT.VIRTUAL) != 0) bits |= OS.LVS_OWNERDATA; + return bits; +} + +TCHAR windowClass () { + return TableClass; +} + +int /*long*/ windowProc () { + return TableProc; +} + +int /*long*/ windowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + if (hwnd != handle) { + switch (msg) { + case OS.WM_CONTEXTMENU: { + LRESULT result = wmContextMenu (hwnd, wParam, lParam); + if (result != null) return result.value; + break; + } + case OS.WM_CAPTURECHANGED: { + /* + * Bug in Windows. When the capture changes during a + * header drag, Windows does not redraw the header item + * such that the header remains pressed. For example, + * when focus is assigned to a push button, the mouse is + * pressed (but not released), then the SPACE key is + * pressed to activate the button, the capture changes, + * the header not notified and NM_RELEASEDCAPTURE is not + * sent. The fix is to redraw the header when the capture + * changes to another control. + * + * This does not happen on XP. + */ + if (OS.COMCTL32_MAJOR < 6) { + if (lParam != 0) { + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + if (lParam != hwndHeader) OS.InvalidateRect (hwndHeader, null, true); + } + } + break; + } + case OS.WM_MOUSELEAVE: { + /* + * Bug in Windows. On XP, when a tooltip is hidden + * due to a time out or mouse press, the tooltip + * remains active although no longer visible and + * won't show again until another tooltip becomes + * active. The fix is to reset the tooltip bounds. + */ + if (OS.COMCTL32_MAJOR >= 6) updateHeaderToolTips (); + updateHeaderToolTips (); + break; + } + case OS.WM_NOTIFY: { + NMHDR hdr = new NMHDR (); + OS.MoveMemory (hdr, lParam, NMHDR.sizeof); + switch (hdr.code) { + case OS.TTN_SHOW: + case OS.TTN_POP: + case OS.TTN_GETDISPINFOA: + case OS.TTN_GETDISPINFOW: + return OS.SendMessage (handle, msg, wParam, lParam); + } + break; + } + case OS.WM_SETCURSOR: { + if (wParam == hwnd) { + int hitTest = (short) OS.LOWORD (lParam); + if (hitTest == OS.HTCLIENT) { + HDHITTESTINFO pinfo = new HDHITTESTINFO (); + int pos = OS.GetMessagePos (); + POINT pt = new POINT (); + OS.POINTSTOPOINT (pt, pos); + OS.ScreenToClient (hwnd, pt); + pinfo.x = pt.x; + pinfo.y = pt.y; + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + int index = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_HITTEST, 0, pinfo); + if (0 <= index && index < columnCount && !columns [index].resizable) { + if ((pinfo.flags & (OS.HHT_ONDIVIDER | OS.HHT_ONDIVOPEN)) != 0) { + OS.SetCursor (OS.LoadCursor (0, OS.IDC_ARROW)); + return 1; + } + } + } + } + break; + } + } + return callWindowProc (hwnd, msg, wParam, lParam); + } + if (msg == Display.DI_GETDRAGIMAGE) { + /* + * Bug in Windows. On Vista, for some reason, DI_GETDRAGIMAGE + * returns an image that does not contain strings. + * + * Bug in Windows. For custom draw control the window origin the + * in HDC is wrong. + * + * The fix for both cases is to create the image using PrintWindow(). + */ + if ((!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) || (style & SWT.VIRTUAL) != 0 || hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) { + int topIndex = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0); + int selection = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETNEXTITEM, topIndex - 1, OS.LVNI_SELECTED); + if (selection == -1) return 0; + POINT mousePos = new POINT (); + OS.POINTSTOPOINT (mousePos, OS.GetMessagePos ()); + OS.MapWindowPoints(0, handle, mousePos, 1); + RECT clientRect = new RECT (); + OS.GetClientRect (handle, clientRect); + TableItem item = _getItem (selection); + RECT rect = item.getBounds (selection, 0, true, true, true); + if ((style & SWT.FULL_SELECTION) != 0) { + int width = DRAG_IMAGE_SIZE; + rect.left = Math.max (clientRect.left, mousePos.x - width / 2); + if (clientRect.right > rect.left + width) { + rect.right = rect.left + width; + } else { + rect.right = clientRect.right; + rect.left = Math.max (clientRect.left, rect.right - width); + } + } + int /*long*/ hRgn = OS.CreateRectRgn (rect.left, rect.top, rect.right, rect.bottom); + while ((selection = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETNEXTITEM, selection, OS.LVNI_SELECTED)) != -1) { + if (rect.bottom - rect.top > DRAG_IMAGE_SIZE) break; + if (rect.bottom > clientRect.bottom) break; + RECT itemRect = item.getBounds (selection, 0, true, true, true); + int /*long*/ rectRgn = OS.CreateRectRgn (rect.left, itemRect.top, rect.right, itemRect.bottom); + OS.CombineRgn (hRgn, hRgn, rectRgn, OS.RGN_OR); + OS.DeleteObject (rectRgn); + rect.bottom = itemRect.bottom; + } + OS.GetRgnBox (hRgn, rect); + + /* Create resources */ + int /*long*/ hdc = OS.GetDC (handle); + int /*long*/ memHdc = OS.CreateCompatibleDC (hdc); + BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER (); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biWidth = rect.right - rect.left; + bmiHeader.biHeight = -(rect.bottom - rect.top); + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = 32; + bmiHeader.biCompression = OS.BI_RGB; + byte [] bmi = new byte [BITMAPINFOHEADER.sizeof]; + OS.MoveMemory (bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + int /*long*/ [] pBits = new int /*long*/ [1]; + int /*long*/ memDib = OS.CreateDIBSection (0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0); + if (memDib == 0) SWT.error (SWT.ERROR_NO_HANDLES); + int /*long*/ oldMemBitmap = OS.SelectObject (memHdc, memDib); + int colorKey = 0x0000FD; + POINT pt = new POINT(); + OS.SetWindowOrgEx (memHdc, rect.left, rect.top, pt); + OS.FillRect (memHdc, rect, findBrush (colorKey, OS.BS_SOLID)); + OS.OffsetRgn (hRgn, -rect.left, -rect.top); + OS.SelectClipRgn (memHdc, hRgn); + OS.PrintWindow (handle, memHdc, 0); + OS.SetWindowOrgEx (memHdc, pt.x, pt.y, null); + OS.SelectObject (memHdc, oldMemBitmap); + OS.DeleteDC (memHdc); + OS.ReleaseDC (0, hdc); + OS.DeleteObject (hRgn); + + SHDRAGIMAGE shdi = new SHDRAGIMAGE (); + shdi.hbmpDragImage = memDib; + shdi.crColorKey = colorKey; + shdi.sizeDragImage.cx = bmiHeader.biWidth; + shdi.sizeDragImage.cy = -bmiHeader.biHeight; + shdi.ptOffset.x = mousePos.x - rect.left; + shdi.ptOffset.y = mousePos.y - rect.top; + if ((style & SWT.MIRRORED) != 0) { + shdi.ptOffset.x = shdi.sizeDragImage.cx - shdi.ptOffset.x; + } + OS.MoveMemory (lParam, shdi, SHDRAGIMAGE.sizeof); + return 1; + } + } + return super.windowProc (hwnd, msg, wParam, lParam); +} + +LRESULT WM_CHAR (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_CHAR (wParam, lParam); + if (result != null) return result; + switch ((int)/*64*/wParam) { + case ' ': + if ((style & SWT.CHECK) != 0) { + int index = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED); + if (index != -1) { + TableItem item = _getItem (index); + item.setChecked (!item.getChecked (), true); + if (!OS.IsWinCE) { + OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, index + 1); + } + } + } + /* + * NOTE: Call the window proc with WM_KEYDOWN rather than WM_CHAR + * so that the key that was ignored during WM_KEYDOWN is processed. + * This allows the application to cancel an operation that is normally + * performed in WM_KEYDOWN from WM_CHAR. + */ + int /*long*/ code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam); + return new LRESULT (code); + case SWT.CR: + /* + * Feature in Windows. Windows sends LVN_ITEMACTIVATE from WM_KEYDOWN + * instead of WM_CHAR. This means that application code that expects + * to consume the key press and therefore avoid a SWT.DefaultSelection + * event will fail. The fix is to ignore LVN_ITEMACTIVATE when it is + * caused by WM_KEYDOWN and send SWT.DefaultSelection from WM_CHAR. + */ + int index = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED); + if (index != -1) { + Event event = new Event (); + event.item = _getItem (index); + postEvent (SWT.DefaultSelection, event); + } + return LRESULT.ZERO; + } + return result; +} + +LRESULT WM_CONTEXTMENU (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. For some reason, when the right + * mouse button is pressed over an item, Windows sends + * a WM_CONTEXTMENU from WM_RBUTTONDOWN, instead of from + * WM_RBUTTONUP. This causes two context menus requests + * to be sent. The fix is to ignore WM_CONTEXTMENU on + * mouse down. + * + * NOTE: This only happens when dragging is disabled. + * When the table is detecting drag, the WM_CONTEXTMENU + * is not sent WM_RBUTTONUP. + */ + if (!display.runDragDrop) return LRESULT.ZERO; + return super.WM_CONTEXTMENU (wParam, lParam); +} + +LRESULT WM_ERASEBKGND (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_ERASEBKGND (wParam, lParam); + if (findImageControl () != null) return LRESULT.ONE; + if (!OS.IsWinCE && OS.COMCTL32_MAJOR < 6) { + if ((style & SWT.DOUBLE_BUFFERED) != 0) { + int bits = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((bits & OS.LVS_EX_DOUBLEBUFFER) == 0) return LRESULT.ONE; + } + } + return result; +} + +LRESULT WM_GETOBJECT (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Ensure that there is an accessible object created for this + * control because support for checked item accessibility is + * temporarily implemented in the accessibility package. + */ + if ((style & SWT.CHECK) != 0) { + if (accessible == null) accessible = new_Accessible (this); + } + return super.WM_GETOBJECT (wParam, lParam); +} + +LRESULT WM_KEYDOWN (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_KEYDOWN (wParam, lParam); + if (result != null) return result; + switch ((int)/*64*/wParam) { + case OS.VK_SPACE: + /* + * Ensure that the window proc does not process VK_SPACE + * so that it can be handled in WM_CHAR. This allows the + * application to cancel an operation that is normally + * performed in WM_KEYDOWN from WM_CHAR. + */ + return LRESULT.ZERO; + case OS.VK_ADD: + if (OS.GetKeyState (OS.VK_CONTROL) < 0) { + int index = 0; + while (index < columnCount) { + if (!columns [index].getResizable ()) break; + index++; + } + if (index != columnCount || hooks (SWT.MeasureItem)) { + TableColumn [] newColumns = new TableColumn [columnCount]; + System.arraycopy (columns, 0, newColumns, 0, columnCount); + for (int i=0; i<newColumns.length; i++) { + TableColumn column = newColumns [i]; + if (!column.isDisposed () && column.getResizable ()) { + column.pack (); + } + } + return LRESULT.ZERO; + } + } + break; + case OS.VK_PRIOR: + case OS.VK_NEXT: + case OS.VK_HOME: + case OS.VK_END: + /* + * When there are many columns in a table, scrolling performance + * can be improved by temporarily unsubclassing the window proc + * so that internal messages are dispatched directly to the table. + * If the application expects to see a paint event, the window + * proc cannot be unsubclassed or the event will not be seen. + * + * NOTE: The header tooltip can subclass the header proc so the + * current proc must be restored or header tooltips stop working. + */ + int /*long*/ oldHeaderProc = 0, oldTableProc = 0; + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + boolean fixSubclass = isOptimizedRedraw (); + if (fixSubclass) { + oldTableProc = OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TableProc); + oldHeaderProc = OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc); + } + int /*long*/ code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam); + result = code == 0 ? LRESULT.ZERO : new LRESULT (code); + if (fixSubclass) { + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldTableProc); + OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, oldHeaderProc); + } + //FALL THROUGH + case OS.VK_UP: + case OS.VK_DOWN: + OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0); + break; + } + return result; +} + +LRESULT WM_KILLFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_KILLFOCUS (wParam, lParam); + /* + * Bug in Windows. When focus is lost, Windows does not + * redraw the selection properly, leaving the image and + * check box appearing selected. The fix is to redraw + * the table. + */ + if (imageList != null || (style & SWT.CHECK) != 0) { + OS.InvalidateRect (handle, null, false); + } + return result; +} + +LRESULT WM_LBUTTONDBLCLK (int /*long*/ wParam, int /*long*/ lParam) { + + /* + * Feature in Windows. When the user selects outside of + * a table item, Windows deselects all the items, even + * when the table is multi-select. While not strictly + * wrong, this is unexpected. The fix is to detect the + * case and avoid calling the window proc. + */ + LVHITTESTINFO pinfo = new LVHITTESTINFO (); + pinfo.x = OS.GET_X_LPARAM (lParam); + pinfo.y = OS.GET_Y_LPARAM (lParam); + int index = (int)/*64*/OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo); + Display display = this.display; + display.captureChanged = false; + sendMouseEvent (SWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam); + if (!sendMouseEvent (SWT.MouseDoubleClick, 1, handle, OS.WM_LBUTTONDBLCLK, wParam, lParam)) { + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + return LRESULT.ZERO; + } + if (pinfo.iItem != -1) callWindowProc (handle, OS.WM_LBUTTONDBLCLK, wParam, lParam); + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + + /* Look for check/uncheck */ + if ((style & SWT.CHECK) != 0) { + /* + * Note that when the table has LVS_EX_FULLROWSELECT and the + * user clicks anywhere on a row except on the check box, all + * of the bits are set. The hit test flags are LVHT_ONITEM. + * This means that a bit test for LVHT_ONITEMSTATEICON is not + * the correct way to determine that the user has selected + * the check box, equality is needed. + */ + if (index != -1 && pinfo.flags == OS.LVHT_ONITEMSTATEICON) { + TableItem item = _getItem (index); + item.setChecked (!item.getChecked (), true); + if (!OS.IsWinCE) { + OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, index + 1); + } + } + } + return LRESULT.ZERO; +} + +LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. For some reason, capturing + * the mouse after processing the mouse event for the + * widget interferes with the normal mouse processing + * for the widget. The fix is to avoid the automatic + * mouse capture. + */ + LRESULT result = sendMouseDownEvent (SWT.MouseDown, 1, OS.WM_LBUTTONDOWN, wParam, lParam); + if (result == LRESULT.ZERO) return result; + + /* Look for check/uncheck */ + if ((style & SWT.CHECK) != 0) { + LVHITTESTINFO pinfo = new LVHITTESTINFO (); + pinfo.x = OS.GET_X_LPARAM (lParam); + pinfo.y = OS.GET_Y_LPARAM (lParam); + /* + * Note that when the table has LVS_EX_FULLROWSELECT and the + * user clicks anywhere on a row except on the check box, all + * of the bits are set. The hit test flags are LVHT_ONITEM. + * This means that a bit test for LVHT_ONITEMSTATEICON is not + * the correct way to determine that the user has selected + * the check box, equality is needed. + */ + int index = (int)/*64*/OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo); + if (index != -1 && pinfo.flags == OS.LVHT_ONITEMSTATEICON) { + TableItem item = _getItem (index); + item.setChecked (!item.getChecked (), true); + if (!OS.IsWinCE) { + OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, index + 1); + } + } + } + return result; +} + +LRESULT WM_MOUSEHOVER (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. Despite the fact that hot + * tracking is not enabled, the hot tracking code + * in WM_MOUSEHOVER is executed causing the item + * under the cursor to be selected. The fix is to + * avoid calling the window proc. + */ + LRESULT result = super.WM_MOUSEHOVER (wParam, lParam); + int bits = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + int mask = OS.LVS_EX_ONECLICKACTIVATE | OS.LVS_EX_TRACKSELECT | OS.LVS_EX_TWOCLICKACTIVATE; + if ((bits & mask) != 0) return result; + return LRESULT.ZERO; +} + +LRESULT WM_PAINT (int /*long*/ wParam, int /*long*/ lParam) { + if (!ignoreShrink) { + /* Resize the item array to match the item count */ + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (items.length > 4 && items.length - count > 3) { + int length = Math.max (4, (count + 3) / 4 * 4); + TableItem [] newItems = new TableItem [length]; + System.arraycopy (items, 0, newItems, 0, count); + items = newItems; + } + } + if (fixScrollWidth) setScrollWidth (null, true); + if (!OS.IsWinCE && OS.COMCTL32_MAJOR < 6) { + if ((style & SWT.DOUBLE_BUFFERED) != 0 || findImageControl () != null) { + int bits = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((bits & OS.LVS_EX_DOUBLEBUFFER) == 0) { + GC gc = null; + int /*long*/ paintDC = 0; + PAINTSTRUCT ps = new PAINTSTRUCT (); + boolean hooksPaint = hooks (SWT.Paint) || filters (SWT.Paint); + if (hooksPaint) { + GCData data = new GCData (); + data.ps = ps; + data.hwnd = handle; + gc = GC.win32_new (this, data); + paintDC = gc.handle; + } else { + paintDC = OS.BeginPaint (handle, ps); + } + int width = ps.right - ps.left; + int height = ps.bottom - ps.top; + if (width != 0 && height != 0) { + int /*long*/ hDC = OS.CreateCompatibleDC (paintDC); + POINT lpPoint1 = new POINT (), lpPoint2 = new POINT (); + OS.SetWindowOrgEx (hDC, ps.left, ps.top, lpPoint1); + OS.SetBrushOrgEx (hDC, ps.left, ps.top, lpPoint2); + int /*long*/ hBitmap = OS.CreateCompatibleBitmap (paintDC, width, height); + int /*long*/ hOldBitmap = OS.SelectObject (hDC, hBitmap); + if ((int)/*64*/OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) != OS.CLR_NONE) { + RECT rect = new RECT (); + OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom); + drawBackground (hDC, rect); + } + callWindowProc (handle, OS.WM_PAINT, hDC, 0); + OS.SetWindowOrgEx (hDC, lpPoint1.x, lpPoint1.y, null); + OS.SetBrushOrgEx (hDC, lpPoint2.x, lpPoint2.y, null); + OS.BitBlt (paintDC, ps.left, ps.top, width, height, hDC, 0, 0, OS.SRCCOPY); + OS.SelectObject (hDC, hOldBitmap); + OS.DeleteObject (hBitmap); + OS.DeleteObject (hDC); + if (hooksPaint) { + Event event = new Event (); + event.gc = gc; + event.x = ps.left; + event.y = ps.top; + event.width = ps.right - ps.left; + event.height = ps.bottom - ps.top; + sendEvent (SWT.Paint, event); + // widget could be disposed at this point + event.gc = null; + } + } + if (hooksPaint) { + gc.dispose (); + } else { + OS.EndPaint (handle, ps); + } + return LRESULT.ZERO; + } + } + } + return super.WM_PAINT (wParam, lParam); +} + +LRESULT WM_RBUTTONDBLCLK (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. When the user selects outside of + * a table item, Windows deselects all the items, even + * when the table is multi-select. While not strictly + * wrong, this is unexpected. The fix is to detect the + * case and avoid calling the window proc. + */ + LVHITTESTINFO pinfo = new LVHITTESTINFO (); + pinfo.x = OS.GET_X_LPARAM (lParam); + pinfo.y = OS.GET_Y_LPARAM (lParam); + OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo); + Display display = this.display; + display.captureChanged = false; + sendMouseEvent (SWT.MouseDown, 3, handle, OS.WM_RBUTTONDOWN, wParam, lParam); + if (sendMouseEvent (SWT.MouseDoubleClick, 3, handle, OS.WM_RBUTTONDBLCLK, wParam, lParam)) { + if (pinfo.iItem != -1) callWindowProc (handle, OS.WM_RBUTTONDBLCLK, wParam, lParam); + } + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + return LRESULT.ZERO; +} + +LRESULT WM_RBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. For some reason, capturing + * the mouse after processing the mouse event for the + * widget interferes with the normal mouse processing + * for the widget. The fix is to avoid the automatic + * mouse capture. + */ + return sendMouseDownEvent (SWT.MouseDown, 3, OS.WM_RBUTTONDOWN, wParam, lParam); +} + +LRESULT WM_SETFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETFOCUS (wParam, lParam); + /* + * Bug in Windows. When focus is gained after the + * selection has been changed using LVM_SETITEMSTATE, + * Windows redraws the selected text but does not + * redraw the image or the check box, leaving them + * appearing unselected. The fix is to redraw + * the table. + */ + if (imageList != null || (style & SWT.CHECK) != 0) { + OS.InvalidateRect (handle, null, false); + } + + /* + * Bug in Windows. For some reason, the table does + * not set the default focus rectangle to be the first + * item in the table when it gets focus and there is + * no selected item. The fix to make the first item + * be the focus item. + */ + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + if (count == 0) return result; + int index = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED); + if (index == -1) { + LVITEM lvItem = new LVITEM (); + lvItem.state = OS.LVIS_FOCUSED; + lvItem.stateMask = OS.LVIS_FOCUSED; + ignoreSelect = true; + OS.SendMessage (handle, OS.LVM_SETITEMSTATE, 0, lvItem); + ignoreSelect = false; + } + return result; +} + +LRESULT WM_SETFONT (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETFONT (wParam, lParam); + if (result != null) return result; + + /* + * Bug in Windows. When a header has a sort indicator + * triangle, Windows resizes the indicator based on the + * size of the n-1th font. The fix is to always make + * the n-1th font be the default. This makes the sort + * indicator always be the default size. + * + * NOTE: The table window proc sets the actual font in + * the header so that all that is necessary here is to + * set the default first. + */ + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + OS.SendMessage (hwndHeader, OS.WM_SETFONT, 0, lParam); + + if (headerToolTipHandle != 0) { + OS.SendMessage (headerToolTipHandle, OS.WM_SETFONT, wParam, lParam); + } + return result; +} + +LRESULT WM_SETREDRAW (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETREDRAW (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. When LVM_SETBKCOLOR is used with CLR_NONE + * to make the background of the table transparent, drawing becomes + * slow. The fix is to temporarily clear CLR_NONE when redraw is + * turned off. + */ + if (wParam == 1) { + if ((int)/*64*/OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) != OS.CLR_NONE) { + if (hooks (SWT.MeasureItem) || hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) { + OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, OS.CLR_NONE); + } + } + } + /* + * Bug in Windows. When WM_SETREDRAW is used to turn off + * redraw for a list, table or tree, the background of the + * control is drawn. The fix is to call DefWindowProc(), + * which stops all graphics output to the control. + */ + OS.DefWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam); + int /*long*/ code = callWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam); + if (wParam == 0) { + if ((int)/*64*/OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) { + OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, 0xFFFFFF); + } + } + return code == 0 ? LRESULT.ZERO : new LRESULT (code); +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreResize) return null; + if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) { + OS.InvalidateRect (handle, null, true); + } + if (resizeCount != 0) { + wasResized = true; + return null; + } + return super.WM_SIZE (wParam, lParam); +} + +LRESULT WM_SYSCOLORCHANGE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam); + if (result != null) return result; + if (findBackgroundControl () == null) { + setBackgroundPixel (defaultBackground ()); + } else { + int oldPixel = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0); + if (oldPixel != OS.CLR_NONE) { + if (findImageControl () == null) { + if ((style & SWT.CHECK) != 0) fixCheckboxImageListColor (true); + } + } + } + return result; +} + +LRESULT WM_HSCROLL (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Bug in Windows. When a table that is drawing grid lines + * is slowly scrolled horizontally to the left, the table does + * not redraw the newly exposed vertical grid lines. The fix + * is to save the old scroll position, call the window proc, + * get the new scroll position and redraw the new area. + */ + int oldPos = 0; + int bits = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((bits & OS.LVS_EX_GRIDLINES) != 0) { + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_POS; + OS.GetScrollInfo (handle, OS.SB_HORZ, info); + oldPos = info.nPos; + } + + /* + * Feature in Windows. When there are many columns in a table, + * scrolling performance can be improved by unsubclassing the + * window proc so that internal messages are dispatched directly + * to the table. If the application expects to see a paint event + * or has a child whose font, foreground or background color might + * be needed, the window proc cannot be unsubclassed + * + * NOTE: The header tooltip can subclass the header proc so the + * current proc must be restored or header tooltips stop working. + */ + int /*long*/ oldHeaderProc = 0, oldTableProc = 0; + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + boolean fixSubclass = isOptimizedRedraw (); + if (fixSubclass) { + oldTableProc = OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TableProc); + oldHeaderProc = OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc); + } + + /* + * Feature in Windows. For some reason, when the table window + * proc processes WM_HSCROLL or WM_VSCROLL when there are many + * columns in the table, scrolling is slow and the table does + * not keep up with the position of the scroll bar. The fix + * is to turn off redraw, scroll, turn redraw back on and redraw + * the entire table. Strangly, redrawing the entire table is + * faster. + */ + boolean fixScroll = false; + if (OS.LOWORD (wParam) != OS.SB_ENDSCROLL) { + if (OS.COMCTL32_MAJOR >= 6) { + if (columnCount > H_SCROLL_LIMIT) { + int rowCount = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0); + if (rowCount > V_SCROLL_LIMIT) fixScroll = getDrawing () && OS.IsWindowVisible (handle); + } + } + } + if (fixScroll) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + LRESULT result = super.WM_HSCROLL (wParam, lParam); + if (fixScroll) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, null, 0, flags); + /* + * Feature in Windows. On Vista only, it is faster to + * compute and answer the data for the visible columns + * of a table when scrolling, rather than just return + * the data for each column when asked. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + RECT headerRect = new RECT (), rect = new RECT (); + OS.GetClientRect (handle, rect); + boolean [] visible = new boolean [columnCount]; + for (int i=0; i<columnCount; i++) { + visible [i] = true; + if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, headerRect) != 0) { + OS.MapWindowPoints (hwndHeader, handle, headerRect, 2); + visible [i] = OS.IntersectRect(headerRect, rect, headerRect); + } + } + try { + display.hwndParent = OS.GetParent (handle); + display.columnVisible = visible; + OS.UpdateWindow (handle); + } finally { + display.columnVisible = null; + } + } + } + + if (fixSubclass) { + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldTableProc); + OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, oldHeaderProc); + } + + /* + * Bug in Windows. When a table that is drawing grid lines + * is slowly scrolled horizontally to the left, the table does + * not redraw the newly exposed vertical grid lines. The fix + * is to save the old scroll position, call the window proc, + * get the new scroll position and redraw the new area. + */ + if ((bits & OS.LVS_EX_GRIDLINES) != 0) { + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_POS; + OS.GetScrollInfo (handle, OS.SB_HORZ, info); + int newPos = info.nPos; + if (newPos < oldPos) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + rect.right = oldPos - newPos + GRID_WIDTH; + OS.InvalidateRect (handle, rect, true); + } + } + return result; +} + +LRESULT WM_VSCROLL (int /*long*/ wParam, int /*long*/ lParam) { + /* + * When there are many columns in a table, scrolling performance + * can be improved by temporarily unsubclassing the window proc + * so that internal messages are dispatched directly to the table. + * If the application expects to see a paint event or has a child + * whose font, foreground or background color might be needed, + * the window proc cannot be unsubclassed. + * + * NOTE: The header tooltip can subclass the header proc so the + * current proc must be restored or header tooltips stop working. + */ + int /*long*/ oldHeaderProc = 0, oldTableProc = 0; + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + boolean fixSubclass = isOptimizedRedraw (); + if (fixSubclass) { + oldTableProc = OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TableProc); + oldHeaderProc = OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc); + } + + /* + * Feature in Windows. For some reason, when the table window + * proc processes WM_HSCROLL or WM_VSCROLL when there are many + * columns in the table, scrolling is slow and the table does + * not keep up with the position of the scroll bar. The fix + * is to turn off redraw, scroll, turn redraw back on and redraw + * the entire table. Strangly, redrawing the entire table is + * faster. + */ + boolean fixScroll = false; + if (OS.LOWORD (wParam) != OS.SB_ENDSCROLL) { + if (OS.COMCTL32_MAJOR >= 6) { + if (columnCount > H_SCROLL_LIMIT) { + int rowCount = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0); + if (rowCount > V_SCROLL_LIMIT) fixScroll = getDrawing () && OS.IsWindowVisible (handle); + } + } + } + if (fixScroll) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + LRESULT result = super.WM_VSCROLL (wParam, lParam); + if (fixScroll) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, null, 0, flags); + /* + * Feature in Windows. On Vista only, it is faster to + * compute and answer the data for the visible columns + * of a table when scrolling, rather than just return + * the data for each column when asked. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + RECT headerRect = new RECT (), rect = new RECT (); + OS.GetClientRect (handle, rect); + boolean [] visible = new boolean [columnCount]; + for (int i=0; i<columnCount; i++) { + visible [i] = true; + if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, headerRect) != 0) { + OS.MapWindowPoints (hwndHeader, handle, headerRect, 2); + visible [i] = OS.IntersectRect(headerRect, rect, headerRect); + } + } + try { + display.hwndParent = OS.GetParent (handle); + display.columnVisible = visible; + OS.UpdateWindow (handle); + } finally { + display.columnVisible = null; + } + } + } + + if (fixSubclass) { + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldTableProc); + OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, oldHeaderProc); + } + + /* + * Bug in Windows. When a table is drawing grid lines and the + * user scrolls vertically up or down by a line or a page, the + * table does not redraw the grid lines for newly exposed items. + * The fix is to invalidate the items. + */ + int bits = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((bits & OS.LVS_EX_GRIDLINES) != 0) { + int code = OS.LOWORD (wParam); + switch (code) { + case OS.SB_ENDSCROLL: + case OS.SB_THUMBPOSITION: + case OS.SB_THUMBTRACK: + case OS.SB_TOP: + case OS.SB_BOTTOM: + break; + case OS.SB_LINEDOWN: + case OS.SB_LINEUP: + RECT rect = new RECT (); + OS.GetWindowRect (hwndHeader, rect); + int headerHeight = rect.bottom - rect.top; + RECT clientRect = new RECT (); + OS.GetClientRect (handle, clientRect); + clientRect.top += headerHeight; + int /*long*/ empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0); + int /*long*/ oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0); + int itemHeight = OS.HIWORD (oneItem) - OS.HIWORD (empty); + if (code == OS.SB_LINEDOWN) { + clientRect.top = clientRect.bottom - itemHeight - GRID_WIDTH; + } else { + clientRect.bottom = clientRect.top + itemHeight + GRID_WIDTH; + } + OS.InvalidateRect (handle, clientRect, true); + break; + case OS.SB_PAGEDOWN: + case OS.SB_PAGEUP: + OS.InvalidateRect (handle, null, true); + break; + } + } + return result; +} + +LRESULT wmMeasureChild (int /*long*/ wParam, int /*long*/ lParam) { + MEASUREITEMSTRUCT struct = new MEASUREITEMSTRUCT (); + OS.MoveMemory (struct, lParam, MEASUREITEMSTRUCT.sizeof); + if (itemHeight == -1) { + int /*long*/ empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0); + int /*long*/ oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0); + struct.itemHeight = OS.HIWORD (oneItem) - OS.HIWORD (empty); + } else { + struct.itemHeight = itemHeight; + } + OS.MoveMemory (lParam, struct, MEASUREITEMSTRUCT.sizeof); + return null; +} + +LRESULT wmNotify (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0); + if (hdr.hwndFrom == hwndToolTip) { + LRESULT result = wmNotifyToolTip (hdr, wParam, lParam); + if (result != null) return result; + } + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + if (hdr.hwndFrom == hwndHeader) { + LRESULT result = wmNotifyHeader (hdr, wParam, lParam); + if (result != null) return result; + } + return super.wmNotify (hdr, wParam, lParam); +} + +LRESULT wmNotifyChild (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + switch (hdr.code) { + case OS.LVN_ODFINDITEMA: + case OS.LVN_ODFINDITEMW: { + if ((style & SWT.VIRTUAL) != 0) return new LRESULT (-1); + break; + } + case OS.LVN_ODSTATECHANGED: { + if ((style & SWT.VIRTUAL) != 0) { + if (!ignoreSelect) { + NMLVODSTATECHANGE lpStateChange = new NMLVODSTATECHANGE (); + OS.MoveMemory (lpStateChange, lParam, NMLVODSTATECHANGE.sizeof); + boolean oldSelected = (lpStateChange.uOldState & OS.LVIS_SELECTED) != 0; + boolean newSelected = (lpStateChange.uNewState & OS.LVIS_SELECTED) != 0; + if (oldSelected != newSelected) wasSelected = true; + } + } + break; + } + case OS.LVN_GETDISPINFOA: + case OS.LVN_GETDISPINFOW: { +// if (drawCount != 0 || !OS.IsWindowVisible (handle)) break; + NMLVDISPINFO plvfi = new NMLVDISPINFO (); + OS.MoveMemory (plvfi, lParam, NMLVDISPINFO.sizeof); + + boolean [] visible = display.columnVisible; + if (visible != null && !visible [plvfi.iSubItem]) { + break; + } + + /* + * When an item is being deleted from a virtual table, do not + * allow the application to provide data for a new item that + * becomes visible until the item has been removed from the + * items array. Because arbitrary application code can run + * during the callback, the items array might be accessed + * in an inconsistent state. Rather than answering the data + * right away, queue a redraw for later. + */ + if ((style & SWT.VIRTUAL) != 0) { + if (ignoreShrink) { + OS.SendMessage (handle, OS.LVM_REDRAWITEMS, plvfi.iItem, plvfi.iItem); + break; + } + } + + /* + * Feature in Windows. When a new table item is inserted + * using LVM_INSERTITEM in a table that is transparent + * (ie. LVM_SETBKCOLOR has been called with CLR_NONE), + * TVM_INSERTITEM calls LVN_GETDISPINFO before the item + * has been added to the array. The fix is to check for + * null. + */ + TableItem item = _getItem (plvfi.iItem); + if (item == null) break; + + /* + * The cached flag is used by both virtual and non-virtual + * tables to indicate that Windows has asked at least once + * for a table item. + */ + if (!item.cached) { + if ((style & SWT.VIRTUAL) != 0) { + lastIndexOf = plvfi.iItem; + if (!checkData (item, lastIndexOf, false)) break; + TableItem newItem = fixScrollWidth ? null : item; + if (setScrollWidth (newItem, true)) { + OS.InvalidateRect (handle, null, true); + } + } + item.cached = true; + } + if ((plvfi.mask & OS.LVIF_TEXT) != 0) { + String string = null; + if (plvfi.iSubItem == 0) { + string = item.text; + } else { + String [] strings = item.strings; + if (strings != null) string = strings [plvfi.iSubItem]; + } + if (string != null) { + /* + * Bug in Windows. When pszText points to a zero length + * NULL terminated string, Windows correctly draws the + * empty string but the cache of the bounds for the item + * is not reset. This means that when the text for the + * item is set and then reset to an empty string, the + * selection draws using the bounds of the previous text. + * The fix is to use a space rather than an empty string + * when anything but a tool tip is requested (to avoid + * a tool tip that is a single space). + * + * NOTE: This is only a problem for items in the first + * column. Assigning NULL to other columns stops Windows + * from drawing the selection when LVS_EX_FULLROWSELECT + * is set. + */ + int length = Math.min (string.length (), plvfi.cchTextMax - 1); + if (!tipRequested && plvfi.iSubItem == 0 && length == 0) { + string = " "; //$NON-NLS-1$ + length = 1; + } + char [] buffer = display.tableBuffer; + if (buffer == null || plvfi.cchTextMax > buffer.length) { + buffer = display.tableBuffer = new char [plvfi.cchTextMax]; + } + string.getChars (0, length, buffer, 0); + buffer [length++] = 0; + if (OS.IsUnicode) { + OS.MoveMemory (plvfi.pszText, buffer, length * 2); + } else { + OS.WideCharToMultiByte (getCodePage (), 0, buffer, length, plvfi.pszText, plvfi.cchTextMax, null, null); + OS.MoveMemory (plvfi.pszText + plvfi.cchTextMax - 1, new byte [1], 1); + } + } + } + boolean move = false; + if ((plvfi.mask & OS.LVIF_IMAGE) != 0) { + Image image = null; + if (plvfi.iSubItem == 0) { + image = item.image; + } else { + Image [] images = item.images; + if (images != null) image = images [plvfi.iSubItem]; + } + if (image != null) { + plvfi.iImage = imageIndex (image, plvfi.iSubItem); + move = true; + } + } + if ((plvfi.mask & OS.LVIF_STATE) != 0) { + if (plvfi.iSubItem == 0) { + int state = 1; + if (item.checked) state++; + if (item.grayed) state +=2; + plvfi.state = state << 12; + plvfi.stateMask = OS.LVIS_STATEIMAGEMASK; + move = true; + } + } + if ((plvfi.mask & OS.LVIF_INDENT) != 0) { + if (plvfi.iSubItem == 0) { + plvfi.iIndent = item.imageIndent; + move = true; + } + } + if (move) OS.MoveMemory (lParam, plvfi, NMLVDISPINFO.sizeof); + break; + } + case OS.NM_CUSTOMDRAW: { + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + if (hdr.hwndFrom == hwndHeader) break; + if (!customDraw && findImageControl () == null) { + /* + * Feature in Windows. When the table is disabled, it draws + * with a gray background but does not gray the text. The fix + * is to explicitly gray the text using Custom Draw. + */ + if (OS.IsWindowEnabled (handle)) { + /* + * Feature in Windows. On Vista using the explorer theme, + * Windows draws a vertical line to separate columns. When + * there is only a single column, the line looks strange. + * The fix is to draw the background using custom draw. + */ + if (!explorerTheme || columnCount != 0) break; + } + } + NMLVCUSTOMDRAW nmcd = new NMLVCUSTOMDRAW (); + OS.MoveMemory (nmcd, lParam, NMLVCUSTOMDRAW.sizeof); + switch (nmcd.dwDrawStage) { + case OS.CDDS_PREPAINT: return CDDS_PREPAINT (nmcd, wParam, lParam); + case OS.CDDS_ITEMPREPAINT: return CDDS_ITEMPREPAINT (nmcd, wParam, lParam); + case OS.CDDS_ITEMPOSTPAINT: return CDDS_ITEMPOSTPAINT (nmcd, wParam, lParam); + case OS.CDDS_SUBITEMPREPAINT: return CDDS_SUBITEMPREPAINT (nmcd, wParam, lParam); + case OS.CDDS_SUBITEMPOSTPAINT: return CDDS_SUBITEMPOSTPAINT (nmcd, wParam, lParam); + case OS.CDDS_POSTPAINT: return CDDS_POSTPAINT (nmcd, wParam, lParam); + } + break; + } + case OS.LVN_MARQUEEBEGIN: { + if ((style & SWT.SINGLE) != 0) return LRESULT.ONE; + if (hooks (SWT.MouseDown) || hooks (SWT.MouseUp)) { + return LRESULT.ONE; + } + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + if (findImageControl () != null) return LRESULT.ONE; + } + break; + } + case OS.LVN_BEGINDRAG: + case OS.LVN_BEGINRDRAG: { + if (OS.GetKeyState (OS.VK_LBUTTON) >= 0) break; + dragStarted = true; + if (hdr.code == OS.LVN_BEGINDRAG) { + int pos = OS.GetMessagePos (); + POINT pt = new POINT (); + OS.POINTSTOPOINT (pt, pos); + OS.ScreenToClient (handle, pt); + sendDragEvent (1, pt.x, pt.y); + } + break; + } + case OS.LVN_COLUMNCLICK: { + NMLISTVIEW pnmlv = new NMLISTVIEW (); + OS.MoveMemory(pnmlv, lParam, NMLISTVIEW.sizeof); + TableColumn column = columns [pnmlv.iSubItem]; + if (column != null) { + column.postEvent (SWT.Selection); + } + break; + } + case OS.LVN_ITEMACTIVATE: { + if (ignoreActivate) break; + NMLISTVIEW pnmlv = new NMLISTVIEW (); + OS.MoveMemory(pnmlv, lParam, NMLISTVIEW.sizeof); + if (pnmlv.iItem != -1) { + Event event = new Event (); + event.item = _getItem (pnmlv.iItem); + postEvent (SWT.DefaultSelection, event); + } + break; + } + case OS.LVN_ITEMCHANGED: { + if (fullRowSelect) { + fullRowSelect = false; + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_FULLROWSELECT, 0); + } + if (!ignoreSelect) { + NMLISTVIEW pnmlv = new NMLISTVIEW (); + OS.MoveMemory (pnmlv, lParam, NMLISTVIEW.sizeof); + if ((pnmlv.uChanged & OS.LVIF_STATE) != 0) { + if (pnmlv.iItem == -1) { + wasSelected = true; + } else { + boolean oldSelected = (pnmlv.uOldState & OS.LVIS_SELECTED) != 0; + boolean newSelected = (pnmlv.uNewState & OS.LVIS_SELECTED) != 0; + if (oldSelected != newSelected) wasSelected = true; + } + } + } + if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) { + int /*long*/ hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0); + int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0); + if (count != 0) { + forceResize (); + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + NMLISTVIEW pnmlv = new NMLISTVIEW (); + OS.MoveMemory (pnmlv, lParam, NMLISTVIEW.sizeof); + if (pnmlv.iItem != -1) { + RECT itemRect = new RECT (); + itemRect.left = OS.LVIR_BOUNDS; + ignoreCustomDraw = true; + OS.SendMessage (handle, OS. LVM_GETITEMRECT, pnmlv.iItem, itemRect); + ignoreCustomDraw = false; + RECT headerRect = new RECT (); + int index = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, count - 1, 0); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect); + OS.MapWindowPoints (hwndHeader, handle, headerRect, 2); + rect.left = headerRect.right; + rect.top = itemRect.top; + rect.bottom = itemRect.bottom; + OS.InvalidateRect (handle, rect, true); + } + } + } + break; + } + case OS.NM_RECOGNIZEGESTURE: + /* + * Feature on Pocket PC. The tree and table controls detect the tap + * and hold gesture by default. They send a GN_CONTEXTMENU message to show + * the popup menu. This default behaviour is unwanted on Pocket PC 2002 + * when no menu has been set, as it still draws a red circle. The fix + * is to disable this default behaviour when no menu is set by returning + * TRUE when receiving the Pocket PC 2002 specific NM_RECOGNIZEGESTURE + * message. + */ + if (OS.IsPPC) { + boolean hasMenu = menu != null && !menu.isDisposed (); + if (!hasMenu && !hooks (SWT.MenuDetect)) return LRESULT.ONE; + } + break; + case OS.GN_CONTEXTMENU: + if (OS.IsPPC) { + boolean hasMenu = menu != null && !menu.isDisposed (); + if (hasMenu || hooks (SWT.MenuDetect)) { + NMRGINFO nmrg = new NMRGINFO (); + OS.MoveMemory (nmrg, lParam, NMRGINFO.sizeof); + showMenu (nmrg.x, nmrg.y); + return LRESULT.ONE; + } + } + break; + } + return super.wmNotifyChild (hdr, wParam, lParam); +} + +LRESULT wmNotifyHeader (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. On NT, the automatically created + * header control is created as a UNICODE window, not an + * ANSI window despite the fact that the parent is created + * as an ANSI window. This means that it sends UNICODE + * notification messages to the parent window on NT for + * no good reason. The data and size in the NMHEADER and + * HDITEM structs is identical between the platforms so no + * different message is actually necessary. Despite this, + * Windows sends different messages. The fix is to look + * for both messages, despite the platform. This works + * because only one will be sent on either platform, never + * both. + */ + switch (hdr.code) { + case OS.HDN_BEGINTRACKW: + case OS.HDN_BEGINTRACKA: + case OS.HDN_DIVIDERDBLCLICKW: + case OS.HDN_DIVIDERDBLCLICKA: { + if (columnCount == 0) return LRESULT.ONE; + NMHEADER phdn = new NMHEADER (); + OS.MoveMemory (phdn, lParam, NMHEADER.sizeof); + TableColumn column = columns [phdn.iItem]; + if (column != null && !column.getResizable ()) { + return LRESULT.ONE; + } + ignoreColumnMove = true; + switch (hdr.code) { + case OS.HDN_DIVIDERDBLCLICKW: + case OS.HDN_DIVIDERDBLCLICKA: + /* + * Bug in Windows. When the first column of a table does not + * have an image and the user double clicks on the divider, + * Windows packs the column but does not take into account + * the empty space left for the image. The fix is to pack + * the column explicitly rather than letting Windows do it. + * + * NOTE: This bug does not happen on Vista. + */ + boolean fixPack = false; + if (!OS.IsWinCE && OS.WIN32_VERSION < OS.VERSION (6, 0)) { + fixPack = phdn.iItem == 0 && !firstColumnImage; + } + if (column != null && (fixPack || hooks (SWT.MeasureItem))) { + column.pack (); + return LRESULT.ONE; + } + } + break; + } + case OS.NM_RELEASEDCAPTURE: { + if (!ignoreColumnMove) { + for (int i=0; i<columnCount; i++) { + TableColumn column = columns [i]; + column.updateToolTip (i); + } + } + ignoreColumnMove = false; + break; + } + case OS.HDN_BEGINDRAG: { + if (ignoreColumnMove) return LRESULT.ONE; + int bits = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((bits & OS.LVS_EX_HEADERDRAGDROP) == 0) break; + if (columnCount == 0) return LRESULT.ONE; + NMHEADER phdn = new NMHEADER (); + OS.MoveMemory (phdn, lParam, NMHEADER.sizeof); + if (phdn.iItem != -1) { + TableColumn column = columns [phdn.iItem]; + if (column != null && !column.getMoveable ()) { + ignoreColumnMove = true; + return LRESULT.ONE; + } + } + break; + } + case OS.HDN_ENDDRAG: { + int bits = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((bits & OS.LVS_EX_HEADERDRAGDROP) == 0) break; + NMHEADER phdn = new NMHEADER (); + OS.MoveMemory (phdn, lParam, NMHEADER.sizeof); + if (phdn.iItem != -1 && phdn.pitem != 0) { + HDITEM pitem = new HDITEM (); + OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof); + if ((pitem.mask & OS.HDI_ORDER) != 0 && pitem.iOrder != -1) { + if (columnCount == 0) break; + int [] order = new int [columnCount]; + OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order); + int index = 0; + while (index < order.length) { + if (order [index] == phdn.iItem) break; + index++; + } + if (index == order.length) index = 0; + if (index == pitem.iOrder) break; + int start = Math.min (index, pitem.iOrder); + int end = Math.max (index, pitem.iOrder); + ignoreColumnMove = false; + for (int i=start; i<=end; i++) { + TableColumn column = columns [order [i]]; + if (!column.isDisposed ()) { + column.postEvent (SWT.Move); + } + } + } + } + break; + } + case OS.HDN_ITEMCHANGEDW: + case OS.HDN_ITEMCHANGEDA: { + /* + * Bug in Windows. When a table has the LVS_EX_GRIDLINES extended + * style and the user drags any column over the first column in the + * table, making the size become zero, when the user drags a column + * such that the size of the first column becomes non-zero, the grid + * lines are not redrawn. The fix is to detect the case and force + * a redraw of the first column. + */ + int width = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0); + if (lastWidth == 0 && width > 0) { + int bits = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if ((bits & OS.LVS_EX_GRIDLINES) != 0) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + rect.right = rect.left + width; + OS.InvalidateRect (handle, rect, true); + } + } + lastWidth = width; + if (!ignoreColumnResize) { + NMHEADER phdn = new NMHEADER (); + OS.MoveMemory (phdn, lParam, NMHEADER.sizeof); + if (phdn.pitem != 0) { + HDITEM pitem = new HDITEM (); + OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof); + if ((pitem.mask & OS.HDI_WIDTH) != 0) { + TableColumn column = columns [phdn.iItem]; + if (column != null) { + column.updateToolTip (phdn.iItem); + column.sendEvent (SWT.Resize); + if (isDisposed ()) return LRESULT.ZERO; + /* + * It is possible (but unlikely), that application + * code could have disposed the column in the move + * event. If this happens, process the move event + * for those columns that have not been destroyed. + */ + TableColumn [] newColumns = new TableColumn [columnCount]; + System.arraycopy (columns, 0, newColumns, 0, columnCount); + int [] order = new int [columnCount]; + OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order); + boolean moved = false; + for (int i=0; i<columnCount; i++) { + TableColumn nextColumn = newColumns [order [i]]; + if (moved && !nextColumn.isDisposed ()) { + nextColumn.updateToolTip (order [i]); + nextColumn.sendEvent (SWT.Move); + } + if (nextColumn == column) moved = true; + } + } + } + } + } + break; + } + case OS.HDN_ITEMDBLCLICKW: + case OS.HDN_ITEMDBLCLICKA: { + NMHEADER phdn = new NMHEADER (); + OS.MoveMemory (phdn, lParam, NMHEADER.sizeof); + TableColumn column = columns [phdn.iItem]; + if (column != null) { + column.postEvent (SWT.DefaultSelection); + } + break; + } + } + return null; +} + +LRESULT wmNotifyToolTip (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + if (OS.IsWinCE) return null; + switch (hdr.code) { + case OS.NM_CUSTOMDRAW: { + if (toolTipText != null) break; + if (isCustomToolTip ()) { + NMTTCUSTOMDRAW nmcd = new NMTTCUSTOMDRAW (); + OS.MoveMemory (nmcd, lParam, NMTTCUSTOMDRAW.sizeof); + return wmNotifyToolTip (nmcd, lParam); + } + break; + } + case OS.TTN_GETDISPINFOA: + case OS.TTN_GETDISPINFOW: + case OS.TTN_SHOW: { + LRESULT result = super.wmNotify (hdr, wParam, lParam); + if (result != null) return result; + if (hdr.code != OS.TTN_SHOW) tipRequested = true; + int /*long*/ code = callWindowProc (handle, OS.WM_NOTIFY, wParam, lParam); + if (hdr.code != OS.TTN_SHOW) tipRequested = false; + if (toolTipText != null) break; + if (isCustomToolTip ()) { + LVHITTESTINFO pinfo = new LVHITTESTINFO (); + int pos = OS.GetMessagePos (); + POINT pt = new POINT(); + OS.POINTSTOPOINT (pt, pos); + OS.ScreenToClient (handle, pt); + pinfo.x = pt.x; + pinfo.y = pt.y; + /* + * Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest + * a point that is above the table, instead of returning -1 to + * indicate that the hittest failed, a negative index is returned. + * The fix is to consider any value that is negative a failure. + */ + if (OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo) >= 0) { + TableItem item = _getItem (pinfo.iItem); + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + int /*long*/ hFont = item.fontHandle (pinfo.iSubItem); + if (hFont != -1) hFont = OS.SelectObject (hDC, hFont); + Event event = sendMeasureItemEvent (item, pinfo.iItem, pinfo.iSubItem, hDC); + if (!isDisposed () && !item.isDisposed ()) { + RECT itemRect = new RECT (); + OS.SetRect (itemRect, event.x, event.y, event.x + event.width, event.y + event.height); + if (hdr.code == OS.TTN_SHOW) { + RECT toolRect = toolTipRect (itemRect); + OS.MapWindowPoints (handle, 0, toolRect, 2); + int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0); + int flags = OS.SWP_NOACTIVATE | OS.SWP_NOZORDER; + int width = toolRect.right - toolRect.left, height = toolRect.bottom - toolRect.top; + SetWindowPos (hwndToolTip, 0, toolRect.left , toolRect.top, width, height, flags); + } else { + NMTTDISPINFO lpnmtdi = null; + if (hdr.code == OS.TTN_GETDISPINFOA) { + lpnmtdi = new NMTTDISPINFOA (); + OS.MoveMemory ((NMTTDISPINFOA)lpnmtdi, lParam, NMTTDISPINFOA.sizeof); + if (lpnmtdi.lpszText != 0) { + OS.MoveMemory (lpnmtdi.lpszText, new byte [1], 1); + OS.MoveMemory (lParam, (NMTTDISPINFOA)lpnmtdi, NMTTDISPINFOA.sizeof); + } + } else { + lpnmtdi = new NMTTDISPINFOW (); + OS.MoveMemory ((NMTTDISPINFOW)lpnmtdi, lParam, NMTTDISPINFOW.sizeof); + if (lpnmtdi.lpszText != 0) { + OS.MoveMemory (lpnmtdi.lpszText, new char [1], 2); + OS.MoveMemory (lParam, (NMTTDISPINFOW)lpnmtdi, NMTTDISPINFOW.sizeof); + } + } + RECT cellRect = item.getBounds (pinfo.iItem, pinfo.iSubItem, true, true, true, true, hDC); + if (itemRect.right > cellRect.right) { + //TEMPORARY CODE + String string = " "; +// String string = null; +// if (pinfo.iSubItem == 0) { +// string = item.text; +// } else { +// String [] strings = item.strings; +// if (strings != null) string = strings [pinfo.iSubItem]; +// } + if (string != null) { + Shell shell = getShell (); + char [] chars = new char [string.length () + 1]; + string.getChars (0, string.length (), chars, 0); + if (hdr.code == OS.TTN_GETDISPINFOA) { + byte [] bytes = new byte [chars.length * 2]; + OS.WideCharToMultiByte (getCodePage (), 0, chars, chars.length, bytes, bytes.length, null, null); + shell.setToolTipText (lpnmtdi, bytes); + OS.MoveMemory (lParam, (NMTTDISPINFOA)lpnmtdi, NMTTDISPINFOA.sizeof); + } else { + shell.setToolTipText (lpnmtdi, chars); + OS.MoveMemory (lParam, (NMTTDISPINFOW)lpnmtdi, NMTTDISPINFOW.sizeof); + } + } + } + } + } + if (hFont != -1) hFont = OS.SelectObject (hDC, hFont); + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + } + } + return new LRESULT (code); + } + } + return null; +} + +LRESULT wmNotifyToolTip (NMTTCUSTOMDRAW nmcd, int /*long*/ lParam) { + if (OS.IsWinCE) return null; + switch (nmcd.dwDrawStage) { + case OS.CDDS_PREPAINT: { + if (isCustomToolTip ()) { + //TEMPORARY CODE +// nmcd.uDrawFlags |= OS.DT_CALCRECT; +// OS.MoveMemory (lParam, nmcd, NMTTCUSTOMDRAW.sizeof); + return new LRESULT (OS.CDRF_NOTIFYPOSTPAINT | OS.CDRF_NEWFONT); + } + break; + } + case OS.CDDS_POSTPAINT: { + LVHITTESTINFO pinfo = new LVHITTESTINFO (); + int pos = OS.GetMessagePos (); + POINT pt = new POINT(); + OS.POINTSTOPOINT (pt, pos); + OS.ScreenToClient (handle, pt); + pinfo.x = pt.x; + pinfo.y = pt.y; + /* + * Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest + * a point that is above the table, instead of returning -1 to + * indicate that the hittest failed, a negative index is returned. + * The fix is to consider any value that is negative a failure. + */ + if (OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo) >= 0) { + TableItem item = _getItem (pinfo.iItem); + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ hFont = item.fontHandle (pinfo.iSubItem); + if (hFont == -1) hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + int /*long*/ oldFont = OS.SelectObject (hDC, hFont); + boolean drawForeground = true; + RECT cellRect = item.getBounds (pinfo.iItem, pinfo.iSubItem, true, true, false, false, hDC); + if (hooks (SWT.EraseItem)) { + Event event = sendEraseItemEvent (item, nmcd, pinfo.iSubItem, cellRect); + if (isDisposed () || item.isDisposed ()) break; + if (event.doit) { + drawForeground = (event.detail & SWT.FOREGROUND) != 0; + } else { + drawForeground = false; + } + } + if (drawForeground) { + int nSavedDC = OS.SaveDC (nmcd.hdc); + int gridWidth = getLinesVisible () ? Table.GRID_WIDTH : 0; + RECT insetRect = toolTipInset (cellRect); + OS.SetWindowOrgEx (nmcd.hdc, insetRect.left, insetRect.top, null); + GCData data = new GCData (); + data.device = display; + data.foreground = OS.GetTextColor (nmcd.hdc); + data.background = OS.GetBkColor (nmcd.hdc); + data.font = Font.win32_new (display, hFont); + GC gc = GC.win32_new (nmcd.hdc, data); + int x = cellRect.left; + if (pinfo.iSubItem != 0) x -= gridWidth; + Image image = item.getImage (pinfo.iSubItem); + if (image != null) { + Rectangle rect = image.getBounds (); + RECT imageRect = item.getBounds (pinfo.iItem, pinfo.iSubItem, false, true, false, false, hDC); + Point size = imageList == null ? new Point (rect.width, rect.height) : imageList.getImageSize (); + int y = imageRect.top; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + y = y + Math.max (0, (imageRect.bottom - imageRect.top - size.y) / 2); + } + gc.drawImage (image, rect.x, rect.y, rect.width, rect.height, x, y, size.x, size.y); + x += size.x + INSET + (pinfo.iSubItem == 0 ? -2 : 4); + } else { + x += INSET + 2; + } + String string = item.getText (pinfo.iSubItem); + if (string != null) { + int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_VCENTER; + TableColumn column = columns != null ? columns [pinfo.iSubItem] : null; + if (column != null) { + if ((column.style & SWT.CENTER) != 0) flags |= OS.DT_CENTER; + if ((column.style & SWT.RIGHT) != 0) flags |= OS.DT_RIGHT; + } + TCHAR buffer = new TCHAR (getCodePage (), string, false); + RECT textRect = new RECT (); + OS.SetRect (textRect, x, cellRect.top, cellRect.right, cellRect.bottom); + OS.DrawText (nmcd.hdc, buffer, buffer.length (), textRect, flags); + } + gc.dispose (); + OS.RestoreDC (nmcd.hdc, nSavedDC); + } + if (hooks (SWT.PaintItem)) { + RECT itemRect = item.getBounds (pinfo.iItem, pinfo.iSubItem, true, true, false, false, hDC); + sendPaintItemEvent (item, nmcd, pinfo.iSubItem, itemRect); + } + OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + } + } + } + return null; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TableColumn.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TableColumn.java new file mode 100755 index 0000000000..be36d9bf7c --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TableColumn.java @@ -0,0 +1,893 @@ +/******************************************************************************* + * 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.win32.*; +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; + boolean resizable, moveable; + String toolTipText; + int id; + +/** + * 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)); + resizable = true; + this.parent = parent; + parent.createItem (this, parent.getColumnCount ()); +} + +/** + * 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)); + resizable = true; + 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 destroyWidget () { + parent.destroyItem (this); + releaseHandle (); +} + +/** + * 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 moveable; +} + +/** + * 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 resizable; +} + +/** + * 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 index = parent.indexOf (this); + if (index == -1) return 0; + int /*long*/ hwnd = parent.handle; + return (int)/*64*/OS.SendMessage (hwnd, OS.LVM_GETCOLUMNWIDTH, index, 0); +} + +/** + * 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 index = parent.indexOf (this); + if (index == -1) return; + int /*long*/ hwnd = parent.handle; + int oldWidth = (int)/*64*/OS.SendMessage (hwnd, OS.LVM_GETCOLUMNWIDTH, index, 0); + TCHAR buffer = new TCHAR (parent.getCodePage (), text, true); + int headerWidth = (int)/*64*/OS.SendMessage (hwnd, OS.LVM_GETSTRINGWIDTH, 0, buffer) + Table.HEADER_MARGIN; + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) headerWidth += Table.HEADER_EXTRA; + boolean hasHeaderImage = false; + if (image != null || parent.sortColumn == this) { + hasHeaderImage = true; + Image headerImage = null; + if (parent.sortColumn == this && parent.sortDirection != SWT.NONE) { + if (OS.COMCTL32_MAJOR < 6) { + headerImage = display.getSortImage (parent.sortDirection); + } else { + headerWidth += Table.SORT_WIDTH; + } + } else { + headerImage = image; + } + if (headerImage != null) { + Rectangle bounds = headerImage.getBounds (); + headerWidth += bounds.width; + } + int margin = 0; + if (OS.COMCTL32_VERSION >= OS.VERSION (5, 80)) { + int /*long*/ hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0); + margin = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETBITMAPMARGIN, 0, 0); + } else { + margin = OS.GetSystemMetrics (OS.SM_CXEDGE) * 3; + } + headerWidth += margin * 4; + } + parent.ignoreColumnResize = true; + int columnWidth = 0; + if (parent.hooks (SWT.MeasureItem)) { + RECT headerRect = new RECT (); + int /*long*/ hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect); + OS.MapWindowPoints (hwndHeader, hwnd, headerRect, 2); + int /*long*/ hDC = OS.GetDC (hwnd); + int /*long*/ oldFont = 0, newFont = OS.SendMessage (hwnd, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + int count = (int)/*64*/OS.SendMessage (hwnd, OS.LVM_GETITEMCOUNT, 0, 0); + for (int i=0; i<count; i++) { + TableItem item = parent.items [i]; + if (item != null) { + int /*long*/ hFont = item.fontHandle (index); + if (hFont != -1) hFont = OS.SelectObject (hDC, hFont); + Event event = parent.sendMeasureItemEvent (item, i, index, hDC); + if (hFont != -1) hFont = OS.SelectObject (hDC, hFont); + if (isDisposed () || parent.isDisposed ()) break; + columnWidth = Math.max (columnWidth, event.x + event.width - headerRect.left); + } + } + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (hwnd, hDC); + OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, columnWidth); + } else { + OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, OS.LVSCW_AUTOSIZE); + columnWidth = (int)/*64*/OS.SendMessage (hwnd, OS.LVM_GETCOLUMNWIDTH, index, 0); + if (index == 0) { + /* + * Bug in Windows. When LVM_SETCOLUMNWIDTH is used with LVSCW_AUTOSIZE + * where each item has I_IMAGECALLBACK but there are no images in the + * table, the size computed by LVM_SETCOLUMNWIDTH is too small for the + * first column, causing long items to be clipped with '...'. The fix + * is to increase the column width by a small amount. + */ + if (parent.imageList == null) columnWidth += 2; + /* + * Bug in Windows. When the first column of a table does not + * have an image and the user double clicks on the divider, + * Windows packs the column but does not take into account + * the empty space left for the image. The fix is to increase + * the column width by the width of the image list. + * + * NOTE: This bug does not happen on Vista. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION < OS.VERSION (6, 0)) { + if (!parent.firstColumnImage) { + int /*long*/ hImageList = OS.SendMessage (hwnd, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0); + if (hImageList != 0) { + int [] cx = new int [1], cy = new int [1]; + OS.ImageList_GetIconSize (hImageList, cx, cy); + columnWidth += cx [0]; + } + } + } + /* + * Bug in Windows. When LVM_SETCOLUMNWIDTH is used with LVSCW_AUTOSIZE + * for a table with a state image list, the column is width does not + * include space for the state icon. The fix is to increase the column + * width by the width of the image list. + */ + if ((parent.style & SWT.CHECK) != 0) { + int /*long*/ hStateList = OS.SendMessage (hwnd, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0); + if (hStateList != 0) { + int [] cx = new int [1], cy = new int [1]; + OS.ImageList_GetIconSize (hStateList, cx, cy); + columnWidth += cx [0]; + } + } + } + } + if (headerWidth > columnWidth) { + if (!hasHeaderImage) { + /* + * Feature in Windows. When LVSCW_AUTOSIZE_USEHEADER is used + * with LVM_SETCOLUMNWIDTH to resize the last column, the last + * column is expanded to fill the client area. The fix is to + * resize the table to be small, set the column width and then + * restore the table to its original size. + */ + RECT rect = null; + boolean fixWidth = index == parent.getColumnCount () - 1; + if (fixWidth) { + rect = new RECT (); + OS.GetWindowRect (hwnd, rect); + OS.UpdateWindow (hwnd); + int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOREDRAW | OS.SWP_NOZORDER; + SetWindowPos (hwnd, 0, 0, 0, 0, rect.bottom - rect.top, flags); + } + OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, OS.LVSCW_AUTOSIZE_USEHEADER); + if (fixWidth) { + int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOZORDER; + SetWindowPos (hwnd, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top, flags); + } + } else { + OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, headerWidth); + } + } else { + if (index == 0) { + OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, columnWidth); + } + } + parent.ignoreColumnResize = false; + int newWidth = (int)/*64*/OS.SendMessage (hwnd, OS.LVM_GETCOLUMNWIDTH, index, 0); + if (oldWidth != newWidth) { + updateToolTip (index); + sendEvent (SWT.Resize); + if (isDisposed ()) return; + boolean moved = false; + int [] order = parent.getColumnOrder (); + TableColumn [] columns = parent.getColumns (); + for (int i=0; i<order.length; i++) { + TableColumn column = columns [order [i]]; + if (moved && !column.isDisposed ()) { + column.updateToolTip (order [i]); + column.sendEvent (SWT.Move); + } + if (column == this) moved = true; + } + } +} + +void releaseHandle () { + super.releaseHandle (); + parent = null; +} + +void releaseParent () { + super.releaseParent (); + 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); + int /*long*/ hwnd = parent.handle; + LVCOLUMN lvColumn = new LVCOLUMN (); + lvColumn.mask = OS.LVCF_FMT; + OS.SendMessage (hwnd, OS.LVM_GETCOLUMN, index, lvColumn); + lvColumn.fmt &= ~OS.LVCFMT_JUSTIFYMASK; + int fmt = 0; + if ((style & SWT.LEFT) == SWT.LEFT) fmt = OS.LVCFMT_LEFT; + if ((style & SWT.CENTER) == SWT.CENTER) fmt = OS.LVCFMT_CENTER; + if ((style & SWT.RIGHT) == SWT.RIGHT) fmt = OS.LVCFMT_RIGHT; + lvColumn.fmt |= fmt; + OS.SendMessage (hwnd, OS.LVM_SETCOLUMN, index, lvColumn); + /* + * Bug in Windows. When LVM_SETCOLUMN is used to change + * the alignment of a column, the column is not redrawn + * to show the new alignment. The fix is to compute the + * visible rectangle for the column and redraw it. + */ + if (index != 0) { + parent.forceResize (); + RECT rect = new RECT (), headerRect = new RECT (); + OS.GetClientRect (hwnd, rect); + int /*long*/ hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect); + OS.MapWindowPoints (hwndHeader, hwnd, headerRect, 2); + rect.left = headerRect.left; + rect.right = headerRect.right; + OS.InvalidateRect (hwnd, rect, true); + } +} + +public void setImage (Image image) { + checkWidget(); + if (image != null && image.isDisposed ()) { + error (SWT.ERROR_INVALID_ARGUMENT); + } + super.setImage (image); + if (parent.sortColumn != this || parent.sortDirection != SWT.NONE) { + setImage (image, false, false); + } +} + +void setImage (Image image, boolean sort, boolean right) { + int index = parent.indexOf (this); + if (index == -1) return; + int /*long*/ hwnd = parent.handle; + if (OS.COMCTL32_MAJOR < 6) { + int /*long*/ hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0); + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_FORMAT | OS.HDI_IMAGE | OS.HDI_BITMAP; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem); + hdItem.fmt &= ~OS.HDF_BITMAP_ON_RIGHT; + if (image != null) { + if (sort) { + hdItem.mask &= ~OS.HDI_IMAGE; + hdItem.fmt &= ~OS.HDF_IMAGE; + hdItem.fmt |= OS.HDF_BITMAP; + hdItem.hbm = image.handle; + } else { + hdItem.mask &= ~OS.HDI_BITMAP; + hdItem.fmt &= ~OS.HDF_BITMAP; + hdItem.fmt |= OS.HDF_IMAGE; + hdItem.iImage = parent.imageIndexHeader (image); + } + if (right) hdItem.fmt |= OS.HDF_BITMAP_ON_RIGHT; + } else { + hdItem.fmt &= ~(OS.HDF_IMAGE | OS.HDF_BITMAP); + } + OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem); + } else { + LVCOLUMN lvColumn = new LVCOLUMN (); + lvColumn.mask = OS.LVCF_FMT | OS.LVCF_IMAGE; + OS.SendMessage (hwnd, OS.LVM_GETCOLUMN, index, lvColumn); + if (image != null) { + lvColumn.fmt |= OS.LVCFMT_IMAGE; + lvColumn.iImage = parent.imageIndexHeader (image); + if (right) lvColumn.fmt |= OS.LVCFMT_BITMAP_ON_RIGHT; + } else { + lvColumn.mask &= ~OS.LVCF_IMAGE; + lvColumn.fmt &= ~(OS.LVCFMT_IMAGE | OS.LVCFMT_BITMAP_ON_RIGHT); + } + OS.SendMessage (hwnd, OS.LVM_SETCOLUMN, index, lvColumn); + } +} + +/** + * 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.moveable = moveable; + parent.updateMoveable (); +} + +/** + * 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 (); + this.resizable = resizable; +} + +void setSortDirection (int direction) { + if (OS.COMCTL32_MAJOR >= 6) { + int index = parent.indexOf (this); + if (index == -1) return; + int /*long*/ hwnd = parent.handle; + int /*long*/ hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0); + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_FORMAT | OS.HDI_IMAGE; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem); + switch (direction) { + case SWT.UP: + hdItem.fmt &= ~(OS.HDF_IMAGE | OS.HDF_SORTDOWN); + hdItem.fmt |= OS.HDF_SORTUP; + if (image == null) hdItem.mask &= ~OS.HDI_IMAGE; + break; + case SWT.DOWN: + hdItem.fmt &= ~(OS.HDF_IMAGE | OS.HDF_SORTUP); + hdItem.fmt |= OS.HDF_SORTDOWN; + if (image == null) hdItem.mask &= ~OS.HDI_IMAGE; + break; + case SWT.NONE: + hdItem.fmt &= ~(OS.HDF_SORTUP | OS.HDF_SORTDOWN); + if (image != null) { + hdItem.fmt |= OS.HDF_IMAGE; + hdItem.iImage = parent.imageIndexHeader (image); + } else { + hdItem.fmt &= ~OS.HDF_IMAGE; + hdItem.mask &= ~OS.HDI_IMAGE; + } + break; + } + OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem); + /* + * Bug in Windows. When LVM_SETSELECTEDCOLUMN is used to + * specify a selected column, Windows does not redraw either + * the new or the previous selected column. The fix is to + * force a redraw of both. + * + * Feature in Windows. When LVM_SETBKCOLOR is used with + * CLR_NONE and LVM_SETSELECTEDCOLUMN is used to select + * a column, Windows fills the column with the selection + * color, drawing on top of the background image and any + * other custom drawing. The fix is to avoid setting the + * selected column. + */ + parent.forceResize (); + RECT rect = new RECT (); + OS.GetClientRect (hwnd, rect); + if ((int)/*64*/OS.SendMessage (hwnd, OS.LVM_GETBKCOLOR, 0, 0) != OS.CLR_NONE) { + int oldColumn = (int)/*64*/OS.SendMessage (hwnd, OS.LVM_GETSELECTEDCOLUMN, 0, 0); + int newColumn = direction == SWT.NONE ? -1 : index; + OS.SendMessage (hwnd, OS.LVM_SETSELECTEDCOLUMN, newColumn, 0); + RECT headerRect = new RECT (); + if (oldColumn != -1) { + if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, oldColumn, headerRect) != 0) { + OS.MapWindowPoints (hwndHeader, hwnd, headerRect, 2); + rect.left = headerRect.left; + rect.right = headerRect.right; + OS.InvalidateRect (hwnd, rect, true); + } + } + } + RECT headerRect = new RECT (); + if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect) != 0) { + OS.MapWindowPoints (hwndHeader, hwnd, headerRect, 2); + rect.left = headerRect.left; + rect.right = headerRect.right; + OS.InvalidateRect (hwnd, rect, true); + } + } else { + switch (direction) { + case SWT.UP: + case SWT.DOWN: + setImage (display.getSortImage (direction), true, true); + break; + case SWT.NONE: + setImage (image, false, false); + break; + } + } +} + +public void setText (String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if (string.equals (text)) return; + int index = parent.indexOf (this); + if (index == -1) return; + super.setText (string); + + /* + * Bug in Windows. For some reason, when the title + * of a column is changed after the column has been + * created, the alignment must also be reset or the + * text does not draw. The fix is to query and then + * set the alignment. + */ + int /*long*/ hwnd = parent.handle; + LVCOLUMN lvColumn = new LVCOLUMN (); + lvColumn.mask = OS.LVCF_FMT; + OS.SendMessage (hwnd, OS.LVM_GETCOLUMN, index, lvColumn); + + /* + * Bug in Windows. When a column header contains a + * mnemonic character, Windows does not measure the + * text properly. This causes '...' to always appear + * at the end of the text. The fix is to remove + * mnemonic characters and replace doubled mnemonics + * with spaces. + */ + int /*long*/ hHeap = OS.GetProcessHeap (); + TCHAR buffer = new TCHAR (parent.getCodePage (), fixMnemonic (string, true), true); + int byteCount = buffer.length () * TCHAR.sizeof; + int /*long*/ pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (pszText, buffer, byteCount); + lvColumn.mask |= OS.LVCF_TEXT; + lvColumn.pszText = pszText; + int /*long*/ result = OS.SendMessage (hwnd, OS.LVM_SETCOLUMN, index, lvColumn); + if (pszText != 0) OS.HeapFree (hHeap, 0, pszText); + if (result == 0) error (SWT.ERROR_CANNOT_SET_TEXT); +} + +/** + * 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; + int /*long*/ hwndHeaderToolTip = parent.headerToolTipHandle; + if (hwndHeaderToolTip == 0) { + parent.createHeaderToolTips (); + parent.updateHeaderToolTips (); + } +} + +/** + * 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; + int index = parent.indexOf (this); + if (index == -1) return; + int /*long*/ hwnd = parent.handle; + if (width != (int)/*64*/OS.SendMessage (hwnd, OS.LVM_GETCOLUMNWIDTH, index, 0)) { + OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, width); + } +} + +void updateToolTip (int index) { + int /*long*/ hwndHeaderToolTip = parent.headerToolTipHandle; + if (hwndHeaderToolTip != 0) { + int /*long*/ hwnd = parent.handle; + int /*long*/ hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0); + RECT rect = new RECT (); + if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, rect) != 0) { + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + lpti.hwnd = hwndHeader; + lpti.uId = id; + lpti.left = rect.left; + lpti.top = rect.top; + lpti.right = rect.right; + lpti.bottom = rect.bottom; + OS.SendMessage (hwndHeaderToolTip, OS.TTM_NEWTOOLRECT, 0, lpti); + } + } +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TableItem.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TableItem.java new file mode 100755 index 0000000000..0b497d4597 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TableItem.java @@ -0,0 +1,1235 @@ +/******************************************************************************* + * 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.win32.*; +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; + Font font; + Font [] cellFont; + boolean checked, grayed, cached; + int imageIndent, background = -1, foreground = -1; + int [] cellBackground, cellForeground; + +/** + * 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; +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +void clear () { + text = ""; + image = null; + strings = null; + images = null; + imageIndent = 0; + checked = grayed = false; + font = null; + background = foreground = -1; + cellFont = null; + cellBackground = cellForeground = null; + if ((parent.style & SWT.VIRTUAL) != 0) cached = false; +} + +void destroyWidget () { + parent.destroyItem (this); + releaseHandle (); +} + +int /*long*/ fontHandle (int index) { + if (cellFont != null && cellFont [index] != null) return cellFont [index].handle; + if (font != null) return font.handle; + return -1; +} + +/** + * 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + if (background == -1) return parent.getBackground (); + return Color.win32_new (display, background); +} + +/** + * 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + int count = Math.max (1, parent.getColumnCount ()); + if (0 > index || index > count - 1) return getBackground (); + int pixel = cellBackground != null ? cellBackground [index] : -1; + return pixel == -1 ? getBackground () : Color.win32_new (display, pixel); +} + +/** + * 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + int itemIndex = parent.indexOf (this); + if (itemIndex == -1) return new Rectangle (0, 0, 0, 0); + RECT rect = getBounds (itemIndex, 0, true, false, false); + int width = rect.right - rect.left, height = rect.bottom - rect.top; + return new Rectangle (rect.left, rect.top, width, 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + int itemIndex = parent.indexOf (this); + if (itemIndex == -1) return new Rectangle (0, 0, 0, 0); + RECT rect = getBounds (itemIndex, index, true, true, true); + int width = rect.right - rect.left, height = rect.bottom - rect.top; + return new Rectangle (rect.left, rect.top, width, height); +} + +RECT getBounds (int row, int column, boolean getText, boolean getImage, boolean fullText) { + return getBounds (row, column, getText, getImage, fullText, false, 0); +} + +RECT getBounds (int row, int column, boolean getText, boolean getImage, boolean fullText, boolean fullImage, int /*long*/ hDC) { + if (!getText && !getImage) return new RECT (); + int columnCount = parent.getColumnCount (); + if (!(0 <= column && column < Math.max (1, columnCount))) { + return new RECT (); + } + if (parent.fixScrollWidth) parent.setScrollWidth (null, true); + RECT rect = new RECT (); + int /*long*/ hwnd = parent.handle; + int bits = (int)/*64*/OS.SendMessage (hwnd, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + if (column == 0 && (bits & OS.LVS_EX_FULLROWSELECT) == 0) { + if (parent.explorerTheme) { + rect.left = OS.LVIR_ICON; + parent.ignoreCustomDraw = true; + int /*long*/ code = OS.SendMessage (hwnd, OS. LVM_GETITEMRECT, row, rect); + parent.ignoreCustomDraw = false; + if (code == 0) return new RECT (); + if (getText) { + int width = 0; + int /*long*/ hFont = fontHandle (column); + if (hFont == -1 && hDC == 0) { + TCHAR buffer = new TCHAR (parent.getCodePage (), text, true); + width = (int)/*64*/OS.SendMessage (hwnd, OS.LVM_GETSTRINGWIDTH, 0, buffer); + } else { + TCHAR buffer = new TCHAR (parent.getCodePage (), text, false); + int /*long*/ textDC = hDC != 0 ? hDC : OS.GetDC (hwnd), oldFont = -1; + if (hDC == 0) { + if (hFont == -1) hFont = OS.SendMessage (hwnd, OS.WM_GETFONT, 0, 0); + oldFont = OS.SelectObject (textDC, hFont); + } + RECT textRect = new RECT (); + int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_CALCRECT; + OS.DrawText (textDC, buffer, buffer.length (), textRect, flags); + width = textRect.right - textRect.left; + if (hDC == 0) { + if (oldFont != -1) OS.SelectObject (textDC, oldFont); + OS.ReleaseDC (hwnd, textDC); + } + } + if (!getImage) rect.left = rect.right; + rect.right += width + Table.INSET * 2; + } + } else { + if (getText) { + rect.left = OS.LVIR_SELECTBOUNDS; + parent.ignoreCustomDraw = true; + int /*long*/ code = OS.SendMessage (hwnd, OS.LVM_GETITEMRECT, row, rect); + parent.ignoreCustomDraw = false; + if (code == 0) return new RECT (); + if (!getImage) { + RECT iconRect = new RECT (); + iconRect.left = OS.LVIR_ICON; + parent.ignoreCustomDraw = true; + code = OS.SendMessage (hwnd, OS. LVM_GETITEMRECT, row, iconRect); + parent.ignoreCustomDraw = false; + if (code != 0) rect.left = iconRect.right; + } + } else { + rect.left = OS.LVIR_ICON; + parent.ignoreCustomDraw = true; + int /*long*/ code = OS.SendMessage (hwnd, OS.LVM_GETITEMRECT, row, rect); + parent.ignoreCustomDraw = false; + if (code == 0) return new RECT (); + } + } + if (fullText || fullImage) { + RECT headerRect = new RECT (); + int /*long*/ hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, 0, headerRect); + OS.MapWindowPoints (hwndHeader, hwnd, headerRect, 2); + if (getText && fullText) rect.right = headerRect.right; + if (getImage && fullImage) rect.left = headerRect.left; + } + } else { + /* + * Feature in Windows. LVM_GETSUBITEMRECT returns an image width + * even when the subitem does not contain an image. The fix is to + * test for this case and adjust the rectangle to represent the area + * the table is actually drawing. + */ + boolean hasImage = (column == 0 && image != null) || (images != null && images [column] != null); + rect.top = column; + if (fullText || fullImage || hDC == 0) { + /* + * Bug in Windows. Despite the fact that the documentation states + * that LVIR_BOUNDS and LVIR_LABEL are identical when used with + * LVM_GETSUBITEMRECT, LVIR_BOUNDS can return a zero height. The + * fix is to use LVIR_LABEL. + */ + rect.left = getText ? OS.LVIR_LABEL : OS.LVIR_ICON; + parent.ignoreCustomDraw = true; + int /*long*/ code = OS.SendMessage (hwnd, OS. LVM_GETSUBITEMRECT, row, rect); + parent.ignoreCustomDraw = false; + if (code == 0) return new RECT (); + /* + * Feature in Windows. Calling LVM_GETSUBITEMRECT with LVIR_LABEL + * and zero for the column number gives the bounds of the first item + * without including the bounds of the icon. This is undocumented. + * When called with values greater than zero, the icon bounds are + * included and this behavior is documented. If the icon is needed + * in the bounds of the first item, the fix is to adjust the item + * bounds using the icon bounds. + */ + if (column == 0 && getText && getImage) { + RECT iconRect = new RECT (); + iconRect.left = OS.LVIR_ICON; + parent.ignoreCustomDraw = true; + code = OS.SendMessage (hwnd, OS. LVM_GETSUBITEMRECT, row, iconRect); + parent.ignoreCustomDraw = false; + if (code != 0) rect.left = iconRect.left; + } + if (hasImage) { + if (column != 0 && getText && !getImage) { + RECT iconRect = new RECT (); + iconRect.top = column; + iconRect.left = OS.LVIR_ICON; + if (OS.SendMessage (hwnd, OS. LVM_GETSUBITEMRECT, row, iconRect) != 0) { + rect.left = iconRect.right + Table.INSET / 2; + } + } + } else { + if (getImage && !getText) rect.right = rect.left; + } + if (column == 0 && fullImage) { + RECT headerRect = new RECT (); + int /*long*/ hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, 0, headerRect); + OS.MapWindowPoints (hwndHeader, hwnd, headerRect, 2); + rect.left = headerRect.left; + } + } else { + rect.left = OS.LVIR_ICON; + parent.ignoreCustomDraw = true; + int /*long*/ code = OS.SendMessage (hwnd, OS. LVM_GETSUBITEMRECT, row, rect); + parent.ignoreCustomDraw = false; + if (code == 0) return new RECT (); + if (!hasImage) rect.right = rect.left; + if (getText) { + String string = column == 0 ? text : strings != null ? strings [column] : null; + if (string != null) { + RECT textRect = new RECT (); + TCHAR buffer = new TCHAR (parent.getCodePage (), string, false); + int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_CALCRECT; + OS.DrawText (hDC, buffer, buffer.length (), textRect, flags); + rect.right += textRect.right - textRect.left + Table.INSET * 3 + 1; + } + } + } + } + /* + * Bug in Windows. In version 5.80 of COMCTL32.DLL, the top + * of the rectangle returned by LVM_GETSUBITEMRECT is off by + * the grid width when the grid is visible. The fix is to + * move the top of the rectangle up by the grid width. + */ + int gridWidth = parent.getLinesVisible () ? Table.GRID_WIDTH : 0; + if (OS.COMCTL32_VERSION >= OS.VERSION (5, 80)) rect.top -= gridWidth; + if (column != 0) rect.left += gridWidth; + rect.right = Math.max (rect.right, rect.left); + rect.top += gridWidth; + rect.bottom = Math.max (rect.bottom - gridWidth, rect.top); + return rect; +} + +/** + * 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, true)) 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, true)) 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + int count = Math.max (1, parent.getColumnCount ()); + 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + if (foreground == -1) return parent.getForeground (); + return Color.win32_new (display, foreground); +} + +/** + * + * 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + int count = Math.max (1, parent.getColumnCount ()); + if (0 > index || index > count -1) return getForeground (); + int pixel = cellForeground != null ? cellForeground [index] : -1; + return pixel == -1 ? getForeground () : Color.win32_new (display, pixel); +} + +/** + * 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + if ((parent.style & SWT.CHECK) == 0) return false; + return grayed; +} + +public Image getImage () { + checkWidget(); + if (!parent.checkData (this, true)) 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, true)) 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + int itemIndex = parent.indexOf (this); + if (itemIndex == -1) return new Rectangle (0, 0, 0, 0); + RECT rect = getBounds (itemIndex, index, false, true, false); + int width = rect.right - rect.left, height = rect.bottom - rect.top; + return new Rectangle (rect.left, rect.top, width, 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + return imageIndent; +} + +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, true)) 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, true)) 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + int itemIndex = parent.indexOf (this); + if (itemIndex == -1) return new Rectangle (0, 0, 0, 0); + RECT rect = getBounds (itemIndex, index, true, false, true); + rect.left += 2; + if (index != 0) rect.left += Table.INSET; + rect.left = Math.min (rect.left, rect.right); + rect.right = rect.right - Table.INSET; + int width = Math.max (0, rect.right - rect.left); + int height = Math.max (0, rect.bottom - rect.top); + return new Rectangle (rect.left, rect.top, width, height); +} + +void redraw () { + if (parent.currentItem == this || !parent.getDrawing ()) return; + int /*long*/ hwnd = parent.handle; + if (!OS.IsWindowVisible (hwnd)) return; + int index = parent.indexOf (this); + if (index == -1) return; + OS.SendMessage (hwnd, OS.LVM_REDRAWITEMS, index, index); +} + +void redraw (int column, boolean drawText, boolean drawImage) { + if (parent.currentItem == this || !parent.getDrawing ()) return; + int /*long*/ hwnd = parent.handle; + if (!OS.IsWindowVisible (hwnd)) return; + int index = parent.indexOf (this); + if (index == -1) return; + RECT rect = getBounds (index, column, drawText, drawImage, true); + OS.InvalidateRect (hwnd, rect, true); +} + +void releaseHandle () { + super.releaseHandle (); + parent = null; +} + +void releaseWidget () { + super.releaseWidget (); + strings = null; + images = null; + cellFont = null; + cellBackground = cellForeground = 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); + } + int pixel = -1; + if (color != null) { + parent.setCustomDraw (true); + pixel = color.handle; + } + if (background == pixel) return; + background = pixel; + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + redraw (); +} + +/** + * 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.getColumnCount ()); + if (0 > index || index > count - 1) return; + int pixel = -1; + if (color != null) { + parent.setCustomDraw (true); + pixel = color.handle; + } + if (cellBackground == null) { + cellBackground = new int [count]; + for (int i = 0; i < count; i++) { + cellBackground [i] = -1; + } + } + if (cellBackground [index] == pixel) return; + cellBackground [index] = pixel; + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + redraw (index, true, true); +} + +/** + * 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; + setChecked (checked, false); +} + +void setChecked (boolean checked, boolean notify) { + this.checked = checked; + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + if (notify) { + Event event = new Event(); + event.item = this; + event.detail = SWT.CHECK; + parent.postEvent (SWT.Selection, event); + } + redraw (); +} + +/** + * 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; + if (font != null) parent.setCustomDraw (true); + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + /* + * Bug in Windows. Despite the fact that every item in the + * table always has LPSTR_TEXTCALLBACK, Windows caches the + * bounds for the selected items. This means that + * when you change the string to be something else, Windows + * correctly asks you for the new string but when the item + * is selected, the selection draws using the bounds of the + * previous item. The fix is to reset LPSTR_TEXTCALLBACK + * even though it has not changed, causing Windows to flush + * cached bounds. + */ + if ((parent.style & SWT.VIRTUAL) == 0 && cached) { + int itemIndex = parent.indexOf (this); + if (itemIndex != -1) { + int /*long*/ hwnd = parent.handle; + LVITEM lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_TEXT; + lvItem.iItem = itemIndex; + lvItem.pszText = OS.LPSTR_TEXTCALLBACK; + OS.SendMessage (hwnd, OS.LVM_SETITEM, 0, lvItem); + cached = false; + } + } + parent.setScrollWidth (this, false); + redraw (); +} + +/** + * 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.getColumnCount ()); + 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; + if (font != null) parent.setCustomDraw (true); + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + if (index == 0) { + /* + * Bug in Windows. Despite the fact that every item in the + * table always has LPSTR_TEXTCALLBACK, Windows caches the + * bounds for the selected items. This means that + * when you change the string to be something else, Windows + * correctly asks you for the new string but when the item + * is selected, the selection draws using the bounds of the + * previous item. The fix is to reset LPSTR_TEXTCALLBACK + * even though it has not changed, causing Windows to flush + * cached bounds. + */ + if ((parent.style & SWT.VIRTUAL) == 0 && cached) { + int itemIndex = parent.indexOf (this); + if (itemIndex != -1) { + int /*long*/ hwnd = parent.handle; + LVITEM lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_TEXT; + lvItem.iItem = itemIndex; + lvItem.pszText = OS.LPSTR_TEXTCALLBACK; + OS.SendMessage (hwnd, OS.LVM_SETITEM, 0, lvItem); + cached = false; + } + } + parent.setScrollWidth (this, false); + } + redraw (index, true, false); +} + +/** + * 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); + } + int pixel = -1; + if (color != null) { + parent.setCustomDraw (true); + pixel = color.handle; + } + if (foreground == pixel) return; + foreground = pixel; + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + redraw (); +} + +/** + * 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.getColumnCount ()); + if (0 > index || index > count - 1) return; + int pixel = -1; + if (color != null) { + parent.setCustomDraw (true); + pixel = color.handle; + } + if (cellForeground == null) { + cellForeground = new int [count]; + for (int i = 0; i < count; i++) { + cellForeground [i] = -1; + } + } + if (cellForeground [index] == pixel) return; + cellForeground [index] = pixel; + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + redraw (index, true, false); +} + +/** + * 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; + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + redraw (); +} + +/** + * 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); + } + Image oldImage = null; + if (index == 0) { + if (image != null && image.type == SWT.ICON) { + if (image.equals (this.image)) return; + } + oldImage = this.image; + super.setImage (image); + } + int count = Math.max (1, parent.getColumnCount ()); + if (0 > index || index > count - 1) return; + if (images == null && index != 0) { + images = new Image [count]; + images [0] = image; + } + if (images != null) { + if (image != null && image.type == SWT.ICON) { + if (image.equals (images [index])) return; + } + oldImage = images [index]; + images [index] = image; + } + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + + /* Ensure that the image list is created */ + parent.imageIndex (image, index); + + if (index == 0) parent.setScrollWidth (this, false); + boolean drawText = (image == null && oldImage != null) || (image != null && oldImage == null); + redraw (index, drawText, true); +} + +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; + if (imageIndent == indent) return; + imageIndent = indent; + if ((parent.style & SWT.VIRTUAL) != 0) { + cached = true; + } else { + int index = parent.indexOf (this); + if (index != -1) { + int /*long*/ hwnd = parent.handle; + LVITEM lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_INDENT; + lvItem.iItem = index; + lvItem.iIndent = indent; + OS.SendMessage (hwnd, OS.LVM_SETITEM, 0, lvItem); + } + } + parent.setScrollWidth (this, false); + redraw (); +} + +/** + * 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; + super.setText (string); + } + int count = Math.max (1, parent.getColumnCount ()); + if (0 > index || index > count - 1) return; + if (strings == null && index != 0) { + strings = new String [count]; + strings [0] = text; + } + if (strings != null) { + if (string.equals (strings [index])) return; + strings [index] = string; + } + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + if (index == 0) { + /* + * Bug in Windows. Despite the fact that every item in the + * table always has LPSTR_TEXTCALLBACK, Windows caches the + * bounds for the selected items. This means that + * when you change the string to be something else, Windows + * correctly asks you for the new string but when the item + * is selected, the selection draws using the bounds of the + * previous item. The fix is to reset LPSTR_TEXTCALLBACK + * even though it has not changed, causing Windows to flush + * cached bounds. + */ + if ((parent.style & SWT.VIRTUAL) == 0 && cached) { + int itemIndex = parent.indexOf (this); + if (itemIndex != -1) { + int /*long*/ hwnd = parent.handle; + LVITEM lvItem = new LVITEM (); + lvItem.mask = OS.LVIF_TEXT; + lvItem.iItem = itemIndex; + lvItem.pszText = OS.LPSTR_TEXTCALLBACK; + OS.SendMessage (hwnd, OS.LVM_SETITEM, 0, lvItem); + cached = false; + } + } + parent.setScrollWidth (this, false); + } + redraw (index, true, false); +} + +public void setText (String string) { + checkWidget(); + setText (0, string); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Text.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Text.java new file mode 100755 index 0000000000..0f0befb5d1 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Text.java @@ -0,0 +1,2543 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * 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 tabs, oldStart, oldEnd; + boolean doubleClick, ignoreModify, ignoreVerify, ignoreCharacter; + String message; + + /** + * 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; + + /* + * This code is intentionally commented. + */ +// static final char PASSWORD; + + /* + * These values can be different on different platforms. + * Therefore they are not initialized in the declaration + * to stop the compiler from inlining. + */ + static { + LIMIT = OS.IsWinNT ? 0x7FFFFFFF : 0x7FFF; + DELIMITER = "\r\n"; + } + + static final int /*long*/ EditProc; + static final TCHAR EditClass = new TCHAR (0, "EDIT", true); + static { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, EditClass, lpWndClass); + EditProc = lpWndClass.lpfnWndProc; + /* + * This code is intentionally commented. + */ +// int /*long*/ hwndText = OS.CreateWindowEx (0, +// EditClass, +// null, +// OS.WS_OVERLAPPED | OS.ES_PASSWORD, +// 0, 0, 0, 0, +// 0, +// 0, +// OS.GetModuleHandle (null), +// null); +// char echo = (char) OS.SendMessage (hwndText, OS.EM_GETPASSWORDCHAR, 0, 0); +// OS.DestroyWindow (hwndText); +// PASSWORD = echo != 0 ? echo : '*'; + } + +/** + * 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)); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + boolean redraw = false; + switch (msg) { + case OS.WM_ERASEBKGND: { + if (findImageControl () != null) return 0; + break; + } + case OS.WM_HSCROLL: + case OS.WM_VSCROLL: { + redraw = findImageControl () != null && getDrawing () && OS.IsWindowVisible (handle); + if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + break; + } + case OS.WM_PAINT: { + boolean doubleBuffer = findImageControl () != null; + boolean drawMessage = false; + if ((style & SWT.SINGLE) != 0 && message.length () > 0) { + if (!OS.IsWinCE && OS.WIN32_VERSION < OS.VERSION (6, 0)) { + drawMessage = hwnd != OS.GetFocus () && OS.GetWindowTextLength (handle) == 0; + } + } + if (doubleBuffer || drawMessage) { + int /*long*/ paintDC = 0; + PAINTSTRUCT ps = new PAINTSTRUCT (); + paintDC = OS.BeginPaint (handle, ps); + int width = ps.right - ps.left; + int height = ps.bottom - ps.top; + if (width != 0 && height != 0) { + int /*long*/ hDC = paintDC, hBitmap = 0, hOldBitmap = 0; + POINT lpPoint1 = null, lpPoint2 = null; + if (doubleBuffer) { + hDC = OS.CreateCompatibleDC (paintDC); + lpPoint1 = new POINT (); + lpPoint2 = new POINT (); + OS.SetWindowOrgEx (hDC, ps.left, ps.top, lpPoint1); + OS.SetBrushOrgEx (hDC, ps.left, ps.top, lpPoint2); + hBitmap = OS.CreateCompatibleBitmap (paintDC, width, height); + hOldBitmap = OS.SelectObject (hDC, hBitmap); + RECT rect = new RECT (); + OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom); + drawBackground (hDC, rect); + } + + OS.CallWindowProc (EditProc, hwnd, OS.WM_PAINT, hDC, lParam); + /* + * Bug in XP. Windows does not draw the cue message on XP when + * East Asian language pack is installed. The fix is to draw + * the cue messages ourselves. + * Note: This bug is fixed on Vista. + */ + if (drawMessage) { + RECT rect = new RECT(); + OS.GetClientRect(handle, rect); + int /*long*/ margins = OS.SendMessage (handle, OS.EM_GETMARGINS, 0, 0); + rect.left += OS.LOWORD (margins); + rect.right -= OS.HIWORD (margins); + if ((style & SWT.BORDER) != 0) { + rect.left++; + rect.top++; + rect.right--; + rect.bottom--; + } + TCHAR buffer = new TCHAR (getCodePage (), message, false); + int uFormat = OS.DT_EDITCONTROL; + boolean rtl = (style & SWT.RIGHT_TO_LEFT) != 0; + if (rtl) uFormat |= OS.DT_RTLREADING; + int alignment = style & (SWT.LEFT | SWT.CENTER | SWT.RIGHT); + switch (alignment) { + case SWT.LEFT: uFormat |= (rtl ? OS.DT_RIGHT : OS.DT_LEFT); break; + case SWT.CENTER: uFormat |= OS.DT_CENTER; + case SWT.RIGHT: uFormat |= (rtl ? OS.DT_LEFT : OS.DT_RIGHT); break; + } + int /*long*/ hFont = OS.SendMessage (hwnd, OS.WM_GETFONT, 0, 0); + int /*long*/ hOldFont = OS.SelectObject (hDC, hFont); + OS.SetTextColor (hDC, OS.GetSysColor (OS.COLOR_GRAYTEXT)); + OS.SetBkMode (hDC, OS.TRANSPARENT); + OS.DrawText (hDC, buffer, buffer.length (), rect, uFormat); + OS.SelectObject (hDC, hOldFont); + } + + if (doubleBuffer) { + OS.SetWindowOrgEx (hDC, lpPoint1.x, lpPoint1.y, null); + OS.SetBrushOrgEx (hDC, lpPoint2.x, lpPoint2.y, null); + OS.BitBlt (paintDC, ps.left, ps.top, width, height, hDC, 0, 0, OS.SRCCOPY); + OS.SelectObject (hDC, hOldBitmap); + OS.DeleteObject (hBitmap); + OS.DeleteObject (hDC); + } + } + OS.EndPaint (handle, ps); + return 0; + } + break; + } + } + int /*long*/ code = OS.CallWindowProc (EditProc, hwnd, msg, wParam, lParam); + switch (msg) { + case OS.WM_HSCROLL: + case OS.WM_VSCROLL: { + if (redraw) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.InvalidateRect (handle, null, true); + } + break; + } + } + return code; +} + +void createHandle () { + super.createHandle (); + OS.SendMessage (handle, OS.EM_LIMITTEXT, 0, 0); + if ((style & SWT.READ_ONLY) != 0) { + if ((style & (SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL)) == 0) { + state |= THEME_BACKGROUND; + } + } +} + +/** + * 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); + string = Display.withCrLf (string); + int length = OS.GetWindowTextLength (handle); + if (hooks (SWT.Verify) || filters (SWT.Verify)) { + string = verifyText (string, length, length, null); + if (string == null) return; + } + OS.SendMessage (handle, OS.EM_SETSEL, length, length); + TCHAR buffer = new TCHAR (getCodePage (), string, true); + /* + * Feature in Windows. When an edit control with ES_MULTILINE + * style that does not have the WS_VSCROLL style is full (i.e. + * there is no space at the end to draw any more characters), + * EM_REPLACESEL sends a WM_CHAR with a backspace character + * to remove any further text that is added. This is an + * implementation detail of the edit control that is unexpected + * and can cause endless recursion when EM_REPLACESEL is sent + * from a WM_CHAR handler. The fix is to ignore calling the + * handler from WM_CHAR. + */ + ignoreCharacter = true; + OS.SendMessage (handle, OS.EM_REPLACESEL, 0, buffer); + ignoreCharacter = false; + OS.SendMessage (handle, OS.EM_SCROLLCARET, 0, 0); +} + +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 (); + if (OS.IsWinCE) { + /* + * Bug in WinCE. Calling EM_SETSEL with -1 and 0 is equivalent + * to calling EM_SETSEL with 0 and -1. It causes the entire + * text to be selected instead of clearing the selection. The + * fix is to set the start of the selection to the end of the + * current selection. + */ + int [] end = new int [1]; + OS.SendMessage (handle, OS.EM_GETSEL, (int []) null, end); + OS.SendMessage (handle, OS.EM_SETSEL, end [0], end [0]); + } else { + OS.SendMessage (handle, OS.EM_SETSEL, -1, 0); + } +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + int height = 0, width = 0; + if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) { + int /*long*/ newFont, oldFont = 0; + int /*long*/ hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA (); + OS.GetTextMetrics (hDC, tm); + int count = (style & SWT.SINGLE) != 0 ? 1 : (int)/*64*/OS.SendMessage (handle, OS.EM_GETLINECOUNT, 0, 0); + height = count * tm.tmHeight; + RECT rect = new RECT (); + int flags = OS.DT_CALCRECT | OS.DT_EDITCONTROL | OS.DT_NOPREFIX; + boolean wrap = (style & SWT.MULTI) != 0 && (style & SWT.WRAP) != 0; + if (wrap && wHint != SWT.DEFAULT) { + flags |= OS.DT_WORDBREAK; + rect.right = wHint; + } + int length = OS.GetWindowTextLength (handle); + if (length != 0) { + TCHAR buffer = new TCHAR (getCodePage (), length + 1); + OS.GetWindowText (handle, buffer, length + 1); + OS.DrawText (hDC, buffer, length, rect, flags); + width = rect.right - rect.left; + } + if (wrap && hHint == SWT.DEFAULT) { + int newHeight = rect.bottom - rect.top; + if (newHeight != 0) height = newHeight; + } + if ((style & SWT.SINGLE) != 0 && message.length () > 0) { + OS.SetRect (rect, 0, 0, 0, 0); + TCHAR buffer = new TCHAR (getCodePage (), message, false); + OS.DrawText (hDC, buffer, buffer.length (), rect, flags); + width = Math.max (width, rect.right - rect.left); + } + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + } + 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); + return new Point (trim.width, trim.height); +} + +public Rectangle computeTrim (int x, int y, int width, int height) { + checkWidget (); + Rectangle rect = super.computeTrim (x, y, width, height); + /* + * The preferred height of a single-line text widget + * has been hand-crafted to be the same height as + * the single-line text widget in an editable combo + * box. + */ + int /*long*/ margins = OS.SendMessage(handle, OS.EM_GETMARGINS, 0, 0); + rect.x -= OS.LOWORD (margins); + rect.width += OS.LOWORD (margins) + OS.HIWORD (margins); + if ((style & SWT.H_SCROLL) != 0) rect.width++; + if ((style & SWT.BORDER) != 0) { + rect.x -= 1; + rect.y -= 1; + rect.width += 2; + rect.height += 2; + } + return rect; +} + +/** + * 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 (); + OS.SendMessage (handle, OS.WM_COPY, 0, 0); +} + +void createWidget () { + super.createWidget (); + message = ""; + doubleClick = true; + setTabStops (tabs = 8); + fixAlignment (); +} + +/** + * 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; + OS.SendMessage (handle, OS.WM_CUT, 0, 0); +} + +int defaultBackground () { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + return OS.GetSysColor ((bits & OS.ES_READONLY) != 0 ? OS.COLOR_3DFACE : OS.COLOR_WINDOW); +} + +boolean dragDetect (int /*long*/ hwnd, int x, int y, boolean filter, boolean [] detect, boolean [] consume) { + if (filter) { + int [] start = new int [1], end = new int [1]; + OS.SendMessage (handle, OS.EM_GETSEL, start, end); + if (start [0] != end [0]) { + int /*long*/ lParam = OS.MAKELPARAM (x, y); + int position = OS.LOWORD (OS.SendMessage (handle, OS.EM_CHARFROMPOS, 0, lParam)); + if (start [0] <= position && position < end [0]) { + if (super.dragDetect (hwnd, x, y, filter, detect, consume)) { + if (consume != null) consume [0] = true; + return true; + } + } + } + return false; + } + return super.dragDetect (hwnd, x, y, filter, detect, consume); +} + +void fixAlignment () { + /* + * Feature in Windows. When the edit control is not + * mirrored, it uses WS_EX_RIGHT, WS_EX_RTLREADING and + * WS_EX_LEFTSCROLLBAR to give the control a right to + * left appearance. This causes the control to be lead + * aligned no matter what alignment was specified by + * the programmer. For example, setting ES_RIGHT and + * WS_EX_LAYOUTRTL should cause the contents of the + * control to be left (trail) aligned in a mirrored world. + * When the orientation is changed by the user or + * specified by the programmer, WS_EX_RIGHT conflicts + * with the mirrored alignment. The fix is to clear + * or set WS_EX_RIGHT to achieve the correct alignment + * according to the orientation and mirroring. + */ + if ((style & SWT.MIRRORED) != 0) return; + int bits1 = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); + int bits2 = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((style & SWT.LEFT_TO_RIGHT) != 0) { + /* + * Bug in Windows 98. When the edit control is created + * with the style ES_RIGHT it automatically sets the + * WS_EX_LEFTSCROLLBAR bit. The fix is to clear the + * bit when the orientation of the control is left + * to right. + */ + bits1 &= ~OS.WS_EX_LEFTSCROLLBAR; + if ((style & SWT.RIGHT) != 0) { + bits1 |= OS.WS_EX_RIGHT; + bits2 |= OS.ES_RIGHT; + } + if ((style & SWT.LEFT) != 0) { + bits1 &= ~OS.WS_EX_RIGHT; + bits2 &= ~OS.ES_RIGHT; + } + } else { + if ((style & SWT.RIGHT) != 0) { + bits1 &= ~OS.WS_EX_RIGHT; + bits2 &= ~OS.ES_RIGHT; + } + if ((style & SWT.LEFT) != 0) { + bits1 |= OS.WS_EX_RIGHT; + bits2 |= OS.ES_RIGHT; + } + } + if ((style & SWT.CENTER) != 0) { + bits2 |= OS.ES_CENTER; + } + OS.SetWindowLong (handle, OS.GWL_EXSTYLE, bits1); + OS.SetWindowLong (handle, OS.GWL_STYLE, bits2); +} + +public int getBorderWidth () { + checkWidget (); + /* + * Feature in Windows 2000 and XP. Despite the fact that WS_BORDER + * is set when the edit control is created, the style bit is cleared. + * The fix is to avoid the check for WS_BORDER and use the SWT widget + * style bits instead. + */ +// if ((style & SWT.BORDER) != 0 && (style & SWT.FLAT) != 0) { +// return OS.GetSystemMetrics (OS.SM_CXBORDER); +// } + return super.getBorderWidth (); +} + +/** + * 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 (); + return (int)/*64*/OS.SendMessage (handle, OS.EM_LINEFROMCHAR, -1, 0); +} + +/** + * 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 (); + /* + * Bug in Windows. For some reason, Windows is unable + * to return the pixel coordinates of the last character + * in the widget. The fix is to temporarily insert a + * space, query the coordinates and delete the space. + * The selection is always an i-beam in this case because + * this is the only time the start of the selection can + * be equal to the last character position in the widget. + * If EM_POSFROMCHAR fails for any other reason, return + * pixel coordinates (0,0). + */ + int position = getCaretPosition (); + int /*long*/ caretPos = OS.SendMessage (handle, OS.EM_POSFROMCHAR, position, 0); + if (caretPos == -1) { + caretPos = 0; + if (position >= OS.GetWindowTextLength (handle)) { + int cp = getCodePage (); + int [] start = new int [1], end = new int [1]; + OS.SendMessage (handle, OS.EM_GETSEL, start, end); + OS.SendMessage (handle, OS.EM_SETSEL, position, position); + /* + * Feature in Windows. When an edit control with ES_MULTILINE + * style that does not have the WS_VSCROLL style is full (i.e. + * there is no space at the end to draw any more characters), + * EM_REPLACESEL sends a WM_CHAR with a backspace character + * to remove any further text that is added. This is an + * implementation detail of the edit control that is unexpected + * and can cause endless recursion when EM_REPLACESEL is sent + * from a WM_CHAR handler. The fix is to ignore calling the + * handler from WM_CHAR. + */ + ignoreCharacter = ignoreModify = true; + OS.SendMessage (handle, OS.EM_REPLACESEL, 0, new TCHAR (cp, " ", true)); + caretPos = OS.SendMessage (handle, OS.EM_POSFROMCHAR, position, 0); + OS.SendMessage (handle, OS.EM_SETSEL, position, position + 1); + OS.SendMessage (handle, OS.EM_REPLACESEL, 0, new TCHAR (cp, "", true)); + ignoreCharacter = ignoreModify = false; + OS.SendMessage (handle, OS.EM_SETSEL, start [0], start [0]); + OS.SendMessage (handle, OS.EM_SETSEL, start [0], end [0]); + } + } + return new Point (OS.GET_X_LPARAM (caretPos), OS.GET_Y_LPARAM (caretPos)); +} + +/** + * 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 (); + int [] start = new int [1], end = new int [1]; + OS.SendMessage (handle, OS.EM_GETSEL, start, end); + /* + * In Windows, there is no API to get the position of the caret + * when the selection is not an i-beam. The best that can be done + * is to query the pixel position of the current caret and compare + * it to the pixel position of the start and end of the selection. + * + * NOTE: This does not work when the i-beam belongs to another + * control. In this case, guess that the i-beam is at the start + * of the selection. + */ + int caret = start [0]; + if (start [0] != end [0]) { + int startLine = (int)/*64*/OS.SendMessage (handle, OS.EM_LINEFROMCHAR, start [0], 0); + int endLine = (int)/*64*/OS.SendMessage (handle, OS.EM_LINEFROMCHAR, end [0], 0); + if (startLine == endLine) { + if (!OS.IsWinCE) { + int idThread = OS.GetWindowThreadProcessId (handle, null); + GUITHREADINFO lpgui = new GUITHREADINFO (); + lpgui.cbSize = GUITHREADINFO.sizeof; + if (OS.GetGUIThreadInfo (idThread, lpgui)) { + if (lpgui.hwndCaret == handle || lpgui.hwndCaret == 0) { + POINT ptCurrentPos = new POINT (); + if (OS.GetCaretPos (ptCurrentPos)) { + int /*long*/ endPos = OS.SendMessage (handle, OS.EM_POSFROMCHAR, end [0], 0); + if (endPos == -1) { + int /*long*/ startPos = OS.SendMessage (handle, OS.EM_POSFROMCHAR, start [0], 0); + int startX = OS.GET_X_LPARAM (startPos); + if (ptCurrentPos.x > startX) caret = end [0]; + } else { + int endX = OS.GET_X_LPARAM (endPos); + if (ptCurrentPos.x >= endX) caret = end [0]; + } + } + } + } + } + } else { + int caretPos = (int)/*64*/OS.SendMessage (handle, OS.EM_LINEINDEX, -1, 0); + int caretLine = (int)/*64*/OS.SendMessage (handle, OS.EM_LINEFROMCHAR, caretPos, 0); + if (caretLine == endLine) caret = end [0]; + } + } + if (!OS.IsUnicode && OS.IsDBLocale) caret = mbcsToWcsPos (caret); + return caret; +} + +/** + * 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 (); + int length = OS.GetWindowTextLength (handle); + if (!OS.IsUnicode && OS.IsDBLocale) length = mbcsToWcsPos (length); + return 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 (); + char echo = (char) OS.SendMessage (handle, OS.EM_GETPASSWORDCHAR, 0, 0); + if (echo != 0 && (echo = Display.mbcsToWcs (echo, getCodePage ())) == 0) echo = '*'; + return echo; +} + +/** + * 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 (); + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + return (bits & OS.ES_READONLY) == 0; +} + +/** + * 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 (); + return (int)/*64*/OS.SendMessage (handle, OS.EM_GETLINECOUNT, 0, 0); +} + +/** + * 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 (); + int /*long*/ newFont, oldFont = 0; + int /*long*/ hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA (); + OS.GetTextMetrics (hDC, tm); + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + return tm.tmHeight; +} + +/** + * 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; +} + +/** + * Returns the character position at the given point in the receiver + * or -1 if no such position exists. The point is in the coordinate + * system of the receiver. + * <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> + * + * @since 3.3 + */ +//TODO - Javadoc +/*public*/ int getPosition (Point point) { + checkWidget(); + if (point == null) error (SWT.ERROR_NULL_ARGUMENT); + int /*long*/ lParam = OS.MAKELPARAM (point.x, point.y); + int position = OS.LOWORD (OS.SendMessage (handle, OS.EM_CHARFROMPOS, 0, lParam)); + if (!OS.IsUnicode && OS.IsDBLocale) position = mbcsToWcsPos (position); + return position; +} + +/** + * 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 (); + int [] start = new int [1], end = new int [1]; + OS.SendMessage (handle, OS.EM_GETSEL, start, end); + if (!OS.IsUnicode && OS.IsDBLocale) { + start [0] = mbcsToWcsPos (start [0]); + end [0] = mbcsToWcsPos (end [0]); + } + return new Point (start [0], end [0]); +} + +/** + * 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 (); + Point selection = getSelection (); + return selection.y - selection.x; +} + +/** + * 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 (); + int length = OS.GetWindowTextLength (handle); + if (length == 0) return ""; + int [] start = new int [1], end = new int [1]; + OS.SendMessage (handle, OS.EM_GETSEL, start, end); + if (start [0] == end [0]) return ""; + TCHAR buffer = new TCHAR (getCodePage (), length + 1); + OS.GetWindowText (handle, buffer, length + 1); + return buffer.toString (start [0], end [0] - start [0]); +} + +/** + * 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; +} + +int getTabWidth (int tabs) { + int /*long*/ oldFont = 0; + RECT rect = new RECT (); + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX; + TCHAR SPACE = new TCHAR (getCodePage (), " ", false); + OS.DrawText (hDC, SPACE, SPACE.length (), rect, flags); + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + return (rect.right - rect.left) * 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 (); + int length = OS.GetWindowTextLength (handle); + if (length == 0) return ""; + TCHAR buffer = new TCHAR (getCodePage (), length + 1); + OS.GetWindowText (handle, buffer, length + 1); + return buffer.toString (0, length); +} + +/** + * 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 ""; + int length = OS.GetWindowTextLength (handle); + if (!OS.IsUnicode && OS.IsDBLocale) length = mbcsToWcsPos (length); + end = Math.min (end, length - 1); + if (start > end) return ""; + start = Math.max (0, start); + /* + * NOTE: The current implementation uses substring () + * which can reference a potentially large character + * array. + */ + return getText ().substring (start, end + 1); +} + +/** + * 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 (int)/*64*/OS.SendMessage (handle, OS.EM_GETLIMITTEXT, 0, 0) & 0x7FFFFFFF; +} + +/** + * 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 (int)/*64*/OS.SendMessage (handle, OS.EM_GETFIRSTVISIBLELINE, 0, 0); +} + +/** + * 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 (); + /* + * Note, EM_GETSCROLLPOS is implemented in Rich Edit 3.0 + * and greater. The plain text widget and previous versions + * of Rich Edit return zero. + */ + int [] buffer = new int [2]; + int /*long*/ code = OS.SendMessage (handle, OS.EM_GETSCROLLPOS, 0, buffer); + if (code == 1) return buffer [1]; + return getTopIndex () * getLineHeight (); +} + +/** + * 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); + string = Display.withCrLf (string); + if (hooks (SWT.Verify) || filters (SWT.Verify)) { + int [] start = new int [1], end = new int [1]; + OS.SendMessage (handle, OS.EM_GETSEL, start, end); + string = verifyText (string, start [0], end [0], null); + if (string == null) return; + } + TCHAR buffer = new TCHAR (getCodePage (), string, true); + /* + * Feature in Windows. When an edit control with ES_MULTILINE + * style that does not have the WS_VSCROLL style is full (i.e. + * there is no space at the end to draw any more characters), + * EM_REPLACESEL sends a WM_CHAR with a backspace character + * to remove any further text that is added. This is an + * implementation detail of the edit control that is unexpected + * and can cause endless recursion when EM_REPLACESEL is sent + * from a WM_CHAR handler. The fix is to ignore calling the + * handler from WM_CHAR. + */ + ignoreCharacter = true; + OS.SendMessage (handle, OS.EM_REPLACESEL, 0, buffer); + ignoreCharacter = false; +} + +int mbcsToWcsPos (int mbcsPos) { + if (mbcsPos <= 0) return 0; + if (OS.IsUnicode) return mbcsPos; + int cp = getCodePage (); + int wcsTotal = 0, mbcsTotal = 0; + byte [] buffer = new byte [128]; + String delimiter = getLineDelimiter(); + int delimiterSize = delimiter.length (); + int count = (int)/*64*/OS.SendMessageA (handle, OS.EM_GETLINECOUNT, 0, 0); + for (int line=0; line<count; line++) { + int wcsSize = 0; + int linePos = (int)/*64*/OS.SendMessageA (handle, OS.EM_LINEINDEX, line, 0); + int mbcsSize = (int)/*64*/OS.SendMessageA (handle, OS.EM_LINELENGTH, linePos, 0); + if (mbcsSize != 0) { + if (mbcsSize + delimiterSize > buffer.length) { + buffer = new byte [mbcsSize + delimiterSize]; + } + //ENDIAN + buffer [0] = (byte) (mbcsSize & 0xFF); + buffer [1] = (byte) (mbcsSize >> 8); + mbcsSize = (int)/*64*/OS.SendMessageA (handle, OS.EM_GETLINE, line, buffer); + wcsSize = OS.MultiByteToWideChar (cp, OS.MB_PRECOMPOSED, buffer, mbcsSize, null, 0); + } + if (line - 1 != count) { + for (int i=0; i<delimiterSize; i++) { + buffer [mbcsSize++] = (byte) delimiter.charAt (i); + } + wcsSize += delimiterSize; + } + if ((mbcsTotal + mbcsSize) >= mbcsPos) { + int bufferSize = mbcsPos - mbcsTotal; + wcsSize = OS.MultiByteToWideChar (cp, OS.MB_PRECOMPOSED, buffer, bufferSize, null, 0); + return wcsTotal + wcsSize; + } + wcsTotal += wcsSize; + mbcsTotal += mbcsSize; + } + return wcsTotal; +} + +/** + * 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; + OS.SendMessage (handle, OS.WM_PASTE, 0, 0); +} + +void releaseWidget () { + super.releaseWidget (); + message = 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 (); + OS.SendMessage (handle, OS.EM_SETSEL, 0, -1); +} + +boolean sendKeyEvent (int type, int msg, int /*long*/ wParam, int /*long*/ lParam, Event event) { + if (!super.sendKeyEvent (type, msg, wParam, lParam, event)) { + return false; + } + if ((style & SWT.READ_ONLY) != 0) return true; + if (ignoreVerify) return true; + if (type != SWT.KeyDown) return true; + if (msg != OS.WM_CHAR && msg != OS.WM_KEYDOWN && msg != OS.WM_IME_CHAR) { + return true; + } + if (event.character == 0) return true; + if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return true; + char key = event.character; + int stateMask = event.stateMask; + + /* + * Disable all magic keys that could modify the text + * and don't send events when Alt, Shift or Ctrl is + * pressed. + */ + switch (msg) { + case OS.WM_CHAR: + if (key != 0x08 && key != 0x7F && key != '\r' && key != '\t' && key != '\n') break; + // FALL THROUGH + case OS.WM_KEYDOWN: + if ((stateMask & (SWT.ALT | SWT.SHIFT | SWT.CONTROL)) != 0) return false; + break; + } + + /* + * Feature in Windows. If the left button is down in + * the text widget, it refuses the character. The fix + * is to detect this case and avoid sending a verify + * event. + */ + if (OS.GetKeyState (OS.VK_LBUTTON) < 0) { + if (handle == OS.GetCapture()) return true; + } + + /* Verify the character */ + String oldText = ""; + int [] start = new int [1], end = new int [1]; + OS.SendMessage (handle, OS.EM_GETSEL, start, end); + switch (key) { + case 0x08: /* Bs */ + if (start [0] == end [0]) { + if (start [0] == 0) return true; + int lineStart = (int)/*64*/OS.SendMessage (handle, OS.EM_LINEINDEX, -1, 0); + if (start [0] == lineStart) { + start [0] = start [0] - DELIMITER.length (); + } else { + start [0] = start [0] - 1; + if (!OS.IsUnicode && OS.IsDBLocale) { + int [] newStart = new int [1], newEnd = new int [1]; + OS.SendMessage (handle, OS.EM_SETSEL, start [0], end [0]); + OS.SendMessage (handle, OS.EM_GETSEL, newStart, newEnd); + if (start [0] != newStart [0]) start [0] = start [0] - 1; + } + } + start [0] = Math.max (start [0], 0); + } + break; + case 0x7F: /* Del */ + if (start [0] == end [0]) { + int length = OS.GetWindowTextLength (handle); + if (start [0] == length) return true; + int line = (int)/*64*/OS.SendMessage (handle, OS.EM_LINEFROMCHAR, end [0], 0); + int lineStart = (int)/*64*/OS.SendMessage (handle, OS.EM_LINEINDEX, line + 1, 0); + if (end [0] == lineStart - DELIMITER.length ()) { + end [0] = end [0] + DELIMITER.length (); + } else { + end [0] = end [0] + 1; + if (!OS.IsUnicode && OS.IsDBLocale) { + int [] newStart = new int [1], newEnd = new int [1]; + OS.SendMessage (handle, OS.EM_SETSEL, start [0], end [0]); + OS.SendMessage (handle, OS.EM_GETSEL, newStart, newEnd); + if (end [0] != newEnd [0]) end [0] = end [0] + 1; + } + } + end [0] = Math.min (end [0], length); + } + break; + case '\r': /* Return */ + if ((style & SWT.SINGLE) != 0) return true; + oldText = DELIMITER; + break; + default: /* Tab and other characters */ + if (key != '\t' && key < 0x20) return true; + oldText = new String (new char [] {key}); + break; + } + String newText = verifyText (oldText, start [0], end [0], event); + if (newText == null) return false; + if (newText == oldText) return true; + newText = Display.withCrLf (newText); + TCHAR buffer = new TCHAR (getCodePage (), newText, true); + OS.SendMessage (handle, OS.EM_SETSEL, start [0], end [0]); + /* + * Feature in Windows. When an edit control with ES_MULTILINE + * style that does not have the WS_VSCROLL style is full (i.e. + * there is no space at the end to draw any more characters), + * EM_REPLACESEL sends a WM_CHAR with a backspace character + * to remove any further text that is added. This is an + * implementation detail of the edit control that is unexpected + * and can cause endless recursion when EM_REPLACESEL is sent + * from a WM_CHAR handler. The fix is to ignore calling the + * handler from WM_CHAR. + */ + ignoreCharacter = true; + OS.SendMessage (handle, OS.EM_REPLACESEL, 0, buffer); + ignoreCharacter = false; + return false; +} + +void setBounds (int x, int y, int width, int height, int flags) { + /* + * Feature in Windows. When the caret is moved, + * the text widget scrolls to show the new location. + * This means that the text widget may be scrolled + * to the right in order to show the caret when the + * widget is not large enough to show both the caret + * location and all the text. Unfortunately, when + * the text widget is resized such that all the text + * and the caret could be visible, Windows does not + * scroll the widget back. The fix is to resize the + * text widget, set the selection to the start of the + * text and then restore the selection. This will + * cause the text widget compute the correct scroll + * position. + */ + if ((flags & OS.SWP_NOSIZE) == 0 && width != 0) { + RECT rect = new RECT (); + OS.GetWindowRect (handle, rect); + int /*long*/ margins = OS.SendMessage (handle, OS.EM_GETMARGINS, 0, 0); + int marginWidth = OS.LOWORD (margins) + OS.HIWORD (margins); + if (rect.right - rect.left <= marginWidth) { + int [] start = new int [1], end = new int [1]; + OS.SendMessage (handle, OS.EM_GETSEL, start, end); + if (start [0] != 0 || end [0] != 0) { + SetWindowPos (handle, 0, x, y, width, height, flags); + OS.SendMessage (handle, OS.EM_SETSEL, 0, 0); + OS.SendMessage (handle, OS.EM_SETSEL, start [0], end [0]); + return; + } + } + } + super.setBounds (x, y, width, height, flags); + + /* + * Bug in Windows. If the client area height is smaller than + * the font height, then the multi-line text widget does not + * update the formatting rectangle when resized. The fix is to + * detect this case and explicitly set the formatting rectangle. + */ + if ((flags & OS.SWP_NOSIZE) == 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.ES_MULTILINE) != 0) { + int /*long*/ newFont, oldFont = 0; + int /*long*/ hDC = OS.GetDC (handle); + newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA (); + OS.GetTextMetrics (hDC, tm); + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + RECT rect = new RECT(); + OS.GetClientRect (handle, rect); + if ((rect.bottom - rect.top) < tm.tmHeight) { + int /*long*/ margins = OS.SendMessage (handle, OS.EM_GETMARGINS, 0, 0); + rect.left += OS.LOWORD (margins); + rect.right -= OS.HIWORD (margins); + rect.top = 0; + rect.bottom = tm.tmHeight; + OS.SendMessage (handle, OS.EM_SETRECT, 0, rect); + } + } + } +} + +void setDefaultFont () { + super.setDefaultFont (); + setMargins (); +} + +/** + * 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 (echo != 0) { + if ((echo = (char) Display.wcsToMbcs (echo, getCodePage ())) == 0) echo = '*'; + } + OS.SendMessage (handle, OS.EM_SETPASSWORDCHAR, echo, 0); + /* + * Bug in Windows. When the password character is changed, + * Windows does not redraw to show the new password character. + * The fix is to force a redraw when the character is set. + */ + OS.InvalidateRect (handle, null, true); +} + +/** + * 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 (); + style &= ~SWT.READ_ONLY; + if (!editable) style |= SWT.READ_ONLY; + OS.SendMessage (handle, OS.EM_SETREADONLY, editable ? 0 : 1, 0); +} + +public void setFont (Font font) { + checkWidget (); + super.setFont (font); + setTabStops (tabs); + setMargins (); +} + +void setMargins () { + /* + * Bug in Windows. When EM_SETCUEBANNER is used to set the + * banner text, the control does not take into account the + * margins, causing the first character to be clipped. The + * fix is to set the margins to zero. + */ + if ((style & SWT.SEARCH) != 0) { + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + OS.SendMessage (handle, OS.EM_SETMARGINS, OS.EC_LEFTMARGIN | OS.EC_RIGHTMARGIN, 0); + } + } +} + +/** + * 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 (!OS.IsWinCE) { + if (OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.ES_MULTILINE) == 0) { + int length = message.length (); + char [] chars = new char [length + 1]; + message.getChars(0, length, chars, 0); + OS.SendMessage (handle, OS.EM_SETCUEBANNER, 0, chars); + } + } else { + OS.InvalidateRect (handle, null, true); + } + } +} + +/** + * 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(); + if (OS.IsWinCE) return; + if (OS.WIN32_VERSION < OS.VERSION (4, 10)) return; + int flags = SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT; + if ((orientation & flags) == 0 || (orientation & flags) == flags) return; + style &= ~flags; + style |= orientation & flags; + int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + bits |= OS.WS_EX_RTLREADING | OS.WS_EX_LEFTSCROLLBAR; + } else { + bits &= ~(OS.WS_EX_RTLREADING | OS.WS_EX_LEFTSCROLLBAR); + } + OS.SetWindowLong (handle, OS.GWL_EXSTYLE, bits); + fixAlignment (); +} + +/** + * 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 (); + if (!OS.IsUnicode && OS.IsDBLocale) start = wcsToMbcsPos (start); + OS.SendMessage (handle, OS.EM_SETSEL, start, start); + OS.SendMessage (handle, OS.EM_SCROLLCARET, 0, 0); +} + +/** + * 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 (!OS.IsUnicode && OS.IsDBLocale) { + start = wcsToMbcsPos (start); + end = wcsToMbcsPos (end); + } + OS.SendMessage (handle, OS.EM_SETSEL, start, end); + OS.SendMessage (handle, OS.EM_SCROLLCARET, 0, 0); +} + +public void setRedraw (boolean redraw) { + checkWidget (); + super.setRedraw (redraw); + /* + * Feature in Windows. When WM_SETREDRAW is used to turn + * redraw off, the edit control is not scrolled to show the + * i-beam. The fix is to detect that the i-beam has moved + * while redraw is turned off and force it to be visible + * when redraw is restored. + */ + if (!getDrawing ()) return; + int [] start = new int [1], end = new int [1]; + OS.SendMessage (handle, OS.EM_GETSEL, start, end); + if (!redraw) { + oldStart = start [0]; oldEnd = end [0]; + } else { + if (oldStart == start [0] && oldEnd == end [0]) return; + OS.SendMessage (handle, OS.EM_SCROLLCARET, 0, 0); + } +} + +/** + * 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 (tabs < 0) return; + setTabStops (this.tabs = tabs); +} + +void setTabStops (int tabs) { + /* + * Feature in Windows. Windows expects the tab spacing in + * dialog units so we must convert from space widths. Due + * to round off error, the tab spacing may not be the exact + * number of space widths, depending on the font. + */ + int width = (getTabWidth (tabs) * 4) / OS.LOWORD (OS.GetDialogBaseUnits ()); + OS.SendMessage (handle, OS.EM_SETTABSTOPS, 1, new int [] {width}); +} + +/** + * 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); + string = Display.withCrLf (string); + if (hooks (SWT.Verify) || filters (SWT.Verify)) { + int length = OS.GetWindowTextLength (handle); + string = verifyText (string, 0, length, null); + if (string == null) return; + } + int limit = (int)/*64*/OS.SendMessage (handle, OS.EM_GETLIMITTEXT, 0, 0) & 0x7FFFFFFF; + if (string.length () > limit) string = string.substring (0, limit); + TCHAR buffer = new TCHAR (getCodePage (), string, true); + OS.SetWindowText (handle, buffer); + /* + * Bug in Windows. When the widget is multi line + * text widget, it does not send a WM_COMMAND with + * control code EN_CHANGE from SetWindowText () to + * notify the application that the text has changed. + * The fix is to send the event. + */ + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.ES_MULTILINE) != 0) { + sendEvent (SWT.Modify); + // widget could be disposed at this point + } +} + +/** + * 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); + OS.SendMessage (handle, OS.EM_SETLIMITTEXT, limit, 0); +} + +/** + * 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 count = (int)/*64*/OS.SendMessage (handle, OS.EM_GETLINECOUNT, 0, 0); + index = Math.min (Math.max (index, 0), count - 1); + int topIndex = (int)/*64*/OS.SendMessage (handle, OS.EM_GETFIRSTVISIBLELINE, 0, 0); + OS.SendMessage (handle, OS.EM_LINESCROLL, 0, index - topIndex); +} + +/** + * 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 (); + OS.SendMessage (handle, OS.EM_SCROLLCARET, 0, 0); +} + +String verifyText (String string, int start, int end, Event keyEvent) { + if (ignoreVerify) return string; + Event event = new Event (); + event.text = string; + event.start = start; + event.end = end; + if (keyEvent != null) { + event.character = keyEvent.character; + event.keyCode = keyEvent.keyCode; + event.stateMask = keyEvent.stateMask; + } + if (!OS.IsUnicode && OS.IsDBLocale) { + event.start = mbcsToWcsPos (start); + event.end = mbcsToWcsPos (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; +} + +int wcsToMbcsPos (int wcsPos) { + if (wcsPos <= 0) return 0; + if (OS.IsUnicode) return wcsPos; + int cp = getCodePage (); + int wcsTotal = 0, mbcsTotal = 0; + byte [] buffer = new byte [128]; + String delimiter = getLineDelimiter (); + int delimiterSize = delimiter.length (); + int count = (int)/*64*/OS.SendMessageA (handle, OS.EM_GETLINECOUNT, 0, 0); + for (int line=0; line<count; line++) { + int wcsSize = 0; + int linePos = (int)/*64*/OS.SendMessageA (handle, OS.EM_LINEINDEX, line, 0); + int mbcsSize = (int)/*64*/OS.SendMessageA (handle, OS.EM_LINELENGTH, linePos, 0); + if (mbcsSize != 0) { + if (mbcsSize + delimiterSize > buffer.length) { + buffer = new byte [mbcsSize + delimiterSize]; + } + //ENDIAN + buffer [0] = (byte) (mbcsSize & 0xFF); + buffer [1] = (byte) (mbcsSize >> 8); + mbcsSize = (int)/*64*/OS.SendMessageA (handle, OS.EM_GETLINE, line, buffer); + wcsSize = OS.MultiByteToWideChar (cp, OS.MB_PRECOMPOSED, buffer, mbcsSize, null, 0); + } + if (line - 1 != count) { + for (int i=0; i<delimiterSize; i++) { + buffer [mbcsSize++] = (byte) delimiter.charAt (i); + } + wcsSize += delimiterSize; + } + if ((wcsTotal + wcsSize) >= wcsPos) { + wcsSize = 0; + int index = 0; + while (index < mbcsSize) { + if ((wcsTotal + wcsSize) == wcsPos) { + return mbcsTotal + index; + } + if (OS.IsDBCSLeadByte (buffer [index++])) index++; + wcsSize++; + } + return mbcsTotal + mbcsSize; + } + wcsTotal += wcsSize; + mbcsTotal += mbcsSize; + } + return mbcsTotal; +} + +int widgetStyle () { + int bits = super.widgetStyle () | OS.ES_AUTOHSCROLL; + if ((style & SWT.PASSWORD) != 0) bits |= OS.ES_PASSWORD; + if ((style & SWT.CENTER) != 0) bits |= OS.ES_CENTER; + if ((style & SWT.RIGHT) != 0) bits |= OS.ES_RIGHT; + if ((style & SWT.READ_ONLY) != 0) bits |= OS.ES_READONLY; + if ((style & SWT.SINGLE) != 0) { + /* + * Feature in Windows. When a text control is read-only, + * uses COLOR_3DFACE for the background . If the text + * controls single-line and is within a tab folder or + * some other themed control, using WM_ERASEBKGND and + * WM_CTRCOLOR to draw the theme background results in + * pixel corruption. The fix is to use an ES_MULTILINE + * text control instead. + */ + if ((style & SWT.READ_ONLY) != 0) { + if ((style & (SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL)) == 0) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + bits |= OS.ES_MULTILINE; + } + } + } + return bits; + } + bits |= OS.ES_MULTILINE | OS.ES_NOHIDESEL | OS.ES_AUTOVSCROLL; + if ((style & SWT.WRAP) != 0) bits &= ~(OS.WS_HSCROLL | OS.ES_AUTOHSCROLL); + return bits; +} + +TCHAR windowClass () { + return EditClass; +} + +int /*long*/ windowProc () { + return EditProc; +} + +int /*long*/ windowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (msg == OS.EM_UNDO) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.ES_MULTILINE) == 0) { + LRESULT result = wmClipboard (OS.EM_UNDO, wParam, lParam); + if (result != null) return result.value; + return callWindowProc (hwnd, OS.EM_UNDO, wParam, lParam); + } + } + if (msg == Display.SWT_RESTORECARET) { + callWindowProc (hwnd, OS.WM_KILLFOCUS, 0, 0); + callWindowProc (hwnd, OS.WM_SETFOCUS, 0, 0); + return 1; + } + return super.windowProc (hwnd, msg, wParam, lParam); +} + +LRESULT WM_CHAR (int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreCharacter) return null; + LRESULT result = super.WM_CHAR (wParam, lParam); + if (result != null) return result; + + /* + * Bug in Windows. When the user types CTRL and BS + * in an edit control, a DEL character is generated. + * Rather than deleting the text, the DEL character + * is inserted into the control. The fix is to detect + * this case and not call the window proc. + */ + switch ((int)/*64*/wParam) { + case SWT.DEL: + if (OS.GetKeyState (OS.VK_CONTROL) < 0) { + return LRESULT.ZERO; + } + } + + /* + * Feature in Windows. For some reason, when the + * widget is a single line text widget, when the + * user presses tab, return or escape, Windows beeps. + * The fix is to look for these keys and not call + * the window proc. + */ + if ((style & SWT.SINGLE) != 0) { + switch ((int)/*64*/wParam) { + case SWT.CR: + postEvent (SWT.DefaultSelection); + // FALL THROUGH + case SWT.TAB: + case SWT.ESC: return LRESULT.ZERO; + } + } + return result; +} + +LRESULT WM_CLEAR (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_CLEAR (wParam, lParam); + if (result != null) return result; + return wmClipboard (OS.WM_CLEAR, wParam, lParam); +} + +LRESULT WM_CUT (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_CUT (wParam, lParam); + if (result != null) return result; + return wmClipboard (OS.WM_CUT, wParam, lParam); +} + +LRESULT WM_ERASEBKGND (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_ERASEBKGND (wParam, lParam); + if ((style & SWT.READ_ONLY) != 0) { + if ((style & (SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL)) == 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.ES_MULTILINE) != 0) { + Control control = findBackgroundControl (); + if (control == null && background == -1) { + if ((state & THEME_BACKGROUND) != 0) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + control = findThemeControl (); + if (control != null) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + fillThemeBackground (wParam, control, rect); + return LRESULT.ONE; + } + } + } + } + } + } + } + return result; +} + +LRESULT WM_GETDLGCODE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_GETDLGCODE (wParam, lParam); + if (result != null) return result; + + /* + * Bug in WinCE PPC. For some reason, sending WM_GETDLGCODE + * to a multi-line edit control causes it to ignore return and + * tab keys. The fix is to return the value which is normally + * returned by the text window proc on other versions of Windows. + */ + if (OS.IsPPC) { + if ((style & SWT.MULTI) != 0 && (style & SWT.READ_ONLY) == 0 && lParam == 0) { + return new LRESULT (OS.DLGC_HASSETSEL | OS.DLGC_WANTALLKEYS | OS.DLGC_WANTCHARS); + } + } + + /* + * Feature in Windows. Despite the fact that the + * edit control is read only, it still returns a + * dialog code indicating that it wants all keys. + * The fix is to detect this case and clear the bits. + * + * NOTE: A read only edit control processes arrow keys + * so DLGC_WANTARROWS should not be cleared. + */ + if ((style & SWT.READ_ONLY) != 0) { + int /*long*/ code = callWindowProc (handle, OS.WM_GETDLGCODE, wParam, lParam); + code &= ~(OS.DLGC_WANTALLKEYS | OS.DLGC_WANTTAB); + return new LRESULT (code); + } + return null; +} + +LRESULT WM_IME_CHAR (int /*long*/ wParam, int /*long*/ lParam) { + + /* Process a DBCS character */ + Display display = this.display; + display.lastKey = 0; + display.lastAscii = (int)/*64*/wParam; + display.lastVirtual = display.lastNull = display.lastDead = false; + if (!sendKeyEvent (SWT.KeyDown, OS.WM_IME_CHAR, wParam, lParam)) { + return LRESULT.ZERO; + } + + /* + * Feature in Windows. The Windows text widget uses + * two 2 WM_CHAR's to process a DBCS key instead of + * using WM_IME_CHAR. The fix is to allow the text + * widget to get the WM_CHAR's but ignore sending + * them to the application. + */ + ignoreCharacter = true; + int /*long*/ result = callWindowProc (handle, OS.WM_IME_CHAR, wParam, lParam); + MSG msg = new MSG (); + int flags = OS.PM_REMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE; + while (OS.PeekMessage (msg, handle, OS.WM_CHAR, OS.WM_CHAR, flags)) { + OS.TranslateMessage (msg); + OS.DispatchMessage (msg); + } + ignoreCharacter = false; + + sendKeyEvent (SWT.KeyUp, OS.WM_IME_CHAR, wParam, lParam); + // widget could be disposed at this point + display.lastKey = display.lastAscii = 0; + return new LRESULT (result); +} + +LRESULT WM_LBUTTONDBLCLK (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Prevent Windows from processing WM_LBUTTONDBLCLK + * when double clicking behavior is disabled by not + * calling the window proc. + */ + LRESULT result = null; + sendMouseEvent (SWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam); + if (!sendMouseEvent (SWT.MouseDoubleClick, 1, handle, OS.WM_LBUTTONDBLCLK, wParam, lParam)) { + result = LRESULT.ZERO; + } + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + if (!doubleClick) return LRESULT.ZERO; + + /* + * Bug in Windows. When the last line of text in the + * widget is double clicked and the line is empty, Windows + * hides the i-beam then moves it to the first line in + * the widget but does not scroll to show the user. + * If the user types without clicking the mouse, invalid + * characters are displayed at the end of each line of + * text in the widget. The fix is to detect this case + * and avoid calling the window proc. + */ + int [] start = new int [1], end = new int [1]; + OS.SendMessage (handle, OS.EM_GETSEL, start, end); + if (start [0] == end [0]) { + int length = OS.GetWindowTextLength (handle); + if (length == start [0]) { + int code = (int)/*64*/OS.SendMessage (handle, OS.EM_LINELENGTH, length, 0); + if (code == 0) return LRESULT.ZERO; + } + } + return result; +} + +LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + if (OS.IsPPC) { + LRESULT result = null; + Display display = this.display; + display.captureChanged = false; + boolean dispatch = sendMouseEvent (SWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam); + /* + * Note: On WinCE PPC, only attempt to recognize the gesture for + * a context menu when the control contains a valid menu or there + * are listeners for the MenuDetect event. + * + * Note: On WinCE PPC, the gesture that brings up a popup menu + * on the text widget must keep the current text selection. As a + * result, the window proc is only called if the menu is not shown. + */ + boolean hasMenu = menu != null && !menu.isDisposed (); + if (hasMenu || hooks (SWT.MenuDetect)) { + int x = OS.GET_X_LPARAM (lParam); + int y = OS.GET_Y_LPARAM (lParam); + SHRGINFO shrg = new SHRGINFO (); + shrg.cbSize = SHRGINFO.sizeof; + shrg.hwndClient = handle; + shrg.ptDown_x = x; + shrg.ptDown_y = y; + shrg.dwFlags = OS.SHRG_RETURNCMD; + int type = OS.SHRecognizeGesture (shrg); + if (type == OS.GN_CONTEXTMENU) { + showMenu (x, y); + return LRESULT.ONE; + } + } + if (dispatch) { + result = new LRESULT (callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam)); + } else { + result = LRESULT.ZERO; + } + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + return result; + } + return super.WM_LBUTTONDOWN (wParam, lParam); +} + +LRESULT WM_PASTE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_PASTE (wParam, lParam); + if (result != null) return result; + return wmClipboard (OS.WM_PASTE, wParam, lParam); +} + +LRESULT WM_UNDO (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_UNDO (wParam, lParam); + if (result != null) return result; + return wmClipboard (OS.WM_UNDO, wParam, lParam); +} + +LRESULT wmClipboard (int msg, int /*long*/ wParam, int /*long*/ lParam) { + if ((style & SWT.READ_ONLY) != 0) return null; + if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return null; + boolean call = false; + int [] start = new int [1], end = new int [1]; + String newText = null; + switch (msg) { + case OS.WM_CLEAR: + case OS.WM_CUT: + OS.SendMessage (handle, OS.EM_GETSEL, start, end); + if (start [0] != end [0]) { + newText = ""; + call = true; + } + break; + case OS.WM_PASTE: + OS.SendMessage (handle, OS.EM_GETSEL, start, end); + newText = getClipboardText (); + break; + case OS.EM_UNDO: + case OS.WM_UNDO: + if (OS.SendMessage (handle, OS.EM_CANUNDO, 0, 0) != 0) { + ignoreModify = ignoreCharacter = true; + callWindowProc (handle, msg, wParam, lParam); + int length = OS.GetWindowTextLength (handle); + int [] newStart = new int [1], newEnd = new int [1]; + OS.SendMessage (handle, OS.EM_GETSEL, newStart, newEnd); + if (length != 0 && newStart [0] != newEnd [0]) { + TCHAR buffer = new TCHAR (getCodePage (), length + 1); + OS.GetWindowText (handle, buffer, length + 1); + newText = buffer.toString (newStart [0], newEnd [0] - newStart [0]); + } else { + newText = ""; + } + callWindowProc (handle, msg, wParam, lParam); + OS.SendMessage (handle, OS.EM_GETSEL, start, end); + ignoreModify = ignoreCharacter = false; + } + break; + } + if (newText != null) { + String oldText = newText; + newText = verifyText (newText, start [0], end [0], null); + if (newText == null) return LRESULT.ZERO; + if (!newText.equals (oldText)) { + if (call) { + callWindowProc (handle, msg, wParam, lParam); + } + newText = Display.withCrLf (newText); + TCHAR buffer = new TCHAR (getCodePage (), newText, true); + /* + * Feature in Windows. When an edit control with ES_MULTILINE + * style that does not have the WS_VSCROLL style is full (i.e. + * there is no space at the end to draw any more characters), + * EM_REPLACESEL sends a WM_CHAR with a backspace character + * to remove any further text that is added. This is an + * implementation detail of the edit control that is unexpected + * and can cause endless recursion when EM_REPLACESEL is sent + * from a WM_CHAR handler. The fix is to ignore calling the + * handler from WM_CHAR. + */ + ignoreCharacter = true; + OS.SendMessage (handle, OS.EM_REPLACESEL, 0, buffer); + ignoreCharacter = false; + return LRESULT.ZERO; + } + } + if (msg == OS.WM_UNDO) { + ignoreVerify = ignoreCharacter = true; + callWindowProc (handle, OS.WM_UNDO, wParam, lParam); + ignoreVerify = ignoreCharacter = false; + return LRESULT.ONE; + } + return null; +} + +LRESULT wmColorChild (int /*long*/ wParam, int /*long*/ lParam) { + if ((style & SWT.READ_ONLY) != 0) { + if ((style & (SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL)) == 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.ES_MULTILINE) != 0) { + Control control = findBackgroundControl (); + if (control == null && background == -1) { + if ((state & THEME_BACKGROUND) != 0) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + control = findThemeControl (); + if (control != null) { + OS.SetTextColor (wParam, getForegroundPixel ()); + OS.SetBkColor (wParam, getBackgroundPixel ()); + OS.SetBkMode (wParam, OS.TRANSPARENT); + return new LRESULT (OS.GetStockObject (OS.NULL_BRUSH)); + } + } + } + } + } + } + } + return super.wmColorChild (wParam, lParam); +} + +LRESULT wmCommandChild (int /*long*/ wParam, int /*long*/ lParam) { + int code = OS.HIWORD (wParam); + switch (code) { + case OS.EN_CHANGE: + if (findImageControl () != null) { + OS.InvalidateRect (handle, null, true); + } + if (ignoreModify) break; + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the modify + * event. If this happens, end the processing of the + * Windows message by returning zero as the result of + * the window proc. + */ + sendEvent (SWT.Modify); + if (isDisposed ()) return LRESULT.ZERO; + break; + case OS.EN_ALIGN_LTR_EC: + style &= ~SWT.RIGHT_TO_LEFT; + style |= SWT.LEFT_TO_RIGHT; + fixAlignment (); + break; + case OS.EN_ALIGN_RTL_EC: + style &= ~SWT.LEFT_TO_RIGHT; + style |= SWT.RIGHT_TO_LEFT; + fixAlignment (); + break; + } + return super.wmCommandChild (wParam, lParam); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolBar.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolBar.java new file mode 100755 index 0000000000..cb95765513 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolBar.java @@ -0,0 +1,1514 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +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 lastFocusId, lastArrowId, lastHotId; + ToolItem [] items; + ToolItem [] tabItemList; + boolean ignoreResize, ignoreMouse; + ImageList imageList, disabledImageList, hotImageList; + static final int /*long*/ ToolBarProc; + static final TCHAR ToolBarClass = new TCHAR (0, OS.TOOLBARCLASSNAME, true); + static { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, ToolBarClass, lpWndClass); + ToolBarProc = lpWndClass.lpfnWndProc; + } + + /* + * From the Windows SDK for TB_SETBUTTONSIZE: + * + * "If an application does not explicitly + * set the button size, the size defaults + * to 24 by 22 pixels". + */ + static final int DEFAULT_WIDTH = 24; + static final int DEFAULT_HEIGHT = 22; + +/** + * 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. + * + * NOTE: The CCS_VERT style cannot be applied when the + * widget is created because of this conflict. + */ + if ((style & SWT.VERTICAL) != 0) { + this.style |= SWT.VERTICAL; + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + /* + * Feature in Windows. When a tool bar has the style + * TBSTYLE_LIST and has a drop down item, Window leaves + * too much padding around the button. This affects + * every button in the tool bar and makes the preferred + * height too big. The fix is to set the TBSTYLE_LIST + * when the tool bar contains both text and images. + * + * NOTE: Tool bars with CCS_VERT must have TBSTYLE_LIST + * set before any item is added or the tool bar does + * not lay out properly. The work around does not run + * in this case. + */ + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if ((style & SWT.RIGHT) != 0) bits |= OS.TBSTYLE_LIST; + } + OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.CCS_VERT); + } else { + this.style |= SWT.HORIZONTAL; + } +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + /* + * Bug in Windows. For some reason, during the processing + * of WM_SYSCHAR, the tool bar window proc does not call the + * default window proc causing mnemonics for the menu bar + * to be ignored. The fix is to always call the default + * window proc for WM_SYSCHAR. + */ + if (msg == OS.WM_SYSCHAR) { + return OS.DefWindowProc (hwnd, msg, wParam, lParam); + } + return OS.CallWindowProc (ToolBarProc, hwnd, msg, wParam, lParam); +} + +static int checkStyle (int style) { + /* + * On Windows, only flat tool bars can be traversed. + */ + if ((style & SWT.FLAT) == 0) style |= SWT.NO_FOCUS; + + /* + * A vertical tool bar cannot wrap because TB_SETROWS + * fails when the toolbar has TBSTYLE_WRAPABLE. + */ + if ((style & SWT.VERTICAL) != 0) style &= ~SWT.WRAP; + + /* + * 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); +} + +void checkBuffered () { + super.checkBuffered (); + if (OS.COMCTL32_MAJOR >= 6) style |= SWT.DOUBLE_BUFFERED; +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + int width = 0, height = 0; + if ((style & SWT.VERTICAL) != 0) { + RECT rect = new RECT (); + TBBUTTON lpButton = new TBBUTTON (); + int count = (int)/*64*/OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0); + for (int i=0; i<count; i++) { + OS.SendMessage (handle, OS.TB_GETITEMRECT, i, rect); + height = Math.max (height, rect.bottom); + OS.SendMessage (handle, OS.TB_GETBUTTON, i, lpButton); + if ((lpButton.fsStyle & OS.BTNS_SEP) != 0) { + TBBUTTONINFO info = new TBBUTTONINFO (); + info.cbSize = TBBUTTONINFO.sizeof; + info.dwMask = OS.TBIF_SIZE; + OS.SendMessage (handle, OS.TB_GETBUTTONINFO, lpButton.idCommand, info); + width = Math.max (width, info.cx); + } else { + width = Math.max (width, rect.right); + } + } + } else { + RECT oldRect = new RECT (); + OS.GetWindowRect (handle, oldRect); + int oldWidth = oldRect.right - oldRect.left; + int oldHeight = oldRect.bottom - oldRect.top; + int border = getBorderWidth (); + int newWidth = wHint == SWT.DEFAULT ? 0x3FFF : wHint + border * 2; + int newHeight = hHint == SWT.DEFAULT ? 0x3FFF : hHint + border * 2; + boolean redraw = getDrawing () && OS.IsWindowVisible (handle); + ignoreResize = true; + if (redraw) OS.UpdateWindow (handle); + int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOREDRAW | OS.SWP_NOZORDER; + SetWindowPos (handle, 0, 0, 0, newWidth, newHeight, flags); + int count = (int)/*64*/OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0); + if (count != 0) { + RECT rect = new RECT (); + OS.SendMessage (handle, OS.TB_GETITEMRECT, count - 1, rect); + width = Math.max (width, rect.right); + height = Math.max (height, rect.bottom); + } + SetWindowPos (handle, 0, 0, 0, oldWidth, oldHeight, flags); + if (redraw) OS.ValidateRect (handle, null); + ignoreResize = false; + } + + /* + * From the Windows SDK for TB_SETBUTTONSIZE: + * + * "If an application does not explicitly + * set the button size, the size defaults + * to 24 by 22 pixels". + */ + 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) { + checkWidget (); + Rectangle trim = super.computeTrim (x, y, width, height); + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.CCS_NODIVIDER) == 0) trim.height += 2; + return trim; +} + +Widget computeTabGroup () { + ToolItem [] items = _getItems (); + if (tabItemList == null) { + int i = 0; + while (i < items.length && items [i].control == null) i++; + if (i == items.length) return super.computeTabGroup (); + } + int index = (int)/*64*/OS.SendMessage (handle, OS.TB_GETHOTITEM, 0, 0); + if (index == -1) index = lastHotId; + while (index >= 0) { + ToolItem item = items [index]; + if (item.isTabGroup ()) return item; + index--; + } + return super.computeTabGroup (); +} + +Widget [] computeTabList () { + ToolItem [] items = _getItems (); + if (tabItemList == null) { + int i = 0; + while (i < items.length && items [i].control == null) i++; + if (i == items.length) return super.computeTabList (); + } + Widget result [] = {}; + if (!isTabGroup () || !isEnabled () || !isVisible ()) return result; + ToolItem [] list = tabList != null ? _getTabItemList () : items; + for (int i=0; i<list.length; i++) { + ToolItem 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; + } + } + if (result.length == 0) result = new Widget [] {this}; + return result; +} + +void createHandle () { + super.createHandle (); + state &= ~CANVAS; + + /* + * Feature in Windows. When TBSTYLE_FLAT is used to create + * a flat toolbar, for some reason TBSTYLE_TRANSPARENT is + * also set. This causes the toolbar to flicker when it is + * moved or resized. The fix is to clear TBSTYLE_TRANSPARENT. + * + * NOTE: This work around is unnecessary on XP. There is no + * flickering and clearing the TBSTYLE_TRANSPARENT interferes + * with the XP theme. + */ + if ((style & SWT.FLAT) != 0) { + if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + bits &= ~OS.TBSTYLE_TRANSPARENT; + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); + } + } + + /* + * Feature in Windows. Despite the fact that the + * tool tip text contains \r\n, the tooltip will + * not honour the new line unless TTM_SETMAXTIPWIDTH + * is set. The fix is to set TTM_SETMAXTIPWIDTH to + * a large value. + */ + /* + * These lines are intentionally commented. The tool + * bar currently sets this value to 300 so it is not + * necessary to set TTM_SETMAXTIPWIDTH. + */ +// int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.TB_GETTOOLTIPS, 0, 0); +// OS.SendMessage (hwndToolTip, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF); + + /* + * Feature in Windows. When the control is created, + * it does not use the default system font. A new HFONT + * is created and destroyed when the control is destroyed. + * This means that a program that queries the font from + * this control, uses the font in another control and then + * destroys this control will have the font unexpectedly + * destroyed in the other control. The fix is to assign + * the font ourselves each time the control is created. + * The control will not destroy a font that it did not + * create. + */ + int /*long*/ hFont = OS.GetStockObject (OS.SYSTEM_FONT); + OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0); + + /* Set the button struct, bitmap and button sizes */ + OS.SendMessage (handle, OS.TB_BUTTONSTRUCTSIZE, TBBUTTON.sizeof, 0); + OS.SendMessage (handle, OS.TB_SETBITMAPSIZE, 0, 0); + OS.SendMessage (handle, OS.TB_SETBUTTONSIZE, 0, 0); + + /* Set the extended style bits */ + int bits = OS.TBSTYLE_EX_DRAWDDARROWS | OS.TBSTYLE_EX_MIXEDBUTTONS | OS.TBSTYLE_EX_HIDECLIPPEDBUTTONS; + if (OS.COMCTL32_MAJOR >= 6) bits |= OS.TBSTYLE_EX_DOUBLEBUFFER; + OS.SendMessage (handle, OS.TB_SETEXTENDEDSTYLE, 0, bits); +} + +void createItem (ToolItem item, int index) { + int count = (int)/*64*/OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0); + if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE); + int id = 0; + while (id < items.length && items [id] != null) id++; + if (id == items.length) { + ToolItem [] newItems = new ToolItem [items.length + 4]; + System.arraycopy (items, 0, newItems, 0, items.length); + items = newItems; + } + int bits = item.widgetStyle (); + TBBUTTON lpButton = new TBBUTTON (); + lpButton.idCommand = id; + lpButton.fsStyle = (byte) bits; + lpButton.fsState = (byte) OS.TBSTATE_ENABLED; + + /* + * Bug in Windows. Despite the fact that the image list + * index has never been set for the item, Windows always + * assumes that the image index for the item is valid. + * When an item is inserted, the image index is zero. + * Therefore, when the first image is inserted and is + * assigned image index zero, every item draws with this + * image. The fix is to set the image index to none + * when the item is created. This is not necessary in + * the case when the item has the BTNS_SEP style because + * separators cannot show images. + */ + if ((bits & OS.BTNS_SEP) == 0) lpButton.iBitmap = OS.I_IMAGENONE; + if (OS.SendMessage (handle, OS.TB_INSERTBUTTON, index, lpButton) == 0) { + error (SWT.ERROR_ITEM_NOT_ADDED); + } + items [item.id = id] = item; + if ((style & SWT.VERTICAL) != 0) setRowCount (count + 1); + layoutItems (); +} + +void createWidget () { + super.createWidget (); + items = new ToolItem [4]; + lastFocusId = lastArrowId = lastHotId = -1; +} + +int defaultBackground () { + if (OS.IsWinCE) return OS.GetSysColor (OS.COLOR_BTNFACE); + return super.defaultBackground (); +} + +void destroyItem (ToolItem item) { + TBBUTTONINFO info = new TBBUTTONINFO (); + info.cbSize = TBBUTTONINFO.sizeof; + info.dwMask = OS.TBIF_IMAGE | OS.TBIF_STYLE; + int index = (int)/*64*/OS.SendMessage (handle, OS.TB_GETBUTTONINFO, item.id, info); + /* + * Feature in Windows. For some reason, a tool item that has + * the style BTNS_SEP does not return I_IMAGENONE when queried + * for an image index, despite the fact that no attempt has been + * made to assign an image to the item. As a result, operations + * on an image list that use the wrong index cause random results. + * The fix is to ensure that the tool item is not a separator + * before using the image index. Since separators cannot have + * an image and one is never assigned, this is not a problem. + */ + if ((info.fsStyle & OS.BTNS_SEP) == 0 && info.iImage != OS.I_IMAGENONE) { + if (imageList != null) imageList.put (info.iImage, null); + if (hotImageList != null) hotImageList.put (info.iImage, null); + if (disabledImageList != null) disabledImageList.put (info.iImage, null); + } + OS.SendMessage (handle, OS.TB_DELETEBUTTON, index, 0); + if (item.id == lastFocusId) lastFocusId = -1; + if (item.id == lastArrowId) lastArrowId = -1; + if (item.id == lastHotId) lastHotId = -1; + items [item.id] = null; + item.id = -1; + int count = (int)/*64*/OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0); + if (count == 0) { + if (imageList != null) { + OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, 0); + display.releaseToolImageList (imageList); + } + if (hotImageList != null) { + OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, 0); + display.releaseToolHotImageList (hotImageList); + } + if (disabledImageList != null) { + OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, 0); + display.releaseToolDisabledImageList (disabledImageList); + } + imageList = hotImageList = disabledImageList = null; + items = new ToolItem [4]; + } + if ((style & SWT.VERTICAL) != 0) setRowCount (count - 1); + layoutItems (); +} + +void enableWidget (boolean enabled) { + super.enableWidget (enabled); + /* + * Bug in Windows. When a tool item with the style + * BTNS_CHECK or BTNS_CHECKGROUP is selected and then + * disabled, the item does not draw using the disabled + * image. The fix is to use the disabled image in all + * image lists for the item. + * + * Feature in Windows. When a tool bar is disabled, + * the text draws disabled but the images do not. + * The fix is to use the disabled image in all image + * lists for all items. + */ + for (int i=0; i<items.length; i++) { + ToolItem item = items [i]; + if (item != null) { + if ((item.style & SWT.SEPARATOR) == 0) { + item.updateImages (enabled && item.getEnabled ()); + } + } + } +} + +ImageList getDisabledImageList () { + return disabledImageList; +} + +ImageList getHotImageList () { + return hotImageList; +} + +ImageList getImageList () { + return imageList; +} + +/** + * 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 (); + int count = (int)/*64*/OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0); + if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE); + TBBUTTON lpButton = new TBBUTTON (); + int /*long*/ result = OS.SendMessage (handle, OS.TB_GETBUTTON, index, lpButton); + if (result == 0) error (SWT.ERROR_CANNOT_GET_ITEM); + return items [lpButton.idCommand]; +} + +/** + * 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 point) { + checkWidget (); + if (point == null) error (SWT.ERROR_NULL_ARGUMENT); + ToolItem [] items = getItems (); + for (int i=0; i<items.length; i++) { + Rectangle rect = items [i].getBounds (); + if (rect.contains (point)) 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 (int)/*64*/OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0); +} + +/** + * 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 (); + return _getItems (); +} + +ToolItem [] _getItems () { + int count = (int)/*64*/OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0); + TBBUTTON lpButton = new TBBUTTON (); + ToolItem [] result = new ToolItem [count]; + for (int i=0; i<count; i++) { + OS.SendMessage (handle, OS.TB_GETBUTTON, i, lpButton); + result [i] = items [lpButton.idCommand]; + } + 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 (); + if ((style & SWT.VERTICAL) != 0) { + return (int)/*64*/OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0); + } + return (int)/*64*/OS.SendMessage (handle, OS.TB_GETROWS, 0, 0); +} + +ToolItem [] _getTabItemList () { + if (tabItemList == null) return tabItemList; + int count = 0; + for (int i=0; i<tabItemList.length; i++) { + if (!tabItemList [i].isDisposed ()) count++; + } + if (count == tabItemList.length) return tabItemList; + ToolItem [] newList = new ToolItem [count]; + int index = 0; + for (int i=0; i<tabItemList.length; i++) { + if (!tabItemList [i].isDisposed ()) { + newList [index++] = tabItemList [i]; + } + } + tabItemList = newList; + return tabItemList; +} + +/** + * 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); + return (int)/*64*/OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, item.id, 0); +} + +void layoutItems () { + /* + * Feature in Windows. When a tool bar has the style + * TBSTYLE_LIST and has a drop down item, Window leaves + * too much padding around the button. This affects + * every button in the tool bar and makes the preferred + * height too big. The fix is to set the TBSTYLE_LIST + * when the tool bar contains both text and images. + * + * NOTE: Tool bars with CCS_VERT must have TBSTYLE_LIST + * set before any item is added or the tool bar does + * not lay out properly. The work around does not run + * in this case. + */ + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if ((style & SWT.RIGHT) != 0 && (style & SWT.VERTICAL) == 0) { + boolean hasText = false, hasImage = false; + for (int i=0; i<items.length; i++) { + ToolItem item = items [i]; + if (item != null) { + if (!hasText) hasText = item.text.length () != 0; + if (!hasImage) hasImage = item.image != null; + if (hasText && hasImage) break; + } + } + int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits; + if (hasText && hasImage) { + newBits |= OS.TBSTYLE_LIST; + } else { + newBits &= ~OS.TBSTYLE_LIST; + } + if (newBits != oldBits) { + setDropDownItems (false); + OS.SetWindowLong (handle, OS.GWL_STYLE, newBits); + /* + * Feature in Windows. For some reason, when the style + * is changed to TBSTYLE_LIST, Windows does not lay out + * the tool items. The fix is to use WM_SETFONT to force + * the tool bar to redraw and lay out. + */ + int /*long*/ hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0); + setDropDownItems (true); + } + } + } + + if ((style & SWT.WRAP) != 0) { + OS.SendMessage (handle, OS.TB_AUTOSIZE, 0, 0); + } + /* + * When the tool bar is vertical, make the width of each button + * be the width of the widest button in the tool bar. Note that + * when the tool bar contains a drop down item, it needs to take + * into account extra padding. + */ + if ((style & SWT.VERTICAL) != 0) { + int itemCount = (int)/*64*/OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0); + if (itemCount > 1) { + TBBUTTONINFO info = new TBBUTTONINFO (); + info.cbSize = TBBUTTONINFO.sizeof; + info.dwMask = OS.TBIF_SIZE; + int /*long*/ size = OS.SendMessage (handle, OS.TB_GETBUTTONSIZE, 0, 0); + info.cx = (short) OS.LOWORD (size); + int index = 0; + while (index < items.length) { + ToolItem item = items [index]; + if (item != null && (item.style & SWT.DROP_DOWN) != 0) break; + index++; + } + if (index < items.length) { + int /*long*/ padding = OS.SendMessage (handle, OS.TB_GETPADDING, 0, 0); + info.cx += OS.LOWORD (padding) * 2; + } + for (int i=0; i<items.length; i++) { + ToolItem item = items [i]; + if (item != null && (item.style & SWT.SEPARATOR) == 0) { + OS.SendMessage (handle, OS.TB_SETBUTTONINFO, item.id, info); + } + } + } + } + for (int i=0; i<items.length; i++) { + ToolItem item = items [i]; + if (item != null) item.resizeControl (); + } +} + +boolean mnemonicHit (char ch) { + int key = Display.wcsToMbcs (ch); + int [] id = new int [1]; + if (OS.SendMessage (handle, OS.TB_MAPACCELERATOR, key, id) == 0) { + return false; + } + if ((style & SWT.FLAT) != 0 && !setTabGroupFocus ()) return false; + int index = (int)/*64*/OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, id [0], 0); + if (index == -1) return false; + OS.SendMessage (handle, OS.TB_SETHOTITEM, index, 0); + items [id [0]].click (false); + return true; +} + +boolean mnemonicMatch (char ch) { + int key = Display.wcsToMbcs (ch); + int [] id = new int [1]; + if (OS.SendMessage (handle, OS.TB_MAPACCELERATOR, key, id) == 0) { + return false; + } + /* + * Feature in Windows. TB_MAPACCELERATOR matches either the mnemonic + * character or the first character in a tool item. This behavior is + * undocumented and unwanted. The fix is to ensure that the tool item + * contains a mnemonic when TB_MAPACCELERATOR returns true. + */ + int index = (int)/*64*/OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, id [0], 0); + if (index == -1) return false; + return findMnemonic (items [id [0]].text) != '\0'; +} + +void releaseChildren (boolean destroy) { + if (items != null) { + for (int i=0; i<items.length; i++) { + ToolItem item = items [i]; + if (item != null && !item.isDisposed ()) { + item.release (false); + } + } + items = null; + } + super.releaseChildren (destroy); +} + +void releaseWidget () { + super.releaseWidget (); + if (imageList != null) { + OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, 0); + display.releaseToolImageList (imageList); + } + if (hotImageList != null) { + OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, 0); + display.releaseToolHotImageList (hotImageList); + } + if (disabledImageList != null) { + OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, 0); + display.releaseToolDisabledImageList (disabledImageList); + } + imageList = hotImageList = disabledImageList = null; +} + +void removeControl (Control control) { + super.removeControl (control); + for (int i=0; i<items.length; i++) { + ToolItem item = items [i]; + if (item != null && item.control == control) { + item.setControl (null); + } + } +} + +void setBackgroundImage (int /*long*/ hBitmap) { + super.setBackgroundImage (hBitmap); + setBackgroundTransparent (hBitmap != 0); +} + +void setBackgroundPixel (int pixel) { + super.setBackgroundPixel (pixel); + setBackgroundTransparent (pixel != -1); +} + +void setBackgroundTransparent (boolean transparent) { + /* + * Feature in Windows. When TBSTYLE_TRANSPARENT is set + * in a tool bar that is drawing a background, images in + * the image list that include transparency information + * do not draw correctly. The fix is to clear and set + * TBSTYLE_TRANSPARENT depending on the background color. + * + * NOTE: This work around is unnecessary on XP. The + * TBSTYLE_TRANSPARENT style is never cleared on that + * platform. + */ + if ((style & SWT.FLAT) != 0) { + if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if (!transparent && findBackgroundControl () == null) { + bits &= ~OS.TBSTYLE_TRANSPARENT; + } else { + bits |= OS.TBSTYLE_TRANSPARENT; + } + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); + } + } +} + +void setBounds (int x, int y, int width, int height, int flags) { + /* + * Feature in Windows. For some reason, when a tool bar is + * repositioned more than once using DeferWindowPos () into + * the same HDWP, the toolbar redraws more than once, defeating + * the purpose of DeferWindowPos (). The fix is to end the + * deferred positioning before the next tool bar is added, + * ensuring that only one tool bar position is deferred at + * any given time. + */ + if (parent.lpwp != null) { + if (getDrawing () && OS.IsWindowVisible (handle)) { + parent.setResizeChildren (false); + parent.setResizeChildren (true); + } + } + super.setBounds (x, y, width, height, flags); +} + +void setDefaultFont () { + super.setDefaultFont (); + OS.SendMessage (handle, OS.TB_SETBITMAPSIZE, 0, 0); + OS.SendMessage (handle, OS.TB_SETBUTTONSIZE, 0, 0); +} + +void setDropDownItems (boolean set) { + /* + * Feature in Windows. When the first button in a tool bar + * is a drop down item, Window leaves too much padding around + * the button. This affects every button in the tool bar and + * makes the preferred height too big. The fix is clear the + * BTNS_DROPDOWN before Windows lays out the tool bar and set + * the bit afterwards. + * + * NOTE: This work around only runs when the tool bar contains + * only images. + */ + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + boolean hasText = false, hasImage = false; + for (int i=0; i<items.length; i++) { + ToolItem item = items [i]; + if (item != null) { + if (!hasText) hasText = item.text.length () != 0; + if (!hasImage) hasImage = item.image != null; + if (hasText && hasImage) break; + } + } + if (hasImage && !hasText) { + for (int i=0; i<items.length; i++) { + ToolItem item = items [i]; + if (item != null && (item.style & SWT.DROP_DOWN) != 0) { + TBBUTTONINFO info = new TBBUTTONINFO (); + info.cbSize = TBBUTTONINFO.sizeof; + info.dwMask = OS.TBIF_STYLE; + OS.SendMessage (handle, OS.TB_GETBUTTONINFO, item.id, info); + if (set) { + info.fsStyle |= OS.BTNS_DROPDOWN; + } else { + info.fsStyle &= ~OS.BTNS_DROPDOWN; + } + OS.SendMessage (handle, OS.TB_SETBUTTONINFO, item.id, info); + } + } + } + } +} + +void setDisabledImageList (ImageList imageList) { + if (disabledImageList == imageList) return; + int /*long*/ hImageList = 0; + if ((disabledImageList = imageList) != null) { + hImageList = disabledImageList.getHandle (); + } + setDropDownItems (false); + OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, hImageList); + setDropDownItems (true); +} + +public void setFont (Font font) { + checkWidget (); + setDropDownItems (false); + super.setFont (font); + setDropDownItems (true); + /* + * Bug in Windows. When WM_SETFONT is sent to a tool bar + * that contains only separators, causes the bitmap and button + * sizes to be set. The fix is to reset these sizes after the font + * has been changed when the tool bar contains only separators. + */ + int index = 0; + int mask = SWT.PUSH | SWT.CHECK | SWT.RADIO | SWT.DROP_DOWN; + while (index < items.length) { + ToolItem item = items [index]; + if (item != null && (item.style & mask) != 0) break; + index++; + } + if (index == items.length) { + OS.SendMessage (handle, OS.TB_SETBITMAPSIZE, 0, 0); + OS.SendMessage (handle, OS.TB_SETBUTTONSIZE, 0, 0); + } + layoutItems (); +} + +void setHotImageList (ImageList imageList) { + if (hotImageList == imageList) return; + int /*long*/ hImageList = 0; + if ((hotImageList = imageList) != null) { + hImageList = hotImageList.getHandle (); + } + setDropDownItems (false); + OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, hImageList); + setDropDownItems (true); +} + +void setImageList (ImageList imageList) { + if (this.imageList == imageList) return; + int /*long*/ hImageList = 0; + if ((this.imageList = imageList) != null) { + hImageList = imageList.getHandle (); + } + setDropDownItems (false); + OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, hImageList); + setDropDownItems (true); +} + +public boolean setParent (Composite parent) { + checkWidget (); + if (!super.setParent (parent)) return false; + int /*long*/ hwndParent = parent.handle; + OS.SendMessage (handle, OS.TB_SETPARENT, hwndParent, 0); + /* + * Bug in Windows. When a tool bar is reparented, the tooltip + * control that is automatically created for the item is not + * reparented to the new shell. The fix is to move the tooltip + * over using SetWindowLongPtr(). Note that for some reason, + * SetParent() does not work. + */ + int /*long*/ hwndShell = parent.getShell ().handle; + int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.TB_GETTOOLTIPS, 0, 0); + OS.SetWindowLongPtr (hwndToolTip, OS.GWLP_HWNDPARENT, hwndShell); + return true; +} + +public void setRedraw (boolean redraw) { + checkWidget (); + setDropDownItems (false); + super.setRedraw (redraw); + setDropDownItems (true); +} + +void setRowCount (int count) { + if ((style & SWT.VERTICAL) != 0) { + /* + * Feature in Windows. When the TB_SETROWS is used to set the + * number of rows in a tool bar, the tool bar is resized to show + * the items. This is unexpected. The fix is to save and restore + * the current size of the tool bar. + */ + RECT rect = new RECT (); + OS.GetWindowRect (handle, rect); + OS.MapWindowPoints (0, parent.handle, rect, 2); + ignoreResize = true; + /* + * Feature in Windows. When the last button in a tool bar has the + * style BTNS_SEP and TB_SETROWS is used to set the number of rows + * in the tool bar, depending on the number of buttons, the toolbar + * will wrap items with the style BTNS_CHECK, even when the fLarger + * flags is used to force the number of rows to be larger than the + * number of items. The fix is to set the number of rows to be two + * larger than the actual number of rows in the tool bar. When items + * are being added, as long as the number of rows is at least one + * item larger than the count, the tool bar is laid out properly. + * When items are being removed, setting the number of rows to be + * one more than the item count has no effect. The number of rows + * is already one more causing TB_SETROWS to do nothing. Therefore, + * choosing two instead of one as the row increment fixes both cases. + */ + count += 2; + OS.SendMessage (handle, OS.TB_SETROWS, OS.MAKEWPARAM (count, 1), 0); + int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOZORDER; + SetWindowPos (handle, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top, flags); + ignoreResize = false; + } +} + +/*public*/ void setTabItemList (ToolItem [] tabList) { + checkWidget (); + if (tabList != null) { + for (int i=0; i<tabList.length; i++) { + ToolItem item = tabList [i]; + if (item == null) error (SWT.ERROR_INVALID_ARGUMENT); + if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + if (item.parent != this) error (SWT.ERROR_INVALID_PARENT); + } + ToolItem [] newList = new ToolItem [tabList.length]; + System.arraycopy (tabList, 0, newList, 0, tabList.length); + tabList = newList; + } + this.tabItemList = tabList; +} + +boolean setTabItemFocus () { + int index = 0; + while (index < items.length) { + ToolItem item = items [index]; + if (item != null && (item.style & SWT.SEPARATOR) == 0) { + if (item.getEnabled ()) break; + } + index++; + } + if (index == items.length) return false; + return super.setTabItemFocus (); +} + +String toolTipText (NMTTDISPINFO hdr) { + if ((hdr.uFlags & OS.TTF_IDISHWND) != 0) { + return null; + } + /* + * Bug in Windows. On Windows XP, when TB_SETHOTITEM is + * used to set the hot item, the tool bar control attempts + * to display the tool tip, even when the cursor is not in + * the hot item. The fix is to detect this case and fail to + * provide the string, causing no tool tip to be displayed. + */ + if (!hasCursor ()) return ""; //$NON-NLS-1$ + int index = (int)/*64*/hdr.idFrom; + int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.TB_GETTOOLTIPS, 0, 0); + if (hwndToolTip == hdr.hwndFrom) { + /* + * Bug in Windows. For some reason the reading order + * in NMTTDISPINFO is sometimes set incorrectly. The + * reading order seems to change every time the mouse + * enters the control from the top edge. The fix is + * to explicitly set TTF_RTLREADING. + */ + if ((style & SWT.RIGHT_TO_LEFT) != 0) { + hdr.uFlags |= OS.TTF_RTLREADING; + } else { + hdr.uFlags &= ~OS.TTF_RTLREADING; + } + if (toolTipText != null) return ""; //$NON-NLS-1$ + if (0 <= index && index < items.length) { + ToolItem item = items [index]; + if (item != null) { + /* + * But in Windows. When the arrow keys are used to change + * the hot item, for some reason, Windows displays the tool + * tip for the hot item in at (0, 0) on the screen rather + * than next to the current hot item. This fix is to disallow + * tool tips while the user is traversing with the arrow keys. + */ + if (lastArrowId != -1) return ""; + return item.toolTipText; + } + } + } + return super.toolTipText (hdr); +} + +int widgetStyle () { + int bits = super.widgetStyle () | OS.CCS_NORESIZE | OS.TBSTYLE_TOOLTIPS | OS.TBSTYLE_CUSTOMERASE; + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) bits |= OS.TBSTYLE_TRANSPARENT; + if ((style & SWT.SHADOW_OUT) == 0) bits |= OS.CCS_NODIVIDER; + if ((style & SWT.WRAP) != 0) bits |= OS.TBSTYLE_WRAPABLE; + if ((style & SWT.FLAT) != 0) bits |= OS.TBSTYLE_FLAT; + /* + * Feature in Windows. When a tool bar has the style + * TBSTYLE_LIST and has a drop down item, Window leaves + * too much padding around the button. This affects + * every button in the tool bar and makes the preferred + * height too big. The fix is to set the TBSTYLE_LIST + * when the tool bar contains both text and images. + * + * NOTE: Tool bars with CCS_VERT must have TBSTYLE_LIST + * set before any item is added or the tool bar does + * not lay out properly. The work around does not run + * in this case. + */ + if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) { + if ((style & SWT.RIGHT) != 0) bits |= OS.TBSTYLE_LIST; + } + return bits; +} + +TCHAR windowClass () { + return ToolBarClass; +} + +int /*long*/ windowProc () { + return ToolBarProc; +} + +LRESULT WM_CAPTURECHANGED (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_CAPTURECHANGED (wParam, lParam); + if (result != null) return result; + /* + * Bug in Windows. When the tool bar loses capture while an + * item is pressed, the item remains pressed. The fix is + * unpress all items using TB_SETSTATE and TBSTATE_PRESSED. + */ + for (int i=0; i<items.length; i++) { + ToolItem item = items [i]; + if (item != null) { + int fsState = (int)/*64*/OS.SendMessage (handle, OS.TB_GETSTATE, item.id, 0); + if ((fsState & OS.TBSTATE_PRESSED) != 0) { + fsState &= ~OS.TBSTATE_PRESSED; + OS.SendMessage (handle, OS.TB_SETSTATE, item.id, fsState); + } + } + } + return result; +} + +LRESULT WM_CHAR (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_CHAR (wParam, lParam); + if (result != null) return result; + switch ((int)/*64*/wParam) { + case ' ': + int index = (int)/*64*/OS.SendMessage (handle, OS.TB_GETHOTITEM, 0, 0); + if (index != -1) { + TBBUTTON lpButton = new TBBUTTON (); + int /*long*/ code = OS.SendMessage (handle, OS.TB_GETBUTTON, index, lpButton); + if (code != 0) { + items [lpButton.idCommand].click (false); + return LRESULT.ZERO; + } + } + } + return result; +} + +LRESULT WM_COMMAND (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. When the toolbar window + * proc processes WM_COMMAND, it forwards this + * message to its parent. This is done so that + * children of this control that send this message + * type to their parent will notify not only + * this control but also the parent of this control, + * which is typically the application window and + * the window that is looking for the message. + * If the control did not forward the message, + * applications would have to subclass the control + * window to see the message. Because the control + * window is subclassed by SWT, the message + * is delivered twice, once by SWT and once when + * the message is forwarded by the window proc. + * The fix is to avoid calling the window proc + * for this control. + */ + LRESULT result = super.WM_COMMAND (wParam, lParam); + if (result != null) return result; + return LRESULT.ZERO; +} + +LRESULT WM_ERASEBKGND (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_ERASEBKGND (wParam, lParam); + /* + * Bug in Windows. For some reason, NM_CUSTOMDRAW with + * CDDS_PREERASE and CDDS_POSTERASE is never sent for + * versions of Windows earlier than XP. The fix is to + * draw the background in WM_ERASEBKGND; + */ + if (findBackgroundControl () != null) { + if (OS.COMCTL32_MAJOR < 6) { + drawBackground (wParam); + return LRESULT.ONE; + } + } + return result; +} + +LRESULT WM_GETDLGCODE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_GETDLGCODE (wParam, lParam); + /* + * Return DLGC_BUTTON so that mnemonics will be + * processed without needing to press the ALT key + * when the widget has focus. + */ + if (result != null) return result; + return new LRESULT (OS.DLGC_BUTTON | OS.DLGC_WANTARROWS); +} + +LRESULT WM_KEYDOWN (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_KEYDOWN (wParam, lParam); + if (result != null) return result; + switch ((int)/*64*/wParam) { + case OS.VK_SPACE: + /* + * Ensure that the window proc does not process VK_SPACE + * so that it can be handled in WM_CHAR. This allows the + * application the opportunity to cancel the operation. + */ + return LRESULT.ZERO; + } + return result; +} + +LRESULT WM_KILLFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + int index = (int)/*64*/OS.SendMessage (handle, OS.TB_GETHOTITEM, 0, 0); + TBBUTTON lpButton = new TBBUTTON (); + int /*long*/ code = OS.SendMessage (handle, OS.TB_GETBUTTON, index, lpButton); + if (code != 0) lastFocusId = lpButton.idCommand; + return super.WM_KILLFOCUS (wParam, lParam); +} + +LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreMouse) return null; + return super.WM_LBUTTONDOWN (wParam, lParam); +} + +LRESULT WM_LBUTTONUP (int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreMouse) return null; + return super.WM_LBUTTONUP (wParam, lParam); +} + +LRESULT WM_MOUSELEAVE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_MOUSELEAVE (wParam, lParam); + if (result != null) return result; + /* + * Bug in Windows. On XP, when a tooltip is + * hidden due to a time out or mouse press, + * the tooltip remains active although no + * longer visible and won't show again until + * another tooltip becomes active. If there + * is only one tooltip in the window, it will + * never show again. The fix is to remove the + * current tooltip and add it again every time + * the mouse leaves the control. + */ + if (OS.COMCTL32_MAJOR >= 6) { + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.TB_GETTOOLTIPS, 0, 0); + if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) != 0) { + if ((lpti.uFlags & OS.TTF_IDISHWND) == 0) { + OS.SendMessage (hwndToolTip, OS.TTM_DELTOOL, 0, lpti); + OS.SendMessage (hwndToolTip, OS.TTM_ADDTOOL, 0, lpti); + } + } + } + return result; +} + +LRESULT WM_MOUSEMOVE (int /*long*/ wParam, int /*long*/ lParam) { + if (OS.GetMessagePos () != display.lastMouse) lastArrowId = -1; + return super.WM_MOUSEMOVE (wParam, lParam); +} + +LRESULT WM_NOTIFY (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. When the toolbar window + * proc processes WM_NOTIFY, it forwards this + * message to its parent. This is done so that + * children of this control that send this message + * type to their parent will notify not only + * this control but also the parent of this control, + * which is typically the application window and + * the window that is looking for the message. + * If the control did not forward the message, + * applications would have to subclass the control + * window to see the message. Because the control + * window is subclassed by SWT, the message + * is delivered twice, once by SWT and once when + * the message is forwarded by the window proc. + * The fix is to avoid calling the window proc + * for this control. + */ + LRESULT result = super.WM_NOTIFY (wParam, lParam); + if (result != null) return result; + return LRESULT.ZERO; +} + +LRESULT WM_SETFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETFOCUS (wParam, lParam); + if (lastFocusId != -1 && handle == OS.GetFocus ()) { + int index = (int)/*64*/OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, lastFocusId, 0); + OS.SendMessage (handle, OS.TB_SETHOTITEM, index, 0); + } + return result; +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreResize) { + int /*long*/ code = callWindowProc (handle, OS.WM_SIZE, wParam, lParam); + if (code == 0) return LRESULT.ZERO; + return new LRESULT (code); + } + LRESULT result = super.WM_SIZE (wParam, lParam); + if (isDisposed ()) return result; + /* + * Bug in Windows. The code in Windows that determines + * when tool items should wrap seems to use the window + * bounds rather than the client area. Unfortunately, + * tool bars with the style TBSTYLE_EX_HIDECLIPPEDBUTTONS + * use the client area. This means that buttons which + * overlap the border are hidden before they are wrapped. + * The fix is to compute TBSTYLE_EX_HIDECLIPPEDBUTTONS + * and set it each time the tool bar is resized. + */ + if ((style & SWT.BORDER) != 0 && (style & SWT.WRAP) != 0) { + RECT windowRect = new RECT (); + OS.GetWindowRect (handle, windowRect); + int index = 0, border = getBorderWidth () * 2; + RECT rect = new RECT (); + int count = (int)/*64*/OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0); + while (index < count) { + OS.SendMessage (handle, OS.TB_GETITEMRECT, index, rect); + OS.MapWindowPoints (handle, 0, rect, 2); + if (rect.right > windowRect.right - border * 2) break; + index++; + } + int bits = (int)/*64*/OS.SendMessage (handle, OS.TB_GETEXTENDEDSTYLE, 0, 0); + if (index == count) { + bits |= OS.TBSTYLE_EX_HIDECLIPPEDBUTTONS; + } else { + bits &= ~OS.TBSTYLE_EX_HIDECLIPPEDBUTTONS; + } + OS.SendMessage (handle, OS.TB_SETEXTENDEDSTYLE, 0, bits); + } + layoutItems (); + return result; +} + +LRESULT WM_WINDOWPOSCHANGING (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_WINDOWPOSCHANGING (wParam, lParam); + if (result != null) return result; + if (ignoreResize) return result; + /* + * Bug in Windows. When a flat tool bar is wrapped, + * Windows draws a horizontal separator between the + * rows. The tool bar does not draw the first or + * the last two pixels of this separator. When the + * toolbar is resized to be bigger, only the new + * area is drawn and the last two pixels, which are + * blank are drawn over by separator. This leaves + * garbage on the screen. The fix is to damage the + * pixels. + */ + if (!getDrawing ()) return result; + if ((style & SWT.WRAP) == 0) return result; + if (!OS.IsWindowVisible (handle)) return result; + if (OS.SendMessage (handle, OS.TB_GETROWS, 0, 0) == 1) { + return result; + } + WINDOWPOS lpwp = new WINDOWPOS (); + OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof); + if ((lpwp.flags & (OS.SWP_NOSIZE | OS.SWP_NOREDRAW)) != 0) { + return result; + } + RECT oldRect = new RECT (); + OS.GetClientRect (handle, oldRect); + RECT newRect = new RECT (); + OS.SetRect (newRect, 0, 0, lpwp.cx, lpwp.cy); + OS.SendMessage (handle, OS.WM_NCCALCSIZE, 0, newRect); + int oldWidth = oldRect.right - oldRect.left; + int newWidth = newRect.right - newRect.left; + if (newWidth > oldWidth) { + RECT rect = new RECT (); + int newHeight = newRect.bottom - newRect.top; + OS.SetRect (rect, oldWidth - 2, 0, oldWidth, newHeight); + OS.InvalidateRect (handle, rect, false); + } + return result; +} + +LRESULT wmCommandChild (int /*long*/ wParam, int /*long*/ lParam) { + ToolItem child = items [OS.LOWORD (wParam)]; + if (child == null) return null; + return child.wmCommandChild (wParam, lParam); +} + +LRESULT wmNotifyChild (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + switch (hdr.code) { + case OS.TBN_DROPDOWN: + NMTOOLBAR lpnmtb = new NMTOOLBAR (); + OS.MoveMemory (lpnmtb, lParam, NMTOOLBAR.sizeof); + ToolItem child = items [lpnmtb.iItem]; + if (child != null) { + Event event = new Event (); + event.detail = SWT.ARROW; + int index = (int)/*64*/OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, lpnmtb.iItem, 0); + RECT rect = new RECT (); + OS.SendMessage (handle, OS.TB_GETITEMRECT, index, rect); + event.x = rect.left; + event.y = rect.bottom; + child.postEvent (SWT.Selection, event); + } + break; + case OS.NM_CUSTOMDRAW: + if (OS.COMCTL32_MAJOR < 6) break; + /* + * Bug in Windows. For some reason, under the XP Silver + * theme, tool bars continue to draw using the gray color + * from the default Blue theme. The fix is to draw the + * background. + */ + NMCUSTOMDRAW nmcd = new NMCUSTOMDRAW (); + OS.MoveMemory (nmcd, lParam, NMCUSTOMDRAW.sizeof); +// if (drawCount != 0 || !OS.IsWindowVisible (handle)) { +// if (!OS.IsWinCE && OS.WindowFromDC (nmcd.hdc) == handle) break; +// } + switch (nmcd.dwDrawStage) { + case OS.CDDS_PREERASE: { + /* + * Bug in Windows. When a tool bar does not have the style + * TBSTYLE_FLAT, the rectangle to be erased in CDDS_PREERASE + * is empty. The fix is to draw the whole client area. + */ + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.TBSTYLE_FLAT) == 0) { + drawBackground (nmcd.hdc); + } else { + RECT rect = new RECT (); + OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom); + drawBackground (nmcd.hdc, rect); + } + return new LRESULT (OS.CDRF_SKIPDEFAULT); + } + } + break; + case OS.TBN_HOTITEMCHANGE: + if (!OS.IsWinCE) { + NMTBHOTITEM lpnmhi = new NMTBHOTITEM (); + OS.MoveMemory (lpnmhi, lParam, NMTBHOTITEM.sizeof); + switch (lpnmhi.dwFlags) { + case OS.HICF_MOUSE: { + /* + * But in Windows. When the tool bar has focus, a mouse is + * in an item and hover help for that item is displayed and + * then the arrow keys are used to change the hot item, + * for some reason, Windows snaps the hot item back to the + * one that is under the mouse. The fix is to disallow + * hot item changes when the user is traversing using the + * arrow keys. + */ + if (lastArrowId != -1) return LRESULT.ONE; + break; + } + case OS.HICF_ARROWKEYS: { + RECT client = new RECT (); + OS.GetClientRect (handle, client); + int index = (int)/*64*/OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, lpnmhi.idNew, 0); + RECT rect = new RECT (); + OS.SendMessage (handle, OS.TB_GETITEMRECT, index, rect); + if (rect.right > client.right || rect.bottom > client.bottom) { + return LRESULT.ONE; + } + lastArrowId = lpnmhi.idNew; + break; + } + default: + lastArrowId = -1; + } + if ((lpnmhi.dwFlags & OS.HICF_LEAVING) == 0) { + lastHotId = lpnmhi.idNew; + } + } + break; + } + return super.wmNotifyChild (hdr, wParam, lParam); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolItem.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolItem.java new file mode 100755 index 0000000000..27386a4545 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolItem.java @@ -0,0 +1,1006 @@ +/******************************************************************************* + * 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.win32.*; +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 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 { + ToolBar parent; + Control control; + String toolTipText; + Image disabledImage, hotImage; + Image disabledImage2; + int id; + +/** + * 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); +} + +/** + * 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); +} + +void click (boolean dropDown) { + int /*long*/ hwnd = parent.handle; + if (OS.GetKeyState (OS.VK_LBUTTON) < 0) return; + int index = (int)/*64*/OS.SendMessage (hwnd, OS.TB_COMMANDTOINDEX, id, 0); + RECT rect = new RECT (); + OS.SendMessage (hwnd, OS.TB_GETITEMRECT, index, rect); + int hotIndex = (int)/*64*/OS.SendMessage (hwnd, OS.TB_GETHOTITEM, 0, 0); + + /* + * In order to emulate all the processing that + * happens when a mnemonic key is pressed, fake + * a mouse press and release. This will ensure + * that radio and pull down items are handled + * properly. + */ + int y = rect.top + (rect.bottom - rect.top) / 2; + int /*long*/ lParam = OS.MAKELPARAM (dropDown ? rect.right - 1 : rect.left, y); + parent.ignoreMouse = true; + OS.SendMessage (hwnd, OS.WM_LBUTTONDOWN, 0, lParam); + OS.SendMessage (hwnd, OS.WM_LBUTTONUP, 0, lParam); + parent.ignoreMouse = false; + + if (hotIndex != -1) { + OS.SendMessage (hwnd, OS.TB_SETHOTITEM, hotIndex, 0); + } +} + +Widget [] computeTabList () { + if (isTabGroup ()) { + if (getEnabled ()) { + if ((style & SWT.SEPARATOR) != 0) { + if (control != null) return control.computeTabList(); + } else { + return new Widget [] {this}; + } + } + } + return new Widget [0]; +} + +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> + */ +public Rectangle getBounds () { + checkWidget(); + int /*long*/ hwnd = parent.handle; + int index = (int)/*64*/OS.SendMessage (hwnd, OS.TB_COMMANDTOINDEX, id, 0); + RECT rect = new RECT (); + OS.SendMessage (hwnd, OS.TB_GETITEMRECT, index, rect); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + return new Rectangle (rect.left, rect.top, width, height); +} + +/** + * 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; +} + +/** + * 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(); + if ((style & SWT.SEPARATOR) != 0) { + return (state & DISABLED) == 0; + } + int /*long*/ hwnd = parent.handle; + int /*long*/ fsState = OS.SendMessage (hwnd, OS.TB_GETSTATE, id, 0); + return (fsState & OS.TBSTATE_ENABLED) != 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; + int /*long*/ hwnd = parent.handle; + int /*long*/ fsState = OS.SendMessage (hwnd, OS.TB_GETSTATE, id, 0); + return (fsState & OS.TBSTATE_CHECKED) != 0; +} + +/** + * 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(); + int /*long*/ hwnd = parent.handle; + int index = (int)/*64*/OS.SendMessage (hwnd, OS.TB_COMMANDTOINDEX, id, 0); + RECT rect = new RECT (); + OS.SendMessage (hwnd, OS.TB_GETITEMRECT, index, rect); + return rect.right - rect.left; +} + +/** + * 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 isTabGroup () { + ToolItem [] tabList = parent._getTabItemList (); + if (tabList != null) { + for (int i=0; i<tabList.length; i++) { + if (tabList [i] == this) return true; + } + } + if ((style & SWT.SEPARATOR) != 0) return true; + int index = parent.indexOf (this); + if (index == 0) return true; + ToolItem previous = parent.getItem (index - 1); + return (previous.getStyle () & SWT.SEPARATOR) != 0; +} + +void releaseWidget () { + super.releaseWidget (); + releaseImages (); + control = null; + toolTipText = null; + disabledImage = hotImage = null; + if (disabledImage2 != null) disabledImage2.dispose (); + disabledImage2 = null; +} + +void releaseHandle () { + super.releaseHandle (); + parent = null; + id = -1; +} + +void releaseImages () { + TBBUTTONINFO info = new TBBUTTONINFO (); + info.cbSize = TBBUTTONINFO.sizeof; + info.dwMask = OS.TBIF_IMAGE | OS.TBIF_STYLE; + int /*long*/ hwnd = parent.handle; + OS.SendMessage (hwnd, OS.TB_GETBUTTONINFO, id, info); + /* + * Feature in Windows. For some reason, a tool item that has + * the style BTNS_SEP does not return I_IMAGENONE when queried + * for an image index, despite the fact that no attempt has been + * made to assign an image to the item. As a result, operations + * on an image list that use the wrong index cause random results. + * The fix is to ensure that the tool item is not a separator + * before using the image index. Since separators cannot have + * an image and one is never assigned, this is not a problem. + */ + if ((info.fsStyle & OS.BTNS_SEP) == 0 && info.iImage != OS.I_IMAGENONE) { + ImageList imageList = parent.getImageList (); + ImageList hotImageList = parent.getHotImageList (); + ImageList disabledImageList = parent.getDisabledImageList(); + if (imageList != null) imageList.put (info.iImage, null); + if (hotImageList != null) hotImageList.put (info.iImage, null); + if (disabledImageList != null) disabledImageList.put (info.iImage, 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 resizeControl () { + if (control != null && !control.isDisposed ()) { + /* + * Set the size and location of the control + * separately to minimize flashing in the + * case where the control does not resize + * to the size that was requested. This + * case can occur when the control is a + * combo box. + */ + Rectangle itemRect = getBounds (); + control.setSize (itemRect.width, itemRect.height); + Rectangle rect = control.getBounds (); + rect.x = itemRect.x + (itemRect.width - rect.width) / 2; + rect.y = itemRect.y + (itemRect.height - rect.height) / 2; + control.setLocation (rect.x, rect.y); + } +} + +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); +} + +/** + * 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; + this.control = control; + /* + * Feature in Windows. When a tool bar wraps, tool items + * with the style BTNS_SEP are used as wrap points. This + * means that controls that are placed on top of separator + * items are not positioned properly. Also, vertical tool + * bars are implemented using TB_SETROWS to set the number + * of rows. When a control is placed on top of a separator, + * the height of the separator does not grow. The fix in + * both cases is to change the tool item style from BTNS_SEP + * to BTNS_BUTTON, causing the item to wrap like a tool item + * button. The new tool item button is disabled to avoid key + * traversal and the image is set to I_IMAGENONE to avoid + * getting the first image from the image list. + */ + if ((parent.style & (SWT.WRAP | SWT.VERTICAL)) != 0) { + boolean changed = false; + int /*long*/ hwnd = parent.handle; + TBBUTTONINFO info = new TBBUTTONINFO (); + info.cbSize = TBBUTTONINFO.sizeof; + info.dwMask = OS.TBIF_STYLE | OS.TBIF_STATE; + OS.SendMessage (hwnd, OS.TB_GETBUTTONINFO, id, info); + if (control == null) { + if ((info.fsStyle & OS.BTNS_SEP) == 0) { + changed = true; + info.fsStyle &= ~(OS.BTNS_BUTTON | OS.BTNS_SHOWTEXT); + info.fsStyle |= OS.BTNS_SEP; + if ((state & DISABLED) != 0) { + info.fsState &= ~OS.TBSTATE_ENABLED; + } else { + info.fsState |= OS.TBSTATE_ENABLED; + } + } + } else { + if ((info.fsStyle & OS.BTNS_SEP) != 0) { + changed = true; + info.fsStyle &= ~OS.BTNS_SEP; + info.fsStyle |= OS.BTNS_BUTTON | OS.BTNS_SHOWTEXT; + info.fsState &= ~OS.TBSTATE_ENABLED; + info.dwMask |= OS.TBIF_IMAGE; + info.iImage = OS.I_IMAGENONE; + } + } + if (changed) { + OS.SendMessage (hwnd, OS.TB_SETBUTTONINFO, id, info); + /* + * Bug in Windows. When TB_SETBUTTONINFO changes the + * style of a tool item from BTNS_SEP to BTNS_BUTTON + * and the tool bar is wrapped, the tool bar does not + * redraw properly. Windows uses separator items as + * wrap points and sometimes draws etching above or + * below and entire row. The fix is to redraw the + * tool bar. + */ + if (OS.SendMessage (hwnd, OS.TB_GETROWS, 0, 0) > 1) { + OS.InvalidateRect (hwnd, null, true); + } + } + } + resizeControl (); +} + +/** + * 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(); + int /*long*/ hwnd = parent.handle; + int fsState = (int)/*64*/OS.SendMessage (hwnd, OS.TB_GETSTATE, id, 0); + /* + * Feature in Windows. When TB_SETSTATE is used to set the + * state of a tool item, the item redraws even when the state + * has not changed. The fix is to detect this case and avoid + * setting the state. + */ + if (((fsState & OS.TBSTATE_ENABLED) != 0) == enabled) return; + if (enabled) { + fsState |= OS.TBSTATE_ENABLED; + state &= ~DISABLED; + } else { + fsState &= ~OS.TBSTATE_ENABLED; + state |= DISABLED; + } + OS.SendMessage (hwnd, OS.TB_SETSTATE, id, fsState); + if ((style & SWT.SEPARATOR) == 0) { + if (image != null) updateImages (enabled && parent.getEnabled ()); + } +} + +/** + * 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 ((style & SWT.SEPARATOR) != 0) return; + if (image != null && image.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + disabledImage = image; + updateImages (getEnabled () && parent.getEnabled ()); +} + +/** + * 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 ((style & SWT.SEPARATOR) != 0) return; + if (image != null && image.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + hotImage = image; + updateImages (getEnabled () && parent.getEnabled ()); +} + +public void setImage (Image image) { + checkWidget(); + if ((style & SWT.SEPARATOR) != 0) return; + if (image != null && image.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + super.setImage (image); + updateImages (getEnabled () && parent.getEnabled ()); +} + +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; + int /*long*/ hwnd = parent.handle; + int fsState = (int)/*64*/OS.SendMessage (hwnd, OS.TB_GETSTATE, id, 0); + /* + * Feature in Windows. When TB_SETSTATE is used to set the + * state of a tool item, the item redraws even when the state + * has not changed. The fix is to detect this case and avoid + * setting the state. + */ + if (((fsState & OS.TBSTATE_CHECKED) != 0) == selected) return; + if (selected) { + fsState |= OS.TBSTATE_CHECKED; + } else { + fsState &= ~OS.TBSTATE_CHECKED; + } + OS.SendMessage (hwnd, OS.TB_SETSTATE, id, fsState); + + /* + * Bug in Windows. When a tool item with the style + * BTNS_CHECK or BTNS_CHECKGROUP is selected and then + * disabled, the item does not draw using the disabled + * image. The fix is to use the disabled image in all + * image lists for the item. + * + * NOTE: This means that the image list must be updated + * when the selection changes in a disabled tool item. + */ + if ((style & (SWT.CHECK | SWT.RADIO)) != 0) { + if (!getEnabled () || !parent.getEnabled ()) { + updateImages (false); + } + } +} + +boolean setTabItemFocus () { + if (parent.setTabItemFocus ()) { + int /*long*/ hwnd = parent.handle; + int index = (int)/*64*/OS.SendMessage (hwnd, OS.TB_COMMANDTOINDEX, id, 0); + OS.SendMessage (hwnd, OS.TB_SETHOTITEM, index, 0); + return true; + } + return false; +} + +/** + * 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; + if (string.equals (text)) return; + super.setText (string); + int /*long*/ hwnd = parent.handle; + TBBUTTONINFO info = new TBBUTTONINFO (); + info.cbSize = TBBUTTONINFO.sizeof; + info.dwMask = OS.TBIF_TEXT | OS.TBIF_STYLE; + info.fsStyle = (byte) (widgetStyle () | OS.BTNS_AUTOSIZE); + int /*long*/ hHeap = OS.GetProcessHeap (), pszText = 0; + if (string.length () != 0) { + info.fsStyle |= OS.BTNS_SHOWTEXT; + TCHAR buffer = new TCHAR (parent.getCodePage (), string, true); + int byteCount = buffer.length () * TCHAR.sizeof; + pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (pszText, buffer, byteCount); + info.pszText = pszText; + } + OS.SendMessage (hwnd, OS.TB_SETBUTTONINFO, id, info); + if (pszText != 0) OS.HeapFree (hHeap, 0, pszText); + + /* + * Bug in Windows. For some reason, when the font is set + * before any tool item has text, the tool items resize to + * a very small size. Also, a tool item will only show text + * when text has already been set on one item and then a new + * item is created. The fix is to use WM_SETFONT to force + * the tool bar to redraw and layout. + */ + parent.setDropDownItems (false); + int /*long*/ hFont = OS.SendMessage (hwnd, OS.WM_GETFONT, 0, 0); + OS.SendMessage (hwnd, OS.WM_SETFONT, hFont, 0); + parent.setDropDownItems (true); + parent.layoutItems (); +} + +/** + * 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; +} + +/** + * 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) return; + int /*long*/ hwnd = parent.handle; + TBBUTTONINFO info = new TBBUTTONINFO (); + info.cbSize = TBBUTTONINFO.sizeof; + info.dwMask = OS.TBIF_SIZE; + info.cx = (short) width; + OS.SendMessage (hwnd, OS.TB_SETBUTTONINFO, id, info); + parent.layoutItems (); +} + +void updateImages (boolean enabled) { + if ((style & SWT.SEPARATOR) != 0) return; + int /*long*/ hwnd = parent.handle; + TBBUTTONINFO info = new TBBUTTONINFO (); + info.cbSize = TBBUTTONINFO.sizeof; + info.dwMask = OS.TBIF_IMAGE; + OS.SendMessage (hwnd, OS.TB_GETBUTTONINFO, id, info); + if (info.iImage == OS.I_IMAGENONE && image == null) return; + ImageList imageList = parent.getImageList (); + ImageList hotImageList = parent.getHotImageList (); + ImageList disabledImageList = parent.getDisabledImageList(); + if (info.iImage == OS.I_IMAGENONE) { + Rectangle bounds = image.getBounds (); + int listStyle = parent.style & SWT.RIGHT_TO_LEFT; + if (imageList == null) { + imageList = display.getImageListToolBar (listStyle, bounds.width, bounds.height); + } + if (disabledImageList == null) { + disabledImageList = display.getImageListToolBarDisabled (listStyle, bounds.width, bounds.height); + } + if (hotImageList == null) { + hotImageList = display.getImageListToolBarHot (listStyle, bounds.width, bounds.height); + } + Image disabled = disabledImage; + if (disabledImage == null) { + if (disabledImage2 != null) disabledImage2.dispose (); + disabledImage2 = null; + disabled = image; + if (!enabled) { + disabled = disabledImage2 = new Image (display, image, SWT.IMAGE_DISABLE); + } + } + /* + * Bug in Windows. When a tool item with the style + * BTNS_CHECK or BTNS_CHECKGROUP is selected and then + * disabled, the item does not draw using the disabled + * image. The fix is to assign the disabled image in + * all image lists. + */ + Image image2 = image, hot = hotImage; + if ((style & (SWT.CHECK | SWT.RADIO)) != 0) { + if (!enabled) image2 = hot = disabled; + } + info.iImage = imageList.add (image2); + disabledImageList.add (disabled); + hotImageList.add (hot != null ? hot : image2); + parent.setImageList (imageList); + parent.setDisabledImageList (disabledImageList); + parent.setHotImageList (hotImageList); + } else { + Image disabled = null; + if (disabledImageList != null) { + if (image != null) { + if (disabledImage2 != null) disabledImage2.dispose (); + disabledImage2 = null; + disabled = disabledImage; + if (disabledImage == null) { + disabled = image; + if (!enabled) { + disabled = disabledImage2 = new Image (display, image, SWT.IMAGE_DISABLE); + } + } + } + disabledImageList.put (info.iImage, disabled); + } + /* + * Bug in Windows. When a tool item with the style + * BTNS_CHECK or BTNS_CHECKGROUP is selected and then + * disabled, the item does not draw using the disabled + * image. The fix is to use the disabled image in all + * image lists. + */ + Image image2 = image, hot = hotImage; + if ((style & (SWT.CHECK | SWT.RADIO)) != 0) { + if (!enabled) image2 = hot = disabled; + } + if (imageList != null) imageList.put (info.iImage, image2); + if (hotImageList != null) { + hotImageList.put (info.iImage, hot != null ? hot : image2); + } + if (image == null) info.iImage = OS.I_IMAGENONE; + } + + /* + * Bug in Windows. If the width of an item has already been + * calculated, the tool bar control will not recalculate it to + * include the space for the image. The fix is to set the width + * to zero, forcing the control recalculate the width for the item. + */ + info.dwMask |= OS.TBIF_SIZE; + info.cx = 0; + OS.SendMessage (hwnd, OS.TB_SETBUTTONINFO, id, info); + + parent.layoutItems (); +} + +int widgetStyle () { + if ((style & SWT.DROP_DOWN) != 0) return OS.BTNS_DROPDOWN; + if ((style & SWT.PUSH) != 0) return OS.BTNS_BUTTON; + if ((style & SWT.CHECK) != 0) return OS.BTNS_CHECK; + /* + * This code is intentionally commented. In order to + * consistently support radio tool items across platforms, + * the platform radio behavior is not used. + */ +// if ((style & SWT.RADIO) != 0) return OS.BTNS_CHECKGROUP; + if ((style & SWT.RADIO) != 0) return OS.BTNS_CHECK; + if ((style & SWT.SEPARATOR) != 0) return OS.BTNS_SEP; + return OS.BTNS_BUTTON; +} + +LRESULT wmCommandChild (int /*long*/ wParam, int /*long*/ lParam) { + if ((style & SWT.RADIO) != 0) { + if ((parent.getStyle () & SWT.NO_RADIO_GROUP) == 0) { + selectRadio (); + } + } + postEvent (SWT.Selection); + return null; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolTip.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolTip.java new file mode 100644 index 0000000000..87a7355a3d --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolTip.java @@ -0,0 +1,555 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; + +/** + * Instances of this class represent popup windows that are used + * to inform or warn the user. + * <p> + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>BALLOON, ICON_ERROR, ICON_INFORMATION, ICON_WARNING</dd> + * <dt><b>Events:</b></dt> + * <dd>Selection</dd> + * </dl> + * </p><p> + * Note: Only one of the styles ICON_ERROR, ICON_INFORMATION, + * and ICON_WARNING 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/#tooltips">Tool Tips 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.2 + * @noextend This class is not intended to be subclassed by clients. + */ + +public class ToolTip extends Widget { + Shell parent; + TrayItem item; + String text = "", message = ""; + int id, x, y; + boolean autoHide = true, hasLocation, visible; + static final int TIMER_ID = 100; + +/** + * 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#BALLOON + * @see SWT#ICON_ERROR + * @see SWT#ICON_INFORMATION + * @see SWT#ICON_WARNING + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public ToolTip (Shell parent, int style) { + super (parent, checkStyle (style)); + this.parent = parent; + checkOrientation (parent); + parent.createToolTip (this); +} + +static int checkStyle (int style) { + int mask = SWT.ICON_ERROR | SWT.ICON_INFORMATION | SWT.ICON_WARNING; + if ((style & mask) == 0) return style; + return checkBits (style, SWT.ICON_INFORMATION, SWT.ICON_WARNING, SWT.ICON_ERROR, 0, 0, 0); +} + +/** + * 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 not called. + * </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); +} + +void destroyWidget () { + if (parent != null) parent.destroyToolTip (this); + releaseHandle (); +} + +/** + * Returns <code>true</code> if the receiver is automatically + * hidden by the platform, and <code>false</code> otherwise. + * + * @return the receiver's auto hide 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 getAutoHide () { + checkWidget(); + return autoHide; +} + +/** + * Returns the receiver's message, which will be an empty + * string if it has never been set. + * + * @return the receiver's 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> + */ +public String getMessage () { + checkWidget(); + return message; +} + +/** + * Returns the receiver's parent, which must be a <code>Shell</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 Shell getParent () { + checkWidget (); + return parent; +} + +/** + * 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; +} + +/** + * 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 (OS.IsWinCE) return false; + if (item != null) return visible; + int /*long*/ hwndToolTip = hwndToolTip (); + if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, 0) != 0) { + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) != 0) { + return (lpti.uFlags & OS.TTF_IDISHWND) == 0 && lpti.uId == id; + } + } + return false; +} + +int /*long*/ hwndToolTip () { + return (style & SWT.BALLOON) != 0 ? parent.balloonTipHandle () : parent.toolTipHandle (); +} + +/** + * 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 (); + if (item != null) return getVisible () && item.getVisible (); + return getVisible (); +} + +void releaseHandle () { + super.releaseHandle (); + parent = null; + item = null; + id = -1; +} + +void releaseWidget () { + super.releaseWidget (); + if (item == null) { + if (autoHide) { + int /*long*/ hwndToolTip = hwndToolTip (); + if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, 0) != 0) { + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) != 0) { + if ((lpti.uFlags & OS.TTF_IDISHWND) == 0) { + if (lpti.uId == id) { + OS.SendMessage (hwndToolTip, OS.TTM_TRACKACTIVATE, 0, lpti); + OS.SendMessage (hwndToolTip, OS.TTM_POP, 0, 0); + OS.KillTimer (hwndToolTip, TIMER_ID); + } + } + } + } + } + } + if (item != null && item.toolTip == this) { + item.toolTip = null; + } + item = null; + text = message = null; +} + +/** + * 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); +} + +/** + * Makes the receiver hide automatically when <code>true</code>, + * and remain visible when <code>false</code>. + * + * @param autoHide the auto hide 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 + * @see #setVisible + */ +public void setAutoHide (boolean autoHide) { + checkWidget (); + this.autoHide = autoHide; + //TODO - update when visible +} + +/** + * Sets the location of the receiver, which must be a tooltip, + * 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> + * + * @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; + //TODO - update when visible +} + +/** + * Sets the location of the receiver, which must be a tooltip, + * 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 tooltips. + * </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> + */ +public void setLocation (Point location) { + checkWidget (); + if (location == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + setLocation (location.x, location.y); +} + +/** + * Sets the receiver's message. + * + * @param string the new message + * + * @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 setMessage (String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + message = string; + //TODO - update when visible +} + +/** + * Sets the receiver's text. + * + * @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; + //TODO - update when visible +} + +/** + * 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 (OS.IsWinCE) return; + if (visible == getVisible ()) return; + if (item == null) { + int /*long*/ hwnd = parent.handle; + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + lpti.uId = id; + lpti.hwnd = hwnd; + int /*long*/ hwndToolTip = hwndToolTip (); + Shell shell = parent.getShell (); + if (text.length () != 0) { + int icon = OS.TTI_NONE; + if ((style & SWT.ICON_INFORMATION) != 0) icon = OS.TTI_INFO; + if ((style & SWT.ICON_WARNING) != 0) icon = OS.TTI_WARNING; + if ((style & SWT.ICON_ERROR) != 0) icon = OS.TTI_ERROR; + shell.setToolTipTitle (hwndToolTip, text, icon); + } else { + shell.setToolTipTitle (hwndToolTip, null, 0); + } + int maxWidth = 0; + if (OS.IsWinCE || OS.WIN32_VERSION < OS.VERSION (4, 10)) { + RECT rect = new RECT (); + OS.SystemParametersInfo (OS.SPI_GETWORKAREA, 0, rect, 0); + maxWidth = (rect.right - rect.left) / 4; + } else { + int /*long*/ hmonitor = OS.MonitorFromWindow (hwnd, OS.MONITOR_DEFAULTTONEAREST); + MONITORINFO lpmi = new MONITORINFO (); + lpmi.cbSize = MONITORINFO.sizeof; + OS.GetMonitorInfo (hmonitor, lpmi); + maxWidth = (lpmi.rcWork_right - lpmi.rcWork_left) / 4; + } + OS.SendMessage (hwndToolTip, OS.TTM_SETMAXTIPWIDTH, 0, maxWidth); + if (visible) { + int nX = x, nY = y; + if (!hasLocation) { + POINT pt = new POINT (); + if (OS.GetCursorPos (pt)) { + nX = pt.x; + nY = pt.y; + } + } + int /*long*/ lParam = OS.MAKELPARAM (nX, nY); + OS.SendMessage (hwndToolTip, OS.TTM_TRACKPOSITION, 0, lParam); + + /* + * Feature in Windows. Windows will not show a tool tip + * if the cursor is outside the parent window (even on XP, + * TTM_POPUP will not do this). The fix is to temporarily + * move the cursor into the tool window, show the tool tip, + * and then restore the cursor. + */ + POINT pt = new POINT (); + OS.GetCursorPos (pt); + RECT rect = new RECT (); + OS.GetClientRect (hwnd, rect); + OS.MapWindowPoints (hwnd, 0, rect, 2); + if (!OS.PtInRect (rect, pt)) { + int /*long*/ hCursor = OS.GetCursor (); + OS.SetCursor (0); + OS.SetCursorPos (rect.left, rect.top); + OS.SendMessage (hwndToolTip, OS.TTM_TRACKACTIVATE, 1, lpti); + OS.SetCursorPos (pt.x, pt.y); + OS.SetCursor (hCursor); + } else { + OS.SendMessage (hwndToolTip, OS.TTM_TRACKACTIVATE, 1, lpti); + } + + int time = (int)/*64*/OS.SendMessage (hwndToolTip, OS.TTM_GETDELAYTIME, OS.TTDT_AUTOPOP, 0); + OS.SetTimer (hwndToolTip, TIMER_ID, time, 0); + } else { + OS.SendMessage (hwndToolTip, OS.TTM_TRACKACTIVATE, 0, lpti); + OS.SendMessage (hwndToolTip, OS.TTM_POP, 0, 0); + OS.KillTimer (hwndToolTip, TIMER_ID); + } + return; + } + if (item != null && OS.SHELL32_MAJOR >= 5) { + if (visible) { + NOTIFYICONDATA iconData = OS.IsUnicode ? (NOTIFYICONDATA) new NOTIFYICONDATAW () : new NOTIFYICONDATAA (); + TCHAR buffer1 = new TCHAR (0, text, true); + TCHAR buffer2 = new TCHAR (0, message, true); + if (OS.IsUnicode) { + char [] szInfoTitle = ((NOTIFYICONDATAW) iconData).szInfoTitle; + int length1 = Math.min (szInfoTitle.length - 1, buffer1.length ()); + System.arraycopy (buffer1.chars, 0, szInfoTitle, 0, length1); + char [] szInfo = ((NOTIFYICONDATAW) iconData).szInfo; + int length2 = Math.min (szInfo.length - 1, buffer2.length ()); + System.arraycopy (buffer2.chars, 0, szInfo, 0, length2); + } else { + byte [] szInfoTitle = ((NOTIFYICONDATAA) iconData).szInfoTitle; + int length = Math.min (szInfoTitle.length - 1, buffer1.length ()); + System.arraycopy (buffer1.bytes, 0, szInfoTitle, 0, length); + byte [] szInfo = ((NOTIFYICONDATAA) iconData).szInfo; + int length2 = Math.min (szInfo.length - 1, buffer2.length ()); + System.arraycopy (buffer2.bytes, 0, szInfo, 0, length2); + } + Display display = item.getDisplay (); + iconData.cbSize = NOTIFYICONDATA.sizeof; + iconData.uID = item.id; + iconData.hWnd = display.hwndMessage; + iconData.uFlags = OS.NIF_INFO; + if ((style & SWT.ICON_INFORMATION) != 0) iconData.dwInfoFlags = OS.NIIF_INFO; + if ((style & SWT.ICON_WARNING) != 0) iconData.dwInfoFlags = OS.NIIF_WARNING; + if ((style & SWT.ICON_ERROR) != 0) iconData.dwInfoFlags = OS.NIIF_ERROR; + sendEvent (SWT.Show); + this.visible = OS.Shell_NotifyIcon (OS.NIM_MODIFY, iconData); + } else { + //TODO - hide the tray item + } + } +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tracker.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tracker.java new file mode 100755 index 0000000000..f45e58c725 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tracker.java @@ -0,0 +1,1188 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; + +/** + * 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; + Rectangle [] rectangles = new Rectangle [0], proportions = rectangles; + Rectangle bounds; + int /*long*/ resizeCursor; + Cursor clientCursor; + int cursorOrientation = SWT.NONE; + boolean inEvent = false; + int /*long*/ hwndTransparent, hwndOpaque, oldTransparentProc, oldOpaqueProc; + 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; + POINT pt = new POINT (); + pt.x = newX; pt.y = newY; + /* + * Convert to screen coordinates iff needed + */ + if (parent != null) { + OS.ClientToScreen (parent.handle, pt); + } + OS.SetCursorPos (pt.x, pt.y); + return new Point (pt.x, pt.y); +} + +Point adjustResizeCursor () { + 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; + } + + POINT pt = new POINT (); + pt.x = newX; pt.y = newY; + /* + * Convert to screen coordinates iff needed + */ + if (parent != null) { + OS.ClientToScreen (parent.handle, pt); + } + OS.SetCursorPos (pt.x, pt.y); + + /* + * If the client has not provided a custom cursor then determine + * the appropriate resize cursor. + */ + if (clientCursor == null) { + int /*long*/ newCursor = 0; + switch (cursorOrientation) { + case SWT.UP: + newCursor = OS.LoadCursor (0, OS.IDC_SIZENS); + break; + case SWT.DOWN: + newCursor = OS.LoadCursor (0, OS.IDC_SIZENS); + break; + case SWT.LEFT: + newCursor = OS.LoadCursor (0, OS.IDC_SIZEWE); + break; + case SWT.RIGHT: + newCursor = OS.LoadCursor (0, OS.IDC_SIZEWE); + break; + case SWT.LEFT | SWT.UP: + newCursor = OS.LoadCursor (0, OS.IDC_SIZENWSE); + break; + case SWT.RIGHT | SWT.DOWN: + newCursor = OS.LoadCursor (0, OS.IDC_SIZENWSE); + break; + case SWT.LEFT | SWT.DOWN: + newCursor = OS.LoadCursor (0, OS.IDC_SIZENESW); + break; + case SWT.RIGHT | SWT.UP: + newCursor = OS.LoadCursor (0, OS.IDC_SIZENESW); + break; + default: + newCursor = OS.LoadCursor (0, OS.IDC_SIZEALL); + break; + } + OS.SetCursor (newCursor); + if (resizeCursor != 0) { + OS.DestroyCursor (resizeCursor); + } + resizeCursor = newCursor; + } + + return new Point (pt.x, pt.y); +} + +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; +} + +/** + * Draw the rectangles displayed by the tracker. + */ +void drawRectangles (Rectangle [] rects, boolean stippled) { + if (parent == null && !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + RECT rect1 = new RECT(); + int bandWidth = stippled ? 3 : 1; + for (int i = 0; i < rects.length; i++) { + Rectangle rect = rects[i]; + rect1.left = rect.x - bandWidth; + rect1.top = rect.y - bandWidth; + rect1.right = rect.x + rect.width + bandWidth * 2; + rect1.bottom = rect.y + rect.height + bandWidth * 2; + OS.RedrawWindow (hwndOpaque, rect1, 0, OS.RDW_INVALIDATE); + } + return; + } + int bandWidth = 1; + int /*long*/ hwndTrack = OS.GetDesktopWindow (); + if (parent != null) hwndTrack = parent.handle; + int /*long*/ hDC = OS.GetDCEx (hwndTrack, 0, OS.DCX_CACHE); + int /*long*/ hBitmap = 0, hBrush = 0, oldBrush = 0; + if (stippled) { + bandWidth = 3; + byte [] bits = {-86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0}; + hBitmap = OS.CreateBitmap (8, 8, 1, 1, bits); + hBrush = OS.CreatePatternBrush (hBitmap); + oldBrush = OS.SelectObject (hDC, hBrush); + } + for (int i=0; i<rects.length; i++) { + Rectangle rect = rects [i]; + OS.PatBlt (hDC, rect.x, rect.y, rect.width, bandWidth, OS.PATINVERT); + OS.PatBlt (hDC, rect.x, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATINVERT); + OS.PatBlt (hDC, rect.x + rect.width - bandWidth, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATINVERT); + OS.PatBlt (hDC, rect.x, rect.y + rect.height - bandWidth, rect.width, bandWidth, OS.PATINVERT); + } + if (stippled) { + OS.SelectObject (hDC, oldBrush); + OS.DeleteObject (hBrush); + OS.DeleteObject (hBitmap); + } + OS.ReleaseDC (hwndTrack, hDC); +} + +/** + * 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 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 (); + cancelled = false; + tracking = true; + + /* + * 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; + } + + /* + * If this tracker is being created without a mouse drag then + * we need to create a transparent window that fills the screen + * in order to get all mouse/keyboard events that occur + * outside of our visible windows (ie.- over the desktop). + */ + Callback newProc = null; + boolean mouseDown = OS.GetKeyState(OS.VK_LBUTTON) < 0; + boolean isVista = !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0); + if ((parent == null && isVista) || !mouseDown) { + int width = OS.GetSystemMetrics (OS.SM_CXSCREEN); + int height = OS.GetSystemMetrics (OS.SM_CYSCREEN); + hwndTransparent = OS.CreateWindowEx ( + isVista ? OS.WS_EX_LAYERED | OS.WS_EX_NOACTIVATE : OS.WS_EX_TRANSPARENT, + display.windowClass, + null, + OS.WS_POPUP, + 0, 0, + width, height, + 0, + 0, + OS.GetModuleHandle (null), + null); + if (isVista) { + OS.SetLayeredWindowAttributes (hwndTransparent, 0xFFFFFF, (byte)0x01, OS.LWA_ALPHA); + } + OS.ShowWindow (hwndTransparent, OS.SW_SHOWNOACTIVATE); + newProc = new Callback (this, "transparentProc", 4); //$NON-NLS-1$ + int /*long*/ newProcAddress = newProc.getAddress (); + if (newProcAddress == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + if (isVista) { + hwndOpaque = OS.CreateWindowEx ( + OS.WS_EX_LAYERED | OS.WS_EX_NOACTIVATE, + display.windowClass, + null, + OS.WS_POPUP, + 0, 0, + width, height, + hwndTransparent, + 0, + OS.GetModuleHandle (null), + null); + oldOpaqueProc = OS.GetWindowLongPtr (hwndOpaque, OS.GWLP_WNDPROC); + OS.SetWindowLongPtr (hwndOpaque, OS.GWLP_WNDPROC, newProcAddress); + } else { + hwndOpaque = hwndTransparent; + } + oldTransparentProc = OS.GetWindowLongPtr (hwndTransparent, OS.GWLP_WNDPROC); + OS.SetWindowLongPtr (hwndTransparent, OS.GWLP_WNDPROC, newProcAddress); + OS.SetLayeredWindowAttributes (hwndOpaque, 0xFFFFFF, (byte)0xFF, OS.LWA_COLORKEY | OS.LWA_ALPHA); + OS.ShowWindow (hwndOpaque, OS.SW_SHOWNOACTIVATE); + } + + update (); + drawRectangles (rectangles, stippled); + Point cursorPos = null; + if (mouseDown) { + POINT pt = new POINT (); + OS.GetCursorPos (pt); + cursorPos = new Point (pt.x, pt.y); + } else { + if ((style & SWT.RESIZE) != 0) { + cursorPos = adjustResizeCursor (); + } else { + cursorPos = adjustMoveCursor (); + } + } + if (cursorPos != null) { + oldX = cursorPos.x; + oldY = cursorPos.y; + } + + try { + /* Tracker behaves like a Dialog with its own OS event loop. */ + MSG msg = new MSG (); + while (tracking && !cancelled) { + if (parent != null && parent.isDisposed ()) break; + OS.GetMessage (msg, 0, 0, 0); + OS.TranslateMessage (msg); + switch (msg.message) { + case OS.WM_LBUTTONUP: + case OS.WM_MOUSEMOVE: + wmMouse (msg.message, msg.wParam, msg.lParam); + break; + case OS.WM_IME_CHAR: wmIMEChar (msg.hwnd, msg.wParam, msg.lParam); break; + case OS.WM_CHAR: wmChar (msg.hwnd, msg.wParam, msg.lParam); break; + case OS.WM_KEYDOWN: wmKeyDown (msg.hwnd, msg.wParam, msg.lParam); break; + case OS.WM_KEYUP: wmKeyUp (msg.hwnd, msg.wParam, msg.lParam); break; + case OS.WM_SYSCHAR: wmSysChar (msg.hwnd, msg.wParam, msg.lParam); break; + case OS.WM_SYSKEYDOWN: wmSysKeyDown (msg.hwnd, msg.wParam, msg.lParam); break; + case OS.WM_SYSKEYUP: wmSysKeyUp (msg.hwnd, msg.wParam, msg.lParam); break; + } + if (OS.WM_KEYFIRST <= msg.message && msg.message <= OS.WM_KEYLAST) continue; + if (OS.WM_MOUSEFIRST <= msg.message && msg.message <= OS.WM_MOUSELAST) continue; + if (!(parent == null && isVista)) { + if (msg.message == OS.WM_PAINT) { + update (); + drawRectangles (rectangles, stippled); + } + } + OS.DispatchMessage (msg); + if (!(parent == null && isVista)) { + if (msg.message == OS.WM_PAINT) { + drawRectangles (rectangles, stippled); + } + } + } + if (mouseDown) OS.ReleaseCapture (); + if (!isDisposed()) { + update (); + drawRectangles (rectangles, stippled); + } + } finally { + /* + * Cleanup: If a transparent window was created in order to capture events then + * destroy it and its callback object now. + */ + if (hwndTransparent != 0) { + OS.DestroyWindow (hwndTransparent); + hwndTransparent = 0; + } + hwndOpaque = 0; + if (newProc != null) { + newProc.dispose (); + oldTransparentProc = oldOpaqueProc = 0; + } + /* + * Cleanup: If this tracker was resizing then the last cursor that it created + * needs to be destroyed. + */ + if (resizeCursor != 0) { + OS.DestroyCursor (resizeCursor); + resizeCursor = 0; + } + } + tracking = false; + 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); +} + +void resizeRectangles (int xChange, int yChange) { + if (bounds == null) return; + /* + * 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)) { + cursorOrientation |= SWT.LEFT; + } + if (xChange > 0 && ((style & SWT.RIGHT) != 0) && ((cursorOrientation & SWT.LEFT) == 0)) { + cursorOrientation |= SWT.RIGHT; + } + if (yChange < 0 && ((style & SWT.UP) != 0) && ((cursorOrientation & SWT.DOWN) == 0)) { + cursorOrientation |= SWT.UP; + } + if (yChange > 0 && ((style & SWT.DOWN) != 0) && ((cursorOrientation & SWT.UP) == 0)) { + cursorOrientation |= SWT.DOWN; + } + + /* + * 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; + 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; + 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; + 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; + 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; +} + +/** + * 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) { + if (inEvent) OS.SetCursor (clientCursor.handle); + } +} + +/** + * 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); + this.rectangles = new Rectangle [rectangles.length]; + for (int i = 0; i < rectangles.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; +} + +int /*long*/ transparentProc (int /*long*/ hwnd, int /*long*/ msg, int /*long*/ wParam, int /*long*/ lParam) { + switch ((int)/*64*/msg) { + /* + * We typically do not want to answer that the transparent window is + * transparent to hits since doing so negates the effect of having it + * to grab events. However, clients of the tracker should not be aware + * of this transparent window. Therefore if there is a hit query + * performed as a result of client code then answer that the transparent + * window is transparent to hits so that its existence will not impact + * the client. + */ + case OS.WM_NCHITTEST: + if (inEvent) return OS.HTTRANSPARENT; + break; + case OS.WM_SETCURSOR: + if (hwndOpaque == hwnd) { + if (clientCursor != null) { + OS.SetCursor (clientCursor.handle); + return 1; + } + if (resizeCursor != 0) { + OS.SetCursor (resizeCursor); + return 1; + } + } + break; + case OS.WM_PAINT: + boolean isVista = !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0); + if (parent == null && isVista && hwndOpaque == hwnd) { + PAINTSTRUCT ps = new PAINTSTRUCT(); + int /*long*/ hDC = OS.BeginPaint (hwnd, ps); + int /*long*/ hBitmap = 0, hBrush = 0, oldBrush = 0; + int /*long*/ transparentBrush = OS.CreateSolidBrush(0xFFFFFF); + oldBrush = OS.SelectObject (hDC, transparentBrush); + OS.PatBlt (hDC, ps.left, ps.top, ps.right - ps.left, ps.bottom - ps.top, OS.PATCOPY); + OS.SelectObject (hDC, oldBrush); + OS.DeleteObject (transparentBrush); + int bandWidth = 1; + if (stippled) { + bandWidth = 3; + byte [] bits = {-86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0}; + hBitmap = OS.CreateBitmap (8, 8, 1, 1, bits); + hBrush = OS.CreatePatternBrush (hBitmap); + oldBrush = OS.SelectObject (hDC, hBrush); + OS.SetBkColor (hDC, 0xF0F0F0); + } else { + oldBrush = OS.SelectObject (hDC, OS.GetStockObject(OS.BLACK_BRUSH)); + } + Rectangle[] rects = this.rectangles; + for (int i=0; i<rects.length; i++) { + Rectangle rect = rects [i]; + OS.PatBlt (hDC, rect.x, rect.y, rect.width, bandWidth, OS.PATCOPY); + OS.PatBlt (hDC, rect.x, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATCOPY); + OS.PatBlt (hDC, rect.x + rect.width - bandWidth, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATCOPY); + OS.PatBlt (hDC, rect.x, rect.y + rect.height - bandWidth, rect.width, bandWidth, OS.PATCOPY); + } + OS.SelectObject (hDC, oldBrush); + if (stippled) { + OS.DeleteObject (hBrush); + OS.DeleteObject (hBitmap); + } + OS.EndPaint (hwnd, ps); + return 0; + } + } + return OS.CallWindowProc (hwnd == hwndTransparent ? oldTransparentProc : oldOpaqueProc, hwnd, (int)/*64*/msg, wParam, lParam); +} + +void update () { + if (parent == null && !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) return; + if (parent != null) { + if (parent.isDisposed ()) return; + Shell shell = parent.getShell (); + shell.update (true); + } else { + display.update (); + } +} + +LRESULT wmKeyDown (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.wmKeyDown (hwnd, wParam, lParam); + if (result != null) return result; + boolean isMirrored = parent != null && (parent.style & SWT.MIRRORED) != 0; + int stepSize = OS.GetKeyState (OS.VK_CONTROL) < 0 ? STEPSIZE_SMALL : STEPSIZE_LARGE; + int xChange = 0, yChange = 0; + switch ((int)/*64*/wParam) { + case OS.VK_ESCAPE: + cancelled = true; + tracking = false; + break; + case OS.VK_RETURN: + tracking = false; + break; + case OS.VK_LEFT: + xChange = isMirrored ? stepSize : -stepSize; + break; + case OS.VK_RIGHT: + xChange = isMirrored ? -stepSize : stepSize; + break; + case OS.VK_UP: + yChange = -stepSize; + break; + case OS.VK_DOWN: + yChange = stepSize; + break; + } + if (xChange != 0 || yChange != 0) { + Rectangle [] oldRectangles = rectangles; + boolean oldStippled = stippled; + 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 = oldX + xChange; + event.y = oldY + yChange; + 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 resize + * event. If this happens return false to indicate + * that the tracking has failed. + */ + if (isDisposed ()) { + cancelled = true; + return LRESULT.ONE; + } + 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 (rectsToErase, oldStippled); + update (); + drawRectangles (rectangles, stippled); + } + cursorPos = adjustResizeCursor (); + } 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 LRESULT.ONE; + } + 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 (rectsToErase, oldStippled); + update (); + drawRectangles (rectangles, stippled); + } + cursorPos = adjustMoveCursor (); + } + if (cursorPos != null) { + oldX = cursorPos.x; + oldY = cursorPos.y; + } + } + return result; +} + +LRESULT wmSysKeyDown (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.wmSysKeyDown (hwnd, wParam, lParam); + if (result != null) return result; + cancelled = true; + tracking = false; + return result; +} + +LRESULT wmMouse (int message, int /*long*/ wParam, int /*long*/ lParam) { + boolean isMirrored = parent != null && (parent.style & SWT.MIRRORED) != 0; + int newPos = OS.GetMessagePos (); + int newX = OS.GET_X_LPARAM (newPos); + int newY = OS.GET_Y_LPARAM (newPos); + if (newX != oldX || newY != oldY) { + Rectangle [] oldRectangles = rectangles; + boolean oldStippled = stippled; + 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) { + if (isMirrored) { + resizeRectangles (oldX - newX, newY - oldY); + } else { + 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 resize + * event. If this happens, return false to indicate + * that the tracking has failed. + */ + if (isDisposed ()) { + cancelled = true; + return LRESULT.ONE; + } + 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 (rectsToErase, oldStippled); + update (); + drawRectangles (rectangles, stippled); + } + Point cursorPos = adjustResizeCursor (); + if (cursorPos != null) { + newX = cursorPos.x; + newY = cursorPos.y; + } + } else { + if (isMirrored) { + moveRectangles (oldX - newX, newY - oldY); + } 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 LRESULT.ONE; + } + 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 (rectsToErase, oldStippled); + update (); + drawRectangles (rectangles, stippled); + } + } + oldX = newX; + oldY = newY; + } + tracking = message != OS.WM_LBUTTONUP; + return null; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TrayItem.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TrayItem.java new file mode 100644 index 0000000000..acd99d6888 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/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.win32.*; + +/** + * 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; + int id; + Image image2; + ToolTip toolTip; + String toolTipText; + boolean visible = true; + +/** + * 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 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); +} + +/** + * 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); +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +void createWidget () { + NOTIFYICONDATA iconData = OS.IsUnicode ? (NOTIFYICONDATA) new NOTIFYICONDATAW () : new NOTIFYICONDATAA (); + iconData.cbSize = NOTIFYICONDATA.sizeof; + iconData.uID = id = display.nextTrayId++; + iconData.hWnd = display.hwndMessage; + iconData.uFlags = OS.NIF_MESSAGE; + iconData.uCallbackMessage = Display.SWT_TRAYICONMSG; + OS.Shell_NotifyIcon (OS.NIM_ADD, iconData); +} + +void destroyWidget () { + parent.destroyItem (this); + releaseHandle (); +} + +/** + * 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; +} + +int /*long*/ messageProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. When the user clicks on the tray + * icon, another application may be the foreground window. + * This means that the event loop is not running and can + * cause problems. For example, if a menu is shown, when + * the user clicks outside of the menu to cancel it, the + * menu is not hidden until an event is processed. If + * another application is the foreground window, then the + * menu is not hidden. The fix is to force the tray icon + * message window to the foreground when sending an event. + */ + switch ((int)/*64*/lParam) { + case OS.WM_LBUTTONDOWN: + if (hooks (SWT.Selection)) { + OS.SetForegroundWindow (hwnd); + postEvent (SWT.Selection); + } + break; + case OS.WM_LBUTTONDBLCLK: + case OS.WM_RBUTTONDBLCLK: + if (hooks (SWT.DefaultSelection)) { + OS.SetForegroundWindow (hwnd); + postEvent (SWT.DefaultSelection); + } + break; + case OS.WM_RBUTTONUP: { + if (hooks (SWT.MenuDetect)) { + OS.SetForegroundWindow (hwnd); + sendEvent (SWT.MenuDetect); + // widget could be disposed at this point + if (isDisposed()) return 0; + } + break; + } + case OS.NIN_BALLOONSHOW: + if (toolTip != null && !toolTip.visible) { + toolTip.visible = true; + if (toolTip.hooks (SWT.Show)) { + OS.SetForegroundWindow (hwnd); + toolTip.sendEvent (SWT.Show); + // widget could be disposed at this point + if (isDisposed()) return 0; + } + } + break; + case OS.NIN_BALLOONHIDE: + case OS.NIN_BALLOONTIMEOUT: + case OS.NIN_BALLOONUSERCLICK: + if (toolTip != null) { + if (toolTip.visible) { + toolTip.visible = false; + if (toolTip.hooks (SWT.Hide)) { + OS.SetForegroundWindow (hwnd); + toolTip.sendEvent (SWT.Hide); + // widget could be disposed at this point + if (isDisposed()) return 0; + } + } + if (lParam == OS.NIN_BALLOONUSERCLICK) { + if (toolTip.hooks (SWT.Selection)) { + OS.SetForegroundWindow (hwnd); + toolTip.postEvent (SWT.Selection); + // widget could be disposed at this point + if (isDisposed()) return 0; + } + } + } + break; + } + display.wakeThread (); + return 0; +} + +void recreate () { + createWidget (); + if (!visible) setVisible (false); + if (text.length () != 0) setText (text); + if (image != null) setImage (image); + if (toolTipText != null) setToolTipText (toolTipText); +} + +void releaseHandle () { + super.releaseHandle (); + parent = null; +} + +void releaseWidget () { + super.releaseWidget (); + if (toolTip != null) toolTip.item = null; + toolTip = null; + if (image2 != null) image2.dispose (); + image2 = null; + toolTipText = null; + NOTIFYICONDATA iconData = OS.IsUnicode ? (NOTIFYICONDATA) new NOTIFYICONDATAW () : new NOTIFYICONDATAA (); + iconData.cbSize = NOTIFYICONDATA.sizeof; + iconData.uID = id; + iconData.hWnd = display.hwndMessage; + OS.Shell_NotifyIcon (OS.NIM_DELETE, iconData); +} + +/** + * 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); +} + +/** + * 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); +} + +/** + * 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); + if (image2 != null) image2.dispose (); + image2 = null; + int /*long*/ hIcon = 0; + Image icon = image; + if (icon != null) { + switch (icon.type) { + case SWT.BITMAP: + image2 = Display.createIcon (image); + hIcon = image2.handle; + break; + case SWT.ICON: + hIcon = icon.handle; + break; + } + } + NOTIFYICONDATA iconData = OS.IsUnicode ? (NOTIFYICONDATA) new NOTIFYICONDATAW () : new NOTIFYICONDATAA (); + iconData.cbSize = NOTIFYICONDATA.sizeof; + iconData.uID = id; + iconData.hWnd = display.hwndMessage; + iconData.hIcon = hIcon; + iconData.uFlags = OS.NIF_ICON; + OS.Shell_NotifyIcon (OS.NIM_MODIFY, iconData); +} + +/** + * 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; + NOTIFYICONDATA iconData = OS.IsUnicode ? (NOTIFYICONDATA) new NOTIFYICONDATAW () : new NOTIFYICONDATAA (); + TCHAR buffer = new TCHAR (0, toolTipText == null ? "" : toolTipText, true); + /* + * Note that the size of the szTip field is different in version 5.0 of shell32.dll. + */ + int length = OS.SHELL32_MAJOR < 5 ? 64 : 128; + if (OS.IsUnicode) { + char [] szTip = ((NOTIFYICONDATAW) iconData).szTip; + length = Math.min (length - 1, buffer.length ()); + System.arraycopy (buffer.chars, 0, szTip, 0, length); + } else { + byte [] szTip = ((NOTIFYICONDATAA) iconData).szTip; + length = Math.min (length - 1, buffer.length ()); + System.arraycopy (buffer.bytes, 0, szTip, 0, length); + } + iconData.cbSize = NOTIFYICONDATA.sizeof; + iconData.uID = id; + iconData.hWnd = display.hwndMessage; + iconData.uFlags = OS.NIF_TIP; + OS.Shell_NotifyIcon (OS.NIM_MODIFY, iconData); +} + +/** + * 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) { + /* + * 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; + } + this.visible = visible; + NOTIFYICONDATA iconData = OS.IsUnicode ? (NOTIFYICONDATA) new NOTIFYICONDATAW () : new NOTIFYICONDATAA (); + iconData.cbSize = NOTIFYICONDATA.sizeof; + iconData.uID = id; + iconData.hWnd = display.hwndMessage; + if (OS.SHELL32_MAJOR < 5) { + if (visible) { + iconData.uFlags = OS.NIF_MESSAGE; + iconData.uCallbackMessage = Display.SWT_TRAYICONMSG; + OS.Shell_NotifyIcon (OS.NIM_ADD, iconData); + setImage (image); + setToolTipText (toolTipText); + } else { + OS.Shell_NotifyIcon (OS.NIM_DELETE, iconData); + } + } else { + iconData.uFlags = OS.NIF_STATE; + iconData.dwState = visible ? 0 : OS.NIS_HIDDEN; + iconData.dwStateMask = OS.NIS_HIDDEN; + OS.Shell_NotifyIcon (OS.NIM_MODIFY, iconData); + } + if (!visible) sendEvent (SWT.Hide); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tree.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tree.java new file mode 100755 index 0000000000..129bb847a7 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tree.java @@ -0,0 +1,7809 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.events.*; + +/** + * 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 { + TreeItem [] items; + TreeColumn [] columns; + int columnCount; + ImageList imageList, headerImageList; + TreeItem currentItem; + TreeColumn sortColumn; + RECT focusRect; + int /*long*/ hwndParent, hwndHeader, hAnchor, hInsert, hSelect; + int lastID; + int /*long*/ hFirstIndexOf, hLastIndexOf; + int lastIndexOf, itemCount, sortDirection; + boolean dragStarted, gestureCompleted, insertAfter, shrink, ignoreShrink; + boolean ignoreSelect, ignoreExpand, ignoreDeselect, ignoreResize; + boolean lockSelection, oldSelected, newSelected, ignoreColumnMove; + boolean linesVisible, customDraw, printClient, painted, ignoreItemHeight; + boolean ignoreCustomDraw, ignoreDrawForeground, ignoreDrawBackground, ignoreDrawFocus; + boolean ignoreDrawSelection, ignoreDrawHot, ignoreFullSelection, explorerTheme; + int scrollWidth, selectionForeground; + int /*long*/ headerToolTipHandle, itemToolTipHandle; + static final int INSET = 3; + static final int GRID_WIDTH = 1; + static final int SORT_WIDTH = 10; + static final int HEADER_MARGIN = 12; + static final int HEADER_EXTRA = 3; + static final int INCREMENT = 5; + static final int EXPLORER_EXTRA = 2; + static final int DRAG_IMAGE_SIZE = 301; + static final boolean EXPLORER_THEME = true; + static final int /*long*/ TreeProc; + static final TCHAR TreeClass = new TCHAR (0, OS.WC_TREEVIEW, true); + static final int /*long*/ HeaderProc; + static final TCHAR HeaderClass = new TCHAR (0, OS.WC_HEADER, true); + static { + WNDCLASS lpWndClass = new WNDCLASS (); + OS.GetClassInfo (0, TreeClass, lpWndClass); + TreeProc = lpWndClass.lpfnWndProc; + OS.GetClassInfo (0, HeaderClass, lpWndClass); + HeaderProc = lpWndClass.lpfnWndProc; + } + +/** + * 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)); +} + +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; + } + /* + * Note: Windows only supports TVS_NOSCROLL and TVS_NOHSCROLL. + */ + if ((style & SWT.H_SCROLL) != 0 && (style & SWT.V_SCROLL) == 0) { + style |= SWT.V_SCROLL; + } + return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0); +} + +void _addListener (int eventType, Listener listener) { + super._addListener (eventType, listener); + switch (eventType) { + case SWT.DragDetect: { + if ((state & DRAG_DETECT) != 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + bits &= ~OS.TVS_DISABLEDRAGDROP; + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); + } + break; + } + case SWT.MeasureItem: + case SWT.EraseItem: + case SWT.PaintItem: { + customDraw = true; + style |= SWT.DOUBLE_BUFFERED; + if (isCustomToolTip ()) createItemToolTips (); + OS.SendMessage (handle, OS.TVM_SETSCROLLTIME, 0, 0); + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if (eventType == SWT.MeasureItem) { + /* + * This code is intentionally commented. + */ +// if (explorerTheme) { +// int bits1 = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0); +// bits1 &= ~OS.TVS_EX_AUTOHSCROLL; +// OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, 0, bits1); +// } + bits |= OS.TVS_NOHSCROLL; + } + /* + * Feature in Windows. When the tree has the style + * TVS_FULLROWSELECT, the background color for the + * entire row is filled when an item is painted, + * drawing on top of any custom drawing. The fix + * is to clear TVS_FULLROWSELECT. + */ + if ((style & SWT.FULL_SELECTION) != 0) { + if (eventType != SWT.MeasureItem) { + if (!explorerTheme) bits &= ~OS.TVS_FULLROWSELECT; + } + } + if (bits != OS.GetWindowLong (handle, OS.GWL_STYLE)) { + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); + OS.InvalidateRect (handle, null, true); + /* + * Bug in Windows. When TVS_NOHSCROLL is set after items + * have been inserted into the tree, Windows shows the + * scroll bar. The fix is to check for this case and + * explicitly hide the scroll bar. + */ + int count = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0); + if (count != 0 && (bits & OS.TVS_NOHSCROLL) != 0) { + if (!OS.IsWinCE) OS.ShowScrollBar (handle, OS.SB_HORZ, false); + } + } + break; + } + } +} + +TreeItem _getItem (int /*long*/ hItem) { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + tvItem.hItem = hItem; + if (OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem) != 0) { + return _getItem (tvItem.hItem, (int)/*64*/tvItem.lParam); + } + return null; +} + +TreeItem _getItem (int /*long*/ hItem, int id) { + if ((style & SWT.VIRTUAL) == 0) return items [id]; + return id != -1 ? items [id] : new TreeItem (this, SWT.NONE, -1, -1, hItem); +} + +void _setBackgroundPixel (int newPixel) { + int oldPixel = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETBKCOLOR, 0, 0); + if (oldPixel != newPixel) { + /* + * Bug in Windows. When TVM_SETBKCOLOR is used more + * than once to set the background color of a tree, + * the background color of the lines and the plus/minus + * does not change to the new color. The fix is to set + * the background color to the default before setting + * the new color. + */ + if (oldPixel != -1) { + OS.SendMessage (handle, OS.TVM_SETBKCOLOR, 0, -1); + } + + /* Set the background color */ + OS.SendMessage (handle, OS.TVM_SETBKCOLOR, 0, newPixel); + + /* + * Feature in Windows. When TVM_SETBKCOLOR is used to + * set the background color of a tree, the plus/minus + * animation draws badly. The fix is to clear the effect. + */ + if (explorerTheme) { + int bits2 = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0); + if (newPixel == -1 && findImageControl () == null) { + bits2 |= OS.TVS_EX_FADEINOUTEXPANDOS; + } else { + bits2 &= ~OS.TVS_EX_FADEINOUTEXPANDOS; + } + OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, 0, bits2); + } + + /* Set the checkbox image list */ + if ((style & SWT.CHECK) != 0) setCheckboxImageList (); + } +} + +/** + * 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 /*long*/ borderHandle () { + return hwndParent != 0 ? hwndParent : handle; +} + +LRESULT CDDS_ITEMPOSTPAINT (NMTVCUSTOMDRAW nmcd, int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreCustomDraw) return null; + if (nmcd.left == nmcd.right) return new LRESULT (OS.CDRF_DODEFAULT); + int /*long*/ hDC = nmcd.hdc; + OS.RestoreDC (hDC, -1); + TreeItem item = getItem (nmcd); + + /* + * Feature in Windows. When a new tree item is inserted + * using TVM_INSERTITEM and the tree is using custom draw, + * a NM_CUSTOMDRAW is sent before TVM_INSERTITEM returns + * and before the item is added to the items array. The + * fix is to check for null. + * + * NOTE: This only happens on XP with the version 6.00 of + * COMCTL32.DLL, + */ + if (item == null) return null; + + /* + * Feature in Windows. Under certain circumstances, Windows + * sends CDDS_ITEMPOSTPAINT for an empty rectangle. This is + * not a problem providing that graphics do not occur outside + * the rectangle. The fix is to test for the rectangle and + * draw nothing. + * + * NOTE: This seems to happen when both I_IMAGECALLBACK + * and LPSTR_TEXTCALLBACK are used at the same time with + * TVM_SETITEM. + */ + if (nmcd.left >= nmcd.right || nmcd.top >= nmcd.bottom) return null; + if (!OS.IsWindowVisible (handle)) return null; + if ((style & SWT.FULL_SELECTION) != 0 || findImageControl () != null || ignoreDrawSelection || explorerTheme) { + OS.SetBkMode (hDC, OS.TRANSPARENT); + } + boolean selected = isItemSelected (nmcd); + boolean hot = explorerTheme && (nmcd.uItemState & OS.CDIS_HOT) != 0; + if (OS.IsWindowEnabled (handle)) { + if (explorerTheme) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.TVS_TRACKSELECT) != 0) { + if ((style & SWT.FULL_SELECTION) != 0 && (selected || hot)) { + OS.SetTextColor (hDC, OS.GetSysColor (OS.COLOR_WINDOWTEXT)); + } else { + OS.SetTextColor (hDC, getForegroundPixel ()); + } + } + } + } + int [] order = null; + RECT clientRect = new RECT (); + OS.GetClientRect (scrolledHandle (), clientRect); + if (hwndHeader != 0) { + OS.MapWindowPoints (hwndParent, handle, clientRect, 2); + if (columnCount != 0) { + order = new int [columnCount]; + OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, order); + } + } + int sortIndex = -1, clrSortBk = -1; + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if (sortColumn != null && sortDirection != SWT.NONE) { + if (findImageControl () == null) { + sortIndex = indexOf (sortColumn); + clrSortBk = getSortColumnPixel (); + } + } + } + int x = 0; + Point size = null; + for (int i=0; i<Math.max (1, columnCount); i++) { + int index = order == null ? i : order [i], width = nmcd.right - nmcd.left; + if (columnCount > 0 && hwndHeader != 0) { + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_WIDTH; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem); + width = hdItem.cxy; + } + if (i == 0) { + if ((style & SWT.FULL_SELECTION) != 0) { + boolean clear = !explorerTheme && !ignoreDrawSelection && findImageControl () == null; + if (clear || (selected && !ignoreDrawSelection) || (hot && !ignoreDrawHot)) { + boolean draw = true; + RECT pClipRect = new RECT (); + OS.SetRect (pClipRect, width, nmcd.top, nmcd.right, nmcd.bottom); + if (explorerTheme) { + if (hooks (SWT.EraseItem)) { + RECT itemRect = item.getBounds (index, true, true, false, false, true, hDC); + itemRect.left -= EXPLORER_EXTRA; + itemRect.right += EXPLORER_EXTRA + 1; + pClipRect.left = itemRect.left; + pClipRect.right = itemRect.right; + if (columnCount > 0 && hwndHeader != 0) { + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_WIDTH; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem); + pClipRect.right = Math.min (pClipRect.right, nmcd.left + hdItem.cxy); + } + } + RECT pRect = new RECT (); + OS.SetRect (pRect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom); + if (columnCount > 0 && hwndHeader != 0) { + int totalWidth = 0; + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_WIDTH; + for (int j=0; j<columnCount; j++) { + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, j, hdItem); + totalWidth += hdItem.cxy; + } + if (totalWidth > clientRect.right - clientRect.left) { + pRect.left = 0; + pRect.right = totalWidth; + } else { + pRect.left = clientRect.left; + pRect.right = clientRect.right; + } + } + draw = false; + int /*long*/ hTheme = OS.OpenThemeData (handle, Display.TREEVIEW); + int iStateId = selected ? OS.TREIS_SELECTED : OS.TREIS_HOT; + if (OS.GetFocus () != handle && selected && !hot) iStateId = OS.TREIS_SELECTEDNOTFOCUS; + OS.DrawThemeBackground (hTheme, hDC, OS.TVP_TREEITEM, iStateId, pRect, pClipRect); + OS.CloseThemeData (hTheme); + } + if (draw) fillBackground (hDC, OS.GetBkColor (hDC), pClipRect); + } + } + } + if (x + width > clientRect.left) { + RECT rect = new RECT (), backgroundRect = null; + boolean drawItem = true, drawText = true, drawImage = true, drawBackground = false; + if (i == 0) { + drawItem = drawImage = drawText = false; + if (findImageControl () != null) { + if (explorerTheme) { + if (OS.IsWindowEnabled (handle) && !hooks (SWT.EraseItem)) { + Image image = null; + if (index == 0) { + image = item.image; + } else { + Image [] images = item.images; + if (images != null) image = images [index]; + } + if (image != null) { + Rectangle bounds = image.getBounds (); + if (size == null) size = getImageSize (); + if (!ignoreDrawForeground) { + GCData data = new GCData(); + data.device = display; + GC gc = GC.win32_new (hDC, data); + RECT iconRect = item.getBounds (index, false, true, false, false, true, hDC); + gc.setClipping (iconRect.left, iconRect.top, iconRect.right - iconRect.left, iconRect.bottom - iconRect.top); + gc.drawImage (image, 0, 0, bounds.width, bounds.height, iconRect.left, iconRect.top, size.x, size.y); + OS.SelectClipRgn (hDC, 0); + gc.dispose (); + } + } + } + } else { + drawItem = drawText = drawBackground = true; + rect = item.getBounds (index, true, false, false, false, true, hDC); + if (linesVisible) { + rect.right++; + rect.bottom++; + } + } + } + if (selected && !ignoreDrawSelection && !ignoreDrawBackground) { + if (!explorerTheme) fillBackground (hDC, OS.GetBkColor (hDC), rect); + drawBackground = false; + } + backgroundRect = rect; + if (hooks (SWT.EraseItem)) { + drawItem = drawText = drawImage = true; + rect = item.getBounds (index, true, true, false, false, true, hDC); + if ((style & SWT.FULL_SELECTION) != 0) { + backgroundRect = rect; + } else { + backgroundRect = item.getBounds (index, true, false, false, false, true, hDC); + } + } + } else { + selectionForeground = -1; + ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = false; + OS.SetRect (rect, x, nmcd.top, x + width, nmcd.bottom); + backgroundRect = rect; + } + int clrText = -1, clrTextBk = -1; + int /*long*/ hFont = item.fontHandle (index); + if (selectionForeground != -1) clrText = selectionForeground; + if (OS.IsWindowEnabled (handle)) { + boolean drawForeground = false; + if (selected) { + if (i != 0 && (style & SWT.FULL_SELECTION) == 0) { + OS.SetTextColor (hDC, getForegroundPixel ()); + OS.SetBkColor (hDC, getBackgroundPixel ()); + drawForeground = drawBackground = true; + } + } else { + drawForeground = drawBackground = true; + } + if (drawForeground) { + clrText = item.cellForeground != null ? item.cellForeground [index] : -1; + if (clrText == -1) clrText = item.foreground; + } + if (drawBackground) { + clrTextBk = item.cellBackground != null ? item.cellBackground [index] : -1; + if (clrTextBk == -1) clrTextBk = item.background; + if (clrTextBk == -1 && index == sortIndex) clrTextBk = clrSortBk; + } + } else { + if (clrTextBk == -1 && index == sortIndex) { + drawBackground = true; + clrTextBk = clrSortBk; + } + } + if (explorerTheme) { + if (selected || (nmcd.uItemState & OS.CDIS_HOT) != 0) { + if ((style & SWT.FULL_SELECTION) != 0) { + drawBackground = false; + } else { + if (i == 0) { + drawBackground = false; + if (!hooks (SWT.EraseItem)) drawText = false; + } + } + } + } + if (drawItem) { + if (i != 0) { + if (hooks (SWT.MeasureItem)) { + sendMeasureItemEvent (item, index, hDC); + if (isDisposed () || item.isDisposed ()) break; + } + if (hooks (SWT.EraseItem)) { + RECT cellRect = item.getBounds (index, true, true, true, true, true, hDC); + int nSavedDC = OS.SaveDC (hDC); + GCData data = new GCData (); + data.device = display; + data.foreground = OS.GetTextColor (hDC); + data.background = OS.GetBkColor (hDC); + if (!selected || (style & SWT.FULL_SELECTION) == 0) { + if (clrText != -1) data.foreground = clrText; + if (clrTextBk != -1) data.background = clrTextBk; + } + data.font = item.getFont (index); + data.uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + GC gc = GC.win32_new (hDC, data); + Event event = new Event (); + event.item = item; + event.index = index; + event.gc = gc; + event.detail |= SWT.FOREGROUND; + if (clrTextBk != -1) event.detail |= SWT.BACKGROUND; + if ((style & SWT.FULL_SELECTION) != 0) { + if (hot) event.detail |= SWT.HOT; + if (selected) event.detail |= SWT.SELECTED; + if (!explorerTheme) { + //if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) { + if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0) == nmcd.dwItemSpec) { + if (handle == OS.GetFocus ()) { + int uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + if ((uiState & OS.UISF_HIDEFOCUS) == 0) event.detail |= SWT.FOCUSED; + } + } + } + } + event.x = cellRect.left; + event.y = cellRect.top; + event.width = cellRect.right - cellRect.left; + event.height = cellRect.bottom - cellRect.top; + gc.setClipping (event.x, event.y, event.width, event.height); + sendEvent (SWT.EraseItem, event); + event.gc = null; + int newTextClr = data.foreground; + gc.dispose (); + OS.RestoreDC (hDC, nSavedDC); + if (isDisposed () || item.isDisposed ()) break; + if (event.doit) { + ignoreDrawForeground = (event.detail & SWT.FOREGROUND) == 0; + ignoreDrawBackground = (event.detail & SWT.BACKGROUND) == 0; + if ((style & SWT.FULL_SELECTION) != 0) { + ignoreDrawSelection = (event.detail & SWT.SELECTED) == 0; + ignoreDrawFocus = (event.detail & SWT.FOCUSED) == 0; + ignoreDrawHot = (event.detail & SWT.HOT) == 0; + } + } else { + ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = true; + } + if (selected && ignoreDrawSelection) ignoreDrawHot = true; + if ((style & SWT.FULL_SELECTION) != 0) { + if (ignoreDrawSelection) ignoreFullSelection = true; + if (!ignoreDrawSelection || !ignoreDrawHot) { + if (!selected && !hot) { + selectionForeground = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT); + } else { + if (!explorerTheme) { + drawBackground = true; + ignoreDrawBackground = false; + if ((handle == OS.GetFocus () || display.getHighContrast ()) && OS.IsWindowEnabled (handle)) { + clrTextBk = OS.GetSysColor (OS.COLOR_HIGHLIGHT); + } else { + clrTextBk = OS.GetSysColor (OS.COLOR_3DFACE); + } + if (!ignoreFullSelection && index == columnCount - 1) { + RECT selectionRect = new RECT (); + OS.SetRect (selectionRect, backgroundRect.left, backgroundRect.top, nmcd.right, backgroundRect.bottom); + backgroundRect = selectionRect; + } + } else { + RECT pRect = new RECT (); + OS.SetRect (pRect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom); + if (columnCount > 0 && hwndHeader != 0) { + int totalWidth = 0; + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_WIDTH; + for (int j=0; j<columnCount; j++) { + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, j, hdItem); + totalWidth += hdItem.cxy; + } + if (totalWidth > clientRect.right - clientRect.left) { + pRect.left = 0; + pRect.right = totalWidth; + } else { + pRect.left = clientRect.left; + pRect.right = clientRect.right; + } + if (index == columnCount - 1) { + RECT selectionRect = new RECT (); + OS.SetRect (selectionRect, backgroundRect.left, backgroundRect.top, pRect.right, backgroundRect.bottom); + backgroundRect = selectionRect; + } + } + int /*long*/ hTheme = OS.OpenThemeData (handle, Display.TREEVIEW); + int iStateId = selected ? OS.TREIS_SELECTED : OS.TREIS_HOT; + if (OS.GetFocus () != handle && selected && !hot) iStateId = OS.TREIS_SELECTEDNOTFOCUS; + OS.DrawThemeBackground (hTheme, hDC, OS.TVP_TREEITEM, iStateId, pRect, backgroundRect); + OS.CloseThemeData (hTheme); + } + } + } else { + if (selected) { + selectionForeground = newTextClr; + if (!explorerTheme) { + if (clrTextBk == -1 && OS.IsWindowEnabled (handle)) { + Control control = findBackgroundControl (); + if (control == null) control = this; + clrTextBk = control.getBackgroundPixel (); + } + } + } + } + } + } + if (selectionForeground != -1) clrText = selectionForeground; + } + if (!ignoreDrawBackground) { + if (clrTextBk != -1) { + if (drawBackground) fillBackground (hDC, clrTextBk, backgroundRect); + } else { + Control control = findImageControl (); + if (control != null) { + if (i == 0) { + int right = Math.min (rect.right, width); + OS.SetRect (rect, rect.left, rect.top, right, rect.bottom); + if (drawBackground) fillImageBackground (hDC, control, rect); + } else { + if (drawBackground) fillImageBackground (hDC, control, rect); + } + } + } + } + rect.left += INSET - 1; + if (drawImage) { + Image image = null; + if (index == 0) { + image = item.image; + } else { + Image [] images = item.images; + if (images != null) image = images [index]; + } + int inset = i != 0 ? INSET : 0; + int offset = i != 0 ? INSET : INSET + 2; + if (image != null) { + Rectangle bounds = image.getBounds (); + if (size == null) size = getImageSize (); + if (!ignoreDrawForeground) { + //int y1 = rect.top + (index == 0 ? (getItemHeight () - size.y) / 2 : 0); + int y1 = rect.top; + int x1 = Math.max (rect.left, rect.left - inset + 1); + GCData data = new GCData(); + data.device = display; + GC gc = GC.win32_new (hDC, data); + gc.setClipping (x1, rect.top, rect.right - x1, rect.bottom - rect.top); + gc.drawImage (image, 0, 0, bounds.width, bounds.height, x1, y1, size.x, size.y); + OS.SelectClipRgn (hDC, 0); + gc.dispose (); + } + OS.SetRect (rect, rect.left + size.x + offset, rect.top, rect.right - inset, rect.bottom); + } else { + if (i == 0) { + if (OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0) != 0) { + if (size == null) size = getImageSize (); + rect.left = Math.min (rect.left + size.x + offset, rect.right); + } + } else { + OS.SetRect (rect, rect.left + offset, rect.top, rect.right - inset, rect.bottom); + } + } + } + if (drawText) { + /* + * Bug in Windows. When DrawText() is used with DT_VCENTER + * and DT_ENDELLIPSIS, the ellipsis can draw outside of the + * rectangle when the rectangle is empty. The fix is avoid + * all text drawing for empty rectangles. + */ + if (rect.left < rect.right) { + String string = null; + if (index == 0) { + string = item.text; + } else { + String [] strings = item.strings; + if (strings != null) string = strings [index]; + } + if (string != null) { + if (hFont != -1) hFont = OS.SelectObject (hDC, hFont); + if (clrText != -1) clrText = OS.SetTextColor (hDC, clrText); + if (clrTextBk != -1) clrTextBk = OS.SetBkColor (hDC, clrTextBk); + int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_VCENTER; + if (i != 0) flags |= OS.DT_ENDELLIPSIS; + TreeColumn column = columns != null ? columns [index] : null; + if (column != null) { + if ((column.style & SWT.CENTER) != 0) flags |= OS.DT_CENTER; + if ((column.style & SWT.RIGHT) != 0) flags |= OS.DT_RIGHT; + } + TCHAR buffer = new TCHAR (getCodePage (), string, false); + if (!ignoreDrawForeground) OS.DrawText (hDC, buffer, buffer.length (), rect, flags); + OS.DrawText (hDC, buffer, buffer.length (), rect, flags | OS.DT_CALCRECT); + if (hFont != -1) hFont = OS.SelectObject (hDC, hFont); + if (clrText != -1) clrText = OS.SetTextColor (hDC, clrText); + if (clrTextBk != -1) clrTextBk = OS.SetBkColor (hDC, clrTextBk); + } + } + } + } + if (selectionForeground != -1) clrText = selectionForeground; + if (hooks (SWT.PaintItem)) { + RECT itemRect = item.getBounds (index, true, true, false, false, false, hDC); + int nSavedDC = OS.SaveDC (hDC); + GCData data = new GCData (); + data.device = display; + data.font = item.getFont (index); + data.foreground = OS.GetTextColor (hDC); + data.background = OS.GetBkColor (hDC); + if (selected && (style & SWT.FULL_SELECTION) != 0) { + if (selectionForeground != -1) data.foreground = selectionForeground; + } else { + if (clrText != -1) data.foreground = clrText; + if (clrTextBk != -1) data.background = clrTextBk; + } + data.uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + GC gc = GC.win32_new (hDC, data); + Event event = new Event (); + event.item = item; + event.index = index; + event.gc = gc; + event.detail |= SWT.FOREGROUND; + if (clrTextBk != -1) event.detail |= SWT.BACKGROUND; + if (hot) event.detail |= SWT.HOT; + if (selected && (i == 0 /*nmcd.iSubItem == 0*/ || (style & SWT.FULL_SELECTION) != 0)) { + event.detail |= SWT.SELECTED; + } + if (!explorerTheme) { + //if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) { + if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0) == nmcd.dwItemSpec) { + if (i == 0 /*nmcd.iSubItem == 0*/ || (style & SWT.FULL_SELECTION) != 0) { + if (handle == OS.GetFocus ()) { + int uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + if ((uiState & OS.UISF_HIDEFOCUS) == 0) event.detail |= SWT.FOCUSED; + } + } + } + } + event.x = itemRect.left; + event.y = itemRect.top; + event.width = itemRect.right - itemRect.left; + event.height = itemRect.bottom - itemRect.top; + RECT cellRect = item.getBounds (index, true, true, true, true, true, hDC); + int cellWidth = cellRect.right - cellRect.left; + int cellHeight = cellRect.bottom - cellRect.top; + gc.setClipping (cellRect.left, cellRect.top, cellWidth, cellHeight); + sendEvent (SWT.PaintItem, event); + if (data.focusDrawn) focusRect = null; + event.gc = null; + gc.dispose (); + OS.RestoreDC (hDC, nSavedDC); + if (isDisposed () || item.isDisposed ()) break; + } + } + x += width; + if (x > clientRect.right) break; + } + if (linesVisible) { + if ((style & SWT.FULL_SELECTION) != 0) { + if (columnCount != 0) { + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_WIDTH; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, 0, hdItem); + RECT rect = new RECT (); + OS.SetRect (rect, nmcd.left + hdItem.cxy, nmcd.top, nmcd.right, nmcd.bottom); + OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM); + } + } + RECT rect = new RECT (); + OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom); + OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM); + } + if (!ignoreDrawFocus && focusRect != null) { + OS.DrawFocusRect (hDC, focusRect); + focusRect = null; + } else { + if (!explorerTheme) { + if (handle == OS.GetFocus ()) { + int uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + if ((uiState & OS.UISF_HIDEFOCUS) == 0) { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (hItem == item.handle) { + if (!ignoreDrawFocus && findImageControl () != null) { + if ((style & SWT.FULL_SELECTION) != 0) { + RECT focusRect = new RECT (); + OS.SetRect (focusRect, 0, nmcd.top, clientRect.right + 1, nmcd.bottom); + OS.DrawFocusRect (hDC, focusRect); + } else { + int index = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0); + RECT focusRect = item.getBounds (index, true, false, false, false, false, hDC); + RECT clipRect = item.getBounds (index, true, false, false, false, true, hDC); + OS.IntersectClipRect (hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); + OS.DrawFocusRect (hDC, focusRect); + OS.SelectClipRgn (hDC, 0); + } + } + } + } + } + } + } + return new LRESULT (OS.CDRF_DODEFAULT); +} + +LRESULT CDDS_ITEMPREPAINT (NMTVCUSTOMDRAW nmcd, int /*long*/ wParam, int /*long*/ lParam) { + /* + * Even when custom draw is being ignored, the font needs + * to be selected into the HDC so that the item bounds are + * measured correctly. + */ + TreeItem item = getItem (nmcd); + /* + * Feature in Windows. When a new tree item is inserted + * using TVM_INSERTITEM and the tree is using custom draw, + * a NM_CUSTOMDRAW is sent before TVM_INSERTITEM returns + * and before the item is added to the items array. The + * fix is to check for null. + * + * NOTE: This only happens on XP with the version 6.00 of + * COMCTL32.DLL, + */ + if (item == null) return null; + int /*long*/ hDC = nmcd.hdc; + int index = hwndHeader != 0 ? (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0) : 0; + int /*long*/ hFont = item.fontHandle (index); + if (hFont != -1) OS.SelectObject (hDC, hFont); + if (ignoreCustomDraw || nmcd.left == nmcd.right) { + return new LRESULT (hFont == -1 ? OS.CDRF_DODEFAULT : OS.CDRF_NEWFONT); + } + RECT clipRect = null; + if (columnCount != 0) { + boolean clip = !printClient; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + clip = true; + } + if (clip) { + clipRect = new RECT (); + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_WIDTH; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem); + OS.SetRect (clipRect, nmcd.left, nmcd.top, nmcd.left + hdItem.cxy, nmcd.bottom); + } + } + int clrText = -1, clrTextBk = -1; + if (OS.IsWindowEnabled (handle)) { + clrText = item.cellForeground != null ? item.cellForeground [index] : -1; + if (clrText == -1) clrText = item.foreground; + clrTextBk = item.cellBackground != null ? item.cellBackground [index] : -1; + if (clrTextBk == -1) clrTextBk = item.background; + } + int clrSortBk = -1; + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if (sortColumn != null && sortDirection != SWT.NONE) { + if (findImageControl () == null) { + if (indexOf (sortColumn) == index) { + clrSortBk = getSortColumnPixel (); + if (clrTextBk == -1) clrTextBk = clrSortBk; + } + } + } + } + boolean selected = isItemSelected (nmcd); + boolean hot = explorerTheme && (nmcd.uItemState & OS.CDIS_HOT) != 0; + boolean focused = explorerTheme && (nmcd.uItemState & OS.CDIS_FOCUS) != 0; + if (OS.IsWindowVisible (handle) && nmcd.left < nmcd.right && nmcd.top < nmcd.bottom) { + if (hFont != -1) OS.SelectObject (hDC, hFont); + if (linesVisible) { + RECT rect = new RECT (); + OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom); + OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM); + } + //TODO - BUG - measure and erase sent when first column is clipped + Event measureEvent = null; + if (hooks (SWT.MeasureItem)) { + measureEvent = sendMeasureItemEvent (item, index, hDC); + if (isDisposed () || item.isDisposed ()) return null; + } + selectionForeground = -1; + ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = ignoreFullSelection = false; + if (hooks (SWT.EraseItem)) { + RECT rect = new RECT (); + OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom); + RECT cellRect = item.getBounds (index, true, true, true, true, true, hDC); + if (clrSortBk != -1) { + drawBackground (hDC, cellRect, clrSortBk); + } else { + if (OS.IsWindowEnabled (handle) || findImageControl () != null) { + drawBackground (hDC, rect); + } else { + fillBackground (hDC, OS.GetBkColor (hDC), rect); + } + } + int nSavedDC = OS.SaveDC (hDC); + GCData data = new GCData (); + data.device = display; + if (selected && explorerTheme) { + data.foreground = OS.GetSysColor (OS.COLOR_WINDOWTEXT); + } else { + data.foreground = OS.GetTextColor (hDC); + } + data.background = OS.GetBkColor (hDC); + if (!selected) { + if (clrText != -1) data.foreground = clrText; + if (clrTextBk != -1) data.background = clrTextBk; + } + data.uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + data.font = item.getFont (index); + GC gc = GC.win32_new (hDC, data); + Event event = new Event (); + event.index = index; + event.item = item; + event.gc = gc; + event.detail |= SWT.FOREGROUND; + if (clrTextBk != -1) event.detail |= SWT.BACKGROUND; + if (hot) event.detail |= SWT.HOT; + if (selected) event.detail |= SWT.SELECTED; + if (!explorerTheme) { + //if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) { + if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0) == nmcd.dwItemSpec) { + if (handle == OS.GetFocus ()) { + int uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + if ((uiState & OS.UISF_HIDEFOCUS) == 0) { + focused = true; + event.detail |= SWT.FOCUSED; + } + } + } + } + event.x = cellRect.left; + event.y = cellRect.top; + event.width = cellRect.right - cellRect.left; + event.height = cellRect.bottom - cellRect.top; + gc.setClipping (event.x, event.y, event.width, event.height); + sendEvent (SWT.EraseItem, event); + event.gc = null; + int newTextClr = data.foreground; + gc.dispose (); + OS.RestoreDC (hDC, nSavedDC); + if (isDisposed () || item.isDisposed ()) return null; + if (event.doit) { + ignoreDrawForeground = (event.detail & SWT.FOREGROUND) == 0; + ignoreDrawBackground = (event.detail & SWT.BACKGROUND) == 0; + ignoreDrawSelection = (event.detail & SWT.SELECTED) == 0; + ignoreDrawFocus = (event.detail & SWT.FOCUSED) == 0; + ignoreDrawHot = (event.detail & SWT.HOT) == 0; + } else { + ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = true; + } + if (selected && ignoreDrawSelection) ignoreDrawHot = true; + if (!ignoreDrawBackground && clrTextBk != -1) { + boolean draw = !selected && !hot; + if (!explorerTheme && selected) draw = !ignoreDrawSelection; + if (draw) { + if (columnCount == 0) { + if ((style & SWT.FULL_SELECTION) != 0) { + fillBackground (hDC, clrTextBk, rect); + } else { + RECT textRect = item.getBounds (index, true, false, false, false, true, hDC); + if (measureEvent != null) { + textRect.right = Math.min (cellRect.right, measureEvent.x + measureEvent.width); + } + fillBackground (hDC, clrTextBk, textRect); + } + } else { + fillBackground (hDC, clrTextBk, cellRect); + } + } + } + if (ignoreDrawSelection) ignoreFullSelection = true; + if (!ignoreDrawSelection || !ignoreDrawHot) { + if (!selected && !hot) { + selectionForeground = clrText = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT); + } + if (explorerTheme) { + if ((style & SWT.FULL_SELECTION) == 0) { + RECT pRect = item.getBounds (index, true, true, false, false, false, hDC); + RECT pClipRect = item.getBounds (index, true, true, true, false, true, hDC); + if (measureEvent != null) { + pRect.right = Math.min (pClipRect.right, measureEvent.x + measureEvent.width); + } else { + pRect.right += EXPLORER_EXTRA; + pClipRect.right += EXPLORER_EXTRA; + } + pRect.left -= EXPLORER_EXTRA; + pClipRect.left -= EXPLORER_EXTRA; + int /*long*/ hTheme = OS.OpenThemeData (handle, Display.TREEVIEW); + int iStateId = selected ? OS.TREIS_SELECTED : OS.TREIS_HOT; + if (OS.GetFocus () != handle && selected && !hot) iStateId = OS.TREIS_SELECTEDNOTFOCUS; + OS.DrawThemeBackground (hTheme, hDC, OS.TVP_TREEITEM, iStateId, pRect, pClipRect); + OS.CloseThemeData (hTheme); + } + } else { + /* + * Feature in Windows. When the tree has the style + * TVS_FULLROWSELECT, the background color for the + * entire row is filled when an item is painted, + * drawing on top of any custom drawing. The fix + * is to emulate TVS_FULLROWSELECT. + */ + if ((style & SWT.FULL_SELECTION) != 0) { + if ((style & SWT.FULL_SELECTION) != 0 && columnCount == 0) { + fillBackground (hDC, OS.GetBkColor (hDC), rect); + } else { + fillBackground (hDC, OS.GetBkColor (hDC), cellRect); + } + } else { + RECT textRect = item.getBounds (index, true, false, false, false, true, hDC); + if (measureEvent != null) { + textRect.right = Math.min (cellRect.right, measureEvent.x + measureEvent.width); + } + fillBackground (hDC, OS.GetBkColor (hDC), textRect); + } + } + } else { + if (selected || hot) { + selectionForeground = clrText = newTextClr; + ignoreDrawSelection = ignoreDrawHot = true; + } + if (explorerTheme) { + nmcd.uItemState |= OS.CDIS_DISABLED; + /* + * Feature in Windows. On Vista only, when the text + * color is unchanged and an item is asked to draw + * disabled, it uses the disabled color. The fix is + * to modify the color so that is it no longer equal. + */ + int newColor = clrText == -1 ? getForegroundPixel () : clrText; + if (nmcd.clrText == newColor) { + nmcd.clrText |= 0x20000000; + if (nmcd.clrText == newColor) nmcd.clrText &= ~0x20000000; + } else { + nmcd.clrText = newColor; + } + OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof); + } + } + if (focused && !ignoreDrawFocus && (style & SWT.FULL_SELECTION) == 0) { + RECT textRect = item.getBounds (index, true, explorerTheme, false, false, true, hDC); + if (measureEvent != null) { + textRect.right = Math.min (cellRect.right, measureEvent.x + measureEvent.width); + } + nmcd.uItemState &= ~OS.CDIS_FOCUS; + OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof); + focusRect = textRect; + } + if (explorerTheme) { + if (selected || (hot && ignoreDrawHot)) nmcd.uItemState &= ~OS.CDIS_HOT; + OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof); + } + RECT itemRect = item.getBounds (index, true, true, false, false, false, hDC); + OS.SaveDC (hDC); + OS.SelectClipRgn (hDC, 0); + if (explorerTheme) { + itemRect.left -= EXPLORER_EXTRA; + itemRect.right += EXPLORER_EXTRA; + } + //TODO - bug in Windows selection or SWT itemRect + /*if (selected)*/ itemRect.right++; + if (linesVisible) itemRect.bottom++; + if (clipRect != null) { + OS.IntersectClipRect (hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); + } + OS.ExcludeClipRect (hDC, itemRect.left, itemRect.top, itemRect.right, itemRect.bottom); + return new LRESULT (OS.CDRF_DODEFAULT | OS.CDRF_NOTIFYPOSTPAINT); + } + /* + * Feature in Windows. When the tree has the style + * TVS_FULLROWSELECT, the background color for the + * entire row is filled when an item is painted, + * drawing on top of any custom drawing. The fix + * is to emulate TVS_FULLROWSELECT. + */ + if ((style & SWT.FULL_SELECTION) != 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.TVS_FULLROWSELECT) == 0) { + RECT rect = new RECT (); + OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom); + if (selected) { + fillBackground (hDC, OS.GetBkColor (hDC), rect); + } else { + if (OS.IsWindowEnabled (handle)) drawBackground (hDC, rect); + } + nmcd.uItemState &= ~OS.CDIS_FOCUS; + OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof); + } + } + } + LRESULT result = null; + if (clrText == -1 && clrTextBk == -1 && hFont == -1) { + result = new LRESULT (OS.CDRF_DODEFAULT | OS.CDRF_NOTIFYPOSTPAINT); + } else { + result = new LRESULT (OS.CDRF_NEWFONT | OS.CDRF_NOTIFYPOSTPAINT); + if (hFont != -1) OS.SelectObject (hDC, hFont); + if (OS.IsWindowEnabled (handle) && OS.IsWindowVisible (handle)) { + /* + * Feature in Windows. Windows does not fill the entire cell + * with the background color when TVS_FULLROWSELECT is not set. + * The fix is to fill the cell with the background color. + */ + if (clrTextBk != -1) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.TVS_FULLROWSELECT) == 0) { + if (columnCount != 0 && hwndHeader != 0) { + RECT rect = new RECT (); + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_WIDTH; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem); + OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.left + hdItem.cxy, nmcd.bottom); + if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) { + RECT itemRect = new RECT (); + if (OS.TreeView_GetItemRect (handle, item.handle, itemRect, true)) { + rect.left = Math.min (itemRect.left, rect.right); + } + } + if ((style & SWT.FULL_SELECTION) != 0) { + if (!selected) fillBackground (hDC, clrTextBk, rect); + } else { + if (explorerTheme) { + if (!selected && !hot) fillBackground (hDC, clrTextBk, rect); + } else { + fillBackground (hDC, clrTextBk, rect); + } + } + } else { + if ((style & SWT.FULL_SELECTION) != 0) { + RECT rect = new RECT (); + OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom); + if (!selected) fillBackground (hDC, clrTextBk, rect); + } + } + } + } + if (!selected) { + nmcd.clrText = clrText == -1 ? getForegroundPixel () : clrText; + nmcd.clrTextBk = clrTextBk == -1 ? getBackgroundPixel () : clrTextBk; + OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof); + } + } + } + if (OS.IsWindowEnabled (handle)) { + /* + * On Vista only, when an item is asked to draw disabled, + * the background of the text is not filled with the + * background color of the tree. This is true for both + * regular and full selection trees. In order to draw a + * background image, mark the item as disabled using + * CDIS_DISABLED (when not selected) and set the text + * to the regular text color to avoid drawing disabled. + */ + if (explorerTheme) { + if (findImageControl () != null) { + if (!selected && (nmcd.uItemState & (OS.CDIS_HOT | OS.CDIS_SELECTED)) == 0) { + nmcd.uItemState |= OS.CDIS_DISABLED; + /* + * Feature in Windows. On Vista only, when the text + * color is unchanged and an item is asked to draw + * disabled, it uses the disabled color. The fix is + * to modify the color so it is no longer equal. + */ + int newColor = clrText == -1 ? getForegroundPixel () : clrText; + if (nmcd.clrText == newColor) { + nmcd.clrText |= 0x20000000; + if (nmcd.clrText == newColor) nmcd.clrText &= ~0x20000000; + } else { + nmcd.clrText = newColor; + } + OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof); + if (clrTextBk != -1) { + if ((style & SWT.FULL_SELECTION) != 0) { + RECT rect = new RECT (); + if (columnCount != 0) { + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_WIDTH; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem); + OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.left + hdItem.cxy, nmcd.bottom); + } else { + OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom); + } + fillBackground (hDC, clrTextBk, rect); + } else { + RECT textRect = item.getBounds (index, true, false, true, false, true, hDC); + fillBackground (hDC, clrTextBk, textRect); + } + } + } + } + } + } else { + /* + * Feature in Windows. When the tree is disabled, it draws + * with a gray background over the sort column. The fix is + * to fill the background with the sort column color. + */ + if (clrSortBk != -1) { + RECT rect = new RECT (); + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_WIDTH; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem); + OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.left + hdItem.cxy, nmcd.bottom); + fillBackground (hDC, clrSortBk, rect); + } + } + OS.SaveDC (hDC); + if (clipRect != null) { + int /*long*/ hRgn = OS.CreateRectRgn (clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); + POINT lpPoint = new POINT (); + OS.GetWindowOrgEx (hDC, lpPoint); + OS.OffsetRgn (hRgn, -lpPoint.x, -lpPoint.y); + OS.SelectClipRgn (hDC, hRgn); + OS.DeleteObject (hRgn); + } + return result; +} + +LRESULT CDDS_POSTPAINT (NMTVCUSTOMDRAW nmcd, int /*long*/ wParam, int /*long*/ lParam) { + if (ignoreCustomDraw) return null; + if (OS.IsWindowVisible (handle)) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if (sortColumn != null && sortDirection != SWT.NONE) { + if (findImageControl () == null) { + int index = indexOf (sortColumn); + if (index != -1) { + int top = nmcd.top; + /* + * Bug in Windows. For some reason, during a collapse, + * when TVM_GETNEXTITEM is sent with TVGN_LASTVISIBLE + * and the collapse causes the item being collapsed + * to become the last visible item in the tree, the + * message takes a long time to process. In order for + * the slowness to happen, the children of the item + * must have children. Times of up to 11 seconds have + * been observed with 23 children, each having one + * child. The fix is to use the bottom partially + * visible item rather than the last possible item + * that could be visible. + * + * NOTE: This problem only happens on Vista during + * WM_NOTIFY with NM_CUSTOMDRAW and CDDS_POSTPAINT. + */ + int /*long*/ hItem = 0; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + hItem = getBottomItem (); + } else { + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_LASTVISIBLE, 0); + } + if (hItem != 0) { + RECT rect = new RECT (); + if (OS.TreeView_GetItemRect (handle, hItem, rect, false)) { + top = rect.bottom; + } + } + RECT rect = new RECT (); + OS.SetRect (rect, nmcd.left, top, nmcd.right, nmcd.bottom); + RECT headerRect = new RECT (); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect); + rect.left = headerRect.left; + rect.right = headerRect.right; + fillBackground (nmcd.hdc, getSortColumnPixel (), rect); + } + } + } + } + if (linesVisible) { + int /*long*/ hDC = nmcd.hdc; + if (hwndHeader != 0) { + int x = 0; + RECT rect = new RECT (); + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_WIDTH; + for (int i=0; i<columnCount; i++) { + int index = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, i, 0); + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem); + OS.SetRect (rect, x, nmcd.top, x + hdItem.cxy, nmcd.bottom); + OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_RIGHT); + x += hdItem.cxy; + } + } + int height = 0; + RECT rect = new RECT (); + /* + * Bug in Windows. For some reason, during a collapse, + * when TVM_GETNEXTITEM is sent with TVGN_LASTVISIBLE + * and the collapse causes the item being collapsed + * to become the last visible item in the tree, the + * message takes a long time to process. In order for + * the slowness to happen, the children of the item + * must have children. Times of up to 11 seconds have + * been observed with 23 children, each having one + * child. The fix is to use the bottom partially + * visible item rather than the last possible item + * that could be visible. + * + * NOTE: This problem only happens on Vista during + * WM_NOTIFY with NM_CUSTOMDRAW and CDDS_POSTPAINT. + */ + int /*long*/ hItem = 0; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + hItem = getBottomItem (); + } else { + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_LASTVISIBLE, 0); + } + if (hItem != 0) { + if (OS.TreeView_GetItemRect (handle, hItem, rect, false)) { + height = rect.bottom - rect.top; + } + } + if (height == 0) { + height = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMHEIGHT, 0, 0); + OS.GetClientRect (handle, rect); + OS.SetRect (rect, rect.left, rect.top, rect.right, rect.top + height); + OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM); + } + while (rect.bottom < nmcd.bottom) { + int top = rect.top + height; + OS.SetRect (rect, rect.left, top, rect.right, top + height); + OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM); + } + } + } + return new LRESULT (OS.CDRF_DODEFAULT); +} + +LRESULT CDDS_PREPAINT (NMTVCUSTOMDRAW nmcd, int /*long*/ wParam, int /*long*/ lParam) { + if (explorerTheme) { + if ((OS.IsWindowEnabled (handle) && hooks (SWT.EraseItem)) || findImageControl () != null) { + RECT rect = new RECT (); + OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom); + drawBackground (nmcd.hdc, rect); + } + } + return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (handle == 0) return 0; + if (hwndParent != 0 && hwnd == hwndParent) { + return OS.DefWindowProc (hwnd, msg, wParam, lParam); + } + if (hwndHeader != 0 && hwnd == hwndHeader) { + return OS.CallWindowProc (HeaderProc, hwnd, msg, wParam, lParam); + } + switch (msg) { + case OS.WM_SETFOCUS: { + /* + * Feature in Windows. When a tree control processes WM_SETFOCUS, + * if no item is selected, the first item in the tree is selected. + * This is unexpected and might clear the previous selection. + * The fix is to detect that there is no selection and set it to + * the first visible item in the tree. If the item was not selected, + * only the focus is assigned. + */ + if ((style & SWT.SINGLE) != 0) break; + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (hItem == 0) { + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); + if (hItem != 0) { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + hSelect = hItem; + ignoreDeselect = ignoreSelect = lockSelection = true; + OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, hItem); + ignoreDeselect = ignoreSelect = lockSelection = false; + hSelect = 0; + if ((tvItem.state & OS.TVIS_SELECTED) == 0) { + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + } + } + break; + } + } + int /*long*/ hItem = 0; + boolean redraw = false; + switch (msg) { + /* Keyboard messages */ + case OS.WM_KEYDOWN: + if (wParam == OS.VK_CONTROL || wParam == OS.VK_SHIFT) break; + //FALL THROUGH + case OS.WM_CHAR: + case OS.WM_IME_CHAR: + case OS.WM_KEYUP: + case OS.WM_SYSCHAR: + case OS.WM_SYSKEYDOWN: + case OS.WM_SYSKEYUP: + //FALL THROUGH + + /* Scroll messages */ + case OS.WM_HSCROLL: + case OS.WM_VSCROLL: + //FALL THROUGH + + /* Resize messages */ + case OS.WM_SIZE: + redraw = findImageControl () != null && getDrawing () && OS.IsWindowVisible (handle); + if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + //FALL THROUGH + + /* Mouse messages */ + case OS.WM_LBUTTONDBLCLK: + case OS.WM_LBUTTONDOWN: + case OS.WM_LBUTTONUP: + case OS.WM_MBUTTONDBLCLK: + case OS.WM_MBUTTONDOWN: + case OS.WM_MBUTTONUP: + case OS.WM_MOUSEHOVER: + case OS.WM_MOUSELEAVE: + case OS.WM_MOUSEMOVE: + case OS.WM_MOUSEWHEEL: + case OS.WM_RBUTTONDBLCLK: + case OS.WM_RBUTTONDOWN: + case OS.WM_RBUTTONUP: + case OS.WM_XBUTTONDBLCLK: + case OS.WM_XBUTTONDOWN: + case OS.WM_XBUTTONUP: + //FALL THROUGH + + /* Other messages */ + case OS.WM_SETFONT: + case OS.WM_TIMER: { + if (findImageControl () != null) { + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); + } + break; + } + } + int /*long*/ code = OS.CallWindowProc (TreeProc, hwnd, msg, wParam, lParam); + switch (msg) { + /* Keyboard messages */ + case OS.WM_KEYDOWN: + if (wParam == OS.VK_CONTROL || wParam == OS.VK_SHIFT) break; + //FALL THROUGH + case OS.WM_CHAR: + case OS.WM_IME_CHAR: + case OS.WM_KEYUP: + case OS.WM_SYSCHAR: + case OS.WM_SYSKEYDOWN: + case OS.WM_SYSKEYUP: + //FALL THROUGH + + /* Scroll messages */ + case OS.WM_HSCROLL: + case OS.WM_VSCROLL: + //FALL THROUGH + + /* Resize messages */ + case OS.WM_SIZE: + if (redraw) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.InvalidateRect (handle, null, true); + if (hwndHeader != 0) OS.InvalidateRect (hwndHeader, null, true); + } + //FALL THROUGH + + /* Mouse messages */ + case OS.WM_LBUTTONDBLCLK: + case OS.WM_LBUTTONDOWN: + case OS.WM_LBUTTONUP: + case OS.WM_MBUTTONDBLCLK: + case OS.WM_MBUTTONDOWN: + case OS.WM_MBUTTONUP: + case OS.WM_MOUSEHOVER: + case OS.WM_MOUSELEAVE: + case OS.WM_MOUSEMOVE: + case OS.WM_MOUSEWHEEL: + case OS.WM_RBUTTONDBLCLK: + case OS.WM_RBUTTONDOWN: + case OS.WM_RBUTTONUP: + case OS.WM_XBUTTONDBLCLK: + case OS.WM_XBUTTONDOWN: + case OS.WM_XBUTTONUP: + //FALL THROUGH + + /* Other messages */ + case OS.WM_SETFONT: + case OS.WM_TIMER: { + if (findImageControl () != null) { + if (hItem != OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0)) { + OS.InvalidateRect (handle, null, true); + } + } + updateScrollBar (); + break; + } + + case OS.WM_PAINT: + painted = true; + break; + } + return code; +} + +void checkBuffered () { + super.checkBuffered (); + if ((style & SWT.VIRTUAL) != 0) { + style |= SWT.DOUBLE_BUFFERED; + OS.SendMessage (handle, OS.TVM_SETSCROLLTIME, 0, 0); + } + if (EXPLORER_THEME) { + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0) && OS.IsAppThemed ()) { + int exStyle = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0); + if ((exStyle & OS.TVS_EX_DOUBLEBUFFER) != 0) style |= SWT.DOUBLE_BUFFERED; + } + } +} + +boolean checkData (TreeItem item, boolean redraw) { + if ((style & SWT.VIRTUAL) == 0) return true; + if (!item.cached) { + TreeItem parentItem = item.getParentItem (); + return checkData (item, parentItem == null ? indexOf (item) : parentItem.indexOf (item), redraw); + } + return true; +} + +boolean checkData (TreeItem item, int index, boolean redraw) { + if ((style & SWT.VIRTUAL) == 0) return true; + if (!item.cached) { + item.cached = true; + Event event = new Event (); + event.item = item; + event.index = index; + TreeItem oldItem = currentItem; + currentItem = item; + /* + * Bug in Windows. If the tree scrolls during WM_NOTIFY + * with TVN_GETDISPINFO, pixel corruption occurs. The fix + * is to detect that the top item has changed and redraw + * the entire tree. + */ + int /*long*/ hTopItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); + sendEvent (SWT.SetData, event); + //widget could be disposed at this point + currentItem = oldItem; + if (isDisposed () || item.isDisposed ()) return false; + if (redraw) item.redraw (); + if (hTopItem != OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0)) { + OS.InvalidateRect (handle, null, true); + } + } + return true; +} + +boolean checkHandle (int /*long*/ hwnd) { + return hwnd == handle || (hwndParent != 0 && hwnd == hwndParent) || (hwndHeader != 0 && hwnd == hwndHeader); +} + +boolean checkScroll (int /*long*/ hItem) { + /* + * Feature in Windows. If redraw is turned off using WM_SETREDRAW + * and a tree item that is not a child of the first root is selected or + * scrolled using TVM_SELECTITEM or TVM_ENSUREVISIBLE, then scrolling + * does not occur. The fix is to detect this case, and make sure + * that redraw is temporarily enabled. To avoid flashing, DefWindowProc() + * is called to disable redrawing. + * + * NOTE: The code that actually works around the problem is in the + * callers of this method. + */ + if (getDrawing ()) return false; + int /*long*/ hRoot = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + int /*long*/ hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem); + while (hParent != hRoot && hParent != 0) { + hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hParent); + } + return hParent == 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 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 /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + if (hItem == 0) error (SWT.ERROR_INVALID_RANGE); + hItem = findItem (hItem, index); + if (hItem == 0) error (SWT.ERROR_INVALID_RANGE); + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + clear (hItem, tvItem); + if (all) { + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); + clearAll (hItem, tvItem, all); + } +} + +void clear (int /*long*/ hItem, TVITEM tvItem) { + tvItem.hItem = hItem; + TreeItem item = null; + if (OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem) != 0) { + item = tvItem.lParam != -1 ? items [(int)/*64*/tvItem.lParam] : null; + } + if (item != null) { + if ((style & SWT.VIRTUAL) != 0 && !item.cached) return; + item.clear (); + item.redraw (); + } +} + +/** + * 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 (); + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + if (hItem == 0) return; + if (all) { + boolean redraw = false; + for (int i=0; i<items.length; i++) { + TreeItem item = items [i]; + if (item != null && item != currentItem) { + item.clear (); + redraw = true; + } + } + if (redraw) OS.InvalidateRect (handle, null, true); + } else { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + clearAll (hItem, tvItem, all); + } +} + +void clearAll (int /*long*/ hItem, TVITEM tvItem, boolean all) { + while (hItem != 0) { + clear (hItem, tvItem); + if (all) { + int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); + clearAll (hFirstItem, tvItem, all); + } + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); + } +} + +int /*long*/ CompareFunc (int /*long*/ lParam1, int /*long*/ lParam2, int /*long*/ lParamSort) { + TreeItem item1 = items [(int)/*64*/lParam1], item2 = items [(int)/*64*/lParam2]; + String text1 = item1.getText ((int)/*64*/lParamSort), text2 = item2.getText ((int)/*64*/lParamSort); + return sortDirection == SWT.UP ? text1.compareTo (text2) : text2.compareTo (text1); +} + +public Point computeSize (int wHint, int hHint, boolean changed) { + checkWidget (); + int width = 0, height = 0; + if (hwndHeader != 0) { + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_WIDTH; + for (int i=0; i<columnCount; i++) { + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, i, hdItem); + width += hdItem.cxy; + } + RECT rect = new RECT (); + OS.GetWindowRect (hwndHeader, rect); + height += rect.bottom - rect.top; + } + RECT rect = new RECT (); + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + while (hItem != 0) { + if ((style & SWT.VIRTUAL) == 0 && !painted) { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_TEXT; + tvItem.hItem = hItem; + tvItem.pszText = OS.LPSTR_TEXTCALLBACK; + ignoreCustomDraw = true; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + ignoreCustomDraw = false; + } + if (OS.TreeView_GetItemRect (handle, hItem, rect, true)) { + width = Math.max (width, rect.right); + height += rect.bottom - rect.top; + } + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem); + } + 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; + if ((style & SWT.V_SCROLL) != 0) { + width += OS.GetSystemMetrics (OS.SM_CXVSCROLL); + } + if ((style & SWT.H_SCROLL) != 0) { + height += OS.GetSystemMetrics (OS.SM_CYHSCROLL); + } + return new Point (width, height); +} + +void createHandle () { + super.createHandle (); + state &= ~(CANVAS | THEME_BACKGROUND); + + /* Use the Explorer theme */ + if (EXPLORER_THEME) { + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0) && OS.IsAppThemed ()) { + explorerTheme = true; + OS.SetWindowTheme (handle, Display.EXPLORER, null); + int bits = OS.TVS_EX_DOUBLEBUFFER | OS.TVS_EX_FADEINOUTEXPANDOS | OS.TVS_EX_RICHTOOLTIP; + /* + * This code is intentionally commented. + */ +// if ((style & SWT.FULL_SELECTION) == 0) bits |= OS.TVS_EX_AUTOHSCROLL; + OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, 0, bits); + /* + * Bug in Windows. When the tree is using the explorer + * theme, it does not use COLOR_WINDOW_TEXT for the + * default foreground color. The fix is to explicitly + * set the foreground. + */ + setForegroundPixel (-1); + } + } + + /* + * Feature in Windows. In version 5.8 of COMCTL32.DLL, + * if the font is changed for an item, the bounds for the + * item are not updated, causing the text to be clipped. + * The fix is to detect the version of COMCTL32.DLL, and + * if it is one of the versions with the problem, then + * use version 5.00 of the control (a version that does + * not have the problem). This is the recommended work + * around from the MSDN. + */ + if (!OS.IsWinCE) { + if (OS.COMCTL32_MAJOR < 6) { + OS.SendMessage (handle, OS.CCM_SETVERSION, 5, 0); + } + } + + /* Set the checkbox image list */ + if ((style & SWT.CHECK) != 0) setCheckboxImageList (); + + /* + * Feature in Windows. When the control is created, + * it does not use the default system font. A new HFONT + * is created and destroyed when the control is destroyed. + * This means that a program that queries the font from + * this control, uses the font in another control and then + * destroys this control will have the font unexpectedly + * destroyed in the other control. The fix is to assign + * the font ourselves each time the control is created. + * The control will not destroy a font that it did not + * create. + */ + int /*long*/ hFont = OS.GetStockObject (OS.SYSTEM_FONT); + OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0); +} + +void createHeaderToolTips () { + if (OS.IsWinCE) return; + if (headerToolTipHandle != 0) return; + int bits = 0; + if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) { + if ((style & SWT.RIGHT_TO_LEFT) != 0) bits |= OS.WS_EX_LAYOUTRTL; + } + headerToolTipHandle = OS.CreateWindowEx ( + bits, + new TCHAR (0, OS.TOOLTIPS_CLASS, true), + null, + OS.TTS_NOPREFIX, + OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0, + handle, + 0, + OS.GetModuleHandle (null), + null); + if (headerToolTipHandle == 0) error (SWT.ERROR_NO_HANDLES); + /* + * Feature in Windows. Despite the fact that the + * tool tip text contains \r\n, the tooltip will + * not honour the new line unless TTM_SETMAXTIPWIDTH + * is set. The fix is to set TTM_SETMAXTIPWIDTH to + * a large value. + */ + OS.SendMessage (headerToolTipHandle, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF); +} + +void createItem (TreeColumn column, int index) { + if (hwndHeader == 0) createParent (); + if (!(0 <= index && index <= columnCount)) error (SWT.ERROR_INVALID_RANGE); + if (columnCount == columns.length) { + TreeColumn [] newColumns = new TreeColumn [columns.length + 4]; + System.arraycopy (columns, 0, newColumns, 0, columns.length); + columns = newColumns; + } + for (int i=0; i<items.length; i++) { + TreeItem item = items [i]; + if (item != null) { + String [] strings = item.strings; + if (strings != null) { + String [] temp = new String [columnCount + 1]; + System.arraycopy (strings, 0, temp, 0, index); + System.arraycopy (strings, index, temp, index + 1, columnCount - index); + item.strings = temp; + } + Image [] images = item.images; + if (images != null) { + Image [] temp = new Image [columnCount + 1]; + System.arraycopy (images, 0, temp, 0, index); + System.arraycopy (images, index, temp, index + 1, columnCount - index); + item.images = temp; + } + if (index == 0) { + if (columnCount != 0) { + if (strings == null) { + item.strings = new String [columnCount + 1]; + item.strings [1] = item.text; + } + item.text = ""; + if (images == null) { + item.images = new Image [columnCount + 1]; + item.images [1] = item.image; + } + item.image = null; + } + } + if (item.cellBackground != null) { + int [] cellBackground = item.cellBackground; + int [] temp = new int [columnCount + 1]; + System.arraycopy (cellBackground, 0, temp, 0, index); + System.arraycopy (cellBackground, index, temp, index + 1, columnCount - index); + temp [index] = -1; + item.cellBackground = temp; + } + if (item.cellForeground != null) { + int [] cellForeground = item.cellForeground; + int [] temp = new int [columnCount + 1]; + System.arraycopy (cellForeground, 0, temp, 0, index); + System.arraycopy (cellForeground, index, temp, index + 1, columnCount - index); + temp [index] = -1; + 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, temp, index + 1, columnCount- index); + item.cellFont = temp; + } + } + } + System.arraycopy (columns, index, columns, index + 1, columnCount++ - index); + columns [index] = column; + + /* + * Bug in Windows. For some reason, when HDM_INSERTITEM + * is used to insert an item into a header without text, + * if is not possible to set the text at a later time. + * The fix is to insert the item with an empty string. + */ + int /*long*/ hHeap = OS.GetProcessHeap (); + int /*long*/ pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof); + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_TEXT | OS.HDI_FORMAT; + hdItem.pszText = pszText; + if ((column.style & SWT.LEFT) == SWT.LEFT) hdItem.fmt = OS.HDF_LEFT; + if ((column.style & SWT.CENTER) == SWT.CENTER) hdItem.fmt = OS.HDF_CENTER; + if ((column.style & SWT.RIGHT) == SWT.RIGHT) hdItem.fmt = OS.HDF_RIGHT; + OS.SendMessage (hwndHeader, OS.HDM_INSERTITEM, index, hdItem); + if (pszText != 0) OS.HeapFree (hHeap, 0, pszText); + + /* When the first column is created, hide the horizontal scroll bar */ + if (columnCount == 1) { + scrollWidth = 0; + if ((style & SWT.H_SCROLL) != 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + bits |= OS.TVS_NOHSCROLL; + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); + } + /* + * Bug in Windows. When TVS_NOHSCROLL is set after items + * have been inserted into the tree, Windows shows the + * scroll bar. The fix is to check for this case and + * explicitly hide the scroll bar explicitly. + */ + int count = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0); + if (count != 0) { + if (!OS.IsWinCE) OS.ShowScrollBar (handle, OS.SB_HORZ, false); + } + createItemToolTips (); + if (itemToolTipHandle != 0) { + OS.SendMessage (itemToolTipHandle, OS.TTM_SETDELAYTIME, OS.TTDT_AUTOMATIC, -1); + } + } + setScrollWidth (); + updateImageList (); + updateScrollBar (); + + /* Redraw to hide the items when the first column is created */ + if (columnCount == 1 && OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0) != 0) { + OS.InvalidateRect (handle, null, true); + } + + /* Add the tool tip item for the header */ + if (headerToolTipHandle != 0) { + RECT rect = new RECT (); + if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, rect) != 0) { + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + lpti.uFlags = OS.TTF_SUBCLASS; + lpti.hwnd = hwndHeader; + lpti.uId = column.id = display.nextToolTipId++; + lpti.left = rect.left; + lpti.top = rect.top; + lpti.right = rect.right; + lpti.bottom = rect.bottom; + lpti.lpszText = OS.LPSTR_TEXTCALLBACK; + OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, lpti); + } + } +} + +void createItem (TreeItem item, int /*long*/ hParent, int /*long*/ hInsertAfter, int /*long*/ hItem) { + int id = -1; + if (item != null) { + id = lastID < items.length ? lastID : 0; + while (id < items.length && items [id] != null) id++; + if (id == items.length) { + /* + * Grow the array faster when redraw is off or the + * table is not visible. When the table is painted, + * the items array is resized to be smaller to reduce + * memory usage. + */ + int length = 0; + if (getDrawing () && OS.IsWindowVisible (handle)) { + length = items.length + 4; + } else { + shrink = true; + length = Math.max (4, items.length * 3 / 2); + } + TreeItem [] newItems = new TreeItem [length]; + System.arraycopy (items, 0, newItems, 0, items.length); + items = newItems; + } + lastID = id + 1; + } + int /*long*/ hNewItem = 0; + int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hParent); + boolean fixParent = hFirstItem == 0; + if (hItem == 0) { + TVINSERTSTRUCT tvInsert = new TVINSERTSTRUCT (); + tvInsert.hParent = hParent; + tvInsert.hInsertAfter = hInsertAfter; + tvInsert.lParam = id; + tvInsert.pszText = OS.LPSTR_TEXTCALLBACK; + tvInsert.iImage = tvInsert.iSelectedImage = OS.I_IMAGECALLBACK; + tvInsert.mask = OS.TVIF_TEXT | OS.TVIF_IMAGE | OS.TVIF_SELECTEDIMAGE | OS.TVIF_HANDLE | OS.TVIF_PARAM; + if ((style & SWT.CHECK) != 0) { + tvInsert.mask = tvInsert.mask | OS.TVIF_STATE; + tvInsert.state = 1 << 12; + tvInsert.stateMask = OS.TVIS_STATEIMAGEMASK; + } + ignoreCustomDraw = true; + hNewItem = OS.SendMessage (handle, OS.TVM_INSERTITEM, 0, tvInsert); + ignoreCustomDraw = false; + if (hNewItem == 0) error (SWT.ERROR_ITEM_NOT_ADDED); + /* + * This code is intentionally commented. + */ +// if (hParent != 0) { +// int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); +// bits |= OS.TVS_LINESATROOT; +// OS.SetWindowLong (handle, OS.GWL_STYLE, bits); +// } + } else { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + tvItem.hItem = hNewItem = hItem; + tvItem.lParam = id; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + if (item != null) { + item.handle = hNewItem; + items [id] = item; + } + if (hFirstItem == 0) { + if (hInsertAfter == OS.TVI_FIRST || hInsertAfter == OS.TVI_LAST) { + hFirstIndexOf = hLastIndexOf = hFirstItem = hNewItem; + itemCount = lastIndexOf = 0; + } + } + if (hFirstItem == hFirstIndexOf && itemCount != -1) itemCount++; + if (hItem == 0) { + /* + * Bug in Windows. When a child item is added to a parent item + * that has no children outside of WM_NOTIFY with control code + * TVN_ITEMEXPANDED, the tree widget does not redraw the +/- + * indicator. The fix is to detect the case when the first + * child is added to a visible parent item and redraw the parent. + */ + if (fixParent) { + if (getDrawing () && OS.IsWindowVisible (handle)) { + RECT rect = new RECT (); + if (OS.TreeView_GetItemRect (handle, hParent, rect, false)) { + OS.InvalidateRect (handle, rect, true); + } + } + } + /* + * Bug in Windows. When a new item is added while Windows + * is requesting data a tree item using TVN_GETDISPINFO, + * outstanding damage for items that are below the new item + * is not scrolled. The fix is to explicitly damage the + * new area. + */ + if ((style & SWT.VIRTUAL) != 0) { + if (currentItem != null) { + RECT rect = new RECT (); + if (OS.TreeView_GetItemRect (handle, hNewItem, rect, false)) { + RECT damageRect = new RECT (); + boolean damaged = OS.GetUpdateRect (handle, damageRect, true); + if (damaged && damageRect.top < rect.bottom) { + if (OS.IsWinCE) { + OS.OffsetRect (damageRect, 0, rect.bottom - rect.top); + OS.InvalidateRect (handle, damageRect, true); + } else { + int /*long*/ rgn = OS.CreateRectRgn (0, 0, 0, 0); + int result = OS.GetUpdateRgn (handle, rgn, true); + if (result != OS.NULLREGION) { + OS.OffsetRgn (rgn, 0, rect.bottom - rect.top); + OS.InvalidateRgn (handle, rgn, true); + } + OS.DeleteObject (rgn); + } + } + } + } + } + updateScrollBar (); + } +} + +void createItemToolTips () { + if (OS.IsWinCE) return; + if (itemToolTipHandle != 0) return; + int bits1 = OS.GetWindowLong (handle, OS.GWL_STYLE); + bits1 |= OS.TVS_NOTOOLTIPS; + OS.SetWindowLong (handle, OS.GWL_STYLE, bits1); + int bits2 = 0; + if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) { + if ((style & SWT.RIGHT_TO_LEFT) != 0) bits2 |= OS.WS_EX_LAYOUTRTL; + } + /* + * Feature in Windows. For some reason, when the user + * clicks on a tool tip, it temporarily takes focus, even + * when WS_EX_NOACTIVATE is specified. The fix is to + * use WS_EX_TRANSPARENT, even though WS_EX_TRANSPARENT + * is documented to affect painting, not hit testing. + * + * NOTE: Windows 2000 doesn't have the problem and + * setting WS_EX_TRANSPARENT causes pixel corruption. + */ + if (OS.COMCTL32_MAJOR >= 6) bits2 |= OS.WS_EX_TRANSPARENT; + itemToolTipHandle = OS.CreateWindowEx ( + bits2, + new TCHAR (0, OS.TOOLTIPS_CLASS, true), + null, + OS.TTS_NOPREFIX | OS.TTS_NOANIMATE | OS.TTS_NOFADE, + OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0, + handle, + 0, + OS.GetModuleHandle (null), + null); + if (itemToolTipHandle == 0) error (SWT.ERROR_NO_HANDLES); + OS.SendMessage (itemToolTipHandle, OS.TTM_SETDELAYTIME, OS.TTDT_INITIAL, 0); + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + lpti.hwnd = handle; + lpti.uId = handle; + lpti.uFlags = OS.TTF_SUBCLASS | OS.TTF_TRANSPARENT; + lpti.lpszText = OS.LPSTR_TEXTCALLBACK; + OS.SendMessage (itemToolTipHandle, OS.TTM_ADDTOOL, 0, lpti); +} + +void createParent () { + forceResize (); + RECT rect = new RECT (); + OS.GetWindowRect (handle, rect); + OS.MapWindowPoints (0, parent.handle, rect, 2); + int oldStyle = OS.GetWindowLong (handle, OS.GWL_STYLE); + int newStyle = super.widgetStyle () & ~OS.WS_VISIBLE; + if ((oldStyle & OS.WS_DISABLED) != 0) newStyle |= OS.WS_DISABLED; +// if ((oldStyle & OS.WS_VISIBLE) != 0) newStyle |= OS.WS_VISIBLE; + hwndParent = OS.CreateWindowEx ( + super.widgetExtStyle (), + super.windowClass (), + null, + newStyle, + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + parent.handle, + 0, + OS.GetModuleHandle (null), + null); + if (hwndParent == 0) error (SWT.ERROR_NO_HANDLES); + OS.SetWindowLongPtr (hwndParent, OS.GWLP_ID, hwndParent); + int bits = 0; + if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) { + bits |= OS.WS_EX_NOINHERITLAYOUT; + if ((style & SWT.RIGHT_TO_LEFT) != 0) bits |= OS.WS_EX_LAYOUTRTL; + } + hwndHeader = OS.CreateWindowEx ( + bits, + HeaderClass, + null, + OS.HDS_BUTTONS | OS.HDS_FULLDRAG | OS.HDS_DRAGDROP | OS.HDS_HIDDEN | OS.WS_CHILD | OS.WS_CLIPSIBLINGS, + 0, 0, 0, 0, + hwndParent, + 0, + OS.GetModuleHandle (null), + null); + if (hwndHeader == 0) error (SWT.ERROR_NO_HANDLES); + OS.SetWindowLongPtr (hwndHeader, OS.GWLP_ID, hwndHeader); + if (OS.IsDBLocale) { + int /*long*/ hIMC = OS.ImmGetContext (handle); + OS.ImmAssociateContext (hwndParent, hIMC); + OS.ImmAssociateContext (hwndHeader, hIMC); + OS.ImmReleaseContext (handle, hIMC); + } + //This code is intentionally commented +// if (!OS.IsPPC) { +// if ((style & SWT.BORDER) != 0) { +// int oldExStyle = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); +// oldExStyle &= ~OS.WS_EX_CLIENTEDGE; +// OS.SetWindowLong (handle, OS.GWL_EXSTYLE, oldExStyle); +// } +// } + int /*long*/ hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (hFont != 0) OS.SendMessage (hwndHeader, OS.WM_SETFONT, hFont, 0); + int /*long*/ hwndInsertAfter = OS.GetWindow (handle, OS.GW_HWNDPREV); + int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE; + SetWindowPos (hwndParent, hwndInsertAfter, 0, 0, 0, 0, flags); + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_RANGE | OS.SIF_PAGE; + OS.GetScrollInfo (hwndParent, OS.SB_HORZ, info); + info.nPage = info.nMax + 1; + OS.SetScrollInfo (hwndParent, OS.SB_HORZ, info, true); + OS.GetScrollInfo (hwndParent, OS.SB_VERT, info); + info.nPage = info.nMax + 1; + OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true); + customDraw = true; + deregister (); + if ((oldStyle & OS.WS_VISIBLE) != 0) { + OS.ShowWindow (hwndParent, OS.SW_SHOW); + } + int /*long*/ hwndFocus = OS.GetFocus (); + if (hwndFocus == handle) OS.SetFocus (hwndParent); + OS.SetParent (handle, hwndParent); + if (hwndFocus == handle) OS.SetFocus (handle); + register (); + subclass (); +} + +void createWidget () { + super.createWidget (); + items = new TreeItem [4]; + columns = new TreeColumn [4]; + itemCount = -1; +} + +int defaultBackground () { + return OS.GetSysColor (OS.COLOR_WINDOW); +} + +void deregister () { + super.deregister (); + if (hwndParent != 0) display.removeControl (hwndParent); + if (hwndHeader != 0) display.removeControl (hwndHeader); +} + +void deselect (int /*long*/ hItem, TVITEM tvItem, int /*long*/ hIgnoreItem) { + while (hItem != 0) { + if (hItem != hIgnoreItem) { + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); + deselect (hFirstItem, tvItem, hIgnoreItem); + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); + } +} + +/** + * 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); + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_SELECTED; + tvItem.hItem = item.handle; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); +} + +/** + * 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 (); + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_SELECTED; + if ((style & SWT.SINGLE) != 0) { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (hItem != 0) { + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + } else { + int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC); + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc); + if ((style & SWT.VIRTUAL) != 0) { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + deselect (hItem, tvItem, 0); + } else { + for (int i=0; i<items.length; i++) { + TreeItem item = items [i]; + if (item != null) { + tvItem.hItem = item.handle; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + } + } + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); + } +} + +void destroyItem (TreeColumn column) { + if (hwndHeader == 0) error (SWT.ERROR_ITEM_NOT_REMOVED); + int index = 0; + while (index < columnCount) { + if (columns [index] == column) break; + index++; + } + int [] oldOrder = new int [columnCount]; + OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, oldOrder); + int orderIndex = 0; + while (orderIndex < columnCount) { + if (oldOrder [orderIndex] == index) break; + orderIndex++; + } + RECT headerRect = new RECT (); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect); + if (OS.SendMessage (hwndHeader, OS.HDM_DELETEITEM, index, 0) == 0) { + error (SWT.ERROR_ITEM_NOT_REMOVED); + } + System.arraycopy (columns, index + 1, columns, index, --columnCount - index); + columns [columnCount] = null; + for (int i=0; i<items.length; i++) { + TreeItem item = items [i]; + if (item != null) { + if (columnCount == 0) { + 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]; + System.arraycopy (strings, 0, temp, 0, index); + System.arraycopy (strings, index + 1, temp, index, columnCount - 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]; + System.arraycopy (images, 0, temp, 0, index); + System.arraycopy (images, index + 1, temp, index, columnCount - index); + item.images = temp; + } else { + if (index == 0) item.image = null; + } + if (item.cellBackground != null) { + int [] cellBackground = item.cellBackground; + int [] temp = new int [columnCount]; + System.arraycopy (cellBackground, 0, temp, 0, index); + System.arraycopy (cellBackground, index + 1, temp, index, columnCount - index); + item.cellBackground = temp; + } + if (item.cellForeground != null) { + int [] cellForeground = item.cellForeground; + int [] temp = new int [columnCount]; + System.arraycopy (cellForeground, 0, temp, 0, index); + System.arraycopy (cellForeground, index + 1, temp, index, columnCount - index); + item.cellForeground = temp; + } + if (item.cellFont != null) { + Font [] cellFont = item.cellFont; + Font [] temp = new Font [columnCount]; + System.arraycopy (cellFont, 0, temp, 0, index); + System.arraycopy (cellFont, index + 1, temp, index, columnCount - index); + item.cellFont = temp; + } + } + } + } + + /* + * When the last column is deleted, show the horizontal + * scroll bar. Otherwise, left align the first column + * and redraw the columns to the right. + */ + if (columnCount == 0) { + scrollWidth = 0; + if (!hooks (SWT.MeasureItem)) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((style & SWT.H_SCROLL) != 0) bits &= ~OS.TVS_NOHSCROLL; + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); + OS.InvalidateRect (handle, null, true); + } + if (itemToolTipHandle != 0) { + OS.SendMessage (itemToolTipHandle, OS.TTM_SETDELAYTIME, OS.TTDT_INITIAL, 0); + } + } else { + if (index == 0) { + columns [0].style &= ~(SWT.LEFT | SWT.RIGHT | SWT.CENTER); + columns [0].style |= SWT.LEFT; + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_FORMAT | OS.HDI_IMAGE; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem); + hdItem.fmt &= ~OS.HDF_JUSTIFYMASK; + hdItem.fmt |= OS.HDF_LEFT; + OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem); + } + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + rect.left = headerRect.left; + OS.InvalidateRect (handle, rect, true); + } + setScrollWidth (); + updateImageList (); + updateScrollBar (); + if (columnCount != 0) { + int [] newOrder = new int [columnCount]; + OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, newOrder); + TreeColumn [] newColumns = new TreeColumn [columnCount - orderIndex]; + for (int i=orderIndex; i<newOrder.length; i++) { + newColumns [i - orderIndex] = columns [newOrder [i]]; + newColumns [i - orderIndex].updateToolTip (newOrder [i]); + } + for (int i=0; i<newColumns.length; i++) { + if (!newColumns [i].isDisposed ()) { + newColumns [i].sendEvent (SWT.Move); + } + } + } + + /* Remove the tool tip item for the header */ + if (headerToolTipHandle != 0) { + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + lpti.uId = column.id; + lpti.hwnd = hwndHeader; + OS.SendMessage (headerToolTipHandle, OS.TTM_DELTOOL, 0, lpti); + } +} + +void destroyItem (TreeItem item, int /*long*/ hItem) { + hFirstIndexOf = hLastIndexOf = 0; + itemCount = -1; + /* + * Feature in Windows. When an item is removed that is not + * visible in the tree because it belongs to a collapsed branch, + * Windows redraws the tree causing a flash for each item that + * is removed. The fix is to detect whether the item is visible, + * force the widget to be fully painted, turn off redraw, remove + * the item and validate the damage caused by the removing of + * the item. + * + * NOTE: This fix is not necessary when double buffering and + * can cause problems for virtual trees due to the call to + * UpdateWindow() that flushes outstanding WM_PAINT events, + * allowing application code to add or remove items during + * this remove operation. + */ + int /*long*/ hParent = 0; + boolean fixRedraw = false; + if ((style & SWT.DOUBLE_BUFFERED) == 0) { + if (getDrawing () && OS.IsWindowVisible (handle)) { + RECT rect = new RECT (); + fixRedraw = !OS.TreeView_GetItemRect (handle, hItem, rect, false); + } + } + if (fixRedraw) { + hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem); + OS.UpdateWindow (handle); + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + /* + * This code is intentionally commented. + */ +// OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); + } + if ((style & SWT.MULTI) != 0) { + ignoreDeselect = ignoreSelect = lockSelection = true; + } + + /* + * Feature in Windows. When an item is deleted and a tool tip + * is showing, Window requests the new text for the new item + * that is under the cursor right away. This means that when + * multiple items are deleted, the tool tip flashes, showing + * each new item in the tool tip as it is scrolled into view. + * The fix is to hide tool tips when any item is deleted. + * + * NOTE: This only happens on Vista. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.TVM_GETTOOLTIPS, 0, 0); + if (hwndToolTip != 0) OS.SendMessage (hwndToolTip, OS.TTM_POP, 0 ,0); + } + + shrink = ignoreShrink = true; + OS.SendMessage (handle, OS.TVM_DELETEITEM, 0, hItem); + ignoreShrink = false; + if ((style & SWT.MULTI) != 0) { + ignoreDeselect = ignoreSelect = lockSelection = false; + } + if (fixRedraw) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.ValidateRect (handle, null); + /* + * If the item that was deleted was the last child of a tree item that + * is visible, redraw the parent item to force the +/- to be updated. + */ + if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hParent) == 0) { + RECT rect = new RECT (); + if (OS.TreeView_GetItemRect (handle, hParent, rect, false)) { + OS.InvalidateRect (handle, rect, true); + } + } + } + int count = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0); + if (count == 0) { + if (imageList != null) { + OS.SendMessage (handle, OS.TVM_SETIMAGELIST, 0, 0); + display.releaseImageList (imageList); + } + imageList = null; + if (hwndParent == 0 && !linesVisible) { + if (!hooks (SWT.MeasureItem) && !hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) { + customDraw = false; + } + } + items = new TreeItem [4]; + scrollWidth = 0; + setScrollWidth (); + } + updateScrollBar (); +} + +void destroyScrollBar (int type) { + super.destroyScrollBar (type); + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((style & (SWT.H_SCROLL | SWT.V_SCROLL)) == 0) { + bits &= ~(OS.WS_HSCROLL | OS.WS_VSCROLL); + bits |= OS.TVS_NOSCROLL; + } else { + if ((style & SWT.H_SCROLL) == 0) { + bits &= ~OS.WS_HSCROLL; + bits |= OS.TVS_NOHSCROLL; + } + } + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); +} + +void enableDrag (boolean enabled) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if (enabled && hooks (SWT.DragDetect)) { + bits &= ~OS.TVS_DISABLEDRAGDROP; + } else { + bits |= OS.TVS_DISABLEDRAGDROP; + } + OS.SetWindowLong (handle, OS.GWL_STYLE, bits); +} + +void enableWidget (boolean enabled) { + super.enableWidget (enabled); + /* + * Feature in Windows. When a tree is given a background color + * using TVM_SETBKCOLOR and the tree is disabled, Windows draws + * the tree using the background color rather than the disabled + * colors. This is different from the table which draws grayed. + * The fix is to set the default background color while the tree + * is disabled and restore it when enabled. + */ + Control control = findBackgroundControl (); + /* + * Bug in Windows. On Vista only, Windows does not draw using + * the background color when the tree is disabled. The fix is + * to set the default color, even when the color has not been + * changed, causing Windows to draw correctly. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + if (control == null) control = this; + } + if (control != null) { + if (control.backgroundImage == null) { + _setBackgroundPixel (enabled ? control.getBackgroundPixel () : -1); + } + } + if (hwndParent != 0) OS.EnableWindow (hwndParent, enabled); + + /* + * Feature in Windows. When the tree has the style + * TVS_FULLROWSELECT, the background color for the + * entire row is filled when an item is painted, + * drawing on top of the sort column color. The fix + * is to clear TVS_FULLROWSELECT when a their is + * as sort column. + */ + updateFullSelection (); +} + +boolean findCell (int x, int y, TreeItem [] item, int [] index, RECT [] cellRect, RECT [] itemRect) { + boolean found = false; + TVHITTESTINFO lpht = new TVHITTESTINFO (); + lpht.x = x; + lpht.y = y; + OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht); + if (lpht.hItem != 0) { + item [0] = _getItem (lpht.hItem); + POINT pt = new POINT (); + pt.x = x; + pt.y = y; + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + RECT rect = new RECT (); + if (hwndParent != 0) { + OS.GetClientRect (hwndParent, rect); + OS.MapWindowPoints (hwndParent, handle, rect, 2); + } else { + OS.GetClientRect (handle, rect); + } + int count = Math.max (1, columnCount); + int [] order = new int [count]; + if (hwndHeader != 0) OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, order); + index [0] = 0; + boolean quit = false; + while (index [0] < count && !quit) { + int /*long*/ hFont = item [0].fontHandle (order [index [0]]); + if (hFont != -1) hFont = OS.SelectObject (hDC, hFont); + cellRect [0] = item [0].getBounds (order [index [0]], true, false, true, false, true, hDC); + if (cellRect [0].left > rect.right) { + quit = true; + } else { + cellRect [0].right = Math.min (cellRect [0].right, rect.right); + if (OS.PtInRect (cellRect [0], pt)) { + if (isCustomToolTip ()) { + Event event = sendMeasureItemEvent (item [0], order [index [0]], hDC); + if (isDisposed () || item [0].isDisposed ()) break; + itemRect [0] = new RECT (); + itemRect [0].left = event.x; + itemRect [0].right = event.x + event.width; + itemRect [0].top = event.y; + itemRect [0].bottom = event.y + event.height; + } else { + itemRect [0] = item [0].getBounds (order [index [0]], true, false, false, false, false, hDC); + } + if (itemRect [0].right > cellRect [0].right) found = true; + quit = true; + } + } + if (hFont != -1) OS.SelectObject (hDC, hFont); + if (!found) index [0]++; + } + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + } + return found; +} + +int findIndex (int /*long*/ hFirstItem, int /*long*/ hItem) { + if (hFirstItem == 0) return -1; + if (hFirstItem == hFirstIndexOf) { + if (hFirstIndexOf == hItem) { + hLastIndexOf = hFirstIndexOf; + return lastIndexOf = 0; + } + if (hLastIndexOf == hItem) return lastIndexOf; + int /*long*/ hPrevItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hLastIndexOf); + if (hPrevItem == hItem) { + hLastIndexOf = hPrevItem; + return --lastIndexOf; + } + int /*long*/ hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hLastIndexOf); + if (hNextItem == hItem) { + hLastIndexOf = hNextItem; + return ++lastIndexOf; + } + int previousIndex = lastIndexOf - 1; + while (hPrevItem != 0 && hPrevItem != hItem) { + hPrevItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hPrevItem); + --previousIndex; + } + if (hPrevItem == hItem) { + hLastIndexOf = hPrevItem; + return lastIndexOf = previousIndex; + } + int nextIndex = lastIndexOf + 1; + while (hNextItem != 0 && hNextItem != hItem) { + hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem); + nextIndex++; + } + if (hNextItem == hItem) { + hLastIndexOf = hNextItem; + return lastIndexOf = nextIndex; + } + return -1; + } + int index = 0; + int /*long*/ hNextItem = hFirstItem; + while (hNextItem != 0 && hNextItem != hItem) { + hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem); + index++; + } + if (hNextItem == hItem) { + itemCount = -1; + hFirstIndexOf = hFirstItem; + hLastIndexOf = hNextItem; + return lastIndexOf = index; + } + return -1; +} + +Widget findItem (int /*long*/ hItem) { + return _getItem (hItem); +} + +int /*long*/ findItem (int /*long*/ hFirstItem, int index) { + if (hFirstItem == 0) return 0; + if (hFirstItem == hFirstIndexOf) { + if (index == 0) { + lastIndexOf = 0; + return hLastIndexOf = hFirstIndexOf; + } + if (lastIndexOf == index) return hLastIndexOf; + if (lastIndexOf - 1 == index) { + --lastIndexOf; + return hLastIndexOf = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hLastIndexOf); + } + if (lastIndexOf + 1 == index) { + lastIndexOf++; + return hLastIndexOf = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hLastIndexOf); + } + if (index < lastIndexOf) { + int previousIndex = lastIndexOf - 1; + int /*long*/ hPrevItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hLastIndexOf); + while (hPrevItem != 0 && index < previousIndex) { + hPrevItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hPrevItem); + --previousIndex; + } + if (index == previousIndex) { + lastIndexOf = previousIndex; + return hLastIndexOf = hPrevItem; + } + } else { + int nextIndex = lastIndexOf + 1; + int /*long*/ hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hLastIndexOf); + while (hNextItem != 0 && nextIndex < index) { + hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem); + nextIndex++; + } + if (index == nextIndex) { + lastIndexOf = nextIndex; + return hLastIndexOf = hNextItem; + } + } + return 0; + } + int nextIndex = 0; + int /*long*/ hNextItem = hFirstItem; + while (hNextItem != 0 && nextIndex < index) { + hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem); + nextIndex++; + } + if (index == nextIndex) { + itemCount = -1; + lastIndexOf = nextIndex; + hFirstIndexOf = hFirstItem; + return hLastIndexOf = hNextItem; + } + return 0; +} + +TreeItem getFocusItem () { +// checkWidget (); + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + return hItem != 0 ? _getItem (hItem) : null; +} + +/** + * 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 GRID_WIDTH; +} + +/** + * 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 (); + if (hwndHeader == 0) return 0; + RECT rect = new RECT (); + OS.GetWindowRect (hwndHeader, rect); + return rect.bottom - rect.top; +} + +/** + * 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 (); + if (hwndHeader == 0) return false; + int bits = OS.GetWindowLong (hwndHeader, OS.GWL_STYLE); + return (bits & OS.WS_VISIBLE) != 0; +} + +Point getImageSize () { + if (imageList != null) return imageList.getImageSize (); + return new Point (0, getItemHeight ()); +} + +int /*long*/ getBottomItem () { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); + if (hItem == 0) return 0; + int index = 0, count = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0); + while (index <= count) { + int /*long*/ hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem); + if (hNextItem == 0) return hItem; + hItem = hNextItem; + index++; + } + return hItem; +} + +/** + * 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 (); + if (columnCount == 0) return new int [0]; + int [] order = new int [columnCount]; + OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, order); + 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 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); + int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + if (hFirstItem == 0) error (SWT.ERROR_INVALID_RANGE); + int /*long*/ hItem = findItem (hFirstItem, index); + if (hItem == 0) error (SWT.ERROR_INVALID_RANGE); + return _getItem (hItem); +} + +TreeItem getItem (NMTVCUSTOMDRAW nmcd) { + /* + * Bug in Windows. If the lParam field of TVITEM + * is changed during custom draw using TVM_SETITEM, + * the lItemlParam field of the NMTVCUSTOMDRAW struct + * is not updated until the next custom draw. The + * fix is to query the field from the item instead + * of using the struct. + */ + int id = (int)/*64*/nmcd.lItemlParam; + if ((style & SWT.VIRTUAL) != 0) { + if (id == -1) { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + tvItem.hItem = nmcd.dwItemSpec; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + id = (int)/*64*/tvItem.lParam; + } + } + return _getItem (nmcd.dwItemSpec, id); +} + +/** + * 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); + TVHITTESTINFO lpht = new TVHITTESTINFO (); + lpht.x = point.x; + lpht.y = point.y; + OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht); + if (lpht.hItem != 0) { + int flags = OS.TVHT_ONITEM; + if ((style & SWT.FULL_SELECTION) != 0) { + flags |= OS.TVHT_ONITEMRIGHT | OS.TVHT_ONITEMINDENT; + } else { + if (hooks (SWT.MeasureItem)) { + lpht.flags &= ~(OS.TVHT_ONITEMICON | OS.TVHT_ONITEMLABEL); + if (hitTestSelection (lpht.hItem, lpht.x, lpht.y)) { + lpht.flags |= OS.TVHT_ONITEMICON | OS.TVHT_ONITEMLABEL; + } + } + } + if ((lpht.flags & flags) != 0) return _getItem (lpht.hItem); + } + 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 (); + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + if (hItem == 0) return 0; + return getItemCount (hItem); +} + +int getItemCount (int /*long*/ hItem) { + int count = 0; + int /*long*/ hFirstItem = hItem; + if (hItem == hFirstIndexOf) { + if (itemCount != -1) return itemCount; + hFirstItem = hLastIndexOf; + count = lastIndexOf; + } + while (hFirstItem != 0) { + hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hFirstItem); + count++; + } + if (hItem == hFirstIndexOf) itemCount = count; + return count; +} + +/** + * 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)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMHEIGHT, 0, 0); +} + +/** + * 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 (); + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + if (hItem == 0) return new TreeItem [0]; + return getItems (hItem); +} + +TreeItem [] getItems (int /*long*/ hTreeItem) { + int count = 0; + int /*long*/ hItem = hTreeItem; + while (hItem != 0) { + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); + count++; + } + int index = 0; + TreeItem [] result = new TreeItem [count]; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + tvItem.hItem = hTreeItem; + /* + * Feature in Windows. In some cases an expand or collapse message + * can occur from within TVM_DELETEITEM. When this happens, the item + * being destroyed has been removed from the list of items but has not + * been deleted from the tree. The fix is to check for null items and + * remove them from the list. + */ + while (tvItem.hItem != 0) { + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + TreeItem item = _getItem (tvItem.hItem, (int)/*64*/tvItem.lParam); + if (item != null) result [index++] = item; + tvItem.hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, tvItem.hItem); + } + if (index != count) { + TreeItem [] newResult = new TreeItem [index]; + System.arraycopy (result, 0, newResult, 0, index); + result = newResult; + } + 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 linesVisible; +} + +int /*long*/ getNextSelection (int /*long*/ hItem, TVITEM tvItem) { + while (hItem != 0) { + int state = 0; + if (OS.IsWinCE) { + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + state = tvItem.state; + } else { + state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED); + } + if ((state & OS.TVIS_SELECTED) != 0) return hItem; + int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); + int /*long*/ hSelected = getNextSelection (hFirstItem, tvItem); + if (hSelected != 0) return hSelected; + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); + } + return 0; +} + +/** + * 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; +} + +int getSelection (int /*long*/ hItem, TVITEM tvItem, TreeItem [] selection, int index, int count, boolean bigSelection, boolean all) { + while (hItem != 0) { + if (OS.IsWinCE || bigSelection) { + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + if ((tvItem.state & OS.TVIS_SELECTED) != 0) { + if (selection != null && index < selection.length) { + selection [index] = _getItem (hItem, (int)/*64*/tvItem.lParam); + } + index++; + } + } else { + int state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED); + if ((state & OS.TVIS_SELECTED) != 0) { + if (tvItem != null && selection != null && index < selection.length) { + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + selection [index] = _getItem (hItem, (int)/*64*/tvItem.lParam); + } + index++; + } + } + if (index == count) break; + if (all) { + int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); + if ((index = getSelection (hFirstItem, tvItem, selection, index, count, bigSelection, all)) == count) { + break; + } + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); + } else { + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem); + } + } + return index; +} + +/** + * 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 (); + if ((style & SWT.SINGLE) != 0) { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (hItem == 0) return new TreeItem [0]; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE; + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + if ((tvItem.state & OS.TVIS_SELECTED) == 0) return new TreeItem [0]; + return new TreeItem [] {_getItem (tvItem.hItem, (int)/*64*/tvItem.lParam)}; + } + int count = 0; + TreeItem [] guess = new TreeItem [(style & SWT.VIRTUAL) != 0 ? 8 : 1]; + int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC); + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc); + if ((style & SWT.VIRTUAL) != 0) { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE; + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + count = getSelection (hItem, tvItem, guess, 0, -1, false, true); + } else { + TVITEM tvItem = null; + if (OS.IsWinCE) { + tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_STATE; + } + for (int i=0; i<items.length; i++) { + TreeItem item = items [i]; + if (item != null) { + int /*long*/ hItem = item.handle; + int state = 0; + if (OS.IsWinCE) { + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + state = tvItem.state; + } else { + state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED); + } + if ((state & OS.TVIS_SELECTED) != 0) { + if (count < guess.length) guess [count] = item; + count++; + } + } + } + } + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); + if (count == 0) return new TreeItem [0]; + if (count == guess.length) return guess; + TreeItem [] result = new TreeItem [count]; + if (count < guess.length) { + System.arraycopy (guess, 0, result, 0, count); + return result; + } + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc); + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE; + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + int itemCount = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0); + boolean bigSelection = result.length > itemCount / 2; + if (count != getSelection (hItem, tvItem, result, 0, count, bigSelection, false)) { + getSelection (hItem, tvItem, result, 0, count, bigSelection, true); + } + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); + 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 (); + if ((style & SWT.SINGLE) != 0) { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (hItem == 0) return 0; + int state = 0; + if (OS.IsWinCE) { + TVITEM tvItem = new TVITEM (); + tvItem.hItem = hItem; + tvItem.mask = OS.TVIF_STATE; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + state = tvItem.state; + } else { + state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED); + } + return (state & OS.TVIS_SELECTED) == 0 ? 0 : 1; + } + int count = 0; + int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC); + TVITEM tvItem = null; + if (OS.IsWinCE) { + tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_STATE; + } + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc); + if ((style & SWT.VIRTUAL) != 0) { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + count = getSelection (hItem, tvItem, null, 0, -1, false, true); + } else { + for (int i=0; i<items.length; i++) { + TreeItem item = items [i]; + if (item != null) { + int /*long*/ hItem = item.handle; + int state = 0; + if (OS.IsWinCE) { + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + state = tvItem.state; + } else { + state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED); + } + if ((state & OS.TVIS_SELECTED) != 0) count++; + } + } + } + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); + return count; +} + +/** + * 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; +} + +int getSortColumnPixel () { + int pixel = OS.IsWindowEnabled (handle) ? getBackgroundPixel () : OS.GetSysColor (OS.COLOR_3DFACE); + int red = pixel & 0xFF; + int green = (pixel & 0xFF00) >> 8; + int blue = (pixel & 0xFF0000) >> 16; + if (red > 240 && green > 240 && blue > 240) { + red -= 8; + green -= 8; + blue -= 8; + } else { + red = Math.min (0xFF, (red / 10) + red); + green = Math.min (0xFF, (green / 10) + green); + blue = Math.min (0xFF, (blue / 10) + blue); + } + return (red & 0xFF) | ((green & 0xFF) << 8) | ((blue & 0xFF) << 16); +} + +/** + * 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 (); + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); + return hItem != 0 ? _getItem (hItem) : null; +} + +boolean hitTestSelection (int /*long*/ hItem, int x, int y) { + if (hItem == 0) return false; + TreeItem item = _getItem (hItem); + if (item == null) return false; + if (!hooks (SWT.MeasureItem)) return false; + boolean result = false; + + //BUG? - moved columns, only hittest first column + //BUG? - check drag detect + int [] order = new int [1], index = new int [1]; + + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + int /*long*/ hFont = item.fontHandle (order [index [0]]); + if (hFont != -1) hFont = OS.SelectObject (hDC, hFont); + Event event = sendMeasureItemEvent (item, order [index [0]], hDC); + if (event.getBounds ().contains (x, y)) result = true; + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); +// if (isDisposed () || item.isDisposed ()) return false; + return result; +} + +int imageIndex (Image image, int index) { + if (image == null) return OS.I_IMAGENONE; + if (imageList == null) { + Rectangle bounds = image.getBounds (); + imageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height); + } + int imageIndex = imageList.indexOf (image); + if (imageIndex == -1) imageIndex = imageList.add (image); + if (hwndHeader == 0 || OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0) == index) { + /* + * Feature in Windows. When setting the same image list multiple + * times, Windows does work making this operation slow. The fix + * is to test for the same image list before setting the new one. + */ + int /*long*/ hImageList = imageList.getHandle (); + int /*long*/ hOldImageList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0); + if (hOldImageList != hImageList) { + OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, hImageList); + updateScrollBar (); + } + } + return imageIndex; +} + +int imageIndexHeader (Image image) { + if (image == null) return OS.I_IMAGENONE; + if (headerImageList == null) { + Rectangle bounds = image.getBounds (); + headerImageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height); + int index = headerImageList.indexOf (image); + if (index == -1) index = headerImageList.add (image); + int /*long*/ hImageList = headerImageList.getHandle (); + if (hwndHeader != 0) { + OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hImageList); + } + updateScrollBar (); + return index; + } + int index = headerImageList.indexOf (image); + if (index != -1) return index; + return headerImageList.add (image); +} + +/** + * 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); + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + return hItem == 0 ? -1 : findIndex (hItem, item.handle); +} + +boolean isCustomToolTip () { + return hooks (SWT.MeasureItem); +} + +boolean isItemSelected (NMTVCUSTOMDRAW nmcd) { + boolean selected = false; + if (OS.IsWindowEnabled (handle)) { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.hItem = nmcd.dwItemSpec; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + if ((tvItem.state & (OS.TVIS_SELECTED | OS.TVIS_DROPHILITED)) != 0) { + selected = true; + /* + * Feature in Windows. When the mouse is pressed and the + * selection is first drawn for a tree, the previously + * selected item is redrawn but the the TVIS_SELECTED bits + * are not cleared. When the user moves the mouse slightly + * and a drag and drop operation is not started, the item is + * drawn again and this time with TVIS_SELECTED is cleared. + * This means that an item that contains colored cells will + * not draw with the correct background until the mouse is + * moved. The fix is to test for the selection colors and + * guess that the item is not selected. + * + * NOTE: This code does not work when the foreground and + * background of the tree are set to the selection colors + * but this does not happen in a regular application. + */ + if (handle == OS.GetFocus ()) { + if (OS.GetTextColor (nmcd.hdc) != OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT)) { + selected = false; + } else { + if (OS.GetBkColor (nmcd.hdc) != OS.GetSysColor (OS.COLOR_HIGHLIGHT)) { + selected = false; + } + } + } + } else { + if (nmcd.dwDrawStage == OS.CDDS_ITEMPOSTPAINT) { + /* + * Feature in Windows. When the mouse is pressed and the + * selection is first drawn for a tree, the item is drawn + * selected, but the TVIS_SELECTED bits for the item are + * not set. When the user moves the mouse slightly and + * a drag and drop operation is not started, the item is + * drawn again and this time TVIS_SELECTED is set. This + * means that an item that is in a tree that has the style + * TVS_FULLROWSELECT and that also contains colored cells + * will not draw the entire row selected until the user + * moves the mouse. The fix is to test for the selection + * colors and guess that the item is selected. + * + * NOTE: This code does not work when the foreground and + * background of the tree are set to the selection colors + * but this does not happen in a regular application. + */ + if (OS.GetTextColor (nmcd.hdc) == OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT)) { + if (OS.GetBkColor (nmcd.hdc) == OS.GetSysColor (OS.COLOR_HIGHLIGHT)) { + selected = true; + } + } + } + } + } + return selected; +} + +void redrawSelection () { + if ((style & SWT.SINGLE) != 0) { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (hItem != 0) { + RECT rect = new RECT (); + if (OS.TreeView_GetItemRect (handle, hItem, rect, false)) { + OS.InvalidateRect (handle, rect, true); + } + } + } else { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); + if (hItem != 0) { + TVITEM tvItem = null; + if (OS.IsWinCE) { + tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_STATE; + } + RECT rect = new RECT (); + int index = 0, count = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0); + while (index <= count && hItem != 0) { + int state = 0; + if (OS.IsWinCE) { + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + state = tvItem.state; + } else { + state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED); + } + if ((state & OS.TVIS_SELECTED) != 0) { + if (OS.TreeView_GetItemRect (handle, hItem, rect, false)) { + OS.InvalidateRect (handle, rect, true); + } + } + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem); + index++; + } + } + } +} + +void register () { + super.register (); + if (hwndParent != 0) display.addControl (hwndParent, this); + if (hwndHeader != 0) display.addControl (hwndHeader, this); +} + +void releaseItem (int /*long*/ hItem, TVITEM tvItem, boolean release) { + if (hItem == hAnchor) hAnchor = 0; + if (hItem == hInsert) hInsert = 0; + tvItem.hItem = hItem; + if (OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem) != 0) { + if (tvItem.lParam != -1) { + if (tvItem.lParam < lastID) lastID = (int)/*64*/tvItem.lParam; + if (release) { + TreeItem item = items [(int)/*64*/tvItem.lParam]; + if (item != null) item.release (false); + } + items [(int)/*64*/tvItem.lParam] = null; + } + } +} + +void releaseItems (int /*long*/ hItem, TVITEM tvItem) { + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); + while (hItem != 0) { + releaseItems (hItem, tvItem); + releaseItem (hItem, tvItem, true); + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); + } +} + +void releaseHandle () { + super.releaseHandle (); + hwndParent = hwndHeader = 0; +} + +void releaseChildren (boolean destroy) { + if (items != null) { + 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<columns.length; i++) { + TreeColumn column = columns [i]; + if (column != null && !column.isDisposed ()) { + column.release (false); + } + } + columns = null; + } + super.releaseChildren (destroy); +} + +void releaseWidget () { + super.releaseWidget (); + /* + * Feature in Windows. For some reason, when TVM_GETIMAGELIST + * or TVM_SETIMAGELIST is sent, the tree issues NM_CUSTOMDRAW + * messages. This behavior is unwanted when the tree is being + * disposed. The fix is to ignore NM_CUSTOMDRAW messages by + * clearing the custom draw flag. + * + * NOTE: This only happens on Windows XP. + */ + customDraw = false; + if (imageList != null) { + OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, 0); + display.releaseImageList (imageList); + } + if (headerImageList != null) { + if (hwndHeader != 0) { + OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, 0); + } + display.releaseImageList (headerImageList); + } + imageList = headerImageList = null; + int /*long*/ hStateList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_STATE, 0); + OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_STATE, 0); + if (hStateList != 0) OS.ImageList_Destroy (hStateList); + if (itemToolTipHandle != 0) OS.DestroyWindow (itemToolTipHandle); + if (headerToolTipHandle != 0) OS.DestroyWindow (headerToolTipHandle); + itemToolTipHandle = headerToolTipHandle = 0; +} + +/** + * 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 (); + hFirstIndexOf = hLastIndexOf = 0; + itemCount = -1; + for (int i=0; i<items.length; i++) { + TreeItem item = items [i]; + if (item != null && !item.isDisposed ()) { + item.release (false); + } + } + ignoreDeselect = ignoreSelect = true; + boolean redraw = getDrawing () && OS.IsWindowVisible (handle); + if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + shrink = ignoreShrink = true; + int /*long*/ result = OS.SendMessage (handle, OS.TVM_DELETEITEM, 0, OS.TVI_ROOT); + ignoreShrink = false; + if (redraw) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.InvalidateRect (handle, null, true); + } + ignoreDeselect = ignoreSelect = false; + if (result == 0) error (SWT.ERROR_ITEM_NOT_REMOVED); + if (imageList != null) { + OS.SendMessage (handle, OS.TVM_SETIMAGELIST, 0, 0); + display.releaseImageList (imageList); + } + imageList = null; + if (hwndParent == 0 && !linesVisible) { + if (!hooks (SWT.MeasureItem) && !hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) { + customDraw = false; + } + } + hAnchor = hInsert = hFirstIndexOf = hLastIndexOf = 0; + itemCount = -1; + items = new TreeItem [4]; + scrollWidth = 0; + setScrollWidth (); + updateScrollBar (); +} + +/** + * 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); +} + +/** + * 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 (); + int /*long*/ hItem = 0; + if (item != null) { + if (item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); + hItem = item.handle; + } + hInsert = hItem; + insertAfter = !before; + OS.SendMessage (handle, OS.TVM_SETINSERTMARK, insertAfter ? 1 : 0, hInsert); +} + +/** + * 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 (); + count = Math.max (0, count); + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + setItemCount (count, OS.TVGN_ROOT, hItem); +} + +void setItemCount (int count, int /*long*/ hParent, int /*long*/ hItem) { + boolean redraw = false; + if (OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0) == 0) { + redraw = getDrawing () && OS.IsWindowVisible (handle); + if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + } + int itemCount = 0; + while (hItem != 0 && itemCount < count) { + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); + itemCount++; + } + boolean expanded = false; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + if (!redraw && (style & SWT.VIRTUAL) != 0) { + if (OS.IsWinCE) { + tvItem.hItem = hParent; + tvItem.mask = OS.TVIF_STATE; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + expanded = (tvItem.state & OS.TVIS_EXPANDED) != 0; + } else { + /* + * Bug in Windows. Despite the fact that TVM_GETITEMSTATE claims + * to return only the bits specified by the stateMask, when called + * with TVIS_EXPANDED, the entire state is returned. The fix is + * to explicitly check for the TVIS_EXPANDED bit. + */ + int state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hParent, OS.TVIS_EXPANDED); + expanded = (state & OS.TVIS_EXPANDED) != 0; + } + } + while (hItem != 0) { + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); + TreeItem item = tvItem.lParam != -1 ? items [(int)/*64*/tvItem.lParam] : null; + if (item != null && !item.isDisposed ()) { + item.dispose (); + } else { + releaseItem (tvItem.hItem, tvItem, false); + destroyItem (null, tvItem.hItem); + } + } + if ((style & SWT.VIRTUAL) != 0) { + for (int i=itemCount; i<count; i++) { + if (expanded) ignoreShrink = true; + createItem (null, hParent, OS.TVI_LAST, 0); + if (expanded) ignoreShrink = false; + } + } else { + shrink = true; + int extra = Math.max (4, (count + 3) / 4 * 4); + TreeItem [] newItems = new TreeItem [items.length + extra]; + System.arraycopy (items, 0, newItems, 0, items.length); + items = newItems; + for (int i=itemCount; i<count; i++) { + new TreeItem (this, SWT.NONE, hParent, OS.TVI_LAST, 0); + } + } + if (redraw) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.InvalidateRect (handle, null, true); + } +} + +/** + * Sets the height of the area which would be used to + * display <em>one</em> of the items in the tree. + * + * @param itemHeight 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> + * + * @since 3.2 + */ +/*public*/ void setItemHeight (int itemHeight) { + checkWidget (); + if (itemHeight < -1) error (SWT.ERROR_INVALID_ARGUMENT); + OS.SendMessage (handle, OS.TVM_SETITEMHEIGHT, itemHeight, 0); +} + +/** + * 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 (); + if (linesVisible == show) return; + linesVisible = show; + if (hwndParent == 0 && linesVisible) customDraw = true; + OS.InvalidateRect (handle, null, true); +} + +int /*long*/ scrolledHandle () { + if (hwndHeader == 0) return handle; + return columnCount == 0 && scrollWidth == 0 ? handle : hwndParent; +} + +void select (int /*long*/ hItem, TVITEM tvItem) { + while (hItem != 0) { + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); + select (hFirstItem, tvItem); + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); + } +} + +/** + * 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); + if ((style & SWT.SINGLE) != 0) { + int /*long*/ hItem = item.handle; + int state = 0; + if (OS.IsWinCE) { + TVITEM tvItem = new TVITEM (); + tvItem.hItem = hItem; + tvItem.mask = OS.TVIF_STATE; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + state = tvItem.state; + } else { + state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED); + } + if ((state & OS.TVIS_SELECTED) != 0) return; + /* + * Feature in Windows. When an item is selected with + * TVM_SELECTITEM and TVGN_CARET, the tree expands and + * scrolls to show the new selected item. Unfortunately, + * there is no other way in Windows to set the focus + * and select an item. The fix is to save the current + * scroll bar positions, turn off redraw, select the item, + * then scroll back to the original position and redraw + * the entire tree. + */ + SCROLLINFO hInfo = null; + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & (OS.TVS_NOHSCROLL | OS.TVS_NOSCROLL)) == 0) { + hInfo = new SCROLLINFO (); + hInfo.cbSize = SCROLLINFO.sizeof; + hInfo.fMask = OS.SIF_ALL; + OS.GetScrollInfo (handle, OS.SB_HORZ, hInfo); + } + SCROLLINFO vInfo = new SCROLLINFO (); + vInfo.cbSize = SCROLLINFO.sizeof; + vInfo.fMask = OS.SIF_ALL; + OS.GetScrollInfo (handle, OS.SB_VERT, vInfo); + boolean redraw = getDrawing () && OS.IsWindowVisible (handle); + if (redraw) { + OS.UpdateWindow (handle); + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + } + setSelection (item); + if (hInfo != null) { + int /*long*/ hThumb = OS.MAKELPARAM (OS.SB_THUMBPOSITION, hInfo.nPos); + OS.SendMessage (handle, OS.WM_HSCROLL, hThumb, 0); + } + /* + * Feature in Windows. It seems that Vista does not + * use wParam to get the new position when WM_VSCROLL + * is sent with SB_THUMBPOSITION. The fix is to use + * SetScrollInfo() to move the scroll bar thumb before + * calling WM_VSCROLL. + * + * NOTE: This code is only necessary on Windows Vista. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + OS.SetScrollInfo (handle, OS.SB_VERT, vInfo, true); + } + int /*long*/ vThumb = OS.MAKELPARAM (OS.SB_THUMBPOSITION, vInfo.nPos); + OS.SendMessage (handle, OS.WM_VSCROLL, vThumb, 0); + if (redraw) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.InvalidateRect (handle, null, true); + if ((style & SWT.DOUBLE_BUFFERED) == 0) { + int oldStyle = style; + style |= SWT.DOUBLE_BUFFERED; + OS.UpdateWindow (handle); + style = oldStyle; + } + } + return; + } + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_SELECTED; + tvItem.state = OS.TVIS_SELECTED; + tvItem.hItem = item.handle; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); +} + +/** + * 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; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.state = OS.TVIS_SELECTED; + tvItem.stateMask = OS.TVIS_SELECTED; + int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC); + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc); + if ((style & SWT.VIRTUAL) != 0) { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + select (hItem, tvItem); + } else { + for (int i=0; i<items.length; i++) { + TreeItem item = items [i]; + if (item != null) { + tvItem.hItem = item.handle; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + } + } + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); +} + +Event sendEraseItemEvent (TreeItem item, NMTTCUSTOMDRAW nmcd, int column, RECT cellRect) { + int nSavedDC = OS.SaveDC (nmcd.hdc); + RECT insetRect = toolTipInset (cellRect); + OS.SetWindowOrgEx (nmcd.hdc, insetRect.left, insetRect.top, null); + GCData data = new GCData (); + data.device = display; + data.foreground = OS.GetTextColor (nmcd.hdc); + data.background = OS.GetBkColor (nmcd.hdc); + data.font = item.getFont (column); + data.uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + GC gc = GC.win32_new (nmcd.hdc, data); + Event event = new Event (); + event.item = item; + event.index = column; + event.gc = gc; + event.detail |= SWT.FOREGROUND; + event.x = cellRect.left; + event.y = cellRect.top; + event.width = cellRect.right - cellRect.left; + event.height = cellRect.bottom - cellRect.top; + //gc.setClipping (event.x, event.y, event.width, event.height); + sendEvent (SWT.EraseItem, event); + event.gc = null; + //int newTextClr = data.foreground; + gc.dispose (); + OS.RestoreDC (nmcd.hdc, nSavedDC); + return event; +} + +Event sendMeasureItemEvent (TreeItem item, int index, int /*long*/ hDC) { + RECT itemRect = item.getBounds (index, true, true, false, false, false, hDC); + int nSavedDC = OS.SaveDC (hDC); + GCData data = new GCData (); + data.device = display; + data.font = item.getFont (index); + GC gc = GC.win32_new (hDC, data); + Event event = new Event (); + event.item = item; + event.gc = gc; + event.index = index; + event.x = itemRect.left; + event.y = itemRect.top; + event.width = itemRect.right - itemRect.left; + event.height = itemRect.bottom - itemRect.top; + sendEvent (SWT.MeasureItem, event); + event.gc = null; + gc.dispose (); + OS.RestoreDC (hDC, nSavedDC); + if (isDisposed () || item.isDisposed ()) return null; + if (hwndHeader != 0) { + if (columnCount == 0) { + if (event.x + event.width > scrollWidth) { + setScrollWidth (scrollWidth = event.x + event.width); + } + } + } + if (event.height > getItemHeight ()) setItemHeight (event.height); + return event; +} + +Event sendPaintItemEvent (TreeItem item, NMTTCUSTOMDRAW nmcd, int column, RECT itemRect) { + int nSavedDC = OS.SaveDC (nmcd.hdc); + RECT insetRect = toolTipInset (itemRect); + OS.SetWindowOrgEx (nmcd.hdc, insetRect.left, insetRect.top, null); + GCData data = new GCData (); + data.device = display; + data.font = item.getFont (column); + data.foreground = OS.GetTextColor (nmcd.hdc); + data.background = OS.GetBkColor (nmcd.hdc); + data.uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + GC gc = GC.win32_new (nmcd.hdc, data); + Event event = new Event (); + event.item = item; + event.index = column; + event.gc = gc; + event.detail |= SWT.FOREGROUND; + event.x = itemRect.left; + event.y = itemRect.top; + event.width = itemRect.right - itemRect.left; + event.height = itemRect.bottom - itemRect.top; + //gc.setClipping (cellRect.left, cellRect.top, cellWidth, cellHeight); + sendEvent (SWT.PaintItem, event); + event.gc = null; + gc.dispose (); + OS.RestoreDC (nmcd.hdc, nSavedDC); + return event; +} + +void setBackgroundImage (int /*long*/ hBitmap) { + super.setBackgroundImage (hBitmap); + if (hBitmap != 0) { + /* + * Feature in Windows. If TVM_SETBKCOLOR is never + * used to set the background color of a tree, the + * background color of the lines and the plus/minus + * will be drawn using the default background color, + * not the HBRUSH returned from WM_CTLCOLOR. The fix + * is to set the background color to the default (when + * it is already the default) to make Windows use the + * brush. + */ + if (OS.SendMessage (handle, OS.TVM_GETBKCOLOR, 0, 0) == -1) { + OS.SendMessage (handle, OS.TVM_SETBKCOLOR, 0, -1); + } + _setBackgroundPixel (-1); + } else { + Control control = findBackgroundControl (); + if (control == null) control = this; + if (control.backgroundImage == null) { + setBackgroundPixel (control.getBackgroundPixel ()); + } + } + /* + * Feature in Windows. When the tree has the style + * TVS_FULLROWSELECT, the background color for the + * entire row is filled when an item is painted, + * drawing on top of the background image. The fix + * is to clear TVS_FULLROWSELECT when a background + * image is set. + */ + updateFullSelection (); +} + +void setBackgroundPixel (int pixel) { + Control control = findImageControl (); + if (control != null) { + setBackgroundImage (control.backgroundImage); + return; + } + /* + * Feature in Windows. When a tree is given a background color + * using TVM_SETBKCOLOR and the tree is disabled, Windows draws + * the tree using the background color rather than the disabled + * colors. This is different from the table which draws grayed. + * The fix is to set the default background color while the tree + * is disabled and restore it when enabled. + */ + if (OS.IsWindowEnabled (handle)) _setBackgroundPixel (pixel); + + /* + * Feature in Windows. When the tree has the style + * TVS_FULLROWSELECT, the background color for the + * entire row is filled when an item is painted, + * drawing on top of the background image. The fix + * is to restore TVS_FULLROWSELECT when a background + * color is set. + */ + updateFullSelection (); +} + +void setCursor () { + /* + * Bug in Windows. Under certain circumstances, when WM_SETCURSOR + * is sent from SendMessage(), Windows GP's in the window proc for + * the tree. The fix is to avoid calling the tree window proc and + * set the cursor for the tree outside of WM_SETCURSOR. + * + * NOTE: This code assumes that the default cursor for the tree + * is IDC_ARROW. + */ + Cursor cursor = findCursor (); + int /*long*/ hCursor = cursor == null ? OS.LoadCursor (0, OS.IDC_ARROW) : cursor.handle; + OS.SetCursor (hCursor); +} + +/** + * 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 = new int [columnCount]; + OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, oldOrder); + 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_RANGE); + if (seen [index]) error (SWT.ERROR_INVALID_ARGUMENT); + seen [index] = true; + if (index != oldOrder [i]) reorder = true; + } + if (reorder) { + RECT [] oldRects = new RECT [columnCount]; + for (int i=0; i<columnCount; i++) { + oldRects [i] = new RECT (); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, oldRects [i]); + } + OS.SendMessage (hwndHeader, OS.HDM_SETORDERARRAY, order.length, order); + OS.InvalidateRect (handle, null, true); + updateImageList (); + TreeColumn [] newColumns = new TreeColumn [columnCount]; + System.arraycopy (columns, 0, newColumns, 0, columnCount); + RECT newRect = new RECT (); + for (int i=0; i<columnCount; i++) { + TreeColumn column = newColumns [i]; + if (!column.isDisposed ()) { + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, newRect); + if (newRect.left != oldRects [i].left) { + column.updateToolTip (i); + column.sendEvent (SWT.Move); + } + } + } + } +} + +void setCheckboxImageList () { + if ((style & SWT.CHECK) == 0) return; + int count = 5, flags = 0; + if (OS.IsWinCE) { + flags |= OS.ILC_COLOR; + } else { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + flags |= OS.ILC_COLOR32; + } else { + int /*long*/ hDC = OS.GetDC (handle); + int bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL); + int planes = OS.GetDeviceCaps (hDC, OS.PLANES); + OS.ReleaseDC (handle, hDC); + int depth = bits * planes; + switch (depth) { + case 4: flags |= OS.ILC_COLOR4; break; + case 8: flags |= OS.ILC_COLOR8; break; + case 16: flags |= OS.ILC_COLOR16; break; + case 24: flags |= OS.ILC_COLOR24; break; + case 32: flags |= OS.ILC_COLOR32; break; + default: flags |= OS.ILC_COLOR; break; + } + flags |= OS.ILC_MASK; + } + } + if ((style & SWT.RIGHT_TO_LEFT) != 0) flags |= OS.ILC_MIRROR; + int height = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMHEIGHT, 0, 0), width = height; + int /*long*/ hStateList = OS.ImageList_Create (width, height, flags, count, count); + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ memDC = OS.CreateCompatibleDC (hDC); + int /*long*/ hBitmap = OS.CreateCompatibleBitmap (hDC, width * count, height); + int /*long*/ hOldBitmap = OS.SelectObject (memDC, hBitmap); + RECT rect = new RECT (); + OS.SetRect (rect, 0, 0, width * count, height); + /* + * NOTE: DrawFrameControl() draws a black and white + * mask when not drawing a push button. In order to + * make the box surrounding the check mark transparent, + * fill it with a color that is neither black or white. + */ + int clrBackground = 0; + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + Control control = findBackgroundControl (); + if (control == null) control = this; + clrBackground = control.getBackgroundPixel (); + } else { + clrBackground = 0x020000FF; + if ((clrBackground & 0xFFFFFF) == OS.GetSysColor (OS.COLOR_WINDOW)) { + clrBackground = 0x0200FF00; + } + } + int /*long*/ hBrush = OS.CreateSolidBrush (clrBackground); + OS.FillRect (memDC, rect, hBrush); + OS.DeleteObject (hBrush); + int /*long*/ oldFont = OS.SelectObject (hDC, defaultFont ()); + TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA (); + OS.GetTextMetrics (hDC, tm); + OS.SelectObject (hDC, oldFont); + int itemWidth = Math.min (tm.tmHeight, width); + int itemHeight = Math.min (tm.tmHeight, height); + int left = (width - itemWidth) / 2, top = (height - itemHeight) / 2 + 1; + OS.SetRect (rect, left + width, top, left + width + itemWidth, top + itemHeight); + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + int /*long*/ hTheme = display.hButtonTheme (); + OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, rect, null); + rect.left += width; rect.right += width; + OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_CHECKEDNORMAL, rect, null); + rect.left += width; rect.right += width; + OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, rect, null); + rect.left += width; rect.right += width; + OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_MIXEDNORMAL, rect, null); + } else { + OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_FLAT); + rect.left += width; rect.right += width; + OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_FLAT); + rect.left += width; rect.right += width; + OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_INACTIVE | OS.DFCS_FLAT); + rect.left += width; rect.right += width; + OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_INACTIVE | OS.DFCS_FLAT); + } + OS.SelectObject (memDC, hOldBitmap); + OS.DeleteDC (memDC); + OS.ReleaseDC (handle, hDC); + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + OS.ImageList_Add (hStateList, hBitmap, 0); + } else { + OS.ImageList_AddMasked (hStateList, hBitmap, clrBackground); + } + OS.DeleteObject (hBitmap); + int /*long*/ hOldStateList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_STATE, 0); + OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_STATE, hStateList); + if (hOldStateList != 0) OS.ImageList_Destroy (hOldStateList); +} + +public void setFont (Font font) { + checkWidget (); + super.setFont (font); + if ((style & SWT.CHECK) != 0) setCheckboxImageList (); +} + +void setForegroundPixel (int pixel) { + /* + * Bug in Windows. When the tree is using the explorer + * theme, it does not use COLOR_WINDOW_TEXT for the + * foreground. When TVM_SETTEXTCOLOR is called with -1, + * it resets the color to black, not COLOR_WINDOW_TEXT. + * The fix is to explicitly set the color. + */ + if (explorerTheme) { + if (pixel == -1) pixel = defaultForeground (); + } + OS.SendMessage (handle, OS.TVM_SETTEXTCOLOR, 0, pixel); +} + +/** + * 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 (); + if (hwndHeader == 0) { + if (!show) return; + createParent (); + } + int bits = OS.GetWindowLong (hwndHeader, OS.GWL_STYLE); + if (show) { + if ((bits & OS.HDS_HIDDEN) == 0) return; + bits &= ~OS.HDS_HIDDEN; + OS.SetWindowLong (hwndHeader, OS.GWL_STYLE, bits); + OS.ShowWindow (hwndHeader, OS.SW_SHOW); + } else { + if ((bits & OS.HDS_HIDDEN) != 0) return; + bits |= OS.HDS_HIDDEN; + OS.SetWindowLong (hwndHeader, OS.GWL_STYLE, bits); + OS.ShowWindow (hwndHeader, OS.SW_HIDE); + } + setScrollWidth (); + updateHeaderToolTips (); + updateScrollBar (); +} + +public void setRedraw (boolean redraw) { + checkWidget (); + /* + * Feature in Windows. When WM_SETREDRAW is used to + * turn off redraw, the scroll bars are updated when + * items are added and removed. The fix is to call + * the default window proc to stop all drawing. + * + * Bug in Windows. For some reason, when WM_SETREDRAW + * is used to turn redraw on for a tree and the tree + * contains no items, the last item in the tree does + * not redraw properly. If the tree has only one item, + * that item is not drawn. If another window is dragged + * on top of the item, parts of the item are redrawn + * and erased at random. The fix is to ensure that this + * case doesn't happen by inserting and deleting an item + * when redraw is turned on and there are no items in + * the tree. + */ + int /*long*/ hItem = 0; + if (redraw) { + if (drawCount == 1) { + int count = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0); + if (count == 0) { + TVINSERTSTRUCT tvInsert = new TVINSERTSTRUCT (); + tvInsert.hInsertAfter = OS.TVI_FIRST; + hItem = OS.SendMessage (handle, OS.TVM_INSERTITEM, 0, tvInsert); + } + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + updateScrollBar (); + } + } + super.setRedraw (redraw); + if (!redraw) { + if (drawCount == 1) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + } + if (hItem != 0) { + ignoreShrink = true; + OS.SendMessage (handle, OS.TVM_DELETEITEM, 0, hItem); + ignoreShrink = false; + } +} + +void setScrollWidth () { + if (hwndHeader == 0 || hwndParent == 0) return; + int width = 0; + HDITEM hdItem = new HDITEM (); + for (int i=0; i<columnCount; i++) { + hdItem.mask = OS.HDI_WIDTH; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, i, hdItem); + width += hdItem.cxy; + } + setScrollWidth (Math.max (scrollWidth, width)); +} + +void setScrollWidth (int width) { + if (hwndHeader == 0 || hwndParent == 0) return; + //TEMPORARY CODE + //scrollWidth = width; + int left = 0; + RECT rect = new RECT (); + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_RANGE | OS.SIF_PAGE; + if (columnCount == 0 && width == 0) { + OS.GetScrollInfo (hwndParent, OS.SB_HORZ, info); + info.nPage = info.nMax + 1; + OS.SetScrollInfo (hwndParent, OS.SB_HORZ, info, true); + OS.GetScrollInfo (hwndParent, OS.SB_VERT, info); + info.nPage = info.nMax + 1; + OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true); + } else { + if ((style & SWT.H_SCROLL) != 0) { + OS.GetClientRect (hwndParent, rect); + OS.GetScrollInfo (hwndParent, OS.SB_HORZ, info); + info.nMax = width; + info.nPage = rect.right - rect.left + 1; + OS.SetScrollInfo (hwndParent, OS.SB_HORZ, info, true); + info.fMask = OS.SIF_POS; + OS.GetScrollInfo (hwndParent, OS.SB_HORZ, info); + left = info.nPos; + } + } + if (horizontalBar != null) { + horizontalBar.setIncrement (INCREMENT); + horizontalBar.setPageIncrement (info.nPage); + } + OS.GetClientRect (hwndParent, rect); + int /*long*/ hHeap = OS.GetProcessHeap (); + HDLAYOUT playout = new HDLAYOUT (); + playout.prc = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, RECT.sizeof); + playout.pwpos = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, WINDOWPOS.sizeof); + OS.MoveMemory (playout.prc, rect, RECT.sizeof); + OS.SendMessage (hwndHeader, OS.HDM_LAYOUT, 0, playout); + WINDOWPOS pos = new WINDOWPOS (); + OS.MoveMemory (pos, playout.pwpos, WINDOWPOS.sizeof); + if (playout.prc != 0) OS.HeapFree (hHeap, 0, playout.prc); + if (playout.pwpos != 0) OS.HeapFree (hHeap, 0, playout.pwpos); + SetWindowPos (hwndHeader, OS.HWND_TOP, pos.x - left, pos.y, pos.cx + left, pos.cy, OS.SWP_NOACTIVATE); + int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); + int b = (bits & OS.WS_EX_CLIENTEDGE) != 0 ? OS.GetSystemMetrics (OS.SM_CXEDGE) : 0; + int w = pos.cx + (columnCount == 0 && width == 0 ? 0 : OS.GetSystemMetrics (OS.SM_CXVSCROLL)); + int h = rect.bottom - rect.top - pos.cy; + boolean oldIgnore = ignoreResize; + ignoreResize = true; + SetWindowPos (handle, 0, pos.x - left - b, pos.y + pos.cy - b, w + left + b * 2, h + b * 2, OS.SWP_NOACTIVATE | OS.SWP_NOZORDER); + ignoreResize = oldIgnore; +} + +void setSelection (int /*long*/ hItem, TVITEM tvItem, TreeItem [] selection) { + while (hItem != 0) { + int index = 0; + while (index < selection.length) { + TreeItem item = selection [index]; + if (item != null && item.handle == hItem) break; + index++; + } + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + if ((tvItem.state & OS.TVIS_SELECTED) != 0) { + if (index == selection.length) { + tvItem.state = 0; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + } else { + if (index != selection.length) { + tvItem.state = OS.TVIS_SELECTED; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + } + int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); + setSelection (hFirstItem, tvItem, selection); + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); + } +} + +/** + * 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); + int length = items.length; + if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) { + deselectAll(); + return; + } + + /* Select/deselect the first item */ + TreeItem item = items [0]; + if (item != null) { + if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + int /*long*/ hOldItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + int /*long*/ hNewItem = hAnchor = item.handle; + + /* + * Bug in Windows. When TVM_SELECTITEM is used to select and + * scroll an item to be visible and the client area of the tree + * is smaller that the size of one item, TVM_SELECTITEM makes + * the next item in the tree visible by making it the top item + * instead of making the desired item visible. The fix is to + * detect the case when the client area is too small and make + * the desired visible item be the top item in the tree. + * + * Note that TVM_SELECTITEM when called with TVGN_FIRSTVISIBLE + * also requires the work around for scrolling. + */ + boolean fixScroll = checkScroll (hNewItem); + if (fixScroll) { + OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0); + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + } + ignoreSelect = true; + OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, hNewItem); + ignoreSelect = false; + if (OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0) == 0) { + OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_FIRSTVISIBLE, hNewItem); + int /*long*/ hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hNewItem); + if (hParent == 0) OS.SendMessage (handle, OS.WM_HSCROLL, OS.SB_TOP, 0); + } + if (fixScroll) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); + } + + /* + * Feature in Windows. When the old and new focused item + * are the same, Windows does not check to make sure that + * the item is actually selected, not just focused. The + * fix is to force the item to draw selected by setting + * the state mask, and to ensure that it is visible. + */ + if (hOldItem == hNewItem) { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.state = OS.TVIS_SELECTED; + tvItem.stateMask = OS.TVIS_SELECTED; + tvItem.hItem = hNewItem; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + showItem (hNewItem); + } + } + if ((style & SWT.SINGLE) != 0) return; + + /* Select/deselect the rest of the items */ + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_SELECTED; + int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC); + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc); + if ((style & SWT.VIRTUAL) != 0) { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + setSelection (hItem, tvItem, items); + } else { + for (int i=0; i<this.items.length; i++) { + item = this.items [i]; + if (item != null) { + int index = 0; + while (index < length) { + if (items [index] == item) break; + index++; + } + tvItem.hItem = item.handle; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + if ((tvItem.state & OS.TVIS_SELECTED) != 0) { + if (index == length) { + tvItem.state = 0; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + } else { + if (index != length) { + tvItem.state = OS.TVIS_SELECTED; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + } + } + } + } + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); +} + +/** + * 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 (sortColumn != null && !sortColumn.isDisposed ()) { + sortColumn.setSortDirection (SWT.NONE); + } + sortColumn = column; + if (sortColumn != null && sortDirection != SWT.NONE) { + sortColumn.setSortDirection (sortDirection); + } +} + +/** + * 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 | SWT.DOWN)) == 0 && direction != SWT.NONE) return; + sortDirection = direction; + if (sortColumn != null && !sortColumn.isDisposed ()) { + sortColumn.setSortDirection (direction); + } +} + +/** + * 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) SWT.error (SWT.ERROR_NULL_ARGUMENT); + if (item.isDisposed ()) SWT.error (SWT.ERROR_INVALID_ARGUMENT); + int /*long*/ hItem = item.handle; + int /*long*/ hTopItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); + if (hItem == hTopItem) return; + boolean fixScroll = checkScroll (hItem), redraw = false; + if (fixScroll) { + OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0); + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + } else { + redraw = getDrawing () && OS.IsWindowVisible (handle); + if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + } + OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_FIRSTVISIBLE, hItem); + int /*long*/ hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem); + if (hParent == 0) OS.SendMessage (handle, OS.WM_HSCROLL, OS.SB_TOP, 0); + if (fixScroll) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); + } else { + if (redraw) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.InvalidateRect (handle, null, true); + } + } + updateScrollBar (); +} + +void showItem (int /*long*/ hItem) { + /* + * Bug in Windows. When TVM_ENSUREVISIBLE is used to ensure + * that an item is visible and the client area of the tree is + * smaller that the size of one item, TVM_ENSUREVISIBLE makes + * the next item in the tree visible by making it the top item + * instead of making the desired item visible. The fix is to + * detect the case when the client area is too small and make + * the desired visible item be the top item in the tree. + */ + if (OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0) == 0) { + boolean fixScroll = checkScroll (hItem); + if (fixScroll) { + OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0); + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + } + OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_FIRSTVISIBLE, hItem); + /* This code is intentionally commented */ + //int hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem); + //if (hParent == 0) OS.SendMessage (handle, OS.WM_HSCROLL, OS.SB_TOP, 0); + OS.SendMessage (handle, OS.WM_HSCROLL, OS.SB_TOP, 0); + if (fixScroll) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); + } + } else { + boolean scroll = true; + RECT itemRect = new RECT (); + if (OS.TreeView_GetItemRect (handle, hItem, itemRect, true)) { + forceResize (); + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + POINT pt = new POINT (); + pt.x = itemRect.left; + pt.y = itemRect.top; + if (OS.PtInRect (rect, pt)) { + pt.y = itemRect.bottom; + if (OS.PtInRect (rect, pt)) scroll = false; + } + } + if (scroll) { + boolean fixScroll = checkScroll (hItem); + if (fixScroll) { + OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0); + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + } + OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, hItem); + if (fixScroll) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); + } + } + } + if (hwndParent != 0) { + RECT itemRect = new RECT (); + if (OS.TreeView_GetItemRect (handle, hItem, itemRect, true)) { + forceResize (); + RECT rect = new RECT (); + OS.GetClientRect (hwndParent, rect); + OS.MapWindowPoints (hwndParent, handle, rect, 2); + POINT pt = new POINT (); + pt.x = itemRect.left; + pt.y = itemRect.top; + if (!OS.PtInRect (rect, pt)) { + pt.y = itemRect.bottom; + if (!OS.PtInRect (rect, pt)) { + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_POS; + info.nPos = Math.max (0, pt.x - Tree.INSET / 2); + OS.SetScrollInfo (hwndParent, OS.SB_HORZ, info, true); + setScrollWidth (); + } + } + } + } + updateScrollBar (); +} + +/** + * 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; + int index = indexOf (column); + if (index == -1) return; + if (0 <= index && index < columnCount) { + forceResize (); + RECT rect = new RECT (); + OS.GetClientRect (hwndParent, rect); + OS.MapWindowPoints (hwndParent, handle, rect, 2); + RECT headerRect = new RECT (); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect); + boolean scroll = headerRect.left < rect.left; + if (!scroll) { + int width = Math.min (rect.right - rect.left, headerRect.right - headerRect.left); + scroll = headerRect.left + width > rect.right; + } + if (scroll) { + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_POS; + info.nPos = Math.max (0, headerRect.left - Tree.INSET / 2); + OS.SetScrollInfo (hwndParent, OS.SB_HORZ, info, true); + setScrollWidth (); + } + } +} + +/** + * 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); + showItem (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 (); + int /*long*/ hItem = 0; + if ((style & SWT.SINGLE) != 0) { + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (hItem == 0) return; + int state = 0; + if (OS.IsWinCE) { + TVITEM tvItem = new TVITEM (); + tvItem.hItem = hItem; + tvItem.mask = OS.TVIF_STATE; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + state = tvItem.state; + } else { + state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED); + } + if ((state & OS.TVIS_SELECTED) == 0) return; + } else { + int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC); + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc); + TVITEM tvItem = null; + if (OS.IsWinCE) { + tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_STATE; + } + if ((style & SWT.VIRTUAL) != 0) { + int /*long*/ hRoot = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + hItem = getNextSelection (hRoot, tvItem); + } else { + //FIXME - this code expands first selected item it finds + int index = 0; + while (index <items.length) { + TreeItem item = items [index]; + if (item != null) { + int state = 0; + if (OS.IsWinCE) { + tvItem.hItem = item.handle; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + state = tvItem.state; + } else { + state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, item.handle, OS.TVIS_SELECTED); + } + if ((state & OS.TVIS_SELECTED) != 0) { + hItem = item.handle; + break; + } + } + index++; + } + } + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); + } + if (hItem != 0) showItem (hItem); +} + +/*public*/ void sort () { + checkWidget (); + if ((style & SWT.VIRTUAL) != 0) return; + sort (OS.TVI_ROOT, false); +} + +void sort (int /*long*/ hParent, boolean all) { + int itemCount = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0); + if (itemCount == 0 || itemCount == 1) return; + hFirstIndexOf = hLastIndexOf = 0; + itemCount = -1; + if (sortDirection == SWT.UP || sortDirection == SWT.NONE) { + OS.SendMessage (handle, OS.TVM_SORTCHILDREN, all ? 1 : 0, hParent); + } else { + Callback compareCallback = new Callback (this, "CompareFunc", 3); + int /*long*/ lpfnCompare = compareCallback.getAddress (); + TVSORTCB psort = new TVSORTCB (); + psort.hParent = hParent; + psort.lpfnCompare = lpfnCompare; + psort.lParam = sortColumn == null ? 0 : indexOf (sortColumn); + OS.SendMessage (handle, OS.TVM_SORTCHILDRENCB, all ? 1 : 0, psort); + compareCallback.dispose (); + } +} + +void subclass () { + super.subclass (); + if (hwndHeader != 0) { + OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, display.windowProc); + } +} + +RECT toolTipInset (RECT rect) { + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + RECT insetRect = new RECT (); + OS.SetRect (insetRect, rect.left - 1, rect.top - 1, rect.right + 1, rect.bottom + 1); + return insetRect; + } + return rect; +} + +RECT toolTipRect (RECT rect) { + RECT toolRect = new RECT (); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + OS.SetRect (toolRect, rect.left - 1, rect.top - 1, rect.right + 1, rect.bottom + 1); + } else { + OS.SetRect (toolRect, rect.left, rect.top, rect.right, rect.bottom); + int dwStyle = OS.GetWindowLong (itemToolTipHandle, OS.GWL_STYLE); + int dwExStyle = OS.GetWindowLong (itemToolTipHandle, OS.GWL_EXSTYLE); + OS.AdjustWindowRectEx (toolRect, dwStyle, false, dwExStyle); + } + return toolRect; +} + +String toolTipText (NMTTDISPINFO hdr) { + int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.TVM_GETTOOLTIPS, 0, 0); + if (hwndToolTip == hdr.hwndFrom && toolTipText != null) return ""; //$NON-NLS-1$ + if (headerToolTipHandle == hdr.hwndFrom) { + for (int i=0; i<columnCount; i++) { + TreeColumn column = columns [i]; + if (column.id == hdr.idFrom) return column.toolTipText; + } + return super.toolTipText (hdr); + } + if (itemToolTipHandle == hdr.hwndFrom) { + if (toolTipText != null) return ""; + int pos = OS.GetMessagePos (); + POINT pt = new POINT(); + OS.POINTSTOPOINT (pt, pos); + OS.ScreenToClient (handle, pt); + int [] index = new int [1]; + TreeItem [] item = new TreeItem [1]; + RECT [] cellRect = new RECT [1], itemRect = new RECT [1]; + if (findCell (pt.x, pt.y, item, index, cellRect, itemRect)) { + String text = null; + if (index [0] == 0) { + text = item [0].text; + } else { + String[] strings = item [0].strings; + if (strings != null) text = strings [index [0]]; + } + //TEMPORARY CODE + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + if (isCustomToolTip ()) text = " "; + } + if (text != null) return text; + } + } + return super.toolTipText (hdr); +} + +int /*long*/ topHandle () { + return hwndParent != 0 ? hwndParent : handle; +} + +void updateFullSelection () { + if ((style & SWT.FULL_SELECTION) != 0) { + int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits; + if ((newBits & OS.TVS_FULLROWSELECT) != 0) { + if (!OS.IsWindowEnabled (handle) || findImageControl () != null) { + if (!explorerTheme) newBits &= ~OS.TVS_FULLROWSELECT; + } + } else { + if (OS.IsWindowEnabled (handle) && findImageControl () == null) { + if (!hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) { + newBits |= OS.TVS_FULLROWSELECT; + } + } + } + if (newBits != oldBits) { + OS.SetWindowLong (handle, OS.GWL_STYLE, newBits); + OS.InvalidateRect (handle, null, true); + } + } +} + +void updateHeaderToolTips () { + if (headerToolTipHandle == 0) return; + RECT rect = new RECT (); + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + lpti.uFlags = OS.TTF_SUBCLASS; + lpti.hwnd = hwndHeader; + lpti.lpszText = OS.LPSTR_TEXTCALLBACK; + for (int i=0; i<columnCount; i++) { + TreeColumn column = columns [i]; + if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, rect) != 0) { + lpti.uId = column.id = display.nextToolTipId++; + lpti.left = rect.left; + lpti.top = rect.top; + lpti.right = rect.right; + lpti.bottom = rect.bottom; + OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, lpti); + } + } +} + +void updateImageList () { + if (imageList == null) return; + if (hwndHeader == 0) return; + int i = 0, index = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0); + while (i < items.length) { + TreeItem item = items [i]; + if (item != null) { + Image image = null; + if (index == 0) { + image = item.image; + } else { + Image [] images = item.images; + if (images != null) image = images [index]; + } + if (image != null) break; + } + i++; + } + /* + * Feature in Windows. When setting the same image list multiple + * times, Windows does work making this operation slow. The fix + * is to test for the same image list before setting the new one. + */ + int /*long*/ hImageList = i == items.length ? 0 : imageList.getHandle (); + int /*long*/ hOldImageList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0); + if (hImageList != hOldImageList) { + OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, hImageList); + } +} + +void updateImages () { + if (sortColumn != null && !sortColumn.isDisposed ()) { + if (OS.COMCTL32_MAJOR < 6) { + switch (sortDirection) { + case SWT.UP: + case SWT.DOWN: + sortColumn.setImage (display.getSortImage (sortDirection), true, true); + break; + } + } + } +} + +void updateScrollBar () { + if (hwndParent != 0) { + if (columnCount != 0 || scrollWidth != 0) { + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_ALL; + int itemCount = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0); + if (itemCount == 0) { + OS.GetScrollInfo (hwndParent, OS.SB_VERT, info); + info.nPage = info.nMax + 1; + OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true); + } else { + OS.GetScrollInfo (handle, OS.SB_VERT, info); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) { + if (info.nPage == 0) { + SCROLLBARINFO psbi = new SCROLLBARINFO (); + psbi.cbSize = SCROLLBARINFO.sizeof; + OS.GetScrollBarInfo (handle, OS.OBJID_VSCROLL, psbi); + if ((psbi.rgstate [0] & OS.STATE_SYSTEM_INVISIBLE) != 0) { + info.nPage = info.nMax + 1; + } + } + } + OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true); + } + } + } +} + +void unsubclass () { + super.unsubclass (); + if (hwndHeader != 0) { + OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc); + } +} + +int widgetStyle () { + int bits = super.widgetStyle () | OS.TVS_SHOWSELALWAYS | OS.TVS_LINESATROOT | OS.TVS_HASBUTTONS | OS.TVS_NONEVENHEIGHT; + if (EXPLORER_THEME && !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0) && OS.IsAppThemed ()) { + bits |= OS.TVS_TRACKSELECT; + if ((style & SWT.FULL_SELECTION) != 0) bits |= OS.TVS_FULLROWSELECT; + } else { + if ((style & SWT.FULL_SELECTION) != 0) { + bits |= OS.TVS_FULLROWSELECT; + } else { + bits |= OS.TVS_HASLINES; + } + } + if ((style & (SWT.H_SCROLL | SWT.V_SCROLL)) == 0) { + bits &= ~(OS.WS_HSCROLL | OS.WS_VSCROLL); + bits |= OS.TVS_NOSCROLL; + } else { + if ((style & SWT.H_SCROLL) == 0) { + bits &= ~OS.WS_HSCROLL; + bits |= OS.TVS_NOHSCROLL; + } + } +// bits |= OS.TVS_NOTOOLTIPS | OS.TVS_DISABLEDRAGDROP; + return bits | OS.TVS_DISABLEDRAGDROP; +} + +TCHAR windowClass () { + return TreeClass; +} + +int /*long*/ windowProc () { + return TreeProc; +} + +int /*long*/ windowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (hwndHeader != 0 && hwnd == hwndHeader) { + switch (msg) { + case OS.WM_CONTEXTMENU: { + LRESULT result = wmContextMenu (hwnd, wParam, lParam); + if (result != null) return result.value; + break; + } + case OS.WM_CAPTURECHANGED: { + /* + * Bug in Windows. When the capture changes during a + * header drag, Windows does not redraw the header item + * such that the header remains pressed. For example, + * when focus is assigned to a push button, the mouse is + * pressed (but not released), then the SPACE key is + * pressed to activate the button, the capture changes, + * the header not notified and NM_RELEASEDCAPTURE is not + * sent. The fix is to redraw the header when the capture + * changes to another control. + * + * This does not happen on XP. + */ + if (OS.COMCTL32_MAJOR < 6) { + if (lParam != 0 && lParam != hwndHeader) { + OS.InvalidateRect (hwndHeader, null, true); + } + } + break; + } + case OS.WM_MOUSELEAVE: { + /* + * Bug in Windows. On XP, when a tooltip is hidden + * due to a time out or mouse press, the tooltip + * remains active although no longer visible and + * won't show again until another tooltip becomes + * active. The fix is to reset the tooltip bounds. + */ + if (OS.COMCTL32_MAJOR >= 6) updateHeaderToolTips (); + updateHeaderToolTips (); + break; + } + case OS.WM_NOTIFY: { + NMHDR hdr = new NMHDR (); + OS.MoveMemory (hdr, lParam, NMHDR.sizeof); + switch (hdr.code) { + case OS.TTN_SHOW: + case OS.TTN_POP: + case OS.TTN_GETDISPINFOA: + case OS.TTN_GETDISPINFOW: + return OS.SendMessage (handle, msg, wParam, lParam); + } + break; + } + case OS.WM_SETCURSOR: { + if (wParam == hwnd) { + int hitTest = (short) OS.LOWORD (lParam); + if (hitTest == OS.HTCLIENT) { + HDHITTESTINFO pinfo = new HDHITTESTINFO (); + int pos = OS.GetMessagePos (); + POINT pt = new POINT (); + OS.POINTSTOPOINT (pt, pos); + OS.ScreenToClient (hwnd, pt); + pinfo.x = pt.x; + pinfo.y = pt.y; + int index = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_HITTEST, 0, pinfo); + if (0 <= index && index < columnCount && !columns [index].resizable) { + if ((pinfo.flags & (OS.HHT_ONDIVIDER | OS.HHT_ONDIVOPEN)) != 0) { + OS.SetCursor (OS.LoadCursor (0, OS.IDC_ARROW)); + return 1; + } + } + } + } + break; + } + } + return callWindowProc (hwnd, msg, wParam, lParam); + } + if (hwndParent != 0 && hwnd == hwndParent) { + switch (msg) { + case OS.WM_MOVE: { + sendEvent (SWT.Move); + return 0; + } + case OS.WM_SIZE: { + setScrollWidth (); + if (ignoreResize) return 0; + setResizeChildren (false); + int /*long*/ code = callWindowProc (hwnd, OS.WM_SIZE, wParam, lParam); + sendEvent (SWT.Resize); + if (isDisposed ()) return 0; + if (layout != null) { + markLayout (false, false); + updateLayout (false, false); + } + setResizeChildren (true); + updateScrollBar (); + return code; + } + case OS.WM_NCPAINT: { + LRESULT result = wmNCPaint (hwnd, wParam, lParam); + if (result != null) return result.value; + break; + } + case OS.WM_PRINT: { + LRESULT result = wmPrint (hwnd, wParam, lParam); + if (result != null) return result.value; + break; + } + case OS.WM_COMMAND: + case OS.WM_NOTIFY: + case OS.WM_SYSCOLORCHANGE: { + return OS.SendMessage (handle, msg, wParam, lParam); + } + case OS.WM_HSCROLL: { + /* + * Bug on WinCE. lParam should be NULL when the message is not sent + * by a scroll bar control, but it contains the handle to the window. + * When the message is sent by a scroll bar control, it correctly + * contains the handle to the scroll bar. The fix is to check for + * both. + */ + if (horizontalBar != null && (lParam == 0 || lParam == hwndParent)) { + wmScroll (horizontalBar, true, hwndParent, OS.WM_HSCROLL, wParam, lParam); + } + setScrollWidth (); + break; + } + case OS.WM_VSCROLL: { + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_ALL; + OS.GetScrollInfo (hwndParent, OS.SB_VERT, info); + /* + * Update the nPos field to match the nTrackPos field + * so that the tree scrolls when the scroll bar of the + * parent is dragged. + * + * NOTE: For some reason, this code is only necessary + * on Windows Vista. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + if (OS.LOWORD (wParam) == OS.SB_THUMBTRACK) { + info.nPos = info.nTrackPos; + } + } + OS.SetScrollInfo (handle, OS.SB_VERT, info, true); + int /*long*/ code = OS.SendMessage (handle, OS.WM_VSCROLL, wParam, lParam); + OS.GetScrollInfo (handle, OS.SB_VERT, info); + OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true); + return code; + } + } + return callWindowProc (hwnd, msg, wParam, lParam); + } + if (msg == Display.DI_GETDRAGIMAGE) { + /* + * When there is more than one item selected, DI_GETDRAGIMAGE + * returns the item under the cursor. This happens because + * the tree does not have implement multi-select. The fix + * is to disable DI_GETDRAGIMAGE when more than one item is + * selected. + */ + if ((style & SWT.MULTI) != 0 || hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); + TreeItem [] items = new TreeItem [10]; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE; + int count = getSelection (hItem, tvItem, items, 0, 10, false, true); + if (count == 0) return 0; + POINT mousePos = new POINT (); + OS.POINTSTOPOINT (mousePos, OS.GetMessagePos ()); + OS.MapWindowPoints (0, handle, mousePos, 1); + RECT clientRect = new RECT (); + OS.GetClientRect(handle, clientRect); + RECT rect = items [0].getBounds (0, true, true, false); + if ((style & SWT.FULL_SELECTION) != 0) { + int width = DRAG_IMAGE_SIZE; + rect.left = Math.max (clientRect.left, mousePos.x - width / 2); + if (clientRect.right > rect.left + width) { + rect.right = rect.left + width; + } else { + rect.right = clientRect.right; + rect.left = Math.max (clientRect.left, rect.right - width); + } + } else { + rect.left = Math.max (rect.left, clientRect.left); + rect.right = Math.min (rect.right, clientRect.right); + } + int /*long*/ hRgn = OS.CreateRectRgn (rect.left, rect.top, rect.right, rect.bottom); + for (int i = 1; i < count; i++) { + if (rect.bottom - rect.top > DRAG_IMAGE_SIZE) break; + if (rect.bottom > clientRect.bottom) break; + RECT itemRect = items[i].getBounds (0, true, true, false); + if ((style & SWT.FULL_SELECTION) != 0) { + itemRect.left = rect.left; + itemRect.right = rect.right; + } else { + itemRect.left = Math.max (itemRect.left, clientRect.left); + itemRect.right = Math.min (itemRect.right, clientRect.right); + } + int /*long*/ rectRgn = OS.CreateRectRgn (itemRect.left, itemRect.top, itemRect.right, itemRect.bottom); + OS.CombineRgn (hRgn, hRgn, rectRgn, OS.RGN_OR); + OS.DeleteObject (rectRgn); + rect.bottom = itemRect.bottom; + + } + OS.GetRgnBox (hRgn, rect); + + /* Create resources */ + int /*long*/ hdc = OS.GetDC (handle); + int /*long*/ memHdc = OS.CreateCompatibleDC (hdc); + BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER (); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biWidth = rect.right - rect.left; + bmiHeader.biHeight = -(rect.bottom - rect.top); + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = 32; + bmiHeader.biCompression = OS.BI_RGB; + byte [] bmi = new byte [BITMAPINFOHEADER.sizeof]; + OS.MoveMemory (bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + int /*long*/ [] pBits = new int /*long*/ [1]; + int /*long*/ memDib = OS.CreateDIBSection (0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0); + if (memDib == 0) SWT.error (SWT.ERROR_NO_HANDLES); + int /*long*/ oldMemBitmap = OS.SelectObject (memHdc, memDib); + int colorKey = 0x0000FD; + POINT pt = new POINT (); + OS.SetWindowOrgEx (memHdc, rect.left, rect.top, pt); + OS.FillRect (memHdc, rect, findBrush (colorKey, OS.BS_SOLID)); + OS.OffsetRgn (hRgn, -rect.left, -rect.top); + OS.SelectClipRgn (memHdc, hRgn); + OS.PrintWindow (handle, memHdc, 0); + OS.SetWindowOrgEx (memHdc, pt.x, pt.y, null); + OS.SelectObject (memHdc, oldMemBitmap); + OS.DeleteDC (memHdc); + OS.ReleaseDC (0, hdc); + OS.DeleteObject (hRgn); + + SHDRAGIMAGE shdi = new SHDRAGIMAGE (); + shdi.hbmpDragImage = memDib; + shdi.crColorKey = colorKey; + shdi.sizeDragImage.cx = bmiHeader.biWidth; + shdi.sizeDragImage.cy = -bmiHeader.biHeight; + shdi.ptOffset.x = mousePos.x - rect.left; + shdi.ptOffset.y = mousePos.y - rect.top; + if ((style & SWT.MIRRORED) != 0) { + shdi.ptOffset.x = shdi.sizeDragImage.cx - shdi.ptOffset.x; + } + OS.MoveMemory (lParam, shdi, SHDRAGIMAGE.sizeof); + return 1; + } + } + return super.windowProc (hwnd, msg, wParam, lParam); +} + +LRESULT WM_CHAR (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_CHAR (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. The tree control beeps + * in WM_CHAR when the search for the item that + * matches the key stroke fails. This is the + * standard tree behavior but is unexpected when + * the key that was typed was ESC, CR or SPACE. + * The fix is to avoid calling the tree window + * proc in these cases. + */ + switch ((int)/*64*/wParam) { + case ' ': { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (hItem != 0) { + hAnchor = hItem; + OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, hItem); + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE | OS.TVIF_PARAM; + tvItem.hItem = hItem; + if ((style & SWT.CHECK) != 0) { + tvItem.stateMask = OS.TVIS_STATEIMAGEMASK; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + int state = tvItem.state >> 12; + if ((state & 0x1) != 0) { + state++; + } else { + --state; + } + tvItem.state = state << 12; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + if (!OS.IsWinCE) { + int /*long*/ id = hItem; + if (OS.COMCTL32_MAJOR >= 6) { + id = OS.SendMessage (handle, OS.TVM_MAPHTREEITEMTOACCID, hItem, 0); + } + OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, (int)/*64*/id); + } + } + tvItem.stateMask = OS.TVIS_SELECTED; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + if ((style & SWT.MULTI) != 0 && OS.GetKeyState (OS.VK_CONTROL) < 0) { + if ((tvItem.state & OS.TVIS_SELECTED) != 0) { + tvItem.state &= ~OS.TVIS_SELECTED; + } else { + tvItem.state |= OS.TVIS_SELECTED; + } + } else { + tvItem.state |= OS.TVIS_SELECTED; + } + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + TreeItem item = _getItem (hItem, (int)/*64*/tvItem.lParam); + Event event = new Event (); + event.item = item; + postEvent (SWT.Selection, event); + if ((style & SWT.CHECK) != 0) { + event = new Event (); + event.item = item; + event.detail = SWT.CHECK; + postEvent (SWT.Selection, event); + } + } + return LRESULT.ZERO; + } + case SWT.CR: { + /* + * Feature in Windows. Windows sends NM_RETURN from WM_KEYDOWN + * instead of using WM_CHAR. This means that application code + * that expects to consume the key press and therefore avoid a + * SWT.DefaultSelection event from WM_CHAR will fail. The fix + * is to implement SWT.DefaultSelection in WM_CHAR instead of + * using NM_RETURN. + */ + Event event = new Event (); + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (hItem != 0) event.item = _getItem (hItem); + postEvent (SWT.DefaultSelection, event); + return LRESULT.ZERO; + } + case SWT.ESC: + return LRESULT.ZERO; + } + return result; +} + +LRESULT WM_ERASEBKGND (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_ERASEBKGND (wParam, lParam); + if ((style & SWT.DOUBLE_BUFFERED) != 0) return LRESULT.ONE; + if (findImageControl () != null) return LRESULT.ONE; + return result; +} + +LRESULT WM_GETOBJECT (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Ensure that there is an accessible object created for this + * control because support for checked item and tree column + * accessibility is temporarily implemented in the accessibility + * package. + */ + if ((style & SWT.CHECK) != 0 || hwndParent != 0) { + if (accessible == null) accessible = new_Accessible (this); + } + return super.WM_GETOBJECT (wParam, lParam); +} + +LRESULT WM_HSCROLL (int /*long*/ wParam, int /*long*/ lParam) { + boolean fixScroll = false; + if ((style & SWT.DOUBLE_BUFFERED) != 0) { + fixScroll = (style & SWT.VIRTUAL) != 0 || hooks (SWT.EraseItem) || hooks (SWT.PaintItem); + } + if (fixScroll) { + style &= ~SWT.DOUBLE_BUFFERED; + if (explorerTheme) { + OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, OS.TVS_EX_DOUBLEBUFFER, 0); + } + } + LRESULT result = super.WM_HSCROLL (wParam, lParam); + if (fixScroll) { + style |= SWT.DOUBLE_BUFFERED; + if (explorerTheme) { + OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, OS.TVS_EX_DOUBLEBUFFER, OS.TVS_EX_DOUBLEBUFFER); + } + } + if (result != null) return result; + return result; +} + +LRESULT WM_KEYDOWN (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_KEYDOWN (wParam, lParam); + if (result != null) return result; + switch ((int)/*64*/wParam) { + case OS.VK_SPACE: + /* + * Ensure that the window proc does not process VK_SPACE + * so that it can be handled in WM_CHAR. This allows the + * application to cancel an operation that is normally + * performed in WM_KEYDOWN from WM_CHAR. + */ + return LRESULT.ZERO; + case OS.VK_ADD: + if (OS.GetKeyState (OS.VK_CONTROL) < 0) { + if (hwndHeader != 0) { + 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 () && column.getResizable ()) { + column.pack (); + } + } + } + } + break; + case OS.VK_UP: + case OS.VK_DOWN: + case OS.VK_PRIOR: + case OS.VK_NEXT: + case OS.VK_HOME: + case OS.VK_END: { + OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0); + if (itemToolTipHandle != 0) OS.ShowWindow (itemToolTipHandle, OS.SW_HIDE); + if ((style & SWT.SINGLE) != 0) break; + if (OS.GetKeyState (OS.VK_SHIFT) < 0) { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (hItem != 0) { + if (hAnchor == 0) hAnchor = hItem; + ignoreSelect = ignoreDeselect = true; + int /*long*/ code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam); + ignoreSelect = ignoreDeselect = false; + int /*long*/ hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_SELECTED; + int /*long*/ hDeselectItem = hItem; + RECT rect1 = new RECT (); + if (!OS.TreeView_GetItemRect (handle, hAnchor, rect1, false)) { + hAnchor = hItem; + OS.TreeView_GetItemRect (handle, hAnchor, rect1, false); + } + RECT rect2 = new RECT (); + OS.TreeView_GetItemRect (handle, hDeselectItem, rect2, false); + int flags = rect1.top < rect2.top ? OS.TVGN_PREVIOUSVISIBLE : OS.TVGN_NEXTVISIBLE; + while (hDeselectItem != hAnchor) { + tvItem.hItem = hDeselectItem; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + hDeselectItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, flags, hDeselectItem); + } + int /*long*/ hSelectItem = hAnchor; + OS.TreeView_GetItemRect (handle, hNewItem, rect1, false); + OS.TreeView_GetItemRect (handle, hSelectItem, rect2, false); + tvItem.state = OS.TVIS_SELECTED; + flags = rect1.top < rect2.top ? OS.TVGN_PREVIOUSVISIBLE : OS.TVGN_NEXTVISIBLE; + while (hSelectItem != hNewItem) { + tvItem.hItem = hSelectItem; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + hSelectItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, flags, hSelectItem); + } + tvItem.hItem = hNewItem; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + tvItem.hItem = hNewItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + Event event = new Event (); + event.item = _getItem (hNewItem, (int)/*64*/tvItem.lParam); + postEvent (SWT.Selection, event); + return new LRESULT (code); + } + } + if (OS.GetKeyState (OS.VK_CONTROL) < 0) { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (hItem != 0) { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_SELECTED; + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + boolean oldSelected = (tvItem.state & OS.TVIS_SELECTED) != 0; + int /*long*/ hNewItem = 0; + switch ((int)/*64*/wParam) { + case OS.VK_UP: + hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUSVISIBLE, hItem); + break; + case OS.VK_DOWN: + hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem); + break; + case OS.VK_HOME: + hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + break; + case OS.VK_PRIOR: + hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); + if (hNewItem == hItem) { + OS.SendMessage (handle, OS.WM_VSCROLL, OS.SB_PAGEUP, 0); + hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); + } + break; + case OS.VK_NEXT: + RECT rect = new RECT (), clientRect = new RECT (); + OS.GetClientRect (handle, clientRect); + hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); + do { + int /*long*/ hVisible = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hNewItem); + if (hVisible == 0) break; + if (!OS.TreeView_GetItemRect (handle, hVisible, rect, false)) break; + if (rect.bottom > clientRect.bottom) break; + if ((hNewItem = hVisible) == hItem) { + OS.SendMessage (handle, OS.WM_VSCROLL, OS.SB_PAGEDOWN, 0); + } + } while (hNewItem != 0); + break; + case OS.VK_END: + hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_LASTVISIBLE, 0); + break; + } + if (hNewItem != 0) { + OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, hNewItem); + tvItem.hItem = hNewItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + boolean newSelected = (tvItem.state & OS.TVIS_SELECTED) != 0; + boolean redraw = !newSelected && getDrawing () && OS.IsWindowVisible (handle); + if (redraw) { + OS.UpdateWindow (handle); + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + } + hSelect = hNewItem; + ignoreSelect = true; + OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, hNewItem); + ignoreSelect = false; + hSelect = 0; + if (oldSelected) { + tvItem.state = OS.TVIS_SELECTED; + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + if (!newSelected) { + tvItem.state = 0; + tvItem.hItem = hNewItem; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + if (redraw) { + RECT rect1 = new RECT (), rect2 = new RECT (); + boolean fItemRect = (style & SWT.FULL_SELECTION) == 0; + if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) fItemRect = false; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) fItemRect = false; + OS.TreeView_GetItemRect (handle, hItem, rect1, fItemRect); + OS.TreeView_GetItemRect (handle, hNewItem, rect2, fItemRect); + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.InvalidateRect (handle, rect1, true); + OS.InvalidateRect (handle, rect2, true); + OS.UpdateWindow (handle); + } + return LRESULT.ZERO; + } + } + } + int /*long*/ code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam); + hAnchor = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + return new LRESULT (code); + } + } + return result; +} + +LRESULT WM_KILLFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Bug in Windows. When a tree item that has an image + * with alpha is expanded or collapsed, the area where + * the image is drawn is not erased before it is drawn. + * This means that the image gets darker each time. + * The fix is to redraw the selection. + * + * Feature in Windows. When multiple item have + * the TVIS_SELECTED state, Windows redraws only + * the focused item in the color used to show the + * selection when the tree loses or gains focus. + * The fix is to force Windows to redraw the + * selection when focus is gained or lost. + */ + boolean redraw = (style & SWT.MULTI) != 0; + if (!redraw) { + if (!OS.IsWinCE && OS.COMCTL32_MAJOR >= 6) { + if (imageList != null) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.TVS_FULLROWSELECT) == 0) { + redraw = true; + } + } + } + } + if (redraw) redrawSelection (); + return super.WM_KILLFOCUS (wParam, lParam); +} + +LRESULT WM_LBUTTONDBLCLK (int /*long*/ wParam, int /*long*/ lParam) { + TVHITTESTINFO lpht = new TVHITTESTINFO (); + lpht.x = OS.GET_X_LPARAM (lParam); + lpht.y = OS.GET_Y_LPARAM (lParam); + OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht); + if (lpht.hItem != 0) { + if ((style & SWT.CHECK) != 0) { + if ((lpht.flags & OS.TVHT_ONITEMSTATEICON) != 0) { + Display display = this.display; + display.captureChanged = false; + sendMouseEvent (SWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam); + if (!sendMouseEvent (SWT.MouseDoubleClick, 1, handle, OS.WM_LBUTTONDBLCLK, wParam, lParam)) { + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + return LRESULT.ZERO; + } + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + OS.SetFocus (handle); + TVITEM tvItem = new TVITEM (); + tvItem.hItem = lpht.hItem; + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_STATEIMAGEMASK; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + int state = tvItem.state >> 12; + if ((state & 0x1) != 0) { + state++; + } else { + --state; + } + tvItem.state = state << 12; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + if (!OS.IsWinCE) { + int /*long*/ id = tvItem.hItem; + if (OS.COMCTL32_MAJOR >= 6) { + id = OS.SendMessage (handle, OS.TVM_MAPHTREEITEMTOACCID, tvItem.hItem, 0); + } + OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, (int)/*64*/id); + } + Event event = new Event (); + event.item = _getItem (tvItem.hItem, (int)/*64*/tvItem.lParam); + event.detail = SWT.CHECK; + postEvent (SWT.Selection, event); + return LRESULT.ZERO; + } + } + } + LRESULT result = super.WM_LBUTTONDBLCLK (wParam, lParam); + if (result == LRESULT.ZERO) return result; + if (lpht.hItem != 0) { + int flags = OS.TVHT_ONITEM; + if ((style & SWT.FULL_SELECTION) != 0) { + flags |= OS.TVHT_ONITEMRIGHT | OS.TVHT_ONITEMINDENT; + } else { + if (hooks (SWT.MeasureItem)) { + lpht.flags &= ~(OS.TVHT_ONITEMICON | OS.TVHT_ONITEMLABEL); + if (hitTestSelection (lpht.hItem, lpht.x, lpht.y)) { + lpht.flags |= OS.TVHT_ONITEMICON | OS.TVHT_ONITEMLABEL; + } + } + } + if ((lpht.flags & flags) != 0) { + Event event = new Event (); + event.item = _getItem (lpht.hItem); + postEvent (SWT.DefaultSelection, event); + } + } + return result; +} + +LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + /* + * In a multi-select tree, if the user is collapsing a subtree that + * contains selected items, clear the selection from these items and + * issue a selection event. Only items that are selected and visible + * are cleared. This code also runs in the case when the white space + * below the last item is selected. + */ + TVHITTESTINFO lpht = new TVHITTESTINFO (); + lpht.x = OS.GET_X_LPARAM (lParam); + lpht.y = OS.GET_Y_LPARAM (lParam); + OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht); + if (lpht.hItem == 0 || (lpht.flags & OS.TVHT_ONITEMBUTTON) != 0) { + Display display = this.display; + display.captureChanged = false; + if (!sendMouseEvent (SWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam)) { + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + return LRESULT.ZERO; + } + boolean fixSelection = false, deselected = false; + int /*long*/ hOldSelection = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (lpht.hItem != 0 && (style & SWT.MULTI) != 0) { + if (hOldSelection != 0) { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.hItem = lpht.hItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + if ((tvItem.state & OS.TVIS_EXPANDED) != 0) { + fixSelection = true; + tvItem.stateMask = OS.TVIS_SELECTED; + int /*long*/ hNext = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, lpht.hItem); + while (hNext != 0) { + if (hNext == hAnchor) hAnchor = 0; + tvItem.hItem = hNext; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + if ((tvItem.state & OS.TVIS_SELECTED) != 0) deselected = true; + tvItem.state = 0; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + int /*long*/ hItem = hNext = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hNext); + while (hItem != 0 && hItem != lpht.hItem) { + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem); + } + if (hItem == 0) break; + } + } + } + } + dragStarted = gestureCompleted = false; + if (fixSelection) ignoreDeselect = ignoreSelect = lockSelection = true; + int /*long*/ code = callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + if (OS.GetFocus () != handle) OS.SetFocus (handle); + } + if (fixSelection) ignoreDeselect = ignoreSelect = lockSelection = false; + int /*long*/ hNewSelection = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (hOldSelection != hNewSelection) hAnchor = hNewSelection; + if (dragStarted) { + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + } + /* + * Bug in Windows. When a tree has no images and an item is + * expanded or collapsed, for some reason, Windows changes + * the size of the selection. When the user expands a tree + * item, the selection rectangle is made a few pixels larger. + * When the user collapses an item, the selection rectangle + * is restored to the original size but the selection is not + * redrawn, causing pixel corruption. The fix is to detect + * this case and redraw the item. + */ + if ((lpht.flags & OS.TVHT_ONITEMBUTTON) != 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.TVS_FULLROWSELECT) == 0) { + if (OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0) == 0) { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (hItem != 0) { + RECT rect = new RECT (); + if (OS.TreeView_GetItemRect (handle, hItem, rect, false)) { + OS.InvalidateRect (handle, rect, true); + } + } + } + } + } + if (deselected) { + Event event = new Event (); + event.item = _getItem (lpht.hItem); + postEvent (SWT.Selection, event); + } + return new LRESULT (code); + } + + /* Look for check/uncheck */ + if ((style & SWT.CHECK) != 0) { + if ((lpht.flags & OS.TVHT_ONITEMSTATEICON) != 0) { + Display display = this.display; + display.captureChanged = false; + if (!sendMouseEvent (SWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam)) { + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + return LRESULT.ZERO; + } + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + OS.SetFocus (handle); + TVITEM tvItem = new TVITEM (); + tvItem.hItem = lpht.hItem; + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_STATEIMAGEMASK; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + int state = tvItem.state >> 12; + if ((state & 0x1) != 0) { + state++; + } else { + --state; + } + tvItem.state = state << 12; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + if (!OS.IsWinCE) { + int /*long*/ id = tvItem.hItem; + if (OS.COMCTL32_MAJOR >= 6) { + id = OS.SendMessage (handle, OS.TVM_MAPHTREEITEMTOACCID, tvItem.hItem, 0); + } + OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, (int)/*64*/id); + } + Event event = new Event (); + event.item = _getItem (tvItem.hItem, (int)/*64*/tvItem.lParam); + event.detail = SWT.CHECK; + postEvent (SWT.Selection, event); + return LRESULT.ZERO; + } + } + + /* + * Feature in Windows. When the tree has the style + * TVS_FULLROWSELECT, the background color for the + * entire row is filled when an item is painted, + * drawing on top of any custom drawing. The fix + * is to emulate TVS_FULLROWSELECT. + */ + boolean selected = false; + boolean fakeSelection = false; + if (lpht.hItem != 0) { + if ((style & SWT.FULL_SELECTION) != 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.TVS_FULLROWSELECT) == 0) fakeSelection = true; + } else { + if (hooks (SWT.MeasureItem)) { + selected = hitTestSelection (lpht.hItem, lpht.x, lpht.y); + if (selected) { + if ((lpht.flags & OS.TVHT_ONITEM) == 0) fakeSelection = true; + } + } + } + } + + /* Process the mouse when an item is not selected */ + if (!selected && (style & SWT.FULL_SELECTION) == 0) { + if ((lpht.flags & OS.TVHT_ONITEM) == 0) { + Display display = this.display; + display.captureChanged = false; + if (!sendMouseEvent (SWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam)) { + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + return LRESULT.ZERO; + } + int /*long*/ code = callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + if (OS.GetFocus () != handle) OS.SetFocus (handle); + } + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + return new LRESULT (code); + } + } + + /* Get the selected state of the item under the mouse */ + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_SELECTED; + boolean hittestSelected = false; + if ((style & SWT.MULTI) != 0) { + tvItem.hItem = lpht.hItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + hittestSelected = (tvItem.state & OS.TVIS_SELECTED) != 0; + } + + /* Get the selected state of the last selected item */ + int /*long*/ hOldItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if ((style & SWT.MULTI) != 0) { + tvItem.hItem = hOldItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + + /* Check for CONTROL or drag selection */ + if (hittestSelected || (wParam & OS.MK_CONTROL) != 0) { + /* + * Feature in Windows. When the tree is not drawing focus + * and the user selects a tree item while the CONTROL key + * is down, the tree window proc sends WM_UPDATEUISTATE + * to the top level window, causing controls within the shell + * to redraw. When drag detect is enabled, the tree window + * proc runs a modal loop that allows WM_PAINT messages to be + * delivered during WM_LBUTTONDOWN. When WM_SETREDRAW is used + * to disable drawing for the tree and a WM_PAINT happens for + * a parent of the tree (or a sibling that overlaps), the parent + * will draw on top of the tree. If WM_SETREDRAW is turned back + * on without redrawing the entire tree, pixel corruption occurs. + * This case only seems to happen when the tree has been given + * focus from WM_MOUSEACTIVATE of the shell. The fix is to + * force the WM_UPDATEUISTATE to be sent before disabling + * the drawing. + * + * NOTE: Any redraw of a parent (or sibling) will be dispatched + * during the modal drag detect loop. This code only fixes the + * case where the tree causes a redraw from WM_UPDATEUISTATE. + * In SWT, the InvalidateRect() that caused the pixel corruption + * is found in Composite.WM_UPDATEUISTATE(). + */ + int uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); + if ((uiState & OS.UISF_HIDEFOCUS) != 0) { + OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0); + } + OS.UpdateWindow (handle); + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + } else { + deselectAll (); + } + } + + /* Do the selection */ + Display display = this.display; + display.captureChanged = false; + if (!sendMouseEvent (SWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam)) { + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + return LRESULT.ZERO; + } + hSelect = lpht.hItem; + dragStarted = gestureCompleted = false; + ignoreDeselect = ignoreSelect = true; + int /*long*/ code = callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + if (OS.GetFocus () != handle) OS.SetFocus (handle); + } + int /*long*/ hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (fakeSelection) { + if (hOldItem == 0 || (hNewItem == hOldItem && lpht.hItem != hOldItem)) { + OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, lpht.hItem); + hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + } + if (!dragStarted && (state & DRAG_DETECT) != 0 && hooks (SWT.DragDetect)) { + dragStarted = dragDetect (handle, lpht.x, lpht.y, false, null, null); + } + } + ignoreDeselect = ignoreSelect = false; + hSelect = 0; + if (dragStarted) { + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + } + + /* + * Feature in Windows. When the old and new focused item + * are the same, Windows does not check to make sure that + * the item is actually selected, not just focused. The + * fix is to force the item to draw selected by setting + * the state mask. This is only necessary when the tree + * is single select. + */ + if ((style & SWT.SINGLE) != 0) { + if (hOldItem == hNewItem) { + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.state = OS.TVIS_SELECTED; + tvItem.stateMask = OS.TVIS_SELECTED; + tvItem.hItem = hNewItem; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + } + + /* Reselect the last item that was unselected */ + if ((style & SWT.MULTI) != 0) { + + /* Check for CONTROL and reselect the last item */ + if (hittestSelected || (wParam & OS.MK_CONTROL) != 0) { + if (hOldItem == hNewItem && hOldItem == lpht.hItem) { + if ((wParam & OS.MK_CONTROL) != 0) { + tvItem.state ^= OS.TVIS_SELECTED; + if (dragStarted) tvItem.state = OS.TVIS_SELECTED; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + } else { + if ((tvItem.state & OS.TVIS_SELECTED) != 0) { + tvItem.state = OS.TVIS_SELECTED; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + if ((wParam & OS.MK_CONTROL) != 0 && !dragStarted) { + if (hittestSelected) { + tvItem.state = 0; + tvItem.hItem = lpht.hItem; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + } + } + RECT rect1 = new RECT (), rect2 = new RECT (); + boolean fItemRect = (style & SWT.FULL_SELECTION) == 0; + if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) fItemRect = false; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) fItemRect = false; + OS.TreeView_GetItemRect (handle, hOldItem, rect1, fItemRect); + OS.TreeView_GetItemRect (handle, hNewItem, rect2, fItemRect); + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.InvalidateRect (handle, rect1, true); + OS.InvalidateRect (handle, rect2, true); + OS.UpdateWindow (handle); + } + + /* Check for SHIFT or normal select and deselect/reselect items */ + if ((wParam & OS.MK_CONTROL) == 0) { + if (!hittestSelected || !dragStarted) { + tvItem.state = 0; + int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC); + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc); + if ((style & SWT.VIRTUAL) != 0) { + int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + deselect (hItem, tvItem, hNewItem); + } else { + for (int i=0; i<items.length; i++) { + TreeItem item = items [i]; + if (item != null && item.handle != hNewItem) { + tvItem.hItem = item.handle; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + } + } + tvItem.hItem = hNewItem; + tvItem.state = OS.TVIS_SELECTED; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); + if ((wParam & OS.MK_SHIFT) != 0) { + RECT rect1 = new RECT (); + if (hAnchor == 0) hAnchor = hNewItem; + if (OS.TreeView_GetItemRect (handle, hAnchor, rect1, false)) { + RECT rect2 = new RECT (); + if (OS.TreeView_GetItemRect (handle, hNewItem, rect2, false)) { + int flags = rect1.top < rect2.top ? OS.TVGN_NEXTVISIBLE : OS.TVGN_PREVIOUSVISIBLE; + tvItem.state = OS.TVIS_SELECTED; + int /*long*/ hItem = tvItem.hItem = hAnchor; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + while (hItem != hNewItem) { + tvItem.hItem = hItem; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, flags, hItem); + } + } + } + } + } + } + } + if ((wParam & OS.MK_SHIFT) == 0) hAnchor = hNewItem; + + /* Issue notification */ + if (!gestureCompleted) { + tvItem.hItem = hNewItem; + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + Event event = new Event (); + event.item = _getItem (tvItem.hItem, (int)/*64*/tvItem.lParam); + postEvent (SWT.Selection, event); + } + gestureCompleted = false; + + /* + * Feature in Windows. Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN, + * the widget starts a modal loop to determine if the user wants + * to begin a drag/drop operation or marquee select. Unfortunately, + * this modal loop eats the corresponding mouse up. The fix is to + * detect the cases when the modal loop has eaten the mouse up and + * issue a fake mouse up. + */ + if (dragStarted) { + sendDragEvent (1, OS.GET_X_LPARAM (lParam), OS.GET_Y_LPARAM (lParam)); + } else { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.TVS_DISABLEDRAGDROP) == 0) { + sendMouseEvent (SWT.MouseUp, 1, handle, OS.WM_LBUTTONUP, wParam, lParam); + } + } + dragStarted = false; + return new LRESULT (code); +} + +LRESULT WM_MOUSEMOVE (int /*long*/ wParam, int /*long*/ lParam) { + Display display = this.display; + LRESULT result = super.WM_MOUSEMOVE (wParam, lParam); + if (result != null) return result; + if (itemToolTipHandle != 0) { + /* + * Bug in Windows. On some machines that do not have XBUTTONs, + * the MK_XBUTTON1 and OS.MK_XBUTTON2 bits are sometimes set, + * causing mouse capture to become stuck. The fix is to test + * for the extra buttons only when they exist. + */ + int mask = OS.MK_LBUTTON | OS.MK_MBUTTON | OS.MK_RBUTTON; + if (display.xMouse) mask |= OS.MK_XBUTTON1 | OS.MK_XBUTTON2; + if ((wParam & mask) == 0) { + int x = OS.GET_X_LPARAM (lParam); + int y = OS.GET_Y_LPARAM (lParam); + int [] index = new int [1]; + TreeItem [] item = new TreeItem [1]; + RECT [] cellRect = new RECT [1], itemRect = new RECT [1]; + if (findCell (x, y, item, index, cellRect, itemRect)) { + /* + * Feature in Windows. When the new tool rectangle is + * set using TTM_NEWTOOLRECT and the tooltip is visible, + * Windows draws the tooltip right away and the sends + * WM_NOTIFY with TTN_SHOW. This means that the tooltip + * shows first at the wrong location and then moves to + * the right one. The fix is to hide the tooltip window. + */ + if (OS.SendMessage (itemToolTipHandle, OS.TTM_GETCURRENTTOOL, 0, 0) == 0) { + if (OS.IsWindowVisible (itemToolTipHandle)) { + OS.ShowWindow (itemToolTipHandle, OS.SW_HIDE); + } + } + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + lpti.hwnd = handle; + lpti.uId = handle; + lpti.uFlags = OS.TTF_SUBCLASS | OS.TTF_TRANSPARENT; + lpti.left = cellRect [0].left; + lpti.top = cellRect [0].top; + lpti.right = cellRect [0].right; + lpti.bottom = cellRect [0].bottom; + OS.SendMessage (itemToolTipHandle, OS.TTM_NEWTOOLRECT, 0, lpti); + } + } + } + return result; +} + +LRESULT WM_MOUSEWHEEL (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_MOUSEWHEEL (wParam, lParam); + if (itemToolTipHandle != 0) OS.ShowWindow (itemToolTipHandle, OS.SW_HIDE); + return result; +} + +LRESULT WM_MOVE (int /*long*/ wParam, int /*long*/ lParam) { + if (itemToolTipHandle != 0) OS.ShowWindow (itemToolTipHandle, OS.SW_HIDE); + if (ignoreResize) return null; + return super.WM_MOVE (wParam, lParam); +} + +LRESULT WM_RBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. The receiver uses WM_RBUTTONDOWN + * to initiate a drag/drop operation depending on how the + * user moves the mouse. If the user clicks the right button, + * without moving the mouse, the tree consumes the corresponding + * WM_RBUTTONUP. The fix is to avoid calling the window proc for + * the tree. + */ + Display display = this.display; + display.captureChanged = false; + if (!sendMouseEvent (SWT.MouseDown, 3, handle, OS.WM_RBUTTONDOWN, wParam, lParam)) { + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != handle) OS.SetCapture (handle); + } + return LRESULT.ZERO; + } + /* + * This code is intentionally commented. + */ +// if (OS.GetCapture () != handle) OS.SetCapture (handle); + if (OS.GetFocus () != handle) OS.SetFocus (handle); + + /* + * Feature in Windows. When the user selects a tree item + * with the right mouse button, the item remains selected + * only as long as the user does not release or move the + * mouse. As soon as this happens, the selection snaps + * back to the previous selection. This behavior can be + * observed in the Explorer but is not instantly apparent + * because the Explorer explicitly sets the selection when + * the user chooses a menu item. If the user cancels the + * menu, the selection snaps back. The fix is to avoid + * calling the window proc and do the selection ourselves. + * This behavior is consistent with the table. + */ + TVHITTESTINFO lpht = new TVHITTESTINFO (); + lpht.x = OS.GET_X_LPARAM (lParam); + lpht.y = OS.GET_Y_LPARAM (lParam); + OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht); + if (lpht.hItem != 0) { + boolean fakeSelection = (style & SWT.FULL_SELECTION) != 0; + if (!fakeSelection) { + if (hooks (SWT.MeasureItem)) { + fakeSelection = hitTestSelection (lpht.hItem, lpht.x, lpht.y); + } else { + int flags = OS.TVHT_ONITEMICON | OS.TVHT_ONITEMLABEL; + fakeSelection = (lpht.flags & flags) != 0; + } + } + if (fakeSelection) { + if ((wParam & (OS.MK_CONTROL | OS.MK_SHIFT)) == 0) { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_SELECTED; + tvItem.hItem = lpht.hItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + if ((tvItem.state & OS.TVIS_SELECTED) == 0) { + ignoreSelect = true; + OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, 0); + ignoreSelect = false; + OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, lpht.hItem); + } + } + } + } + return LRESULT.ZERO; +} + +LRESULT WM_PAINT (int /*long*/ wParam, int /*long*/ lParam) { + if (shrink && !ignoreShrink) { + /* Resize the item array to fit the last item */ + int count = items.length - 1; + while (count >= 0) { + if (items [count] != null) break; + --count; + } + count++; + if (items.length > 4 && items.length - count > 3) { + int length = Math.max (4, (count + 3) / 4 * 4); + TreeItem [] newItems = new TreeItem [length]; + System.arraycopy (items, 0, newItems, 0, count); + items = newItems; + } + shrink = false; + } + if ((style & SWT.DOUBLE_BUFFERED) != 0 || findImageControl () != null) { + boolean doubleBuffer = true; + if (explorerTheme) { + int exStyle = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0); + if ((exStyle & OS.TVS_EX_DOUBLEBUFFER) != 0) doubleBuffer = false; + } + if (doubleBuffer) { + GC gc = null; + int /*long*/ paintDC = 0; + PAINTSTRUCT ps = new PAINTSTRUCT (); + boolean hooksPaint = hooks (SWT.Paint) || filters (SWT.Paint); + if (hooksPaint) { + GCData data = new GCData (); + data.ps = ps; + data.hwnd = handle; + gc = GC.win32_new (this, data); + paintDC = gc.handle; + } else { + paintDC = OS.BeginPaint (handle, ps); + } + int width = ps.right - ps.left; + int height = ps.bottom - ps.top; + if (width != 0 && height != 0) { + int /*long*/ hDC = OS.CreateCompatibleDC (paintDC); + POINT lpPoint1 = new POINT (), lpPoint2 = new POINT (); + OS.SetWindowOrgEx (hDC, ps.left, ps.top, lpPoint1); + OS.SetBrushOrgEx (hDC, ps.left, ps.top, lpPoint2); + int /*long*/ hBitmap = OS.CreateCompatibleBitmap (paintDC, width, height); + int /*long*/ hOldBitmap = OS.SelectObject (hDC, hBitmap); + RECT rect = new RECT (); + OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom); + drawBackground (hDC, rect); + callWindowProc (handle, OS.WM_PAINT, hDC, 0); + OS.SetWindowOrgEx (hDC, lpPoint1.x, lpPoint1.y, null); + OS.SetBrushOrgEx (hDC, lpPoint2.x, lpPoint2.y, null); + OS.BitBlt (paintDC, ps.left, ps.top, width, height, hDC, 0, 0, OS.SRCCOPY); + OS.SelectObject (hDC, hOldBitmap); + OS.DeleteObject (hBitmap); + OS.DeleteObject (hDC); + if (hooksPaint) { + Event event = new Event (); + event.gc = gc; + event.x = ps.left; + event.y = ps.top; + event.width = ps.right - ps.left; + event.height = ps.bottom - ps.top; + sendEvent (SWT.Paint, event); + // widget could be disposed at this point + event.gc = null; + } + } + if (hooksPaint) { + gc.dispose (); + } else { + OS.EndPaint (handle, ps); + } + return LRESULT.ZERO; + } + } + return super.WM_PAINT (wParam, lParam); +} + +LRESULT WM_PRINTCLIENT (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_PRINTCLIENT (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. For some reason, when WM_PRINT is used + * to capture an image of a hierarchy that contains a tree with + * columns, the clipping that is used to stop the first column + * from drawing on top of subsequent columns stops the first + * column and the tree lines from drawing. This does not happen + * during WM_PAINT. The fix is to draw without clipping and + * then draw the rest of the columns on top. Since the drawing + * is happening in WM_PRINTCLIENT, the redrawing is not visible. + */ + printClient = true; + int /*long*/ code = callWindowProc (handle, OS.WM_PRINTCLIENT, wParam, lParam); + printClient = false; + return new LRESULT (code); +} + +LRESULT WM_SETFOCUS (int /*long*/ wParam, int /*long*/ lParam) { + /* + * Bug in Windows. When a tree item that has an image + * with alpha is expanded or collapsed, the area where + * the image is drawn is not erased before it is drawn. + * This means that the image gets darker each time. + * The fix is to redraw the selection. + * + * Feature in Windows. When multiple item have + * the TVIS_SELECTED state, Windows redraws only + * the focused item in the color used to show the + * selection when the tree loses or gains focus. + * The fix is to force Windows to redraw the + * selection when focus is gained or lost. + */ + boolean redraw = (style & SWT.MULTI) != 0; + if (!redraw) { + if (!OS.IsWinCE && OS.COMCTL32_MAJOR >= 6) { + if (imageList != null) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.TVS_FULLROWSELECT) == 0) { + redraw = true; + } + } + } + } + if (redraw) redrawSelection (); + return super.WM_SETFOCUS (wParam, lParam); +} + +LRESULT WM_SETFONT (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETFONT (wParam, lParam); + if (result != null) return result; + if (hwndHeader != 0) { + /* + * Bug in Windows. When a header has a sort indicator + * triangle, Windows resizes the indicator based on the + * size of the n-1th font. The fix is to always make + * the n-1th font be the default. This makes the sort + * indicator always be the default size. + */ + OS.SendMessage (hwndHeader, OS.WM_SETFONT, 0, lParam); + OS.SendMessage (hwndHeader, OS.WM_SETFONT, wParam, lParam); + } + if (itemToolTipHandle != 0) { + OS.ShowWindow (itemToolTipHandle, OS.SW_HIDE); + OS.SendMessage (itemToolTipHandle, OS.WM_SETFONT, wParam, lParam); + } + if (headerToolTipHandle != 0) { + OS.SendMessage (headerToolTipHandle, OS.WM_SETFONT, wParam, lParam); + updateHeaderToolTips (); + } + return result; +} + +LRESULT WM_SETREDRAW (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SETREDRAW (wParam, lParam); + if (result != null) return result; + if (itemToolTipHandle != 0) OS.ShowWindow (itemToolTipHandle, OS.SW_HIDE); + /* + * Bug in Windows. Under certain circumstances, when + * WM_SETREDRAW is used to turn off drawing and then + * TVM_GETITEMRECT is sent to get the bounds of an item + * that is not inside the client area, Windows segment + * faults. The fix is to call the default window proc + * rather than the default tree proc. + * + * NOTE: This problem is intermittent and happens on + * Windows Vista running under the theme manager. + */ + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + int /*long*/ code = OS.DefWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam); + return code == 0 ? LRESULT.ZERO : new LRESULT (code); + } + return result; +} + +LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) { + if (itemToolTipHandle != 0) OS.ShowWindow (itemToolTipHandle, OS.SW_HIDE); + /* + * Bug in Windows. When TVS_NOHSCROLL is set when the + * size of the tree is zero, the scroll bar is shown the + * next time the tree resizes. The fix is to hide the + * scroll bar every time the tree is resized. + */ + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.TVS_NOHSCROLL) != 0) { + if (!OS.IsWinCE) OS.ShowScrollBar (handle, OS.SB_HORZ, false); + } + /* + * Bug in Windows. On Vista, when the Explorer theme + * is used with a full selection tree, when the tree + * is resized to be smaller, the rounded right edge + * of the selected items is not drawn. The fix is the + * redraw the entire tree. + */ + if (explorerTheme && (style & SWT.FULL_SELECTION) != 0) { + OS.InvalidateRect (handle, null, false); + } + if (ignoreResize) return null; + return super.WM_SIZE (wParam, lParam); +} + +LRESULT WM_SYSCOLORCHANGE (int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam); + if (result != null) return result; + /* + * Bug in Windows. When the tree is using the explorer + * theme, it does not use COLOR_WINDOW_TEXT for the + * default foreground color. The fix is to explicitly + * set the foreground. + */ + if (explorerTheme) { + if (foreground == -1) setForegroundPixel (-1); + } + if ((style & SWT.CHECK) != 0) setCheckboxImageList (); + return result; +} + +LRESULT WM_VSCROLL (int /*long*/ wParam, int /*long*/ lParam) { + boolean fixScroll = false; + if ((style & SWT.DOUBLE_BUFFERED) != 0) { + int code = OS.LOWORD (wParam); + switch (code) { + case OS.SB_TOP: + case OS.SB_BOTTOM: + case OS.SB_LINEDOWN: + case OS.SB_LINEUP: + case OS.SB_PAGEDOWN: + case OS.SB_PAGEUP: + fixScroll = (style & SWT.VIRTUAL) != 0 || hooks (SWT.EraseItem) || hooks (SWT.PaintItem); + break; + } + } + if (fixScroll) { + style &= ~SWT.DOUBLE_BUFFERED; + if (explorerTheme) { + OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, OS.TVS_EX_DOUBLEBUFFER, 0); + } + } + LRESULT result = super.WM_VSCROLL (wParam, lParam); + if (fixScroll) { + style |= SWT.DOUBLE_BUFFERED; + if (explorerTheme) { + OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, OS.TVS_EX_DOUBLEBUFFER, OS.TVS_EX_DOUBLEBUFFER); + } + } + if (result != null) return result; + return result; +} + +LRESULT wmColorChild (int /*long*/ wParam, int /*long*/ lParam) { + if (findImageControl () != null) { + if (OS.COMCTL32_MAJOR < 6) { + return super.wmColorChild (wParam, lParam); + } + return new LRESULT (OS.GetStockObject (OS.NULL_BRUSH)); + } + /* + * Feature in Windows. Tree controls send WM_CTLCOLOREDIT + * to allow application code to change the default colors. + * This is undocumented and conflicts with TVM_SETTEXTCOLOR + * and TVM_SETBKCOLOR, the documented way to do this. The + * fix is to ignore WM_CTLCOLOREDIT messages from trees. + */ + return null; +} + +LRESULT wmNotify (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + if (hdr.hwndFrom == itemToolTipHandle) { + LRESULT result = wmNotifyToolTip (hdr, wParam, lParam); + if (result != null) return result; + } + if (hdr.hwndFrom == hwndHeader) { + LRESULT result = wmNotifyHeader (hdr, wParam, lParam); + if (result != null) return result; + } + return super.wmNotify (hdr, wParam, lParam); +} + +LRESULT wmNotifyChild (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + switch (hdr.code) { + case OS.TVN_GETDISPINFOA: + case OS.TVN_GETDISPINFOW: { + NMTVDISPINFO lptvdi = new NMTVDISPINFO (); + OS.MoveMemory (lptvdi, lParam, NMTVDISPINFO.sizeof); + if ((style & SWT.VIRTUAL) != 0) { + /* + * Feature in Windows. When a new tree item is inserted + * using TVM_INSERTITEM, a TVN_GETDISPINFO is sent before + * TVM_INSERTITEM returns and before the item is added to + * the items array. The fix is to check for null. + * + * NOTE: This only happens on XP with the version 6.00 of + * COMCTL32.DLL. + */ + boolean checkVisible = true; + /* + * When an item is being deleted from a virtual tree, do not + * allow the application to provide data for a new item that + * becomes visible until the item has been removed from the + * items array. Because arbitrary application code can run + * during the callback, the items array might be accessed + * in an inconsistent state. Rather than answering the data + * right away, queue a redraw for later. + */ + if (!ignoreShrink) { + if (items != null && lptvdi.lParam != -1) { + if (items [(int)/*64*/lptvdi.lParam] != null && items [(int)/*64*/lptvdi.lParam].cached) { + checkVisible = false; + } + } + } + if (checkVisible) { + if (!getDrawing () || !OS.IsWindowVisible (handle)) break; + RECT itemRect = new RECT (); + if (!OS.TreeView_GetItemRect (handle, lptvdi.hItem, itemRect, false)) { + break; + } + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + if (!OS.IntersectRect (rect, rect, itemRect)) break; + if (ignoreShrink) { + OS.InvalidateRect (handle, rect, true); + break; + } + } + } + if (items == null) break; + /* + * Bug in Windows. If the lParam field of TVITEM + * is changed during custom draw using TVM_SETITEM, + * the lItemlParam field of the NMTVCUSTOMDRAW struct + * is not updated until the next custom draw. The + * fix is to query the field from the item instead + * of using the struct. + */ + int id = (int)/*64*/lptvdi.lParam; + if ((style & SWT.VIRTUAL) != 0) { + if (id == -1) { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + tvItem.hItem = lptvdi.hItem; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + id = (int)/*64*/tvItem.lParam; + } + } + TreeItem item = _getItem (lptvdi.hItem, id); + /* + * Feature in Windows. When a new tree item is inserted + * using TVM_INSERTITEM, a TVN_GETDISPINFO is sent before + * TVM_INSERTITEM returns and before the item is added to + * the items array. The fix is to check for null. + * + * NOTE: This only happens on XP with the version 6.00 of + * COMCTL32.DLL. + * + * Feature in Windows. When TVM_DELETEITEM is called with + * TVI_ROOT to remove all items from a tree, under certain + * circumstances, the tree sends TVN_GETDISPINFO for items + * that are about to be disposed. The fix is to check for + * disposed items. + */ + if (item == null) break; + if (item.isDisposed ()) break; + if (!item.cached) { + if ((style & SWT.VIRTUAL) != 0) { + if (!checkData (item, false)) break; + } + if (painted) item.cached = true; + } + int index = 0; + if (hwndHeader != 0) { + index = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0); + } + if ((lptvdi.mask & OS.TVIF_TEXT) != 0) { + String string = null; + if (index == 0) { + string = item.text; + } else { + String [] strings = item.strings; + if (strings != null) string = strings [index]; + } + if (string != null) { + TCHAR buffer = new TCHAR (getCodePage (), string, false); + int byteCount = Math.min (buffer.length (), lptvdi.cchTextMax - 1) * TCHAR.sizeof; + OS.MoveMemory (lptvdi.pszText, buffer, byteCount); + OS.MoveMemory (lptvdi.pszText + byteCount, new byte [TCHAR.sizeof], TCHAR.sizeof); + lptvdi.cchTextMax = Math.min (lptvdi.cchTextMax, string.length () + 1); + } + } + if ((lptvdi.mask & (OS.TVIF_IMAGE | OS.TVIF_SELECTEDIMAGE)) != 0) { + Image image = null; + if (index == 0) { + image = item.image; + } else { + Image [] images = item.images; + if (images != null) image = images [index]; + } + lptvdi.iImage = lptvdi.iSelectedImage = OS.I_IMAGENONE; + if (image != null) { + lptvdi.iImage = lptvdi.iSelectedImage = imageIndex (image, index); + } + if (explorerTheme && OS.IsWindowEnabled (handle)) { + if (findImageControl () != null) { + lptvdi.iImage = lptvdi.iSelectedImage = OS.I_IMAGENONE; + } + } + } + OS.MoveMemory (lParam, lptvdi, NMTVDISPINFO.sizeof); + break; + } + case OS.NM_CUSTOMDRAW: { + if (hdr.hwndFrom == hwndHeader) break; + if (hooks (SWT.MeasureItem)) { + if (hwndHeader == 0) createParent (); + } + if (!customDraw && findImageControl () == null) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + if (sortColumn == null || sortDirection == SWT.NONE) { + break; + } + } + } + NMTVCUSTOMDRAW nmcd = new NMTVCUSTOMDRAW (); + OS.MoveMemory (nmcd, lParam, NMTVCUSTOMDRAW.sizeof); + switch (nmcd.dwDrawStage) { + case OS.CDDS_PREPAINT: return CDDS_PREPAINT (nmcd, wParam, lParam); + case OS.CDDS_ITEMPREPAINT: return CDDS_ITEMPREPAINT (nmcd, wParam, lParam); + case OS.CDDS_ITEMPOSTPAINT: return CDDS_ITEMPOSTPAINT (nmcd, wParam, lParam); + case OS.CDDS_POSTPAINT: return CDDS_POSTPAINT (nmcd, wParam, lParam); + } + break; + } + case OS.NM_DBLCLK: { + /* + * When the user double clicks on a tree item + * or a line beside the item, the window proc + * for the tree collapses or expand the branch. + * When application code associates an action + * with double clicking, then the tree expand + * is unexpected and unwanted. The fix is to + * avoid the operation by testing to see whether + * the mouse was inside a tree item. + */ + if (hooks (SWT.MeasureItem)) return LRESULT.ONE; + if (hooks (SWT.DefaultSelection)) { + POINT pt = new POINT (); + int pos = OS.GetMessagePos (); + OS.POINTSTOPOINT (pt, pos); + OS.ScreenToClient (handle, pt); + TVHITTESTINFO lpht = new TVHITTESTINFO (); + lpht.x = pt.x; + lpht.y = pt.y; + OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht); + if (lpht.hItem != 0 && (lpht.flags & OS.TVHT_ONITEM) != 0) { + return LRESULT.ONE; + } + } + break; + } + /* + * Bug in Windows. On Vista, when TVM_SELECTITEM is called + * with TVGN_CARET in order to set the selection, for some + * reason, Windows deselects the previous two items that + * were selected. The fix is to stop the selection from + * changing on all but the item that is supposed to be + * selected. + */ + case OS.TVN_ITEMCHANGINGA: + case OS.TVN_ITEMCHANGINGW: { + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + if ((style & SWT.MULTI) != 0) { + if (hSelect != 0) { + NMTVITEMCHANGE pnm = new NMTVITEMCHANGE (); + OS.MoveMemory (pnm, lParam, NMTVITEMCHANGE.sizeof); + if (hSelect == pnm.hItem) break; + return LRESULT.ONE; + } + } + } + break; + } + case OS.TVN_SELCHANGINGA: + case OS.TVN_SELCHANGINGW: { + if ((style & SWT.MULTI) != 0) { + if (lockSelection) { + /* Save the old selection state for both items */ + NMTREEVIEW treeView = new NMTREEVIEW (); + OS.MoveMemory (treeView, lParam, NMTREEVIEW.sizeof); + TVITEM tvItem = treeView.itemOld; + oldSelected = (tvItem.state & OS.TVIS_SELECTED) != 0; + tvItem = treeView.itemNew; + newSelected = (tvItem.state & OS.TVIS_SELECTED) != 0; + } + } + if (!ignoreSelect && !ignoreDeselect) { + hAnchor = 0; + if ((style & SWT.MULTI) != 0) deselectAll (); + } + break; + } + case OS.TVN_SELCHANGEDA: + case OS.TVN_SELCHANGEDW: { + NMTREEVIEW treeView = null; + if ((style & SWT.MULTI) != 0) { + if (lockSelection) { + /* Restore the old selection state of both items */ + if (oldSelected) { + if (treeView == null) { + treeView = new NMTREEVIEW (); + OS.MoveMemory (treeView, lParam, NMTREEVIEW.sizeof); + } + TVITEM tvItem = treeView.itemOld; + tvItem.mask = OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_SELECTED; + tvItem.state = OS.TVIS_SELECTED; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + if (!newSelected && ignoreSelect) { + if (treeView == null) { + treeView = new NMTREEVIEW (); + OS.MoveMemory (treeView, lParam, NMTREEVIEW.sizeof); + } + TVITEM tvItem = treeView.itemNew; + tvItem.mask = OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_SELECTED; + tvItem.state = 0; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + } + } + } + if (!ignoreSelect) { + if (treeView == null) { + treeView = new NMTREEVIEW (); + OS.MoveMemory (treeView, lParam, NMTREEVIEW.sizeof); + } + TVITEM tvItem = treeView.itemNew; + hAnchor = tvItem.hItem; + Event event = new Event (); + event.item = _getItem (tvItem.hItem, (int)/*64*/tvItem.lParam); + postEvent (SWT.Selection, event); + } + updateScrollBar (); + break; + } + case OS.TVN_ITEMEXPANDINGA: + case OS.TVN_ITEMEXPANDINGW: { + if (itemToolTipHandle != 0) OS.ShowWindow (itemToolTipHandle, OS.SW_HIDE); + boolean runExpanded = false; + if ((style & SWT.VIRTUAL) != 0) style &= ~SWT.DOUBLE_BUFFERED; + if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) style &= ~SWT.DOUBLE_BUFFERED; + if (findImageControl () != null && getDrawing () && OS.IsWindowVisible (handle)) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); + } + /* + * Bug in Windows. When TVM_SETINSERTMARK is used to set + * an insert mark for a tree and an item is expanded or + * collapsed near the insert mark, the tree does not redraw + * the insert mark properly. The fix is to hide and show + * the insert mark whenever an item is expanded or collapsed. + */ + if (hInsert != 0) { + OS.SendMessage (handle, OS.TVM_SETINSERTMARK, 0, 0); + } + if (!ignoreExpand) { + NMTREEVIEW treeView = new NMTREEVIEW (); + OS.MoveMemory (treeView, lParam, NMTREEVIEW.sizeof); + TVITEM tvItem = treeView.itemNew; + /* + * Feature in Windows. In some cases, TVM_ITEMEXPANDING + * is sent from within TVM_DELETEITEM for the tree item + * being destroyed. By the time the message is sent, + * the item has already been removed from the list of + * items. The fix is to check for null. + */ + if (items == null) break; + TreeItem item = _getItem (tvItem.hItem, (int)/*64*/tvItem.lParam); + if (item == null) break; + Event event = new Event (); + event.item = item; + switch (treeView.action) { + case OS.TVE_EXPAND: + /* + * Bug in Windows. When the numeric keypad asterisk + * key is used to expand every item in the tree, Windows + * sends TVN_ITEMEXPANDING to items in the tree that + * have already been expanded. The fix is to detect + * that the item is already expanded and ignore the + * notification. + */ + if ((tvItem.state & OS.TVIS_EXPANDED) == 0) { + sendEvent (SWT.Expand, event); + if (isDisposed ()) return LRESULT.ZERO; + } + break; + case OS.TVE_COLLAPSE: + sendEvent (SWT.Collapse, event); + if (isDisposed ()) return LRESULT.ZERO; + break; + } + /* + * Bug in Windows. When all of the items are deleted during + * TVN_ITEMEXPANDING, Windows does not send TVN_ITEMEXPANDED. + * The fix is to detect this case and run the TVN_ITEMEXPANDED + * code in this method. + */ + int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, tvItem.hItem); + runExpanded = hFirstItem == 0; + } + if (!runExpanded) break; + //FALL THROUGH + } + case OS.TVN_ITEMEXPANDEDA: + case OS.TVN_ITEMEXPANDEDW: { + if ((style & SWT.VIRTUAL) != 0) style |= SWT.DOUBLE_BUFFERED; + if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) style |= SWT.DOUBLE_BUFFERED; + if (findImageControl () != null && getDrawing () /*&& OS.IsWindowVisible (handle)*/) { + OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); + OS.InvalidateRect (handle, null, true); + } + /* + * Bug in Windows. When TVM_SETINSERTMARK is used to set + * an insert mark for a tree and an item is expanded or + * collapsed near the insert mark, the tree does not redraw + * the insert mark properly. The fix is to hide and show + * the insert mark whenever an item is expanded or collapsed. + */ + if (hInsert != 0) { + OS.SendMessage (handle, OS.TVM_SETINSERTMARK, insertAfter ? 1 : 0, hInsert); + } + /* + * Bug in Windows. When a tree item that has an image + * with alpha is expanded or collapsed, the area where + * the image is drawn is not erased before it is drawn. + * This means that the image gets darker each time. + * The fix is to redraw the item. + */ + if (!OS.IsWinCE && OS.COMCTL32_MAJOR >= 6) { + if (imageList != null) { + NMTREEVIEW treeView = new NMTREEVIEW (); + OS.MoveMemory (treeView, lParam, NMTREEVIEW.sizeof); + TVITEM tvItem = treeView.itemNew; + if (tvItem.hItem != 0) { + int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); + if ((bits & OS.TVS_FULLROWSELECT) == 0) { + RECT rect = new RECT (); + if (OS.TreeView_GetItemRect (handle, tvItem.hItem, rect, false)) { + OS.InvalidateRect (handle, rect, true); + } + } + } + } + } + updateScrollBar (); + break; + } + case OS.TVN_BEGINDRAGA: + case OS.TVN_BEGINDRAGW: + if (OS.GetKeyState (OS.VK_LBUTTON) >= 0) break; + //FALL THROUGH + case OS.TVN_BEGINRDRAGA: + case OS.TVN_BEGINRDRAGW: { + dragStarted = true; + NMTREEVIEW treeView = new NMTREEVIEW (); + OS.MoveMemory (treeView, lParam, NMTREEVIEW.sizeof); + TVITEM tvItem = treeView.itemNew; + if (tvItem.hItem != 0 && (tvItem.state & OS.TVIS_SELECTED) == 0) { + hSelect = tvItem.hItem; + ignoreSelect = ignoreDeselect = true; + OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, tvItem.hItem); + ignoreSelect = ignoreDeselect = false; + hSelect = 0; + } + break; + } + case OS.NM_RECOGNIZEGESTURE: { + /* + * Feature in Pocket PC. The tree and table controls detect the tap + * and hold gesture by default. They send a GN_CONTEXTMENU message to show + * the popup menu. This default behaviour is unwanted on Pocket PC 2002 + * when no menu has been set, as it still draws a red circle. The fix + * is to disable this default behaviour when no menu is set by returning + * TRUE when receiving the Pocket PC 2002 specific NM_RECOGNIZEGESTURE + * message. + */ + if (OS.IsPPC) { + boolean hasMenu = menu != null && !menu.isDisposed (); + if (!hasMenu && !hooks (SWT.MenuDetect)) return LRESULT.ONE; + } + break; + } + case OS.GN_CONTEXTMENU: { + if (OS.IsPPC) { + boolean hasMenu = menu != null && !menu.isDisposed (); + if (hasMenu || hooks (SWT.MenuDetect)) { + NMRGINFO nmrg = new NMRGINFO (); + OS.MoveMemory (nmrg, lParam, NMRGINFO.sizeof); + showMenu (nmrg.x, nmrg.y); + gestureCompleted = true; + return LRESULT.ONE; + } + } + break; + } + } + return super.wmNotifyChild (hdr, wParam, lParam); +} + +LRESULT wmNotifyHeader (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. On NT, the automatically created + * header control is created as a UNICODE window, not an + * ANSI window despite the fact that the parent is created + * as an ANSI window. This means that it sends UNICODE + * notification messages to the parent window on NT for + * no good reason. The data and size in the NMHEADER and + * HDITEM structs is identical between the platforms so no + * different message is actually necessary. Despite this, + * Windows sends different messages. The fix is to look + * for both messages, despite the platform. This works + * because only one will be sent on either platform, never + * both. + */ + switch (hdr.code) { + case OS.HDN_BEGINTRACKW: + case OS.HDN_BEGINTRACKA: + case OS.HDN_DIVIDERDBLCLICKW: + case OS.HDN_DIVIDERDBLCLICKA: { + NMHEADER phdn = new NMHEADER (); + OS.MoveMemory (phdn, lParam, NMHEADER.sizeof); + TreeColumn column = columns [phdn.iItem]; + if (column != null && !column.getResizable ()) { + return LRESULT.ONE; + } + ignoreColumnMove = true; + switch (hdr.code) { + case OS.HDN_DIVIDERDBLCLICKW: + case OS.HDN_DIVIDERDBLCLICKA: + if (column != null) column.pack (); + } + break; + } + case OS.NM_RELEASEDCAPTURE: { + if (!ignoreColumnMove) { + for (int i=0; i<columnCount; i++) { + TreeColumn column = columns [i]; + column.updateToolTip (i); + } + updateImageList (); + } + ignoreColumnMove = false; + break; + } + case OS.HDN_BEGINDRAG: { + if (ignoreColumnMove) return LRESULT.ONE; + NMHEADER phdn = new NMHEADER (); + OS.MoveMemory (phdn, lParam, NMHEADER.sizeof); + if (phdn.iItem != -1) { + TreeColumn column = columns [phdn.iItem]; + if (column != null && !column.getMoveable ()) { + ignoreColumnMove = true; + return LRESULT.ONE; + } + } + break; + } + case OS.HDN_ENDDRAG: { + NMHEADER phdn = new NMHEADER (); + OS.MoveMemory (phdn, lParam, NMHEADER.sizeof); + if (phdn.iItem != -1 && phdn.pitem != 0) { + HDITEM pitem = new HDITEM (); + OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof); + if ((pitem.mask & OS.HDI_ORDER) != 0 && pitem.iOrder != -1) { + int [] order = new int [columnCount]; + OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, order); + int index = 0; + while (index < order.length) { + if (order [index] == phdn.iItem) break; + index++; + } + if (index == order.length) index = 0; + if (index == pitem.iOrder) break; + int start = Math.min (index, pitem.iOrder); + int end = Math.max (index, pitem.iOrder); + RECT rect = new RECT (), headerRect = new RECT (); + OS.GetClientRect (handle, rect); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, order [start], headerRect); + rect.left = Math.max (rect.left, headerRect.left); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, order [end], headerRect); + rect.right = Math.min (rect.right, headerRect.right); + OS.InvalidateRect (handle, rect, true); + ignoreColumnMove = false; + for (int i=start; i<=end; i++) { + TreeColumn column = columns [order [i]]; + if (!column.isDisposed ()) { + column.postEvent (SWT.Move); + } + } + } + } + break; + } + case OS.HDN_ITEMCHANGINGW: + case OS.HDN_ITEMCHANGINGA: { + NMHEADER phdn = new NMHEADER (); + OS.MoveMemory (phdn, lParam, NMHEADER.sizeof); + if (phdn.pitem != 0) { + HDITEM newItem = new HDITEM (); + OS.MoveMemory (newItem, phdn.pitem, HDITEM.sizeof); + if ((newItem.mask & OS.HDI_WIDTH) != 0) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + HDITEM oldItem = new HDITEM (); + oldItem.mask = OS.HDI_WIDTH; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, phdn.iItem, oldItem); + int deltaX = newItem.cxy - oldItem.cxy; + RECT headerRect = new RECT (); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, phdn.iItem, headerRect); + int gridWidth = linesVisible ? GRID_WIDTH : 0; + rect.left = headerRect.right - gridWidth; + int newX = rect.left + deltaX; + rect.right = Math.max (rect.right, rect.left + Math.abs (deltaX)); + if (explorerTheme || (findImageControl () != null || hooks (SWT.MeasureItem) || hooks (SWT.EraseItem) || hooks (SWT.PaintItem))) { + rect.left -= OS.GetSystemMetrics (OS.SM_CXFOCUSBORDER); + OS.InvalidateRect (handle, rect, true); + OS.OffsetRect (rect, deltaX, 0); + OS.InvalidateRect (handle, rect, true); + } else { + int flags = OS.SW_INVALIDATE | OS.SW_ERASE; + OS.ScrollWindowEx (handle, deltaX, 0, rect, null, 0, null, flags); + } + if (OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, phdn.iItem, 0) != 0) { + rect.left = headerRect.left; + rect.right = newX; + OS.InvalidateRect (handle, rect, true); + } + setScrollWidth (); + } + } + break; + } + case OS.HDN_ITEMCHANGEDW: + case OS.HDN_ITEMCHANGEDA: { + NMHEADER phdn = new NMHEADER (); + OS.MoveMemory (phdn, lParam, NMHEADER.sizeof); + if (phdn.pitem != 0) { + HDITEM pitem = new HDITEM (); + OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof); + if ((pitem.mask & OS.HDI_WIDTH) != 0) { + if (ignoreColumnMove) { + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { + int flags = OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (handle, null, 0, flags); + } else { + if ((style & SWT.DOUBLE_BUFFERED) == 0) { + int oldStyle = style; + style |= SWT.DOUBLE_BUFFERED; + OS.UpdateWindow (handle); + style = oldStyle; + } + } + } + TreeColumn column = columns [phdn.iItem]; + if (column != null) { + column.updateToolTip (phdn.iItem); + column.sendEvent (SWT.Resize); + if (isDisposed ()) return LRESULT.ZERO; + TreeColumn [] newColumns = new TreeColumn [columnCount]; + System.arraycopy (columns, 0, newColumns, 0, columnCount); + int [] order = new int [columnCount]; + OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, order); + boolean moved = false; + for (int i=0; i<columnCount; i++) { + TreeColumn nextColumn = newColumns [order [i]]; + if (moved && !nextColumn.isDisposed ()) { + nextColumn.updateToolTip (order [i]); + nextColumn.sendEvent (SWT.Move); + } + if (nextColumn == column) moved = true; + } + } + } + setScrollWidth (); + } + break; + } + case OS.HDN_ITEMCLICKW: + case OS.HDN_ITEMCLICKA: { + NMHEADER phdn = new NMHEADER (); + OS.MoveMemory (phdn, lParam, NMHEADER.sizeof); + TreeColumn column = columns [phdn.iItem]; + if (column != null) { + column.postEvent (SWT.Selection); + } + break; + } + case OS.HDN_ITEMDBLCLICKW: + case OS.HDN_ITEMDBLCLICKA: { + NMHEADER phdn = new NMHEADER (); + OS.MoveMemory (phdn, lParam, NMHEADER.sizeof); + TreeColumn column = columns [phdn.iItem]; + if (column != null) { + column.postEvent (SWT.DefaultSelection); + } + break; + } + } + return null; +} + +LRESULT wmNotifyToolTip (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) { + if (OS.IsWinCE) return null; + switch (hdr.code) { + case OS.NM_CUSTOMDRAW: { + NMTTCUSTOMDRAW nmcd = new NMTTCUSTOMDRAW (); + OS.MoveMemory (nmcd, lParam, NMTTCUSTOMDRAW.sizeof); + return wmNotifyToolTip (nmcd, lParam); + } + case OS.TTN_SHOW: { + LRESULT result = super.wmNotify (hdr, wParam, lParam); + if (result != null) return result; + int pos = OS.GetMessagePos (); + POINT pt = new POINT(); + OS.POINTSTOPOINT (pt, pos); + OS.ScreenToClient (handle, pt); + int [] index = new int [1]; + TreeItem [] item = new TreeItem [1]; + RECT [] cellRect = new RECT [1], itemRect = new RECT [1]; + if (findCell (pt.x, pt.y, item, index, cellRect, itemRect)) { + RECT toolRect = toolTipRect (itemRect [0]); + OS.MapWindowPoints (handle, 0, toolRect, 2); + int width = toolRect.right - toolRect.left; + int height = toolRect.bottom - toolRect.top; + int flags = OS.SWP_NOACTIVATE | OS.SWP_NOZORDER | OS.SWP_NOSIZE; + if (isCustomToolTip ()) flags &= ~OS.SWP_NOSIZE; + SetWindowPos (itemToolTipHandle, 0, toolRect.left, toolRect.top, width, height, flags); + return LRESULT.ONE; + } + return result; + } + } + return null; +} + +LRESULT wmNotifyToolTip (NMTTCUSTOMDRAW nmcd, int /*long*/ lParam) { + if (OS.IsWinCE) return null; + switch (nmcd.dwDrawStage) { + case OS.CDDS_PREPAINT: { + if (isCustomToolTip ()) { + //TEMPORARY CODE + //nmcd.uDrawFlags |= OS.DT_CALCRECT; + //OS.MoveMemory (lParam, nmcd, NMTTCUSTOMDRAW.sizeof); + if (!OS.IsWinCE && OS.WIN32_VERSION < OS.VERSION (6, 0)) { + OS.SetTextColor (nmcd.hdc, OS.GetSysColor (OS.COLOR_INFOBK)); + } + return new LRESULT (OS.CDRF_NOTIFYPOSTPAINT | OS.CDRF_NEWFONT); + } + break; + } + case OS.CDDS_POSTPAINT: { + if (!OS.IsWinCE && OS.WIN32_VERSION < OS.VERSION (6, 0)) { + OS.SetTextColor (nmcd.hdc, OS.GetSysColor (OS.COLOR_INFOTEXT)); + } + if (OS.SendMessage (itemToolTipHandle, OS.TTM_GETCURRENTTOOL, 0, 0) != 0) { + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + if (OS.SendMessage (itemToolTipHandle, OS.TTM_GETCURRENTTOOL, 0, lpti) != 0) { + int [] index = new int [1]; + TreeItem [] item = new TreeItem [1]; + RECT [] cellRect = new RECT [1], itemRect = new RECT [1]; + int pos = OS.GetMessagePos (); + POINT pt = new POINT(); + OS.POINTSTOPOINT (pt, pos); + OS.ScreenToClient (handle, pt); + if (findCell (pt.x, pt.y, item, index, cellRect, itemRect)) { + int /*long*/ hDC = OS.GetDC (handle); + int /*long*/ hFont = item [0].fontHandle (index [0]); + if (hFont == -1) hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); + int /*long*/ oldFont = OS.SelectObject (hDC, hFont); + LRESULT result = null; + boolean drawForeground = true; + cellRect [0] = item [0].getBounds (index [0], true, true, false, false, false, hDC); + if (hooks (SWT.EraseItem)) { + Event event = sendEraseItemEvent (item [0], nmcd, index [0], cellRect [0]); + if (isDisposed () || item [0].isDisposed ()) break; + if (event.doit) { + drawForeground = (event.detail & SWT.FOREGROUND) != 0; + } else { + drawForeground = false; + } + } + if (drawForeground) { + int nSavedDC = OS.SaveDC (nmcd.hdc); + int gridWidth = getLinesVisible () ? Table.GRID_WIDTH : 0; + RECT insetRect = toolTipInset (cellRect [0]); + OS.SetWindowOrgEx (nmcd.hdc, insetRect.left, insetRect.top, null); + GCData data = new GCData (); + data.device = display; + data.foreground = OS.GetTextColor (nmcd.hdc); + data.background = OS.GetBkColor (nmcd.hdc); + data.font = Font.win32_new (display, hFont); + GC gc = GC.win32_new (nmcd.hdc, data); + int x = cellRect [0].left + INSET; + if (index [0] != 0) x -= gridWidth; + Image image = item [0].getImage (index [0]); + if (image != null || index [0] == 0) { + Point size = getImageSize (); + RECT imageRect = item [0].getBounds (index [0], false, true, false, false, false, hDC); + if (imageList == null) size.x = imageRect.right - imageRect.left; + if (image != null) { + Rectangle rect = image.getBounds (); + gc.drawImage (image, rect.x, rect.y, rect.width, rect.height, x, imageRect.top, size.x, size.y); + x += INSET + (index [0] == 0 ? 1 : 0); + } + x += size.x; + } else { + x += INSET; + } + String string = item [0].getText (index [0]); + if (string != null) { + int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_VCENTER; + TreeColumn column = columns != null ? columns [index [0]] : null; + if (column != null) { + if ((column.style & SWT.CENTER) != 0) flags |= OS.DT_CENTER; + if ((column.style & SWT.RIGHT) != 0) flags |= OS.DT_RIGHT; + } + TCHAR buffer = new TCHAR (getCodePage (), string, false); + RECT textRect = new RECT (); + OS.SetRect (textRect, x, cellRect [0].top, cellRect [0].right, cellRect [0].bottom); + OS.DrawText (nmcd.hdc, buffer, buffer.length (), textRect, flags); + } + gc.dispose (); + OS.RestoreDC (nmcd.hdc, nSavedDC); + } + if (hooks (SWT.PaintItem)) { + itemRect [0] = item [0].getBounds (index [0], true, true, false, false, false, hDC); + sendPaintItemEvent (item [0], nmcd, index[0], itemRect [0]); + } + OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (handle, hDC); + if (result != null) return result; + } + break; + } + } + break; + } + } + return null; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TreeColumn.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TreeColumn.java new file mode 100644 index 0000000000..fbd4e6a519 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TreeColumn.java @@ -0,0 +1,758 @@ +/******************************************************************************* + * 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.win32.*; +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 { + Tree parent; + boolean resizable, moveable; + String toolTipText; + int id; + +/** + * 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)); + resizable = true; + this.parent = parent; + parent.createItem (this, parent.getColumnCount ()); +} + +/** + * 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)); + resizable = true; + 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 destroyWidget () { + parent.destroyItem (this); + releaseHandle (); +} + +/** + * 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; +} + +/** + * 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 moveable; +} + +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 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 resizable; +} + +/** + * 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 index = parent.indexOf (this); + if (index == -1) return 0; + int /*long*/ hwndHeader = parent.hwndHeader; + if (hwndHeader == 0) return 0; + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_WIDTH; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem); + return hdItem.cxy; +} + +/** + * 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 index = parent.indexOf (this); + if (index == -1) return; + int columnWidth = 0; + int /*long*/ hwnd = parent.handle, hwndHeader = parent.hwndHeader; + RECT headerRect = new RECT (); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect); + int /*long*/ hDC = OS.GetDC (hwnd); + int /*long*/ oldFont = 0, newFont = OS.SendMessage (hwnd, OS.WM_GETFONT, 0, 0); + if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont); + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + tvItem.hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + while (tvItem.hItem != 0) { + OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem); + TreeItem item = tvItem.lParam != -1 ? parent.items [(int)/*64*/tvItem.lParam] : null; + if (item != null) { + int itemRight = 0; + if (parent.hooks (SWT.MeasureItem)) { + Event event = parent.sendMeasureItemEvent (item, index, hDC); + if (isDisposed () || parent.isDisposed ()) break; + itemRight = event.x + event.width; + } else { + int /*long*/ hFont = item.fontHandle (index); + if (hFont != -1) hFont = OS.SelectObject (hDC, hFont); + RECT itemRect = item.getBounds (index, true, true, false, false, false, hDC); + if (hFont != -1) OS.SelectObject (hDC, hFont); + itemRight = itemRect.right; + } + columnWidth = Math.max (columnWidth, itemRight - headerRect.left); + } + tvItem.hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, tvItem.hItem); + } + RECT rect = new RECT (); + int flags = OS.DT_CALCRECT | OS.DT_NOPREFIX; + TCHAR buffer = new TCHAR (parent.getCodePage (), text, false); + OS.DrawText (hDC, buffer, buffer.length (), rect, flags); + int headerWidth = rect.right - rect.left + Tree.HEADER_MARGIN; + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) headerWidth += Tree.HEADER_EXTRA; + if (image != null || parent.sortColumn == this) { + Image headerImage = null; + if (parent.sortColumn == this && parent.sortDirection != SWT.NONE) { + if (OS.COMCTL32_MAJOR < 6) { + headerImage = display.getSortImage (parent.sortDirection); + } else { + headerWidth += Tree.SORT_WIDTH; + } + } else { + headerImage = image; + } + if (headerImage != null) { + Rectangle bounds = headerImage.getBounds (); + headerWidth += bounds.width; + } + int margin = 0; + if (hwndHeader != 0 && OS.COMCTL32_VERSION >= OS.VERSION (5, 80)) { + margin = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETBITMAPMARGIN, 0, 0); + } else { + margin = OS.GetSystemMetrics (OS.SM_CXEDGE) * 3; + } + headerWidth += margin * 2; + } + if (newFont != 0) OS.SelectObject (hDC, oldFont); + OS.ReleaseDC (hwnd, hDC); + int gridWidth = parent.linesVisible ? Tree.GRID_WIDTH : 0; + setWidth (Math.max (headerWidth, columnWidth + gridWidth)); +} + +void releaseHandle () { + super.releaseHandle (); + parent = null; +} + +void releaseParent () { + super.releaseParent (); + 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); + int /*long*/ hwndHeader = parent.hwndHeader; + if (hwndHeader == 0) return; + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_FORMAT; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem); + hdItem.fmt &= ~OS.HDF_JUSTIFYMASK; + if ((style & SWT.LEFT) == SWT.LEFT) hdItem.fmt |= OS.HDF_LEFT; + if ((style & SWT.CENTER) == SWT.CENTER) hdItem.fmt |= OS.HDF_CENTER; + if ((style & SWT.RIGHT) == SWT.RIGHT) hdItem.fmt |= OS.HDF_RIGHT; + OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem); + if (index != 0) { + int /*long*/ hwnd = parent.handle; + parent.forceResize (); + RECT rect = new RECT (), headerRect = new RECT (); + OS.GetClientRect (hwnd, rect); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect); + rect.left = headerRect.left; + rect.right = headerRect.right; + OS.InvalidateRect (hwnd, rect, true); + } +} + +public void setImage (Image image) { + checkWidget(); + if (image != null && image.isDisposed ()) { + error (SWT.ERROR_INVALID_ARGUMENT); + } + super.setImage (image); + if (parent.sortColumn != this || parent.sortDirection != SWT.NONE) { + setImage (image, false, false); + } +} + +void setImage (Image image, boolean sort, boolean right) { + int index = parent.indexOf (this); + if (index == -1) return; + int /*long*/ hwndHeader = parent.hwndHeader; + if (hwndHeader == 0) return; + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_FORMAT | OS.HDI_IMAGE | OS.HDI_BITMAP; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem); + hdItem.fmt &= ~OS.HDF_BITMAP_ON_RIGHT; + if (image != null) { + if (sort) { + hdItem.mask &= ~OS.HDI_IMAGE; + hdItem.fmt &= ~OS.HDF_IMAGE; + hdItem.fmt |= OS.HDF_BITMAP; + hdItem.hbm = image.handle; + } else { + hdItem.mask &= ~OS.HDI_BITMAP; + hdItem.fmt &= ~OS.HDF_BITMAP; + hdItem.fmt |= OS.HDF_IMAGE; + hdItem.iImage = parent.imageIndexHeader (image); + } + if (right) hdItem.fmt |= OS.HDF_BITMAP_ON_RIGHT; + } else { + hdItem.mask &= ~(OS.HDI_IMAGE | OS.HDI_BITMAP); + hdItem.fmt &= ~(OS.HDF_IMAGE | OS.HDF_BITMAP); + } + OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem); +} + +/** + * 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.moveable = 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 (); + this.resizable = resizable; +} + +void setSortDirection (int direction) { + if (OS.COMCTL32_MAJOR >= 6) { + int /*long*/ hwndHeader = parent.hwndHeader; + if (hwndHeader != 0) { + int index = parent.indexOf (this); + if (index == -1) return; + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_FORMAT | OS.HDI_IMAGE; + OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem); + switch (direction) { + case SWT.UP: + hdItem.fmt &= ~(OS.HDF_IMAGE | OS.HDF_SORTDOWN); + hdItem.fmt |= OS.HDF_SORTUP; + if (image == null) hdItem.mask &= ~OS.HDI_IMAGE; + break; + case SWT.DOWN: + hdItem.fmt &= ~(OS.HDF_IMAGE | OS.HDF_SORTUP); + hdItem.fmt |= OS.HDF_SORTDOWN; + if (image == null) hdItem.mask &= ~OS.HDI_IMAGE; + break; + case SWT.NONE: + hdItem.fmt &= ~(OS.HDF_SORTUP | OS.HDF_SORTDOWN); + if (image != null) { + hdItem.fmt |= OS.HDF_IMAGE; + hdItem.iImage = parent.imageIndexHeader (image); + } else { + hdItem.fmt &= ~OS.HDF_IMAGE; + hdItem.mask &= ~OS.HDI_IMAGE; + } + break; + } + OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem); + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + int /*long*/ hwnd = parent.handle; + parent.forceResize (); + RECT rect = new RECT (), headerRect = new RECT (); + OS.GetClientRect (hwnd, rect); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect); + rect.left = headerRect.left; + rect.right = headerRect.right; + OS.InvalidateRect (hwnd, rect, true); + } + } + } else { + switch (direction) { + case SWT.UP: + case SWT.DOWN: + setImage (display.getSortImage (direction), true, true); + break; + case SWT.NONE: + setImage (image, false, false); + break; + } + } +} + +public void setText (String string) { + checkWidget (); + if (string == null) error (SWT.ERROR_NULL_ARGUMENT); + if (string.equals (text)) return; + int index = parent.indexOf (this); + if (index == -1) return; + super.setText (string); + /* + * Bug in Windows. When a column header contains a + * mnemonic character, Windows does not measure the + * text properly. This causes '...' to always appear + * at the end of the text. The fix is to remove + * mnemonic characters and replace doubled mnemonics + * with spaces. + */ + int /*long*/ hHeap = OS.GetProcessHeap (); + TCHAR buffer = new TCHAR (parent.getCodePage (), fixMnemonic (string, true), true); + int byteCount = buffer.length () * TCHAR.sizeof; + int /*long*/ pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); + OS.MoveMemory (pszText, buffer, byteCount); + int /*long*/ hwndHeader = parent.hwndHeader; + if (hwndHeader == 0) return; + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_TEXT; + hdItem.pszText = pszText; + int /*long*/ result = OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem); + if (pszText != 0) OS.HeapFree (hHeap, 0, pszText); + if (result == 0) error (SWT.ERROR_CANNOT_SET_TEXT); +} + +/** + * 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; + int /*long*/ hwndHeaderToolTip = parent.headerToolTipHandle; + if (hwndHeaderToolTip == 0) { + parent.createHeaderToolTips (); + parent.updateHeaderToolTips (); + } +} + +/** + * 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; + int index = parent.indexOf (this); + if (index == -1) return; + int /*long*/ hwndHeader = parent.hwndHeader; + if (hwndHeader == 0) return; + HDITEM hdItem = new HDITEM (); + hdItem.mask = OS.HDI_WIDTH; + hdItem.cxy = width; + OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem); + RECT headerRect = new RECT (); + OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect); + parent.forceResize (); + int /*long*/ hwnd = parent.handle; + RECT rect = new RECT (); + OS.GetClientRect (hwnd, rect); + rect.left = headerRect.left; + OS.InvalidateRect (hwnd, rect, true); + parent.setScrollWidth (); +} + +void updateToolTip (int index) { + int /*long*/ hwndHeaderToolTip = parent.headerToolTipHandle; + if (hwndHeaderToolTip != 0) { + int /*long*/ hwndHeader = parent.hwndHeader; + RECT rect = new RECT (); + if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, rect) != 0) { + TOOLINFO lpti = new TOOLINFO (); + lpti.cbSize = TOOLINFO.sizeof; + lpti.hwnd = hwndHeader; + lpti.uId = id; + lpti.left = rect.left; + lpti.top = rect.top; + lpti.right = rect.right; + lpti.bottom = rect.bottom; + OS.SendMessage (hwndHeaderToolTip, OS.TTM_NEWTOOLRECT, 0, lpti); + } + } +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TreeItem.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TreeItem.java new file mode 100755 index 0000000000..40bf5358b0 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TreeItem.java @@ -0,0 +1,1819 @@ +/******************************************************************************* + * 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.win32.*; +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 { + /** + * 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 int /*long*/ handle; + Tree parent; + String [] strings; + Image [] images; + Font font; + Font [] cellFont; + boolean cached; + int background = -1, foreground = -1; + int [] cellBackground, cellForeground; + +/** + * 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 (parent, style, OS.TVGN_ROOT, OS.TVI_LAST, 0); +} + +/** + * 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 (parent, style, OS.TVGN_ROOT, findPrevious (parent, index), 0); +} + +/** + * 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, style, parentItem.handle, OS.TVI_LAST, 0); +} + +/** + * 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, style, parentItem.handle, findPrevious (parentItem, index), 0); +} + +TreeItem (Tree parent, int style, int /*long*/ hParent, int /*long*/ hInsertAfter, int /*long*/ hItem) { + super (parent, style); + this.parent = parent; + parent.createItem (this, hParent, hInsertAfter, hItem); +} + +static TreeItem checkNull (TreeItem item) { + if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return item; +} + +static int /*long*/ findPrevious (Tree parent, int index) { + if (parent == null) return 0; + if (index < 0) SWT.error (SWT.ERROR_INVALID_RANGE); + if (index == 0) return OS.TVI_FIRST; + int /*long*/ hwnd = parent.handle; + int /*long*/ hFirstItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); + int /*long*/ hItem = parent.findItem (hFirstItem, index - 1); + if (hItem == 0) SWT.error (SWT.ERROR_INVALID_RANGE); + return hItem; +} + +static int /*long*/ findPrevious (TreeItem parentItem, int index) { + if (parentItem == null) return 0; + if (index < 0) SWT.error (SWT.ERROR_INVALID_RANGE); + if (index == 0) return OS.TVI_FIRST; + Tree parent = parentItem.parent; + int /*long*/ hwnd = parent.handle, hParent = parentItem.handle; + int /*long*/ hFirstItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hParent); + int /*long*/ hItem = parent.findItem (hFirstItem, index - 1); + if (hItem == 0) SWT.error (SWT.ERROR_INVALID_RANGE); + return hItem; +} + +protected void checkSubclass () { + if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); +} + +void clear () { + text = ""; + image = null; + strings = null; + images = null; + if ((parent.style & SWT.CHECK) != 0) { + int /*long*/ hwnd = parent.handle; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_STATEIMAGEMASK; + tvItem.state = 1 << 12; + tvItem.hItem = handle; + OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem); + } + background = foreground = -1; + font = null; + cellBackground = cellForeground = null; + cellFont = null; + if ((parent.style & SWT.VIRTUAL) != 0) cached = false; +} + +/** + * 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 /*long*/ hwnd = parent.handle; + int /*long*/ hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle); + if (hItem == 0) error (SWT.ERROR_INVALID_RANGE); + hItem = parent.findItem (hItem, index); + if (hItem == 0) error (SWT.ERROR_INVALID_RANGE); + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + parent.clear (hItem, tvItem); + if (all) { + hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); + parent.clearAll (hItem, tvItem, 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 (); + int /*long*/ hwnd = parent.handle; + int /*long*/ hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle); + if (hItem == 0) return; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + parent.clearAll (hItem, tvItem, all); +} + +void destroyWidget () { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + parent.releaseItem (handle, tvItem, false); + parent.destroyItem (this, handle); + releaseHandle (); +} + +int /*long*/ fontHandle (int index) { + if (cellFont != null && cellFont [index] != null) return cellFont [index].handle; + if (font != null) return font.handle; + return -1; +} + +/** + * 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + if (background == -1) return parent.getBackground (); + return Color.win32_new (display, background); +} + +/** + * 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + int count = Math.max (1, parent.getColumnCount ()); + if (0 > index || index > count - 1) return getBackground (); + int pixel = cellBackground != null ? cellBackground [index] : -1; + return pixel == -1 ? getBackground () : Color.win32_new (display, pixel); +} + +/** + * 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + RECT rect = getBounds (0, true, false, false); + int width = rect.right - rect.left, height = rect.bottom - rect.top; + return new Rectangle (rect.left, rect.top, width, 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + RECT rect = getBounds (index, true, true, true); + int width = rect.right - rect.left, height = rect.bottom - rect.top; + return new Rectangle (rect.left, rect.top, width, height); +} + +RECT getBounds (int index, boolean getText, boolean getImage, boolean fullText) { + return getBounds (index, getText, getImage, fullText, false, true, 0); +} + +//TODO - take into account grid (add boolean arg) to damage less during redraw +RECT getBounds (int index, boolean getText, boolean getImage, boolean fullText, boolean fullImage, boolean clip, int /*long*/ hDC) { + if (!getText && !getImage) return new RECT (); + int /*long*/ hwnd = parent.handle; + if ((parent.style & SWT.VIRTUAL) == 0 && !cached && !parent.painted) { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_TEXT; + tvItem.hItem = handle; + tvItem.pszText = OS.LPSTR_TEXTCALLBACK; + parent.ignoreCustomDraw = true; + OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem); + parent.ignoreCustomDraw = false; + } + boolean firstColumn = index == 0; + int columnCount = 0; + int /*long*/ hwndHeader = parent.hwndHeader; + if (hwndHeader != 0) { + columnCount = parent.columnCount; + firstColumn = index == OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0); + } + RECT rect = new RECT (); + if (firstColumn) { + boolean full = columnCount == 0 && getText && getImage && fullText && fullImage; + if (!OS.TreeView_GetItemRect (hwnd, handle, rect, !full)) { + return new RECT (); + } + if (getImage && !fullImage) { + if (OS.SendMessage (hwnd, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0) != 0) { + Point size = parent.getImageSize (); + rect.left -= size.x + Tree.INSET; + if (!getText) rect.right = rect.left + size.x; + } else { + if (!getText) rect.right = rect.left; + } + } + if (fullText || fullImage || clip) { + if (hwndHeader != 0) { + RECT headerRect = new RECT (); + if (columnCount != 0) { + if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect) == 0) { + return new RECT (); + } + } else { + headerRect.right = parent.scrollWidth; + if (headerRect.right == 0) headerRect = rect; + } + if (fullText && clip) rect.right = headerRect.right; + if (fullImage) rect.left = headerRect.left; + if (clip && headerRect.right < rect.right) { + rect.right = headerRect.right; + } + } + } + } else { + if (!(0 <= index && index < columnCount)) return new RECT (); + RECT headerRect = new RECT (); + if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect) == 0) { + return new RECT (); + } + if (!OS.TreeView_GetItemRect (hwnd, handle, rect, false)) { + return new RECT (); + } + rect.left = headerRect.left; + if (fullText && getImage && clip) { + rect.right = headerRect.right; + } else { + rect.right = headerRect.left; + Image image = null; + if (index == 0) { + image = this.image; + } else { + if (images != null) image = images [index]; + } + if (image != null) { + Point size = parent.getImageSize (); + rect.right += size.x; + } + if (getText) { + if (fullText && clip) { + rect.left = rect.right + Tree.INSET; + rect.right = headerRect.right; + } else { + String string = index == 0 ? text : strings != null ? strings [index] : null; + if (string != null) { + RECT textRect = new RECT (); + TCHAR buffer = new TCHAR (parent.getCodePage (), string, false); + int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_CALCRECT; + int /*long*/ hNewDC = hDC, hFont = 0; + if (hDC == 0) { + hNewDC = OS.GetDC (hwnd); + hFont = fontHandle (index); + if (hFont == -1) hFont = OS.SendMessage (hwnd, OS.WM_GETFONT, 0, 0); + hFont = OS.SelectObject (hNewDC, hFont); + } + OS.DrawText (hNewDC, buffer, buffer.length (), textRect, flags); + if (hDC == 0) { + OS.SelectObject (hNewDC, hFont); + OS.ReleaseDC (hwnd, hNewDC); + } + if (getImage) { + rect.right += textRect.right - textRect.left + Tree.INSET * 3; + } else { + rect.left = rect.right + Tree.INSET; + rect.right = rect.left + (textRect.right - textRect.left) + Tree.INSET; + } + } + } + } + if (clip && headerRect.right < rect.right) { + rect.right = headerRect.right; + } + } + } + int gridWidth = parent.linesVisible && columnCount != 0 ? Tree.GRID_WIDTH : 0; + if (getText || !getImage) { + rect.right = Math.max (rect.left, rect.right - gridWidth); + } + rect.bottom = Math.max (rect.top, rect.bottom - gridWidth); + return rect; +} + +/** + * 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + if ((parent.style & SWT.CHECK) == 0) return false; + int /*long*/ hwnd = parent.handle; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_STATEIMAGEMASK; + tvItem.hItem = handle; + int /*long*/ result = OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem); + return (result != 0) && (((tvItem.state >> 12) & 1) == 0); +} + +/** + * 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 (); + int /*long*/ hwnd = parent.handle; + int state = 0; + if (OS.IsWinCE) { + TVITEM tvItem = new TVITEM (); + tvItem.hItem = handle; + tvItem.mask = OS.TVIF_STATE; + OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem); + state = tvItem.state; + } else { + /* + * Bug in Windows. Despite the fact that TVM_GETITEMSTATE claims + * to return only the bits specified by the stateMask, when called + * with TVIS_EXPANDED, the entire state is returned. The fix is + * to explicitly check for the TVIS_EXPANDED bit. + */ + state = (int)/*64*/OS.SendMessage (hwnd, OS.TVM_GETITEMSTATE, handle, OS.TVIS_EXPANDED); + } + return (state & OS.TVIS_EXPANDED) != 0; +} + +/** + * 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, true)) 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + int count = Math.max (1, parent.getColumnCount ()); + 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + if (foreground == -1) return parent.getForeground (); + return Color.win32_new (display, foreground); +} + +/** + * + * 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + int count = Math.max (1, parent.getColumnCount ()); + if (0 > index || index > count -1) return getForeground (); + int pixel = cellForeground != null ? cellForeground [index] : -1; + return pixel == -1 ? getForeground () : Color.win32_new (display, pixel); +} + +/** + * 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + if ((parent.style & SWT.CHECK) == 0) return false; + int /*long*/ hwnd = parent.handle; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_STATEIMAGEMASK; + tvItem.hItem = handle; + int /*long*/ result = OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem); + return (result != 0) && ((tvItem.state >> 12) > 2); +} + +/** + * 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + int /*long*/ hwnd = parent.handle; + int /*long*/ hFirstItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle); + if (hFirstItem == 0) error (SWT.ERROR_INVALID_RANGE); + int /*long*/ hItem = parent.findItem (hFirstItem, index); + if (hItem == 0) error (SWT.ERROR_INVALID_RANGE); + return parent._getItem (hItem); +} + +/** + * 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + int /*long*/ hwnd = parent.handle; + int /*long*/ hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle); + if (hItem == 0) return 0; + return parent.getItemCount (hItem); +} + +/** + * 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + int /*long*/ hwnd = parent.handle; + int /*long*/ hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle); + if (hItem == 0) return new TreeItem [0]; + return parent.getItems (hItem); +} + +public Image getImage () { + checkWidget(); + if (!parent.checkData (this, true)) 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, true)) 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + RECT rect = getBounds (index, false, true, false); + int width = rect.right - rect.left, height = rect.bottom - rect.top; + return new Rectangle (rect.left, rect.top, width, height); +} + +/** + * 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 (); + int /*long*/ hwnd = parent.handle; + int /*long*/ hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, handle); + return hItem != 0 ? parent._getItem (hItem) : null; +} + +public String getText () { + checkWidget(); + if (!parent.checkData (this, true)) 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, true)) 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, true)) error (SWT.ERROR_WIDGET_DISPOSED); + RECT rect = getBounds (index, true, false, true); + if (index == 0) rect.left += Tree.INSET - 1; + rect.left = Math.min (rect.left, rect.right); + rect.right = rect.right - Tree.INSET; + int width = Math.max (0, rect.right - rect.left); + int height = Math.max (0, rect.bottom - rect.top); + return new Rectangle (rect.left, rect.top, width, 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); + int /*long*/ hwnd = parent.handle; + int /*long*/ hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle); + return hItem == 0 ? -1 : parent.findIndex (hItem, item.handle); +} + +void redraw () { + if (parent.currentItem == this || !parent.getDrawing ()) return; + int /*long*/ hwnd = parent.handle; + if (!OS.IsWindowVisible (hwnd)) return; + /* + * When there are no columns and the tree is not + * full selection, redraw only the text. This is + * an optimization to reduce flashing. + */ + boolean full = (parent.style & (SWT.FULL_SELECTION | SWT.VIRTUAL)) != 0; + if (!full) { + full = parent.columnCount != 0; + if (!full) { + if (parent.hooks (SWT.EraseItem) || parent.hooks (SWT.PaintItem)) { + full = true; + } + } + } + RECT rect = new RECT (); + if (OS.TreeView_GetItemRect (hwnd, handle, rect, !full)) { + OS.InvalidateRect (hwnd, rect, true); + } +} + +void redraw (int column, boolean drawText, boolean drawImage) { + if (parent.currentItem == this || !parent.getDrawing ()) return; + int /*long*/ hwnd = parent.handle; + if (!OS.IsWindowVisible (hwnd)) return; + boolean fullImage = column == 0 && drawText && drawImage; + RECT rect = getBounds (column, drawText, drawImage, true, fullImage, true, 0); + OS.InvalidateRect (hwnd, rect, true); +} + +void releaseChildren (boolean destroy) { + if (destroy) { + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + parent.releaseItems (handle, tvItem); + } + super.releaseChildren (destroy); +} + +void releaseHandle () { + super.releaseHandle (); + handle = 0; + parent = null; +} + +void releaseWidget () { + super.releaseWidget (); + strings = null; + images = 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 (); + int /*long*/ hwnd = parent.handle; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; + tvItem.hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle); + while (tvItem.hItem != 0) { + OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem); + TreeItem item = tvItem.lParam != -1 ? parent.items [(int)/*64*/tvItem.lParam] : null; + if (item != null && !item.isDisposed ()) { + item.dispose (); + } else { + parent.releaseItem (tvItem.hItem, tvItem, false); + parent.destroyItem (null, tvItem.hItem); + } + tvItem.hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle); + } +} + +/** + * 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); + } + int pixel = -1; + if (color != null) { + parent.customDraw = true; + pixel = color.handle; + } + if (background == pixel) return; + background = pixel; + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + redraw (); +} + +/** + * 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.getColumnCount ()); + if (0 > index || index > count - 1) return; + int pixel = -1; + if (color != null) { + parent.customDraw = true; + pixel = color.handle; + } + if (cellBackground == null) { + cellBackground = new int [count]; + for (int i = 0; i < count; i++) { + cellBackground [i] = -1; + } + } + if (cellBackground [index] == pixel) return; + cellBackground [index] = pixel; + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + redraw (index, true, true); +} + +/** + * 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; + int /*long*/ hwnd = parent.handle; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_STATEIMAGEMASK; + tvItem.hItem = handle; + OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem); + int state = tvItem.state >> 12; + if (checked) { + if ((state & 0x1) != 0) state++; + } else { + if ((state & 0x1) == 0) --state; + } + state <<= 12; + if (tvItem.state == state) return; + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + tvItem.state = state; + OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem); + /* + * Bug in Windows. When TVM_SETITEM is used to set + * the state image of an item inside TVN_GETDISPINFO, + * the new state is not redrawn. The fix is to force + * a redraw. + */ + if ((parent.style & SWT.VIRTUAL) != 0) { + if (parent.currentItem == this && OS.IsWindowVisible (hwnd)) { + RECT rect = new RECT (); + if (OS.TreeView_GetItemRect (hwnd, handle, rect, false)) { + OS.InvalidateRect (hwnd, rect, true); + } + } + } +} + +/** + * 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 */ + int /*long*/ hwnd = parent.handle; + if (OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle) == 0) { + return; + } + int state = 0; + if (OS.IsWinCE) { + TVITEM tvItem = new TVITEM (); + tvItem.hItem = handle; + tvItem.mask = OS.TVIF_STATE; + OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem); + state = tvItem.state; + } else { + /* + * Bug in Windows. Despite the fact that TVM_GETITEMSTATE claims + * to return only the bits specified by the stateMask, when called + * with TVIS_EXPANDED, the entire state is returned. The fix is + * to explicitly check for the TVIS_EXPANDED bit. + */ + state = (int)/*64*/OS.SendMessage (hwnd, OS.TVM_GETITEMSTATE, handle, OS.TVIS_EXPANDED); + } + if (((state & OS.TVIS_EXPANDED) != 0) == expanded) return; + + /* + * Feature in Windows. When TVM_EXPAND is used to expand + * an item, the widget scrolls to show the item and the + * newly expanded items. While not strictly incorrect, + * this means that application code that expands tree items + * in a background thread can scroll the widget while the + * user is interacting with it. The fix is to remember + * the top item and the bounds of every tree item, turn + * redraw off, expand the item, scroll back to the top + * item. If none of the rectangles have moved, then + * it is safe to turn redraw back on without redrawing + * the control. + */ + RECT oldRect = null; + RECT [] rects = null; + SCROLLINFO oldInfo = null; + int count = 0; + int /*long*/ hBottomItem = 0; + boolean redraw = false, noScroll = true; + int /*long*/ hTopItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); + if (noScroll && hTopItem != 0) { + oldInfo = new SCROLLINFO (); + oldInfo.cbSize = SCROLLINFO.sizeof; + oldInfo.fMask = OS.SIF_ALL; + if (!OS.GetScrollInfo (hwnd, OS.SB_HORZ, oldInfo)) { + oldInfo = null; + } + if (parent.getDrawing () && OS.IsWindowVisible (hwnd)) { + boolean noAnimate = true; + count = (int)/*64*/OS.SendMessage (hwnd, OS.TVM_GETVISIBLECOUNT, 0, 0); + rects = new RECT [count + 1]; + int /*long*/ hItem = hTopItem; + int index = 0; + while (hItem != 0 && (noAnimate || hItem != handle) && index < count) { + RECT rect = new RECT (); + if (OS.TreeView_GetItemRect (hwnd, hItem, rect, true)) { + rects [index++] = rect; + } + hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem); + } + if (noAnimate || hItem != handle) { + redraw = true; + count = index; + hBottomItem = hItem; + oldRect = new RECT (); + OS.GetClientRect (hwnd, oldRect); + int /*long*/ topHandle = parent.topHandle (); + OS.UpdateWindow (topHandle); + OS.DefWindowProc (topHandle, OS.WM_SETREDRAW, 0, 0); + if (hwnd != topHandle) { + OS.UpdateWindow (hwnd); + OS.DefWindowProc (hwnd, OS.WM_SETREDRAW, 0, 0); + } + /* + * This code is intentionally commented. + */ +// OS.SendMessage (hwnd, OS.WM_SETREDRAW, 0, 0); + } + } + } + + /* + * Feature in Windows. When the user collapses the root + * of a subtree that has the focus item, Windows moves + * the selection to the root of the subtree and issues + * a TVN_SELCHANGED to inform the programmer that the + * selection has changed. When the programmer collapses + * the same subtree using TVM_EXPAND, Windows does not + * send the selection changed notification. This is not + * strictly wrong but is inconsistent. The fix is to + * check whether the selection has changed and issue + * the event. + */ + int /*long*/ hOldItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + + /* Expand or collapse the item */ + parent.ignoreExpand = true; + OS.SendMessage (hwnd, OS.TVM_EXPAND, expanded ? OS.TVE_EXPAND : OS.TVE_COLLAPSE, handle); + parent.ignoreExpand = false; + + /* Scroll back to the top item */ + if (noScroll && hTopItem != 0) { + boolean collapsed = false; + if (!expanded) { + RECT rect = new RECT (); + while (hTopItem != 0 && !OS.TreeView_GetItemRect (hwnd, hTopItem, rect, false)) { + hTopItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hTopItem); + collapsed = true; + } + } + boolean scrolled = true; + if (hTopItem != 0) { + OS.SendMessage (hwnd, OS.TVM_SELECTITEM, OS.TVGN_FIRSTVISIBLE, hTopItem); + scrolled = hTopItem != OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); + } + if (!collapsed && !scrolled && oldInfo != null) { + SCROLLINFO newInfo = new SCROLLINFO (); + newInfo.cbSize = SCROLLINFO.sizeof; + newInfo.fMask = OS.SIF_ALL; + if (OS.GetScrollInfo (hwnd, OS.SB_HORZ, newInfo)) { + if (oldInfo.nPos != newInfo.nPos) { + int /*long*/ lParam = OS.MAKELPARAM (OS.SB_THUMBPOSITION, oldInfo.nPos); + OS.SendMessage (hwnd, OS.WM_HSCROLL, lParam, 0); + } + } + } + if (redraw) { + boolean fixScroll = false; + if (!collapsed && !scrolled) { + RECT newRect = new RECT (); + OS.GetClientRect (hwnd, newRect); + if (OS.EqualRect (oldRect, newRect)) { + int /*long*/ hItem = hTopItem; + int index = 0; + while (hItem != 0 && index < count) { + RECT rect = new RECT (); + if (OS.TreeView_GetItemRect (hwnd, hItem, rect, true)) { + if (!OS.EqualRect (rect, rects [index])) { + break; + } + } + hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem); + index++; + } + fixScroll = index == count && hItem == hBottomItem; + } + } + int /*long*/ topHandle = parent.topHandle (); + OS.DefWindowProc (topHandle, OS.WM_SETREDRAW, 1, 0); + if (hwnd != topHandle) { + OS.DefWindowProc (hwnd, OS.WM_SETREDRAW, 1, 0); + } + /* + * This code is intentionally commented. + */ +// OS.SendMessage (hwnd, OS.WM_SETREDRAW, 1, 0); + if (fixScroll) { + parent.updateScrollBar (); + SCROLLINFO info = new SCROLLINFO (); + info.cbSize = SCROLLINFO.sizeof; + info.fMask = OS.SIF_ALL; + if (OS.GetScrollInfo (hwnd, OS.SB_VERT, info)) { + OS.SetScrollInfo (hwnd, OS.SB_VERT, info, true); + } + if (handle == hBottomItem) { + RECT rect = new RECT (); + if (OS.TreeView_GetItemRect (hwnd, hBottomItem, rect, false)) { + OS.InvalidateRect (hwnd, rect, true); + } + } + } else { + if (OS.IsWinCE) { + OS.InvalidateRect (topHandle, null, true); + if (hwnd != topHandle) OS.InvalidateRect (hwnd, null, true); + } else { + int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (topHandle, null, 0, flags); + } + } + } + } + + /* Check for a selection event */ + int /*long*/ hNewItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); + if (hNewItem != hOldItem) { + Event event = new Event (); + if (hNewItem != 0) { + event.item = parent._getItem (hNewItem); + parent.hAnchor = hNewItem; + } + parent.sendEvent (SWT.Selection, event); + } +} + +/** + * 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; + if (font != null) parent.customDraw = true; + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + /* + * Bug in Windows. When the font is changed for an item, + * the bounds for the item are not updated, causing the text + * to be clipped. The fix is to reset the text, causing + * Windows to compute the new bounds using the new font. + */ + if ((parent.style & SWT.VIRTUAL) == 0 && !cached && !parent.painted) { + return; + } + int /*long*/ hwnd = parent.handle; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_TEXT; + tvItem.hItem = handle; + tvItem.pszText = OS.LPSTR_TEXTCALLBACK; + OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem); +} + + +/** + * 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.getColumnCount ()); + 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; + if (font != null) parent.customDraw = true; + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + /* + * Bug in Windows. When the font is changed for an item, + * the bounds for the item are not updated, causing the text + * to be clipped. The fix is to reset the text, causing + * Windows to compute the new bounds using the new font. + */ + if (index == 0) { + if ((parent.style & SWT.VIRTUAL) == 0 && !cached && !parent.painted) { + return; + } + int /*long*/ hwnd = parent.handle; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_TEXT; + tvItem.hItem = handle; + tvItem.pszText = OS.LPSTR_TEXTCALLBACK; + OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem); + } else { + redraw (index, true, false); + } +} + +/** + * 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); + } + int pixel = -1; + if (color != null) { + parent.customDraw = true; + pixel = color.handle; + } + if (foreground == pixel) return; + foreground = pixel; + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + redraw (); +} + +/** + * 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.getColumnCount ()); + if (0 > index || index > count - 1) return; + int pixel = -1; + if (color != null) { + parent.customDraw = true; + pixel = color.handle; + } + if (cellForeground == null) { + cellForeground = new int [count]; + for (int i = 0; i < count; i++) { + cellForeground [i] = -1; + } + } + if (cellForeground [index] == pixel) return; + cellForeground [index] = pixel; + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + redraw (index, true, false); +} + +/** + * 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; + int /*long*/ hwnd = parent.handle; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_STATEIMAGEMASK; + tvItem.hItem = handle; + OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem); + int state = tvItem.state >> 12; + if (grayed) { + if (state <= 2) state +=2; + } else { + if (state > 2) state -=2; + } + state <<= 12; + if (tvItem.state == state) return; + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + tvItem.state = state; + OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem); + /* + * Bug in Windows. When TVM_SETITEM is used to set + * the state image of an item inside TVN_GETDISPINFO, + * the new state is not redrawn. The fix is to force + * a redraw. + */ + if ((parent.style & SWT.VIRTUAL) != 0) { + if (parent.currentItem == this && OS.IsWindowVisible (hwnd)) { + RECT rect = new RECT (); + if (OS.TreeView_GetItemRect (hwnd, handle, rect, false)) { + OS.InvalidateRect (hwnd, rect, true); + } + } + } +} + +/** + * 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); + } + Image oldImage = null; + if (index == 0) { + if (image != null && image.type == SWT.ICON) { + if (image.equals (this.image)) return; + } + oldImage = this.image; + super.setImage (image); + } + int count = Math.max (1, parent.getColumnCount ()); + if (0 > index || index > count - 1) return; + if (images == null && index != 0) { + images = new Image [count]; + images [0] = image; + } + if (images != null) { + if (image != null && image.type == SWT.ICON) { + if (image.equals (images [index])) return; + } + oldImage = images [index]; + images [index] = image; + } + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + + /* Ensure that the image list is created */ + //TODO - items that are not in column zero don't need to be in the image list + parent.imageIndex (image, index); + + if (index == 0) { + if ((parent.style & SWT.VIRTUAL) == 0 &&!cached && !parent.painted) { + return; + } + int /*long*/ hwnd = parent.handle; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_IMAGE | OS.TVIF_SELECTEDIMAGE; + tvItem.hItem = handle; + tvItem.iImage = tvItem.iSelectedImage = OS.I_IMAGECALLBACK; + /* + * Bug in Windows. When I_IMAGECALLBACK is used with TVM_SETITEM + * to indicate that an image has changed, Windows does not draw + * the new image. The fix is to use LPSTR_TEXTCALLBACK to force + * Windows to ask for the text, causing Windows to ask for both. + */ + tvItem.mask |= OS.TVIF_TEXT; + tvItem.pszText = OS.LPSTR_TEXTCALLBACK; + OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem); + } else { + boolean drawText = (image == null && oldImage != null) || (image != null && oldImage == null); + redraw (index, drawText, true); + } +} + +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); + int /*long*/ hwnd = parent.handle; + int /*long*/ hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle); + parent.setItemCount (count, handle, hItem); +} + +/** + * 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; + super.setText (string); + } + int count = Math.max (1, parent.getColumnCount ()); + if (0 > index || index > count - 1) return; + if (strings == null && index != 0) { + strings = new String [count]; + strings [0] = text; + } + if (strings != null) { + if (string.equals (strings [index])) return; + strings [index] = string; + } + if ((parent.style & SWT.VIRTUAL) != 0) cached = true; + if (index == 0) { + if ((parent.style & SWT.VIRTUAL) == 0 && !cached && !parent.painted) { + return; + } + int /*long*/ hwnd = parent.handle; + TVITEM tvItem = new TVITEM (); + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_TEXT; + tvItem.hItem = handle; + tvItem.pszText = OS.LPSTR_TEXTCALLBACK; + OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem); + } else { + redraw (index, true, false); + } +} + +public void setText (String string) { + checkWidget(); + setText (0, string); +} + +/*public*/ void sort () { + checkWidget (); + if ((parent.style & SWT.VIRTUAL) != 0) return; + parent.sort (handle, false); +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Widget.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Widget.java new file mode 100755 index 0000000000..a5b8368f1f --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Widget.java @@ -0,0 +1,2517 @@ +/******************************************************************************* + * 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.win32.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +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; + + /* 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; + + /* A layout was requested on this widget */ + static final int LAYOUT_NEEDED = 1<<5; + + /* The preferred size of a child has changed */ + static final int LAYOUT_CHANGED = 1<<6; + + /* A layout was requested in this widget hierarchy */ + static final int LAYOUT_CHILD = 1<<7; + + /* Background flags */ + static final int THEME_BACKGROUND = 1<<8; + static final int DRAW_BACKGROUND = 1<<9; + static final int PARENT_BACKGROUND = 1<<10; + + /* Dispose and release flags */ + static final int RELEASED = 1<<11; + static final int DISPOSE_SENT = 1<<12; + + /* More global widget state flags */ + static final int TRACK_MOUSE = 1<<13; + static final int FOREIGN_HANDLE = 1<<14; + static final int DRAG_DETECT = 1<<15; + + /* Move and resize state flags */ + static final int MOVE_OCCURRED = 1<<16; + static final int MOVE_DEFERRED = 1<<17; + static final int RESIZE_OCCURRED = 1<<18; + static final int RESIZE_DEFERRED = 1<<19; + + /* Ignore WM_CHANGEUISTATE */ + static final int IGNORE_WM_CHANGEUISTATE = 1<<20; + + /* Default size for widgets */ + static final int DEFAULT_WIDTH = 64; + static final int DEFAULT_HEIGHT = 64; + + /* Check and initialize the Common Controls DLL */ + static final int MAJOR = 5, MINOR = 80; + static { + if (!OS.IsWinCE) { + if (OS.COMCTL32_VERSION < OS.VERSION (MAJOR, MINOR)) { + System.out.println ("***WARNING: SWT requires comctl32.dll version " + MAJOR + "." + MINOR + " or greater"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + System.out.println ("***WARNING: Detected: " + OS.COMCTL32_MAJOR + "." + OS.COMCTL32_MINOR); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + OS.InitCommonControls (); + } + +/** + * Prevents uninitialized instances from being created outside the package. + */ +Widget () { +} + +/** + * 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; +} + +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 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); +} + +/** + * 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); +} + +int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + return 0; +} + +/** + * Returns a style with exactly one style bit set out of + * the specified set of exclusive style bits. All other + * possible bits are cleared when the first matching bit + * is found. Bits that are not part of the possible set + * are untouched. + * + * @param style the original style bits + * @param int0 the 0th possible style bit + * @param int1 the 1st possible style bit + * @param int2 the 2nd possible style bit + * @param int3 the 3rd possible style bit + * @param int4 the 4th possible style bit + * @param int5 the 5th possible style bit + * + * @return the new style bits + */ +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 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 checkOpened () { + /* Do nothing */ +} + +/** + * Throws an exception if the specified widget can not be + * used as a parent for the receiver. + * + * @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> + * </ul> + */ +void checkParent (Widget parent) { + if (parent == null) error (SWT.ERROR_NULL_ARGUMENT); + if (parent.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); + parent.checkWidget (); + parent.checkOpened (); +} + +/** + * 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 ()) { + /* + * Bug in IBM JVM 1.6. For some reason, under + * conditions that are yet to be full understood, + * Thread.currentThread() is either returning null + * or a different instance from the one that was + * saved when the Display was created. This is + * possibly a JIT problem because modifying this + * method to print logging information when the + * error happens seems to fix the problem. The + * fix is to use operating system calls to verify + * that the current thread is not the Display thread. + * + * NOTE: Despite the fact that Thread.currentThread() + * is used in other places, the failure has not been + * observed in all places where it is called. + */ + if (display.threadId != OS.GetCurrentThreadId ()) { + error (SWT.ERROR_THREAD_INVALID_ACCESS); + } + } + if ((state & DISPOSED) != 0) error (SWT.ERROR_WIDGET_DISPOSED); +} + +/** + * Destroys the widget in the operating system and releases + * the widget's handle. If the widget does not have a handle, + * this method may hide the widget, mark the widget as destroyed + * or do nothing, depending on the widget. + * <p> + * When a widget is destroyed in the operating system, its + * descendants are also destroyed by the operating system. + * This means that it is only necessary to call <code>destroyWidget</code> + * on the root of the widget tree. + * </p><p> + * This method is called after <code>releaseWidget()</code>. + * </p><p> + * See also <code>releaseChild()</code>, <code>releaseWidget()</code> + * and <code>releaseHandle()</code>. + * </p> + * + * @see #dispose + */ +void destroyWidget () { + releaseHandle (); +} + +int /*long*/ DeferWindowPos(int /*long*/ hWinPosInfo, int /*long*/ hWnd, int /*long*/ hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags){ + if (OS.IsWinCE) { + /* + * Feature in Windows. On Windows CE, DeferWindowPos always causes + * a WM_SIZE message, even when the new size is the same as the old + * size. The fix is to detect that the size has not changed and set + * SWP_NOSIZE. + */ + if ((uFlags & OS.SWP_NOSIZE) == 0) { + RECT lpRect = new RECT (); + OS.GetWindowRect (hWnd, lpRect); + if (cy == lpRect.bottom - lpRect.top && cx == lpRect.right - lpRect.left) { + /* + * Feature in Windows. On Windows CE, DeferWindowPos when called + * with SWP_DRAWFRAME always causes a WM_SIZE message, even + * when SWP_NOSIZE is set and when the new size is the same as the + * old size. The fix is to clear SWP_DRAWFRAME when the size is + * the same. + */ + uFlags &= ~OS.SWP_DRAWFRAME; + uFlags |= OS.SWP_NOSIZE; + } + } + } + return OS.DeferWindowPos (hWinPosInfo, hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); +} + +/** + * 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); +} + +boolean dragDetect (int /*long*/ hwnd, int x, int y, boolean filter, boolean [] detect, boolean [] consume) { + if (consume != null) consume [0] = false; + if (detect != null) detect [0] = true; + POINT pt = new POINT (); + pt.x = x; + pt.y = y; + OS.ClientToScreen (hwnd, pt); + return OS.DragDetect (hwnd, pt); +} + +/** + * Does whatever widget specific cleanup is required, and then + * uses the code in <code>SWTError.error</code> to handle the error. + * + * @param code the descriptive error code + * + * @see SWT#error(int) + */ +void error (int code) { + SWT.error(code); +} + +boolean filters (int eventType) { + return display.filters (eventType); +} + +Widget findItem (int /*long*/ id) { + return null; +} + +char [] fixMnemonic (String string) { + return fixMnemonic (string, false); +} + +char [] fixMnemonic (String string, boolean spaces) { + char [] buffer = new char [string.length () + 1]; + string.getChars (0, string.length (), buffer, 0); + int i = 0, j = 0; + while (i < buffer.length) { + if (buffer [i] == '&') { + if (i + 1 < buffer.length && buffer [i + 1] == '&') { + if (spaces) buffer [j] = ' '; + j++; + i++; + } + i++; + } else { + buffer [j++] = buffer [i++]; + } + } + while (j < buffer.length) buffer [j++] = 0; + return buffer; +} + +/** + * 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; +} + +/** + * 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); +} + +Menu getMenu () { + return null; +} + +/** + * Returns the name of the widget. This is the name of + * the class without the package name. + * + * @return the name of the widget + */ +String getName () { + String string = getClass ().getName (); + int index = string.lastIndexOf ('.'); + if (index == -1) return string; + return string.substring (index + 1, string.length ()); +} + +/* + * Returns a short printable representation for the contents + * of a widget. For example, a button may answer the label + * text. This is used by <code>toString</code> to provide a + * more meaningful description of the widget. + * + * @return the contents string for the widget + * + * @see #toString + */ +String getNameText () { + return ""; //$NON-NLS-1$ +} + +/** + * 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; +} + +/* + * Returns <code>true</code> if the specified eventType is + * hooked, and <code>false</code> otherwise. Implementations + * of SWT can avoid creating objects and sending events + * when an event happens in the operating system but + * there are no listeners hooked for the event. + * + * @param eventType the event to be checked + * + * @return <code>true</code> when the eventType is hooked and <code>false</code> otherwise + * + * @see #isListening + */ +boolean hooks (int eventType) { + if (eventTable == null) return false; + return eventTable.hooks (eventType); +} + +/** + * 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; +} + +/** + * 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); +} + +/* + * Returns <code>true</code> when subclassing is + * allowed and <code>false</code> otherwise + * + * @return <code>true</code> when subclassing is allowed and <code>false</code> otherwise + */ +boolean isValidSubclass () { + return Display.isValidClass (getClass ()); +} + +/* + * Returns <code>true</code> when the current thread is + * the thread that created the widget and <code>false</code> + * otherwise. + * + * @return <code>true</code> when the current thread is the thread that created the widget and <code>false</code> otherwise + */ +boolean isValidThread () { + return getDisplay ().isValidThread (); +} + +void mapEvent (int /*long*/ hwnd, Event event) { +} + +GC new_GC (GCData data) { + return null; +} + +/** + * 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 postEvent (int eventType) { + sendEvent (eventType, null, false); +} + +void postEvent (int eventType, Event event) { + sendEvent (eventType, event, false); +} + +/* + * Releases the widget hierarchy and optionally destroys + * the receiver. + * <p> + * Typically, a widget with children will broadcast this + * message to all children so that they too can release their + * resources. The <code>releaseHandle</code> method is used + * as part of this broadcast to zero the handle fields of the + * children without calling <code>destroyWidget</code>. In + * this scenario, the children are actually destroyed later, + * when the operating system destroys the widget tree. + * </p> + * + * @param destroy indicates that the receiver should be destroyed + * + * @see #dispose + * @see #releaseHandle + * @see #releaseParent + * @see #releaseWidget +*/ +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) { +} + +/* + * Releases the widget's handle by zero'ing it out. + * Does not destroy or release any operating system + * resources. + * <p> + * This method is called after <code>releaseWidget</code> + * or from <code>destroyWidget</code> when a widget is being + * destroyed to ensure that the widget is marked as destroyed + * in case the act of destroying the widget in the operating + * system causes application code to run in callback that + * could access the widget. + * </p> + * + * @see #dispose + * @see #releaseChildren + * @see #releaseParent + * @see #releaseWidget + */ +void releaseHandle () { + state |= DISPOSED; + display = null; +} + +/* + * Releases the receiver, a child in a widget hierarchy, + * from its parent. + * <p> + * When a widget is destroyed, it may be necessary to remove + * it from an internal data structure of the parent. When + * a widget has no handle, it may also be necessary for the + * parent to hide the widget or otherwise indicate that the + * widget has been disposed. For example, disposing a menu + * bar requires that the menu bar first be released from the + * shell when the menu bar is active. + * </p> + * + * @see #dispose + * @see #releaseChildren + * @see #releaseWidget + * @see #releaseHandle + */ +void releaseParent () { +} + +/* + * Releases any internal resources back to the operating + * system and clears all fields except the widget handle. + * <p> + * When a widget is destroyed, resources that were acquired + * on behalf of the programmer need to be returned to the + * operating system. For example, if the widget made a + * copy of an icon, supplied by the programmer, this copy + * would be freed in <code>releaseWidget</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 widget, all fields except the + * handle are zero'd. The handle is needed by <code>destroyWidget</code>. + * </p> + * + * @see #dispose + * @see #releaseChildren + * @see #releaseHandle + * @see #releaseParent + */ +void releaseWidget () { + 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 listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (eventType, listener); +} + +/** + * 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 listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (eventType, listener); +} + +/** + * 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); +} + +boolean sendDragEvent (int button, int x, int y) { + Event event = new Event (); + event.button = button; + event.x = x; + event.y = y; + setInputState (event, SWT.DragDetect); + postEvent (SWT.DragDetect, event); + if (isDisposed ()) return false; + return event.doit; +} + +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); + if (isDisposed ()) return false; + return event.doit; +} + +void sendEvent (Event event) { + Display display = event.display; + if (!display.filterEvent (event)) { + if (eventTable != null) eventTable.sendEvent (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 (int type, int msg, int /*long*/ wParam, int /*long*/ lParam) { + Event event = new Event (); + if (!setKeyState (event, type, wParam, lParam)) return true; + return sendKeyEvent (type, msg, wParam, lParam, event); +} + +boolean sendKeyEvent (int type, int msg, int /*long*/ wParam, int /*long*/ lParam, Event event) { + sendEvent (type, event); + if (isDisposed ()) return false; + return event.doit; +} + +boolean sendMouseEvent (int type, int button, int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + return sendMouseEvent (type, button, display.getClickCount (type, button, hwnd, lParam), 0, false, hwnd, msg, wParam, lParam); +} + +boolean sendMouseEvent (int type, int button, int count, int detail, boolean send, int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) { + if (!hooks (type) && !filters (type)) return true; + Event event = new Event (); + event.button = button; + event.detail = detail; + event.count = count; + event.x = OS.GET_X_LPARAM (lParam); + event.y = OS.GET_Y_LPARAM (lParam); + setInputState (event, type); + mapEvent (hwnd, event); + if (send) { + sendEvent (type, event); + if (isDisposed ()) return false; + } else { + postEvent (type, event); + } + return event.doit; +} + +/** + * 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 ((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); + 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; + } + } + } + } +} + +boolean sendFocusEvent (int type) { + sendEvent (type); + // widget could be disposed at this point + return true; +} + +boolean setInputState (Event event, int type) { + if (OS.GetKeyState (OS.VK_MENU) < 0) event.stateMask |= SWT.ALT; + if (OS.GetKeyState (OS.VK_SHIFT) < 0) event.stateMask |= SWT.SHIFT; + if (OS.GetKeyState (OS.VK_CONTROL) < 0) event.stateMask |= SWT.CONTROL; + if (OS.GetKeyState (OS.VK_LBUTTON) < 0) event.stateMask |= SWT.BUTTON1; + if (OS.GetKeyState (OS.VK_MBUTTON) < 0) event.stateMask |= SWT.BUTTON2; + if (OS.GetKeyState (OS.VK_RBUTTON) < 0) event.stateMask |= SWT.BUTTON3; + /* + * Bug in Windows. On some machines that do not have XBUTTONs, + * the MK_XBUTTON1 and OS.MK_XBUTTON2 bits are sometimes set, + * causing mouse capture to become stuck. The fix is to test + * for the extra buttons only when they exist. + */ + if (display.xMouse) { + if (OS.GetKeyState (OS.VK_XBUTTON1) < 0) event.stateMask |= SWT.BUTTON4; + if (OS.GetKeyState (OS.VK_XBUTTON2) < 0) event.stateMask |= SWT.BUTTON5; + } + 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; + 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; + break; + } + return true; +} + +boolean setKeyState (Event event, int type, int /*long*/ wParam, int /*long*/ lParam) { + + /* + * Feature in Windows. When the user presses Ctrl+Backspace + * or Ctrl+Enter, Windows sends a WM_CHAR with Delete (0x7F) + * and '\n' instead of '\b' and '\r'. This is the correct + * platform behavior but is not portable. The fix is to detect + * these cases and convert the character. + */ + switch (display.lastAscii) { + case SWT.DEL: + if (display.lastKey == SWT.BS) display.lastAscii = SWT.BS; + break; + case SWT.LF: + if (display.lastKey == SWT.CR) display.lastAscii = SWT.CR; + break; + } + + /* + * Feature in Windows. When the user presses either the Enter + * key or the numeric keypad Enter key, Windows sends a WM_KEYDOWN + * with wParam=VK_RETURN in both cases. In order to distinguish + * between the keys, the extended key bit is tested. If the bit + * is set, assume that the numeric keypad Enter was pressed. + */ + if (display.lastKey == SWT.CR && display.lastAscii == SWT.CR) { + if ((lParam & 0x1000000) != 0) display.lastKey = SWT.KEYPAD_CR; + } + + if (display.lastVirtual) { + /* + * Feature in Windows. The virtual key VK_DELETE is not + * treated as both a virtual key and an ASCII key by Windows. + * Therefore, we will not receive a WM_CHAR for this key. + * The fix is to treat VK_DELETE as a special case and map + * the ASCII value explicitly (Delete is 0x7F). + */ + if (display.lastKey == OS.VK_DELETE) display.lastAscii = 0x7F; + + /* + * Feature in Windows. When the user presses Ctrl+Pause, the + * VK_CANCEL key is generated and a WM_CHAR is sent with 0x03, + * possibly to allow an application to look for Ctrl+C and the + * the Break key at the same time. This is unexpected and + * unwanted. The fix is to detect the case and set the character + * to zero. + */ + if (display.lastKey == OS.VK_CANCEL) display.lastAscii = 0x0; + + event.keyCode = Display.translateKey (display.lastKey); + } else { + event.keyCode = display.lastKey; + } + if (display.lastAscii != 0 || display.lastNull) { + event.character = Display.mbcsToWcs ((char) display.lastAscii); + } + if (event.keyCode == 0 && event.character == 0) { + if (!display.lastNull) return false; + } + return setInputState (event, type); +} + +boolean setTabGroupFocus () { + return setTabItemFocus (); +} + +boolean setTabItemFocus () { + return false; +} + +boolean SetWindowPos (int /*long*/ hWnd, int /*long*/ hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags) { + if (OS.IsWinCE) { + /* + * Feature in Windows. On Windows CE, SetWindowPos() always causes + * a WM_SIZE message, even when the new size is the same as the old + * size. The fix is to detect that the size has not changed and set + * SWP_NOSIZE. + */ + if ((uFlags & OS.SWP_NOSIZE) == 0) { + RECT lpRect = new RECT (); + OS.GetWindowRect (hWnd, lpRect); + if (cy == lpRect.bottom - lpRect.top && cx == lpRect.right - lpRect.left) { + /* + * Feature in Windows. On Windows CE, SetWindowPos() when called + * with SWP_DRAWFRAME always causes a WM_SIZE message, even + * when SWP_NOSIZE is set and when the new size is the same as the + * old size. The fix is to clear SWP_DRAWFRAME when the size is + * the same. + */ + uFlags &= ~OS.SWP_DRAWFRAME; + uFlags |= OS.SWP_NOSIZE; + } + } + } + return OS.SetWindowPos (hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); +} + +boolean showMenu (int x, int 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 false; + if (!event.doit) return true; + 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 true; + } + return false; +} + +/** + * 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*"; //$NON-NLS-1$ + if (!isDisposed ()) { + string = "*Wrong Thread*"; //$NON-NLS-1$ + if (isValidThread ()) string = getNameText (); + } + return getName () + " {" + string + "}"; //$NON-NLS-1$ //$NON-NLS-2$ +} + +LRESULT wmCaptureChanged (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + display.captureChanged = true; + return null; +} + +LRESULT wmChar (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + /* + * Do not report a lead byte as a key pressed. + */ + if (!OS.IsUnicode && OS.IsDBLocale) { + byte lead = (byte) (wParam & 0xFF); + if (OS.IsDBCSLeadByte (lead)) return null; + } + display.lastAscii = (int)/*64*/wParam; + display.lastNull = wParam == 0; + if (!sendKeyEvent (SWT.KeyDown, OS.WM_CHAR, wParam, lParam)) { + return LRESULT.ONE; + } + // widget could be disposed at this point + return null; +} + +LRESULT wmContextMenu (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + if (wParam != hwnd) return null; + + /* + * Feature in Windows. SHRecognizeGesture() sends an undocumented + * WM_CONTEXTMENU notification when the flag SHRG_NOTIFY_PARENT is + * not set. This causes the context menu to be displayed twice, + * once by the caller of SHRecognizeGesture() and once from this + * method. The fix is to ignore WM_CONTEXTMENU notifications on + * all WinCE platforms. + * + * NOTE: This only happens on WM2003. Previous WinCE versions did + * not support WM_CONTEXTMENU. + */ + if (OS.IsWinCE) return null; + + /* + * Feature in Windows. When the user presses WM_NCRBUTTONUP, + * a WM_CONTEXTMENU message is generated. This happens when + * the user releases the mouse over a scroll bar. Normally, + * window displays the default scrolling menu but applications + * can process WM_CONTEXTMENU to display a different menu. + * Typically, an application does not want to supply a special + * scroll menu. The fix is to look for a WM_CONTEXTMENU that + * originated from a mouse event and display the menu when the + * mouse was released in the client area. + */ + int x = 0, y = 0; + if (lParam != -1) { + POINT pt = new POINT (); + OS.POINTSTOPOINT (pt, lParam); + x = pt.x; + y = pt.y; + OS.ScreenToClient (hwnd, pt); + RECT rect = new RECT (); + OS.GetClientRect (hwnd, rect); + if (!OS.PtInRect (rect, pt)) return null; + } else { + int pos = OS.GetMessagePos (); + x = OS.GET_X_LPARAM (pos); + y = OS.GET_Y_LPARAM (pos); + } + + /* Show the menu */ + return showMenu (x, y) ? LRESULT.ZERO : null; +} + +LRESULT wmIMEChar (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + Display display = this.display; + display.lastKey = 0; + display.lastAscii = (int)/*64*/wParam; + display.lastVirtual = display.lastNull = display.lastDead = false; + if (!sendKeyEvent (SWT.KeyDown, OS.WM_IME_CHAR, wParam, lParam)) { + return LRESULT.ONE; + } + sendKeyEvent (SWT.KeyUp, OS.WM_IME_CHAR, wParam, lParam); + // widget could be disposed at this point + display.lastKey = display.lastAscii = 0; + return LRESULT.ONE; +} + +LRESULT wmKeyDown (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + + /* Ignore repeating modifier keys by testing key down state */ + switch ((int)/*64*/wParam) { + case OS.VK_SHIFT: + case OS.VK_MENU: + case OS.VK_CONTROL: + case OS.VK_CAPITAL: + case OS.VK_NUMLOCK: + case OS.VK_SCROLL: + if ((lParam & 0x40000000) != 0) return null; + } + + /* Clear last key and last ascii because a new key has been typed */ + display.lastAscii = display.lastKey = 0; + display.lastVirtual = display.lastNull = display.lastDead = false; + + /* + * Do not report a lead byte as a key pressed. + */ + if (!OS.IsUnicode && OS.IsDBLocale) { + byte lead = (byte) (wParam & 0xFF); + if (OS.IsDBCSLeadByte (lead)) return null; + } + + /* Map the virtual key */ + /* + * Bug in WinCE. MapVirtualKey() returns incorrect values. + * The fix is to rely on a key mappings table to determine + * whether the key event must be sent now or if a WM_CHAR + * event will follow. The key mappings table maps virtual + * keys to SWT key codes and does not contain mappings for + * Windows virtual keys like VK_A. Virtual keys that are + * both virtual and ASCII are a special case. + */ + int mapKey = 0; + if (OS.IsWinCE) { + switch ((int)/*64*/wParam) { + case OS.VK_BACK: mapKey = SWT.BS; break; + case OS.VK_RETURN: mapKey = SWT.CR; break; + case OS.VK_DELETE: mapKey = SWT.DEL; break; + case OS.VK_ESCAPE: mapKey = SWT.ESC; break; + case OS.VK_TAB: mapKey = SWT.TAB; break; + } + } else { + /* + * Feature in Windows. For numbers in Marathi and Bengali, + * MapVirtualKey() returns the localized number instead of + * the ASCII equivalent. For example, MapVirtualKey() + * maps VK_1 on the Marathi keyboard to \u2407, which is + * a valid Unicode Marathi '1' character, but not ASCII. + * The fix is to test for VK_0 to VK_9 and map these + * explicitly. + * + * NOTE: VK_0 to VK_9 are the same as ASCII. + */ + if ('0' <= wParam && wParam <= '9') { + mapKey = (int)/*64*/wParam; + } else { + mapKey = OS.MapVirtualKey ((int)/*64*/wParam, 2); + } + } + + /* + * Bug in Windows 95 and NT. When the user types an accent key such + * as ^ to get an accented character on a German keyboard, the accent + * key should be ignored and the next key that the user types is the + * accented key. The fix is to detect the accent key stroke (called + * a dead key) by testing the high bit of the value returned by + * MapVirtualKey(). A further problem is that the high bit on + * Windows NT is bit 32 while the high bit on Windows 95 is bit 16. + * They should both be bit 32. + * + * When the user types an accent key that does not correspond to a + * virtual key, MapVirtualKey() won't set the high bit to indicate + * a dead key. This happens when an accent key, such as '^' is the + * result of a modifier such as Shift key and MapVirtualKey() always + * returns the unshifted key. The fix is to peek for a WM_DEADCHAR + * and avoid issuing the event. + */ + if (OS.IsWinNT) { + if ((mapKey & 0x80000000) != 0) return null; + } else { + if ((mapKey & 0x8000) != 0) return null; + } + MSG msg = new MSG (); + int flags = OS.PM_NOREMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE; + if (OS.PeekMessage (msg, hwnd, OS.WM_DEADCHAR, OS.WM_DEADCHAR, flags)) { + display.lastDead = true; + display.lastVirtual = mapKey == 0; + display.lastKey = display.lastVirtual ? (int)/*64*/wParam : mapKey; + return null; + } + + /* + * Bug in Windows. Somehow, the widget is becoming disposed after + * calling PeekMessage(). In rare circumstances, it seems that + * PeekMessage() can allow SWT listeners to run that might contain + * application code that disposes the widget. It is not exactly + * clear how this can happen. PeekMessage() is only looking for + * WM_DEADCHAR. It is not dispatching any message that it finds + * or removing any message from the queue. Cross-thread messages + * are disabled. The fix is to check for a disposed widget and + * return without calling the window proc. + */ + if (isDisposed ()) return LRESULT.ONE; + + /* + * If we are going to get a WM_CHAR, ensure that last key has + * the correct character value for the key down and key up + * events. It is not sufficient to ignore the WM_KEYDOWN + * (when we know we are going to get a WM_CHAR) and compute + * the key in WM_CHAR because there is not enough information + * by the time we get the WM_CHAR. For example, when the user + * types Ctrl+Shift+6 on a US keyboard, we get a WM_CHAR with + * wParam=30. When the user types Ctrl+Shift+6 on a German + * keyboard, we also get a WM_CHAR with wParam=30. On the US + * keyboard Shift+6 is ^, on the German keyboard Shift+6 is &. + * There is no way to map wParam=30 in WM_CHAR to the correct + * value. Also, on international keyboards, the control key + * may be down when the user has not entered a control character. + * + * NOTE: On Windows 98, keypad keys are virtual despite the + * fact that a WM_CHAR is issued. On Windows 2000 and XP, + * they are not virtual. Therefore it is necessary to force + * numeric keypad keys to be virtual. + */ + display.lastVirtual = mapKey == 0 || display.numpadKey ((int)/*64*/wParam) != 0; + if (display.lastVirtual) { + display.lastKey = (int)/*64*/wParam; + /* + * Feature in Windows. The virtual key VK_DELETE is not + * treated as both a virtual key and an ASCII key by Windows. + * Therefore, we will not receive a WM_CHAR for this key. + * The fix is to treat VK_DELETE as a special case and map + * the ASCII value explicitly (Delete is 0x7F). + */ + if (display.lastKey == OS.VK_DELETE) display.lastAscii = 0x7F; + + /* + * It is possible to get a WM_CHAR for a virtual key when + * Num Lock is on. If the user types Home while Num Lock + * is down, a WM_CHAR is issued with WPARM=55 (for the + * character 7). If we are going to get a WM_CHAR we need + * to ensure that the last key has the correct value. Note + * that Ctrl+Home does not issue a WM_CHAR when Num Lock is + * down. + */ + if (OS.VK_NUMPAD0 <= display.lastKey && display.lastKey <= OS.VK_DIVIDE) { + /* + * Feature in Windows. Calling to ToAscii() or ToUnicode(), clears + * the accented state such that the next WM_CHAR loses the accent. + * This makes is critical that the accent key is detected. Also, + * these functions clear the character that is entered using the + * special Windows keypad sequence when NumLock is down (ie. typing + * ALT+0231 should gives 'c' with a cedilla when NumLock is down). + */ + if (display.asciiKey (display.lastKey) != 0) return null; + display.lastAscii = display.numpadKey (display.lastKey); + } + } else { + /* + * Convert LastKey to lower case because Windows non-virtual + * keys that are also ASCII keys, such as like VK_A, are have + * upper case values in WM_KEYDOWN despite the fact that the + * Shift was not pressed. + */ + display.lastKey = (int)/*64*/OS.CharLower ((short) mapKey); + + /* + * Feature in Windows. The virtual key VK_CANCEL is treated + * as both a virtual key and ASCII key by Windows. This + * means that a WM_CHAR with WPARAM=3 will be issued for + * this key. In order to distinguish between this key and + * Ctrl+C, mark the key as virtual. + */ + if (wParam == OS.VK_CANCEL) display.lastVirtual = true; + + /* + * Some key combinations map to Windows ASCII keys depending + * on the keyboard. For example, Ctrl+Alt+Q maps to @ on a + * German keyboard. If the current key combination is special, + * the correct character is placed in wParam for processing in + * WM_CHAR. If this is the case, issue the key down event from + * inside WM_CHAR. + */ + int asciiKey = display.asciiKey ((int)/*64*/wParam); + if (asciiKey != 0) { + /* + * When the user types Ctrl+Space, ToAscii () maps this to + * Space. Normally, ToAscii () maps a key to a different + * key if both a WM_KEYDOWN and a WM_CHAR will be issued. + * To avoid the extra SWT.KeyDown, look for a space and + * issue the event from WM_CHAR. + */ + if (asciiKey == ' ') return null; + if (asciiKey != (int)/*64*/wParam) return null; + /* + * Feature in Windows. The virtual key VK_CANCEL is treated + * as both a virtual key and ASCII key by Windows. This + * means that a WM_CHAR with WPARAM=3 will be issued for + * this key. To avoid the extra SWT.KeyDown, look for + * VK_CANCEL and issue the event from WM_CHAR. + */ + if (wParam == OS.VK_CANCEL) return null; + } + + /* + * If the control key is not down at this point, then + * the key that was pressed was an accent key or a regular + * key such as 'A' or Shift+A. In that case, issue the + * key event from WM_CHAR. + */ + if (OS.GetKeyState (OS.VK_CONTROL) >= 0) return null; + + /* + * Get the shifted state or convert to lower case if necessary. + * If the user types Ctrl+A, LastAscii should be 'a', not 'A'. + * If the user types Ctrl+Shift+A, LastAscii should be 'A'. + * If the user types Ctrl+Shift+6, the value of LastAscii will + * depend on the international keyboard. + */ + if (OS.GetKeyState (OS.VK_SHIFT) < 0) { + display.lastAscii = display.shiftedKey ((int)/*64*/wParam); + if (display.lastAscii == 0) display.lastAscii = mapKey; + } else { + display.lastAscii = (int)/*64*/OS.CharLower ((short) mapKey); + } + + /* Note that Ctrl+'@' is ASCII NUL and is delivered in WM_CHAR */ + if (display.lastAscii == '@') return null; + display.lastAscii = display.controlKey (display.lastAscii); + } + if (!sendKeyEvent (SWT.KeyDown, OS.WM_KEYDOWN, wParam, lParam)) { + return LRESULT.ONE; + } + // widget could be disposed at this point + return null; +} + +LRESULT wmKeyUp (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + Display display = this.display; + + /* Check for hardware keys */ + if (OS.IsWinCE) { + if (OS.VK_APP1 <= wParam && wParam <= OS.VK_APP6) { + display.lastKey = display.lastAscii = 0; + display.lastVirtual = display.lastNull = display.lastDead = false; + Event event = new Event (); + event.detail = (int)/*64*/wParam - OS.VK_APP1 + 1; + /* Check the bit 30 to get the key state */ + int type = (lParam & 0x40000000) != 0 ? SWT.HardKeyUp : SWT.HardKeyDown; + if (setInputState (event, type)) sendEvent (type, event); + // widget could be disposed at this point + return null; + } + } + + /* + * If the key up is not hooked, reset last key + * and last ascii in case the key down is hooked. + */ + if (!hooks (SWT.KeyUp) && !display.filters (SWT.KeyUp)) { + display.lastKey = display.lastAscii = 0; + display.lastVirtual = display.lastNull = display.lastDead = false; + return null; + } + + /* Map the virtual key. */ + /* + * Bug in WinCE. MapVirtualKey() returns incorrect values. + * The fix is to rely on a key mappings table to determine + * whether the key event must be sent now or if a WM_CHAR + * event will follow. The key mappings table maps virtual + * keys to SWT key codes and does not contain mappings for + * Windows virtual keys like VK_A. Virtual keys that are + * both virtual and ASCII are a special case. + */ + int mapKey = 0; + if (OS.IsWinCE) { + switch ((int)/*64*/wParam) { + case OS.VK_BACK: mapKey = SWT.BS; break; + case OS.VK_RETURN: mapKey = SWT.CR; break; + case OS.VK_DELETE: mapKey = SWT.DEL; break; + case OS.VK_ESCAPE: mapKey = SWT.ESC; break; + case OS.VK_TAB: mapKey = SWT.TAB; break; + } + } else { + mapKey = OS.MapVirtualKey ((int)/*64*/wParam, 2); + } + + /* + * Bug in Windows 95 and NT. When the user types an accent key such + * as ^ to get an accented character on a German keyboard, the accent + * key should be ignored and the next key that the user types is the + * accented key. The fix is to detect the accent key stroke (called + * a dead key) by testing the high bit of the value returned by + * MapVirtualKey (). A further problem is that the high bit on + * Windows NT is bit 32 while the high bit on Windows 95 is bit 16. + * They should both be bit 32. + */ + if (OS.IsWinNT) { + if ((mapKey & 0x80000000) != 0) return null; + } else { + if ((mapKey & 0x8000) != 0) return null; + } + if (display.lastDead) return null; + + /* + * NOTE: On Windows 98, keypad keys are virtual despite the + * fact that a WM_CHAR is issued. On Windows 2000 and XP, + * they are not virtual. Therefore it is necessary to force + * numeric keypad keys to be virtual. + */ + display.lastVirtual = mapKey == 0 || display.numpadKey ((int)/*64*/wParam) != 0; + if (display.lastVirtual) { + display.lastKey = (int)/*64*/wParam; + } else { + /* + * Feature in Windows. The virtual key VK_CANCEL is treated + * as both a virtual key and ASCII key by Windows. This + * means that a WM_CHAR with WPARAM=3 will be issued for + * this key. In order to distinguish between this key and + * Ctrl+C, mark the key as virtual. + */ + if (wParam == OS.VK_CANCEL) display.lastVirtual = true; + if (display.lastKey == 0) { + display.lastAscii = 0; + display.lastNull = display.lastDead = false; + return null; + } + } + LRESULT result = null; + if (!sendKeyEvent (SWT.KeyUp, OS.WM_KEYUP, wParam, lParam)) { + result = LRESULT.ONE; + } + // widget could be disposed at this point + display.lastKey = display.lastAscii = 0; + display.lastVirtual = display.lastNull = display.lastDead = false; + return result; +} + +LRESULT wmKillFocus (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + display.scrollRemainder = 0; + int /*long*/ code = callWindowProc (hwnd, OS.WM_KILLFOCUS, wParam, lParam); + sendFocusEvent (SWT.FocusOut); + // widget could be disposed at this point + + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the focus + * or deactivate events. If this happens, end the + * processing of the Windows message by returning + * zero as the result of the window proc. + */ + if (isDisposed ()) return LRESULT.ZERO; + if (code == 0) return LRESULT.ZERO; + return new LRESULT (code); +} + +LRESULT wmLButtonDblClk (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. Windows sends the following + * messages when the user double clicks the mouse: + * + * WM_LBUTTONDOWN - mouse down + * WM_LBUTTONUP - mouse up + * WM_LBUTTONDBLCLK - double click + * WM_LBUTTONUP - mouse up + * + * Applications that expect matching mouse down/up + * pairs will not see the second mouse down. The + * fix is to send a mouse down event. + */ + LRESULT result = null; + Display display = this.display; + display.captureChanged = false; + sendMouseEvent (SWT.MouseDown, 1, hwnd, OS.WM_LBUTTONDOWN, wParam, lParam); + if (sendMouseEvent (SWT.MouseDoubleClick, 1, hwnd, OS.WM_LBUTTONDBLCLK, wParam, lParam)) { + result = new LRESULT (callWindowProc (hwnd, OS.WM_LBUTTONDBLCLK, wParam, lParam)); + } else { + result = LRESULT.ZERO; + } + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != hwnd) OS.SetCapture (hwnd); + } + return result; +} + +LRESULT wmLButtonDown (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + Display display = this.display; + LRESULT result = null; + int x = OS.GET_X_LPARAM (lParam); + int y = OS.GET_Y_LPARAM (lParam); + boolean [] consume = null, detect = null; + boolean dragging = false, mouseDown = true; + int count = display.getClickCount (SWT.MouseDown, 1, hwnd, lParam); + if (count == 1 && (state & DRAG_DETECT) != 0 && hooks (SWT.DragDetect)) { + if (!OS.IsWinCE) { + /* + * Feature in Windows. It's possible that the drag + * operation will not be started while the mouse is + * down, meaning that the mouse should be captured. + * This can happen when the user types the ESC key + * to cancel the drag. The fix is to query the state + * of the mouse and capture the mouse accordingly. + */ + detect = new boolean [1]; + consume = new boolean [1]; + dragging = dragDetect (hwnd, x, y, true, detect, consume); + if (isDisposed ()) return LRESULT.ZERO; + mouseDown = OS.GetKeyState (OS.VK_LBUTTON) < 0; + } + } + display.captureChanged = false; + boolean dispatch = sendMouseEvent (SWT.MouseDown, 1, count, 0, false, hwnd, OS.WM_LBUTTONDOWN, wParam, lParam); + if (dispatch && (consume == null || !consume [0])) { + result = new LRESULT (callWindowProc (hwnd, OS.WM_LBUTTONDOWN, wParam, lParam)); + } else { + result = LRESULT.ZERO; + } + if (OS.IsPPC) { + /* + * Note: On WinCE PPC, only attempt to recognize the gesture for + * a context menu when the control contains a valid menu or there + * are listeners for the MenuDetect event. + */ + Menu menu = getMenu (); + boolean hasMenu = menu != null && !menu.isDisposed (); + if (hasMenu || hooks (SWT.MenuDetect)) { + SHRGINFO shrg = new SHRGINFO (); + shrg.cbSize = SHRGINFO.sizeof; + shrg.hwndClient = hwnd; + shrg.ptDown_x = x; + shrg.ptDown_y = y; + shrg.dwFlags = OS.SHRG_RETURNCMD; + int type = OS.SHRecognizeGesture (shrg); + if (type == OS.GN_CONTEXTMENU) showMenu (x, y); + } + } + if (mouseDown) { + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != hwnd) OS.SetCapture (hwnd); + } + } + if (dragging) { + sendDragEvent (1, x, y); + } else { + if (detect != null && detect [0]) { + /* + * Feature in Windows. DragDetect() captures the mouse + * and tracks its movement until the user releases the + * left mouse button, presses the ESC key, or moves the + * mouse outside the drag rectangle. If the user moves + * the mouse outside of the drag rectangle, DragDetect() + * returns true and a drag and drop operation can be + * started. When the left mouse button is released or + * the ESC key is pressed, these events are consumed by + * DragDetect() so that application code that matches + * mouse down/up pairs or looks for the ESC key will not + * function properly. The fix is to send the missing + * events when the drag has not started. + * + * NOTE: For now, don't send a fake WM_KEYDOWN/WM_KEYUP + * events for the ESC key. This would require computing + * wParam (the key) and lParam (the repeat count, scan code, + * extended-key flag, context code, previous key-state flag, + * and transition-state flag) which is non-trivial. + */ + if (OS.GetKeyState (OS.VK_ESCAPE) >= 0) { + OS.SendMessage (hwnd, OS.WM_LBUTTONUP, wParam, lParam); + } + } + } + return result; +} + +LRESULT wmLButtonUp (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + Display display = this.display; + LRESULT result = null; + if (sendMouseEvent (SWT.MouseUp, 1, hwnd, OS.WM_LBUTTONUP, wParam, lParam)) { + result = new LRESULT (callWindowProc (hwnd, OS.WM_LBUTTONUP, wParam, lParam)); + } else { + result = LRESULT.ZERO; + } + /* + * Bug in Windows. On some machines that do not have XBUTTONs, + * the MK_XBUTTON1 and OS.MK_XBUTTON2 bits are sometimes set, + * causing mouse capture to become stuck. The fix is to test + * for the extra buttons only when they exist. + */ + int mask = OS.MK_LBUTTON | OS.MK_MBUTTON | OS.MK_RBUTTON; + if (display.xMouse) mask |= OS.MK_XBUTTON1 | OS.MK_XBUTTON2; + if ((wParam & mask) == 0) { + if (OS.GetCapture () == hwnd) OS.ReleaseCapture (); + } + return result; +} + +LRESULT wmMButtonDblClk (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. Windows sends the following + * messages when the user double clicks the mouse: + * + * WM_MBUTTONDOWN - mouse down + * WM_MBUTTONUP - mouse up + * WM_MLBUTTONDBLCLK - double click + * WM_MBUTTONUP - mouse up + * + * Applications that expect matching mouse down/up + * pairs will not see the second mouse down. The + * fix is to send a mouse down event. + */ + LRESULT result = null; + Display display = this.display; + display.captureChanged = false; + sendMouseEvent (SWT.MouseDown, 2, hwnd, OS.WM_MBUTTONDOWN, wParam, lParam); + if (sendMouseEvent (SWT.MouseDoubleClick, 2, hwnd, OS.WM_MBUTTONDBLCLK, wParam, lParam)) { + result = new LRESULT (callWindowProc (hwnd, OS.WM_MBUTTONDBLCLK, wParam, lParam)); + } else { + result = LRESULT.ZERO; + } + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != hwnd) OS.SetCapture (hwnd); + } + return result; +} + +LRESULT wmMButtonDown (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = null; + Display display = this.display; + display.captureChanged = false; + if (sendMouseEvent (SWT.MouseDown, 2, hwnd, OS.WM_MBUTTONDOWN, wParam, lParam)) { + result = new LRESULT (callWindowProc (hwnd, OS.WM_MBUTTONDOWN, wParam, lParam)); + } else { + result = LRESULT.ZERO; + } + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != hwnd) OS.SetCapture (hwnd); + } + return result; +} + +LRESULT wmMButtonUp (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + Display display = this.display; + LRESULT result = null; + if (sendMouseEvent (SWT.MouseUp, 2, hwnd, OS.WM_MBUTTONUP, wParam, lParam)) { + result = new LRESULT (callWindowProc (hwnd, OS.WM_MBUTTONUP, wParam, lParam)); + } else { + result = LRESULT.ZERO; + } + /* + * Bug in Windows. On some machines that do not have XBUTTONs, + * the MK_XBUTTON1 and OS.MK_XBUTTON2 bits are sometimes set, + * causing mouse capture to become stuck. The fix is to test + * for the extra buttons only when they exist. + */ + int mask = OS.MK_LBUTTON | OS.MK_MBUTTON | OS.MK_RBUTTON; + if (display.xMouse) mask |= OS.MK_XBUTTON1 | OS.MK_XBUTTON2; + if ((wParam & mask) == 0) { + if (OS.GetCapture () == hwnd) OS.ReleaseCapture (); + } + return result; +} + +LRESULT wmMouseHover (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + if (!sendMouseEvent (SWT.MouseHover, 0, hwnd, OS.WM_MOUSEHOVER, wParam, lParam)) { + return LRESULT.ZERO; + } + return null; +} + +LRESULT wmMouseLeave (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + if (!hooks (SWT.MouseExit) && !filters (SWT.MouseExit)) return null; + int pos = OS.GetMessagePos (); + POINT pt = new POINT (); + OS.POINTSTOPOINT (pt, pos); + OS.ScreenToClient (hwnd, pt); + lParam = OS.MAKELPARAM (pt.x, pt.y); + if (!sendMouseEvent (SWT.MouseExit, 0, hwnd, OS.WM_MOUSELEAVE, wParam, lParam)) { + return LRESULT.ZERO; + } + return null; +} + +LRESULT wmMouseMove (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = null; + Display display = this.display; + int pos = OS.GetMessagePos (); + if (pos != display.lastMouse || display.captureChanged) { + if (!OS.IsWinCE) { + boolean trackMouse = (state & TRACK_MOUSE) != 0; + boolean mouseEnter = hooks (SWT.MouseEnter) || display.filters (SWT.MouseEnter); + boolean mouseExit = hooks (SWT.MouseExit) || display.filters (SWT.MouseExit); + boolean mouseHover = hooks (SWT.MouseHover) || display.filters (SWT.MouseHover); + if (trackMouse || mouseEnter || mouseExit || mouseHover) { + TRACKMOUSEEVENT lpEventTrack = new TRACKMOUSEEVENT (); + lpEventTrack.cbSize = TRACKMOUSEEVENT.sizeof; + lpEventTrack.dwFlags = OS.TME_QUERY; + lpEventTrack.hwndTrack = hwnd; + OS.TrackMouseEvent (lpEventTrack); + if (lpEventTrack.dwFlags == 0) { + lpEventTrack.dwFlags = OS.TME_LEAVE | OS.TME_HOVER; + lpEventTrack.hwndTrack = hwnd; + OS.TrackMouseEvent (lpEventTrack); + if (mouseEnter) { + /* + * Force all outstanding WM_MOUSELEAVE messages to be dispatched before + * issuing a mouse enter. This causes mouse exit events to be processed + * before mouse enter events. Note that WM_MOUSELEAVE is posted to the + * event queue by TrackMouseEvent(). + */ + MSG msg = new MSG (); + int flags = OS.PM_REMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE; + while (OS.PeekMessage (msg, 0, OS.WM_MOUSELEAVE, OS.WM_MOUSELEAVE, flags)) { + OS.TranslateMessage (msg); + OS.DispatchMessage (msg); + } + sendMouseEvent (SWT.MouseEnter, 0, hwnd, OS.WM_MOUSEMOVE, wParam, lParam); + } + } else { + lpEventTrack.dwFlags = OS.TME_HOVER; + OS.TrackMouseEvent (lpEventTrack); + } + } + } + if (pos != display.lastMouse) { + display.lastMouse = pos; + if (!sendMouseEvent (SWT.MouseMove, 0, hwnd, OS.WM_MOUSEMOVE, wParam, lParam)) { + result = LRESULT.ZERO; + } + } + } + display.captureChanged = false; + return result; +} + +LRESULT wmMouseWheel (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + int delta = OS.GET_WHEEL_DELTA_WPARAM (wParam); + int [] linesToScroll = new int [1]; + int detail; + OS.SystemParametersInfo (OS.SPI_GETWHEELSCROLLLINES, 0, linesToScroll, 0); + if (linesToScroll [0] == OS.WHEEL_PAGESCROLL) { + detail = SWT.SCROLL_PAGE; + } else { + detail = SWT.SCROLL_LINE; + delta *= linesToScroll [0]; + } + /* Check if the delta and the remainder have the same direction (sign) */ + if ((delta ^ display.scrollRemainder) >= 0) delta += display.scrollRemainder; + display.scrollRemainder = delta % OS.WHEEL_DELTA; + + if (!hooks (SWT.MouseWheel) && !filters (SWT.MouseWheel)) return null; + int count = delta / OS.WHEEL_DELTA; + POINT pt = new POINT (); + OS.POINTSTOPOINT (pt, lParam); + OS.ScreenToClient (hwnd, pt); + lParam = OS.MAKELPARAM (pt.x, pt.y); + if (!sendMouseEvent (SWT.MouseWheel, 0, count, detail, true, hwnd, OS.WM_MOUSEWHEEL, wParam, lParam)) { + return LRESULT.ZERO; + } + return null; +} + +LRESULT wmNCPaint (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + return null; +} + +LRESULT wmPaint (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + + /* Exit early - don't draw the background */ + if (!hooks (SWT.Paint) && !filters (SWT.Paint)) { + return null; + } + + /* Issue a paint event */ + int /*long*/ result = 0; + if (OS.IsWinCE) { + RECT rect = new RECT (); + OS.GetUpdateRect (hwnd, rect, false); + result = callWindowProc (hwnd, OS.WM_PAINT, wParam, lParam); + /* + * Bug in Windows. When InvalidateRgn(), InvalidateRect() + * or RedrawWindow() with RDW_INVALIDATE is called from + * within WM_PAINT to invalidate a region for a further + * BeginPaint(), the caret is not properly erased causing + * pixel corruption. The fix is to hide and show the + * caret. + */ + OS.HideCaret (hwnd); + OS.InvalidateRect (hwnd, rect, false); + OS.ShowCaret (hwnd); + PAINTSTRUCT ps = new PAINTSTRUCT (); + GCData data = new GCData (); + data.ps = ps; + data.hwnd = hwnd; + GC gc = new_GC (data); + if (gc != null) { + int width = ps.right - ps.left; + int height = ps.bottom - ps.top; + if (width != 0 && height != 0) { + Event event = new Event (); + event.gc = gc; + event.x = ps.left; + event.y = ps.top; + event.width = width; + event.height = height; + sendEvent (SWT.Paint, event); + // widget could be disposed at this point + event.gc = null; + } + gc.dispose (); + } + } else { + int /*long*/ rgn = OS.CreateRectRgn (0, 0, 0, 0); + OS.GetUpdateRgn (hwnd, rgn, false); + result = callWindowProc (hwnd, OS.WM_PAINT, wParam, lParam); + GCData data = new GCData (); + data.hwnd = hwnd; + GC gc = new_GC (data); + if (gc != null) { + OS.HideCaret (hwnd); + RECT rect = new RECT(); + OS.GetRgnBox (rgn, rect); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + if (width != 0 && height != 0) { + int /*long*/ hDC = gc.handle; + OS.SelectClipRgn (hDC, rgn); + OS.SetMetaRgn (hDC); + Event event = new Event (); + event.gc = gc; + event.x = rect.left; + event.y = rect.top; + event.width = width; + event.height = height; + sendEvent (SWT.Paint, event); + // widget could be disposed at this point + event.gc = null; + } + gc.dispose (); + OS.ShowCaret (hwnd); + } + OS.DeleteObject (rgn); + } + if (result == 0) return LRESULT.ZERO; + return new LRESULT (result); +} + +LRESULT wmPrint (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + /* + * Bug in Windows. When WM_PRINT is used to print the contents + * of a control that has WS_EX_CLIENTEDGE, the old 3D border is + * drawn instead of the theme border. The fix is to call the + * default window proc and then draw the theme border on top. + */ + if ((lParam & OS.PRF_NONCLIENT) != 0) { + if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { + int bits = OS.GetWindowLong (hwnd, OS.GWL_EXSTYLE); + if ((bits & OS.WS_EX_CLIENTEDGE) != 0) { + int /*long*/ code = callWindowProc (hwnd, OS.WM_PRINT, wParam, lParam); + RECT rect = new RECT (); + OS.GetWindowRect (hwnd, rect); + rect.right -= rect.left; + rect.bottom -= rect.top; + rect.left = rect.top = 0; + int border = OS.GetSystemMetrics (OS.SM_CXEDGE); + OS.ExcludeClipRect (wParam, border, border, rect.right - border, rect.bottom - border); + OS.DrawThemeBackground (display.hEditTheme (), wParam, OS.EP_EDITTEXT, OS.ETS_NORMAL, rect, null); + return new LRESULT (code); + } + } + } + return null; +} + +LRESULT wmRButtonDblClk (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. Windows sends the following + * messages when the user double clicks the mouse: + * + * WM_RBUTTONDOWN - mouse down + * WM_RBUTTONUP - mouse up + * WM_RBUTTONDBLCLK - double click + * WM_LBUTTONUP - mouse up + * + * Applications that expect matching mouse down/up + * pairs will not see the second mouse down. The + * fix is to send a mouse down event. + */ + LRESULT result = null; + Display display = this.display; + display.captureChanged = false; + sendMouseEvent (SWT.MouseDown, 3, hwnd, OS.WM_RBUTTONDOWN, wParam, lParam); + if (sendMouseEvent (SWT.MouseDoubleClick, 3, hwnd, OS.WM_RBUTTONDBLCLK, wParam, lParam)) { + result = new LRESULT (callWindowProc (hwnd, OS.WM_RBUTTONDBLCLK, wParam, lParam)); + } else { + result = LRESULT.ZERO; + } + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != hwnd) OS.SetCapture (hwnd); + } + return result; +} + +LRESULT wmRButtonDown (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = null; + Display display = this.display; + display.captureChanged = false; + if (sendMouseEvent (SWT.MouseDown, 3, hwnd, OS.WM_RBUTTONDOWN, wParam, lParam)) { + result = new LRESULT (callWindowProc (hwnd, OS.WM_RBUTTONDOWN, wParam, lParam)); + } else { + result = LRESULT.ZERO; + } + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != hwnd) OS.SetCapture (hwnd); + } + return result; +} + +LRESULT wmRButtonUp (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + Display display = this.display; + LRESULT result = null; + if (sendMouseEvent (SWT.MouseUp, 3, hwnd, OS.WM_RBUTTONUP, wParam, lParam)) { + result = new LRESULT (callWindowProc (hwnd, OS.WM_RBUTTONUP, wParam, lParam)); + } else { + /* Call the DefWindowProc() to support WM_CONTEXTMENU */ + OS.DefWindowProc (hwnd, OS.WM_RBUTTONUP, wParam, lParam); + result = LRESULT.ZERO; + } + /* + * Bug in Windows. On some machines that do not have XBUTTONs, + * the MK_XBUTTON1 and OS.MK_XBUTTON2 bits are sometimes set, + * causing mouse capture to become stuck. The fix is to test + * for the extra buttons only when they exist. + */ + int mask = OS.MK_LBUTTON | OS.MK_MBUTTON | OS.MK_RBUTTON; + if (display.xMouse) mask |= OS.MK_XBUTTON1 | OS.MK_XBUTTON2; + if ((wParam & mask) == 0) { + if (OS.GetCapture () == hwnd) OS.ReleaseCapture (); + } + return result; +} + +LRESULT wmSetFocus (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + int /*long*/ code = callWindowProc (hwnd, OS.WM_SETFOCUS, wParam, lParam); + sendFocusEvent (SWT.FocusIn); + // widget could be disposed at this point + + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the focus + * or activate events. If this happens, end the + * processing of the Windows message by returning + * zero as the result of the window proc. + */ + if (isDisposed ()) return LRESULT.ZERO; + if (code == 0) return LRESULT.ZERO; + return new LRESULT (code); +} + +LRESULT wmSysChar (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + Display display = this.display; + display.lastAscii = (int)/*64*/wParam; + display.lastNull = wParam == 0; + + /* Do not issue a key down if a menu bar mnemonic was invoked */ + if (!hooks (SWT.KeyDown) && !display.filters (SWT.KeyDown)) { + return null; + } + + /* Call the window proc to determine whether it is a system key or mnemonic */ + boolean oldKeyHit = display.mnemonicKeyHit; + display.mnemonicKeyHit = true; + int /*long*/ result = callWindowProc (hwnd, OS.WM_SYSCHAR, wParam, lParam); + boolean consumed = false; + if (!display.mnemonicKeyHit) { + consumed = !sendKeyEvent (SWT.KeyDown, OS.WM_SYSCHAR, wParam, lParam); + // widget could be disposed at this point + } + consumed |= display.mnemonicKeyHit; + display.mnemonicKeyHit = oldKeyHit; + return consumed ? LRESULT.ONE : new LRESULT (result); +} + +LRESULT wmSysKeyDown (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. When WM_SYSKEYDOWN is sent, + * the user pressed ALT+<key> or F10 to get to the + * menu bar. In order to issue events for F10 but + * ignore other key presses when the ALT is not down, + * make sure that either F10 was pressed or that ALT + * is pressed. + */ + if (wParam != OS.VK_F10) { + /* Make sure WM_SYSKEYDOWN was sent by ALT-<aKey>. */ + if ((lParam & 0x20000000) == 0) return null; + } + + /* Ignore well known system keys */ + switch ((int)/*64*/wParam) { + case OS.VK_F4: { + int /*long*/ hwndShell = hwnd; + while (OS.GetParent (hwndShell) != 0) { + if (OS.GetWindow (hwndShell, OS.GW_OWNER) != 0) break; + hwndShell = OS.GetParent (hwndShell); + } + int bits = OS.GetWindowLong (hwndShell, OS.GWL_STYLE); + if ((bits & OS.WS_SYSMENU) != 0) return null; + } + } + + /* Ignore repeating modifier keys by testing key down state */ + switch ((int)/*64*/wParam) { + case OS.VK_SHIFT: + case OS.VK_MENU: + case OS.VK_CONTROL: + case OS.VK_CAPITAL: + case OS.VK_NUMLOCK: + case OS.VK_SCROLL: + if ((lParam & 0x40000000) != 0) return null; + } + + /* Clear last key and last ascii because a new key has been typed */ + display.lastAscii = display.lastKey = 0; + display.lastVirtual = display.lastNull = display.lastDead = false; + + /* If are going to get a WM_SYSCHAR, ignore this message. */ + /* + * Bug in WinCE. MapVirtualKey() returns incorrect values. + * The fix is to rely on a key mappings table to determine + * whether the key event must be sent now or if a WM_CHAR + * event will follow. The key mappings table maps virtual + * keys to SWT key codes and does not contain mappings for + * Windows virtual keys like VK_A. Virtual keys that are + * both virtual and ASCII are a special case. + */ + int mapKey = 0; + if (OS.IsWinCE) { + switch ((int)/*64*/wParam) { + case OS.VK_BACK: mapKey = SWT.BS; break; + case OS.VK_RETURN: mapKey = SWT.CR; break; + case OS.VK_DELETE: mapKey = SWT.DEL; break; + case OS.VK_ESCAPE: mapKey = SWT.ESC; break; + case OS.VK_TAB: mapKey = SWT.TAB; break; + } + } else { + mapKey = OS.MapVirtualKey ((int)/*64*/wParam, 2); + } + display.lastVirtual = mapKey == 0 || display.numpadKey ((int)/*64*/wParam) != 0; + if (display.lastVirtual) { + display.lastKey = (int)/*64*/wParam; + /* + * Feature in Windows. The virtual key VK_DELETE is not + * treated as both a virtual key and an ASCII key by Windows. + * Therefore, we will not receive a WM_SYSCHAR for this key. + * The fix is to treat VK_DELETE as a special case and map + * the ASCII value explicitly (Delete is 0x7F). + */ + if (display.lastKey == OS.VK_DELETE) display.lastAscii = 0x7F; + + /* When a keypad key is typed, a WM_SYSCHAR is not issued */ + if (OS.VK_NUMPAD0 <= display.lastKey && display.lastKey <= OS.VK_DIVIDE) { + /* + * A WM_SYSCHAR will be issued for '*', '+', '-', '.' and '/' + * on the numeric keypad. Avoid issuing the key event twice + * by checking for these keys. Note that calling to ToAscii() + * or ToUnicode(), clear the character that is entered using + * the special Windows keypad sequence when NumLock is down + * (ie. typing ALT+0231 should gives 'c' with a cedilla when + * NumLock is down). Do not call either of these from here. + */ + switch (display.lastKey) { + case OS.VK_MULTIPLY: + case OS.VK_ADD: + case OS.VK_SUBTRACT: + case OS.VK_DECIMAL: + case OS.VK_DIVIDE: return null; + } + display.lastAscii = display.numpadKey (display.lastKey); + } + } else { + /* + * Convert LastKey to lower case because Windows non-virtual + * keys that are also ASCII keys, such as like VK_A, are have + * upper case values in WM_SYSKEYDOWN despite the fact that the + * Shift was not pressed. + */ + display.lastKey = (int)/*64*/OS.CharLower ((short) mapKey); + + /* + * Feature in Windows 98. MapVirtualKey() indicates that + * a WM_SYSCHAR message will occur for Alt+Enter but + * this message never happens. The fix is to issue the + * event from WM_SYSKEYDOWN and map VK_RETURN to '\r'. + */ + if (OS.IsWinNT) return null; + if (wParam != OS.VK_RETURN) return null; + display.lastAscii = '\r'; + } + + if (!sendKeyEvent (SWT.KeyDown, OS.WM_SYSKEYDOWN, wParam, lParam)) { + return LRESULT.ONE; + } + // widget could be disposed at this point + return null; +} + +LRESULT wmSysKeyUp (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + return wmKeyUp (hwnd, wParam, lParam); +} + +LRESULT wmXButtonDblClk (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + /* + * Feature in Windows. Windows sends the following + * messages when the user double clicks the mouse: + * + * WM_XBUTTONDOWN - mouse down + * WM_XBUTTONUP - mouse up + * WM_XLBUTTONDBLCLK - double click + * WM_XBUTTONUP - mouse up + * + * Applications that expect matching mouse down/up + * pairs will not see the second mouse down. The + * fix is to send a mouse down event. + */ + LRESULT result = null; + Display display = this.display; + display.captureChanged = false; + int button = OS.HIWORD (wParam) == OS.XBUTTON1 ? 4 : 5; + sendMouseEvent (SWT.MouseDown, button, hwnd, OS.WM_XBUTTONDOWN, wParam, lParam); + if (sendMouseEvent (SWT.MouseDoubleClick, button, hwnd, OS.WM_XBUTTONDBLCLK, wParam, lParam)) { + result = new LRESULT (callWindowProc (hwnd, OS.WM_XBUTTONDBLCLK, wParam, lParam)); + } else { + result = LRESULT.ZERO; + } + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != hwnd) OS.SetCapture (hwnd); + } + return result; +} + +LRESULT wmXButtonDown (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + LRESULT result = null; + Display display = this.display; + display.captureChanged = false; + display.xMouse = true; + int button = OS.HIWORD (wParam) == OS.XBUTTON1 ? 4 : 5; + if (sendMouseEvent (SWT.MouseDown, button, hwnd, OS.WM_XBUTTONDOWN, wParam, lParam)) { + result = new LRESULT (callWindowProc (hwnd, OS.WM_XBUTTONDOWN, wParam, lParam)); + } else { + result = LRESULT.ZERO; + } + if (!display.captureChanged && !isDisposed ()) { + if (OS.GetCapture () != hwnd) OS.SetCapture (hwnd); + } + return result; +} + +LRESULT wmXButtonUp (int /*long*/ hwnd, int /*long*/ wParam, int /*long*/ lParam) { + Display display = this.display; + LRESULT result = null; + int button = OS.HIWORD (wParam) == OS.XBUTTON1 ? 4 : 5; + if (sendMouseEvent (SWT.MouseUp, button, hwnd, OS.WM_XBUTTONUP, wParam, lParam)) { + result = new LRESULT (callWindowProc (hwnd, OS.WM_XBUTTONUP, wParam, lParam)); + } else { + result = LRESULT.ZERO; + } + /* + * Bug in Windows. On some machines that do not have XBUTTONs, + * the MK_XBUTTON1 and OS.MK_XBUTTON2 bits are sometimes set, + * causing mouse capture to become stuck. The fix is to test + * for the extra buttons only when they exist. + */ + int mask = OS.MK_LBUTTON | OS.MK_MBUTTON | OS.MK_RBUTTON; + if (display.xMouse) mask |= OS.MK_XBUTTON1 | OS.MK_XBUTTON2; + if ((wParam & mask) == 0) { + if (OS.GetCapture () == hwnd) OS.ReleaseCapture (); + } + return result; +} +} |