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