comparison org.eclipse.jface/src/org/eclipse/jface/action/ActionContributionItem.d @ 12:bc29606a740c

Added dwt-addons in original directory structure of eclipse.org
author Frank Benoit <benoit@tionex.de>
date Sat, 14 Mar 2009 18:23:29 +0100
parents
children 735224fcc45f
comparison
equal deleted inserted replaced
11:43904fec5dca 12:bc29606a740c
1 /*******************************************************************************
2 * Copyright (c) 2000, 2008 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module org.eclipse.jface.action.ActionContributionItem;
14
15 import org.eclipse.jface.action.ContributionItem;
16 import org.eclipse.jface.action.IAction;
17 import org.eclipse.jface.action.LegacyActionTools;
18 import org.eclipse.jface.action.Action;
19 import org.eclipse.jface.action.IMenuCreator;
20 import org.eclipse.jface.action.IContributionManagerOverrides;
21
22 import org.eclipse.swt.SWT;
23 import org.eclipse.swt.graphics.GC;
24 import org.eclipse.swt.graphics.Point;
25 import org.eclipse.swt.widgets.Button;
26 import org.eclipse.swt.widgets.Composite;
27 import org.eclipse.swt.widgets.Display;
28 import org.eclipse.swt.widgets.Event;
29 import org.eclipse.swt.widgets.Item;
30 import org.eclipse.swt.widgets.Listener;
31 import org.eclipse.swt.widgets.Menu;
32 import org.eclipse.swt.widgets.MenuItem;
33 import org.eclipse.swt.widgets.ToolBar;
34 import org.eclipse.swt.widgets.ToolItem;
35 import org.eclipse.swt.widgets.Widget;
36 import org.eclipse.jface.action.ExternalActionManager;
37 import org.eclipse.core.commands.ExecutionException;
38 import org.eclipse.core.commands.NotEnabledException;
39 import org.eclipse.jface.bindings.Trigger;
40 import org.eclipse.jface.bindings.TriggerSequence;
41 import org.eclipse.jface.bindings.keys.IKeyLookup;
42 import org.eclipse.jface.bindings.keys.KeyLookupFactory;
43 import org.eclipse.jface.bindings.keys.KeyStroke;
44 import org.eclipse.jface.resource.ImageDescriptor;
45 import org.eclipse.jface.resource.JFaceResources;
46 import org.eclipse.jface.resource.LocalResourceManager;
47 import org.eclipse.jface.resource.ResourceManager;
48 import org.eclipse.jface.util.IPropertyChangeListener;
49 import org.eclipse.jface.util.Policy;
50 import org.eclipse.jface.util.PropertyChangeEvent;
51
52 import java.lang.all;
53 import java.util.Set;
54 import java.lang.JThread;
55 import tango.io.Stdout;
56
57 /**
58 * A contribution item which delegates to an action.
59 * <p>
60 * This class may be instantiated; it is not intended to be subclassed.
61 * </p>
62 * @noextend This class is not intended to be subclassed by clients.
63 */
64 public class ActionContributionItem : ContributionItem {
65 alias ContributionItem.fill fill;
66
67 /**
68 * Mode bit: Show text on tool items or buttons, even if an image is
69 * present. If this mode bit is not set, text is only shown on tool items if
70 * there is no image present.
71 *
72 * @since 3.0
73 */
74 public static int MODE_FORCE_TEXT = 1;
75
76 /** a string inserted in the middle of text that has been shortened */
77 private static const String ellipsis = "..."; //$NON-NLS-1$
78
79 /**
80 * Stores the result of the action. False when the action returned failure.
81 */
82 private Boolean result = null;
83
84 private static bool USE_COLOR_ICONS = true;
85
86 /**
87 * Returns whether color icons should be used in toolbars.
88 *
89 * @return <code>true</code> if color icons should be used in toolbars,
90 * <code>false</code> otherwise
91 */
92 public static bool getUseColorIconsInToolbars() {
93 return USE_COLOR_ICONS;
94 }
95
96 /**
97 * Sets whether color icons should be used in toolbars.
98 *
99 * @param useColorIcons
100 * <code>true</code> if color icons should be used in toolbars,
101 * <code>false</code> otherwise
102 */
103 public static void setUseColorIconsInToolbars(bool useColorIcons) {
104 USE_COLOR_ICONS = useColorIcons;
105 }
106
107 /**
108 * The presentation mode.
109 */
110 private int mode = 0;
111
112 /**
113 * The action.
114 */
115 private IAction action;
116
117 /**
118 * The listener for changes to the text of the action contributed by an
119 * external source.
120 */
121 private const IPropertyChangeListener actionTextListener;
122
123 /**
124 * Remembers all images in use by this contribution item
125 */
126 private LocalResourceManager imageManager;
127
128 /**
129 * Listener for SWT button widget events.
130 */
131 private Listener buttonListener;
132
133 /**
134 * Listener for SWT menu item widget events.
135 */
136 private Listener menuItemListener;
137
138 /**
139 * Listener for action property change notifications.
140 */
141 private const IPropertyChangeListener propertyListener;
142
143 /**
144 * Listener for SWT tool item widget events.
145 */
146 private Listener toolItemListener;
147
148 /**
149 * The widget created for this item; <code>null</code> before creation and
150 * after disposal.
151 */
152 private Widget widget = null;
153
154 private Listener menuCreatorListener;
155
156 /**
157 * Creates a new contribution item from the given action. The id of the
158 * action is used as the id of the item.
159 *
160 * @param action
161 * the action
162 */
163 public this(IAction action) {
164 super(action.getId());
165 this.action = action;
166 actionTextListener = new class IPropertyChangeListener {
167 /**
168 * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
169 */
170 public void propertyChange(PropertyChangeEvent event) {
171 update(event.getProperty());
172 }
173 };
174 propertyListener = new class IPropertyChangeListener {
175 public void propertyChange(PropertyChangeEvent event) {
176 actionPropertyChange(event);
177 }
178 };
179 }
180
181 /**
182 * Handles a property change event on the action (forwarded by nested
183 * listener).
184 */
185 private void actionPropertyChange(PropertyChangeEvent e) {
186 // This code should be removed. Avoid using free asyncExec
187
188 if (isVisible() && widget !is null) {
189 Display display = widget.getDisplay();
190 if (display.getThread() is JThread.currentThread().nativeThread()) {
191 update(e.getProperty());
192 } else {
193 display.asyncExec(dgRunnable( (PropertyChangeEvent e_) {
194 update(e_.getProperty());
195 }, e));
196 }
197
198 }
199 }
200
201 /**
202 * Compares this action contribution item with another object. Two action
203 * contribution items are equal if they refer to the identical Action.
204 */
205 public override int opEquals(Object o) {
206 if (!(cast(ActionContributionItem)o )) {
207 return false;
208 }
209 return (cast(Object)action).opEquals(cast(Object)(cast(ActionContributionItem) o).action);
210 }
211
212 /**
213 * The <code>ActionContributionItem</code> implementation of this
214 * <code>IContributionItem</code> method creates an SWT
215 * <code>Button</code> for the action using the action's style. If the
216 * action's checked property has been set, the button is created and primed
217 * to the value of the checked property.
218 */
219 public override void fill(Composite parent) {
220 if (widget is null && parent !is null) {
221 int flags = SWT.PUSH;
222 if (action !is null) {
223 if (action.getStyle() is IAction.AS_CHECK_BOX) {
224 flags = SWT.TOGGLE;
225 }
226 if (action.getStyle() is IAction.AS_RADIO_BUTTON) {
227 flags = SWT.RADIO;
228 }
229 }
230
231 Button b = new Button(parent, flags);
232 b.setData(this);
233 b.addListener(SWT.Dispose, getButtonListener());
234 // Don't hook a dispose listener on the parent
235 b.addListener(SWT.Selection, getButtonListener());
236 if (action.getHelpListener() !is null) {
237 b.addHelpListener(action.getHelpListener());
238 }
239 widget = b;
240
241 update(null);
242
243 // Attach some extra listeners.
244 action.addPropertyChangeListener(propertyListener);
245 if (action !is null) {
246 String commandId = action.getActionDefinitionId();
247 ExternalActionManager.ICallback callback = ExternalActionManager
248 .getInstance().getCallback();
249
250 if ((callback !is null) && (commandId !is null)) {
251 callback.addPropertyChangeListener(commandId,
252 actionTextListener);
253 }
254 }
255 }
256 }
257
258 /**
259 * The <code>ActionContributionItem</code> implementation of this
260 * <code>IContributionItem</code> method creates an SWT
261 * <code>MenuItem</code> for the action using the action's style. If the
262 * action's checked property has been set, a button is created and primed to
263 * the value of the checked property. If the action's menu creator property
264 * has been set, a cascading submenu is created.
265 */
266 public override void fill(Menu parent, int index) {
267 if (widget is null && parent !is null) {
268 int flags = SWT.PUSH;
269 if (action !is null) {
270 int style = action.getStyle();
271 if (style is IAction.AS_CHECK_BOX) {
272 flags = SWT.CHECK;
273 } else if (style is IAction.AS_RADIO_BUTTON) {
274 flags = SWT.RADIO;
275 } else if (style is IAction.AS_DROP_DOWN_MENU) {
276 flags = SWT.CASCADE;
277 }
278 }
279
280 MenuItem mi = null;
281 if (index >= 0) {
282 mi = new MenuItem(parent, flags, index);
283 } else {
284 mi = new MenuItem(parent, flags);
285 }
286 widget = mi;
287
288 mi.setData(this);
289 mi.addListener(SWT.Dispose, getMenuItemListener());
290 mi.addListener(SWT.Selection, getMenuItemListener());
291 if (action.getHelpListener() !is null) {
292 mi.addHelpListener(action.getHelpListener());
293 }
294
295 if (flags is SWT.CASCADE) {
296 // just create a proxy for now, if the user shows it then
297 // fill it in
298 Menu subMenu = new Menu(parent);
299 subMenu.addListener(SWT.Show, getMenuCreatorListener());
300 subMenu.addListener(SWT.Hide, getMenuCreatorListener());
301 mi.setMenu(subMenu);
302 }
303
304 update(null);
305
306 // Attach some extra listeners.
307 action.addPropertyChangeListener(propertyListener);
308 if (action !is null) {
309 String commandId = action.getActionDefinitionId();
310 ExternalActionManager.ICallback callback = ExternalActionManager
311 .getInstance().getCallback();
312
313 if ((callback !is null) && (commandId !is null)) {
314 callback.addPropertyChangeListener(commandId,
315 actionTextListener);
316 }
317 }
318 }
319 }
320
321 /**
322 * The <code>ActionContributionItem</code> implementation of this ,
323 * <code>IContributionItem</code> method creates an SWT
324 * <code>ToolItem</code> for the action using the action's style. If the
325 * action's checked property has been set, a button is created and primed to
326 * the value of the checked property. If the action's menu creator property
327 * has been set, a drop-down tool item is created.
328 */
329 public override void fill(ToolBar parent, int index) {
330 if (widget is null && parent !is null) {
331 int flags = SWT.PUSH;
332 if (action !is null) {
333 int style = action.getStyle();
334 if (style is IAction.AS_CHECK_BOX) {
335 flags = SWT.CHECK;
336 } else if (style is IAction.AS_RADIO_BUTTON) {
337 flags = SWT.RADIO;
338 } else if (style is IAction.AS_DROP_DOWN_MENU) {
339 flags = SWT.DROP_DOWN;
340 }
341 }
342
343 ToolItem ti = null;
344 if (index >= 0) {
345 ti = new ToolItem(parent, flags, index);
346 } else {
347 ti = new ToolItem(parent, flags);
348 }
349 ti.setData(this);
350 ti.addListener(SWT.Selection, getToolItemListener());
351 ti.addListener(SWT.Dispose, getToolItemListener());
352
353 widget = ti;
354
355 update(null);
356
357 // Attach some extra listeners.
358 action.addPropertyChangeListener(propertyListener);
359 if (action !is null) {
360 String commandId = action.getActionDefinitionId();
361 ExternalActionManager.ICallback callback = ExternalActionManager
362 .getInstance().getCallback();
363
364 if ((callback !is null) && (commandId !is null)) {
365 callback.addPropertyChangeListener(commandId,
366 actionTextListener);
367 }
368 }
369 }
370 }
371
372 /**
373 * Returns the action associated with this contribution item.
374 *
375 * @return the action
376 */
377 public IAction getAction() {
378 return action;
379 }
380
381 /**
382 * Returns the listener for SWT button widget events.
383 *
384 * @return a listener for button events
385 */
386 private Listener getButtonListener() {
387 if (buttonListener is null) {
388 buttonListener = new class Listener {
389 public void handleEvent(Event event) {
390 switch (event.type) {
391 case SWT.Dispose:
392 handleWidgetDispose(event);
393 break;
394 case SWT.Selection:
395 Widget ew = event.widget;
396 if (ew !is null) {
397 handleWidgetSelection(event, (cast(Button) ew)
398 .getSelection());
399 }
400 break;
401 default:
402 }
403 }
404 };
405 }
406 return buttonListener;
407 }
408
409 /**
410 * Returns the listener for SWT menu item widget events.
411 *
412 * @return a listener for menu item events
413 */
414 private Listener getMenuItemListener() {
415 if (menuItemListener is null) {
416 menuItemListener = new class Listener {
417 public void handleEvent(Event event) {
418 switch (event.type) {
419 case SWT.Dispose:
420 handleWidgetDispose(event);
421 break;
422 case SWT.Selection:
423 Widget ew = event.widget;
424 if (ew !is null) {
425 handleWidgetSelection(event, (cast(MenuItem) ew)
426 .getSelection());
427 }
428 break;
429 default:
430 }
431 }
432 };
433 }
434 return menuItemListener;
435 }
436
437 /**
438 * Returns the presentation mode, which is the bitwise-or of the
439 * <code>MODE_*</code> constants. The default mode setting is 0, meaning
440 * that for menu items, both text and image are shown (if present), but for
441 * tool items, the text is shown only if there is no image.
442 *
443 * @return the presentation mode settings
444 *
445 * @since 3.0
446 */
447 public int getMode() {
448 return mode;
449 }
450
451 /**
452 * Returns the listener for SWT tool item widget events.
453 *
454 * @return a listener for tool item events
455 */
456 private Listener getToolItemListener() {
457 if (toolItemListener is null) {
458 toolItemListener = new class Listener {
459 public void handleEvent(Event event) {
460 switch (event.type) {
461 case SWT.Dispose:
462 handleWidgetDispose(event);
463 break;
464 case SWT.Selection:
465 Widget ew = event.widget;
466 if (ew !is null) {
467 handleWidgetSelection(event, (cast(ToolItem) ew)
468 .getSelection());
469 }
470 break;
471 default:
472 }
473 }
474 };
475 }
476 return toolItemListener;
477 }
478
479 /**
480 * Handles a widget dispose event for the widget corresponding to this item.
481 */
482 private void handleWidgetDispose(Event e) {
483 // Check if our widget is the one being disposed.
484 if (e.widget is widget) {
485 // Dispose of the menu creator.
486 if (action.getStyle() is IAction.AS_DROP_DOWN_MENU
487 && menuCreatorCalled) {
488 IMenuCreator mc = action.getMenuCreator();
489 if (mc !is null) {
490 mc.dispose();
491 }
492 }
493
494 // Unhook all of the listeners.
495 action.removePropertyChangeListener(propertyListener);
496 if (action !is null) {
497 String commandId = action.getActionDefinitionId();
498 ExternalActionManager.ICallback callback = ExternalActionManager
499 .getInstance().getCallback();
500
501 if ((callback !is null) && (commandId !is null)) {
502 callback.removePropertyChangeListener(commandId,
503 actionTextListener);
504 }
505 }
506
507 // Clear the widget field.
508 widget = null;
509
510 disposeOldImages();
511 }
512 }
513
514 /**
515 * Handles a widget selection event.
516 */
517 private void handleWidgetSelection(Event e, bool selection) {
518
519 Widget item = e.widget;
520 if (item !is null) {
521 int style = item.getStyle();
522
523 if ((style & (SWT.TOGGLE | SWT.CHECK)) !is 0) {
524 if (action.getStyle() is IAction.AS_CHECK_BOX) {
525 action.setChecked(selection);
526 }
527 } else if ((style & SWT.RADIO) !is 0) {
528 if (action.getStyle() is IAction.AS_RADIO_BUTTON) {
529 action.setChecked(selection);
530 }
531 } else if ((style & SWT.DROP_DOWN) !is 0) {
532 if (e.detail is 4) { // on drop-down button
533 if (action.getStyle() is IAction.AS_DROP_DOWN_MENU) {
534 IMenuCreator mc = action.getMenuCreator();
535 menuCreatorCalled = true;
536 ToolItem ti = cast(ToolItem) item;
537 // we create the menu as a sub-menu of "dummy" so that
538 // we can use
539 // it in a cascading menu too.
540 // If created on a SWT control we would get an SWT
541 // error...
542 // Menu dummy= new Menu(ti.getParent());
543 // Menu m= mc.getMenu(dummy);
544 // dummy.dispose();
545 if (mc !is null) {
546 Menu m = mc.getMenu(ti.getParent());
547 if (m !is null) {
548 // position the menu below the drop down item
549 Point point = ti.getParent().toDisplay(
550 new Point(e.x, e.y));
551 m.setLocation(point.x, point.y); // waiting
552 // for SWT
553 // 0.42
554 m.setVisible(true);
555 return; // we don't fire the action
556 }
557 }
558 }
559 }
560 }
561
562 ExternalActionManager.IExecuteCallback callback = null;
563 String actionDefinitionId = action.getActionDefinitionId();
564 if (actionDefinitionId !is null) {
565 Object obj = cast(Object) ExternalActionManager.getInstance()
566 .getCallback();
567 if (null !is cast(ExternalActionManager.IExecuteCallback)obj ) {
568 callback = cast(ExternalActionManager.IExecuteCallback) obj;
569 }
570 }
571
572 // Ensure action is enabled first.
573 // See 1GAN3M6: ITPUI:WINNT - Any IAction in the workbench can be
574 // executed while disabled.
575 if (action.isEnabled()) {
576 bool trace = Policy.TRACE_ACTIONS;
577
578 long ms = 0L;
579 if (trace) {
580 ms = System.currentTimeMillis();
581 Stdout.formatln("Running action: {}", action.getText()); //$NON-NLS-1$
582 }
583
584 IPropertyChangeListener resultListener = null;
585 if (callback !is null) {
586 resultListener = new class IPropertyChangeListener {
587 public void propertyChange(PropertyChangeEvent event) {
588 // Check on result
589 if (event.getProperty().equals(IAction.RESULT)) {
590 if (null !is cast(Boolean)event.getNewValue() ) {
591 result = cast(Boolean) event.getNewValue();
592 }
593 }
594 }
595 };
596 action.addPropertyChangeListener(resultListener);
597 callback.preExecute(action, e);
598 }
599
600 action.runWithEvent(e);
601
602 if (callback !is null) {
603 if (result is null || result.opEquals(Boolean.TRUE)) {
604 callback.postExecuteSuccess(action, Boolean.TRUE);
605 } else {
606 callback.postExecuteFailure(action,
607 new ExecutionException(action.getText()
608 ~ " returned failure.")); //$NON-NLS-1$
609 }
610 }
611
612 if (resultListener !is null) {
613 result = null;
614 action.removePropertyChangeListener(resultListener);
615 }
616 if (trace) {
617 Stdout.formatln("{} ms to run action: {}",(System.currentTimeMillis() - ms), action.getText()); //$NON-NLS-1$
618 }
619 } else {
620 if (callback !is null) {
621 callback.notEnabled(action, new NotEnabledException(action
622 .getText()
623 ~ " is not enabled.")); //$NON-NLS-1$
624 }
625 }
626 }
627 }
628
629 /*
630 * (non-Javadoc) Method declared on Object.
631 */
632 public override hash_t toHash() {
633 return (cast(Object)action).toHash();
634 }
635
636 /**
637 * Returns whether the given action has any images.
638 *
639 * @param actionToCheck
640 * the action
641 * @return <code>true</code> if the action has any images,
642 * <code>false</code> if not
643 */
644 private bool hasImages(IAction actionToCheck) {
645 return actionToCheck.getImageDescriptor() !is null
646 || actionToCheck.getHoverImageDescriptor() !is null
647 || actionToCheck.getDisabledImageDescriptor() !is null;
648 }
649
650 /**
651 * Returns whether the command corresponding to this action is active.
652 */
653 private bool isCommandActive() {
654 IAction actionToCheck = getAction();
655
656 if (actionToCheck !is null) {
657 String commandId = actionToCheck.getActionDefinitionId();
658 ExternalActionManager.ICallback callback = ExternalActionManager
659 .getInstance().getCallback();
660
661 if (callback !is null) {
662 return callback.isActive(commandId);
663 }
664 }
665 return true;
666 }
667
668 /**
669 * The action item implementation of this <code>IContributionItem</code>
670 * method returns <code>true</code> for menu items and <code>false</code>
671 * for everything else.
672 */
673 public override bool isDynamic() {
674 if (cast(MenuItem)widget ) {
675 // Optimization. Only recreate the item is the check or radio style
676 // has changed.
677 bool itemIsCheck = (widget.getStyle() & SWT.CHECK) !is 0;
678 bool actionIsCheck = getAction() !is null
679 && getAction().getStyle() is IAction.AS_CHECK_BOX;
680 bool itemIsRadio = (widget.getStyle() & SWT.RADIO) !is 0;
681 bool actionIsRadio = getAction() !is null
682 && getAction().getStyle() is IAction.AS_RADIO_BUTTON;
683 return (itemIsCheck !is actionIsCheck)
684 || (itemIsRadio !is actionIsRadio);
685 }
686 return false;
687 }
688
689 /*
690 * (non-Javadoc) Method declared on IContributionItem.
691 */
692 public override bool isEnabled() {
693 return action !is null && action.isEnabled();
694 }
695
696 /**
697 * Returns <code>true</code> if this item is allowed to enable,
698 * <code>false</code> otherwise.
699 *
700 * @return if this item is allowed to be enabled
701 * @since 2.0
702 */
703 protected bool isEnabledAllowed() {
704 if (getParent() is null) {
705 return true;
706 }
707 auto value = getParent().getOverrides().getEnabled(this);
708 return (value is null) ? true : value.value;
709 }
710
711 /**
712 * The <code>ActionContributionItem</code> implementation of this
713 * <code>ContributionItem</code> method extends the super implementation
714 * by also checking whether the command corresponding to this action is
715 * active.
716 */
717 public override bool isVisible() {
718 return super.isVisible() && isCommandActive();
719 }
720
721 /**
722 * Sets the presentation mode, which is the bitwise-or of the
723 * <code>MODE_*</code> constants.
724 *
725 * @param mode
726 * the presentation mode settings
727 *
728 * @since 3.0
729 */
730 public void setMode(int mode) {
731 this.mode = mode;
732 update();
733 }
734
735 /**
736 * The action item implementation of this <code>IContributionItem</code>
737 * method calls <code>update(null)</code>.
738 */
739 public override final void update() {
740 update(null);
741 }
742
743 /**
744 * Synchronizes the UI with the given property.
745 *
746 * @param propertyName
747 * the name of the property, or <code>null</code> meaning all
748 * applicable properties
749 */
750 public override void update(String propertyName) {
751 if (widget !is null) {
752 // determine what to do
753 bool textChanged = propertyName is null
754 || propertyName.equals(IAction.TEXT);
755 bool imageChanged = propertyName is null
756 || propertyName.equals(IAction.IMAGE);
757 bool tooltipTextChanged = propertyName is null
758 || propertyName.equals(IAction.TOOL_TIP_TEXT);
759 bool enableStateChanged = propertyName is null
760 || propertyName.equals(IAction.ENABLED)
761 || propertyName
762 .equals(IContributionManagerOverrides.P_ENABLED);
763 bool checkChanged = (action.getStyle() is IAction.AS_CHECK_BOX || action
764 .getStyle() is IAction.AS_RADIO_BUTTON)
765 && (propertyName is null || propertyName
766 .equals(IAction.CHECKED));
767
768 if (cast(ToolItem)widget ) {
769 ToolItem ti = cast(ToolItem) widget;
770 String text = action.getText();
771 // the set text is shown only if there is no image or if forced
772 // by MODE_FORCE_TEXT
773 bool showText = text !is null
774 && ((getMode() & MODE_FORCE_TEXT) !is 0 || !hasImages(action));
775
776 // only do the trimming if the text will be used
777 if (showText && text !is null) {
778 text = Action.removeAcceleratorText(text);
779 text = Action.removeMnemonics(text);
780 }
781
782 if (textChanged) {
783 String textToSet = showText ? text : ""; //$NON-NLS-1$
784 bool rightStyle = (ti.getParent().getStyle() & SWT.RIGHT) !is 0;
785 if (rightStyle || !ti.getText().equals(textToSet)) {
786 // In addition to being required to update the text if
787 // it
788 // gets nulled out in the action, this is also a
789 // workaround
790 // for bug 50151: Using SWT.RIGHT on a ToolBar leaves
791 // blank space
792 ti.setText(textToSet);
793 }
794 }
795
796 if (imageChanged) {
797 // only substitute a missing image if it has no text
798 updateImages(!showText);
799 }
800
801 if (tooltipTextChanged || textChanged) {
802 String toolTip = action.getToolTipText();
803 if ((toolTip is null) || (toolTip.length is 0)) {
804 toolTip = text;
805 }
806
807 ExternalActionManager.ICallback callback = ExternalActionManager
808 .getInstance().getCallback();
809 String commandId = action.getActionDefinitionId();
810 if ((callback !is null) && (commandId !is null)
811 && (toolTip !is null)) {
812 String acceleratorText = callback
813 .getAcceleratorText(commandId);
814 if (acceleratorText !is null
815 && acceleratorText.length !is 0) {
816 toolTip = JFaceResources.format(
817 "Toolbar_Tooltip_Accelerator", //$NON-NLS-1$
818 [ toolTip, acceleratorText ]);
819 }
820 }
821
822 // if the text is showing, then only set the tooltip if
823 // different
824 if (!showText || toolTip !is null && !toolTip.equals(text)) {
825 ti.setToolTipText(toolTip);
826 } else {
827 ti.setToolTipText(null);
828 }
829 }
830
831 if (enableStateChanged) {
832 bool shouldBeEnabled = action.isEnabled()
833 && isEnabledAllowed();
834
835 if (ti.getEnabled() !is shouldBeEnabled) {
836 ti.setEnabled(shouldBeEnabled);
837 }
838 }
839
840 if (checkChanged) {
841 bool bv = action.isChecked();
842
843 if (ti.getSelection() !is bv) {
844 ti.setSelection(bv);
845 }
846 }
847 return;
848 }
849
850 if (cast(MenuItem)widget ) {
851 MenuItem mi = cast(MenuItem) widget;
852
853 if (textChanged) {
854 int accelerator = 0;
855 String acceleratorText = null;
856 IAction updatedAction = getAction();
857 String text = null;
858 accelerator = updatedAction.getAccelerator();
859 ExternalActionManager.ICallback callback = ExternalActionManager
860 .getInstance().getCallback();
861
862 // Block accelerators that are already in use.
863 if ((accelerator !is 0) && (callback !is null)
864 && (callback.isAcceleratorInUse(accelerator))) {
865 accelerator = 0;
866 }
867
868 /*
869 * Process accelerators on GTK in a special way to avoid Bug
870 * 42009. We will override the native input method by
871 * allowing these reserved accelerators to be placed on the
872 * menu. We will only do this for "Ctrl+Shift+[0-9A-FU]".
873 */
874 String commandId = updatedAction
875 .getActionDefinitionId();
876 if (("gtk".equals(SWT.getPlatform())) && (cast(ExternalActionManager.IBindingManagerCallback)callback ) //$NON-NLS-1$
877 && (commandId !is null)) {
878 ExternalActionManager.IBindingManagerCallback bindingManagerCallback = cast(ExternalActionManager.IBindingManagerCallback) callback;
879 IKeyLookup lookup = KeyLookupFactory.getDefault();
880 TriggerSequence[] triggerSequences = bindingManagerCallback
881 .getActiveBindingsFor(commandId);
882 for (int i = 0; i < triggerSequences.length; i++) {
883 TriggerSequence triggerSequence = triggerSequences[i];
884 Trigger[] triggers = triggerSequence
885 .getTriggers();
886 if (triggers.length is 1) {
887 Trigger trigger = triggers[0];
888 if (cast(KeyStroke)trigger ) {
889 KeyStroke currentKeyStroke = cast(KeyStroke) trigger;
890 int currentNaturalKey = currentKeyStroke
891 .getNaturalKey();
892 if ((currentKeyStroke.getModifierKeys() is (lookup
893 .getCtrl() | lookup.getShift()))
894 && ((currentNaturalKey >= '0' && currentNaturalKey <= '9')
895 || (currentNaturalKey >= 'A' && currentNaturalKey <= 'F') || (currentNaturalKey is 'U'))) {
896 accelerator = currentKeyStroke
897 .getModifierKeys()
898 | currentNaturalKey;
899 acceleratorText = triggerSequence
900 .format();
901 break;
902 }
903 }
904 }
905 }
906 }
907
908 if (accelerator is 0) {
909 if ((callback !is null) && (commandId !is null)) {
910 acceleratorText = callback
911 .getAcceleratorText(commandId);
912 }
913 }
914
915 IContributionManagerOverrides overrides = null;
916
917 if (getParent() !is null) {
918 overrides = getParent().getOverrides();
919 }
920
921 if (overrides !is null) {
922 text = getParent().getOverrides().getText(this);
923 }
924
925 mi.setAccelerator(accelerator);
926
927 if (text is null) {
928 text = updatedAction.getText();
929 }
930
931 if (text !is null && acceleratorText is null) {
932 // use extracted accelerator text in case accelerator
933 // cannot be fully represented in one int (e.g.
934 // multi-stroke keys)
935 acceleratorText = LegacyActionTools
936 .extractAcceleratorText(text);
937 if (acceleratorText is null && accelerator !is 0) {
938 acceleratorText = Action
939 .convertAccelerator(accelerator);
940 }
941 }
942
943 if (text is null) {
944 text = ""; //$NON-NLS-1$
945 } else {
946 text = Action.removeAcceleratorText(text);
947 }
948
949 if (acceleratorText is null) {
950 mi.setText(text);
951 } else {
952 mi.setText(text ~ '\t' ~ acceleratorText);
953 }
954 }
955
956 if (imageChanged) {
957 updateImages(false);
958 }
959
960 if (enableStateChanged) {
961 bool shouldBeEnabled = action.isEnabled()
962 && isEnabledAllowed();
963
964 if (mi.getEnabled() !is shouldBeEnabled) {
965 mi.setEnabled(shouldBeEnabled);
966 }
967 }
968
969 if (checkChanged) {
970 bool bv = action.isChecked();
971
972 if (mi.getSelection() !is bv) {
973 mi.setSelection(bv);
974 }
975 }
976
977 return;
978 }
979
980 if (cast(Button)widget ) {
981 Button button = cast(Button) widget;
982
983 if (imageChanged) {
984 updateImages(false);
985 }
986
987 if (textChanged) {
988 String text = action.getText();
989 bool showText = text !is null && ((getMode() & MODE_FORCE_TEXT) !is 0 || !hasImages(action));
990 // only do the trimming if the text will be used
991 if (showText) {
992 text = Action.removeAcceleratorText(text);
993 }
994 String textToSet = showText ? text : ""; //$NON-NLS-1$
995 button.setText(textToSet);
996 }
997
998 if (tooltipTextChanged) {
999 button.setToolTipText(action.getToolTipText());
1000 }
1001
1002 if (enableStateChanged) {
1003 bool shouldBeEnabled = action.isEnabled()
1004 && isEnabledAllowed();
1005
1006 if (button.getEnabled() !is shouldBeEnabled) {
1007 button.setEnabled(shouldBeEnabled);
1008 }
1009 }
1010
1011 if (checkChanged) {
1012 bool bv = action.isChecked();
1013
1014 if (button.getSelection() !is bv) {
1015 button.setSelection(bv);
1016 }
1017 }
1018 return;
1019 }
1020 }
1021 }
1022
1023 /**
1024 * Updates the images for this action.
1025 *
1026 * @param forceImage
1027 * <code>true</code> if some form of image is compulsory, and
1028 * <code>false</code> if it is acceptable for this item to have
1029 * no image
1030 * @return <code>true</code> if there are images for this action,
1031 * <code>false</code> if not
1032 */
1033 private bool updateImages(bool forceImage) {
1034
1035 ResourceManager parentResourceManager = JFaceResources.getResources();
1036
1037 if (cast(ToolItem)widget ) {
1038 if (USE_COLOR_ICONS) {
1039 ImageDescriptor image = action.getHoverImageDescriptor();
1040 if (image is null) {
1041 image = action.getImageDescriptor();
1042 }
1043 ImageDescriptor disabledImage = action
1044 .getDisabledImageDescriptor();
1045
1046 // Make sure there is a valid image.
1047 if (image is null && forceImage) {
1048 image = ImageDescriptor.getMissingImageDescriptor();
1049 }
1050
1051 LocalResourceManager localManager = new LocalResourceManager(
1052 parentResourceManager);
1053
1054 // performance: more efficient in SWT to set disabled and hot
1055 // image before regular image
1056 (cast(ToolItem) widget)
1057 .setDisabledImage(disabledImage is null ? null
1058 : localManager
1059 .createImageWithDefault(disabledImage));
1060 (cast(ToolItem) widget).setImage(image is null ? null
1061 : localManager.createImageWithDefault(image));
1062
1063 disposeOldImages();
1064 imageManager = localManager;
1065
1066 return image !is null;
1067 }
1068 ImageDescriptor image = action.getImageDescriptor();
1069 ImageDescriptor hoverImage = action.getHoverImageDescriptor();
1070 ImageDescriptor disabledImage = action.getDisabledImageDescriptor();
1071
1072 // If there is no regular image, but there is a hover image,
1073 // convert the hover image to gray and use it as the regular image.
1074 if (image is null && hoverImage !is null) {
1075 image = ImageDescriptor.createWithFlags(action
1076 .getHoverImageDescriptor(), SWT.IMAGE_GRAY);
1077 } else {
1078 // If there is no hover image, use the regular image as the
1079 // hover image,
1080 // and convert the regular image to gray
1081 if (hoverImage is null && image !is null) {
1082 hoverImage = image;
1083 image = ImageDescriptor.createWithFlags(action
1084 .getImageDescriptor(), SWT.IMAGE_GRAY);
1085 }
1086 }
1087
1088 // Make sure there is a valid image.
1089 if (hoverImage is null && image is null && forceImage) {
1090 image = ImageDescriptor.getMissingImageDescriptor();
1091 }
1092
1093 // Create a local resource manager to remember the images we've
1094 // allocated for this tool item
1095 LocalResourceManager localManager = new LocalResourceManager(
1096 parentResourceManager);
1097
1098 // performance: more efficient in SWT to set disabled and hot image
1099 // before regular image
1100 (cast(ToolItem) widget).setDisabledImage(disabledImage is null ? null
1101 : localManager.createImageWithDefault(disabledImage));
1102 (cast(ToolItem) widget).setHotImage(hoverImage is null ? null
1103 : localManager.createImageWithDefault(hoverImage));
1104 (cast(ToolItem) widget).setImage(image is null ? null : localManager
1105 .createImageWithDefault(image));
1106
1107 // Now that we're no longer referencing the old images, clear them
1108 // out.
1109 disposeOldImages();
1110 imageManager = localManager;
1111
1112 return image !is null;
1113 } else if (cast(Item)widget || cast(Button)widget ) {
1114
1115 // Use hover image if there is one, otherwise use regular image.
1116 ImageDescriptor image = action.getHoverImageDescriptor();
1117 if (image is null) {
1118 image = action.getImageDescriptor();
1119 }
1120 // Make sure there is a valid image.
1121 if (image is null && forceImage) {
1122 image = ImageDescriptor.getMissingImageDescriptor();
1123 }
1124
1125 // Create a local resource manager to remember the images we've
1126 // allocated for this widget
1127 LocalResourceManager localManager = new LocalResourceManager(
1128 parentResourceManager);
1129
1130 if (cast(Item)widget) {
1131 (cast(Item) widget).setImage(image is null ? null : localManager
1132 .createImageWithDefault(image));
1133 } else if (cast(Button)widget) {
1134 (cast(Button) widget).setImage(image is null ? null : localManager
1135 .createImageWithDefault(image));
1136 }
1137
1138 // Now that we're no longer referencing the old images, clear them
1139 // out.
1140 disposeOldImages();
1141 imageManager = localManager;
1142
1143 return image !is null;
1144 }
1145 return false;
1146 }
1147
1148 /**
1149 * Dispose any images allocated for this contribution item
1150 */
1151 private void disposeOldImages() {
1152 if (imageManager !is null) {
1153 imageManager.dispose();
1154 imageManager = null;
1155 }
1156 }
1157
1158 /**
1159 * Shorten the given text <code>t</code> so that its length doesn't exceed
1160 * the width of the given ToolItem.The default implementation replaces
1161 * characters in the center of the original string with an ellipsis ("...").
1162 * Override if you need a different strategy.
1163 *
1164 * @param textValue
1165 * the text to shorten
1166 * @param item
1167 * the tool item the text belongs to
1168 * @return the shortened string
1169 *
1170 */
1171 protected String shortenText(String textValue, ToolItem item) {
1172 if (textValue is null) {
1173 return null;
1174 }
1175
1176 GC gc = new GC(item.getParent());
1177
1178 int maxWidth = item.getImage().getBounds().width * 4;
1179
1180 if (gc.textExtent(textValue).x < maxWidth) {
1181 gc.dispose();
1182 return textValue;
1183 }
1184
1185 for (int i = textValue.length; i > 0; i--) {
1186 String test = textValue.substring(0, i);
1187 test = test ~ ellipsis;
1188 if (gc.textExtent(test).x < maxWidth) {
1189 gc.dispose();
1190 return test;
1191 }
1192
1193 }
1194 gc.dispose();
1195 // If for some reason we fall through abort
1196 return textValue;
1197 }
1198
1199 /*
1200 * (non-Javadoc)
1201 *
1202 * @see org.eclipse.jface.action.ContributionItem#dispose()
1203 */
1204 public void dispose() {
1205 if (widget !is null) {
1206 widget.dispose();
1207 widget = null;
1208 }
1209 holdMenu = null;
1210 }
1211
1212 /**
1213 * Handle show and hide on the proxy menu for IAction.AS_DROP_DOWN_MENU
1214 * actions.
1215 *
1216 * @return the appropriate listener
1217 * @since 3.4
1218 */
1219 private Listener getMenuCreatorListener() {
1220 if (menuCreatorListener is null) {
1221 menuCreatorListener = new class Listener {
1222 public void handleEvent(Event event) {
1223 switch (event.type) {
1224 case SWT.Show:
1225 handleShowProxy(cast(Menu) event.widget);
1226 break;
1227 case SWT.Hide:
1228 handleHideProxy(cast(Menu) event.widget);
1229 break;
1230 default:
1231 }
1232 }
1233 };
1234 }
1235 return menuCreatorListener;
1236 }
1237
1238 /**
1239 * This is the easiest way to hold the menu until we can swap it in to the
1240 * proxy.
1241 */
1242 private Menu holdMenu = null;
1243
1244 private bool menuCreatorCalled = false;
1245
1246 /**
1247 * The proxy menu is being shown, we better get the real menu.
1248 *
1249 * @param proxy
1250 * the proxy menu
1251 * @since 3.4
1252 */
1253 private void handleShowProxy(Menu proxy) {
1254 proxy.removeListener(SWT.Show, getMenuCreatorListener());
1255 IMenuCreator mc = action.getMenuCreator();
1256 menuCreatorCalled = true;
1257 if (mc is null) {
1258 return;
1259 }
1260 holdMenu = mc.getMenu(proxy.getParentMenu());
1261 if (holdMenu is null) {
1262 return;
1263 }
1264 copyMenu(holdMenu, proxy);
1265 }
1266
1267 /**
1268 * Create MenuItems in the proxy menu that can execute the real menu items
1269 * if selected. Create proxy menus for any real item submenus.
1270 *
1271 * @param realMenu
1272 * the real menu to copy from
1273 * @param proxy
1274 * the proxy menu to populate
1275 * @since 3.4
1276 */
1277 private void copyMenu(Menu realMenu, Menu proxy) {
1278 if (realMenu.isDisposed() || proxy.isDisposed()) {
1279 return;
1280 }
1281
1282 // we notify the real menu so it can populate itself if it was
1283 // listening for SWT.Show
1284 realMenu.notifyListeners(SWT.Show, null);
1285
1286 final Listener passThrough = new class Listener {
1287 public void handleEvent(Event event) {
1288 if (!event.widget.isDisposed()) {
1289 Widget realItem = cast(Widget) event.widget.getData();
1290 if (!realItem.isDisposed()) {
1291 int style = event.widget.getStyle();
1292 if (event.type is SWT.Selection
1293 && ((style & (SWT.TOGGLE | SWT.CHECK)) !is 0)
1294 && (null !is cast(MenuItem)realItem )) {
1295 (cast(MenuItem) realItem)
1296 .setSelection((cast(MenuItem) event.widget)
1297 .getSelection());
1298 }
1299 event.widget = realItem;
1300 realItem.notifyListeners(event.type, event);
1301 }
1302 }
1303 }
1304 };
1305
1306 MenuItem[] items = realMenu.getItems();
1307 for (int i = 0; i < items.length; i++) {
1308 final MenuItem realItem = items[i];
1309 final MenuItem proxyItem = new MenuItem(proxy, realItem.getStyle());
1310 proxyItem.setData(realItem);
1311 proxyItem.setAccelerator(realItem.getAccelerator());
1312 proxyItem.setEnabled(realItem.getEnabled());
1313 proxyItem.setImage(realItem.getImage());
1314 proxyItem.setSelection(realItem.getSelection());
1315 proxyItem.setText(realItem.getText());
1316
1317 // pass through any events
1318 proxyItem.addListener(SWT.Selection, passThrough);
1319 proxyItem.addListener(SWT.Arm, passThrough);
1320 proxyItem.addListener(SWT.Help, passThrough);
1321
1322 final Menu itemMenu = realItem.getMenu();
1323 if (itemMenu !is null) {
1324 // create a proxy for any sub menu items
1325 final Menu subMenu = new Menu(proxy);
1326 subMenu.setData(itemMenu);
1327 proxyItem.setMenu(subMenu);
1328 subMenu.addListener(SWT.Show, new class(subMenu, itemMenu) Listener {
1329 Menu subMenu_;
1330 Menu itemMenu_;
1331 this(Menu a,Menu b){
1332 subMenu_=a;
1333 itemMenu_=b;
1334 }
1335 void handleEvent(Event event){
1336 event.widget.removeListener(SWT.Show, this);
1337 if (event.type is SWT.Show) {
1338 copyMenu(itemMenu_, subMenu_);
1339 }
1340 }
1341 });
1342 subMenu.addListener(SWT.Help, passThrough);
1343 subMenu.addListener(SWT.Hide, passThrough);
1344 }
1345 }
1346 }
1347
1348 /**
1349 * The proxy menu is being hidden, so we need to make it go away.
1350 *
1351 * @param proxy
1352 * the proxy menu
1353 * @since 3.4
1354 */
1355 private void handleHideProxy(Menu proxy) {
1356 proxy.removeListener(SWT.Hide, getMenuCreatorListener());
1357 proxy.getDisplay().asyncExec(dgRunnable( (Menu proxy_) {
1358 if (!proxy_.isDisposed()) {
1359 MenuItem parentItem = proxy_.getParentItem();
1360 proxy_.dispose();
1361 parentItem.setMenu(holdMenu);
1362 }
1363 if (holdMenu !is null && !holdMenu.isDisposed()) {
1364 holdMenu.notifyListeners(SWT.Hide, null);
1365 }
1366 holdMenu = null;
1367 }, proxy ));
1368 }
1369
1370 /**
1371 * Return the widget associated with this contribution item. It should not
1372 * be cached, as it can be disposed and re-created by its containing
1373 * ContributionManager, which controls all of the widgets lifecycle methods.
1374 * <p>
1375 * This can be used to set layout data on the widget if appropriate. The
1376 * actual type of the widget can be any valid control for this
1377 * ContributionItem's current ContributionManager.
1378 * </p>
1379 *
1380 * @return the widget, or <code>null</code> depending on the lifecycle.
1381 * @since 3.4
1382 */
1383 public Widget getWidget() {
1384 return widget;
1385 }
1386 }