/******************************************************************************
* Copyright (c) 2002, 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.gmf.runtime.common.ui.action;
import org.eclipse.gmf.runtime.common.core.util.Log;
import org.eclipse.gmf.runtime.common.ui.internal.CommonUIPlugin;
import org.eclipse.gmf.runtime.common.ui.internal.CommonUIStatusCodes;
import org.eclipse.jface.action.AbstractGroupMarker;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IContributionManager;
import org.eclipse.jface.action.IMenuCreator;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.SubContributionItem;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
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.ui.actions.LabelRetargetAction;
/**
* An implementation of an <code>IMenuManager</code> that inherits its
* UI (text + icon + hints) from a given action.
*
* When filled in a toolbar, the menu is rendered as a tool item
* with two parts: a button, whose icon comes from the supplied
* action handler, and a drop-down menu arrow.
* When the arrow is pressed, the drop-down menu is shown. When the
* button is pressed, the associated action is executed. The manager can have
* an optional style to retarget the last executed action. In this
* case the tool item UI reflects the last executed sub-action from the menu.
*
* When filled in a menu, this menu shows up as a normal cascading menu with
* its GUI inherited from the supplied action.
*
* @author melaasar
*/
public class ActionMenuManager extends MenuManager {
/**
* An action that provides a menu and fills it from the contribution
* items of the enclosing menu manager. It also retargets to the
* manager's supplied action handler.
*/
public class MenuCreatorAction
extends LabelRetargetAction
implements IMenuCreator {
// the menu widget
private Menu menu;
// menu item selection listener: listens to selection events
private Listener menuItemListener = new Listener() {
public void handleEvent(Event event) {
if (SWT.Selection == event.type
&& !event.widget.isDisposed()) {
ActionContributionItem item =
(ActionContributionItem) event.widget.getData();
if (retargetLastAction) {
setActionHandler(item.getAction());
setDefaultAction(item.getAction());
}
subActionSelected(item.getAction());
}
}
};
/**
* Creates a new menu creator action
*
* @param actionHandler the action handler
*/
public MenuCreatorAction(IAction actionHandler) {
super(actionHandler.getId(), actionHandler.getText());
setEnabled(false); // initially untill a menu item is added
setActionHandler(actionHandler);
setMenuCreator(this);
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IMenuCreator#getMenu(org.eclipse.swt.widgets.Control)
*/
public Menu getMenu(Control parent) {
if (menu != null)
menu.dispose();
menu = new Menu(parent);
return createMenu(menu);
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IMenuCreator#getMenu(org.eclipse.swt.widgets.Menu)
*/
public Menu getMenu(Menu parent) {
if (menu != null)
menu.dispose();
menu = new Menu(parent);
return createMenu(menu);
}
/* (non-Javadoc)
* @see org.eclipse.ui.actions.ActionFactory.IWorkbenchAction#dispose()
*/
public void dispose() {
if (menu != null) {
menu.dispose();
menu = null;
}
super.dispose();
ActionMenuManager.this.dispose();
}
/**
* Create the drop-down/pop-up menu.
*
* @param mnu <code>Menu</code> for which to create the drop-down/pop-up menu
* @return <code>Menu</code> the drop-down/pop-up menu
*/
protected Menu createMenu(Menu mnu) {
IContributionItem[] items = getRealItems();
IContributionItem lastGroupMarker = null;
for (int i = 0; i < items.length; i++) {
IContributionItem item = items[i];
if (item instanceof AbstractGroupMarker) {
if (i == 0
|| i == items.length - 1
|| items[i + 1] instanceof AbstractGroupMarker
|| mnu.getItemCount() < 1
|| !item.isVisible()) {
continue;
} else {
// Do not add last group marker until we know that there
// will be items following it.
lastGroupMarker = item;
}
} else {
if (!item.isVisible()) {
continue;
}
try {
if (lastGroupMarker != null) {
lastGroupMarker.fill(menu, -1);
lastGroupMarker = null;
}
item.fill(menu, -1);
} catch (Exception e) {
Log.info(CommonUIPlugin.getDefault(), CommonUIStatusCodes.GENERAL_UI_FAILURE, "The contribution item (" + item.getId() + ") failed to fill within the menu"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
MenuItem menuItems[] = mnu.getItems();
for (int i = 0; i < menuItems.length; i++) {
if (menuItems[i].getStyle() == SWT.SEPARATOR)
continue;
menuItems[i].addListener(SWT.Selection, menuItemListener);
}
return mnu;
}
/**
* Ignores the action handler's "enable" event since "enablement"
* is determined by the sub-action(s) enablement state
*
*/
protected void propagateChange(PropertyChangeEvent event) {
if (!event.getProperty().equals(Action.ENABLED))
super.propagateChange(event);
}
/**
* Ignores the action handler's "enable" event since "enablement"
* is determined by the sub-action(s)
*
*/
protected void setActionHandler(IAction handler) {
boolean enabled = MenuCreatorAction.this.isEnabled();
super.setActionHandler(handler);
MenuCreatorAction.this.setEnabled(enabled);
}
/**
* Only run the action handler if it is enabled
*
*/
public void run() {
if (getActionHandler() != null && getActionHandler().isEnabled())
super.run();
else if (getDefaultAction().isEnabled()) {
setActionHandler(getDefaultAction());
super.run();
}
}
/**
* Only run the action handler if it is enabled
*
*/
public void runWithEvent(Event event) {
if (getActionHandler() != null && getActionHandler().isEnabled())
super.runWithEvent(event);
else if (getDefaultAction().isEnabled()) {
setActionHandler(getDefaultAction());
super.runWithEvent(event);
}
}
}
/** the associated menu action */
protected final MenuCreatorAction action;
/** the associated menu action */
protected IAction defaultAction = null;
/** the delege action contribution item */
private final ActionContributionItem actionContributionItem;
/** an option to retarget the last action */
private boolean retargetLastAction;
/**
* Creates a new instance of <code>ActionMenuManager</code> with
* a given action handler. The manager does not retarget the last
* selected action from the menu
*
* @param id The menu manager id
* @param actionHandler the menu associated action handler
*/
public ActionMenuManager(String id, IAction actionHandler) {
this(id, actionHandler, false);
}
/**
* Creates a new instance of <code>ActionMenuManager</code> with
* a given action handler and an option to retarget the last
* executed menu action.
*
* @param id The menu manager id
* @param actionHandler the menu associated action handler
* @param retargetLastAction whether to retarget the last action or not
*/
public ActionMenuManager(
String id,
IAction actionHandler,
boolean retargetLastAction) {
super(actionHandler.getText(), id);
assert null != actionHandler;
action = new MenuCreatorAction(actionHandler);
defaultAction = actionHandler;
actionContributionItem = new ActionContributionItem(action);
this.retargetLastAction = retargetLastAction;
}
/**
* Returns whether the option to retarget last action was requested
*
* @return <code>true</code> if retargetLastAction is enabled, <code>false</code> otherwise
*/
protected boolean isRetargetLastAction() {
return retargetLastAction;
}
/**
* Handle subaction selection
*
* @param subActionHandler The selected sub action handler
*/
protected void subActionSelected(IAction subActionHandler) {
/* method not implemented */
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IContributionItem#fill(org.eclipse.swt.widgets.Composite)
*/
public void fill(Composite parent) {
// this is only relevant in toolbars
retargetLastAction = false;
actionContributionItem.fill(parent);
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IContributionItem#fill(org.eclipse.swt.widgets.Menu, int)
*/
public void fill(Menu parent, int index) {
// this is only relevant in toolbars
retargetLastAction = false;
actionContributionItem.fill(parent, index);
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IContributionItem#fill(org.eclipse.swt.widgets.ToolBar, int)
*/
public void fill(ToolBar parent, int index) {
actionContributionItem.fill(parent, index);
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IContributionItem#dispose()
*/
public void dispose() {
actionContributionItem.dispose();
super.dispose();
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IContributionItem#isEnabled()
*/
public boolean isEnabled() {
return actionContributionItem.isEnabled();
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IContributionManager#isDirty()
*/
public boolean isDirty() {
return actionContributionItem.isDirty();
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IContributionItem#isDynamic()
*/
public boolean isDynamic() {
return actionContributionItem.isDynamic();
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IContributionItem#isGroupMarker()
*/
public boolean isGroupMarker() {
return actionContributionItem.isGroupMarker();
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IContributionItem#isSeparator()
*/
public boolean isSeparator() {
return actionContributionItem.isSeparator();
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IContributionItem#isVisible()
*/
public boolean isVisible() {
IContributionItem[] items = getRealItems();
for (int i = 0; i < items.length; i++) {
IContributionItem item = items[i];
if (!(item instanceof AbstractGroupMarker) && item.isVisible()) {
return actionContributionItem.isVisible();
}
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IContributionItem#setParent(org.eclipse.jface.action.IContributionManager)
*/
public void setParent(IContributionManager parent) {
actionContributionItem.setParent(parent);
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IContributionItem#setVisible(boolean)
*/
public void setVisible(boolean visible) {
actionContributionItem.setVisible(visible);
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IContributionItem#update()
*/
public void update() {
actionContributionItem.update();
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IContributionItem#update(java.lang.String)
*/
public void update(String id) {
actionContributionItem.update(id);
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IMenuManager#updateAll(boolean)
*/
public void updateAll(boolean force) {
update(force);
IContributionItem[] items = getRealItems();
for (int i = 0; i < items.length; ++i) {
IContributionItem ci = items[i];
if (ci instanceof IMenuManager) {
IMenuManager mm = (IMenuManager) ci;
if (mm.isVisible()) {
mm.updateAll(force);
}
}
}
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IContributionManager#update(boolean)
*/
public void update(boolean force) {
update();
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.ContributionManager#itemAdded(org.eclipse.jface.action.IContributionItem)
*/
protected void itemAdded(IContributionItem item) {
super.itemAdded(item);
if (item instanceof SubContributionItem)
item = ((SubContributionItem) item).getInnerItem();
if (!item.isGroupMarker())
action.setEnabled(true);
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.ContributionManager#itemRemoved(org.eclipse.jface.action.IContributionItem)
*/
protected void itemRemoved(IContributionItem item) {
super.itemRemoved(item);
if (item instanceof SubContributionItem)
item = ((SubContributionItem) item).getInnerItem();
if (!item.isGroupMarker()) {
action.setEnabled(false);
IContributionItem[] items = getItems();
for (int i = 0; i < items.length; i++)
if (!items[i].isGroupMarker()){
action.setEnabled(true);
break;
}
}
}
/**
* Returns the contribution items of this manager. If an item
* is wrapper in a SubContributionItem instance it extracts the
* real item instance
*
* @return An array of real items of this contribution manager
*/
protected IContributionItem[] getRealItems() {
IContributionItem[] items = getItems();
IContributionItem[] realItems = new IContributionItem[items.length];
for (int i = 0; i < items.length; i++) {
if (items[i] instanceof SubContributionItem) {
realItems[i] = ((SubContributionItem) items[i]).getInnerItem();
} else {
realItems[i] = items[i];
}
}
return realItems;
}
public IAction getDefaultAction() {
return defaultAction;
}
protected void setDefaultAction(IAction defaultAction) {
this.defaultAction = defaultAction;
}
}