blob: e4e6d133e2587b0f1913c9cf86810a473684e183 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2007 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.ui.internal.menus;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.core.commands.common.CommandException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.menus.IWidget;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
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.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.IWorkbenchWindowPulldownDelegate;
import org.eclipse.ui.IWorkbenchWindowPulldownDelegate2;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.services.IServiceLocator;
/**
* <p>
* A proxy for a {@link IWorkbenchWindowPulldownDelegate} on a pulldown action
* set action. This delays the class loading until the delegate is really asked
* for information. Asking a proxy for anything (except disposing) will cause
* the proxy to instantiate the proxied delegate.
* </p>
* <p>
* This class is not intended for use outside of the
* <code>org.eclipse.ui.workbench</code> plug-in.
* </p>
*
* @since 3.2
*/
final class PulldownDelegateWidgetProxy implements IWidget {
/**
* A wrapper for loading the menu that defends against possible exceptions
* triggered outside of the workbench.
*/
private static final class MenuLoader implements ISafeRunnable {
/**
* The parent for the menu to be created. This value is
* <code>null</code> if the parent is a menu.
*/
private final Control control;
/**
* The delegate from which to load the menu.
*/
private final IWorkbenchWindowPulldownDelegate delegate;
/**
* The loaded menu. This value is <code>null</code> if the load
* failed, or if it hasn't been loaded yet.
*/
private Menu menu = null;
/**
* The parent for the menu to be created. This value is
* <code>null</code> if the parent is a control.
*/
private final Menu parent;
/**
* Constructs a new instance of <code>MenuLoader</code>
*
* @param delegate
* The delegate from which the menu will be loaded; this
* value must not be <code>null</code>.
* @param parent
* The parent of the menu to be loaded; this value must not
* be <code>null</code>.
*/
private MenuLoader(final IWorkbenchWindowPulldownDelegate delegate,
final Control parent) {
this.delegate = delegate;
this.parent = null;
this.control = parent;
}
/**
* Constructs a new instance of <code>MenuLoader</code>
*
* @param delegate
* The delegate from which the menu will be loaded; this
* value must not be <code>null</code>.
* @param parent
* The parent of the menu to be loaded; this value must not
* be <code>null</code>.
*/
private MenuLoader(final IWorkbenchWindowPulldownDelegate2 delegate,
final Menu parent) {
this.delegate = delegate;
this.parent = parent;
this.control = null;
}
/**
* Returns the menu loaded, if any.
*
* @return the loaded menu, or <code>null</code> if none.
*/
private Menu getMenu() {
return menu;
}
/**
* @see ISafeRunnable#handleException(java.lang.Throwable)
*/
public void handleException(Throwable exception) {
// Do nothing
}
/**
* @see ISafeRunnable#run()
*/
public void run() throws Exception {
if (parent == null) {
menu = delegate.getMenu(control);
} else {
menu = ((IWorkbenchWindowPulldownDelegate2) delegate)
.getMenu(parent);
}
}
}
/**
* The command to execute when the pulldown delegate appears in a tool bar,
* and the arrow is <em>not</em> clicked. This also carries a help context
* identifier. This value must be <code>null</code>.
*/
private final ParameterizedCommand command;
/**
* The configuration element from which the delegate can be created. This
* value will exist until the element is converted into a real class -- at
* which point this value will be set to <code>null</code>.
*/
private IConfigurationElement configurationElement;
/**
* The real delegate. This value is <code>null</code> until the proxy is
* forced to load the real delegate. At this point, the configuration
* element is converted, nulled out, and this delegate gains a reference.
*/
private IWorkbenchWindowPulldownDelegate delegate = null;
/**
* The name of the configuration element attribute which contains the
* information necessary to instantiate the real delegate.
*/
private final String delegateAttributeName;
private final DisposeListener disposeListener = new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
if (e.widget == widget) {
dispose();
widget = null;
// TODO Is this necessary?
// disposeOldImages();
}
}
};
/**
* The service locator from which a handler service can be retrieved. This
* is needed if the pulldown appears in the tool bar, and the drop-down
* arrow is <em>not</em> clicked. This value must not be <code>null</code>.
*/
private final IServiceLocator locator;
private final Listener selectionListener = new Listener() {
public final void handleEvent(final Event event) {
final Widget item = event.widget;
if (item == null) {
return;
}
final int style = item.getStyle();
if (((style & SWT.DROP_DOWN) != 0) && (event.detail == SWT.ARROW)
&& (item instanceof ToolItem)) {
// Create the submenu.
final ToolItem toolItem = (ToolItem) item;
final ToolBar toolBar = toolItem.getParent();
if (loadDelegate()
&& (delegate instanceof IWorkbenchWindowPulldownDelegate2)) {
final IWorkbenchWindowPulldownDelegate2 delegate2 = (IWorkbenchWindowPulldownDelegate2) delegate;
final MenuLoader loader = new MenuLoader(delegate2, toolBar);
SafeRunner.run(loader);
final Menu subMenu = loader.getMenu();
if (subMenu != null) {
// position the menu below the drop down item
final Rectangle bounds = toolItem.getBounds();
final Point location = toolBar.toDisplay(new Point(
bounds.x, bounds.y + bounds.height));
subMenu.setLocation(location);
subMenu.setVisible(true);
return; // we don't fire the command
}
}
}
final IHandlerService service = (IHandlerService) locator
.getService(IHandlerService.class);
try {
service.executeCommand(command, event);
} catch (final CommandException e) {
/*
* TODO There should be an API on IHandlerService that handles
* the exceptions.
*/
}
}
};
/**
* The widget created for this pulldown delegate. If this proxy has not been
* asked to fill or it has been disposed, then this value is
* <code>null</code>.
*/
private Widget widget = null;
/**
* Constructs a new instance of <code>PulldownDelegateWidgetProxy</code>
* with all the information it needs to try to avoid loading until it is
* needed.
*
* @param configurationElement
* The configuration element from which the real class can be
* loaded at run-time; must not be <code>null</code>.
* @param delegateAttributeName
* The name of the attibute or element containing the delegate;
* must not be <code>null</code>.
* @param command
* The command to execute if this the pulldown is not shown; must
* not be <code>null</code>.
* @param locator
* A service locator from which a handler service can be
* retrieved; must not be <code>null</code>.
*/
public PulldownDelegateWidgetProxy(
final IConfigurationElement configurationElement,
final String delegateAttributeName,
final ParameterizedCommand command, final IServiceLocator locator) {
if (configurationElement == null) {
throw new NullPointerException(
"The configuration element backing a handler proxy cannot be null"); //$NON-NLS-1$
}
if (delegateAttributeName == null) {
throw new NullPointerException(
"The attribute containing the handler class must be known"); //$NON-NLS-1$
}
if (command == null) {
throw new NullPointerException("The command cannot be null"); //$NON-NLS-1$
}
this.configurationElement = configurationElement;
this.delegateAttributeName = delegateAttributeName;
this.command = command;
this.locator = locator;
}
/**
* Passes the dipose on to the proxied handler, if it has been loaded.
*/
public final void dispose() {
if (delegate != null) {
delegate.dispose();
}
}
public final void fill(final Composite parent) {
// This does not need to be supported.
}
public final void fill(CoolBar parent, final int index) {
// This does not need to be supported.
}
public final void fill(final Menu parent, final int index) {
if ((widget != null) || (parent == null)) {
return;
}
// Create the menu item.
final MenuItem menuItem;
if (index >= 0) {
menuItem = new MenuItem(parent, SWT.CASCADE, index);
} else {
menuItem = new MenuItem(parent, SWT.CASCADE);
}
menuItem.setData(this);
widget = menuItem;
// Create the submenu.
if (loadDelegate()
&& (delegate instanceof IWorkbenchWindowPulldownDelegate2)) {
final IWorkbenchWindowPulldownDelegate2 delegate2 = (IWorkbenchWindowPulldownDelegate2) delegate;
final MenuLoader loader = new MenuLoader(delegate2, parent);
SafeRunner.run(loader);
final Menu subMenu = loader.getMenu();
if (subMenu != null) {
menuItem.setMenu(subMenu);
}
}
menuItem.addDisposeListener(disposeListener);
menuItem.addListener(SWT.Selection, selectionListener);
// TODO Needs a way to be linked to a command.
// if (action.getHelpListener() != null)
// menuItem.addHelpListener(action.getHelpListener());
// TODO Needs a way of updating itself
// update(null);
}
public final void fill(final ToolBar parent, final int index) {
if ((widget != null) && (parent == null)) {
return;
}
final ToolItem toolItem;
if (index >= 0) {
toolItem = new ToolItem(parent, SWT.DROP_DOWN, index);
} else {
toolItem = new ToolItem(parent, SWT.DROP_DOWN);
}
toolItem.setData(this);
widget = toolItem;
// Attach some listeners.
toolItem.addDisposeListener(disposeListener);
toolItem.addListener(SWT.Selection, selectionListener);
// TODO Needs a way to be linked to a command.
// toolItem.addListener(SWT.Selection, getToolItemListener());
// action.addPropertyChangeListener(propertyListener);
// if (action != null) {
// String commandId = action.getActionDefinitionId();
// ExternalActionManager.ICallback callback = ExternalActionManager
// .getInstance().getCallback();
//
// if ((callback != null) && (commandId != null)) {
// callback.addPropertyChangeListener(commandId,
// actionTextListener);
// }
// }
// TODO Needs a way of updating itself
// update(null);
}
/**
* Loads the delegate, if possible. If the delegate is loaded, then the
* member variables are updated accordingly.
*
* @return <code>true</code> if the delegate is now non-null;
* <code>false</code> otherwise.
*/
private final boolean loadDelegate() {
if (delegate == null) {
// Load the handler.
try {
delegate = (IWorkbenchWindowPulldownDelegate) configurationElement
.createExecutableExtension(delegateAttributeName);
configurationElement = null;
return true;
} catch (final ClassCastException e) {
final String message = "The proxied delegate was the wrong class"; //$NON-NLS-1$
final IStatus status = new Status(IStatus.ERROR,
WorkbenchPlugin.PI_WORKBENCH, 0, message, e);
WorkbenchPlugin.log(message, status);
return false;
} catch (final CoreException e) {
final String message = "The proxied delegate for '" + configurationElement.getAttribute(delegateAttributeName) //$NON-NLS-1$
+ "' could not be loaded"; //$NON-NLS-1$
IStatus status = new Status(IStatus.ERROR,
WorkbenchPlugin.PI_WORKBENCH, 0, message, e);
WorkbenchPlugin.log(message, status);
return false;
}
}
return true;
}
public final String toString() {
if (delegate == null) {
return configurationElement.getAttribute(delegateAttributeName);
}
return delegate.toString();
}
}