--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.*;
+import org.eclipse.swt.internal.win32.*;
+
+/**
+ * Instances of this class are controls which are capable
+ * of containing other controls.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>NO_BACKGROUND, NO_FOCUS, NO_MERGE_PAINTS, NO_REDRAW_RESIZE, NO_RADIO_GROUP, EMBEDDED, DOUBLE_BUFFERED</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * Note: The <code>NO_BACKGROUND</code>, <code>NO_FOCUS</code>, <code>NO_MERGE_PAINTS</code>,
+ * and <code>NO_REDRAW_RESIZE</code> styles are intended for use with <code>Canvas</code>.
+ * They can be used with <code>Composite</code> if you are drawing your own, but their
+ * behavior is undefined if they are used with subclasses of <code>Composite</code> other
+ * than <code>Canvas</code>.
+ * </p><p>
+ * Note: The <code>CENTER</code> style, although undefined for composites, has the
+ * same value as <code>EMBEDDED</code> which is used to embed widgets from other
+ * widget toolkits into SWT. On some operating systems (GTK), this may cause
+ * the children of this composite to be obscured.
+ * </p><p>
+ * This class may be subclassed by custom control implementors
+ * who are building controls that are constructed from aggregates
+ * of other controls.
+ * </p>
+ *
+ * @see Canvas
+ * @see <a href="http://www.eclipse.org/swt/snippets/#composite">Composite snippets</a>
+ * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
+ */
+public class Composite extends Scrollable {
+ Layout layout;
+ WINDOWPOS [] lpwp;
+ Control [] tabList;
+ int layoutCount, backgroundMode;
+
+ static final int TOOLTIP_LIMIT = 4096;
+
+/**
+ * Prevents uninitialized instances from being created outside the package.
+ */
+Composite () {
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of widget to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT#NO_BACKGROUND
+ * @see SWT#NO_FOCUS
+ * @see SWT#NO_MERGE_PAINTS
+ * @see SWT#NO_REDRAW_RESIZE
+ * @see SWT#NO_RADIO_GROUP
+ * @see SWT#EMBEDDED
+ * @see SWT#DOUBLE_BUFFERED
+ * @see Widget#getStyle
+ */
+public Composite (Composite parent, int style) {
+ super (parent, style);
+}
+
+Control [] _getChildren () {
+ int count = 0;
+ long hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
+ if (hwndChild == 0) return new Control [0];
+ while (hwndChild != 0) {
+ count++;
+ hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
+ }
+ Control [] children = new Control [count];
+ int index = 0;
+ hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
+ while (hwndChild != 0) {
+ Control control = display.getControl (hwndChild);
+ if (control != null && control != this) {
+ children [index++] = control;
+ }
+ hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
+ }
+ if (count == index) return children;
+ Control [] newChildren = new Control [index];
+ System.arraycopy (children, 0, newChildren, 0, index);
+ return newChildren;
+}
+
+Control [] _getTabList () {
+ if (tabList == null) return tabList;
+ int count = 0;
+ for (int i=0; i<tabList.length; i++) {
+ if (!tabList [i].isDisposed ()) count++;
+ }
+ if (count == tabList.length) return tabList;
+ Control [] newList = new Control [count];
+ int index = 0;
+ for (int i=0; i<tabList.length; i++) {
+ if (!tabList [i].isDisposed ()) {
+ newList [index++] = tabList [i];
+ }
+ }
+ tabList = newList;
+ return tabList;
+}
+
+/**
+ * Clears any data that has been cached by a Layout for all widgets that
+ * are in the parent hierarchy of the changed control up to and including the
+ * receiver. If an ancestor does not have a layout, it is skipped.
+ *
+ * @param changed an array of controls that changed state and require a recalculation of size
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the changed array is null any of its controls are null or have been disposed</li>
+ * <li>ERROR_INVALID_PARENT - if any control in changed is not in the widget tree of the receiver</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @deprecated use {@link Composite#layout(Control[], int)} instead
+ * @since 3.1
+ */
+@Deprecated
+public void changed (Control[] changed) {
+ layout(changed, SWT.DEFER);
+}
+
+@Override
+void checkBuffered () {
+ if ((state & CANVAS) == 0) {
+ super.checkBuffered ();
+ }
+}
+
+@Override
+void checkComposited () {
+ if ((state & CANVAS) != 0) {
+ if ((style & SWT.TRANSPARENT) != 0) {
+ long hwndParent = parent.handle;
+ int bits = OS.GetWindowLong (hwndParent, OS.GWL_EXSTYLE);
+ bits |= OS.WS_EX_COMPOSITED;
+ OS.SetWindowLong (hwndParent, OS.GWL_EXSTYLE, bits);
+ }
+ }
+}
+
+@Override
+protected void checkSubclass () {
+ /* Do nothing - Subclassing is allowed */
+}
+
+@Override
+Widget [] computeTabList () {
+ Widget result [] = super.computeTabList ();
+ if (result.length == 0) return result;
+ Control [] list = tabList != null ? _getTabList () : _getChildren ();
+ for (int i=0; i<list.length; i++) {
+ Control child = list [i];
+ Widget [] childList = child.computeTabList ();
+ if (childList.length != 0) {
+ Widget [] newResult = new Widget [result.length + childList.length];
+ System.arraycopy (result, 0, newResult, 0, result.length);
+ System.arraycopy (childList, 0, newResult, result.length, childList.length);
+ result = newResult;
+ }
+ }
+ return result;
+}
+
+@Override
+Point computeSizeInPixels (int wHint, int hHint, boolean changed) {
+ display.runSkin ();
+ Point size;
+ if (layout != null) {
+ if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) {
+ changed |= (state & LAYOUT_CHANGED) != 0;
+ state &= ~LAYOUT_CHANGED;
+ size = DPIUtil.autoScaleUp(layout.computeSize (this, DPIUtil.autoScaleDown(wHint), DPIUtil.autoScaleDown(hHint), changed));
+ } else {
+ size = new Point (wHint, hHint);
+ }
+ } else {
+ size = minimumSize (wHint, hHint, changed);
+ if (size.x == 0) size.x = DEFAULT_WIDTH;
+ if (size.y == 0) size.y = DEFAULT_HEIGHT;
+ }
+ if (wHint != SWT.DEFAULT) size.x = wHint;
+ if (hHint != SWT.DEFAULT) size.y = hHint;
+ /*
+ * Since computeTrim can be overridden by subclasses, we cannot
+ * call computeTrimInPixels directly.
+ */
+ Rectangle trim = DPIUtil.autoScaleUp(computeTrim (0, 0, DPIUtil.autoScaleDown(size.x), DPIUtil.autoScaleDown(size.y)));
+ return new Point (trim.width, trim.height);
+}
+
+/**
+ * Copies a rectangular area of the receiver at the specified
+ * position using the gc.
+ *
+ * @param gc the gc where the rectangle is to be filled
+ * @param x the x coordinate of the rectangle to be filled
+ * @param y the y coordinate of the rectangle to be filled
+ * @param width the width of the rectangle to be filled
+ * @param height the height of the rectangle to be filled
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the gc is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the gc has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+/*public*/ void copyArea (GC gc, int x, int y, int width, int height) {
+ checkWidget ();
+ if (gc == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (gc.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+
+ //XP only, no GDI+
+ //#define PW_CLIENTONLY 0x00000001
+ //DCOrg() wrong
+ //topHandle wrong for Tree?
+ long hDC = gc.handle;
+ int nSavedDC = OS.SaveDC (hDC);
+ OS.IntersectClipRect (hDC, 0, 0, width, height);
+
+ //WRONG PARENT
+ POINT lpPoint = new POINT ();
+ long hwndParent = OS.GetParent (handle);
+ OS.MapWindowPoints (handle, hwndParent, lpPoint, 1);
+ RECT rect = new RECT ();
+ OS.GetWindowRect (handle, rect);
+ POINT lpPoint1 = new POINT (), lpPoint2 = new POINT ();
+ x = x + (lpPoint.x - rect.left);
+ y = y + (lpPoint.y - rect.top);
+ OS.SetWindowOrgEx (hDC, x, y, lpPoint1);
+ OS.SetBrushOrgEx (hDC, x, y, lpPoint2);
+ int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+ if ((bits & OS.WS_VISIBLE) == 0) {
+ OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+ }
+ //NECESSARY?
+ OS.RedrawWindow (handle, null, 0, OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN);
+ OS.PrintWindow (handle, hDC, 0);//0x00000001);
+ if ((bits & OS.WS_VISIBLE) == 0) {
+ OS.DefWindowProc(handle, OS.WM_SETREDRAW, 0, 0);
+ }
+ OS.RestoreDC (hDC, nSavedDC);
+}
+
+@Override
+void createHandle () {
+ super.createHandle ();
+ state |= CANVAS;
+ if ((style & (SWT.H_SCROLL | SWT.V_SCROLL)) == 0 || findThemeControl () == parent) {
+ state |= THEME_BACKGROUND;
+ }
+ if ((style & SWT.TRANSPARENT) != 0) {
+ int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
+ bits |= OS.WS_EX_TRANSPARENT;
+ OS.SetWindowLong (handle, OS.GWL_EXSTYLE, bits);
+ }
+}
+
+@Override
+int applyThemeBackground () {
+ /*
+ * Composite with scrollbars would not inherit the theme because it was
+ * probably being used to implement a control similar to a Text, List,
+ * Table, or Tree, and those controls do not inherit the background theme.
+ * We assume that a Composite that did not have scrollbars was probably just
+ * being used to group some other controls, therefore it should inherit.
+ *
+ * But when Composite background is set to COLOR_TRANSPARENT (i.e.
+ * backgroundAlpha as '0') which means parent theme should be inherited, so
+ * enable the THEME_BACKGROUND in 'state' to support background transparent.
+ * Refer bug 463127 & related bug 234649.
+ */
+ return (backgroundAlpha == 0 || (style & (SWT.H_SCROLL | SWT.V_SCROLL)) == 0 || findThemeControl () == parent) ? 1 : 0;
+}
+
+/**
+ * Fills the interior of the rectangle specified by the arguments,
+ * with the receiver's background.
+ *
+ * <p>The <code>offsetX</code> and <code>offsetY</code> are used to map from
+ * the <code>gc</code> origin to the origin of the parent image background. This is useful
+ * to ensure proper alignment of the image background.</p>
+ *
+ * @param gc the gc where the rectangle is to be filled
+ * @param x the x coordinate of the rectangle to be filled
+ * @param y the y coordinate of the rectangle to be filled
+ * @param width the width of the rectangle to be filled
+ * @param height the height of the rectangle to be filled
+ * @param offsetX the image background x offset
+ * @param offsetY the image background y offset
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the gc is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the gc has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.6
+ */
+public void drawBackground (GC gc, int x, int y, int width, int height, int offsetX, int offsetY) {
+ checkWidget ();
+ x = DPIUtil.autoScaleUp(x);
+ y = DPIUtil.autoScaleUp(y);
+ width = DPIUtil.autoScaleUp(width);
+ height = DPIUtil.autoScaleUp(height);
+ offsetX = DPIUtil.autoScaleUp(offsetX);
+ offsetY = DPIUtil.autoScaleUp(offsetY);
+ drawBackgroundInPixels(gc, x, y, width, height, offsetX, offsetY);
+}
+
+void drawBackgroundInPixels(GC gc, int x, int y, int width, int height, int offsetX, int offsetY) {
+ if (gc == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (gc.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+ RECT rect = new RECT ();
+ OS.SetRect (rect, x, y, x + width, y + height);
+ long hDC = gc.handle;
+ int pixel = background == -1 ? gc.getBackground ().handle : -1;
+ drawBackground (hDC, rect, pixel, offsetX, offsetY);
+}
+
+Composite findDeferredControl () {
+ return layoutCount > 0 ? this : parent.findDeferredControl ();
+}
+
+@Override
+Menu [] findMenus (Control control) {
+ if (control == this) return new Menu [0];
+ Menu result [] = super.findMenus (control);
+ Control [] children = _getChildren ();
+ for (int i=0; i<children.length; i++) {
+ Control child = children [i];
+ Menu [] menuList = child.findMenus (control);
+ if (menuList.length != 0) {
+ Menu [] newResult = new Menu [result.length + menuList.length];
+ System.arraycopy (result, 0, newResult, 0, result.length);
+ System.arraycopy (menuList, 0, newResult, result.length, menuList.length);
+ result = newResult;
+ }
+ }
+ return result;
+}
+
+@Override
+void fixChildren (Shell newShell, Shell oldShell, Decorations newDecorations, Decorations oldDecorations, Menu [] menus) {
+ super.fixChildren (newShell, oldShell, newDecorations, oldDecorations, menus);
+ Control [] children = _getChildren ();
+ for (int i=0; i<children.length; i++) {
+ children [i].fixChildren (newShell, oldShell, newDecorations, oldDecorations, menus);
+ }
+}
+
+void fixTabList (Control control) {
+ if (tabList == null) return;
+ int count = 0;
+ for (int i=0; i<tabList.length; i++) {
+ if (tabList [i] == control) count++;
+ }
+ if (count == 0) return;
+ Control [] newList = null;
+ int length = tabList.length - count;
+ if (length != 0) {
+ newList = new Control [length];
+ int index = 0;
+ for (int i=0; i<tabList.length; i++) {
+ if (tabList [i] != control) {
+ newList [index++] = tabList [i];
+ }
+ }
+ }
+ tabList = newList;
+}
+
+/**
+ * Returns the receiver's background drawing mode. This
+ * will be one of the following constants defined in class
+ * <code>SWT</code>:
+ * <code>INHERIT_NONE</code>, <code>INHERIT_DEFAULT</code>,
+ * <code>INHERIT_FORCE</code>.
+ *
+ * @return the background mode
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SWT
+ *
+ * @since 3.2
+ */
+public int getBackgroundMode () {
+ checkWidget ();
+ return backgroundMode;
+}
+
+/**
+ * Returns a (possibly empty) array containing the receiver's children.
+ * Children are returned in the order that they are drawn. The topmost
+ * control appears at the beginning of the array. Subsequent controls
+ * draw beneath this control and appear later in the array.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of children, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return an array of children
+ *
+ * @see Control#moveAbove
+ * @see Control#moveBelow
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Control [] getChildren () {
+ checkWidget ();
+ return _getChildren ();
+}
+
+int getChildrenCount () {
+ /*
+ * NOTE: The current implementation will count
+ * non-registered children.
+ */
+ int count = 0;
+ long hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
+ while (hwndChild != 0) {
+ count++;
+ hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
+ }
+ return count;
+}
+
+/**
+ * Returns layout which is associated with the receiver, or
+ * null if one has not been set.
+ *
+ * @return the receiver's layout or null
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Layout getLayout () {
+ checkWidget ();
+ return layout;
+}
+
+/**
+ * Gets the (possibly empty) tabbing order for the control.
+ *
+ * @return tabList the ordered list of controls representing the tab order
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setTabList
+ */
+public Control [] getTabList () {
+ checkWidget ();
+ Control [] tabList = _getTabList ();
+ if (tabList == null) {
+ int count = 0;
+ Control [] list =_getChildren ();
+ for (int i=0; i<list.length; i++) {
+ if (list [i].isTabGroup ()) count++;
+ }
+ tabList = new Control [count];
+ int index = 0;
+ for (int i=0; i<list.length; i++) {
+ if (list [i].isTabGroup ()) {
+ tabList [index++] = list [i];
+ }
+ }
+ }
+ return tabList;
+}
+
+boolean hooksKeys () {
+ return hooks (SWT.KeyDown) || hooks (SWT.KeyUp);
+}
+
+/**
+ * Returns <code>true</code> if the receiver has deferred
+ * the performing of layout, and <code>false</code> otherwise.
+ *
+ * @return the receiver's deferred layout state
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setLayoutDeferred(boolean)
+ * @see #isLayoutDeferred()
+ *
+ * @since 3.1
+ */
+public boolean getLayoutDeferred () {
+ checkWidget ();
+ return layoutCount > 0 ;
+}
+
+/**
+ * Returns <code>true</code> if the receiver or any ancestor
+ * up to and including the receiver's nearest ancestor shell
+ * has deferred the performing of layouts. Otherwise, <code>false</code>
+ * is returned.
+ *
+ * @return the receiver's deferred layout state
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setLayoutDeferred(boolean)
+ * @see #getLayoutDeferred()
+ *
+ * @since 3.1
+ */
+public boolean isLayoutDeferred () {
+ checkWidget ();
+ return findDeferredControl () != null;
+}
+
+/**
+ * If the receiver has a layout, asks the layout to <em>lay out</em>
+ * (that is, set the size and location of) the receiver's children.
+ * If the receiver does not have a layout, do nothing.
+ * <p>
+ * Use of this method is discouraged since it is the least-efficient
+ * way to trigger a layout. The use of <code>layout(true)</code>
+ * discards all cached layout information, even from controls which
+ * have not changed. It is much more efficient to invoke
+ * {@link Control#requestLayout()} on every control which has changed
+ * in the layout than it is to invoke this method on the layout itself.
+ * </p>
+ * <p>
+ * This is equivalent to calling <code>layout(true)</code>.
+ * </p>
+ * <p>
+ * Note: Layout is different from painting. If a child is
+ * moved or resized such that an area in the parent is
+ * exposed, then the parent will paint. If no child is
+ * affected, the parent will not paint.
+ * </p>
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void layout () {
+ checkWidget ();
+ layout (true);
+}
+
+/**
+ * If the receiver has a layout, asks the layout to <em>lay out</em>
+ * (that is, set the size and location of) the receiver's children.
+ * If the argument is <code>true</code> the layout must not rely
+ * on any information it has cached about the immediate children. If it
+ * is <code>false</code> the layout may (potentially) optimize the
+ * work it is doing by assuming that none of the receiver's
+ * children has changed state since the last layout.
+ * If the receiver does not have a layout, do nothing.
+ * <p>
+ * It is normally more efficient to invoke {@link Control#requestLayout()}
+ * on every control which has changed in the layout than it is to invoke
+ * this method on the layout itself. Clients are encouraged to use
+ * {@link Control#requestLayout()} where possible instead of calling
+ * this method.
+ * </p>
+ * <p>
+ * If a child is resized as a result of a call to layout, the
+ * resize event will invoke the layout of the child. The layout
+ * will cascade down through all child widgets in the receiver's widget
+ * tree until a child is encountered that does not resize. Note that
+ * a layout due to a resize will not flush any cached information
+ * (same as <code>layout(false)</code>).
+ * </p>
+ * <p>
+ * Note: Layout is different from painting. If a child is
+ * moved or resized such that an area in the parent is
+ * exposed, then the parent will paint. If no child is
+ * affected, the parent will not paint.
+ * </p>
+ *
+ * @param changed <code>true</code> if the layout must flush its caches, and <code>false</code> otherwise
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void layout (boolean changed) {
+ checkWidget ();
+ if (layout == null) return;
+ layout (changed, false);
+}
+
+/**
+ * If the receiver has a layout, asks the layout to <em>lay out</em>
+ * (that is, set the size and location of) the receiver's children.
+ * If the changed argument is <code>true</code> the layout must not rely
+ * on any information it has cached about its children. If it
+ * is <code>false</code> the layout may (potentially) optimize the
+ * work it is doing by assuming that none of the receiver's
+ * children has changed state since the last layout.
+ * If the all argument is <code>true</code> the layout will cascade down
+ * through all child widgets in the receiver's widget tree, regardless of
+ * whether the child has changed size. The changed argument is applied to
+ * all layouts. If the all argument is <code>false</code>, the layout will
+ * <em>not</em> cascade down through all child widgets in the receiver's widget
+ * tree. However, if a child is resized as a result of a call to layout, the
+ * resize event will invoke the layout of the child. Note that
+ * a layout due to a resize will not flush any cached information
+ * (same as <code>layout(false)</code>).
+ * <p>
+ * It is normally more efficient to invoke {@link Control#requestLayout()}
+ * on every control which has changed in the layout than it is to invoke
+ * this method on the layout itself. Clients are encouraged to use
+ * {@link Control#requestLayout()} where possible instead of calling
+ * this method.
+ * </p>
+ * <p>
+ * Note: Layout is different from painting. If a child is
+ * moved or resized such that an area in the parent is
+ * exposed, then the parent will paint. If no child is
+ * affected, the parent will not paint.
+ * </p>
+ *
+ * @param changed <code>true</code> if the layout must flush its caches, and <code>false</code> otherwise
+ * @param all <code>true</code> if all children in the receiver's widget tree should be laid out, and <code>false</code> otherwise
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void layout (boolean changed, boolean all) {
+ checkWidget ();
+ if (layout == null && !all) return;
+ markLayout (changed, all);
+ updateLayout (all);
+}
+
+/**
+ * Forces a lay out (that is, sets the size and location) of all widgets that
+ * are in the parent hierarchy of the changed control up to and including the
+ * receiver. The layouts in the hierarchy must not rely on any information
+ * cached about the changed control or any of its ancestors. The layout may
+ * (potentially) optimize the work it is doing by assuming that none of the
+ * peers of the changed control have changed state since the last layout.
+ * If an ancestor does not have a layout, skip it.
+ * <p>
+ * It is normally more efficient to invoke {@link Control#requestLayout()}
+ * on every control which has changed in the layout than it is to invoke
+ * this method on the layout itself. Clients are encouraged to use
+ * {@link Control#requestLayout()} where possible instead of calling
+ * this method.
+ * </p>
+ * <p>
+ * Note: Layout is different from painting. If a child is
+ * moved or resized such that an area in the parent is
+ * exposed, then the parent will paint. If no child is
+ * affected, the parent will not paint.
+ * </p>
+ *
+ * @param changed a control that has had a state change which requires a recalculation of its size
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the changed array is null any of its controls are null or have been disposed</li>
+ * <li>ERROR_INVALID_PARENT - if any control in changed is not in the widget tree of the receiver</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void layout (Control [] changed) {
+ checkWidget ();
+ if (changed == null) error (SWT.ERROR_INVALID_ARGUMENT);
+ layout (changed, SWT.NONE);
+}
+
+/**
+ * Forces a lay out (that is, sets the size and location) of all widgets that
+ * are in the parent hierarchy of the changed control up to and including the
+ * receiver.
+ * <p>
+ * The parameter <code>flags</code> may be a combination of:
+ * </p>
+ * <dl>
+ * <dt><b>SWT.ALL</b></dt>
+ * <dd>all children in the receiver's widget tree should be laid out</dd>
+ * <dt><b>SWT.CHANGED</b></dt>
+ * <dd>the layout must flush its caches</dd>
+ * <dt><b>SWT.DEFER</b></dt>
+ * <dd>layout will be deferred</dd>
+ * </dl>
+ * <p>
+ * When the <code>changed</code> array is specified, the flags <code>SWT.ALL</code>
+ * and <code>SWT.CHANGED</code> have no effect. In this case, the layouts in the
+ * hierarchy must not rely on any information cached about the changed control or
+ * any of its ancestors. The layout may (potentially) optimize the
+ * work it is doing by assuming that none of the peers of the changed
+ * control have changed state since the last layout.
+ * If an ancestor does not have a layout, skip it.
+ * </p>
+ * <p>
+ * When the <code>changed</code> array is not specified, the flag <code>SWT.ALL</code>
+ * indicates that the whole widget tree should be laid out. And the flag
+ * <code>SWT.CHANGED</code> indicates that the layouts should flush any cached
+ * information for all controls that are laid out.
+ * </p>
+ * <p>
+ * The <code>SWT.DEFER</code> flag always causes the layout to be deferred by
+ * calling <code>Composite.setLayoutDeferred(true)</code> and scheduling a call
+ * to <code>Composite.setLayoutDeferred(false)</code>, which will happen when
+ * appropriate (usually before the next event is handled). When this flag is set,
+ * the application should not call <code>Composite.setLayoutDeferred(boolean)</code>.
+ * </p>
+ * <p>
+ * Note: Layout is different from painting. If a child is
+ * moved or resized such that an area in the parent is
+ * exposed, then the parent will paint. If no child is
+ * affected, the parent will not paint.
+ * </p>
+ *
+ * @param changed a control that has had a state change which requires a recalculation of its size
+ * @param flags the flags specifying how the layout should happen
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if any of the controls in changed is null or has been disposed</li>
+ * <li>ERROR_INVALID_PARENT - if any control in changed is not in the widget tree of the receiver</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.6
+ */
+public void layout (Control [] changed, int flags) {
+ checkWidget ();
+ if (changed != null) {
+ for (int i=0; i<changed.length; i++) {
+ Control control = changed [i];
+ if (control == null) error (SWT.ERROR_INVALID_ARGUMENT);
+ if (control.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+ boolean ancestor = false;
+ Composite composite = control.parent;
+ while (composite != null) {
+ ancestor = composite == this;
+ if (ancestor) break;
+ composite = composite.parent;
+ }
+ if (!ancestor) error (SWT.ERROR_INVALID_PARENT);
+ }
+ int updateCount = 0;
+ Composite [] update = new Composite [16];
+ for (int i=0; i<changed.length; i++) {
+ Control child = changed [i];
+ Composite composite = child.parent;
+ // Update layout when the list of children has changed.
+ // See bug 497812.
+ child.markLayout(false, false);
+ while (child != this) {
+ if (composite.layout != null) {
+ composite.state |= LAYOUT_NEEDED;
+ if (!composite.layout.flushCache (child)) {
+ composite.state |= LAYOUT_CHANGED;
+ }
+ }
+ if (updateCount == update.length) {
+ Composite [] newUpdate = new Composite [update.length + 16];
+ System.arraycopy (update, 0, newUpdate, 0, update.length);
+ update = newUpdate;
+ }
+ child = update [updateCount++] = composite;
+ composite = child.parent;
+ }
+ }
+ if (!display.externalEventLoop && (flags & SWT.DEFER) != 0) {
+ setLayoutDeferred (true);
+ display.addLayoutDeferred (this);
+ }
+ for (int i=updateCount-1; i>=0; i--) {
+ update [i].updateLayout (false);
+ }
+ } else {
+ if (layout == null && (flags & SWT.ALL) == 0) return;
+ markLayout ((flags & SWT.CHANGED) != 0, (flags & SWT.ALL) != 0);
+ if (!display.externalEventLoop && (flags & SWT.DEFER) != 0) {
+ setLayoutDeferred (true);
+ display.addLayoutDeferred (this);
+ }
+ updateLayout ((flags & SWT.ALL) != 0);
+ }
+}
+
+@Override
+void markLayout (boolean changed, boolean all) {
+ if (layout != null) {
+ state |= LAYOUT_NEEDED;
+ if (changed) state |= LAYOUT_CHANGED;
+ }
+ if (all) {
+ Control [] children = _getChildren ();
+ for (int i=0; i<children.length; i++) {
+ children [i].markLayout (changed, all);
+ }
+ }
+}
+
+Point minimumSize (int wHint, int hHint, boolean changed) {
+ Control [] children = _getChildren ();
+ /*
+ * Since getClientArea can be overridden by subclasses, we cannot
+ * call getClientAreaInPixels directly.
+ */
+ Rectangle clientArea = DPIUtil.autoScaleUp(getClientArea ());
+ int width = 0, height = 0;
+ for (int i=0; i<children.length; i++) {
+ Rectangle rect = DPIUtil.autoScaleUp(children [i].getBounds ());
+ width = Math.max (width, rect.x - clientArea.x + rect.width);
+ height = Math.max (height, rect.y - clientArea.y + rect.height);
+ }
+ return new Point (width, height);
+}
+
+@Override
+boolean redrawChildren () {
+ if (!super.redrawChildren ()) return false;
+ Control [] children = _getChildren ();
+ for (int i=0; i<children.length; i++) {
+ children [i].redrawChildren ();
+ }
+ return true;
+}
+
+@Override
+void releaseParent () {
+ super.releaseParent ();
+ if ((state & CANVAS) != 0) {
+ if ((style & SWT.TRANSPARENT) != 0) {
+ long hwndParent = parent.handle;
+ long hwndChild = OS.GetWindow (hwndParent, OS.GW_CHILD);
+ while (hwndChild != 0) {
+ if (hwndChild != handle) {
+ int bits = OS.GetWindowLong (hwndParent, OS.GWL_EXSTYLE);
+ if ((bits & OS.WS_EX_TRANSPARENT) != 0) return;
+ }
+ hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
+ }
+ int bits = OS.GetWindowLong (hwndParent, OS.GWL_EXSTYLE);
+ bits &= ~OS.WS_EX_COMPOSITED;
+ OS.SetWindowLong (hwndParent, OS.GWL_EXSTYLE, bits);
+ }
+ }
+}
+
+@Override
+void releaseChildren (boolean destroy) {
+ Control [] children = _getChildren ();
+ for (int i=0; i<children.length; i++) {
+ Control child = children [i];
+ if (child != null && !child.isDisposed ()) {
+ child.release (false);
+ }
+ }
+ super.releaseChildren (destroy);
+}
+
+@Override
+void releaseWidget () {
+ super.releaseWidget ();
+ if ((state & CANVAS) != 0 && (style & SWT.EMBEDDED) != 0) {
+ long hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
+ if (hwndChild != 0) {
+ int threadId = OS.GetWindowThreadProcessId (hwndChild, null);
+ if (threadId != OS.GetCurrentThreadId ()) {
+ OS.ShowWindow (hwndChild, OS.SW_HIDE);
+ OS.SetParent (hwndChild, 0);
+ }
+ }
+ }
+ layout = null;
+ tabList = null;
+ lpwp = null;
+}
+
+void removeControl (Control control) {
+ fixTabList (control);
+ resizeChildren ();
+}
+
+@Override
+void reskinChildren (int flags) {
+ super.reskinChildren (flags);
+ Control [] children = _getChildren ();
+ for (int i=0; i<children.length; i++) {
+ Control child = children [i];
+ if (child != null) child.reskin (flags);
+ }
+}
+
+void resizeChildren () {
+ if (lpwp == null) return;
+ do {
+ WINDOWPOS [] currentLpwp = lpwp;
+ lpwp = null;
+ if (!resizeChildren (true, currentLpwp)) {
+ resizeChildren (false, currentLpwp);
+ }
+ } while (lpwp != null);
+}
+
+boolean resizeChildren (boolean defer, WINDOWPOS [] pwp) {
+ if (pwp == null) return true;
+ long hdwp = 0;
+ if (defer) {
+ hdwp = OS.BeginDeferWindowPos (pwp.length);
+ if (hdwp == 0) return false;
+ }
+ for (int i=0; i<pwp.length; i++) {
+ WINDOWPOS wp = pwp [i];
+ if (wp != null) {
+ /*
+ * This code is intentionally commented. All widgets that
+ * are created by SWT have WS_CLIPSIBLINGS to ensure that
+ * application code does not draw outside of the control.
+ */
+// int count = parent.getChildrenCount ();
+// if (count > 1) {
+// int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+// if ((bits & OS.WS_CLIPSIBLINGS) == 0) wp.flags |= OS.SWP_NOCOPYBITS;
+// }
+ if (defer) {
+ hdwp = OS.DeferWindowPos (hdwp, wp.hwnd, 0, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
+ if (hdwp == 0) return false;
+ } else {
+ OS.SetWindowPos (wp.hwnd, 0, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
+ }
+ }
+ }
+ if (defer) return OS.EndDeferWindowPos (hdwp);
+ return true;
+}
+
+void resizeEmbeddedHandle(long embeddedHandle, int width, int height) {
+ if (embeddedHandle == 0) return;
+ int [] processID = new int [1];
+ int threadId = OS.GetWindowThreadProcessId (embeddedHandle, processID);
+ if (threadId != OS.GetCurrentThreadId ()) {
+ if (processID [0] == OS.GetCurrentProcessId ()) {
+ if (display.msgHook == 0) {
+ display.getMsgCallback = new Callback (display, "getMsgProc", 3);
+ display.getMsgProc = display.getMsgCallback.getAddress ();
+ if (display.getMsgProc == 0) error (SWT.ERROR_NO_MORE_CALLBACKS);
+ display.msgHook = OS.SetWindowsHookEx (OS.WH_GETMESSAGE, display.getMsgProc, OS.GetLibraryHandle(), threadId);
+ OS.PostThreadMessage (threadId, OS.WM_NULL, 0, 0);
+ }
+ }
+ int flags = OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE | OS.SWP_ASYNCWINDOWPOS;
+ OS.SetWindowPos (embeddedHandle, 0, 0, 0, width, height, flags);
+ }
+}
+
+@Override
+void sendResize () {
+ setResizeChildren (false);
+ super.sendResize ();
+ if (isDisposed ()) return;
+ if (layout != null) {
+ markLayout (false, false);
+ updateLayout (false, false);
+ }
+ setResizeChildren (true);
+}
+
+/**
+ * Sets the background drawing mode to the argument which should
+ * be one of the following constants defined in class <code>SWT</code>:
+ * <code>INHERIT_NONE</code>, <code>INHERIT_DEFAULT</code>,
+ * <code>INHERIT_FORCE</code>.
+ *
+ * @param mode the new background mode
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SWT
+ *
+ * @since 3.2
+ */
+public void setBackgroundMode (int mode) {
+ checkWidget ();
+ backgroundMode = mode;
+ Control [] children = _getChildren ();
+ for (int i = 0; i < children.length; i++) {
+ children [i].updateBackgroundMode ();
+ }
+}
+
+@Override
+void setBoundsInPixels (int x, int y, int width, int height, int flags, boolean defer) {
+ if (display.resizeCount > Display.RESIZE_LIMIT) {
+ defer = false;
+ }
+ if (!defer && (state & CANVAS) != 0) {
+ state &= ~(RESIZE_OCCURRED | MOVE_OCCURRED);
+ state |= RESIZE_DEFERRED | MOVE_DEFERRED;
+ }
+ super.setBoundsInPixels (x, y, width, height, flags, defer);
+ if (!defer && (state & CANVAS) != 0) {
+ boolean wasMoved = (state & MOVE_OCCURRED) != 0;
+ boolean wasResized = (state & RESIZE_OCCURRED) != 0;
+ state &= ~(RESIZE_DEFERRED | MOVE_DEFERRED);
+ if (wasMoved && !isDisposed ()) sendMove ();
+ if (wasResized && !isDisposed ()) sendResize ();
+ }
+}
+
+@Override
+public boolean setFocus () {
+ checkWidget ();
+ Control [] children = _getChildren ();
+ for (int i=0; i<children.length; i++) {
+ Control child = children [i];
+ if (child.setRadioFocus (false)) return true;
+ }
+ for (int i=0; i<children.length; i++) {
+ Control child = children [i];
+ if (child.setFocus ()) return true;
+ }
+ return super.setFocus ();
+}
+
+/**
+ * Sets the layout which is associated with the receiver to be
+ * the argument which may be null.
+ *
+ * @param layout the receiver's new layout or null
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setLayout (Layout layout) {
+ checkWidget ();
+ this.layout = layout;
+}
+
+/**
+ * If the argument is <code>true</code>, causes subsequent layout
+ * operations in the receiver or any of its children to be ignored.
+ * No layout of any kind can occur in the receiver or any of its
+ * children until the flag is set to false.
+ * Layout operations that occurred while the flag was
+ * <code>true</code> are remembered and when the flag is set to
+ * <code>false</code>, the layout operations are performed in an
+ * optimized manner. Nested calls to this method are stacked.
+ *
+ * @param defer the new defer state
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #layout(boolean)
+ * @see #layout(Control[])
+ *
+ * @since 3.1
+ */
+public void setLayoutDeferred (boolean defer) {
+ checkWidget();
+ if (!defer) {
+ if (--layoutCount == 0) {
+ if ((state & LAYOUT_CHILD) != 0 || (state & LAYOUT_NEEDED) != 0) {
+ updateLayout (true);
+ }
+ }
+ } else {
+ layoutCount++;
+ }
+}
+/**
+ * Sets the tabbing order for the specified controls to
+ * match the order that they occur in the argument list.
+ *
+ * @param tabList the ordered list of controls representing the tab order or null
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if a widget in the tabList is null or has been disposed</li>
+ * <li>ERROR_INVALID_PARENT - if widget in the tabList is not in the same widget tree</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setTabList (Control [] tabList) {
+ checkWidget ();
+ if (tabList != null) {
+ for (int i=0; i<tabList.length; i++) {
+ Control control = tabList [i];
+ if (control == null) error (SWT.ERROR_INVALID_ARGUMENT);
+ if (control.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+ if (control.parent != this) error (SWT.ERROR_INVALID_PARENT);
+ }
+ Control [] newList = new Control [tabList.length];
+ System.arraycopy (tabList, 0, newList, 0, tabList.length);
+ tabList = newList;
+ }
+ this.tabList = tabList;
+}
+
+void setResizeChildren (boolean resize) {
+ if (resize) {
+ resizeChildren ();
+ } else {
+ if (display.resizeCount > Display.RESIZE_LIMIT) {
+ return;
+ }
+ int count = getChildrenCount ();
+ if (count > 1 && lpwp == null) {
+ lpwp = new WINDOWPOS [count];
+ }
+ }
+}
+
+@Override
+boolean setTabGroupFocus () {
+ if (isTabItem ()) return setTabItemFocus ();
+ boolean takeFocus = (style & SWT.NO_FOCUS) == 0;
+ if ((state & CANVAS) != 0) {
+ takeFocus = hooksKeys ();
+ if ((style & SWT.EMBEDDED) != 0) takeFocus = true;
+ }
+ if (takeFocus && setTabItemFocus ()) return true;
+ Control [] children = _getChildren ();
+ for (int i=0; i<children.length; i++) {
+ Control child = children [i];
+ if (child.isTabItem () && child.setRadioFocus (true)) return true;
+ }
+ for (int i=0; i<children.length; i++) {
+ Control child = children [i];
+ if (child.isTabItem () && !child.isTabGroup () && child.setTabItemFocus ()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+@Override
+boolean updateTextDirection(int textDirection) {
+ super.updateTextDirection (textDirection);
+ /*
+ * Always continue, communicating the direction to the children since
+ * OS.WS_EX_RTLREADING doesn't propagate to them natively, and since
+ * the direction might need to be handled by each child individually.
+ */
+ Control[] children = _getChildren ();
+ int i = children.length;
+ while (i-- > 0) {
+ if (children[i] != null && !children[i].isDisposed ()) {
+ children[i].updateTextDirection(textDirection);
+ }
+ }
+ /*
+ * Return value indicates whether or not to update derivatives, so in case
+ * of AUTO always return true regardless of the actual update.
+ */
+ return true;
+}
+
+String toolTipText (NMTTDISPINFO hdr) {
+ Shell shell = getShell ();
+ if ((hdr.uFlags & OS.TTF_IDISHWND) == 0) {
+ String string = null;
+ ToolTip toolTip = shell.findToolTip ((int)hdr.idFrom);
+ if (toolTip != null) {
+ string = toolTip.message;
+ if (string == null || string.length () == 0) string = " ";
+ /*
+ * Bug in Windows. On Windows 7, tool tips hang when displaying large
+ * unwrapped strings. The fix is to wrap the string ourselves.
+ */
+ if (string.length () > TOOLTIP_LIMIT / 4) {
+ string = display.wrapText (string, handle, toolTip.getWidth ());
+ }
+ }
+ return string;
+ }
+ shell.setToolTipTitle (hdr.hwndFrom, null, 0);
+ OS.SendMessage (hdr.hwndFrom, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
+ Control control = display.getControl (hdr.idFrom);
+ return control != null ? control.toolTipText : null;
+}
+
+@Override
+boolean translateMnemonic (Event event, Control control) {
+ if (super.translateMnemonic (event, control)) return true;
+ if (control != null) {
+ Control [] children = _getChildren ();
+ for (int i=0; i<children.length; i++) {
+ Control child = children [i];
+ if (child.translateMnemonic (event, control)) return true;
+ }
+ }
+ return false;
+}
+
+@Override
+boolean translateTraversal (MSG msg) {
+ if ((state & CANVAS) != 0 ) {
+ if ((style & SWT.EMBEDDED) != 0) return false;
+ switch ((int)msg.wParam) {
+ case OS.VK_UP:
+ case OS.VK_LEFT:
+ case OS.VK_DOWN:
+ case OS.VK_RIGHT:
+ case OS.VK_PRIOR:
+ case OS.VK_NEXT:
+ int uiState = (int)OS.SendMessage (msg.hwnd, OS.WM_QUERYUISTATE, 0, 0);
+ if ((uiState & OS.UISF_HIDEFOCUS) != 0) {
+ OS.SendMessage (msg.hwnd, OS.WM_UPDATEUISTATE, OS.MAKEWPARAM (OS.UIS_CLEAR, OS.UISF_HIDEFOCUS), 0);
+ }
+ break;
+ }
+ }
+ return super.translateTraversal (msg);
+}
+
+@Override
+void updateBackgroundColor () {
+ super.updateBackgroundColor ();
+ Control [] children = _getChildren ();
+ for (int i=0; i<children.length; i++) {
+ if ((children [i].state & PARENT_BACKGROUND) != 0) {
+ children [i].updateBackgroundColor ();
+ }
+ }
+}
+
+@Override
+void updateBackgroundImage () {
+ super.updateBackgroundImage ();
+ Control [] children = _getChildren ();
+ for (int i=0; i<children.length; i++) {
+ if ((children [i].state & PARENT_BACKGROUND) != 0) {
+ children [i].updateBackgroundImage ();
+ }
+ }
+}
+
+@Override
+void updateBackgroundMode () {
+ super.updateBackgroundMode ();
+ Control [] children = _getChildren ();
+ for (int i = 0; i < children.length; i++) {
+ children [i].updateBackgroundMode ();
+ }
+}
+
+@Override
+void updateFont (Font oldFont, Font newFont) {
+ super.updateFont (oldFont, newFont);
+ Control [] children = _getChildren ();
+ for (int i=0; i<children.length; i++) {
+ Control control = children [i];
+ if (!control.isDisposed ()) {
+ control.updateFont (oldFont, newFont);
+ }
+ }
+}
+
+void updateLayout (boolean all) {
+ updateLayout (true, all);
+}
+
+@Override
+void updateLayout (boolean resize, boolean all) {
+ Composite parent = findDeferredControl ();
+ if (parent != null) {
+ parent.state |= LAYOUT_CHILD;
+ return;
+ }
+ if ((state & LAYOUT_NEEDED) != 0) {
+ boolean changed = (state & LAYOUT_CHANGED) != 0;
+ state &= ~(LAYOUT_NEEDED | LAYOUT_CHANGED);
+ display.runSkin();
+ if (resize) setResizeChildren (false);
+ layout.layout (this, changed);
+ if (resize) setResizeChildren (true);
+ }
+ if (all) {
+ state &= ~LAYOUT_CHILD;
+ Control [] children = _getChildren ();
+ for (int i=0; i<children.length; i++) {
+ children [i].updateLayout (resize, all);
+ }
+ }
+}
+
+@Override
+void updateOrientation () {
+ Control [] controls = _getChildren ();
+ RECT [] rects = new RECT [controls.length];
+ for (int i=0; i<controls.length; i++) {
+ Control control = controls [i];
+ RECT rect = rects [i] = new RECT();
+ control.forceResize ();
+ OS.GetWindowRect (control.topHandle (), rect);
+ OS.MapWindowPoints (0, handle, rect, 2);
+ }
+ int orientation = style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT);
+ super.updateOrientation ();
+ for (int i=0; i<controls.length; i++) {
+ Control control = controls [i];
+ RECT rect = rects [i];
+ control.setOrientation (orientation);
+ int flags = OS.SWP_NOSIZE | OS.SWP_NOZORDER | OS.SWP_NOACTIVATE;
+ OS.SetWindowPos (control.topHandle (), 0, rect.left, rect.top, 0, 0, flags);
+ }
+}
+
+void updateUIState () {
+ long hwndShell = getShell ().handle;
+ int uiState = (int)OS.SendMessage (hwndShell, OS.WM_QUERYUISTATE, 0, 0);
+ if ((uiState & OS.UISF_HIDEFOCUS) != 0) {
+ OS.SendMessage (hwndShell, OS.WM_CHANGEUISTATE, OS.MAKEWPARAM (OS.UIS_CLEAR, OS.UISF_HIDEFOCUS), 0);
+ }
+}
+
+@Override
+int widgetStyle () {
+ /* Force clipping of children by setting WS_CLIPCHILDREN */
+ return super.widgetStyle () | OS.WS_CLIPCHILDREN;
+}
+
+@Override
+LRESULT WM_ERASEBKGND (long wParam, long lParam) {
+ LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
+ if (result != null) return result;
+ if ((state & CANVAS) != 0) {
+ /* Return zero to indicate that the background was not erased */
+ if ((style & (SWT.NO_BACKGROUND | SWT.TRANSPARENT)) != 0) {
+ return LRESULT.ZERO;
+ }
+ }
+ return result;
+}
+
+@Override
+LRESULT WM_GETDLGCODE (long wParam, long lParam) {
+ LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
+ if (result != null) return result;
+ if ((state & CANVAS) != 0) {
+ int flags = 0;
+ if (hooksKeys ()) {
+ flags |= OS.DLGC_WANTALLKEYS | OS.DLGC_WANTARROWS | OS.DLGC_WANTTAB;
+ }
+ if ((style & SWT.NO_FOCUS) != 0) flags |= OS.DLGC_STATIC;
+ if (OS.GetWindow (handle, OS.GW_CHILD) != 0) flags |= OS.DLGC_STATIC;
+ if (flags != 0) return new LRESULT (flags);
+ }
+ return result;
+}
+
+@Override
+LRESULT WM_GETFONT (long wParam, long lParam) {
+ LRESULT result = super.WM_GETFONT (wParam, lParam);
+ if (result != null) return result;
+ long code = callWindowProc (handle, OS.WM_GETFONT, wParam, lParam);
+ if (code != 0) return new LRESULT (code);
+ return new LRESULT (font != null ? font.handle : defaultFont ());
+}
+
+@Override
+LRESULT WM_LBUTTONDOWN (long wParam, long lParam) {
+ LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam);
+ if (result == LRESULT.ZERO) return result;
+
+ /* Set focus for a canvas with no children */
+ if ((state & CANVAS) != 0) {
+ if ((style & SWT.NO_FOCUS) == 0 && hooksKeys ()) {
+ if (OS.GetWindow (handle, OS.GW_CHILD) == 0) setFocus ();
+ }
+ }
+ return result;
+}
+
+@Override
+LRESULT WM_NCHITTEST (long wParam, long lParam) {
+ LRESULT result = super.WM_NCHITTEST (wParam, lParam);
+ if (result != null) return result;
+ /*
+ * Bug in Windows. For some reason, under circumstances
+ * that are not understood, when one scrolled window is
+ * embedded in another and the outer window scrolls the
+ * inner horizontally by moving the location of the inner
+ * one, the vertical scroll bars of the inner window no
+ * longer function. Specifically, WM_NCHITTEST returns
+ * HTCLIENT instead of HTVSCROLL. The fix is to detect
+ * the case where the result of WM_NCHITTEST is HTCLIENT
+ * and the point is not in the client area, and redraw
+ * the trim, which somehow fixes the next WM_NCHITTEST.
+ */
+ if (OS.IsAppThemed ()) {
+ if ((state & CANVAS)!= 0) {
+ long code = callWindowProc (handle, OS.WM_NCHITTEST, wParam, lParam);
+ if (code == OS.HTCLIENT) {
+ RECT rect = new RECT ();
+ OS.GetClientRect (handle, rect);
+ POINT pt = new POINT ();
+ pt.x = OS.GET_X_LPARAM (lParam);
+ pt.y = OS.GET_Y_LPARAM (lParam);
+ OS.MapWindowPoints (0, handle, pt, 1);
+ if (!OS.PtInRect (rect, pt)) {
+ int flags = OS.RDW_FRAME | OS.RDW_INVALIDATE;
+ OS.RedrawWindow (handle, null, 0, flags);
+ }
+ }
+ return new LRESULT (code);
+ }
+ }
+ return result;
+}
+
+@Override
+LRESULT WM_PARENTNOTIFY (long wParam, long lParam) {
+ if ((state & CANVAS) != 0 && (style & SWT.EMBEDDED) != 0) {
+ if (OS.LOWORD (wParam) == OS.WM_CREATE) {
+ RECT rect = new RECT ();
+ OS.GetClientRect (handle, rect);
+ resizeEmbeddedHandle (lParam, rect.right - rect.left, rect.bottom - rect.top);
+ }
+ }
+ return super.WM_PARENTNOTIFY (wParam, lParam);
+}
+
+@Override
+LRESULT WM_PAINT (long wParam, long lParam) {
+ if ((state & DISPOSE_SENT) != 0) return LRESULT.ZERO;
+ if ((state & CANVAS) == 0 || (state & FOREIGN_HANDLE) != 0) {
+ return super.WM_PAINT (wParam, lParam);
+ }
+
+ /* Set the clipping bits */
+ int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+ int newBits = oldBits | OS.WS_CLIPSIBLINGS | OS.WS_CLIPCHILDREN;
+ if (newBits != oldBits) OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
+
+ /* Paint the control and the background */
+ PAINTSTRUCT ps = new PAINTSTRUCT ();
+ if (hooks (SWT.Paint) || filters (SWT.Paint)) {
+
+ /* Use the buffered paint when possible */
+ boolean bufferedPaint = false;
+ if ((style & SWT.DOUBLE_BUFFERED) != 0) {
+ if ((style & (SWT.NO_MERGE_PAINTS | SWT.RIGHT_TO_LEFT | SWT.TRANSPARENT)) == 0) {
+ bufferedPaint = true;
+ }
+ }
+ if (bufferedPaint) {
+ long hDC = OS.BeginPaint (handle, ps);
+ int width = ps.right - ps.left;
+ int height = ps.bottom - ps.top;
+ if (width != 0 && height != 0) {
+ long [] phdc = new long [1];
+ int flags = OS.BPBF_COMPATIBLEBITMAP;
+ RECT prcTarget = new RECT ();
+ OS.SetRect (prcTarget, ps.left, ps.top, ps.right, ps.bottom);
+ long hBufferedPaint = OS.BeginBufferedPaint (hDC, prcTarget, flags, null, phdc);
+ GCData data = new GCData ();
+ data.device = display;
+ data.foreground = getForegroundPixel ();
+ Control control = findBackgroundControl ();
+ if (control == null) control = this;
+ data.background = control.getBackgroundPixel ();
+ data.font = Font.win32_new(display, OS.SendMessage (handle, OS.WM_GETFONT, 0, 0));
+ data.uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+ if ((style & SWT.NO_BACKGROUND) != 0) {
+ /* This code is intentionally commented because it may be slow to copy bits from the screen */
+ //paintGC.copyArea (image, ps.left, ps.top);
+ } else {
+ RECT rect = new RECT ();
+ OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom);
+ drawBackground (phdc [0], rect);
+ }
+ GC gc = GC.win32_new (phdc [0], data);
+ Event event = new Event ();
+ event.gc = gc;
+ event.setBoundsInPixels(new Rectangle(ps.left, ps.top, width, height));
+ sendEvent (SWT.Paint, event);
+ if (data.focusDrawn && !isDisposed ()) updateUIState ();
+ gc.dispose ();
+ OS.EndBufferedPaint (hBufferedPaint, true);
+ }
+ OS.EndPaint (handle, ps);
+ } else {
+
+ /* Create the paint GC */
+ GCData data = new GCData ();
+ data.ps = ps;
+ data.hwnd = handle;
+ GC gc = GC.win32_new (this, data);
+
+ /* Get the system region for the paint HDC */
+ long sysRgn = 0;
+ if ((style & (SWT.DOUBLE_BUFFERED | SWT.TRANSPARENT)) != 0 || (style & SWT.NO_MERGE_PAINTS) != 0) {
+ sysRgn = OS.CreateRectRgn (0, 0, 0, 0);
+ if (OS.GetRandomRgn (gc.handle, sysRgn, OS.SYSRGN) == 1) {
+ if ((OS.GetLayout (gc.handle) & OS.LAYOUT_RTL) != 0) {
+ int nBytes = OS.GetRegionData (sysRgn, 0, null);
+ int [] lpRgnData = new int [nBytes / 4];
+ OS.GetRegionData (sysRgn, nBytes, lpRgnData);
+ long newSysRgn = OS.ExtCreateRegion (new float [] {-1, 0, 0, 1, 0, 0}, nBytes, lpRgnData);
+ OS.DeleteObject (sysRgn);
+ sysRgn = newSysRgn;
+ }
+ POINT pt = new POINT();
+ OS.MapWindowPoints (0, handle, pt, 1);
+ OS.OffsetRgn (sysRgn, pt.x, pt.y);
+ }
+ }
+
+ /* Send the paint event */
+ int width = ps.right - ps.left;
+ int height = ps.bottom - ps.top;
+ if (width != 0 && height != 0) {
+ GC paintGC = null;
+ Image image = null;
+ if ((style & (SWT.DOUBLE_BUFFERED | SWT.TRANSPARENT)) != 0) {
+ image = new Image (display, width, height);
+ paintGC = gc;
+ gc = new GC (image, paintGC.getStyle() & SWT.RIGHT_TO_LEFT);
+ GCData gcData = gc.getGCData ();
+ gcData.uiState = data.uiState;
+ gc.setForeground (getForeground ());
+ gc.setBackground (getBackground ());
+ gc.setFont (getFont ());
+ if ((style & SWT.TRANSPARENT) != 0) {
+ OS.BitBlt (gc.handle, 0, 0, width, height, paintGC.handle, ps.left, ps.top, OS.SRCCOPY);
+ }
+ OS.OffsetRgn (sysRgn, -ps.left, -ps.top);
+ OS.SelectClipRgn (gc.handle, sysRgn);
+ OS.OffsetRgn (sysRgn, ps.left, ps.top);
+ OS.SetMetaRgn (gc.handle);
+ OS.SetWindowOrgEx (gc.handle, ps.left, ps.top, null);
+ OS.SetBrushOrgEx (gc.handle, ps.left, ps.top, null);
+ if ((style & (SWT.NO_BACKGROUND | SWT.TRANSPARENT)) != 0) {
+ /* This code is intentionally commented because it may be slow to copy bits from the screen */
+ //paintGC.copyArea (image, ps.left, ps.top);
+ } else {
+ RECT rect = new RECT ();
+ OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom);
+ drawBackground (gc.handle, rect);
+ }
+ }
+ Event event = new Event ();
+ event.gc = gc;
+ RECT rect = null;
+ if ((style & SWT.NO_MERGE_PAINTS) != 0 && OS.GetRgnBox (sysRgn, rect = new RECT ()) == OS.COMPLEXREGION) {
+ int nBytes = OS.GetRegionData (sysRgn, 0, null);
+ int [] lpRgnData = new int [nBytes / 4];
+ OS.GetRegionData (sysRgn, nBytes, lpRgnData);
+ int count = lpRgnData [2];
+ for (int i=0; i<count; i++) {
+ int offset = 8 + (i << 2);
+ OS.SetRect (rect, lpRgnData [offset], lpRgnData [offset + 1], lpRgnData [offset + 2], lpRgnData [offset + 3]);
+ if ((style & (SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.TRANSPARENT)) == 0) {
+ drawBackground (gc.handle, rect);
+ }
+ event.setBoundsInPixels(new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top));
+ event.count = count - 1 - i;
+ sendEvent (SWT.Paint, event);
+ }
+ } else {
+ if ((style & (SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.TRANSPARENT)) == 0) {
+ if (rect == null) rect = new RECT ();
+ OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom);
+ drawBackground (gc.handle, rect);
+ }
+ event.setBoundsInPixels(new Rectangle(ps.left, ps.top, width, height));
+ sendEvent (SWT.Paint, event);
+ }
+ // widget could be disposed at this point
+ event.gc = null;
+ if ((style & (SWT.DOUBLE_BUFFERED | SWT.TRANSPARENT)) != 0) {
+ if (!gc.isDisposed ()) {
+ GCData gcData = gc.getGCData ();
+ if (gcData.focusDrawn && !isDisposed ()) updateUIState ();
+ }
+ gc.dispose();
+ if (!isDisposed ()) paintGC.drawImage (image, DPIUtil.autoScaleDown(ps.left), DPIUtil.autoScaleDown(ps.top));
+ image.dispose ();
+ gc = paintGC;
+ }
+ }
+ if (sysRgn != 0) OS.DeleteObject (sysRgn);
+ if (data.focusDrawn && !isDisposed ()) updateUIState ();
+
+ /* Dispose the paint GC */
+ gc.dispose ();
+ }
+ } else {
+ long hDC = OS.BeginPaint (handle, ps);
+ if ((style & (SWT.NO_BACKGROUND | SWT.TRANSPARENT)) == 0) {
+ RECT rect = new RECT ();
+ OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom);
+ drawBackground (hDC, rect);
+ }
+ OS.EndPaint (handle, ps);
+ }
+
+ /* Restore the clipping bits */
+ if (!isDisposed ()) {
+ if (newBits != oldBits) {
+ /*
+ * It is possible (but unlikely), that application
+ * code could have disposed the widget in the paint
+ * event. If this happens, don't attempt to restore
+ * the style.
+ */
+ if (!isDisposed ()) {
+ OS.SetWindowLong (handle, OS.GWL_STYLE, oldBits);
+ }
+ }
+ }
+ return LRESULT.ZERO;
+}
+
+@Override
+LRESULT WM_PRINTCLIENT (long wParam, long lParam) {
+ LRESULT result = super.WM_PRINTCLIENT (wParam, lParam);
+ if (result != null) return result;
+ if ((state & CANVAS) != 0) {
+ forceResize ();
+ int nSavedDC = OS.SaveDC (wParam);
+ RECT rect = new RECT ();
+ OS.GetClientRect (handle, rect);
+ if ((style & (SWT.NO_BACKGROUND | SWT.TRANSPARENT)) == 0) {
+ drawBackground (wParam, rect);
+ }
+ if (hooks (SWT.Paint) || filters (SWT.Paint)) {
+ GCData data = new GCData ();
+ data.device = display;
+ data.foreground = getForegroundPixel ();
+ Control control = findBackgroundControl ();
+ if (control == null) control = this;
+ data.background = control.getBackgroundPixel ();
+ data.font = Font.win32_new(display, OS.SendMessage (handle, OS.WM_GETFONT, 0, 0));
+ data.uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+ GC gc = GC.win32_new (wParam, data);
+ Event event = new Event ();
+ event.gc = gc;
+ event.setBoundsInPixels(new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top));
+ sendEvent (SWT.Paint, event);
+ event.gc = null;
+ gc.dispose ();
+ }
+ OS.RestoreDC (wParam, nSavedDC);
+ }
+ return result;
+}
+
+@Override
+LRESULT WM_SETFONT (long wParam, long lParam) {
+ if (lParam != 0) OS.InvalidateRect (handle, null, true);
+ return super.WM_SETFONT (wParam, lParam);
+}
+
+@Override
+LRESULT WM_SIZE (long wParam, long lParam) {
+ LRESULT result = null;
+ if ((state & RESIZE_DEFERRED) != 0) {
+ result = super.WM_SIZE (wParam, lParam);
+ } else {
+ /* Begin deferred window positioning */
+ setResizeChildren (false);
+
+ /* Resize and Layout */
+ result = super.WM_SIZE (wParam, lParam);
+ /*
+ * It is possible (but unlikely), that application
+ * code could have disposed the widget in the resize
+ * event. If this happens, end the processing of the
+ * Windows message by returning the result of the
+ * WM_SIZE message.
+ */
+ if (isDisposed ()) return result;
+ if (layout != null) {
+ markLayout (false, false);
+ updateLayout (false, false);
+ }
+
+ /* End deferred window positioning */
+ setResizeChildren (true);
+ }
+
+ /* Damage the widget to cause a repaint */
+ if (OS.IsWindowVisible (handle)) {
+ if ((state & CANVAS) != 0) {
+ if ((style & SWT.NO_REDRAW_RESIZE) == 0) {
+ if (hooks (SWT.Paint)) {
+ OS.InvalidateRect (handle, null, true);
+ }
+ }
+ }
+ if (OS.IsAppThemed ()) {
+ if (findThemeControl () != null) redrawChildren ();
+ }
+ }
+
+ /* Resize the embedded window */
+ if ((state & CANVAS) != 0 && (style & SWT.EMBEDDED) != 0) {
+ resizeEmbeddedHandle (OS.GetWindow (handle, OS.GW_CHILD), OS.LOWORD (lParam), OS.HIWORD (lParam));
+ }
+ return result;
+}
+
+@Override
+LRESULT WM_SYSCOLORCHANGE (long wParam, long lParam) {
+ LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam);
+ if (result != null) return result;
+ long hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
+ while (hwndChild != 0) {
+ OS.SendMessage (hwndChild, OS.WM_SYSCOLORCHANGE, 0, 0);
+ hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
+ }
+ return result;
+}
+
+@Override
+LRESULT WM_SYSCOMMAND (long wParam, long lParam) {
+ LRESULT result = super.WM_SYSCOMMAND (wParam, lParam);
+ if (result != null) return result;
+
+ /*
+ * Check to see if the command is a system command or
+ * a user menu item that was added to the system menu.
+ *
+ * NOTE: This is undocumented.
+ */
+ if ((wParam & 0xF000) == 0) return result;
+
+ /*
+ * Bug in Windows. When a vertical or horizontal scroll bar is
+ * hidden or shown while the opposite scroll bar is being scrolled
+ * by the user (with WM_HSCROLL code SB_LINEDOWN), the scroll bar
+ * does not redraw properly. The fix is to detect this case and
+ * redraw the non-client area.
+ */
+ int cmd = (int)wParam & 0xFFF0;
+ switch (cmd) {
+ case OS.SC_HSCROLL:
+ case OS.SC_VSCROLL:
+ boolean showHBar = horizontalBar != null && horizontalBar.getVisible ();
+ boolean showVBar = verticalBar != null && verticalBar.getVisible ();
+ long code = callWindowProc (handle, OS.WM_SYSCOMMAND, wParam, lParam);
+ if ((showHBar != (horizontalBar != null && horizontalBar.getVisible ())) ||
+ (showVBar != (verticalBar != null && verticalBar.getVisible ()))) {
+ int flags = OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_UPDATENOW;
+ OS.RedrawWindow (handle, null, 0, flags);
+ }
+ if (code == 0) return LRESULT.ZERO;
+ return new LRESULT (code);
+ }
+
+ /* Return the result */
+ return result;
+}
+
+@Override
+LRESULT WM_UPDATEUISTATE (long wParam, long lParam) {
+ LRESULT result = super.WM_UPDATEUISTATE (wParam, lParam);
+ if (result != null) return result;
+ if ((state & CANVAS) != 0 && hooks (SWT.Paint)) {
+ OS.InvalidateRect (handle, null, true);
+ }
+ return result;
+}
+
+@Override
+LRESULT wmNCPaint (long hwnd, long wParam, long lParam) {
+ LRESULT result = super.wmNCPaint (hwnd, wParam, lParam);
+ if (result != null) return result;
+ long borderHandle = borderHandle ();
+ if ((state & CANVAS) != 0 || (hwnd == borderHandle && handle != borderHandle)) {
+ if (OS.IsAppThemed ()) {
+ int bits1 = OS.GetWindowLong (hwnd, OS.GWL_EXSTYLE);
+ if ((bits1 & OS.WS_EX_CLIENTEDGE) != 0) {
+ long code = 0;
+ int bits2 = OS.GetWindowLong (hwnd, OS.GWL_STYLE);
+ if ((bits2 & (OS.WS_HSCROLL | OS.WS_VSCROLL)) != 0) {
+ code = callWindowProc (hwnd, OS.WM_NCPAINT, wParam, lParam);
+ }
+ long hDC = OS.GetWindowDC (hwnd);
+ RECT rect = new RECT ();
+ OS.GetWindowRect (hwnd, rect);
+ rect.right -= rect.left;
+ rect.bottom -= rect.top;
+ rect.left = rect.top = 0;
+ int border = OS.GetSystemMetrics (OS.SM_CXEDGE);
+ OS.ExcludeClipRect (hDC, border, border, rect.right - border, rect.bottom - border);
+ OS.DrawThemeBackground (display.hEditTheme (), hDC, OS.EP_EDITTEXT, OS.ETS_NORMAL, rect, null);
+ OS.ReleaseDC (hwnd, hDC);
+ return new LRESULT (code);
+ }
+ }
+ }
+ return result;
+}
+
+@Override
+LRESULT wmNotify (NMHDR hdr, long wParam, long lParam) {
+ switch (hdr.code) {
+ /*
+ * Feature in Windows. When the tool tip control is
+ * created, the parent of the tool tip is the shell.
+ * If SetParent () is used to reparent the tool bar
+ * into a new shell, the tool tip is not reparented
+ * and pops up underneath the new shell. The fix is
+ * to make sure the tool tip is a topmost window.
+ */
+ case OS.TTN_SHOW:
+ case OS.TTN_POP: {
+ /*
+ * Bug in Windows 98 and NT. Setting the tool tip to be the
+ * top most window using HWND_TOPMOST can result in a parent
+ * dialog shell being moved behind its parent if the dialog
+ * has a sibling that is currently on top. The fix is to
+ * lock the z-order of the active window.
+ *
+ * Feature in Windows. Using SetWindowPos() with HWND_NOTOPMOST
+ * to clear the topmost state of a window whose parent is already
+ * topmost clears the topmost state of the parent. The fix is to
+ * check if the parent is already on top and neither set or clear
+ * the topmost status of the tool tip.
+ */
+ long hwndParent = hdr.hwndFrom;
+ do {
+ hwndParent = OS.GetParent (hwndParent);
+ if (hwndParent == 0) break;
+ int bits = OS.GetWindowLong (hwndParent, OS.GWL_EXSTYLE);
+ if ((bits & OS.WS_EX_TOPMOST) != 0) break;
+ } while (true);
+ if (hwndParent != 0) break;
+ /*
+ * Bug in Windows. TTN_SHOW is sent for inactive shells. When
+ * SetWindowPos is called as a reaction, inactive shells can
+ * wrongly end up on top. The fix is to swallow such requests.
+ *
+ * A visible effect is that spurious tool tips can show up and
+ * disappear in a split second. This is a mostly harmless
+ * feature that can also be observed in the Windows Explorer.
+ * See bug 491627 for more details.
+ */
+ if (display.getActiveShell () == null) return LRESULT.ONE;
+
+ display.lockActiveWindow = true;
+ int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOSIZE;
+ long hwndInsertAfter = hdr.code == OS.TTN_SHOW ? OS.HWND_TOPMOST : OS.HWND_NOTOPMOST;
+ OS.SetWindowPos (hdr.hwndFrom, hwndInsertAfter, 0, 0, 0, 0, flags);
+ display.lockActiveWindow = false;
+ break;
+ }
+ case OS.TTN_GETDISPINFO: {
+ NMTTDISPINFO lpnmtdi = new NMTTDISPINFO ();
+ OS.MoveMemory (lpnmtdi, lParam, NMTTDISPINFO.sizeof);
+ String string = toolTipText (lpnmtdi);
+ if (string != null) {
+ Shell shell = getShell ();
+ string = Display.withCrLf (string);
+ /*
+ * Bug in Windows. On Windows 7, tool tips hang when displaying large
+ * strings. The fix is to limit the tool tip string to 4Kb.
+ */
+ if (string.length() > TOOLTIP_LIMIT) {
+ string = string.substring(0, TOOLTIP_LIMIT);
+ }
+ /*
+ * Bug 475858: In Japanese like languages where mnemonics are not taken from the
+ * source label text but appended in parentheses like "(&M)" at end. In order to
+ * allow the reuse of such label text as a tool-tip text as well, "(&M)" like
+ * character sequence has to be removed from the end of CJK-style mnemonics.
+ */
+ char [] chars = fixMnemonic (string, false, true);
+
+ /*
+ * Ensure that the orientation of the tool tip matches
+ * the orientation of the control.
+ */
+ Widget widget = null;
+ long hwnd = hdr.idFrom;
+ if ((lpnmtdi.uFlags & OS.TTF_IDISHWND) != 0) {
+ widget = display.getControl (hwnd);
+ } else {
+ if (hdr.hwndFrom == shell.toolTipHandle || hdr.hwndFrom == shell.balloonTipHandle) {
+ widget = shell.findToolTip ((int)hdr.idFrom);
+ }
+ }
+ if (widget != null) {
+ int style = widget.getStyle();
+ int flags = SWT.RIGHT_TO_LEFT | SWT.FLIP_TEXT_DIRECTION;
+ if ((style & flags) != 0 && (style & flags) != flags) {
+ lpnmtdi.uFlags |= OS.TTF_RTLREADING;
+ } else {
+ lpnmtdi.uFlags &= ~OS.TTF_RTLREADING;
+ }
+ }
+ shell.setToolTipText (lpnmtdi, chars);
+ OS.MoveMemory (lParam, lpnmtdi, NMTTDISPINFO.sizeof);
+ return LRESULT.ZERO;
+ }
+ break;
+ }
+ }
+ return super.wmNotify (hdr, wParam, lParam);
+}
+
+}