org.eclipse.jface.action.ActionContributionItem Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.eclipse.jface Show documentation
Show all versions of org.eclipse.jface Show documentation
This is org.eclipse.jface jar used by Scout SDK
The newest version!
/*******************************************************************************
* 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
* Jan-Ove Weichel - Bug 475879
*******************************************************************************/
package org.eclipse.jface.action;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.NotEnabledException;
import org.eclipse.jface.action.ExternalActionManager.IBindingManagerCallback;
import org.eclipse.jface.bindings.Trigger;
import org.eclipse.jface.bindings.TriggerSequence;
import org.eclipse.jface.bindings.keys.IKeyLookup;
import org.eclipse.jface.bindings.keys.KeyLookupFactory;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.Policy;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.util.Util;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Item;
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;
/**
* A contribution item which delegates to an action.
*
* This class may be instantiated; it is not intended to be subclassed.
*
* @noextend This class is not intended to be subclassed by clients.
*/
public class ActionContributionItem extends ContributionItem {
/**
* Mode bit: Show text on tool items or buttons, even if an image is
* present. If this mode bit is not set, text is only shown on tool items if
* there is no image present.
*
* @since 3.0
*/
public static int MODE_FORCE_TEXT = 1;
/** a string inserted in the middle of text that has been shortened */
private static final String ellipsis = "..."; //$NON-NLS-1$
/**
* Stores the result of the action. False when the action returned failure.
*/
private Boolean result = null;
private static boolean USE_COLOR_ICONS = true;
/**
* Returns whether color icons should be used in toolbars.
*
* @return true
if color icons should be used in toolbars,
* false
otherwise
*/
public static boolean getUseColorIconsInToolbars() {
return USE_COLOR_ICONS;
}
/**
* Sets whether color icons should be used in toolbars.
*
* @param useColorIcons
* true
if color icons should be used in toolbars,
* false
otherwise
*/
public static void setUseColorIconsInToolbars(boolean useColorIcons) {
USE_COLOR_ICONS = useColorIcons;
}
/**
* The presentation mode.
*/
private int mode = 0;
/**
* The action.
*/
private IAction action;
/**
* The listener for changes to the text of the action contributed by an
* external source.
*/
private final IPropertyChangeListener actionTextListener = event -> update(event.getProperty());
/**
* Remembers all images in use by this contribution item
*/
private LocalResourceManager imageManager;
/**
* Listener for SWT button widget events.
*/
private Listener buttonListener;
/**
* Listener for SWT menu item widget events.
*/
private Listener menuItemListener;
/**
* Listener for action property change notifications.
*/
private final IPropertyChangeListener propertyListener = event -> actionPropertyChange(event);
/**
* Listener for SWT tool item widget events.
*/
private Listener toolItemListener;
/**
* The widget created for this item; null
before creation and
* after disposal.
*/
private Widget widget = null;
private Listener menuCreatorListener;
/**
* Creates a new contribution item from the given action. The id of the
* action is used as the id of the item.
*
* @param action
* the action
*/
public ActionContributionItem(IAction action) {
super(action.getId());
this.action = action;
}
/**
* Handles a property change event on the action (forwarded by nested
* listener).
*/
private void actionPropertyChange(final PropertyChangeEvent e) {
// This code should be removed. Avoid using free asyncExec
if (isVisible() && widget != null) {
Display display = widget.getDisplay();
if (display.getThread() == Thread.currentThread()) {
update(e.getProperty());
} else {
display.asyncExec(() -> update(e.getProperty()));
}
}
}
/**
* Compares this action contribution item with another object. Two action
* contribution items are equal if they refer to the identical Action.
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof ActionContributionItem)) {
return false;
}
return action.equals(((ActionContributionItem) o).action);
}
/**
* The ActionContributionItem
implementation of this
* IContributionItem
method creates an SWT
* Button
for the action using the action's style. If the
* action's checked property has been set, the button is created and primed
* to the value of the checked property.
*/
@Override
public void fill(Composite parent) {
if (widget == null && parent != null) {
int flags = SWT.PUSH;
if (action != null) {
if (action.getStyle() == IAction.AS_CHECK_BOX) {
flags = SWT.TOGGLE;
}
if (action.getStyle() == IAction.AS_RADIO_BUTTON) {
flags = SWT.RADIO;
}
}
Button b = new Button(parent, flags);
b.setData(this);
b.addListener(SWT.Dispose, getButtonListener());
// Don't hook a dispose listener on the parent
b.addListener(SWT.Selection, getButtonListener());
if (action.getHelpListener() != null) {
b.addHelpListener(action.getHelpListener());
}
widget = b;
update(null);
// Attach some extra listeners.
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);
}
}
}
}
/**
* The ActionContributionItem
implementation of this
* IContributionItem
method creates an SWT
* MenuItem
for the action using the action's style. If the
* action's checked property has been set, a button is created and primed to
* the value of the checked property. If the action's menu creator property
* has been set, a cascading submenu is created.
*/
@Override
public void fill(Menu parent, int index) {
if (widget == null && parent != null) {
int flags = SWT.PUSH;
if (action != null) {
int style = action.getStyle();
if (style == IAction.AS_CHECK_BOX) {
flags = SWT.CHECK;
} else if (style == IAction.AS_RADIO_BUTTON) {
flags = SWT.RADIO;
} else if (style == IAction.AS_DROP_DOWN_MENU) {
flags = SWT.CASCADE;
}
}
MenuItem mi = null;
if (index >= 0) {
mi = new MenuItem(parent, flags, index);
} else {
mi = new MenuItem(parent, flags);
}
widget = mi;
mi.setData(this);
mi.addListener(SWT.Dispose, getMenuItemListener());
mi.addListener(SWT.Selection, getMenuItemListener());
if (action.getHelpListener() != null) {
mi.addHelpListener(action.getHelpListener());
}
if (flags == SWT.CASCADE) {
// just create a proxy for now, if the user shows it then
// fill it in
Menu subMenu = new Menu(parent);
subMenu.addListener(SWT.Show, getMenuCreatorListener());
subMenu.addListener(SWT.Hide, getMenuCreatorListener());
mi.setMenu(subMenu);
}
update(null);
// Attach some extra listeners.
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);
}
}
}
}
/**
* The ActionContributionItem
implementation of this ,
* IContributionItem
method creates an SWT
* ToolItem
for the action using the action's style. If the
* action's checked property has been set, a button is created and primed to
* the value of the checked property. If the action's menu creator property
* has been set, a drop-down tool item is created.
*/
@Override
public void fill(ToolBar parent, int index) {
if (widget == null && parent != null) {
int flags = SWT.PUSH;
if (action != null) {
int style = action.getStyle();
if (style == IAction.AS_CHECK_BOX) {
flags = SWT.CHECK;
} else if (style == IAction.AS_RADIO_BUTTON) {
flags = SWT.RADIO;
} else if (style == IAction.AS_DROP_DOWN_MENU) {
flags = SWT.DROP_DOWN;
}
}
ToolItem ti = null;
if (index >= 0) {
ti = new ToolItem(parent, flags, index);
} else {
ti = new ToolItem(parent, flags);
}
ti.setData(this);
ti.addListener(SWT.Selection, getToolItemListener());
ti.addListener(SWT.Dispose, getToolItemListener());
widget = ti;
update(null);
// Attach some extra listeners.
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);
}
}
}
}
/**
* Returns the action associated with this contribution item.
*
* @return the action
*/
public IAction getAction() {
return action;
}
/**
* Returns the listener for SWT button widget events.
*
* @return a listener for button events
*/
private Listener getButtonListener() {
if (buttonListener == null) {
buttonListener = event -> {
switch (event.type) {
case SWT.Dispose:
handleWidgetDispose(event);
break;
case SWT.Selection:
Widget ew = event.widget;
if (ew != null) {
handleWidgetSelection(event, ((Button) ew)
.getSelection());
}
break;
}
};
}
return buttonListener;
}
/**
* Returns the listener for SWT menu item widget events.
*
* @return a listener for menu item events
*/
private Listener getMenuItemListener() {
if (menuItemListener == null) {
menuItemListener = event -> {
switch (event.type) {
case SWT.Dispose:
handleWidgetDispose(event);
break;
case SWT.Selection:
Widget ew = event.widget;
if (ew != null) {
handleWidgetSelection(event, ((MenuItem) ew)
.getSelection());
}
break;
}
};
}
return menuItemListener;
}
/**
* Returns the presentation mode, which is the bitwise-or of the
* MODE_*
constants. The default mode setting is 0, meaning
* that for menu items, both text and image are shown (if present), but for
* tool items, the text is shown only if there is no image.
*
* @return the presentation mode settings
*
* @since 3.0
*/
public int getMode() {
return mode;
}
/**
* Returns the listener for SWT tool item widget events.
*
* @return a listener for tool item events
*/
private Listener getToolItemListener() {
if (toolItemListener == null) {
toolItemListener = event -> {
switch (event.type) {
case SWT.Dispose:
handleWidgetDispose(event);
break;
case SWT.Selection:
Widget ew = event.widget;
if (ew != null) {
handleWidgetSelection(event, ((ToolItem) ew)
.getSelection());
}
break;
}
};
}
return toolItemListener;
}
/**
* Handles a widget dispose event for the widget corresponding to this item.
*/
private void handleWidgetDispose(Event e) {
// Check if our widget is the one being disposed.
if (e.widget == widget) {
// Dispose of the menu creator.
if (action.getStyle() == IAction.AS_DROP_DOWN_MENU
&& menuCreatorCalled) {
IMenuCreator mc = action.getMenuCreator();
if (mc != null) {
mc.dispose();
}
}
// Unhook all of the listeners.
action.removePropertyChangeListener(propertyListener);
if (action != null) {
String commandId = action.getActionDefinitionId();
ExternalActionManager.ICallback callback = ExternalActionManager
.getInstance().getCallback();
if ((callback != null) && (commandId != null)) {
callback.removePropertyChangeListener(commandId,
actionTextListener);
}
}
// Clear the widget field.
widget = null;
disposeOldImages();
}
}
/**
* Handles a widget selection event.
*/
private void handleWidgetSelection(Event e, boolean selection) {
Widget item = e.widget;
if (item != null) {
int style = item.getStyle();
if ((style & (SWT.TOGGLE | SWT.CHECK)) != 0) {
if (action.getStyle() == IAction.AS_CHECK_BOX) {
action.setChecked(selection);
}
} else if ((style & SWT.RADIO) != 0) {
if (action.getStyle() == IAction.AS_RADIO_BUTTON) {
action.setChecked(selection);
}
} else if ((style & SWT.DROP_DOWN) != 0) {
if (e.detail == 4) { // on drop-down button
if (action.getStyle() == IAction.AS_DROP_DOWN_MENU) {
IMenuCreator mc = action.getMenuCreator();
menuCreatorCalled = true;
ToolItem ti = (ToolItem) item;
// we create the menu as a sub-menu of "dummy" so that
// we can use
// it in a cascading menu too.
// If created on a SWT control we would get an SWT
// error...
// Menu dummy= new Menu(ti.getParent());
// Menu m= mc.getMenu(dummy);
// dummy.dispose();
if (mc != null) {
Menu m = mc.getMenu(ti.getParent());
if (m != null) {
// position the menu below the drop down item
Point point = ti.getParent().toDisplay(
new Point(e.x, e.y));
m.setLocation(point.x, point.y); // waiting
// for SWT
// 0.42
m.setVisible(true);
return; // we don't fire the action
}
}
}
}
}
ExternalActionManager.IExecuteCallback callback = null;
String actionDefinitionId = action.getActionDefinitionId();
if (actionDefinitionId != null) {
Object obj = ExternalActionManager.getInstance()
.getCallback();
if (obj instanceof ExternalActionManager.IExecuteCallback) {
callback = (ExternalActionManager.IExecuteCallback) obj;
}
}
// Ensure action is enabled first.
// See 1GAN3M6: ITPUI:WINNT - Any IAction in the workbench can be
// executed while disabled.
if (action.isEnabled()) {
boolean trace = Policy.TRACE_ACTIONS;
long ms = 0L;
if (trace) {
ms = System.currentTimeMillis();
System.out.println("Running action: " + action.getText()); //$NON-NLS-1$
}
IPropertyChangeListener resultListener = null;
if (callback != null) {
resultListener = event -> {
// Check on result
if (event.getProperty().equals(IAction.RESULT)) {
if (event.getNewValue() instanceof Boolean) {
result = (Boolean) event.getNewValue();
}
}
};
action.addPropertyChangeListener(resultListener);
callback.preExecute(action, e);
}
action.runWithEvent(e);
if (callback != null) {
if (result == null || result.equals(Boolean.TRUE)) {
callback.postExecuteSuccess(action, Boolean.TRUE);
} else {
callback.postExecuteFailure(action,
new ExecutionException(action.getText()
+ " returned failure.")); //$NON-NLS-1$
}
}
if (resultListener!=null) {
result = null;
action.removePropertyChangeListener(resultListener);
}
if (trace) {
System.out.println((System.currentTimeMillis() - ms)
+ " ms to run action: " + action.getText()); //$NON-NLS-1$
}
} else {
if (callback != null) {
callback.notEnabled(action, new NotEnabledException(action
.getText()
+ " is not enabled.")); //$NON-NLS-1$
}
}
}
}
@Override
public int hashCode() {
return action.hashCode();
}
/**
* Returns whether the given action has any images.
*
* @param actionToCheck
* the action
* @return true
if the action has any images,
* false
if not
*/
private boolean hasImages(IAction actionToCheck) {
return actionToCheck.getImageDescriptor() != null
|| actionToCheck.getHoverImageDescriptor() != null
|| actionToCheck.getDisabledImageDescriptor() != null;
}
/**
* Returns whether the command corresponding to this action is active.
*/
private boolean isCommandActive() {
IAction actionToCheck = getAction();
if (actionToCheck != null) {
String commandId = actionToCheck.getActionDefinitionId();
ExternalActionManager.ICallback callback = ExternalActionManager
.getInstance().getCallback();
if (callback != null) {
return callback.isActive(commandId);
}
}
return true;
}
/**
* The action item implementation of this IContributionItem
* method returns true
for menu items and false
* for everything else.
*/
@Override
public boolean isDynamic() {
if (widget instanceof MenuItem) {
// Optimization. Only recreate the item is the check or radio style
// has changed.
boolean itemIsCheck = (widget.getStyle() & SWT.CHECK) != 0;
boolean actionIsCheck = getAction() != null
&& getAction().getStyle() == IAction.AS_CHECK_BOX;
boolean itemIsRadio = (widget.getStyle() & SWT.RADIO) != 0;
boolean actionIsRadio = getAction() != null
&& getAction().getStyle() == IAction.AS_RADIO_BUTTON;
return (itemIsCheck != actionIsCheck)
|| (itemIsRadio != actionIsRadio);
}
return false;
}
@Override
public boolean isEnabled() {
return action != null && action.isEnabled();
}
/**
* Returns true
if this item is allowed to enable,
* false
otherwise.
*
* @return if this item is allowed to be enabled
* @since 2.0
*/
protected boolean isEnabledAllowed() {
if (getParent() == null) {
return true;
}
Boolean value = getParent().getOverrides().getEnabled(this);
return (value == null) ? true : value.booleanValue();
}
/**
* The ActionContributionItem
implementation of this
* ContributionItem
method extends the super implementation
* by also checking whether the command corresponding to this action is
* active.
*/
@Override
public boolean isVisible() {
return super.isVisible() && isCommandActive();
}
/**
* Sets the presentation mode, which is the bitwise-or of the
* MODE_*
constants.
*
* @param mode
* the presentation mode settings
*
* @since 3.0
*/
public void setMode(int mode) {
this.mode = mode;
update();
}
/**
* The action item implementation of this IContributionItem
* method calls update(null)
.
*/
@Override
public final void update() {
update(null);
}
/**
* Synchronizes the UI with the given property.
*
* @param propertyName
* the name of the property, or null
meaning all
* applicable properties
*/
@Override
public void update(String propertyName) {
if (widget != null) {
// determine what to do
boolean textChanged = propertyName == null
|| propertyName.equals(IAction.TEXT);
boolean imageChanged = propertyName == null
|| propertyName.equals(IAction.IMAGE);
boolean tooltipTextChanged = propertyName == null
|| propertyName.equals(IAction.TOOL_TIP_TEXT);
boolean enableStateChanged = propertyName == null
|| propertyName.equals(IAction.ENABLED)
|| propertyName
.equals(IContributionManagerOverrides.P_ENABLED);
boolean checkChanged = (action.getStyle() == IAction.AS_CHECK_BOX || action
.getStyle() == IAction.AS_RADIO_BUTTON)
&& (propertyName == null || propertyName
.equals(IAction.CHECKED));
if (widget instanceof ToolItem) {
ToolItem ti = (ToolItem) widget;
String text = action.getText();
// the set text is shown only if there is no image or if forced
// by MODE_FORCE_TEXT
boolean showText = text != null
&& ((getMode() & MODE_FORCE_TEXT) != 0 || !hasImages(action));
// only do the trimming if the text will be used
if (showText && text != null) {
text = Action.removeAcceleratorText(text);
text = Action.removeMnemonics(text);
}
if (textChanged) {
String textToSet = showText ? text : ""; //$NON-NLS-1$
boolean rightStyle = (ti.getParent().getStyle() & SWT.RIGHT) != 0;
if (rightStyle || !ti.getText().equals(textToSet)) {
// In addition to being required to update the text if
// it
// gets nulled out in the action, this is also a
// workaround
// for bug 50151: Using SWT.RIGHT on a ToolBar leaves
// blank space
ti.setText(textToSet);
}
}
if (imageChanged) {
// only substitute a missing image if it has no text
updateImages(!showText);
}
if (tooltipTextChanged || textChanged) {
String toolTip = action.getToolTipText();
if ((toolTip == null) || (toolTip.length() == 0)) {
toolTip = text;
}
ExternalActionManager.ICallback callback = ExternalActionManager
.getInstance().getCallback();
String commandId = action.getActionDefinitionId();
if ((callback != null) && (commandId != null)
&& (toolTip != null)) {
String acceleratorText = callback
.getAcceleratorText(commandId);
if (acceleratorText != null
&& acceleratorText.length() != 0) {
toolTip = JFaceResources.format(
"Toolbar_Tooltip_Accelerator", //$NON-NLS-1$
toolTip, acceleratorText);
}
}
// if the text is showing, then only set the tooltip if
// different
if (!showText || toolTip != null && !toolTip.equals(text)) {
ti.setToolTipText(toolTip);
} else {
ti.setToolTipText(null);
}
}
if (enableStateChanged) {
boolean shouldBeEnabled = action.isEnabled()
&& isEnabledAllowed();
if (ti.getEnabled() != shouldBeEnabled) {
ti.setEnabled(shouldBeEnabled);
}
}
if (checkChanged) {
boolean bv = action.isChecked();
if (ti.getSelection() != bv) {
ti.setSelection(bv);
}
}
return;
}
if (widget instanceof MenuItem) {
MenuItem mi = (MenuItem) widget;
if (textChanged) {
int accelerator = 0;
String acceleratorText = null;
IAction updatedAction = getAction();
String text = null;
accelerator = updatedAction.getAccelerator();
ExternalActionManager.ICallback callback = ExternalActionManager
.getInstance().getCallback();
// Block accelerators that are already in use.
if ((accelerator != 0) && (callback != null)
&& (callback.isAcceleratorInUse(accelerator))) {
accelerator = 0;
}
/*
* Process accelerators on GTK in a special way to avoid Bug
* 42009. We will override the native input method by
* allowing these reserved accelerators to be placed on the
* menu. We will only do this for "Ctrl+Shift+[0-9A-FU]".
*/
final String commandId = updatedAction
.getActionDefinitionId();
if ((Util.isGtk()) && (callback instanceof IBindingManagerCallback)
&& (commandId != null)) {
final IBindingManagerCallback bindingManagerCallback = (IBindingManagerCallback) callback;
final IKeyLookup lookup = KeyLookupFactory.getDefault();
final TriggerSequence[] triggerSequences = bindingManagerCallback
.getActiveBindingsFor(commandId);
for (int i = 0; i < triggerSequences.length; i++) {
final TriggerSequence triggerSequence = triggerSequences[i];
final Trigger[] triggers = triggerSequence
.getTriggers();
if (triggers.length == 1) {
final Trigger trigger = triggers[0];
if (trigger instanceof KeyStroke) {
final KeyStroke currentKeyStroke = (KeyStroke) trigger;
final int currentNaturalKey = currentKeyStroke
.getNaturalKey();
if ((currentKeyStroke.getModifierKeys() == (lookup
.getCtrl() | lookup.getShift()))
&& ((currentNaturalKey >= '0' && currentNaturalKey <= '9')
|| (currentNaturalKey >= 'A' && currentNaturalKey <= 'F') || (currentNaturalKey == 'U'))) {
accelerator = currentKeyStroke
.getModifierKeys()
| currentNaturalKey;
acceleratorText = triggerSequence
.format();
break;
}
}
}
}
}
if (accelerator == 0) {
if ((callback != null) && (commandId != null)) {
acceleratorText = callback
.getAcceleratorText(commandId);
}
}
IContributionManagerOverrides overrides = null;
if (getParent() != null) {
overrides = getParent().getOverrides();
}
if (overrides != null) {
text = getParent().getOverrides().getText(this);
}
mi.setAccelerator(accelerator);
if (text == null) {
text = updatedAction.getText();
}
if (text != null && acceleratorText == null) {
// use extracted accelerator text in case accelerator
// cannot be fully represented in one int (e.g.
// multi-stroke keys)
acceleratorText = LegacyActionTools
.extractAcceleratorText(text);
if (acceleratorText == null && accelerator != 0) {
acceleratorText = Action
.convertAccelerator(accelerator);
}
}
if (text == null) {
text = ""; //$NON-NLS-1$
} else {
text = Action.removeAcceleratorText(text);
}
if (acceleratorText == null) {
mi.setText(text);
} else {
mi.setText(text + '\t' + acceleratorText);
}
}
if (imageChanged) {
updateImages(false);
}
if (enableStateChanged) {
boolean shouldBeEnabled = action.isEnabled()
&& isEnabledAllowed();
if (mi.getEnabled() != shouldBeEnabled) {
mi.setEnabled(shouldBeEnabled);
}
}
if (checkChanged) {
boolean bv = action.isChecked();
if (mi.getSelection() != bv) {
mi.setSelection(bv);
}
}
return;
}
if (widget instanceof Button) {
Button button = (Button) widget;
if (imageChanged) {
updateImages(false);
}
if (textChanged) {
String text = action.getText();
boolean showText = text != null && ((getMode() & MODE_FORCE_TEXT) != 0 || !hasImages(action));
// only do the trimming if the text will be used
if (showText) {
text = Action.removeAcceleratorText(text);
}
String textToSet = showText ? text : ""; //$NON-NLS-1$
button.setText(textToSet);
}
if (tooltipTextChanged) {
button.setToolTipText(action.getToolTipText());
}
if (enableStateChanged) {
boolean shouldBeEnabled = action.isEnabled()
&& isEnabledAllowed();
if (button.getEnabled() != shouldBeEnabled) {
button.setEnabled(shouldBeEnabled);
}
}
if (checkChanged) {
boolean bv = action.isChecked();
if (button.getSelection() != bv) {
button.setSelection(bv);
}
}
return;
}
}
}
/**
* Updates the images for this action.
*
* @param forceImage
* true
if some form of image is compulsory, and
* false
if it is acceptable for this item to have
* no image
* @return true
if there are images for this action,
* false
if not
*/
private boolean updateImages(boolean forceImage) {
ResourceManager parentResourceManager = JFaceResources.getResources();
if (widget instanceof ToolItem) {
if (USE_COLOR_ICONS) {
ImageDescriptor image = action.getHoverImageDescriptor();
if (image == null) {
image = action.getImageDescriptor();
}
ImageDescriptor disabledImage = action
.getDisabledImageDescriptor();
// Make sure there is a valid image.
if (image == null && forceImage) {
image = ImageDescriptor.getMissingImageDescriptor();
}
LocalResourceManager localManager = new LocalResourceManager(
parentResourceManager);
// performance: more efficient in SWT to set disabled and hot
// image before regular image
((ToolItem) widget)
.setDisabledImage(disabledImage == null ? null
: localManager
.createImageWithDefault(disabledImage));
((ToolItem) widget).setImage(image == null ? null
: localManager.createImageWithDefault(image));
disposeOldImages();
imageManager = localManager;
return image != null;
}
ImageDescriptor image = action.getImageDescriptor();
ImageDescriptor hoverImage = action.getHoverImageDescriptor();
ImageDescriptor disabledImage = action.getDisabledImageDescriptor();
// If there is no regular image, but there is a hover image,
// convert the hover image to gray and use it as the regular image.
if (image == null && hoverImage != null) {
image = ImageDescriptor.createWithFlags(action
.getHoverImageDescriptor(), SWT.IMAGE_GRAY);
} else {
// If there is no hover image, use the regular image as the
// hover image,
// and convert the regular image to gray
if (hoverImage == null && image != null) {
hoverImage = image;
image = ImageDescriptor.createWithFlags(action
.getImageDescriptor(), SWT.IMAGE_GRAY);
}
}
// Make sure there is a valid image.
if (hoverImage == null && image == null && forceImage) {
image = ImageDescriptor.getMissingImageDescriptor();
}
// Create a local resource manager to remember the images we've
// allocated for this tool item
LocalResourceManager localManager = new LocalResourceManager(
parentResourceManager);
// performance: more efficient in SWT to set disabled and hot image
// before regular image
((ToolItem) widget).setDisabledImage(disabledImage == null ? null
: localManager.createImageWithDefault(disabledImage));
((ToolItem) widget).setHotImage(hoverImage == null ? null
: localManager.createImageWithDefault(hoverImage));
((ToolItem) widget).setImage(image == null ? null : localManager
.createImageWithDefault(image));
// Now that we're no longer referencing the old images, clear them
// out.
disposeOldImages();
imageManager = localManager;
return image != null;
} else if (widget instanceof Item || widget instanceof Button) {
// Use hover image if there is one, otherwise use regular image.
ImageDescriptor image = action.getHoverImageDescriptor();
if (image == null) {
image = action.getImageDescriptor();
}
// Make sure there is a valid image.
if (image == null && forceImage) {
image = ImageDescriptor.getMissingImageDescriptor();
}
// Create a local resource manager to remember the images we've
// allocated for this widget
LocalResourceManager localManager = new LocalResourceManager(
parentResourceManager);
if (widget instanceof Item) {
((Item) widget).setImage(image == null ? null : localManager
.createImageWithDefault(image));
} else if (widget instanceof Button) {
((Button) widget).setImage(image == null ? null : localManager
.createImageWithDefault(image));
}
// Now that we're no longer referencing the old images, clear them
// out.
disposeOldImages();
imageManager = localManager;
return image != null;
}
return false;
}
/**
* Dispose any images allocated for this contribution item
*/
private void disposeOldImages() {
if (imageManager != null) {
imageManager.dispose();
imageManager = null;
}
}
/**
* Shorten the given text t
so that its length doesn't exceed
* the width of the given ToolItem.The default implementation replaces
* characters in the center of the original string with an ellipsis ("...").
* Override if you need a different strategy.
*
* @param textValue
* the text to shorten
* @param item
* the tool item the text belongs to
* @return the shortened string
*
*/
protected String shortenText(String textValue, ToolItem item) {
if (textValue == null) {
return null;
}
GC gc = new GC(item.getParent());
int maxWidth = item.getImage().getBounds().width * 4;
if (gc.textExtent(textValue).x < maxWidth) {
gc.dispose();
return textValue;
}
for (int i = textValue.length(); i > 0; i--) {
String test = textValue.substring(0, i);
test = test + ellipsis;
if (gc.textExtent(test).x < maxWidth) {
gc.dispose();
return test;
}
}
gc.dispose();
// If for some reason we fall through abort
return textValue;
}
@Override
public void dispose() {
if (widget != null) {
widget.dispose();
widget = null;
}
holdMenu = null;
}
/**
* Handle show and hide on the proxy menu for IAction.AS_DROP_DOWN_MENU
* actions.
*
* @return the appropriate listener
* @since 3.4
*/
private Listener getMenuCreatorListener() {
if (menuCreatorListener == null) {
menuCreatorListener = event -> {
switch (event.type) {
case SWT.Show:
handleShowProxy((Menu) event.widget);
break;
case SWT.Hide:
handleHideProxy((Menu) event.widget);
break;
}
};
}
return menuCreatorListener;
}
/**
* This is the easiest way to hold the menu until we can swap it in to the
* proxy.
*/
private Menu holdMenu = null;
private boolean menuCreatorCalled = false;
/**
* The proxy menu is being shown, we better get the real menu.
*
* @param proxy
* the proxy menu
* @since 3.4
*/
private void handleShowProxy(Menu proxy) {
proxy.removeListener(SWT.Show, getMenuCreatorListener());
IMenuCreator mc = action.getMenuCreator();
menuCreatorCalled = true;
if (mc == null) {
return;
}
holdMenu = mc.getMenu(proxy.getParentMenu());
if (holdMenu == null) {
return;
}
copyMenu(holdMenu, proxy);
}
/**
* Create MenuItems in the proxy menu that can execute the real menu items
* if selected. Create proxy menus for any real item submenus.
*
* @param realMenu
* the real menu to copy from
* @param proxy
* the proxy menu to populate
* @since 3.4
*/
private void copyMenu(Menu realMenu, Menu proxy) {
if (realMenu.isDisposed() || proxy.isDisposed()) {
return;
}
// we notify the real menu so it can populate itself if it was
// listening for SWT.Show
realMenu.notifyListeners(SWT.Show, null);
final Listener passThrough = event -> {
if (!event.widget.isDisposed()) {
Widget realItem = (Widget) event.widget.getData();
if (!realItem.isDisposed()) {
int style = event.widget.getStyle();
if (event.type == SWT.Selection
&& ((style & (SWT.TOGGLE | SWT.CHECK | SWT.RADIO)) != 0)
&& realItem instanceof MenuItem) {
((MenuItem) realItem)
.setSelection(((MenuItem) event.widget)
.getSelection());
}
event.widget = realItem;
realItem.notifyListeners(event.type, event);
}
}
};
MenuItem[] items = realMenu.getItems();
for (int i = 0; i < items.length; i++) {
final MenuItem realItem = items[i];
final MenuItem proxyItem = new MenuItem(proxy, realItem.getStyle());
proxyItem.setData(realItem);
proxyItem.setAccelerator(realItem.getAccelerator());
proxyItem.setEnabled(realItem.getEnabled());
proxyItem.setImage(realItem.getImage());
proxyItem.setSelection(realItem.getSelection());
proxyItem.setText(realItem.getText());
// pass through any events
proxyItem.addListener(SWT.Selection, passThrough);
proxyItem.addListener(SWT.Arm, passThrough);
proxyItem.addListener(SWT.Help, passThrough);
final Menu itemMenu = realItem.getMenu();
if (itemMenu != null) {
// create a proxy for any sub menu items
final Menu subMenu = new Menu(proxy);
subMenu.setData(itemMenu);
proxyItem.setMenu(subMenu);
subMenu.addListener(SWT.Show, new Listener() {
@Override
public void handleEvent(Event event) {
event.widget.removeListener(SWT.Show, this);
if (event.type == SWT.Show) {
copyMenu(itemMenu, subMenu);
}
}
});
subMenu.addListener(SWT.Help, passThrough);
subMenu.addListener(SWT.Hide, passThrough);
}
}
}
/**
* The proxy menu is being hidden, so we need to make it go away.
*
* @param proxy
* the proxy menu
* @since 3.4
*/
private void handleHideProxy(final Menu proxy) {
proxy.removeListener(SWT.Hide, getMenuCreatorListener());
proxy.getDisplay().asyncExec(() -> {
if (!proxy.isDisposed()) {
MenuItem parentItem = proxy.getParentItem();
proxy.dispose();
parentItem.setMenu(holdMenu);
}
if (holdMenu != null && !holdMenu.isDisposed()) {
holdMenu.notifyListeners(SWT.Hide, null);
}
holdMenu = null;
});
}
/**
* Return the widget associated with this contribution item. It should not
* be cached, as it can be disposed and re-created by its containing
* ContributionManager, which controls all of the widgets lifecycle methods.
*
* This can be used to set layout data on the widget if appropriate. The
* actual type of the widget can be any valid control for this
* ContributionItem's current ContributionManager.
*
*
* @return the widget, or null
depending on the lifecycle.
* @since 3.4
*/
public Widget getWidget() {
return widget;
}
}