All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jface.window.Window Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2000, 2015 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.jface.window;

import java.util.ArrayList;

import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.Geometry;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.util.Util;
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.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.
 * 

* Creating a window involves the following steps: *

*
    *
  • creating an instance of a concrete subclass of Window
  • *
  • creating the window's shell and widget tree by calling * create (optional)
  • *
  • assigning the window to a window manager using * WindowManager.add (optional)
  • *
  • opening the window by calling open
  • *
*

* 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. *

*

* The JFace window framework (this package) consists of this class, * Window, the abstract base of all windows, and one concrete * window classes (ApplicationWindow) which may also be subclassed. * Clients may define additional window subclasses as required. *

*

* The Window class provides methods that subclasses may override * to configure the window, including: *

*
    *
  • close- extend to free other SWT resources
  • *
  • configureShell- extend or reimplement to set shell * properties before window opens
  • *
  • createContents- extend or reimplement to create controls * before window opens
  • *
  • getInitialSize- reimplement to give the initial size for the * shell
  • *
  • getInitialLocation- reimplement to give the initial location * for the shell
  • *
  • getShellListener- extend or reimplement to receive shell * events
  • *
  • handleFontChange- reimplement to respond to font changes *
  • *
  • handleShellCloseEvent- extend or reimplement to handle shell * closings
  • *
*/ 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 occurred. */ public void handleException(Throwable t); } /** * Defines a default exception handler. */ private static class DefaultExceptionHandler implements IExceptionHandler { @Override 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 = () -> { 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 null if none. * * @see #setWindowManager */ private WindowManager windowManager; /** * Window shell, or null if none. */ private Shell shell; /** * Top level SWT control, or null if none */ private Control contents; /** * Window return code; initially OK. * * @see #setReturnCode */ private int returnCode = OK; /** * true if the open method should not return * until the window closes, and false if the * open method should return immediately; initially * false (non-blocking). * * @see #setBlockOnOpen */ private boolean block = false; /** * Internal class for informing this window when fonts change. */ private class FontChangeListener implements IPropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent event) { handleFontChange(event); } } /** * Internal font change listener. */ private FontChangeListener fontChangeListener; /** * Internal fields to detect if shell size has been set * @since 3.9 */ protected 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, open does not block. * * @param parentShell * the parent shell, or null 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. *

* The default implementation of this framework method returns * true, which will allow the * handleShellCloseEvent method to be called. Subclasses may * extend or reimplement. *

* * @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). *

* This framework method may be extended (super.close must * be called). *

*

* Note that in order to prevent recursive calls to this method * it does not call Shell#close(). As a result ShellListeners * will not receive a shellClosed event. *

* * @return true if the window is (or was already) closed, and * false 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. *

* The default implementation of this framework method sets the shell's * image and gives it a grid layout. Subclasses may extend or reimplement. *

* * @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 (Image defaultImage : defaultImages) { if (defaultImage != null && !defaultImage.isDisposed()) { nonDisposedImages.add(defaultImage); } } 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. * *

* 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. *

* * @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. *

* The default implementation of this framework method creates this window's * shell (by calling createShell), and its controls (by * calling createContents), then initializes this window's * shell bounds (by calling initializeBounds). *

*/ 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. * *

* It is common practice to create and return a single composite that * contains the entire window contents. *

* *

* The default implementation of this framework method creates an instance * of Composite. Subclasses may override. *

* * @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. *

* This method creates a new shell and configures * it using configureShell. Subclasses * should override configureShell if the * shell needs to be customized. *

* * @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 = 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 null 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 setDefaultImage. * * @return the default image, or null 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 * getInitialSize. * @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 * Shell.computeSize(SWT.DEFAULT, SWT.DEFAULT, true). * * @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 null 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: * OK and CANCEL. * * @return the return code */ public int getReturnCode() { return returnCode; } /** * Returns this window's shell. * * @return this window's shell, or null if this window's * shell has not been created yet */ @Override public Shell getShell() { return shell; } /** * Returns a shell listener. This shell listener gets registered with this * window's shell. *

* 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 handleShellCloseEvent when the shell is closed. * Subclasses may extend or reimplement. *

* * @return a shell listener */ protected ShellListener getShellListener() { return new ShellAdapter() { @Override public void shellClosed(ShellEvent event) { event.doit = false; // don't close now if (canHandleShellCloseEvent()) { handleShellCloseEvent(); } } }; } /** * Returns the shell style bits. *

* The default value is SWT.CLOSE|SWT.MIN|SWT.MAX|SWT.RESIZE. * Subclasses should call setShellStyle to change this value, * rather than overriding this method. *

* * @return the shell style bits */ protected int getShellStyle() { return shellStyle; } /** * Returns the window manager of this window. * * @return the WindowManager, or null if none */ public WindowManager getWindowManager() { return windowManager; } /** * Notifies of a font property change. *

* The default implementation of this framework method does nothing. * Subclasses may reimplement. *

* * @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. *

* The default implementation of this framework method sets the window's * return code to CANCEL and closes the window using * close. Subclasses may extend or reimplement. *

*/ protected void handleShellCloseEvent() { setReturnCode(CANCEL); close(); } /** * Initializes the location and size of this window's SWT shell after it has * been created. *

* This framework method is called by the create framework * method. The default implementation calls getInitialSize * and getInitialLocation and passes the results to * Shell.setBounds. This is only done if the bounds of the * shell have not already been modified. Subclasses may extend or * reimplement. *

*/ 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. *

* If this window has been configured to block on open ( * setBlockOnOpen), 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: * OK and CANCEL. *

* * @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 open method should block until the window * closes. * * @param shouldBlock * true if the open method should * not return until the window closes, and false * if the open 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 null 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 null 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 open if block on open is enabled. For non-blocking * opens, the return code needs to be retrieved manually using * getReturnCode. * * @param code * the return code */ protected void setReturnCode(int code) { returnCode = code; } /** * 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 = Util.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. *

* The shell style bits are used by the framework method * createShell when creating this window's shell. *

* * @param newShellStyle * the new shell style bits */ protected void setShellStyle(int newShellStyle) { shellStyle = newShellStyle; } /** * Sets the window manager of this window. *

* Note that this method is used by WindowManager to maintain * a backpointer. Clients must not call the method directly. *

* * @param manager * the window manager, or null if none */ public void setWindowManager(WindowManager manager) { windowManager = manager; // Code to detect invalid usage if (manager != null) { Window[] windows = manager.getWindows(); for (Window window : windows) { if (window == this) { return; } } manager.add(this); } } /** * Sets the exception handler for this application. *

* Note that the handler may only be set once. Subsequent calls to this method * will be ignored. *

* * @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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy