Java tutorial
/******************************************************************************* * Copyright (c) 2000, 2017 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 ((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 ((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); } }