Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse')
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Color.java325
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Cursor.java460
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Device.java972
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/DeviceData.java23
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Font.java258
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/FontData.java669
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/FontMetrics.java183
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java4979
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GCData.java64
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java2129
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Path.java602
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Pattern.java250
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Region.java597
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java3328
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Transform.java369
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/BidiUtil.java642
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java459
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Button.java1388
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Canvas.java490
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Caret.java600
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ColorDialog.java281
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Combo.java2489
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Composite.java1760
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java4894
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/CoolBar.java1195
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/CoolItem.java717
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/DateTime.java1048
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Decorations.java1798
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/DirectoryDialog.java310
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java4653
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ExpandBar.java824
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ExpandItem.java489
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/FileDialog.java618
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/FontDialog.java325
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Group.java569
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/IME.java566
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Label.java686
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Link.java1010
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/List.java1716
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Menu.java1566
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/MenuItem.java1174
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/MessageBox.java265
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ProgressBar.java458
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Sash.java421
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Scale.java561
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ScrollBar.java952
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Scrollable.java495
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Shell.java2518
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Slider.java788
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Spinner.java1460
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TabFolder.java1010
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TabItem.java372
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Table.java6966
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TableColumn.java893
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TableItem.java1235
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Text.java2543
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolBar.java1514
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolItem.java1006
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolTip.java555
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tracker.java1188
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TrayItem.java537
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tree.java7809
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TreeColumn.java758
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TreeItem.java1819
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Widget.java2517
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 '&amp;' 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 '&amp;' can be
+ * escaped by doubling it in the string, causing a single
+ * '&amp;' 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 '&amp;') is not displayed in a tool tip.
+ * To display a single '&amp;' in the tool tip, the character '&amp;' 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 '&amp;' 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
+ * '&amp;' can be escaped by doubling it in the string, causing
+ * a single '&amp;' 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 '&amp;' 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
+ * '&amp;' can be escaped by doubling it in the string, causing
+ * a single '&amp;' 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, &lt;A&gt; and &lt;/A&gt;. 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 '&amp;' 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 '&amp;' can be
+ * escaped by doubling it in the string, causing a single
+ * '&amp;' 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 '&amp;' 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 '&amp;' can be
+ * escaped by doubling it in the string, causing a single
+ * '&amp;' 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 '&amp;') is not displayed in a tool tip.
+ * To display a single '&amp;' in the tool tip, the character '&amp;' 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 '&amp;') is not displayed in a tool tip.
+ * To display a single '&amp;' in the tool tip, the character '&amp;' 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 '&amp;' 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 '&amp;' can be
+ * escaped by doubling it in the string, causing a single
+ * '&amp;' 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 '&amp;') is not displayed in a tool tip.
+ * To display a single '&amp;' in the tool tip, the character '&amp;' 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 '&amp;') is not displayed in a tool tip.
+ * To display a single '&amp;' in the tool tip, the character '&amp;' 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 '&amp;') is not displayed in a tool tip.
+ * To display a single '&amp;' in the tool tip, the character '&amp;' 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;
+}
+}

Back to the top