Mercurial > projects > dwt-addons
diff dwtx/jface/action/ActionContributionItem.d @ 16:e0f0aaf75edd
PopupDialog, bindings and actions
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Tue, 01 Apr 2008 08:00:31 +0200 |
parents | |
children | 644f1334b451 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/action/ActionContributionItem.d Tue Apr 01 08:00:31 2008 +0200 @@ -0,0 +1,1136 @@ +/******************************************************************************* + * Copyright (c) 2000, 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 + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.jface.action.ActionContributionItem; + +import dwtx.jface.action.ContributionItem; +import dwtx.jface.action.IAction; +import dwtx.jface.action.LegacyActionTools; +import dwtx.jface.action.Action; +import dwtx.jface.action.IMenuCreator; +import dwtx.jface.action.IContributionManagerOverrides; + +import dwt.DWT; +import dwt.graphics.GC; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.widgets.Button; +import dwt.widgets.Composite; +import dwt.widgets.Display; +import dwt.widgets.Event; +import dwt.widgets.Item; +import dwt.widgets.Listener; +import dwt.widgets.Menu; +import dwt.widgets.MenuItem; +import dwt.widgets.ToolBar; +import dwt.widgets.ToolItem; +import dwt.widgets.Widget; +import dwtx.jface.action.ExternalActionManager; +import dwtx.jface.bindings.Trigger; +import dwtx.jface.bindings.TriggerSequence; +import dwtx.jface.bindings.keys.IKeyLookup; +import dwtx.jface.bindings.keys.KeyLookupFactory; +import dwtx.jface.bindings.keys.KeyStroke; +import dwtx.jface.resource.ImageDescriptor; +import dwtx.jface.resource.JFaceResources; +import dwtx.jface.resource.LocalResourceManager; +import dwtx.jface.resource.ResourceManager; +import dwtx.jface.util.IPropertyChangeListener; +import dwtx.jface.util.Policy; +import dwtx.jface.util.PropertyChangeEvent; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; +import tango.core.Thread; +import tango.io.Stdout; + +/** + * A contribution item which delegates to an action. + * <p> + * This class may be instantiated; it is not intended to be subclassed. + * </p> + */ +public class ActionContributionItem : ContributionItem { + + /** + * Mode bit: Show text on tool items, 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 const String ellipsis = "..."; //$NON-NLS-1$ + + private static bool USE_COLOR_ICONS = true; + + /** + * Returns whether color icons should be used in toolbars. + * + * @return <code>true</code> if color icons should be used in toolbars, + * <code>false</code> otherwise + */ + public static bool getUseColorIconsInToolbars() { + return USE_COLOR_ICONS; + } + + /** + * Sets whether color icons should be used in toolbars. + * + * @param useColorIcons + * <code>true</code> if color icons should be used in toolbars, + * <code>false</code> otherwise + */ + public static void setUseColorIconsInToolbars(bool 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 const IPropertyChangeListener actionTextListener; + + /** + * Remembers all images in use by this contribution item + */ + private LocalResourceManager imageManager; + + /** + * Listener for DWT button widget events. + */ + private Listener buttonListener; + + /** + * Listener for DWT menu item widget events. + */ + private Listener menuItemListener; + + /** + * Listener for action property change notifications. + */ + private const IPropertyChangeListener propertyListener; + + /** + * Listener for DWT tool item widget events. + */ + private Listener toolItemListener; + + /** + * The widget created for this item; <code>null</code> before creation and + * after disposal. + */ + private Widget widget = null; + + /** + * 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 this(IAction action) { + super(action.getId()); + this.action = action; + actionTextListener = new class IPropertyChangeListener { + /** + * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent) + */ + public void propertyChange(PropertyChangeEvent event) { + update(event.getProperty()); + } + }; + propertyListener = new class IPropertyChangeListener { + public void propertyChange(PropertyChangeEvent event) { + actionPropertyChange(event); + } + }; + } + + /** + * Handles a property change event on the action (forwarded by nested + * listener). + */ + private void actionPropertyChange(PropertyChangeEvent e) { + // This code should be removed. Avoid using free asyncExec + + if (isVisible() && widget !is null) { + Display display = widget.getDisplay(); + if (display.getThread() is Thread.getThis()) { + update(e.getProperty()); + } else { + display.asyncExec(new class Runnable { + PropertyChangeEvent e_; + this(){ + e_=e; + } + public void run() { + update(e_.getProperty()); + } + }); + } + + } + } + + /** + * Compares this action contribution item with another object. Two action + * contribution items are equal if they refer to the identical Action. + */ + public override int opEquals(Object o) { + if (!(cast(ActionContributionItem)o )) { + return false; + } + return (cast(Object)action).opEquals(cast(Object)(cast(ActionContributionItem) o).action); + } + + /** + * The <code>ActionContributionItem</code> implementation of this + * <code>IContributionItem</code> method creates an DWT + * <code>Button</code> 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. + */ + public void fill(Composite parent) { + if (widget is null && parent !is null) { + int flags = DWT.PUSH; + if (action !is null) { + if (action.getStyle() is IAction.AS_CHECK_BOX) { + flags = DWT.TOGGLE; + } + if (action.getStyle() is IAction.AS_RADIO_BUTTON) { + flags = DWT.RADIO; + } + } + + Button b = new Button(parent, flags); + b.setData(this); + b.addListener(DWT.Dispose, getButtonListener()); + // Don't hook a dispose listener on the parent + b.addListener(DWT.Selection, getButtonListener()); + if (action.getHelpListener() !is null) { + b.addHelpListener(action.getHelpListener()); + } + widget = b; + + update(null); + + // Attach some extra listeners. + action.addPropertyChangeListener(propertyListener); + if (action !is null) { + String commandId = action.getActionDefinitionId(); + ExternalActionManager.ICallback callback = ExternalActionManager + .getInstance().getCallback(); + + if ((callback !is null) && (commandId !is null)) { + callback.addPropertyChangeListener(commandId, + actionTextListener); + } + } + } + } + + /** + * The <code>ActionContributionItem</code> implementation of this + * <code>IContributionItem</code> method creates an DWT + * <code>MenuItem</code> 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. + */ + public void fill(Menu parent, int index) { + if (widget is null && parent !is null) { + Menu subMenu = null; + int flags = DWT.PUSH; + if (action !is null) { + int style = action.getStyle(); + if (style is IAction.AS_CHECK_BOX) { + flags = DWT.CHECK; + } else if (style is IAction.AS_RADIO_BUTTON) { + flags = DWT.RADIO; + } else if (style is IAction.AS_DROP_DOWN_MENU) { + IMenuCreator mc = action.getMenuCreator(); + if (mc !is null) { + subMenu = mc.getMenu(parent); + flags = DWT.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(DWT.Dispose, getMenuItemListener()); + mi.addListener(DWT.Selection, getMenuItemListener()); + if (action.getHelpListener() !is null) { + mi.addHelpListener(action.getHelpListener()); + } + + if (subMenu !is null) { + mi.setMenu(subMenu); + } + + update(null); + + // Attach some extra listeners. + action.addPropertyChangeListener(propertyListener); + if (action !is null) { + String commandId = action.getActionDefinitionId(); + ExternalActionManager.ICallback callback = ExternalActionManager + .getInstance().getCallback(); + + if ((callback !is null) && (commandId !is null)) { + callback.addPropertyChangeListener(commandId, + actionTextListener); + } + } + } + } + + /** + * The <code>ActionContributionItem</code> implementation of this , + * <code>IContributionItem</code> method creates an DWT + * <code>ToolItem</code> 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. + */ + public void fill(ToolBar parent, int index) { + if (widget is null && parent !is null) { + int flags = DWT.PUSH; + if (action !is null) { + int style = action.getStyle(); + if (style is IAction.AS_CHECK_BOX) { + flags = DWT.CHECK; + } else if (style is IAction.AS_RADIO_BUTTON) { + flags = DWT.RADIO; + } else if (style is IAction.AS_DROP_DOWN_MENU) { + flags = DWT.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(DWT.Selection, getToolItemListener()); + ti.addListener(DWT.Dispose, getToolItemListener()); + + widget = ti; + + update(null); + + // Attach some extra listeners. + action.addPropertyChangeListener(propertyListener); + if (action !is null) { + String commandId = action.getActionDefinitionId(); + ExternalActionManager.ICallback callback = ExternalActionManager + .getInstance().getCallback(); + + if ((callback !is null) && (commandId !is 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 DWT button widget events. + * + * @return a listener for button events + */ + private Listener getButtonListener() { + if (buttonListener is null) { + buttonListener = new class Listener { + public void handleEvent(Event event) { + switch (event.type) { + case DWT.Dispose: + handleWidgetDispose(event); + break; + case DWT.Selection: + Widget ew = event.widget; + if (ew !is null) { + handleWidgetSelection(event, (cast(Button) ew) + .getSelection()); + } + break; + } + } + }; + } + return buttonListener; + } + + /** + * Returns the listener for DWT menu item widget events. + * + * @return a listener for menu item events + */ + private Listener getMenuItemListener() { + if (menuItemListener is null) { + menuItemListener = new class Listener { + public void handleEvent(Event event) { + switch (event.type) { + case DWT.Dispose: + handleWidgetDispose(event); + break; + case DWT.Selection: + Widget ew = event.widget; + if (ew !is null) { + handleWidgetSelection(event, (cast(MenuItem) ew) + .getSelection()); + } + break; + } + } + }; + } + return menuItemListener; + } + + /** + * Returns the presentation mode, which is the bitwise-or of the + * <code>MODE_*</code> 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 DWT tool item widget events. + * + * @return a listener for tool item events + */ + private Listener getToolItemListener() { + if (toolItemListener is null) { + toolItemListener = new class Listener { + public void handleEvent(Event event) { + switch (event.type) { + case DWT.Dispose: + handleWidgetDispose(event); + break; + case DWT.Selection: + Widget ew = event.widget; + if (ew !is null) { + handleWidgetSelection(event, (cast(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 is widget) { + // Dispose of the menu creator. + if (action.getStyle() is IAction.AS_DROP_DOWN_MENU) { + IMenuCreator mc = action.getMenuCreator(); + if (mc !is null) { + mc.dispose(); + } + } + + // Unhook all of the listeners. + action.removePropertyChangeListener(propertyListener); + if (action !is null) { + String commandId = action.getActionDefinitionId(); + ExternalActionManager.ICallback callback = ExternalActionManager + .getInstance().getCallback(); + + if ((callback !is null) && (commandId !is null)) { + callback.removePropertyChangeListener(commandId, + actionTextListener); + } + } + + // Clear the widget field. + widget = null; + + disposeOldImages(); + } + } + + /** + * Handles a widget selection event. + */ + private void handleWidgetSelection(Event e, bool selection) { + + Widget item = e.widget; + if (item !is null) { + int style = item.getStyle(); + + if ((style & (DWT.TOGGLE | DWT.CHECK)) !is 0) { + if (action.getStyle() is IAction.AS_CHECK_BOX) { + action.setChecked(selection); + } + } else if ((style & DWT.RADIO) !is 0) { + if (action.getStyle() is IAction.AS_RADIO_BUTTON) { + action.setChecked(selection); + } + } else if ((style & DWT.DROP_DOWN) !is 0) { + if (e.detail is 4) { // on drop-down button + if (action.getStyle() is IAction.AS_DROP_DOWN_MENU) { + IMenuCreator mc = action.getMenuCreator(); + ToolItem ti = cast(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 DWT control we would get an DWT + // error... + // Menu dummy= new Menu(ti.getParent()); + // Menu m= mc.getMenu(dummy); + // dummy.dispose(); + if (mc !is null) { + Menu m = mc.getMenu(ti.getParent()); + if (m !is null) { + // position the menu below the drop down item + Rectangle b = ti.getBounds(); + Point p = ti.getParent().toDisplay( + new Point(b.x, b.y + b.height)); + m.setLocation(p.x, p.y); // waiting for DWT + // 0.42 + m.setVisible(true); + return; // we don't fire the action + } + } + } + } + } + + // Ensure action is enabled first. + // See 1GAN3M6: ITPUI:WINNT - Any IAction in the workbench can be + // executed while disabled. + if (action.isEnabled()) { + bool trace = Policy.TRACE_ACTIONS; + + long ms = 0L; + if (trace) { + ms = System.currentTimeMillis(); + Stdout.formatln("Running action: {}", action.getText()); //$NON-NLS-1$ + } + + action.runWithEvent(e); + + if (trace) { + Stdout.formatln("{} ms to run action: {}",(System.currentTimeMillis() - ms), action.getText()); //$NON-NLS-1$ + } + } + } + } + + /* + * (non-Javadoc) Method declared on Object. + */ + public override hash_t toHash() { + return (cast(Object)action).toHash(); + } + + /** + * Returns whether the given action has any images. + * + * @param actionToCheck + * the action + * @return <code>true</code> if the action has any images, + * <code>false</code> if not + */ + private bool hasImages(IAction actionToCheck) { + return actionToCheck.getImageDescriptor() !is null + || actionToCheck.getHoverImageDescriptor() !is null + || actionToCheck.getDisabledImageDescriptor() !is null; + } + + /** + * Returns whether the command corresponding to this action is active. + */ + private bool isCommandActive() { + IAction actionToCheck = getAction(); + + if (actionToCheck !is null) { + String commandId = actionToCheck.getActionDefinitionId(); + ExternalActionManager.ICallback callback = ExternalActionManager + .getInstance().getCallback(); + + if (callback !is null) { + return callback.isActive(commandId); + } + } + return true; + } + + /** + * The action item implementation of this <code>IContributionItem</code> + * method returns <code>true</code> for menu items and <code>false</code> + * for everything else. + */ + public bool isDynamic() { + if (cast(MenuItem)widget ) { + // Optimization. Only recreate the item is the check or radio style + // has changed. + bool itemIsCheck = (widget.getStyle() & DWT.CHECK) !is 0; + bool actionIsCheck = getAction() !is null + && getAction().getStyle() is IAction.AS_CHECK_BOX; + bool itemIsRadio = (widget.getStyle() & DWT.RADIO) !is 0; + bool actionIsRadio = getAction() !is null + && getAction().getStyle() is IAction.AS_RADIO_BUTTON; + return (itemIsCheck !is actionIsCheck) + || (itemIsRadio !is actionIsRadio); + } + return false; + } + + /* + * (non-Javadoc) Method declared on IContributionItem. + */ + public bool isEnabled() { + return action !is null && action.isEnabled(); + } + + /** + * Returns <code>true</code> if this item is allowed to enable, + * <code>false</code> otherwise. + * + * @return if this item is allowed to be enabled + * @since 2.0 + */ + protected bool isEnabledAllowed() { + if (getParent() is null) { + return true; + } + auto value = getParent().getOverrides().getEnabled(this); + return (value is null) ? true : value.value; + } + + /** + * The <code>ActionContributionItem</code> implementation of this + * <code>ContributionItem</code> method extends the super implementation + * by also checking whether the command corresponding to this action is + * active. + */ + public bool isVisible() { + return super.isVisible() && isCommandActive(); + } + + /** + * Sets the presentation mode, which is the bitwise-or of the + * <code>MODE_*</code> 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 <code>IContributionItem</code> + * method calls <code>update(null)</code>. + */ + public final void update() { + update(null); + } + + /** + * Synchronizes the UI with the given property. + * + * @param propertyName + * the name of the property, or <code>null</code> meaning all + * applicable properties + */ + public void update(String propertyName) { + if (widget !is null) { + // determine what to do + bool textChanged = propertyName is null + || propertyName.equals(IAction.TEXT); + bool imageChanged = propertyName is null + || propertyName.equals(IAction.IMAGE); + bool tooltipTextChanged = propertyName is null + || propertyName.equals(IAction.TOOL_TIP_TEXT); + bool enableStateChanged = propertyName is null + || propertyName.equals(IAction.ENABLED) + || propertyName + .equals(IContributionManagerOverrides.P_ENABLED); + bool checkChanged = (action.getStyle() is IAction.AS_CHECK_BOX || action + .getStyle() is IAction.AS_RADIO_BUTTON) + && (propertyName is null || propertyName + .equals(IAction.CHECKED)); + + if (cast(ToolItem)widget ) { + ToolItem ti = cast(ToolItem) widget; + String text = action.getText(); + // the set text is shown only if there is no image or if forced + // by MODE_FORCE_TEXT + bool showText = text !is null + && ((getMode() & MODE_FORCE_TEXT) !is 0 || !hasImages(action)); + + // only do the trimming if the text will be used + if (showText && text !is null) { + text = Action.removeAcceleratorText(text); + text = Action.removeMnemonics(text); + } + + if (textChanged) { + String textToSet = showText ? text : ""; //$NON-NLS-1$ + bool rightStyle = (ti.getParent().getStyle() & DWT.RIGHT) !is 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 DWT.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 is null) || (toolTip.length is 0)) { + toolTip = text; + } + + ExternalActionManager.ICallback callback = ExternalActionManager + .getInstance().getCallback(); + String commandId = action.getActionDefinitionId(); + if ((callback !is null) && (commandId !is null) && (toolTip !is null)) { + String acceleratorText = callback.getAcceleratorText(commandId); + if (acceleratorText !is null && acceleratorText.length !is 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 !is null && !toolTip.equals(text)) { + ti.setToolTipText(toolTip); + } else { + ti.setToolTipText(null); + } + } + + if (enableStateChanged) { + bool shouldBeEnabled = action.isEnabled() + && isEnabledAllowed(); + + if (ti.getEnabled() !is shouldBeEnabled) { + ti.setEnabled(shouldBeEnabled); + } + } + + if (checkChanged) { + bool bv = action.isChecked(); + + if (ti.getSelection() !is bv) { + ti.setSelection(bv); + } + } + return; + } + + if (cast(MenuItem)widget ) { + MenuItem mi = cast(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 !is 0) && (callback !is 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]". + */ + String commandId = updatedAction + .getActionDefinitionId(); + if (("gtk".equals(DWT.getPlatform())) && (cast(ExternalActionManager.IBindingManagerCallback)callback ) //$NON-NLS-1$ + && (commandId !is null)) { + ExternalActionManager.IBindingManagerCallback bindingManagerCallback = cast(ExternalActionManager.IBindingManagerCallback) callback; + IKeyLookup lookup = KeyLookupFactory.getDefault(); + TriggerSequence[] triggerSequences = bindingManagerCallback + .getActiveBindingsFor(commandId); + for (int i = 0; i < triggerSequences.length; i++) { + TriggerSequence triggerSequence = triggerSequences[i]; + Trigger[] triggers = triggerSequence + .getTriggers(); + if (triggers.length is 1) { + Trigger trigger = triggers[0]; + if (cast(KeyStroke)trigger ) { + KeyStroke currentKeyStroke = cast(KeyStroke) trigger; + int currentNaturalKey = currentKeyStroke + .getNaturalKey(); + if ((currentKeyStroke.getModifierKeys() is (lookup + .getCtrl() | lookup.getShift())) + && ((currentNaturalKey >= '0' && currentNaturalKey <= '9') + || (currentNaturalKey >= 'A' && currentNaturalKey <= 'F') || (currentNaturalKey is 'U'))) { + accelerator = currentKeyStroke + .getModifierKeys() + | currentNaturalKey; + acceleratorText = triggerSequence + .format(); + break; + } + } + } + } + } + + if (accelerator is 0) { + if ((callback !is null) && (commandId !is null)) { + acceleratorText = callback + .getAcceleratorText(commandId); + } + } + + IContributionManagerOverrides overrides = null; + + if (getParent() !is null) { + overrides = getParent().getOverrides(); + } + + if (overrides !is null) { + text = getParent().getOverrides().getText(this); + } + + mi.setAccelerator(accelerator); + + if (text is null) { + text = updatedAction.getText(); + } + + if (text !is null && acceleratorText is 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 is null && accelerator !is 0) { + acceleratorText= Action.convertAccelerator(accelerator); + } + } + + if (text is null) { + text = ""; //$NON-NLS-1$ + } else { + text = Action.removeAcceleratorText(text); + } + + if (acceleratorText is null) { + mi.setText(text); + } else { + mi.setText(text ~ '\t' ~ acceleratorText); + } + } + + if (imageChanged) { + updateImages(false); + } + + if (enableStateChanged) { + bool shouldBeEnabled = action.isEnabled() + && isEnabledAllowed(); + + if (mi.getEnabled() !is shouldBeEnabled) { + mi.setEnabled(shouldBeEnabled); + } + } + + if (checkChanged) { + bool bv = action.isChecked(); + + if (mi.getSelection() !is bv) { + mi.setSelection(bv); + } + } + + return; + } + + if (cast(Button)widget ) { + Button button = cast(Button) widget; + + if (imageChanged && updateImages(false)) { + textChanged = false; // don't update text if it has an + // image + } + + if (textChanged) { + String text = action.getText(); + if (text is null) { + text = ""; //$NON-NLS-1$ + } else { + text = Action.removeAcceleratorText(text); + } + button.setText(text); + } + + if (tooltipTextChanged) { + button.setToolTipText(action.getToolTipText()); + } + + if (enableStateChanged) { + bool shouldBeEnabled = action.isEnabled() + && isEnabledAllowed(); + + if (button.getEnabled() !is shouldBeEnabled) { + button.setEnabled(shouldBeEnabled); + } + } + + if (checkChanged) { + bool bv = action.isChecked(); + + if (button.getSelection() !is bv) { + button.setSelection(bv); + } + } + return; + } + } + } + + /** + * Updates the images for this action. + * + * @param forceImage + * <code>true</code> if some form of image is compulsory, and + * <code>false</code> if it is acceptable for this item to have + * no image + * @return <code>true</code> if there are images for this action, + * <code>false</code> if not + */ + private bool updateImages(bool forceImage) { + + ResourceManager parentResourceManager = JFaceResources.getResources(); + + if (cast(ToolItem)widget ) { + if (USE_COLOR_ICONS) { + ImageDescriptor image = action.getHoverImageDescriptor(); + if (image is null) { + image = action.getImageDescriptor(); + } + ImageDescriptor disabledImage = action + .getDisabledImageDescriptor(); + + // Make sure there is a valid image. + if (image is null && forceImage) { + image = ImageDescriptor.getMissingImageDescriptor(); + } + + LocalResourceManager localManager = new LocalResourceManager( + parentResourceManager); + + // performance: more efficient in DWT to set disabled and hot + // image before regular image + (cast(ToolItem) widget) + .setDisabledImage(disabledImage is null ? null + : localManager + .createImageWithDefault(disabledImage)); + (cast(ToolItem) widget).setImage(image is null ? null + : localManager.createImageWithDefault(image)); + + disposeOldImages(); + imageManager = localManager; + + return image !is 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 is null && hoverImage !is null) { + image = ImageDescriptor.createWithFlags(action + .getHoverImageDescriptor(), DWT.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 is null && image !is null) { + hoverImage = image; + image = ImageDescriptor.createWithFlags(action + .getImageDescriptor(), DWT.IMAGE_GRAY); + } + } + + // Make sure there is a valid image. + if (hoverImage is null && image is 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 DWT to set disabled and hot image + // before regular image + (cast(ToolItem) widget).setDisabledImage(disabledImage is null ? null + : localManager.createImageWithDefault(disabledImage)); + (cast(ToolItem) widget).setHotImage(hoverImage is null ? null + : localManager.createImageWithDefault(hoverImage)); + (cast(ToolItem) widget).setImage(image is null ? null : localManager + .createImageWithDefault(image)); + + // Now that we're no longer referencing the old images, clear them + // out. + disposeOldImages(); + imageManager = localManager; + + return image !is null; + } else if (cast(Item)widget || cast(Button)widget ) { + + // Use hover image if there is one, otherwise use regular image. + ImageDescriptor image = action.getHoverImageDescriptor(); + if (image is null) { + image = action.getImageDescriptor(); + } + // Make sure there is a valid image. + if (image is 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 (cast(Item)widget) { + (cast(Item) widget).setImage(image is null ? null : localManager + .createImageWithDefault(image)); + } else if (cast(Button)widget) { + (cast(Button) widget).setImage(image is null ? null : localManager + .createImageWithDefault(image)); + } + + // Now that we're no longer referencing the old images, clear them + // out. + disposeOldImages(); + imageManager = localManager; + + return image !is null; + } + return false; + } + + /** + * Dispose any images allocated for this contribution item + */ + private void disposeOldImages() { + if (imageManager !is null) { + imageManager.dispose(); + imageManager = null; + } + } + + /** + * Shorten the given text <code>t</code> 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 is 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; + } +}