001: /*******************************************************************************
002: * Copyright (c) 2006, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.menus;
011:
012: import java.util.ArrayList;
013: import java.util.Iterator;
014: import java.util.Map;
015:
016: import org.eclipse.core.commands.Command;
017: import org.eclipse.core.commands.CommandEvent;
018: import org.eclipse.core.commands.ExecutionException;
019: import org.eclipse.core.commands.ICommandListener;
020: import org.eclipse.core.commands.IParameter;
021: import org.eclipse.core.commands.NotEnabledException;
022: import org.eclipse.core.commands.NotHandledException;
023: import org.eclipse.core.commands.Parameterization;
024: import org.eclipse.core.commands.ParameterizedCommand;
025: import org.eclipse.core.commands.common.NotDefinedException;
026: import org.eclipse.jface.action.ContributionItem;
027: import org.eclipse.jface.action.IMenuListener;
028: import org.eclipse.jface.action.IMenuManager;
029: import org.eclipse.jface.action.MenuManager;
030: import org.eclipse.jface.bindings.TriggerSequence;
031: import org.eclipse.jface.resource.ImageDescriptor;
032: import org.eclipse.jface.resource.JFaceResources;
033: import org.eclipse.jface.resource.LocalResourceManager;
034: import org.eclipse.swt.SWT;
035: import org.eclipse.swt.graphics.Point;
036: import org.eclipse.swt.widgets.Event;
037: import org.eclipse.swt.widgets.Listener;
038: import org.eclipse.swt.widgets.Menu;
039: import org.eclipse.swt.widgets.MenuItem;
040: import org.eclipse.swt.widgets.ToolBar;
041: import org.eclipse.swt.widgets.ToolItem;
042: import org.eclipse.swt.widgets.Widget;
043: import org.eclipse.ui.IWorkbenchPartSite;
044: import org.eclipse.ui.IWorkbenchWindow;
045: import org.eclipse.ui.commands.ICommandService;
046: import org.eclipse.ui.commands.IElementReference;
047: import org.eclipse.ui.handlers.IHandlerService;
048: import org.eclipse.ui.internal.WorkbenchPlugin;
049: import org.eclipse.ui.internal.commands.ICommandImageService;
050: import org.eclipse.ui.keys.IBindingService;
051: import org.eclipse.ui.services.IServiceLocator;
052:
053: /**
054: * A contribution item which delegates to a command. It can be used in
055: * {@link AbstractContributionFactory#createContributionItems(IServiceLocator, IContributionRoot)}.
056: * <p>
057: * It currently supports placement in menus and toolbars.
058: * </p>
059: * <p>
060: * This class may be instantiated; it is not intended to be subclassed.
061: * </p>
062: *
063: * @since 3.3
064: */
065: public final class CommandContributionItem extends ContributionItem {
066: /**
067: * A push button tool item or menu item.
068: */
069: public static final int STYLE_PUSH = SWT.PUSH;
070:
071: /**
072: * A checked tool item or menu item.
073: */
074: public static final int STYLE_CHECK = SWT.CHECK;
075:
076: /**
077: * A radio-button style menu item.
078: */
079: public static final int STYLE_RADIO = SWT.RADIO;
080:
081: /**
082: * A ToolBar pulldown item.
083: */
084: public static final int STYLE_PULLDOWN = SWT.DROP_DOWN;
085:
086: private LocalResourceManager localResourceManager;
087:
088: private Listener menuItemListener;
089:
090: private Widget widget;
091:
092: private IMenuService menuService;
093:
094: private ICommandService commandService;
095:
096: private IHandlerService handlerService;
097:
098: private IBindingService bindingService;
099:
100: private ParameterizedCommand command;
101:
102: private ImageDescriptor icon;
103:
104: private String label;
105:
106: private String tooltip;
107:
108: private ImageDescriptor disabledIcon;
109:
110: private ImageDescriptor hoverIcon;
111:
112: private String mnemonic;
113:
114: private IElementReference elementRef;
115:
116: private boolean checkedState;
117:
118: private int style;
119:
120: private ICommandListener commandListener;
121:
122: private String dropDownMenuOverride;
123:
124: /**
125: * Create a CommandContributionItem to place in a ContributionManager.
126: *
127: * @param serviceLocator
128: * a service locator that is most appropriate for this
129: * contribution. Typically the local {@link IWorkbenchWindow} or
130: * {@link IWorkbenchPartSite} will be sufficient.
131: * @param id
132: * The id for this item. May be <code>null</code>. Items
133: * without an id cannot be referenced later.
134: * @param commandId
135: * A command id for a defined command. Must not be
136: * <code>null</code>.
137: * @param parameters
138: * A map of strings to strings which represent parameter names to
139: * values. The parameter names must match those in the command
140: * definition.
141: * @param icon
142: * An icon for this item. May be <code>null</code>.
143: * @param disabledIcon
144: * A disabled icon for this item. May be <code>null</code>.
145: * @param hoverIcon
146: * A hover icon for this item. May be <code>null</code>.
147: * @param label
148: * A label for this item. May be <code>null</code>.
149: * @param mnemonic
150: * A mnemonic for this item to be applied to the label. May be
151: * <code>null</code>.
152: * @param tooltip
153: * A tooltip for this item. May be <code>null</code>. Tooltips
154: * are currently only valid for toolbar contributions.
155: * @param style
156: * The style of this menu contribution. See the STYLE_* contants.
157: */
158: public CommandContributionItem(IServiceLocator serviceLocator,
159: String id, String commandId, Map parameters,
160: ImageDescriptor icon, ImageDescriptor disabledIcon,
161: ImageDescriptor hoverIcon, String label, String mnemonic,
162: String tooltip, int style) {
163: super (id);
164: this .icon = icon;
165: this .disabledIcon = disabledIcon;
166: this .hoverIcon = hoverIcon;
167: this .label = label;
168: this .mnemonic = mnemonic;
169: this .tooltip = tooltip;
170: this .style = style;
171: menuService = (IMenuService) serviceLocator
172: .getService(IMenuService.class);
173: commandService = (ICommandService) serviceLocator
174: .getService(ICommandService.class);
175: handlerService = (IHandlerService) serviceLocator
176: .getService(IHandlerService.class);
177: bindingService = (IBindingService) serviceLocator
178: .getService(IBindingService.class);
179: createCommand(commandId, parameters);
180:
181: if (command != null) {
182: try {
183: UIElement callback = new UIElement(serviceLocator) {
184:
185: public void setChecked(boolean checked) {
186: CommandContributionItem.this
187: .setChecked(checked);
188: }
189:
190: public void setDisabledIcon(ImageDescriptor desc) {
191: CommandContributionItem.this
192: .setDisabledIcon(desc);
193: }
194:
195: public void setHoverIcon(ImageDescriptor desc) {
196: CommandContributionItem.this .setHoverIcon(desc);
197: }
198:
199: public void setIcon(ImageDescriptor desc) {
200: CommandContributionItem.this .setIcon(desc);
201: }
202:
203: public void setText(String text) {
204: CommandContributionItem.this .setText(text);
205: }
206:
207: public void setTooltip(String text) {
208: CommandContributionItem.this .setTooltip(text);
209: }
210:
211: public void setDropDownId(String id) {
212: dropDownMenuOverride = id;
213: }
214: };
215: elementRef = commandService.registerElementForCommand(
216: command, callback);
217: command.getCommand().addCommandListener(
218: getCommandListener());
219: setImages(serviceLocator);
220: } catch (NotDefinedException e) {
221: WorkbenchPlugin
222: .log("Unable to register menu item \"" + getId() //$NON-NLS-1$
223: + "\", command \"" + commandId + "\" not defined"); //$NON-NLS-1$ //$NON-NLS-2$
224: }
225: }
226: }
227:
228: private void setImages(IServiceLocator locator) {
229: if (icon == null) {
230: ICommandImageService service = (ICommandImageService) locator
231: .getService(ICommandImageService.class);
232: icon = service.getImageDescriptor(command.getId(),
233: ICommandImageService.TYPE_DEFAULT);
234: disabledIcon = service.getImageDescriptor(command.getId(),
235: ICommandImageService.TYPE_DISABLED);
236: hoverIcon = service.getImageDescriptor(command.getId(),
237: ICommandImageService.TYPE_HOVER);
238: }
239: }
240:
241: /**
242: * @return
243: */
244: private ICommandListener getCommandListener() {
245: if (commandListener == null) {
246: commandListener = new ICommandListener() {
247: public void commandChanged(CommandEvent commandEvent) {
248: if (commandEvent.isHandledChanged()
249: || commandEvent.isEnabledChanged()
250: || commandEvent.isDefinedChanged()) {
251: if (commandEvent.isHandledChanged()) {
252: dropDownMenuOverride = null;
253: }
254: if (commandEvent.getCommand().isDefined()) {
255: update(null);
256: }
257: }
258: }
259: };
260: }
261: return commandListener;
262: }
263:
264: ParameterizedCommand getCommand() {
265: return command;
266: }
267:
268: void createCommand(String commandId, Map parameters) {
269: if (commandId == null) {
270: WorkbenchPlugin
271: .log("Unable to create menu item \"" + getId() //$NON-NLS-1$
272: + "\", no command id"); //$NON-NLS-1$
273: return;
274: }
275: Command cmd = commandService.getCommand(commandId);
276: if (!cmd.isDefined()) {
277: WorkbenchPlugin
278: .log("Unable to create menu item \"" + getId() //$NON-NLS-1$
279: + "\", command \"" + commandId + "\" not defined"); //$NON-NLS-1$ //$NON-NLS-2$
280: return;
281: }
282:
283: if (parameters == null || parameters.size() == 0) {
284: command = new ParameterizedCommand(cmd, null);
285: return;
286: }
287:
288: try {
289: ArrayList parmList = new ArrayList();
290: Iterator i = parameters.entrySet().iterator();
291: while (i.hasNext()) {
292: Map.Entry entry = (Map.Entry) i.next();
293: String parmName = (String) entry.getKey();
294: IParameter parm;
295: parm = cmd.getParameter(parmName);
296: if (parm == null) {
297: WorkbenchPlugin
298: .log("Unable to create menu item \"" + getId() //$NON-NLS-1$
299: + "\", parameter \"" + parmName + "\" for command \"" //$NON-NLS-1$ //$NON-NLS-2$
300: + commandId + "\" is not defined"); //$NON-NLS-1$
301: return;
302: }
303: parmList.add(new Parameterization(parm, (String) entry
304: .getValue()));
305: }
306: command = new ParameterizedCommand(cmd,
307: (Parameterization[]) parmList
308: .toArray(new Parameterization[parmList
309: .size()]));
310: } catch (NotDefinedException e) {
311: // this shouldn't happen as we checked for !defined, but we
312: // won't take the chance
313: WorkbenchPlugin.log("Failed to create menu item " //$NON-NLS-1$
314: + getId(), e);
315: }
316: }
317:
318: /*
319: * (non-Javadoc)
320: *
321: * @see org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets.Menu,
322: * int)
323: */
324: public void fill(Menu parent, int index) {
325: if (command == null) {
326: return;
327: }
328: if (widget != null || parent == null) {
329: return;
330: }
331:
332: // Menus don't support the pulldown style
333: int tmpStyle = style;
334: if (tmpStyle == STYLE_PULLDOWN)
335: tmpStyle = STYLE_PUSH;
336:
337: MenuItem item = null;
338: if (index >= 0) {
339: item = new MenuItem(parent, tmpStyle, index);
340: } else {
341: item = new MenuItem(parent, tmpStyle);
342: }
343: item.setData(this );
344:
345: item.addListener(SWT.Dispose, getItemListener());
346: item.addListener(SWT.Selection, getItemListener());
347: widget = item;
348:
349: update(null);
350: }
351:
352: /*
353: * (non-Javadoc)
354: *
355: * @see org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets.ToolBar,
356: * int)
357: */
358: public void fill(ToolBar parent, int index) {
359: if (command == null) {
360: return;
361: }
362: if (widget != null || parent == null) {
363: return;
364: }
365:
366: ToolItem item = null;
367: if (index >= 0) {
368: item = new ToolItem(parent, style, index);
369: } else {
370: item = new ToolItem(parent, style);
371: }
372:
373: item.setData(this );
374:
375: item.addListener(SWT.Selection, getItemListener());
376: item.addListener(SWT.Dispose, getItemListener());
377: widget = item;
378:
379: update(null);
380: }
381:
382: /*
383: * (non-Javadoc)
384: *
385: * @see org.eclipse.jface.action.ContributionItem#update()
386: */
387: public void update() {
388: update(null);
389: }
390:
391: /*
392: * (non-Javadoc)
393: *
394: * @see org.eclipse.jface.action.ContributionItem#update(java.lang.String)
395: */
396: public void update(String id) {
397: if (widget != null) {
398: if (widget instanceof MenuItem) {
399: MenuItem item = (MenuItem) widget;
400:
401: String text = label;
402: if (text == null) {
403: if (command != null) {
404: try {
405: text = command.getCommand().getName();
406: } catch (NotDefinedException e) {
407: WorkbenchPlugin.log("Update item failed " //$NON-NLS-1$
408: + getId(), e);
409: }
410: }
411: }
412: text = updateMnemonic(text);
413:
414: String keyBindingText = null;
415: if (command != null) {
416: TriggerSequence[] bindings = bindingService
417: .getActiveBindingsFor(command);
418: if (bindings.length > 0) {
419: keyBindingText = bindings[0].format();
420: }
421: }
422: if (text != null) {
423: if (keyBindingText == null) {
424: item.setText(text);
425: } else {
426: item.setText(text + '\t' + keyBindingText);
427: }
428: }
429:
430: updateIcons();
431: if (item.getSelection() != checkedState) {
432: item.setSelection(checkedState);
433: }
434:
435: boolean shouldBeEnabled = isEnabled();
436: if (item.getEnabled() != shouldBeEnabled) {
437: item.setEnabled(shouldBeEnabled);
438: }
439: } else if (widget instanceof ToolItem) {
440: ToolItem item = (ToolItem) widget;
441:
442: if (icon != null) {
443: updateIcons();
444: } else if (label != null) {
445: item.setText(label);
446: }
447:
448: if (tooltip != null)
449: item.setToolTipText(tooltip);
450: else {
451: String text = label;
452: if (text == null) {
453: if (command != null) {
454: try {
455: text = command.getCommand().getName();
456: } catch (NotDefinedException e) {
457: WorkbenchPlugin.log(
458: "Update item failed " //$NON-NLS-1$
459: + getId(), e);
460: }
461: }
462: }
463: if (text != null) {
464: item.setToolTipText(text);
465: }
466: }
467:
468: if (item.getSelection() != checkedState) {
469: item.setSelection(checkedState);
470: }
471:
472: boolean shouldBeEnabled = isEnabled();
473: if (item.getEnabled() != shouldBeEnabled) {
474: item.setEnabled(shouldBeEnabled);
475: }
476: }
477: }
478: }
479:
480: private String updateMnemonic(String s) {
481: if (mnemonic == null || s == null) {
482: return s;
483: }
484: int idx = s.indexOf(mnemonic);
485: if (idx == -1) {
486: return s;
487: }
488:
489: return s.substring(0, idx) + '&' + s.substring(idx);
490: }
491:
492: private void handleWidgetDispose(Event event) {
493: if (event.widget == widget) {
494: widget.removeListener(SWT.Selection, getItemListener());
495: widget.removeListener(SWT.Dispose, getItemListener());
496: widget = null;
497: disposeOldImages();
498: }
499: }
500:
501: /*
502: * (non-Javadoc)
503: *
504: * @see org.eclipse.jface.action.ContributionItem#dispose()
505: */
506: public void dispose() {
507: if (elementRef != null) {
508: commandService.unregisterElement(elementRef);
509: elementRef = null;
510: }
511: if (commandListener != null) {
512: command.getCommand().removeCommandListener(commandListener);
513: commandListener = null;
514: }
515: command = null;
516: commandService = null;
517: disposeOldImages();
518: super .dispose();
519: }
520:
521: private void disposeOldImages() {
522: if (localResourceManager != null) {
523: localResourceManager.dispose();
524: localResourceManager = null;
525: }
526: }
527:
528: private Listener getItemListener() {
529: if (menuItemListener == null) {
530: menuItemListener = new Listener() {
531: public void handleEvent(Event event) {
532: switch (event.type) {
533: case SWT.Dispose:
534: handleWidgetDispose(event);
535: break;
536: case SWT.Selection:
537: if (event.widget != null) {
538: handleWidgetSelection(event);
539: }
540: break;
541: }
542: }
543: };
544: }
545: return menuItemListener;
546: }
547:
548: private void handleWidgetSelection(Event event) {
549: // Special check for ToolBar dropdowns...
550: if (openDropDownMenu(event))
551: return;
552:
553: if ((style & (SWT.TOGGLE | SWT.CHECK)) != 0) {
554: if (event.widget instanceof ToolItem) {
555: checkedState = ((ToolItem) event.widget).getSelection();
556: } else if (event.widget instanceof MenuItem) {
557: checkedState = ((MenuItem) event.widget).getSelection();
558: }
559: }
560:
561: try {
562: handlerService.executeCommand(command, event);
563: } catch (ExecutionException e) {
564: WorkbenchPlugin.log("Failed to execute item " //$NON-NLS-1$
565: + getId(), e);
566: } catch (NotDefinedException e) {
567: WorkbenchPlugin.log("Failed to execute item " //$NON-NLS-1$
568: + getId(), e);
569: } catch (NotEnabledException e) {
570: WorkbenchPlugin.log("Failed to execute item " //$NON-NLS-1$
571: + getId(), e);
572: } catch (NotHandledException e) {
573: WorkbenchPlugin.log("Failed to execute item " //$NON-NLS-1$
574: + getId(), e);
575: }
576: }
577:
578: /**
579: * Determines if the selection was on the dropdown affordance and, if so,
580: * opens the drop down menu (populated using the same id as this item...
581: *
582: * @param event
583: * The <code>SWT.Selection</code> event to be tested
584: *
585: * @return <code>true</code> iff a drop down menu was opened
586: */
587: private boolean openDropDownMenu(Event event) {
588: Widget item = event.widget;
589: if (item != null) {
590: int style = item.getStyle();
591: if ((style & SWT.DROP_DOWN) != 0) {
592: if (event.detail == 4) { // on drop-down button
593: ToolItem ti = (ToolItem) item;
594:
595: final MenuManager menuManager = new MenuManager();
596: Menu menu = menuManager.createContextMenu(ti
597: .getParent());
598: menuManager.addMenuListener(new IMenuListener() {
599: public void menuAboutToShow(IMenuManager manager) {
600: String id = getId();
601: if (dropDownMenuOverride != null) {
602: id = dropDownMenuOverride;
603: }
604: menuService.populateContributionManager(
605: menuManager, "menu:" + id); //$NON-NLS-1$
606: }
607: });
608:
609: // position the menu below the drop down item
610: Point point = ti.getParent().toDisplay(
611: new Point(event.x, event.y));
612: menu.setLocation(point.x, point.y); // waiting for SWT
613: // 0.42
614: menu.setVisible(true);
615: return true; // we don't fire the action
616: }
617: }
618: }
619:
620: return false;
621: }
622:
623: private void setIcon(ImageDescriptor desc) {
624: icon = desc;
625: updateIcons();
626: }
627:
628: private void updateIcons() {
629: if (widget instanceof MenuItem) {
630: MenuItem item = (MenuItem) widget;
631: LocalResourceManager m = new LocalResourceManager(
632: JFaceResources.getResources());
633: item.setImage(icon == null ? null : m.createImage(icon));
634: disposeOldImages();
635: localResourceManager = m;
636: } else if (widget instanceof ToolItem) {
637: ToolItem item = (ToolItem) widget;
638: LocalResourceManager m = new LocalResourceManager(
639: JFaceResources.getResources());
640: item.setDisabledImage(disabledIcon == null ? null : m
641: .createImage(disabledIcon));
642: item.setHotImage(hoverIcon == null ? null : m
643: .createImage(hoverIcon));
644: item.setImage(icon == null ? null : m.createImage(icon));
645: disposeOldImages();
646: localResourceManager = m;
647: }
648: }
649:
650: private void setText(String text) {
651: label = text;
652: update(null);
653: }
654:
655: private void setChecked(boolean checked) {
656: if (checkedState == checked) {
657: return;
658: }
659: checkedState = checked;
660: if (widget instanceof MenuItem) {
661: ((MenuItem) widget).setSelection(checkedState);
662: } else if (widget instanceof ToolItem) {
663: ((ToolItem) widget).setSelection(checkedState);
664: }
665: }
666:
667: private void setTooltip(String text) {
668: tooltip = text;
669: if (widget instanceof ToolItem) {
670: ((ToolItem) widget).setToolTipText(text);
671: }
672: }
673:
674: private void setDisabledIcon(ImageDescriptor desc) {
675: disabledIcon = desc;
676: updateIcons();
677: }
678:
679: private void setHoverIcon(ImageDescriptor desc) {
680: hoverIcon = desc;
681: updateIcons();
682: }
683:
684: /*
685: * (non-Javadoc)
686: *
687: * @see org.eclipse.jface.action.ContributionItem#isEnabled()
688: */
689: public boolean isEnabled() {
690: if (command != null) {
691: return command.getCommand().isEnabled();
692: }
693: return false;
694: }
695: }
|