1 package org.simantics.ui.toolbar;
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.HashSet;
10 import org.eclipse.core.commands.Command;
11 import org.eclipse.core.commands.CommandEvent;
12 import org.eclipse.core.commands.ExecutionException;
13 import org.eclipse.core.commands.ICommandListener;
14 import org.eclipse.core.commands.IParameter;
15 import org.eclipse.core.commands.Parameterization;
16 import org.eclipse.core.commands.ParameterizedCommand;
17 import org.eclipse.core.commands.State;
18 import org.eclipse.core.runtime.CoreException;
19 import org.eclipse.core.runtime.IConfigurationElement;
20 import org.eclipse.core.runtime.IExecutableExtension;
21 import org.eclipse.jface.action.Action;
22 import org.eclipse.jface.action.ActionContributionItem;
23 import org.eclipse.jface.action.IContributionItem;
24 import org.eclipse.jface.action.ICoolBarManager;
25 import org.eclipse.jface.action.IToolBarManager;
26 import org.eclipse.jface.action.ToolBarContributionItem;
27 import org.eclipse.jface.resource.ImageDescriptor;
28 import org.eclipse.nebula.widgets.tablecombo.TableCombo;
29 import org.eclipse.swt.SWT;
30 import org.eclipse.swt.events.SelectionEvent;
31 import org.eclipse.swt.events.SelectionListener;
32 import org.eclipse.swt.layout.GridData;
33 import org.eclipse.swt.layout.GridLayout;
34 import org.eclipse.swt.widgets.Composite;
35 import org.eclipse.swt.widgets.Control;
36 import org.eclipse.swt.widgets.TableItem;
37 import org.eclipse.ui.IEditorPart;
38 import org.eclipse.ui.IPartListener;
39 import org.eclipse.ui.IWorkbenchPart;
40 import org.eclipse.ui.PlatformUI;
41 import org.eclipse.ui.commands.ICommandService;
42 import org.eclipse.ui.handlers.HandlerUtil;
43 import org.eclipse.ui.handlers.IHandlerService;
44 import org.eclipse.ui.handlers.RadioState;
45 import org.eclipse.ui.handlers.RegistryToggleState;
46 import org.eclipse.ui.menus.WorkbenchWindowControlContribution;
47 import org.eclipse.ui.part.EditorActionBarContributor;
48 import org.simantics.db.common.utils.Logger;
49 import org.simantics.ui.internal.Activator;
50 import org.simantics.ui.toolbar.ToolBarCommandRegistry.Parameter;
51 import org.simantics.ui.toolbar.ToolBarCommandRegistry.ToolbarCommandExtension;
52 import org.simantics.utils.datastructures.MapList;
53 import org.simantics.utils.ui.ExceptionUtils;
57 * EditorBarContributor, which tracks toggle states separately for each command.
59 * @see org.simantics.g3d.toolbarCommand Extension Point
61 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
65 * TODO : configuring the position of buttons.
68 public class ToolbarContributor extends EditorActionBarContributor implements ICommandListener, IPartListener, CommandStateListener, IExecutableExtension {
70 private static boolean DEBUG = false; // Print debug messages to console
71 private boolean REUSE = true; // true: Reuse contribution items (leave toolbar in disabled state when editor closes)
72 // false: delete items on dispose (remove toolbar editor closes)
74 private static final String PLATFORM = "platform:/plugin/";
76 private String toolbarId;
78 private IEditorPart activePart;
79 private Set<IEditorPart> parts = new HashSet<IEditorPart>();
83 ICommandService service;
84 IHandlerService handlerService;
85 List<IContributionItem> items = new ArrayList<IContributionItem>();
86 MapList<String, CommandAction> actions = new MapList<String, ToolbarContributor.CommandAction>();
88 CommandStateRegistry stateRegistry;
90 IPartListener partListener;
92 private Map<String,ComboContribution> menus = new HashMap<String, ComboContribution>();
94 public ToolbarContributor() {
95 service = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);
96 handlerService = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
97 stateRegistry = CommandStateRegistry.getInstance();
98 // we need part listener to be notified for other editor activations (i.e editors that do not use this contributor).
99 partListener = new IPartListener() {
102 public void partOpened(IWorkbenchPart part) {}
105 public void partDeactivated(IWorkbenchPart part) {}
108 public void partClosed(IWorkbenchPart part) {}
111 public void partBroughtToTop(IWorkbenchPart part) {}
114 public void partActivated(IWorkbenchPart part) {
115 if (part instanceof IEditorPart)
116 setContext2((IEditorPart)part);
119 PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().addPartListener(partListener);
124 public void setInitializationData(IConfigurationElement config,String propertyName, Object data) throws CoreException {
125 if (data instanceof String) {
126 String[] parameters = ((String) data).split(";");
127 for (String parameter : parameters) {
128 String[] keyValue = parameter.split("=");
129 if (keyValue.length > 2) {
130 //ErrorLogger.defaultLogWarning("Invalid parameter '" + parameter + ". Complete view argument: " + data, null);
133 String key = keyValue[0];
134 String value = keyValue.length > 1 ? keyValue[1] : "";
136 if ("toolbar".equals(key)) {
138 } else if ("hide".equals(key)) {
139 REUSE = !Boolean.parseBoolean(value);
146 public String getToolbarId() {
151 private ICoolBarManager coolBarManager;
152 private IContributionItem toolBar;
153 public void contributeToCoolBar(ICoolBarManager coolBarManager) {
154 this.coolBarManager = coolBarManager;
155 toolBar = coolBarManager.find(getToolbarId());
157 if (toolBar instanceof ToolBarContributionItem)
158 mgr = ((ToolBarContributionItem) toolBar).getToolBarManager();
168 private void createCommands() {
169 for (ToolbarCommandExtension ext : ToolBarCommandRegistry.getInstance().getExtensions(getToolbarId())) {
174 private void addCommand(ToolbarCommandExtension ext) {
175 if (DEBUG) System.out.println("Adding command to toolbar " +getToolbarId() + " " + ext);
176 String commandId = ext.commandId;
177 Command command = service.getCommand(commandId);
178 ICommandWrapper wrapper = new CommandWrapper(command);
180 ParameterizedCommand parameterizedCommand = null;
182 if (ext.parameters.size() > 0) {
184 Parameterization parameterizations[] = new Parameterization[ext.parameters.size()];
185 for (int i = 0; i < ext.parameters.size(); i++) {
186 Parameter param = ext.parameters.get(i);
187 IParameter parameter = command.getParameter(param.name);
188 parameterizations[i] = new Parameterization(parameter, param.value);
190 parameterizedCommand = new ParameterizedCommand(command, parameterizations);
191 wrapper = new ParameterizedCommandWrapper(parameterizedCommand);
192 } catch (org.eclipse.core.commands.common.NotDefinedException e) {
194 ExceptionUtils.logError(e);
199 String type = ext.type;
201 State toggleState = command.getState(RegistryToggleState.STATE_ID);
202 State radioState = command.getState(RadioState.STATE_ID);
204 String name = ext.name;
207 ImageDescriptor image = getImage(ext);
209 CommandAction a = null;
210 if (type.equals("toggle") && toggleState != null) {
211 a = new CommandCheckboxAction(wrapper,name,image);
213 stateRegistry.storeDefaultState(commandId);
214 } else if (radioState != null && ext.value != null) {
215 stateRegistry.storeDefaultState(commandId);
216 if (type.equals("radio")) {
217 a = new CommandRadioAction(wrapper,name,ext.value,image);
218 } else if (type.equals("combo")) {
219 a = new CommandRadioAction(wrapper,name,ext.value,image);
220 ComboContribution combo = menus.get(commandId);
221 if (REUSE && combo == null) {
222 combo = (ComboContribution)mgr.find(commandId);
224 menus.put(commandId, combo);
226 a.getCommand().addCommandListener(this);
227 CommandStateRegistry.getInstance().addListener(commandId, this);
231 combo = new ComboContribution();
232 combo.setId(commandId);
233 menus.put(commandId, combo);
236 a.getCommand().addCommandListener(this);
237 CommandStateRegistry.getInstance().addListener(commandId, this);
239 actions.add(commandId,a);
243 } else if (type.equals("push")) {
244 a = new CommandPushAction(wrapper,name,image);
246 if (DEBUG) System.out.println(ext + " is not valid.");
247 Logger.defaultLogError(ext + " is not valid.");
250 IContributionItem item = null;
252 String id = commandId;
253 if (ext.value != null)
257 item = new ActionContributionItem(a);
258 ((ActionContributionItem)item).setId(id);
260 if (DEBUG) System.out.println("Reusing " + ext);
261 a = (CommandAction)((ActionContributionItem)item).getAction();
264 item = new ActionContributionItem(a);
266 a.getCommand().addCommandListener(this);
267 actions.add(commandId,a);
270 CommandStateRegistry.getInstance().addListener(commandId, this);
273 private ImageDescriptor getImage(ToolbarCommandExtension ext) {
274 ImageDescriptor image = null;
275 if (ext.image != null) {
276 String plugin = null;
278 if (ext.image.startsWith(PLATFORM)) {
279 String s = ext.image.substring(PLATFORM.length());
280 int i = s.indexOf("/");
281 plugin = s.substring(0,i);
282 file = s.substring(i+1);
284 plugin = ext.contributorId;
287 image = Activator.imageDescriptorFromPlugin(plugin, file);
295 public void commandChanged(CommandEvent commandEvent) {
296 if (commandEvent.isHandledChanged()||commandEvent.isEnabledChanged()) {
297 Command command = commandEvent.getCommand();
298 String commandId = command.getId();
299 for (CommandAction a : actions.getValues(commandId)) {
300 a.setEnabled(command.isHandled() && command.isEnabled());
306 public void setActiveEditor(IEditorPart targetEditor) {
307 if (targetEditor == activePart)
309 setContext(targetEditor);
313 public void stateChanged(IWorkbenchPart part, String commandId, String state) {
314 // TODO : update only given command
317 if (part instanceof IEditorPart)
318 setContext((IEditorPart)part);
321 private void setContext2(IEditorPart part) {
324 if (this.activePart == part)
329 private void setContext(IEditorPart part) {
330 this.activePart = part;
331 if (activePart != null && !parts.contains(activePart)) {
332 activePart.getSite().getPage().addPartListener(this);
335 for (String commandId : actions.getKeys()) {
336 for (CommandAction a : actions.getValues(commandId)) {
339 ComboContribution menu = menus.get(commandId);
341 menu.setEnabled(true);
344 updateActionBars(part);
346 for (String commandId : actions.getKeys()) {
347 for (CommandAction a : actions.getValues(commandId)) {
350 ComboContribution menu = menus.get(commandId);
352 menu.setEnabled(false);
359 private void updateActionBars(IEditorPart part) {
360 restoreActionStates();
361 part.getEditorSite().getActionBars().updateActionBars();
365 public void dispose() {
366 if (DEBUG) System.out.println("ToolBarContributor.dispose()");
367 setActiveEditor(null);
370 for (IContributionItem item : items) {
377 for (String commandId : actions.getKeys()) {
378 for (CommandAction a : actions.getValues(commandId)) {
379 a.getCommand().removeCommandListener(this);
385 // without this the contributed toolbar widgets would continue reserve the space even when they are destroyed.
386 coolBarManager.update(true);
389 CommandStateRegistry.getInstance().removeListener(this);
392 if (partListener != null) {
393 PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().addPartListener(partListener);
398 boolean settingState = false;
399 private void storeRadioActionState(CommandRadioAction action, boolean checked) {
400 if (activePart == null)
403 stateRegistry.setEditorState(activePart, action.getCommandId(), action.getValue());
404 settingState = false;
407 private void storeToggleActionState(CommandAction action, boolean checked) {
408 if (activePart == null)
411 stateRegistry.setEditorState(activePart, action.getCommandId(), checked);
412 settingState = false;
417 private void restoreActionStates() {
418 if (activePart == null)
420 if (DEBUG)System.out.println("Restore " + activePart);
422 Map<String,Boolean> defaultToggleStates = stateRegistry.getDefaultToggleStates();
423 for (String commandId : defaultToggleStates.keySet()) {
424 for (CommandAction a : actions.getValues(commandId)) {
425 if (DEBUG)System.out.println(commandId + " def " + defaultToggleStates.get(commandId));
426 a.setChecked(defaultToggleStates.get(commandId));
429 Map<String,Boolean> editorStates = stateRegistry.getEditorToggleStates(activePart);//toggleStates.get(activePart);
430 if (editorStates != null) {
431 for (String commandId : editorStates.keySet()) {
432 for (CommandAction a : actions.getValues(commandId)) {
433 if (DEBUG)System.out.println(commandId + " " + editorStates.get(commandId));
434 a.setChecked(editorStates.get(commandId));
439 Map<String,String> defaultRadioStates = stateRegistry.getDefaultRadioStates();
440 for (String commandId : defaultRadioStates.keySet()) {
441 String defaultValue = defaultRadioStates.get(commandId);
442 for (CommandAction a : actions.getValues(commandId)) {
443 CommandRadioAction r = (CommandRadioAction)a;
444 if (DEBUG)System.out.println(commandId + " def " + r.getValue().equals(defaultValue) +" " + r.getValue());
445 r.setChecked(r.getValue().equals(defaultValue));
449 Map<String,String> editorRadioStates = stateRegistry.getEditorRadioStates(activePart);//radioStates.get(activePart);
450 if (editorRadioStates != null) {
451 for (String commandId : editorRadioStates.keySet()) {
452 String defaultValue = editorRadioStates.get(commandId);
453 for (CommandAction a : actions.getValues(commandId)) {
454 CommandRadioAction r = (CommandRadioAction)a;
455 if (DEBUG)System.out.println(commandId + " " + r.getValue().equals(defaultValue) +" " + r.getValue());
456 r.setChecked(r.getValue().equals(defaultValue));
461 for (ComboContribution c : menus.values()) {
468 public void partActivated(IWorkbenchPart part) {
473 public void partBroughtToTop(IWorkbenchPart part) {
478 public void partClosed(IWorkbenchPart part) {
480 stateRegistry.clearStates(part);
481 part.getSite().getPage().removePartListener(this);
482 if (parts.size() == 0) {
485 if (part instanceof IEditorPart)
486 ((IEditorPart)part).getEditorSite().getActionBars().updateActionBars();
491 public void partDeactivated(IWorkbenchPart part) {
496 public void partOpened(IWorkbenchPart part) {
500 private boolean getToggleState(Command command) {
501 State toggleState = command.getState(RegistryToggleState.STATE_ID);
502 return (Boolean)toggleState.getValue();
505 private interface ICommandWrapper {
507 public Command getCommand();
508 public String getCommandId();
512 private class CommandWrapper implements ICommandWrapper{
513 private Command command;
515 public CommandWrapper(Command command) {
516 this.command = command;
520 public Command getCommand() {
525 public String getCommandId() {
526 return command.getId();
532 handlerService.executeCommand(command.getId(), null);
533 } catch (Exception e) {
539 public boolean equals(Object obj) {
540 if (obj.getClass() != getClass())
542 CommandWrapper other= (CommandWrapper)obj;
543 return other.getCommandId().equals(getCommandId());
547 private class ParameterizedCommandWrapper implements ICommandWrapper{
548 private ParameterizedCommand command;
550 public ParameterizedCommandWrapper(ParameterizedCommand command) {
551 this.command = command;
555 public Command getCommand() {
556 return command.getCommand();
560 public String getCommandId() {
561 return command.getId();
567 handlerService.executeCommand(command, null);
568 } catch (Exception e) {
574 public boolean equals(Object obj) {
575 if (obj.getClass() != getClass())
577 ParameterizedCommandWrapper other= (ParameterizedCommandWrapper)obj;
578 return other.command.equals(command);
582 private abstract class CommandAction extends Action {
583 private ICommandWrapper command;
585 public CommandAction(ICommandWrapper command, String name, ImageDescriptor image, int style) {
587 this.command = command;
589 setImageDescriptor(image);
597 public Command getCommand() {
598 return command.getCommand();
601 public String getCommandId() {
602 return command.getCommandId();
607 public boolean equals(Object obj) {
608 if (obj.getClass() != getClass())
610 CommandAction other= (CommandAction)obj;
611 return command.equals(other.command);
616 public int hashCode() {
617 return command.getCommandId().hashCode();
622 private class CommandCheckboxAction extends CommandAction {
624 public CommandCheckboxAction(ICommandWrapper command, String name, ImageDescriptor image) {
625 super(command,name,image,Action.AS_CHECK_BOX);
631 boolean checked = isChecked();
632 storeToggleActionState(this, checked);
634 if (checked == getToggleState(getCommand()))
635 HandlerUtil.toggleCommandState(getCommand());
636 } catch (ExecutionException e) {
644 private class CommandRadioAction extends CommandAction {
646 private String value;
648 public CommandRadioAction(ICommandWrapper command, String name, String value, ImageDescriptor image) {
649 super(command,name,image,Action.AS_RADIO_BUTTON);
655 boolean checked = isChecked();
656 storeRadioActionState(this, checked);
658 HandlerUtil.updateRadioState(getCommand(), value);
659 } catch (ExecutionException e) {
666 public String getValue() {
671 public boolean equals(Object obj) {
672 if (obj.getClass() != getClass())
674 CommandRadioAction other= (CommandRadioAction)obj;
675 if (!other.getCommandId().equals(getCommandId()))
677 if (!other.value.equals(value))
684 private class CommandPushAction extends CommandAction {
686 public CommandPushAction(ICommandWrapper command, String name, ImageDescriptor image) {
687 super(command,name,image,Action.AS_PUSH_BUTTON);
692 private class ComboContribution extends WorkbenchWindowControlContribution {
693 private TableCombo combo;
695 private List<Action> actions = new ArrayList<Action>();
698 protected Control createControl(Composite parent) {
699 Composite container = new Composite(parent, SWT.NONE);
700 GridLayout glContainer = new GridLayout(1, false);
701 glContainer.marginTop = 0;
702 glContainer.marginHeight = 0;
703 glContainer.marginWidth = 0;
704 container.setLayout(glContainer);
705 GridData glReader = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1);
706 combo = new TableCombo(container, SWT.BORDER | SWT.READ_ONLY);
707 combo.setLayoutData(glReader);
709 for (Action a : actions) {
710 TableItem item = new TableItem(combo.getTable(), SWT.NONE);
711 item.setText(a.getText());
712 if (a.getImageDescriptor() != null)
713 item.setImage(a.getImageDescriptor().createImage());
716 combo.addSelectionListener(new SelectionListener() {
719 public void widgetSelected(SelectionEvent e) {
720 int index = combo.getSelectionIndex();
723 actions.get(index).run();
727 public void widgetDefaultSelected(SelectionEvent e) {
735 public boolean addAction(Action a) {
736 // old action must be replaced. (otherwise reused ComboContributor would use different instances to ToolBarContributor.)
743 void updateSelection() {
746 for (int i = 0; i < actions.size(); i++) {
747 if (actions.get(i).isChecked()) {
754 public void setEnabled(boolean enabled) {
756 combo.setEnabled(enabled);
760 public void dispose() {