/*******************************************************************************
 * Copyright (c) 2000, 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jface.window;

import java.util.ArrayList;

import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.util.Geometry;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.ShellListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Monitor;
import org.eclipse.swt.widgets.Shell;

/**
 * A JFace window is an object that has no visual representation (no widgets)
 * until it is told to open.
 * <p>
 * Creating a window involves the following steps:
 * <ul>
 * <li>creating an instance of a concrete subclass of <code>Window</code>
 * </li>
 * <li>creating the window's shell and widget tree by calling
 * <code>create</code> (optional)</li>
 * <li>assigning the window to a window manager using
 * <code>WindowManager.add</code> (optional)</li>
 * <li>opening the window by calling <code>open</code></li>
 * </ul>
 * Opening the window will create its shell and widget tree if they have not
 * already been created. When the window is closed, the shell and widget tree
 * are disposed of and are no longer referenced, and the window is automatically
 * removed from its window manager. A window may be reopened.
 * </p>
 * <p>
 * The JFace window framework (this package) consists of this class,
 * <code>Window</code>, the abstract base of all windows, and one concrete
 * window classes (<code>ApplicationWindow</code>) which may also be
 * subclassed. Clients may define additional window subclasses as required.
 * </p>
 * <p>
 * The <code>Window</code> class provides methods that subclasses may 
 * override to configure the window, including:
 * <ul>
 * <li><code>close</code>- extend to free other SWT resources</li>
 * <li><code>configureShell</code>- extend or reimplement to set shell
 * properties before window opens</li>
 * <li><code>createContents</code>- extend or reimplement to create controls
 * before window opens</li>
 * <li><code>getInitialSize</code>- reimplement to give the initial size for
 * the shell</li>
 * <li><code>getInitialLocation</code>- reimplement to give the initial
 * location for the shell</li>
 * <li><code>getShellListener</code>- extend or reimplement to receive shell
 * events</li>
 * <li><code>handleFontChange</code>- reimplement to respond to font changes
 * </li>
 * <li><code>handleShellCloseEvent</code>- extend or reimplement to handle
 * shell closings</li>
 * </ul>
 * </p>
 */
public abstract class Window implements IShellProvider {

	/**
	 * Standard return code constant (value 0) indicating that the window was
	 * opened.
	 * 
	 * @see #open
	 */
	public static final int OK = 0;

	/**
	 * Standard return code constant (value 1) indicating that the window was
	 * canceled.
	 * 
	 * @see #open
	 */
	public static final int CANCEL = 1;

	/**
	 * An array of images to be used for the window. It is expected that the
	 * array will contain the same icon rendered at different resolutions.
	 */
	private static Image[] defaultImages;

	/**
	 * This interface defines a Exception Handler which can be set as a global
	 * handler and will be called if an exception happens in the event loop.
	 */
	public static interface IExceptionHandler {
		/**
		 * Handle the exception.
		 * 
		 * @param t
		 *            The exception that occured.
		 */
		public void handleException(Throwable t);
	}

	/**
	 * Defines a default exception handler.
	 */
	private static class DefaultExceptionHandler implements IExceptionHandler {
		/*
		 * (non-Javadoc)
		 * 
		 * @see org.eclipse.jface.window.Window.IExceptionHandler#handleException(java.lang.Throwable)
		 */
		public void handleException(Throwable t) {
			if (t instanceof ThreadDeath) {
				// Don't catch ThreadDeath as this is a normal occurrence when
				// the thread dies
				throw (ThreadDeath) t;
			}
			// Try to keep running.
			t.printStackTrace();
		}
	}

	/**
	 * The exception handler for this application.
	 */
	private static IExceptionHandler exceptionHandler = new DefaultExceptionHandler();
	
	/**
	 * The default orientation of the window. By default
	 * it is SWT#NONE but it can also be SWT#LEFT_TO_RIGHT
	 * or SWT#RIGHT_TO_LEFT
	 */
	private static int orientation = SWT.NONE;

    /**
     * Object used to locate the default parent for modal shells
     */
    private static IShellProvider defaultModalParent = new IShellProvider() {
        public Shell getShell() {
            Display d = Display.getCurrent();
            
            if (d == null) {
                return null;
            }

            Shell parent = d.getActiveShell();
            
            // Make sure we don't pick a parent that has a modal child (this can lock the app)
            if (parent == null) {
                // If this is a top-level window, then there must not be any open modal windows.
                parent = getModalChild(Display.getCurrent().getShells());
            } else {
                // If we picked a parent with a modal child, use the modal child instead
                Shell modalChild = getModalChild(parent.getShells());
                if (modalChild != null) {
                    parent = modalChild;
                }
            }
            
            return parent;
        }
    };
    
	/**
	 * Object that returns the parent shell.
	 */
	private IShellProvider parentShell;

	/**
	 * Shell style bits.
	 * 
	 * @see #setShellStyle
	 */
	private int shellStyle = SWT.SHELL_TRIM;

	/**
	 * Window manager, or <code>null</code> if none.
	 * 
	 * @see #setWindowManager
	 */
	private WindowManager windowManager;

	/**
	 * Window shell, or <code>null</code> if none.
	 */
	private Shell shell;

	/**
	 * Top level SWT control, or <code>null</code> if none
	 */
	private Control contents;

	/**
	 * Window return code; initially <code>OK</code>.
	 * 
	 * @see #setReturnCode
	 */
	private int returnCode = OK;

	/**
	 * <code>true</code> if the <code>open</code> method should not return
	 * until the window closes, and <code>false</code> if the
	 * <code>open</code> method should return immediately; initially
	 * <code>false</code> (non-blocking).
	 * 
	 * @see #setBlockOnOpen
	 */
	private boolean block = false;

	/**
	 * Internal class for informing this window when fonts change.
	 */
	private class FontChangeListener implements IPropertyChangeListener {
		public void propertyChange(PropertyChangeEvent event) {
			handleFontChange(event);
		}
	}

	/**
	 * Internal font change listener.
	 */
	private FontChangeListener fontChangeListener;

	/**
	 * Internal fields to detect if shell size has been set
	 */
	private boolean resizeHasOccurred = false;

	private Listener resizeListener;
 
	/**
	 * Creates a window instance, whose shell will be created under the given
	 * parent shell. Note that the window will have no visual representation
	 * until it is told to open. By default, <code>open</code> does not block.
	 * 
	 * @param parentShell
	 *            the parent shell, or <code>null</code> to create a top-level
	 *            shell. Try passing "(Shell)null" to this method instead of "null"
     *            if your compiler complains about an ambiguity error.
	 * @see #setBlockOnOpen
	 * @see #getDefaultOrientation()
	 */
	protected Window(Shell parentShell) {
        this(new SameShellProvider(parentShell));
        
        if(parentShell == null) {
			setShellStyle(getShellStyle() | getDefaultOrientation());
		}
	}
    
    /**
     * Creates a new window which will create its shell as a child of whatever
     * the given shellProvider returns.
     * 
     * @param shellProvider object that will return the current parent shell. Not null.
     * 
     * @since 3.1
     */
    protected Window(IShellProvider shellProvider) {
        Assert.isNotNull(shellProvider);
        this.parentShell = shellProvider;   
    }

	/**
	 * Determines if the window should handle the close event or do nothing.
	 * <p>
	 * The default implementation of this framework method returns
	 * <code>true</code>, which will allow the
	 * <code>handleShellCloseEvent</code> method to be called. Subclasses may
	 * extend or reimplement.
	 * </p>
	 * 
	 * @return whether the window should handle the close event.
	 */
	protected boolean canHandleShellCloseEvent() {
		return true;
	}

	/**
	 * Closes this window, disposes its shell, and removes this window from its
	 * window manager (if it has one).
	 * <p>
	 * This framework method may be extended (<code>super.close</code> must
	 * be called).
	 * </p>
	 * <p>
	 *  Note that in order to prevent recursive calls to this method 
	 *  it does not call <code>Shell#close()</code>. As a result <code>ShellListener</code>s 
	 *  will not receive a <code>shellClosed</code> event.
	 *  </p>
	 * 
	 * @return <code>true</code> if the window is (or was already) closed, and
	 *         <code>false</code> if it is still open
	 */
	public boolean close() {
		
		// stop listening for font changes
		if (fontChangeListener != null) {
			JFaceResources.getFontRegistry().removeListener(fontChangeListener);
			fontChangeListener = null;
		}
		
		// remove this window from a window manager if it has one
		if (windowManager != null) {
			windowManager.remove(this);
			windowManager = null;
		}

		if (shell == null || shell.isDisposed()) {
			return true;
		}

		// If we "close" the shell recursion will occur.
		// Instead, we need to "dispose" the shell to remove it from the
		// display.
		shell.dispose();
		shell = null;
		contents = null;

		return true;
	}

	/**
	 * Configures the given shell in preparation for opening this window in it.
	 * <p>
	 * The default implementation of this framework method sets the shell's
	 * image and gives it a grid layout. Subclasses may extend or reimplement.
	 * </p>
	 * 
	 * @param newShell
	 *            the shell
	 */
	protected void configureShell(Shell newShell) {

		// The single image version of this code had a comment related to bug
		// 46624,
		// and some code that did nothing if the stored image was already
		// disposed.
		// The equivalent in the multi-image version seems to be to remove the
		// disposed images from the array passed to the shell.
		if (defaultImages != null && defaultImages.length > 0) {
			ArrayList nonDisposedImages = new ArrayList(defaultImages.length);
			for (int i = 0; i < defaultImages.length; ++i) {
				if (defaultImages[i] != null && !defaultImages[i].isDisposed()) {
					nonDisposedImages.add(defaultImages[i]);
				}
			}

			if (nonDisposedImages.size() <= 0) {
				System.err.println("Window.configureShell: images disposed"); //$NON-NLS-1$
			} else {
				Image[] array = new Image[nonDisposedImages.size()];
				nonDisposedImages.toArray(array);
				newShell.setImages(array);
			}
		}

		Layout layout = getLayout();
		if (layout != null) {
			newShell.setLayout(layout);
		}
	}

	/**
	 * Creates the layout for the shell. The layout created here will be
	 * attached to the composite passed into createContents. The default
	 * implementation returns a GridLayout with no margins. Subclasses that
	 * change the layout type by overriding this method should also override
	 * createContents.
	 * 
	 * <p>
	 * A return value of null indicates that no layout should be attached to the
	 * composite. In this case, the layout may be attached within
	 * createContents.
	 * </p>
	 * 
	 * @return a newly created Layout or null if no layout should be attached.
	 * @since 3.0
	 */
	protected Layout getLayout() {
		GridLayout layout = new GridLayout();
		layout.marginHeight = 0;
		layout.marginWidth = 0;
		return layout;
	}

	/**
	 * Constrain the shell size to be no larger than the display bounds.
	 * 
	 * @since 2.0
	 */
	protected void constrainShellSize() {
		// limit the shell size to the display size
		Rectangle bounds = shell.getBounds();
		Rectangle constrained = getConstrainedShellBounds(bounds);
		if (!bounds.equals(constrained)) {
			shell.setBounds(constrained);
		}
	}

	/**
	 * Creates this window's widgetry in a new top-level shell.
	 * <p>
	 * The default implementation of this framework method creates this window's
	 * shell (by calling <code>createShell</code>), and its controls (by
	 * calling <code>createContents</code>), then initializes this window's
	 * shell bounds (by calling <code>initializeBounds</code>).
	 * </p>
	 */
	public void create() {
		shell = createShell();
		contents = createContents(shell);

		//initialize the bounds of the shell to that appropriate for the
		// contents
		initializeBounds();
	}

	/**
	 * Creates and returns this window's contents. Subclasses may attach any
	 * number of children to the parent. As a convenience, the return value of
	 * this method will be remembered and returned by subsequent calls to
	 * getContents(). Subclasses may modify the parent's layout if they overload
	 * getLayout() to return null.
	 * 
	 * <p>
	 * It is common practise to create and return a single composite that
	 * contains the entire window contents.
	 * </p>
	 * 
	 * <p>
	 * The default implementation of this framework method creates an instance
	 * of <code>Composite</code>. Subclasses may override.
	 * </p>
	 * 
	 * @param parent
	 *            the parent composite for the controls in this window. The type
	 *            of layout used is determined by getLayout()
	 * 
	 * @return the control that will be returned by subsequent calls to
	 *         getContents()
	 */
	protected Control createContents(Composite parent) {
		// by default, just create a composite
		return new Composite(parent, SWT.NONE);
	}

	/**
	 * Creates and returns this window's shell.
	 * <p>
	 * This method creates a new shell and configures
	 * it using <code>configureShell</code>. Subclasses
	 * should  override <code>configureShell</code> if the
	 * shell needs to be customized.
	 * </p>
	 * 
	 * @return the shell
	 */
	protected final Shell createShell() {

		Shell newParent = getParentShell();
		if(newParent != null &&  newParent.isDisposed()){
			parentShell = new SameShellProvider(null);
			newParent = getParentShell();//Find a better parent
		}
		
		//Create the shell
		Shell newShell = new Shell(newParent, getShellStyle());

		resizeListener = new Listener() {
			public void handleEvent(Event e) {
				resizeHasOccurred = true;
			}
		};

		newShell.addListener(SWT.Resize, resizeListener);
		newShell.setData(this);

		//Add a listener
		newShell.addShellListener(getShellListener());

		//Set the layout
		configureShell(newShell);

		//Register for font changes
		if (fontChangeListener == null) {
			fontChangeListener = new FontChangeListener();
		}
		JFaceResources.getFontRegistry().addListener(fontChangeListener);

		return newShell;
	}

	/**
	 * Returns the top level control for this window. The parent of this control
	 * is the shell.
	 * 
	 * @return the top level control, or <code>null</code> if this window's
	 *         control has not been created yet
	 */
	protected Control getContents() {
		return contents;
	}

	/**
	 * Returns the default image. This is the image that will be used for
	 * windows that have no shell image at the time they are opened. There is no
	 * default image unless one is installed via <code>setDefaultImage</code>.
	 * 
	 * @return the default image, or <code>null</code> if none
	 * @see #setDefaultImage
	 */
	public static Image getDefaultImage() {
		return (defaultImages == null || defaultImages.length < 1) ? null
				: defaultImages[0];
	}

	/**
	 * Returns the array of default images to use for newly opened windows. It
	 * is expected that the array will contain the same icon rendered at
	 * different resolutions.
	 * 
	 * @see org.eclipse.swt.widgets.Decorations#setImages(org.eclipse.swt.graphics.Image[])
	 * 
	 * @return the array of images to be used when a new window is opened
	 * @see #setDefaultImages
	 * @since 3.0
	 */
	public static Image[] getDefaultImages() {
		return (defaultImages == null ? new Image[0] : defaultImages);
	}

	/**
	 * Returns the initial location to use for the shell. The default
	 * implementation centers the shell horizontally (1/2 of the difference to
	 * the left and 1/2 to the right) and vertically (1/3 above and 2/3 below)
	 * relative to the parent shell, or display bounds if there is no parent
	 * shell.
	 * 
	 * @param initialSize
	 *            the initial size of the shell, as returned by
	 *            <code>getInitialSize</code>.
	 * @return the initial location of the shell
	 */
	protected Point getInitialLocation(Point initialSize) {
		Composite parent = shell.getParent();

		Monitor monitor = shell.getDisplay().getPrimaryMonitor();
		if (parent != null) {
			monitor = parent.getMonitor();
		}

		Rectangle monitorBounds = monitor.getClientArea();
		Point centerPoint;
		if (parent != null) {
			centerPoint = Geometry.centerPoint(parent.getBounds());
		} else {
			centerPoint = Geometry.centerPoint(monitorBounds);
		}

		return new Point(centerPoint.x - (initialSize.x / 2), Math.max(
				monitorBounds.y, Math.min(centerPoint.y
						- (initialSize.y * 2 / 3), monitorBounds.y
						+ monitorBounds.height - initialSize.y)));
	}

	/**
	 * Returns the initial size to use for the shell. The default implementation
	 * returns the preferred size of the shell, using
	 * <code>Shell.computeSize(SWT.DEFAULT, SWT.DEFAULT, true)</code>.
	 * 
	 * @return the initial size of the shell
	 */
	protected Point getInitialSize() {
		return shell.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
	}

    /**
     * Returns the most specific modal child from the given list of Shells. 
     * 
     * @param toSearch shells to search for modal children
     * @return the most specific modal child, or null if none
     * 
     * @since 3.1
     */
    private static Shell getModalChild(Shell[] toSearch) {
        int modal = SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL | SWT.PRIMARY_MODAL;
        
        for (int i = toSearch.length - 1; i >= 0; i--) {
            Shell shell = toSearch[i];
            
            // Check if this shell has a modal child
            Shell[] children = shell.getShells();
            Shell modalChild = getModalChild(children);
            if (modalChild != null) {
                return modalChild;
            }
            
            // If not, check if this shell is modal itself
            if (shell.isVisible() && (shell.getStyle() & modal) != 0) {
                return shell;
            }
        }
        
        return null;
    }
    
	/**
	 * Returns parent shell, under which this window's shell is created.
	 * 
	 * @return the parent shell, or <code>null</code> if there is no parent
	 *         shell
	 */
	protected Shell getParentShell() {
        Shell parent = parentShell.getShell();
        
        int modal = SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL | SWT.PRIMARY_MODAL;
        
        if ((getShellStyle() & modal) != 0) {
            // If this is a modal shell with no parent, pick a shell using defaultModalParent.
            if (parent == null) {
                parent = defaultModalParent.getShell();
            }
        }
        
        return parent;
	}

	/**
	 * Returns this window's return code. A window's return codes are
	 * window-specific, although two standard return codes are predefined:
	 * <code>OK</code> and <code>CANCEL</code>.
	 * 
	 * @return the return code
	 */
	public int getReturnCode() {
		return returnCode;
	}

	/**
	 * Returns this window's shell.
	 * 
	 * @return this window's shell, or <code>null</code> if this window's
	 *         shell has not been created yet
	 */
	public Shell getShell() {
		return shell;
	}

	/**
	 * Returns a shell listener. This shell listener gets registered with this
	 * window's shell.
	 * <p>
	 * The default implementation of this framework method returns a new
	 * listener that makes this window the active window for its window manager
	 * (if it has one) when the shell is activated, and calls the framework
	 * method <code>handleShellCloseEvent</code> when the shell is closed.
	 * Subclasses may extend or reimplement.
	 * </p>
	 * 
	 * @return a shell listener
	 */
	protected ShellListener getShellListener() {
		return new ShellAdapter() {
			public void shellClosed(ShellEvent event) {
				event.doit = false; // don't close now
				if (canHandleShellCloseEvent()) {
					handleShellCloseEvent();
				}
			}
		};
	}

	/**
	 * Returns the shell style bits.
	 * <p>
	 * The default value is <code>SWT.CLOSE|SWT.MIN|SWT.MAX|SWT.RESIZE</code>.
	 * Subclassers should call <code>setShellStyle</code> to change this
	 * value, rather than overriding this method.
	 * </p>
	 * 
	 * @return the shell style bits
	 */
	protected int getShellStyle() {
		return shellStyle;
	}

	/**
	 * Returns the window manager of this window.
	 * 
	 * @return the WindowManager, or <code>null</code> if none
	 */
	public WindowManager getWindowManager() {
		return windowManager;
	}

	/**
	 * Notifies of a font property change.
	 * <p>
	 * The default implementation of this framework method does nothing.
	 * Subclasses may reimplement.
	 * </p>
	 * 
	 * @param event
	 *            the property change event detailing what changed
	 */
	protected void handleFontChange(PropertyChangeEvent event) {
		// do nothing
	}

	/**
	 * Notifies that the window's close button was pressed, the close menu was
	 * selected, or the ESCAPE key pressed.
	 * <p>
	 * The default implementation of this framework method sets the window's
	 * return code to <code>CANCEL</code> and closes the window using
	 * <code>close</code>. Subclasses may extend or reimplement.
	 * </p>
	 */
	protected void handleShellCloseEvent() {
		setReturnCode(CANCEL);
		close();
	}

	/**
	 * Initializes the location and size of this window's SWT shell after it has
	 * been created.
	 * <p>
	 * This framework method is called by the <code>create</code> framework
	 * method. The default implementation calls <code>getInitialSize</code>
	 * and <code>getInitialLocation</code> and passes the results to
	 * <code>Shell.setBounds</code>. This is only done if the bounds of the
	 * shell have not already been modified. Subclasses may extend or
	 * reimplement.
	 * </p>
	 */
	protected void initializeBounds() {
		if (resizeListener != null) {
			shell.removeListener(SWT.Resize, resizeListener);
		}
		if (resizeHasOccurred) { // Check if shell size has been set already.
			return;
		}

		Point size = getInitialSize();
		Point location = getInitialLocation(size);
		shell.setBounds(getConstrainedShellBounds(new Rectangle(location.x,
				location.y, size.x, size.y)));
	}

	/**
	 * Opens this window, creating it first if it has not yet been created.
	 * <p>
	 * If this window has been configured to block on open (
	 * <code>setBlockOnOpen</code>), this method waits until the window is
	 * closed by the end user, and then it returns the window's return code;
	 * otherwise, this method returns immediately. A window's return codes are
	 * window-specific, although two standard return codes are predefined:
	 * <code>OK</code> and <code>CANCEL</code>.
	 * </p>
	 * 
	 * @return the return code
	 * 
	 * @see #create()
	 */
	public int open() {
        
		if (shell == null || shell.isDisposed()) {
            shell = null;
			// create the window
			create();
		}

		// limit the shell size to the display size
		constrainShellSize();

		// open the window
		shell.open();

		// run the event loop if specified
		if (block) {
			runEventLoop(shell);
		}

		return returnCode;
	}

	/**
	 * Runs the event loop for the given shell.
	 * 
	 * @param loopShell
	 *            the shell
	 */
	private void runEventLoop(Shell loopShell) {

		//Use the display provided by the shell if possible
		Display display;
		if (shell == null) {
			display = Display.getCurrent();
		} else {
			display = loopShell.getDisplay();
		}

		while (loopShell != null && !loopShell.isDisposed()) {
			try {
				if (!display.readAndDispatch()) {
					display.sleep();
				}
			} catch (Throwable e) {
				exceptionHandler.handleException(e);
			}
		}
		if (!display.isDisposed()) display.update();
	}

	/**
	 * Sets whether the <code>open</code> method should block until the window
	 * closes.
	 * 
	 * @param shouldBlock
	 *            <code>true</code> if the <code>open</code> method should
	 *            not return until the window closes, and <code>false</code>
	 *            if the <code>open</code> method should return immediately
	 */
	public void setBlockOnOpen(boolean shouldBlock) {
		block = shouldBlock;
	}

	/**
	 * Sets the default image. This is the image that will be used for windows
	 * that have no shell image at the time they are opened. There is no default
	 * image unless one is installed via this method.
	 * 
	 * @param image
	 *            the default image, or <code>null</code> if none
	 */
	public static void setDefaultImage(Image image) {
		defaultImages = image == null ? null : new Image[] { image };
	}

	/**
	 * Sets the array of default images to use for newly opened windows. It is
	 * expected that the array will contain the same icon rendered at different
	 * resolutions.
	 * 
	 * @see org.eclipse.swt.widgets.Decorations#setImages(org.eclipse.swt.graphics.Image[])
	 * 
	 * @param images
	 *            the array of images to be used when this window is opened
	 * @since 3.0
	 */
	public static void setDefaultImages(Image[] images) {
		Image[] newArray = new Image[images.length];
		System.arraycopy(images, 0, newArray, 0, newArray.length);
		defaultImages = newArray;
	}
	
	/**
     * Changes the parent shell. This is only safe to use when the shell is not
     * yet realized (i.e., created). Once the shell is created, it must be
     * disposed (i.e., closed) before this method can be called.
     * 
     * @param newParentShell
     *            The new parent shell; this value may be <code>null</code> if
     *            there is to be no parent.
     * @since 3.1
     */
    protected void setParentShell(final Shell newParentShell) {
        Assert.isTrue((shell == null), "There must not be an existing shell."); //$NON-NLS-1$
        parentShell = new SameShellProvider(newParentShell);
    }

	/**
	 * Sets this window's return code. The return code is automatically returned
	 * by <code>open</code> if block on open is enabled. For non-blocking
	 * opens, the return code needs to be retrieved manually using
	 * <code>getReturnCode</code>.
	 * 
	 * @param code
	 *            the return code
	 */
	protected void setReturnCode(int code) {
		returnCode = code;
	}

	/**
	 * Returns the monitor whose client area contains the given point. If no
	 * monitor contains the point, returns the monitor that is closest to the
	 * point. If this is ever made public, it should be moved into a separate
	 * utility class.
	 * 
	 * @param toSearch
	 *            point to find (display coordinates)
	 * @param toFind
	 *            point to find (display coordinates)
	 * @return the montor closest to the given point
	 */
	private static Monitor getClosestMonitor(Display toSearch, Point toFind) {
		int closest = Integer.MAX_VALUE;

		Monitor[] monitors = toSearch.getMonitors();
		Monitor result = monitors[0];

		for (int idx = 0; idx < monitors.length; idx++) {
			Monitor current = monitors[idx];

			Rectangle clientArea = current.getClientArea();

			if (clientArea.contains(toFind)) {
				return current;
			}

			int distance = Geometry.distanceSquared(Geometry
					.centerPoint(clientArea), toFind);
			if (distance < closest) {
				closest = distance;
				result = current;
			}
		}

		return result;
	}

	/**
	 * Given the desired position of the window, this method returns an adjusted
	 * position such that the window is no larger than its monitor, and does not
	 * extend beyond the edge of the monitor. This is used for computing the
	 * initial window position, and subclasses can use this as a utility method
	 * if they want to limit the region in which the window may be moved.
	 * 
	 * @param preferredSize
	 *            the preferred position of the window
	 * @return a rectangle as close as possible to preferredSize that does not
	 *         extend outside the monitor
	 * 
	 * @since 3.0
	 */
	protected Rectangle getConstrainedShellBounds(Rectangle preferredSize) {
		Rectangle result = new Rectangle(preferredSize.x, preferredSize.y,
				preferredSize.width, preferredSize.height);

		Monitor mon = getClosestMonitor(getShell().getDisplay(), Geometry
				.centerPoint(result));

		Rectangle bounds = mon.getClientArea();

		if (result.height > bounds.height) {
			result.height = bounds.height;
		}

		if (result.width > bounds.width) {
			result.width = bounds.width;
		}

		result.x = Math.max(bounds.x, Math.min(result.x, bounds.x
				+ bounds.width - result.width));
		result.y = Math.max(bounds.y, Math.min(result.y, bounds.y
				+ bounds.height - result.height));

		return result;
	}

	/**
	 * Sets the shell style bits. This method has no effect after the shell is
	 * created.
	 * <p>
	 * The shell style bits are used by the framework method
	 * <code>createShell</code> when creating this window's shell.
	 * </p>
	 * 
	 * @param newShellStyle
	 *            the new shell style bits
	 */
	protected void setShellStyle(int newShellStyle) {
		shellStyle = newShellStyle;
	}

	/**
	 * Sets the window manager of this window.
	 * <p>
	 * Note that this method is used by <code>WindowManager</code> to maintain
	 * a backpointer. Clients must not call the method directly.
	 * </p>
	 * 
	 * @param manager
	 *            the window manager, or <code>null</code> if none
	 */
	public void setWindowManager(WindowManager manager) {
		windowManager = manager;

		// Code to detect invalid usage

		if (manager != null) {
			Window[] windows = manager.getWindows();
			for (int i = 0; i < windows.length; i++) {
				if (windows[i] == this) {
					return;
				}
			}
			manager.add(this);
		}
	}

	/**
	 * Sets the exception handler for this application.
	 * <p>
	 * Note that the handler may only be set once.  Subsequent calls to this method will be
	 * ignored.
	 * <p>
	 * 
	 * @param handler
	 *            the exception handler for the application.
	 */
	public static void setExceptionHandler(IExceptionHandler handler) {
		if (exceptionHandler instanceof DefaultExceptionHandler) {
			exceptionHandler = handler;
		}
	}
    
    /**
     * Sets the default parent for modal Windows. This will be used to locate
     * the parent for any modal Window constructed with a null parent.
     * 
     * @param provider shell provider that will be used to locate the parent shell
     * whenever a Window is created with a null parent
     * @since 3.1
     */
    public static void setDefaultModalParent(IShellProvider provider) {
        defaultModalParent = provider;
    }
    
	/**
	 * Gets the default orientation for windows. If it is not
	 * set the default value will be unspecified (SWT#NONE).
	 * 
	 * 
	 * @return SWT#NONE, SWT.RIGHT_TO_LEFT or SWT.LEFT_TO_RIGHT
	 * @see SWT#RIGHT_TO_LEFT
	 * @see SWT#LEFT_TO_RIGHT
	 * @see SWT#NONE
	 * @since 3.1
	 */
	public static int getDefaultOrientation() {
		return orientation;

	}

	/**
	 * Sets the default orientation of windows.
	 * @param defaultOrientation one of 
	 * 	SWT#RIGHT_TO_LEFT, SWT#LEFT_TO_RIGHT ,SWT#NONE
	 * @see SWT#RIGHT_TO_LEFT
	 * @see SWT#LEFT_TO_RIGHT
	 * @see SWT#NONE
	 * @since 3.1
	 */
	public static void setDefaultOrientation(int defaultOrientation) {
		orientation = defaultOrientation;
		
	}

}
