org.eclipse.swt.widgets.Shell.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.swt.widgets.Shell.java

Source

/*******************************************************************************
 * Copyright (c) 2002, 2015 Innoopract Informationssysteme GmbH and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Innoopract Informationssysteme GmbH - initial API and implementation
 *    EclipseSource - ongoing development
 *    Rdiger Herrmann - bug 335112
 ******************************************************************************/
package org.eclipse.swt.widgets;

import org.eclipse.rap.rwt.internal.lifecycle.ProcessActionRunner;
import org.eclipse.rap.rwt.internal.lifecycle.WidgetLCA;
import org.eclipse.rap.rwt.internal.theme.CssBoxDimensions;
import org.eclipse.rap.rwt.internal.theme.ThemeAdapter;
import org.eclipse.rap.rwt.theme.BoxDimensions;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.ShellListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.internal.widgets.IDisplayAdapter;
import org.eclipse.swt.internal.widgets.IShellAdapter;
import org.eclipse.swt.internal.widgets.MenuHolder;
import org.eclipse.swt.internal.widgets.shellkit.ShellLCA;
import org.eclipse.swt.internal.widgets.shellkit.ShellThemeAdapter;

/**
 * Instances of this class represent the "windows"
 * which the desktop or "window manager" is managing.
 * Instances that do not have a parent (that is, they
 * are built using the constructor, which takes a
 * <code>Display</code> as the argument) are described
 * as <em>top level</em> shells. Instances that do have
 * a parent are described as <em>secondary</em> or
 * <em>dialog</em> shells.
 * <p>
 * Instances are always displayed in one of the maximized,
 * minimized or normal states:
 * <ul>
 * <li>
 * When an instance is marked as <em>maximized</em>, the
 * window manager will typically resize it to fill the
 * entire visible area of the display, and the instance
 * is usually put in a state where it can not be resized
 * (even if it has style <code>RESIZE</code>) until it is
 * no longer maximized.
 * </li><li>
 * When an instance is in the <em>normal</em> state (neither
 * maximized or minimized), its appearance is controlled by
 * the style constants which were specified when it was created
 * and the restrictions of the window manager (see below).
 * </li><li>
 * When an instance has been marked as <em>minimized</em>,
 * its contents (client area) will usually not be visible,
 * and depending on the window manager, it may be
 * "iconified" (that is, replaced on the desktop by a small
 * simplified representation of itself), relocated to a
 * distinguished area of the screen, or hidden. Combinations
 * of these changes are also possible.
 * </li>
 * </ul>
 * </p><p>
 * The <em>modality</em> of an instance may be specified using
 * style bits. The modality style bits are used to determine
 * whether input is blocked for other shells on the display.
 * The <code>PRIMARY_MODAL</code> style allows an instance to block
 * input to its parent. The <code>APPLICATION_MODAL</code> style
 * allows an instance to block input to every other shell in the
 * display. The <code>SYSTEM_MODAL</code> style allows an instance
 * to block input to all shells, including shells belonging to
 * different applications.
 * </p><p>
 * Note: The styles supported by this class are treated
 * as <em>HINT</em>s, since the window manager for the
 * desktop on which the instance is visible has ultimate
 * control over the appearance and behavior of decorations
 * and modality. For example, some window managers only
 * support resizable windows and will always assume the
 * RESIZE style, even if it is not set. In addition, if a
 * modality style is not supported, it is "upgraded" to a
 * more restrictive modality style that is supported. For
 * example, if <code>PRIMARY_MODAL</code> is not supported,
 * it would be upgraded to <code>APPLICATION_MODAL</code>.
 * A modality style may also be "downgraded" to a less
 * restrictive style. For example, most operating systems
 * no longer support <code>SYSTEM_MODAL</code> because
 * it can freeze up the desktop, so this is typically
 * downgraded to <code>APPLICATION_MODAL</code>.
 * <dl>
 * <dt><b>Styles:</b></dt>
 * <dd>BORDER, CLOSE, MIN, MAX, NO_TRIM, RESIZE, TITLE, ON_TOP, TOOL, SHEET</dd>
 * <dd>APPLICATION_MODAL, MODELESS, PRIMARY_MODAL, SYSTEM_MODAL</dd>
 * <dt><b>Events:</b></dt>
 * <dd>Activate, Close, Deactivate, Deiconify, Iconify</dd>
 * </dl>
 * Class <code>SWT</code> provides two "convenience constants"
 * for the most commonly required style combinations:
 * <dl>
 * <dt><code>SHELL_TRIM</code></dt>
 * <dd>
 * the result of combining the constants which are required
 * to produce a typical application top level shell: (that
 * is, <code>CLOSE | TITLE | MIN | MAX | RESIZE</code>)
 * </dd>
 * <dt><code>DIALOG_TRIM</code></dt>
 * <dd>
 * the result of combining the constants which are required
 * to produce a typical application dialog shell: (that
 * is, <code>TITLE | CLOSE | BORDER</code>)
 * </dd>
 * </dl>
 * </p>
 * <p>
 * Note: Only one of the styles APPLICATION_MODAL, MODELESS,
 * PRIMARY_MODAL and SYSTEM_MODAL may be specified.
 * </p><p>
 * IMPORTANT: This class is not intended to be subclassed.
 * </p>
 *
 * @see SWT
 */
public class Shell extends Decorations {

    private static final BoxDimensions ZERO = new BoxDimensions(0, 0, 0, 0);
    private static final int MODE_NONE = 0;
    private static final int MODE_MAXIMIZED = 1;
    private static final int MODE_MINIMIZED = 2;
    private static final int MODE_FULLSCREEN = 4;

    private static final int INITIAL_SIZE_PERCENT = 60;
    private static final int MIN_WIDTH_LIMIT = 30;

    private class ShellAdapter implements IShellAdapter {

        @Override
        public Control getActiveControl() {
            return lastActive;
        }

        @Override
        public void setActiveControl(Control control) {
            Shell.this.setActiveControl(control);
        }

        @Override
        public Rectangle getMenuBounds() {
            return Shell.this.getMenuBounds();
        }

        @Override
        public int getTopTrim() {
            return Shell.this.getTopTrim();
        }

        @Override
        public void setBounds(Rectangle bounds) {
            Shell.this.setBounds(bounds, false);
        }

        @Override
        public ToolTip[] getToolTips() {
            return Shell.this.getToolTips();
        }

    }

    private Control lastActive;
    private transient IShellAdapter shellAdapter;
    private int alpha;
    private Rectangle savedBounds;
    private int mode;
    private boolean modified;
    private int minWidth;
    private int minHeight;
    private ToolTip[] toolTips;

    private Shell(Display display, Shell parent, int style) {
        super(checkParent(parent));
        if (display != null) {
            this.display = display;
        } else {
            this.display = Display.getCurrent();
            if (this.display == null) {
                this.display = Display.getDefault();
            }
        }
        alpha = 0xFF;
        mode = MODE_NONE;
        this.style = checkStyle(style);
        addState(HIDDEN);
        minWidth = MIN_WIDTH_LIMIT;
        minHeight = getMinHeightLimit();
        this.display.addShell(this);
        reskinWidget();
        createWidget();
        setInitialSize();
    }

    /**
     * Constructs a new instance of this class. This is equivalent
     * to calling <code>Shell((Display) null)</code>.
     *
     * @exception SWTException <ul>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
     *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
     * </ul>
     *
     * @since 1.3
     */
    public Shell() {
        this((Display) null);
    }

    /**
     * Constructs a new instance of this class given only the style value
     * describing its behavior and appearance. This is equivalent to calling
     * <code>Shell((Display) null, style)</code>.
     * <p>
     * The style value is either one of the style constants defined in class
     * <code>SWT</code> which is applicable to instances of this class, or must
     * be built by <em>bitwise OR</em>'ing together (that is, using the
     * <code>int</code> "|" operator) two or more of those <code>SWT</code>
     * style constants. The class description lists the style constants that are
     * applicable to the class. Style bits are also inherited from superclasses.
     * </p>
     *
     * @param style the style of control to construct
     * @exception SWTException
     *                <ul>
     *                <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
     *                thread that created the parent</li>
     *                <li>ERROR_INVALID_SUBCLASS - if this class is not an
     *                allowed subclass</li>
     *                </ul>
     * @see SWT#BORDER
     * @see SWT#CLOSE
     * @see SWT#MIN
     * @see SWT#MAX
     * @see SWT#RESIZE
     * @see SWT#TITLE
     * @see SWT#NO_TRIM
     * @see SWT#SHELL_TRIM
     * @see SWT#DIALOG_TRIM
     * <!--@see SWT#MODELESS-->
     * <!--@see SWT#PRIMARY_MODAL-->
     * @see SWT#APPLICATION_MODAL
     * <!--@see SWT#SYSTEM_MODAL-->
     * @see SWT#SHEET
     */
    public Shell(int style) {
        this((Display) null, style);
    }

    /**
     * Constructs a new instance of this class given only the display
     * to create it on. It is created with style <code>SWT.SHELL_TRIM</code>.
     * <p>
     * Note: Currently, null can be passed in for the display argument.
     * This has the effect of creating the shell on the currently active
     * display if there is one. If there is no current display, the
     * shell is created on a "default" display. <b>Passing in null as
     * the display argument is not considered to be good coding style,
     * and may not be supported in a future release of SWT.</b>
     * </p>
     *
     * @param display the display to create the shell on
     *
     * @exception SWTException <ul>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
     *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
     * </ul>
     */
    public Shell(Display display) {
        this(display, SWT.SHELL_TRIM);
    }

    /**
     * Constructs a new instance of this class given the display
     * to create it on and a style value describing its behavior
     * and appearance.
     * <p>
     * The style value is either one of the style constants defined in
     * class <code>SWT</code> which is applicable to instances of this
     * class, or must be built by <em>bitwise OR</em>'ing together
     * (that is, using the <code>int</code> "|" operator) two or more
     * of those <code>SWT</code> style constants. The class description
     * lists the style constants that are applicable to the class.
     * Style bits are also inherited from superclasses.
     * </p><p>
     * Note: Currently, null can be passed in for the display argument.
     * This has the effect of creating the shell on the currently active
     * display if there is one. If there is no current display, the
     * shell is created on a "default" display. <b>Passing in null as
     * the display argument is not considered to be good coding style,
     * and may not be supported in a future release of SWT.</b>
     * </p>
     *
     * @param display the display to create the shell on
     * @param style the style of control to construct
     *
     * @exception SWTException <ul>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
     *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
     * </ul>
     *
     * @see SWT#BORDER
     * @see SWT#CLOSE
     * @see SWT#MIN
     * @see SWT#MAX
     * @see SWT#RESIZE
     * @see SWT#TITLE
     * @see SWT#NO_TRIM
     * @see SWT#SHELL_TRIM
     * @see SWT#DIALOG_TRIM
     * @see SWT#MODELESS
     * @see SWT#PRIMARY_MODAL
     * @see SWT#APPLICATION_MODAL
     * @see SWT#SYSTEM_MODAL
     * @see SWT#SHEET
     */
    public Shell(Display display, int style) {
        this(display, null, style);
    }

    /**
     * Constructs a new instance of this class given only its
     * parent. It is created with style <code>SWT.DIALOG_TRIM</code>.
     * <p>
     * Note: Currently, null can be passed in for the parent.
     * This has the effect of creating the shell on the currently active
     * display if there is one. If there is no current display, the
     * shell is created on a "default" display. <b>Passing in null as
     * the parent is not considered to be good coding style,
     * and may not be supported in a future release of SWT.</b>
     * </p>
     *
     * @param parent a shell which will be the parent of the new instance
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_INVALID_ARGUMENT - if the parent is disposed</li>
     * </ul>
     * @exception SWTException <ul>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
     *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
     * </ul>
     */
    public Shell(Shell parent) {
        this(parent, SWT.DIALOG_TRIM);
    }

    /**
     * Constructs a new instance of this class given its parent
     * and a style value describing its behavior and appearance.
     * <p>
     * The style value is either one of the style constants defined in
     * class <code>SWT</code> which is applicable to instances of this
     * class, or must be built by <em>bitwise OR</em>'ing together
     * (that is, using the <code>int</code> "|" operator) two or more
     * of those <code>SWT</code> style constants. The class description
     * lists the style constants that are applicable to the class.
     * Style bits are also inherited from superclasses.
     * </p><p>
     * Note: Currently, null can be passed in for the parent.
     * This has the effect of creating the shell on the currently active
     * display if there is one. If there is no current display, the
     * shell is created on a "default" display. <b>Passing in null as
     * the parent is not considered to be good coding style,
     * and may not be supported in a future release of SWT.</b>
     * </p>
     *
     * @param parent a shell which will be the parent of the new instance
     * @param style the style of control to construct
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_INVALID_ARGUMENT - if the parent is disposed</li>
     * </ul>
     * @exception SWTException <ul>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
     *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
     * </ul>
     *
     * @see SWT#BORDER
     * @see SWT#CLOSE
     * @see SWT#MIN
     * @see SWT#MAX
     * @see SWT#RESIZE
     * @see SWT#TITLE
     * @see SWT#NO_TRIM
     * @see SWT#SHELL_TRIM
     * @see SWT#DIALOG_TRIM
     * @see SWT#ON_TOP
     * @see SWT#TOOL
     * @see SWT#SHEET
     * <!--@see SWT#MODELESS-->
     * <!--@see SWT#PRIMARY_MODAL-->
     * @see SWT#APPLICATION_MODAL
     * <!--@see SWT#SYSTEM_MODAL-->
     */
    public Shell(Shell parent, int style) {
        this(parent != null ? parent.display : null, parent, style);
    }

    @Override
    Shell internalGetShell() {
        return this;
    }

    /**
     * Returns an array containing all shells which are
     * descendents of the receiver.
     * <p>
     * @return the dialog shells
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public Shell[] getShells() {
        checkWidget();
        return internalGetShells();
    }

    private Shell[] internalGetShells() {
        int count = 0;
        Shell[] shells = display.getShells();
        for (int i = 0; i < shells.length; i++) {
            Control shell = shells[i];
            do {
                shell = shell._getParent();
            } while (shell != null && shell != this);
            if (shell == this) {
                count++;
            }
        }
        int index = 0;
        Shell[] result = new Shell[count];
        for (int i = 0; i < shells.length; i++) {
            Control shell = shells[i];
            do {
                shell = shell._getParent();
            } while (shell != null && shell != this);
            if (shell == this) {
                result[index++] = shells[i];
            }
        }
        return result;
    }

    /**
     * If the receiver is visible, moves it to the top of the
     * drawing order for the display on which it was created
     * (so that all other shells on that display, which are not
     * the receiver's children will be drawn behind it) and asks
     * the window manager to make the shell active
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see Control#moveAbove
     * @see Control#setFocus
     * @see Control#setVisible
     * @see Display#getActiveShell
     * @see Decorations#setDefaultButton(Button)
     * @see Shell#open
     * @see Shell#setActive
     */
    public void setActive() {
        checkWidget();
        if (isVisible()) {
            display.setActiveShell(this);
        }
    }

    /**
     * If the receiver is visible, moves it to the top of the drawing order for
     * the display on which it was created (so that all other shells on that
     * display, which are not the receiver's children will be drawn behind it) and
     * forces the window manager to make the shell active.
     *
     * @exception SWTException <ul>
     *   <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *   <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
     *   created the receiver</li>
     * </ul>
     * @since 1.2
     * @see Control#moveAbove
     * @see Control#setFocus
     * @see Control#setVisible
     * @see Display#getActiveShell
     * @see Decorations#setDefaultButton(Button)
     * @see Shell#open
     * @see Shell#setActive
     */
    public void forceActive() {
        checkWidget();
        setActive();
    }

    /////////////////////
    // Shell measurements

    // TODO [rst] Move to class Decorations, as soon as it exists
    @Override
    public Rectangle getClientArea() {
        checkWidget();
        Rectangle bounds = getBounds();
        BoxDimensions padding = getPadding();
        BoxDimensions titleBarMargin = getTitleBarMargin();
        int hTopTrim;
        hTopTrim = titleBarMargin.top + titleBarMargin.bottom;
        hTopTrim += getTitleBarHeight();
        hTopTrim += getMenuBarHeight();
        BoxDimensions border = getBorder();
        int padingWidth = padding.left + padding.right;
        int paddingHeight = padding.top + padding.bottom;
        int borderWidth = border.left + border.right;
        int borderHeight = border.top + border.bottom;
        return new Rectangle(padding.left, hTopTrim + padding.top, bounds.width - padingWidth - borderWidth,
                bounds.height - hTopTrim - paddingHeight - borderHeight);
    }

    // TODO [rst] Move to class Decorations, as soon as it exists
    @Override
    public Rectangle computeTrim(int x, int y, int width, int height) {
        checkWidget();
        int hTopTrim = getTopTrim();
        BoxDimensions padding = getPadding();
        BoxDimensions border = getBorder();
        int paddingWidth = padding.left + padding.right;
        int paddingHeight = padding.top + padding.bottom;
        int borderWidth = border.left + border.right;
        int borderHeight = border.top + border.bottom;
        return new Rectangle(x - padding.left - border.left, y - hTopTrim - padding.top - border.top,
                width + paddingWidth + borderWidth, height + hTopTrim + paddingHeight + borderHeight);
    }

    private void setInitialSize() {
        int width = display.getBounds().width * INITIAL_SIZE_PERCENT / 100;
        int height = display.getBounds().height * INITIAL_SIZE_PERCENT / 100;
        _setBounds(new Rectangle(0, 0, width, height));
    }

    private int getMinHeightLimit() {
        BoxDimensions border = getBorder();
        BoxDimensions titleBarMargin = getTitleBarMargin();
        int titleBarHeight = getTitleBarHeight();
        return titleBarMargin.top + titleBarMargin.bottom + titleBarHeight + border.top + border.bottom;
    }

    private Rectangle getMenuBounds() {
        Rectangle result = null;
        if (getMenuBar() == null) {
            result = new Rectangle(0, 0, 0, 0);
        } else {
            Rectangle bounds = getBounds();
            int hTop = (style & SWT.TITLE) != 0 ? 1 : 0;
            hTop += getTitleBarHeight();
            BoxDimensions padding = getPadding();
            BoxDimensions border = getBorder();
            int paddingWidth = padding.left + padding.right;
            int borderWidth = border.left + border.right;
            result = new Rectangle(padding.left, hTop + padding.top, bounds.width - paddingWidth - borderWidth,
                    getMenuBarHeight());
        }
        return result;
    }

    @Override
    public int getBorderWidth() {
        return getFullScreen() ? 0 : super.getBorderWidth();
    }

    @Override
    BoxDimensions getBorder() {
        return getFullScreen() ? ZERO : super.getBorder();
    }

    private int getTopTrim() {
        BoxDimensions titleBarMargin = getTitleBarMargin();
        return titleBarMargin.top + titleBarMargin.bottom + getTitleBarHeight() + getMenuBarHeight();
    }

    private int getTitleBarHeight() {
        int result = 0;
        if (!getFullScreen()) {
            result = getThemeAdapter().getTitleBarHeight(this);
        }
        return result;
    }

    private BoxDimensions getTitleBarMargin() {
        if (!getFullScreen()) {
            return getThemeAdapter().getTitleBarMargin(this);
        }
        return CssBoxDimensions.ZERO.dimensions;
    }

    private int getMenuBarHeight() {
        return getThemeAdapter().getMenuBarHeight(this);
    }

    private ShellThemeAdapter getThemeAdapter() {
        return (ShellThemeAdapter) getAdapter(ThemeAdapter.class);
    }

    @Override
    Composite findDeferredControl() {
        return layoutCount > 0 ? this : null;
    }

    @Override
    void updateMode() {
        mode &= ~MODE_MAXIMIZED;
        mode &= ~MODE_MINIMIZED;
        mode &= ~MODE_FULLSCREEN;
    }

    /////////////////////
    // Adaptable override

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getAdapter(Class<T> adapter) {
        if (adapter == IShellAdapter.class) {
            if (shellAdapter == null) {
                shellAdapter = new ShellAdapter();
            }
            return (T) shellAdapter;
        }
        if (adapter == WidgetLCA.class) {
            return (T) ShellLCA.INSTANCE;
        }
        return super.getAdapter(adapter);
    }

    /////////////
    // Enablement

    @Override
    public void setEnabled(boolean enabled) {
        checkWidget();
        if (getEnabled() != enabled) {
            super.setEnabled(enabled);
            if (enabled) {
                if (!restoreFocus()) {
                    traverseGroup(true);
                }
            }
        }
    }

    @Override
    public boolean isEnabled() {
        checkWidget();
        return getEnabled();
    }

    /////////////
    // Visibility

    @Override
    public boolean isVisible() {
        checkWidget();
        return getVisible();
    }

    @Override
    public void setVisible(boolean visible) {
        checkWidget();
        boolean wasVisible = getVisible();
        super.setVisible(visible);
        // Emulate OS behavior: in SWT, a layout is triggered during
        // Shell#setVisible(true)
        if (visible && !wasVisible && !isDisposed()) {
            changed(getChildren());
            layout(true, true);
        }
    }

    /**
     * Moves the receiver to the top of the drawing order for
     * the display on which it was created (so that all other
     * shells on that display, which are not the receiver's
     * children will be drawn behind it), marks it visible,
     * sets the focus and asks the window manager to make the
     * shell active.
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see Control#moveAbove
     * @see Control#setFocus
     * @see Control#setVisible
     * @see Display#getActiveShell
     * @see Decorations#setDefaultButton(Button)
     * @see Shell#setActive
     * @see Shell#forceActive
     */
    public void open() {
        checkWidget();
        // Order of setActiveShell/bringToTop/setVisible is crucial
        display.setActiveShell(this);
        bringToTop();
        setVisible(true);
        if (!restoreFocus() && !traverseGroup(true)) {
            setFocus();
        }
    }

    /**
     * Requests that the window manager close the receiver in
     * the same way it would be closed when the user clicks on
     * the "close box" or performs some other platform specific
     * key or mouse combination that indicates the window
     * should be removed.
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see SWT#Close
     * @see Shell#dispose()
     */
    public void close() {
        checkWidget();
        ProcessActionRunner.add(new Runnable() {
            @Override
            public void run() {
                Event event = new Event();
                notifyListeners(SWT.Close, event);
                if (event.doit) {
                    Shell.this.dispose();
                }
            }
        });
    }

    /**
     * Sets the receiver's alpha value.
     * <p>
     * This operation <!-- requires the operating system's advanced
     * widgets subsystem which --> may not be available on some
     * platforms.
     * </p>
     * @param alpha the alpha value
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @since 1.1
     */
    public void setAlpha(int alpha) {
        checkWidget();
        this.alpha = alpha & 0xFF;
    }

    /**
     * Returns the receiver's alpha value.
     *
     * @return the alpha value
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @since 1.1
     */
    public int getAlpha() {
        checkWidget();
        return alpha;
    }

    /**
     * Sets the receiver's modified state as specified by the argument.
     *
     * @param modified the new modified state for the receiver
     *
     * </ul>
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @since 1.3
     */
    public void setModified(boolean modified) {
        checkWidget();
        this.modified = modified;
    }

    /**
     * Gets the receiver's modified state.
     *
     * </ul>
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @since 1.3
     */
    public boolean getModified() {
        checkWidget();
        return modified;
    }

    /**
     * Sets the receiver's minimum size to the size specified by the arguments.
     * If the new minimum size is larger than the current size of the receiver,
     * the receiver is resized to the new minimum size.
     *
     * @param width the new minimum width for the receiver
     * @param height the new minimum height for the receiver
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @since 1.3
     */
    public void setMinimumSize(int width, int height) {
        checkWidget();
        minWidth = Math.max(MIN_WIDTH_LIMIT, width);
        minHeight = Math.max(getMinHeightLimit(), height);
        Point size = getSize();
        int newWidth = Math.max(size.x, minWidth);
        int newHeight = Math.max(size.y, minHeight);
        if (newWidth != size.x || newHeight != size.y) {
            setSize(newWidth, newHeight);
        }
    }

    /**
     * Sets the receiver's minimum size to the size specified by the argument.
     * If the new minimum size is larger than the current size of the receiver,
     * the receiver is resized to the new minimum size.
     *
     * @param size the new minimum size for the receiver
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
     * </ul>
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @since 1.3
     */
    public void setMinimumSize(Point size) {
        checkWidget();
        if (size == null) {
            error(SWT.ERROR_NULL_ARGUMENT);
        }
        setMinimumSize(size.x, size.y);
    }

    /**
     * Returns a point describing the minimum receiver's size. The
     * x coordinate of the result is the minimum width of the receiver.
     * The y coordinate of the result is the minimum height of the
     * receiver.
     *
     * @return the receiver's size
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @since 1.3
     */
    public Point getMinimumSize() {
        checkWidget();
        return new Point(minWidth, minHeight);
    }

    @Override
    public void setBounds(Rectangle bounds) {
        int newWidth = Math.max(bounds.width, minWidth);
        int newHeight = Math.max(bounds.height, minHeight);
        super.setBounds(new Rectangle(bounds.x, bounds.y, newWidth, newHeight));
    }

    /**
     * Returns the instance of the ToolBar object representing the tool bar that can appear on the
     * trim of the shell. This will return <code>null</code> if the platform does not support tool
     * bars that not part of the content area of the shell, or if the style of the shell does not
     * support a tool bar.
     * <p>
     *
     * @return a ToolBar object representing the window's tool bar or null.
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @since 1.4
     */
    public ToolBar getToolBar() {
        checkWidget();
        return null;
    }

    // ///////////////////////////////////////////////
    // Event listener registration and deregistration

    /**
     * Adds the listener to the collection of listeners who will
     * be notified when operations are performed on the receiver,
     * by sending the listener one of the messages defined in the
     * <code>ShellListener</code> interface.
     *
     * @param listener the listener which should be notified
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see ShellListener
     * @see #removeShellListener
     */
    public void addShellListener(ShellListener listener) {
        checkWidget();
        if (listener == null) {
            error(SWT.ERROR_NULL_ARGUMENT);
        }
        TypedListener typedListener = new TypedListener(listener);
        addListener(SWT.Close, typedListener);
        addListener(SWT.Activate, typedListener);
        addListener(SWT.Deactivate, typedListener);
    }

    /**
     * Removes the listener from the collection of listeners who will
     * be notified when operations are performed on the receiver.
     *
     * @param listener the listener which should no longer be notified
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see ShellListener
     * @see #addShellListener
     */
    public void removeShellListener(ShellListener listener) {
        checkWidget();
        if (listener == null) {
            error(SWT.ERROR_NULL_ARGUMENT);
        }
        removeListener(SWT.Close, listener);
        removeListener(SWT.Activate, listener);
        removeListener(SWT.Deactivate, listener);
    }

    ///////////
    // Disposal

    @Override
    void releaseChildren() {
        super.releaseChildren();
        Shell[] dialogShells = internalGetShells();
        for (int i = 0; i < dialogShells.length; i++) {
            dialogShells[i].dispose();
        }
        Menu[] menus = getAdapter(MenuHolder.class).getMenus();
        for (Menu menu : menus) {
            menu.dispose();
        }
        if (toolTips != null) {
            for (int i = 0; i < toolTips.length; i++) {
                if (toolTips[i] != null) {
                    toolTips[i].dispose();
                }
            }
        }
    }

    @Override
    void releaseParent() {
        // Do not call super.releaseParent()
        // This method would try to remove a child-shell from its ControlHolder
        // but shells are currently not added to the ControlHolder of its parent
        display.removeShell(this);
    }

    ////////////////////////////////////////////////////////////
    // Methods to maintain activeControl and send ActivateEvents

    void setActiveControl(Control activateControl) {
        Control control = activateControl;
        if (control != null && control.isDisposed()) {
            control = null;
        }
        if (lastActive != null && lastActive.isDisposed()) {
            lastActive = null;
        }
        if (lastActive != control) {
            // Compute the list of controls to be activated and deactivated by finding
            // the first common parent control.
            Control[] activate = (control == null) ? new Control[0] : control.getPath();
            Control[] deactivate = lastActive == null ? new Control[0] : lastActive.getPath();
            lastActive = control;

            int index = 0;
            int length = Math.min(activate.length, deactivate.length);
            while (index < length && activate[index] == deactivate[index]) {
                index++;
            }
            // It is possible (but unlikely), that application code could have
            // destroyed some of the widgets. If this happens, keep processing those
            // widgets that are not disposed.
            for (int i = deactivate.length - 1; i >= index; --i) {
                if (!deactivate[i].isDisposed()) {
                    deactivate[i].notifyListeners(SWT.Deactivate, new Event());
                }
            }
            for (int i = activate.length - 1; i >= index; --i) {
                if (!activate[i].isDisposed()) {
                    activate[i].notifyListeners(SWT.Activate, new Event());
                }
            }
        }
    }

    private void bringToTop() {
        Object adapter = display.getAdapter(IDisplayAdapter.class);
        IDisplayAdapter displayAdapter = (IDisplayAdapter) adapter;
        displayAdapter.setFocusControl(this, true);
        // When a Shell is opened client-side the widget that is currently focused
        // loses its focus. This is unwanted in the case that the request that
        // opened the Shell sets the focus to some widget after opening the Shell.
        // The fix is to force the DisplayLCA to issue JavaScript that sets the
        // focus on the server-side focused widget.
        displayAdapter.invalidateFocus();
    }

    ////////////////
    // Tab traversal

    @SuppressWarnings("unused")
    private boolean traverseGroup(boolean next) {
        // TODO [rh] fake implementation
        boolean result = false;
        if (getChildren().length > 0) {
            result = getChildren()[0].forceFocus();
        }
        return result;
    }

    //////////////////////
    // minimize / maximize

    // TODO [rst] Move these methods to class Decorations when implemented

    /**
     * Sets the minimized stated of the receiver.
     * If the argument is <code>true</code> causes the receiver
     * to switch to the minimized state, and if the argument is
     * <code>false</code> and the receiver was previously minimized,
     * causes the receiver to switch back to either the maximized
     * or normal states.
     * <!--
     * <p>
     * Note: The result of intermixing calls to <code>setMaximized(true)</code>
     * and <code>setMinimized(true)</code> will vary by platform. Typically,
     * the behavior will match the platform user's expectations, but not
     * always. This should be avoided if possible.
     * </p>
     * -->
     * @param minimized the new maximized state
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see #setMaximized
     */
    public void setMinimized(boolean minimized) {
        checkWidget();
        if (minimized) {
            mode |= MODE_MINIMIZED;
        } else {
            if ((mode & MODE_MINIMIZED) != 0) {
                setActive();
            }
            mode &= ~MODE_MINIMIZED;
        }
    }

    /**
     * Sets the maximized state of the receiver.
     * If the argument is <code>true</code> causes the receiver
     * to switch to the maximized state, and if the argument is
     * <code>false</code> and the receiver was previously maximized,
     * causes the receiver to switch back to either the minimized
     * or normal states.
     * <!--<p>
     * Note: The result of intermixing calls to <code>setMaximized(true)</code>
     * and <code>setMinimized(true)</code> will vary by platform. Typically,
     * the behavior will match the platform user's expectations, but not
     * always. This should be avoided if possible.
     * </p>
     * -->
     * @param maximized the new maximized state
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see #setMinimized
     */
    public void setMaximized(boolean maximized) {
        checkWidget();
        if ((mode & MODE_FULLSCREEN) == 0) {
            if (maximized) {
                if ((mode & MODE_MAXIMIZED) == 0) {
                    setActive();
                    savedBounds = getBounds();
                    setBounds(display.getBounds(), false);
                }
                mode |= MODE_MAXIMIZED;
                mode &= ~MODE_MINIMIZED;
            } else {
                if ((mode & MODE_MAXIMIZED) != 0) {
                    setBounds(savedBounds, false);
                }
                mode &= ~MODE_MAXIMIZED;
            }
        }
    }

    /**
     * Returns <code>true</code> if the receiver is currently
     * minimized, and false otherwise.
     * <p>
     *
     * @return the minimized state
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see #setMinimized
     */
    public boolean getMinimized() {
        checkWidget();
        return (mode & MODE_MINIMIZED) != 0;
    }

    /**
     * Returns <code>true</code> if the receiver is currently
     * maximized, and false otherwise.
     * <p>
     *
     * @return the maximized state
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see #setMaximized
     */
    public boolean getMaximized() {
        checkWidget();
        return (mode & MODE_FULLSCREEN) == 0 && (mode & MODE_MINIMIZED) == 0 && (mode & MODE_MAXIMIZED) != 0;
    }

    /**
     * Sets the full screen state of the receiver.
     * If the argument is <code>true</code> causes the receiver
     * to switch to the full screen state, and if the argument is
     * <code>false</code> and the receiver was previously switched
     * into full screen state, causes the receiver to switch back
     * to either the maximized or normal states.
     * <p>
     * Note: The result of intermixing calls to <code>setFullScreen(true)</code>,
     * <code>setMaximized(true)</code> and <code>setMinimized(true)</code> will
     * vary by platform. Typically, the behavior will match the platform user's
     * expectations, but not always. This should be avoided if possible.
     * </p>
     *
     * @param fullScreen the new fullscreen state
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @since 1.3
     */
    public void setFullScreen(boolean fullScreen) {
        checkWidget();
        if (((mode & MODE_FULLSCREEN) != 0) != fullScreen) {
            if (fullScreen) {
                setActive();
                if ((mode & MODE_MAXIMIZED) == 0) {
                    savedBounds = getBounds();
                }
                setBounds(display.getBounds(), false);
                mode |= MODE_FULLSCREEN;
                mode &= ~MODE_MINIMIZED;
            } else {
                if ((mode & MODE_MAXIMIZED) == 0) {
                    setBounds(savedBounds, false);
                }
                mode &= ~MODE_FULLSCREEN;
            }
            layout();
        }
    }

    /**
     * Returns <code>true</code> if the receiver is currently
     * in fullscreen state, and false otherwise.
     * <p>
     *
     * @return the fullscreen state
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @since 1.3
     */
    public boolean getFullScreen() {
        checkWidget();
        return (mode & MODE_FULLSCREEN) != 0;
    }

    void fixShell(Shell newShell, Control control) {
        if (newShell != this && control == lastActive) {
            setActiveControl(null);
        }
    }

    ///////////////////
    // ToolTips support

    void createToolTip(ToolTip toolTip) {
        int id = 0;
        if (toolTips == null) {
            toolTips = new ToolTip[4];
        }
        while (id < toolTips.length && toolTips[id] != null) {
            id++;
        }
        if (id == toolTips.length) {
            ToolTip[] newToolTips = new ToolTip[toolTips.length + 4];
            System.arraycopy(toolTips, 0, newToolTips, 0, toolTips.length);
            toolTips = newToolTips;
        }
        toolTips[id] = toolTip;
    }

    void destroyToolTip(ToolTip toolTip) {
        boolean found = false;
        for (int i = 0; !found && i < toolTips.length; i++) {
            if (toolTips[i] == toolTip) {
                toolTips[i] = null;
                found = true;
            }
        }
    }

    private ToolTip[] getToolTips() {
        ToolTip[] result;
        if (toolTips == null) {
            result = new ToolTip[0];
        } else {
            int count = 0;
            for (int i = 0; i < toolTips.length; i++) {
                if (toolTips[i] != null) {
                    count++;
                }
            }
            result = new ToolTip[count];
            int index = 0;
            for (int i = 0; i < toolTips.length; i++) {
                if (toolTips[i] != null) {
                    result[index] = toolTips[i];
                    index++;
                }
            }
        }
        return result;
    }

    private static Shell checkParent(Shell parent) {
        if (parent != null && parent.isDisposed()) {
            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
        }
        return parent;
    }

    ///////////////////
    // Skinning support

    @Override
    void reskinChildren(int flags) {
        Shell[] shells = getShells();
        for (int i = 0; i < shells.length; i++) {
            Shell shell = shells[i];
            if (shell != null) {
                shell.reskin(flags);
            }
        }
        super.reskinChildren(flags);
    }
}