/******************************************************************************* * Copyright (c) 2000, 2015 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 * Roman Dawydkin - bug 55116 *******************************************************************************/ package org.eclipse.jface.window; import java.lang.reflect.InvocationTargetException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jface.action.CoolBarManager; import org.eclipse.jface.action.ICoolBarManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.StatusLineManager; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.internal.provisional.action.ICoolBarManager2; import org.eclipse.jface.internal.provisional.action.IToolBarManager2; import org.eclipse.jface.operation.IRunnableContext; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.operation.ModalContext; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.util.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.CoolBar; import org.eclipse.swt.widgets.Decorations; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Layout; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.ToolBar; /** * An application window is a high-level "main window", with built-in * support for an optional menu bar with standard menus, an optional toolbar, * and an optional status line. * <p> * Creating an application window involves the following steps: * <ul> * <li>creating an instance of <code>ApplicationWindow</code> * </li> * <li>assigning the window to a window manager (optional) * </li> * <li>opening the window by calling <code>open</code> * </li> * </ul> * Only on the last step, when the window is told to open, are * the window's shell and widget tree 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. Like all windows, an application window may be reopened. * </p> * <p> * An application window is also a suitable context in which to perform * long-running operations (that is, it implements <code>IRunnableContext</code>). * </p> */ public class ApplicationWindow extends Window implements IRunnableContext { /** * Menu bar manager, or <code>null</code> if none (default). * * @see #addMenuBar */ private MenuManager menuBarManager = null; /** * Tool bar manager, or <code>null</code> if none (default). * * @see #addToolBar */ private IToolBarManager toolBarManager = null; /** * Status line manager, or <code>null</code> if none (default). * * @see #addStatusLine */ private StatusLineManager statusLineManager = null; /** * Cool bar manager, or <code>null</code> if none (default). * * @see #addCoolBar * @since 3.0 */ private ICoolBarManager coolBarManager = null; /** * The seperator between the menu bar and the rest of the window. */ protected Label seperator1; /** * A flag indicating that an operation is running. */ private boolean operationInProgress = false; /** * Internal application window layout class. * This vertical layout supports a tool bar area (fixed size), * a separator line, the content area (variable size), and a * status line (fixed size). */ /*package*/class ApplicationWindowLayout extends Layout { static final int VGAP = 2; static final int BAR_SIZE = 23; @Override protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { if (wHint != SWT.DEFAULT && hHint != SWT.DEFAULT) { return new Point(wHint, hHint); } Point result = new Point(0, 0); Control[] ws = composite.getChildren(); for (int i = 0; i < ws.length; i++) { Control w = ws[i]; boolean hide = false; if (getToolBarControl() == w) { if (!toolBarChildrenExist()) { hide = true; result.y += BAR_SIZE; // REVISIT } } else if (getCoolBarControl() == w) { if (!coolBarChildrenExist()) { hide = true; result.y += BAR_SIZE; } } else if (statusLineManager != null && statusLineManager.getControl() == w) { } else if (i > 0) { /* we assume this window is contents */ hide = false; } if (!hide) { Point e = w.computeSize(wHint, hHint, flushCache); result.x = Math.max(result.x, e.x); result.y += e.y + VGAP; } } if (wHint != SWT.DEFAULT) { result.x = wHint; } if (hHint != SWT.DEFAULT) { result.y = hHint; } return result; } @Override protected void layout(Composite composite, boolean flushCache) { Rectangle clientArea = composite.getClientArea(); Control[] ws = composite.getChildren(); // Lay out the separator, the tool bar control, the cool bar control, the status line, and the page composite. // The following code assumes that the page composite is the last child, and that there are no unexpected other controls. for (Control w : ws) { if (w == seperator1) { // Separator Point e = w.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache); w.setBounds(clientArea.x, clientArea.y, clientArea.width, e.y); clientArea.y += e.y; clientArea.height -= e.y; } else if (getToolBarControl() == w) { if (toolBarChildrenExist()) { Point e = w.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache); w.setBounds(clientArea.x, clientArea.y, clientArea.width, e.y); clientArea.y += e.y + VGAP; clientArea.height -= e.y + VGAP; } } else if (getCoolBarControl() == w) { if (coolBarChildrenExist()) { Point e = w.computeSize(clientArea.width, SWT.DEFAULT, flushCache); w.setBounds(clientArea.x, clientArea.y, clientArea.width, e.y); clientArea.y += e.y + VGAP; clientArea.height -= e.y + VGAP; } } else if (statusLineManager != null && statusLineManager.getControl() == w) { Point e = w.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache); w.setBounds(clientArea.x, clientArea.y + clientArea.height - e.y, clientArea.width, e.y); clientArea.height -= e.y + VGAP; } else { w.setBounds(clientArea.x, clientArea.y + VGAP, clientArea.width, clientArea.height - VGAP); } } } } /** * Return the top seperator. * @return Label */ protected Label getSeperator1() { return seperator1; } /** * Create an application window instance, whose shell will be created under the * given parent shell. * Note that the window will have no visual representation (no widgets) * 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 */ public ApplicationWindow(Shell parentShell) { super(parentShell); } /** * Configures this window to have a menu bar. * Does nothing if it already has one. * This method must be called before this window's shell is created. */ protected void addMenuBar() { if ((getShell() == null) && (menuBarManager == null)) { menuBarManager = createMenuManager(); } } /** * Configures this window to have a status line. * Does nothing if it already has one. * This method must be called before this window's shell is created. */ protected void addStatusLine() { if ((getShell() == null) && (statusLineManager == null)) { statusLineManager = createStatusLineManager(); } } /** * Configures this window to have a tool bar. * Does nothing if it already has one. * This method must be called before this window's shell is created. * @param style swt style bits used to create the Toolbar * @see ToolBarManager#ToolBarManager(int) * @see ToolBar for style bits */ protected void addToolBar(int style) { if ((getShell() == null) && (toolBarManager == null) && (coolBarManager == null)) { toolBarManager = createToolBarManager2(style); } } /** * Configures this window to have a cool bar. * Does nothing if it already has one. * This method must be called before this window's shell is created. * * @param style the cool bar style * @since 3.0 */ protected void addCoolBar(int style) { if ((getShell() == null) && (toolBarManager == null) && (coolBarManager == null)) { coolBarManager = createCoolBarManager2(style); } } @Override protected boolean canHandleShellCloseEvent() { return super.canHandleShellCloseEvent() && !operationInProgress; } @Override public boolean close() { if (operationInProgress) { return false; } if (super.close()) { if (menuBarManager != null) { menuBarManager.dispose(); menuBarManager = null; } if (toolBarManager != null) { if (toolBarManager instanceof IToolBarManager2) { ((IToolBarManager2) toolBarManager).dispose(); } else if (toolBarManager instanceof ToolBarManager) { ((ToolBarManager) toolBarManager).dispose(); } toolBarManager = null; } if (statusLineManager != null) { statusLineManager.dispose(); statusLineManager = null; } if (coolBarManager != null) { if (coolBarManager instanceof ICoolBarManager2) { ((ICoolBarManager2) coolBarManager).dispose(); } else if (coolBarManager instanceof CoolBarManager) { ((CoolBarManager) coolBarManager).dispose(); } coolBarManager = null; } return true; } return false; } /** * Extends the super implementation by creating the trim widgets using <code>createTrimWidgets</code>. */ @Override protected void configureShell(Shell shell) { super.configureShell(shell); createTrimWidgets(shell); } /** * Creates the trim widgets around the content area. * * @param shell the shell * @since 3.0 */ protected void createTrimWidgets(Shell shell) { if (menuBarManager != null) { boolean resizeHasOccurredBackup = this.resizeHasOccurred; shell.setMenuBar(menuBarManager.createMenuBar((Decorations) shell)); this.resizeHasOccurred = resizeHasOccurredBackup; menuBarManager.updateAll(true); } if (showTopSeperator()) { seperator1 = new Label(shell, SWT.SEPARATOR | SWT.HORIZONTAL); } // will create either a cool bar or a tool bar createToolBarControl(shell); createCoolBarControl(shell); createStatusLine(shell); } @Override protected Layout getLayout() { return new ApplicationWindowLayout(); } /** * Returns whether to show a top separator line between the menu bar * and the rest of the window contents. On some platforms such as the Mac, * the menu is separated from the main window already, so a separator line * is not desired. * * @return <code>true</code> to show the top separator, <code>false</code> * to not show it * @since 3.0 */ protected boolean showTopSeperator() { return !Util.isMac(); } /** * Create the status line if required. * @param shell */ protected void createStatusLine(Shell shell) { if (statusLineManager != null) { statusLineManager.createControl(shell, SWT.NONE); } } /** * Returns a new menu manager for the window. * <p> * Subclasses may override this method to customize the menu manager. * </p> * @return a menu manager */ protected MenuManager createMenuManager() { return new MenuManager(); } /** * Returns a new status line manager for the window. * <p> * Subclasses may override this method to customize the status line manager. * </p> * @return a status line manager */ protected StatusLineManager createStatusLineManager() { return new StatusLineManager(); } /** * Returns a new tool bar manager for the window. * <p> * Subclasses may override this method to customize the tool bar manager. * </p> * @param style swt style bits used to create the Toolbar * * @return a tool bar manager * @see ToolBarManager#ToolBarManager(int) * @see ToolBar for style bits */ protected ToolBarManager createToolBarManager(int style) { return new ToolBarManager(style); } /** * Returns a new tool bar manager for the window. * <p> * By default this method calls <code>createToolBarManager</code>. Subclasses * may override this method to provide an alternative implementation for the * tool bar manager. * </p> * * @param style swt style bits used to create the Toolbar * * @return a tool bar manager * @since 3.2 * @see #createToolBarManager(int) */ protected IToolBarManager createToolBarManager2(int style) { return createToolBarManager(style); } /** * Returns a new cool bar manager for the window. * <p> * Subclasses may override this method to customize the cool bar manager. * </p> * * @param style swt style bits used to create the Coolbar * * @return a cool bar manager * @since 3.0 * @see CoolBarManager#CoolBarManager(int) * @see CoolBar for style bits */ protected CoolBarManager createCoolBarManager(int style) { return new CoolBarManager(style); } /** * Returns a new cool bar manager for the window. * <p> * By default this method calls <code>createCoolBarManager</code>. Subclasses * may override this method to provide an alternative implementation for the * cool bar manager. * </p> * * @param style swt style bits used to create the Coolbar * * @return a cool bar manager * @since 3.2 * @see #createCoolBarManager(int) */ protected ICoolBarManager createCoolBarManager2(int style) { return createCoolBarManager(style); } /** * Creates the control for the tool bar manager. * <p> * Subclasses may override this method to customize the tool bar manager. * </p> * @param parent the parent used for the control * @return a Control */ protected Control createToolBarControl(Composite parent) { if (toolBarManager != null) { if (toolBarManager instanceof IToolBarManager2) { return ((IToolBarManager2) toolBarManager).createControl2(parent); } if (toolBarManager instanceof ToolBarManager) { return ((ToolBarManager) toolBarManager).createControl(parent); } } return null; } /** * Creates the control for the cool bar manager. * <p> * Subclasses may override this method to customize the cool bar manager. * </p> * @param composite the parent used for the control * * @return an instance of <code>CoolBar</code> * @since 3.0 */ protected Control createCoolBarControl(Composite composite) { if (coolBarManager != null) { if (coolBarManager instanceof ICoolBarManager2) { return ((ICoolBarManager2) coolBarManager).createControl2(composite); } if (coolBarManager instanceof CoolBarManager) { return ((CoolBarManager) coolBarManager).createControl(composite); } } return null; } /** * Returns the default font used for this window. * <p> * The default implementation of this framework method * obtains the symbolic name of the font from the * <code>getSymbolicFontName</code> framework method * and retrieves this font from JFace's font * registry using <code>JFaceResources.getFont</code>. * Subclasses may override to use a different registry, * etc. * </p> * * @return the default font, or <code>null</code> if none */ protected Font getFont() { return JFaceResources.getFont(getSymbolicFontName()); } /** * Returns the menu bar manager for this window (if it has one). * * @return the menu bar manager, or <code>null</code> if * this window does not have a menu bar * @see #addMenuBar() */ public MenuManager getMenuBarManager() { return menuBarManager; } /** * Returns the status line manager for this window (if it has one). * * @return the status line manager, or <code>null</code> if * this window does not have a status line * @see #addStatusLine */ protected StatusLineManager getStatusLineManager() { return statusLineManager; } /** * Returns the symbolic font name of the font to be * used to display text in this window. * This is not recommended and is included for backwards * compatability. * It is recommended to use the default font provided by * SWT (that is, do not set the font). * * @return the symbolic font name */ public String getSymbolicFontName() { return JFaceResources.TEXT_FONT; } /** * Returns the tool bar manager for this window (if it has one). * * @return the tool bar manager, or <code>null</code> if * this window does not have a tool bar * @see #addToolBar(int) */ public ToolBarManager getToolBarManager() { if (toolBarManager instanceof ToolBarManager) { return (ToolBarManager)toolBarManager; } return null; } /** * Returns the tool bar manager for this window (if it has one). * * @return the tool bar manager, or <code>null</code> if * this window does not have a tool bar * @see #addToolBar(int) * @since 3.2 */ public IToolBarManager getToolBarManager2() { return toolBarManager; } /** * Returns the cool bar manager for this window. * * @return the cool bar manager, or <code>null</code> if * this window does not have a cool bar * @see #addCoolBar(int) * @since 3.0 */ public CoolBarManager getCoolBarManager() { if (coolBarManager instanceof CoolBarManager) { return (CoolBarManager)coolBarManager; } return null; } /** * Returns the cool bar manager for this window. * * @return the cool bar manager, or <code>null</code> if * this window does not have a cool bar * @see #addCoolBar(int) * @since 3.2 */ public ICoolBarManager getCoolBarManager2() { return coolBarManager; } /** * Returns the control for the window's toolbar. * <p> * Subclasses may override this method to customize the tool bar manager. * </p> * @return a Control */ protected Control getToolBarControl() { if (toolBarManager != null) { if (toolBarManager instanceof IToolBarManager2) { return ((IToolBarManager2) toolBarManager).getControl2(); } if (toolBarManager instanceof ToolBarManager) { return ((ToolBarManager) toolBarManager).getControl(); } } return null; } /** * Returns the control for the window's cool bar. * <p> * Subclasses may override this method to customize the cool bar manager. * </p> * * @return an instance of <code>CoolBar</code> * @since 3.0 */ protected Control getCoolBarControl() { if (coolBarManager != null) { if (coolBarManager instanceof ICoolBarManager2) { return ((ICoolBarManager2) coolBarManager).getControl2(); } if (coolBarManager instanceof CoolBarManager) { return ((CoolBarManager) coolBarManager).getControl(); } } return null; } /** * This implementation of IRunnableContext#run(boolean, boolean, * IRunnableWithProgress) blocks until the runnable has been run, * regardless of the value of <code>fork</code>. * It is recommended that <code>fork</code> is set to * true in most cases. If <code>fork</code> is set to <code>false</code>, * the runnable will run in the UI thread and it is the runnable's * responsibility to call <code>Display.readAndDispatch()</code> * to ensure UI responsiveness. */ @Override public void run(final boolean fork, boolean cancelable, final IRunnableWithProgress runnable) throws InvocationTargetException, InterruptedException { try { operationInProgress = true; final StatusLineManager mgr = getStatusLineManager(); if (mgr == null) { runnable.run(new NullProgressMonitor()); return; } boolean cancelWasEnabled = mgr.isCancelEnabled(); final Control contents = getContents(); final Display display = contents.getDisplay(); Shell shell = getShell(); boolean contentsWasEnabled = contents.getEnabled(); MenuManager manager = getMenuBarManager(); Menu menuBar = null; if (manager != null) { menuBar = manager.getMenu(); manager = null; } boolean menuBarWasEnabled = false; if (menuBar != null) { menuBarWasEnabled = menuBar.getEnabled(); } Control toolbarControl = getToolBarControl(); boolean toolbarWasEnabled = false; if (toolbarControl != null) { toolbarWasEnabled = toolbarControl.getEnabled(); } Control coolbarControl = getCoolBarControl(); boolean coolbarWasEnabled = false; if (coolbarControl != null) { coolbarWasEnabled = coolbarControl.getEnabled(); } // Disable the rest of the shells on the current display Shell[] shells = display.getShells(); boolean[] enabled = new boolean[shells.length]; for (int i = 0; i < shells.length; i++) { Shell current = shells[i]; if (current == shell) { continue; } if (current != null && !current.isDisposed()) { enabled[i] = current.getEnabled(); current.setEnabled(false); } } Control currentFocus = display.getFocusControl(); try { contents.setEnabled(false); if (menuBar != null) { menuBar.setEnabled(false); } if (toolbarControl != null) { toolbarControl.setEnabled(false); } if (coolbarControl != null) { coolbarControl.setEnabled(false); } mgr.setCancelEnabled(cancelable); final Exception[] holder = new Exception[1]; BusyIndicator.showWhile(display, () -> { try { ModalContext.run(runnable, fork, mgr .getProgressMonitor(), display); } catch (InvocationTargetException ite) { holder[0] = ite; } catch (InterruptedException ie) { holder[0] = ie; } }); if (holder[0] != null) { if (holder[0] instanceof InvocationTargetException) { throw (InvocationTargetException) holder[0]; } else if (holder[0] instanceof InterruptedException) { throw (InterruptedException) holder[0]; } } } finally { operationInProgress = false; // Enable the rest of the shells on the current display for (int i = 0; i < shells.length; i++) { Shell current = shells[i]; if (current == shell) { continue; } if (current != null && !current.isDisposed()) { current.setEnabled(enabled[i]); } } if (!contents.isDisposed()) { contents.setEnabled(contentsWasEnabled); } if (menuBar != null && !menuBar.isDisposed()) { menuBar.setEnabled(menuBarWasEnabled); } if (toolbarControl != null && !toolbarControl.isDisposed()) { toolbarControl.setEnabled(toolbarWasEnabled); } if (coolbarControl != null && !coolbarControl.isDisposed()) { coolbarControl.setEnabled(coolbarWasEnabled); } mgr.setCancelEnabled(cancelWasEnabled); if (currentFocus != null && !currentFocus.isDisposed()) { // It's necessary to restore focus after reenabling the controls // because disabling them causes focus to jump elsewhere. // Use forceFocus rather than setFocus to avoid SWT's // search for children which can take focus, so focus // ends up back on the actual control that previously had it. currentFocus.forceFocus(); } } } finally { operationInProgress = false; } } /** * Sets or clears the message displayed in this window's status * line (if it has one). This method has no effect if the * window does not have a status line. * * @param message the status message, or <code>null</code> to clear it */ public void setStatus(String message) { if (statusLineManager != null) { statusLineManager.setMessage(message); } } /** * Returns whether or not children exist for the Application Window's * toolbar control. * <p> * @return boolean true if children exist, false otherwise */ protected boolean toolBarChildrenExist() { Control toolControl = getToolBarControl(); if (toolControl instanceof ToolBar) { return ((ToolBar) toolControl).getItemCount() > 0; } return false; } /** * Returns whether or not children exist for this application window's * cool bar control. * * @return boolean true if children exist, false otherwise * @since 3.0 */ protected boolean coolBarChildrenExist() { Control coolControl = getCoolBarControl(); if (coolControl instanceof CoolBar) { return ((CoolBar) coolControl).getItemCount() > 0; } return false; } }