diff options
author | Silenio Quarti <silenio> | 2009-07-01 14:50:54 +0000 |
---|---|---|
committer | Silenio Quarti <silenio> | 2009-07-01 14:50:54 +0000 |
commit | 093c579a4ffd9551acb901bba9617e7aa776989d (patch) | |
tree | 71cf23798b651ef92f188390841a8d130908fb11 /bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics | |
parent | f664d297f7bb009784868bf3fcf0b3e3bb9a646b (diff) | |
download | eclipse.platform.swt-093c579a4ffd9551acb901bba9617e7aa776989d.tar.gz eclipse.platform.swt-093c579a4ffd9551acb901bba9617e7aa776989d.tar.xz eclipse.platform.swt-093c579a4ffd9551acb901bba9617e7aa776989d.zip |
restore HEAD after accidental deletion by error in automated build script
Diffstat (limited to 'bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics')
15 files changed, 15208 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] + "}"; +} + +} |