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.State;
\r
15 import org.eclipse.core.runtime.CoreException;
\r
16 import org.eclipse.core.runtime.IConfigurationElement;
\r
17 import org.eclipse.core.runtime.IExecutableExtension;
\r
18 import org.eclipse.jface.action.Action;
\r
19 import org.eclipse.jface.action.ActionContributionItem;
\r
20 import org.eclipse.jface.action.IContributionItem;
\r
21 import org.eclipse.jface.action.ICoolBarManager;
\r
22 import org.eclipse.jface.action.IToolBarManager;
\r
23 import org.eclipse.jface.action.ToolBarContributionItem;
\r
24 import org.eclipse.jface.resource.ImageDescriptor;
\r
25 import org.eclipse.nebula.widgets.tablecombo.TableCombo;
\r
26 import org.eclipse.swt.SWT;
\r
27 import org.eclipse.swt.events.SelectionEvent;
\r
28 import org.eclipse.swt.events.SelectionListener;
\r
29 import org.eclipse.swt.layout.GridData;
\r
30 import org.eclipse.swt.layout.GridLayout;
\r
31 import org.eclipse.swt.widgets.Composite;
\r
32 import org.eclipse.swt.widgets.Control;
\r
33 import org.eclipse.swt.widgets.TableItem;
\r
34 import org.eclipse.ui.IEditorPart;
\r
35 import org.eclipse.ui.IPartListener;
\r
36 import org.eclipse.ui.IWorkbenchPart;
\r
37 import org.eclipse.ui.PlatformUI;
\r
38 import org.eclipse.ui.commands.ICommandService;
\r
39 import org.eclipse.ui.handlers.HandlerUtil;
\r
40 import org.eclipse.ui.handlers.IHandlerService;
\r
41 import org.eclipse.ui.handlers.RadioState;
\r
42 import org.eclipse.ui.handlers.RegistryToggleState;
\r
43 import org.eclipse.ui.menus.WorkbenchWindowControlContribution;
\r
44 import org.eclipse.ui.part.EditorActionBarContributor;
\r
46 import org.simantics.db.common.utils.Logger;
\r
47 import org.simantics.ui.internal.Activator;
\r
48 import org.simantics.ui.toolbar.ToolBarCommandRegistry.ToolbarCommandExtension;
\r
49 import org.simantics.utils.datastructures.MapList;
\r
53 * EditorBarContributor, which tracks toggle states separately for each command.
\r
55 * @see org.simantics.g3d.toolbarCommand Extension Point
\r
57 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
\r
61 * TODO : configuring the position of buttons.
\r
64 public class ToolbarContributor extends EditorActionBarContributor implements ICommandListener, IPartListener, CommandStateListener, IExecutableExtension {
\r
66 private static boolean DEBUG = false; // Print debug messages to console
\r
67 private static boolean REUSE = true; // true: Reuse contribution items / false: delete items on dispose
\r
69 private static final String PLATFORM = "platform:/plugin/";
\r
71 private String toolbarId;
\r
73 private IEditorPart activePart;
\r
74 private Set<IEditorPart> parts = new HashSet<IEditorPart>();
\r
75 IToolBarManager mgr;
\r
78 ICommandService service;
\r
79 IHandlerService handlerService;
\r
80 List<IContributionItem> items = new ArrayList<IContributionItem>();
\r
81 MapList<String, CommandAction> actions = new MapList<String, ToolbarContributor.CommandAction>();
\r
83 CommandStateRegistry stateRegistry;
\r
85 private Map<String,ComboContribution> menus = new HashMap<String, ComboContribution>();
\r
87 public ToolbarContributor() {
\r
88 service = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);
\r
89 handlerService = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
\r
90 stateRegistry = CommandStateRegistry.getInstance();
\r
95 public void setInitializationData(IConfigurationElement config,String propertyName, Object data) throws CoreException {
\r
96 if (data instanceof String) {
\r
97 String[] parameters = ((String) data).split(";");
\r
98 for (String parameter : parameters) {
\r
99 String[] keyValue = parameter.split("=");
\r
100 if (keyValue.length > 2) {
\r
101 //ErrorLogger.defaultLogWarning("Invalid parameter '" + parameter + ". Complete view argument: " + data, null);
\r
104 String key = keyValue[0];
\r
105 String value = keyValue.length > 1 ? keyValue[1] : "";
\r
107 if ("toolbar".equals(key)) {
\r
115 public String getToolbarId() {
\r
120 private ICoolBarManager coolBarManager;
\r
121 private IContributionItem toolBar;
\r
122 public void contributeToCoolBar(ICoolBarManager coolBarManager) {
\r
123 this.coolBarManager = coolBarManager;
\r
124 toolBar = coolBarManager.find(getToolbarId());
\r
126 if (toolBar instanceof ToolBarContributionItem)
\r
127 mgr = ((ToolBarContributionItem) toolBar).getToolBarManager();
\r
137 private void createCommands() {
\r
138 for (ToolbarCommandExtension ext : ToolBarCommandRegistry.getInstance().getExtensions(getToolbarId())) {
\r
143 private void addCommand(ToolbarCommandExtension ext) {
\r
144 if (DEBUG) System.out.println("Adding command to toolbar " +getToolbarId() + " " + ext);
\r
145 String commandId = ext.commandId;
\r
146 Command command = service.getCommand(commandId);
\r
148 String type = ext.type;
\r
150 State toggleState = command.getState(RegistryToggleState.STATE_ID);
\r
151 State radioState = command.getState(RadioState.STATE_ID);
\r
153 String name = ext.name;
\r
156 ImageDescriptor image = getImage(ext);
\r
158 CommandAction a = null;
\r
159 if (type.equals("toggle") && toggleState != null) {
\r
160 a = new CommandCheckboxAction(command,name,image);
\r
162 stateRegistry.storeDefaultState(commandId);
\r
163 } else if (radioState != null && ext.value != null) {
\r
164 stateRegistry.storeDefaultState(commandId);
\r
165 if (type.equals("radio")) {
\r
166 a = new CommandRadioAction(command,name,ext.value,image);
\r
167 } else if (type.equals("combo")) {
\r
168 a = new CommandRadioAction(command,name,ext.value,image);
\r
169 ComboContribution combo = menus.get(commandId);
\r
170 if (REUSE && combo == null) {
\r
171 combo = (ComboContribution)mgr.find(commandId);
\r
172 if (combo != null) {
\r
173 menus.put(commandId, combo);
\r
175 a.getCommand().addCommandListener(this);
\r
176 CommandStateRegistry.getInstance().addListener(commandId, this);
\r
179 if (combo == null) {
\r
180 combo = new ComboContribution();
\r
181 combo.setId(commandId);
\r
182 menus.put(commandId, combo);
\r
185 a.getCommand().addCommandListener(this);
\r
186 CommandStateRegistry.getInstance().addListener(commandId, this);
\r
188 actions.add(commandId,a);
\r
189 combo.addAction(a);
\r
192 } else if (type.equals("push")) {
\r
193 a = new CommandPushAction(command,name,image);
\r
195 if (DEBUG) System.out.println(ext + " is not valid.");
\r
196 Logger.defaultLogError(ext + " is not valid.");
\r
199 IContributionItem item = null;
\r
201 String id = commandId;
\r
202 if (ext.value != null)
\r
203 id += "."+ext.value;
\r
204 item = mgr.find(id);
\r
205 if (item == null) {
\r
206 item = new ActionContributionItem(a);
\r
207 ((ActionContributionItem)item).setId(id);
\r
209 if (DEBUG) System.out.println("Reusing " + ext);
\r
210 a = (CommandAction)((ActionContributionItem)item).getAction();
\r
213 item = new ActionContributionItem(a);
\r
215 a.getCommand().addCommandListener(this);
\r
216 actions.add(commandId,a);
\r
219 CommandStateRegistry.getInstance().addListener(commandId, this);
\r
222 private ImageDescriptor getImage(ToolbarCommandExtension ext) {
\r
223 ImageDescriptor image = null;
\r
224 if (ext.image != null) {
\r
225 String plugin = null;
\r
226 String file = null;
\r
227 if (ext.image.startsWith(PLATFORM)) {
\r
228 String s = ext.image.substring(PLATFORM.length());
\r
229 int i = s.indexOf("/");
\r
230 plugin = s.substring(0,i);
\r
231 file = s.substring(i+1);
\r
233 plugin = ext.contributorId;
\r
236 image = Activator.imageDescriptorFromPlugin(plugin, file);
\r
244 public void commandChanged(CommandEvent commandEvent) {
\r
245 if (commandEvent.isHandledChanged()||commandEvent.isEnabledChanged()) {
\r
246 Command command = commandEvent.getCommand();
\r
247 String commandId = command.getId();
\r
248 for (CommandAction a : actions.getValues(commandId)) {
\r
249 a.setEnabled(command.isHandled() && command.isEnabled());
\r
255 public void setActiveEditor(IEditorPart targetEditor) {
\r
256 if (targetEditor == activePart)
\r
258 setContext(targetEditor);
\r
262 public void stateChanged(IWorkbenchPart part, String commandId, String state) {
\r
263 // TODO : update only given command
\r
266 if (part instanceof IEditorPart)
\r
267 setContext((IEditorPart)part);
\r
270 private void setContext(IEditorPart part) {
\r
271 this.activePart = part;
\r
272 if (activePart != null && !parts.contains(activePart)) {
\r
273 activePart.getSite().getPage().addPartListener(this);
\r
275 if (part != null) {
\r
276 for (String commandId : actions.getKeys()) {
\r
277 for (CommandAction a : actions.getValues(commandId)) {
\r
278 a.setEnabled(true);
\r
280 ComboContribution menu = menus.get(commandId);
\r
281 if (menu != null) {
\r
282 menu.setEnabled(true);
\r
285 updateActionBars(part);
\r
287 for (String commandId : actions.getKeys()) {
\r
288 for (CommandAction a : actions.getValues(commandId)) {
\r
289 a.setEnabled(false);
\r
291 ComboContribution menu = menus.get(commandId);
\r
293 menu.setEnabled(false);
\r
299 private void updateActionBars(IEditorPart part) {
\r
300 restoreActionStates();
\r
301 part.getEditorSite().getActionBars().updateActionBars();
\r
305 public void dispose() {
\r
306 if (DEBUG) System.out.println("ToolBarContributor.dispose()");
\r
307 setActiveEditor(null);
\r
310 for (IContributionItem item : items) {
\r
317 for (String commandId : actions.getKeys()) {
\r
318 for (CommandAction a : actions.getValues(commandId)) {
\r
319 a.getCommand().removeCommandListener(this);
\r
325 // without this the contributed toolbar widgets would continue reserve the space even when they are destroyed.
\r
326 // TODO : how to make the toolbar fix its layout?
\r
327 // Note: Using REUSE flag alleviates the problem, since the widgets are not removed.
\r
328 coolBarManager.update(true);
\r
330 CommandStateRegistry.getInstance().removeListener(this);
\r
335 boolean settingState = false;
\r
336 private void storeRadioActionState(CommandRadioAction action, boolean checked) {
\r
337 if (activePart == null)
\r
339 settingState = true;
\r
340 stateRegistry.setEditorState(activePart, action.getCommandId(), action.getValue());
\r
341 settingState = false;
\r
344 private void storeToggleActionState(CommandAction action, boolean checked) {
\r
345 if (activePart == null)
\r
347 settingState = true;
\r
348 stateRegistry.setEditorState(activePart, action.getCommandId(), checked);
\r
349 settingState = false;
\r
354 private void restoreActionStates() {
\r
355 if (activePart == null)
\r
358 Map<String,Boolean> defaultToggleStates = stateRegistry.getDefaultToggleStates();
\r
359 for (String commandId : defaultToggleStates.keySet()) {
\r
360 for (CommandAction a : actions.getValues(commandId)) {
\r
361 a.setChecked(defaultToggleStates.get(commandId));
\r
364 Map<String,Boolean> editorStates = stateRegistry.getEditorToggleStates(activePart);//toggleStates.get(activePart);
\r
365 if (editorStates != null) {
\r
366 for (String commandId : editorStates.keySet()) {
\r
367 for (CommandAction a : actions.getValues(commandId)) {
\r
368 a.setChecked(editorStates.get(commandId));
\r
373 Map<String,String> defaultRadioStates = stateRegistry.getDefaultRadioStates();
\r
374 for (String commandId : defaultRadioStates.keySet()) {
\r
375 String defaultValue = defaultRadioStates.get(commandId);
\r
376 for (CommandAction a : actions.getValues(commandId)) {
\r
377 CommandRadioAction r = (CommandRadioAction)a;
\r
378 r.setChecked(r.getValue().equals(defaultValue));
\r
382 Map<String,String> editorRadioStates = stateRegistry.getEditorRadioStates(activePart);//radioStates.get(activePart);
\r
383 if (editorRadioStates != null) {
\r
384 for (String commandId : editorRadioStates.keySet()) {
\r
385 String defaultValue = editorRadioStates.get(commandId);
\r
386 for (CommandAction a : actions.getValues(commandId)) {
\r
387 CommandRadioAction r = (CommandRadioAction)a;
\r
388 r.setChecked(r.getValue().equals(defaultValue));
\r
393 for (ComboContribution c : menus.values()) {
\r
394 c.updateSelection();
\r
400 public void partActivated(IWorkbenchPart part) {
\r
405 public void partBroughtToTop(IWorkbenchPart part) {
\r
410 public void partClosed(IWorkbenchPart part) {
\r
411 parts.remove(part);
\r
412 stateRegistry.clearStates(part);
\r
413 part.getSite().getPage().removePartListener(this);
\r
414 if (parts.size() == 0) {
\r
417 if (part instanceof IEditorPart)
\r
418 ((IEditorPart)part).getEditorSite().getActionBars().updateActionBars();
\r
423 public void partDeactivated(IWorkbenchPart part) {
\r
428 public void partOpened(IWorkbenchPart part) {
\r
432 private boolean getToggleState(Command command) {
\r
433 State toggleState = command.getState(RegistryToggleState.STATE_ID);
\r
434 return (Boolean)toggleState.getValue();
\r
437 private abstract class CommandAction extends Action {
\r
438 private Command command;
\r
440 public CommandAction(Command command, String name, ImageDescriptor image, int style) {
\r
442 this.command = command;
\r
444 setImageDescriptor(image);
\r
448 public void run() {
\r
450 handlerService.executeCommand(command.getId(), null);
\r
451 } catch (Exception e) {
\r
452 e.printStackTrace();
\r
456 public Command getCommand() {
\r
460 public String getCommandId() {
\r
461 return command.getId();
\r
466 public boolean equals(Object obj) {
\r
467 if (obj.getClass() != getClass())
\r
469 CommandAction other= (CommandAction)obj;
\r
470 if (!other.getCommandId().equals(getCommandId()))
\r
477 public int hashCode() {
\r
478 return command.getId().hashCode();
\r
482 private class CommandCheckboxAction extends CommandAction {
\r
484 public CommandCheckboxAction(Command command, String name, ImageDescriptor image) {
\r
485 super(command,name,image,Action.AS_CHECK_BOX);
\r
490 public void run() {
\r
491 boolean checked = isChecked();
\r
492 storeToggleActionState(this, checked);
\r
494 if (checked == getToggleState(getCommand()))
\r
495 HandlerUtil.toggleCommandState(getCommand());
\r
496 } catch (ExecutionException e) {
\r
497 e.printStackTrace();
\r
504 private class CommandRadioAction extends CommandAction {
\r
506 private String value;
\r
508 public CommandRadioAction(Command command, String name, String value, ImageDescriptor image) {
\r
509 super(command,name,image,Action.AS_RADIO_BUTTON);
\r
510 this.value = value;
\r
514 public void run() {
\r
515 boolean checked = isChecked();
\r
516 storeRadioActionState(this, checked);
\r
518 HandlerUtil.updateRadioState(getCommand(), value);
\r
519 } catch (ExecutionException e) {
\r
520 e.printStackTrace();
\r
526 public String getValue() {
\r
531 public boolean equals(Object obj) {
\r
532 if (obj.getClass() != getClass())
\r
534 CommandRadioAction other= (CommandRadioAction)obj;
\r
535 if (!other.getCommandId().equals(getCommandId()))
\r
537 if (!other.value.equals(value))
\r
544 private class CommandPushAction extends CommandAction {
\r
546 public CommandPushAction(Command command, String name, ImageDescriptor image) {
\r
547 super(command,name,image,Action.AS_PUSH_BUTTON);
\r
552 private class ComboContribution extends WorkbenchWindowControlContribution {
\r
553 private TableCombo combo;
\r
555 private List<Action> actions = new ArrayList<Action>();
\r
558 protected Control createControl(Composite parent) {
\r
559 Composite container = new Composite(parent, SWT.NONE);
\r
560 GridLayout glContainer = new GridLayout(1, false);
\r
561 glContainer.marginTop = 0;
\r
562 glContainer.marginHeight = 0;
\r
563 glContainer.marginWidth = 0;
\r
564 container.setLayout(glContainer);
\r
565 GridData glReader = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1);
\r
566 combo = new TableCombo(container, SWT.BORDER | SWT.READ_ONLY);
\r
567 combo.setLayoutData(glReader);
\r
569 for (Action a : actions) {
\r
570 TableItem item = new TableItem(combo.getTable(), SWT.NONE);
\r
571 item.setText(a.getText());
\r
572 if (a.getImageDescriptor() != null)
\r
573 item.setImage(a.getImageDescriptor().createImage());
\r
576 combo.addSelectionListener(new SelectionListener() {
\r
579 public void widgetSelected(SelectionEvent e) {
\r
580 int index = combo.getSelectionIndex();
\r
583 actions.get(index).run();
\r
587 public void widgetDefaultSelected(SelectionEvent e) {
\r
595 public boolean addAction(Action a) {
\r
596 // old action must be replaced. (otherwise reused ComboContributor would use different instances to ToolBarContributor.)
\r
603 void updateSelection() {
\r
606 for (int i = 0; i < actions.size(); i++) {
\r
607 if (actions.get(i).isChecked()) {
\r
614 public void setEnabled(boolean enabled) {
\r
616 combo.setEnabled(enabled);
\r