+package org.simantics.ui.toolbar;\r
+\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+\r
+import org.eclipse.core.commands.Command;\r
+import org.eclipse.core.commands.CommandEvent;\r
+import org.eclipse.core.commands.ExecutionException;\r
+import org.eclipse.core.commands.ICommandListener;\r
+import org.eclipse.core.commands.State;\r
+import org.eclipse.core.runtime.CoreException;\r
+import org.eclipse.core.runtime.IConfigurationElement;\r
+import org.eclipse.core.runtime.IExecutableExtension;\r
+import org.eclipse.jface.action.Action;\r
+import org.eclipse.jface.action.ActionContributionItem;\r
+import org.eclipse.jface.action.IContributionItem;\r
+import org.eclipse.jface.action.ICoolBarManager;\r
+import org.eclipse.jface.action.IToolBarManager;\r
+import org.eclipse.jface.action.ToolBarContributionItem;\r
+import org.eclipse.jface.resource.ImageDescriptor;\r
+import org.eclipse.nebula.widgets.tablecombo.TableCombo;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.events.SelectionEvent;\r
+import org.eclipse.swt.events.SelectionListener;\r
+import org.eclipse.swt.layout.GridData;\r
+import org.eclipse.swt.layout.GridLayout;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Control;\r
+import org.eclipse.swt.widgets.TableItem;\r
+import org.eclipse.ui.IEditorPart;\r
+import org.eclipse.ui.IPartListener;\r
+import org.eclipse.ui.IWorkbenchPart;\r
+import org.eclipse.ui.PlatformUI;\r
+import org.eclipse.ui.commands.ICommandService;\r
+import org.eclipse.ui.handlers.HandlerUtil;\r
+import org.eclipse.ui.handlers.IHandlerService;\r
+import org.eclipse.ui.handlers.RadioState;\r
+import org.eclipse.ui.handlers.RegistryToggleState;\r
+import org.eclipse.ui.menus.WorkbenchWindowControlContribution;\r
+import org.eclipse.ui.part.EditorActionBarContributor;\r
+\r
+import org.simantics.db.common.utils.Logger;\r
+import org.simantics.ui.internal.Activator;\r
+import org.simantics.ui.toolbar.ToolBarCommandRegistry.ToolbarCommandExtension;\r
+import org.simantics.utils.datastructures.MapList;\r
+\r
+\r
+/**\r
+ * EditorBarContributor, which tracks toggle states separately for each command.\r
+ * \r
+ * @see org.simantics.g3d.toolbarCommand Extension Point\r
+ * \r
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>\r
+ * \r
+ * \r
+ * \r
+ * TODO : configuring the position of buttons. \r
+ *\r
+ */\r
+public class ToolbarContributor extends EditorActionBarContributor implements ICommandListener, IPartListener, CommandStateListener, IExecutableExtension {\r
+\r
+ private static boolean DEBUG = false; // Print debug messages to console\r
+ private static boolean REUSE = true; // true: Reuse contribution items / false: delete items on dispose \r
+ \r
+ private static final String PLATFORM = "platform:/plugin/";\r
+\r
+ private String toolbarId;\r
+ \r
+ private IEditorPart activePart;\r
+ private Set<IEditorPart> parts = new HashSet<IEditorPart>();\r
+ IToolBarManager mgr;\r
+ \r
+ \r
+ ICommandService service;\r
+ IHandlerService handlerService;\r
+ List<IContributionItem> items = new ArrayList<IContributionItem>();\r
+ MapList<String, CommandAction> actions = new MapList<String, ToolbarContributor.CommandAction>();\r
+ \r
+ CommandStateRegistry stateRegistry;\r
+ \r
+ private Map<String,ComboContribution> menus = new HashMap<String, ComboContribution>();\r
+ \r
+ public ToolbarContributor() {\r
+ service = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class); \r
+ handlerService = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);\r
+ stateRegistry = CommandStateRegistry.getInstance();\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public void setInitializationData(IConfigurationElement config,String propertyName, Object data) throws CoreException {\r
+ if (data instanceof String) {\r
+ String[] parameters = ((String) data).split(";");\r
+ for (String parameter : parameters) {\r
+ String[] keyValue = parameter.split("=");\r
+ if (keyValue.length > 2) {\r
+ //ErrorLogger.defaultLogWarning("Invalid parameter '" + parameter + ". Complete view argument: " + data, null);\r
+ continue;\r
+ }\r
+ String key = keyValue[0];\r
+ String value = keyValue.length > 1 ? keyValue[1] : "";\r
+\r
+ if ("toolbar".equals(key)) {\r
+ toolbarId = value;\r
+ } \r
+ }\r
+ }\r
+ \r
+ }\r
+ \r
+ public String getToolbarId() {\r
+ return toolbarId;\r
+ }\r
+ \r
+ \r
+ private ICoolBarManager coolBarManager;\r
+ private IContributionItem toolBar;\r
+ public void contributeToCoolBar(ICoolBarManager coolBarManager) {\r
+ this.coolBarManager = coolBarManager;\r
+ toolBar = coolBarManager.find(getToolbarId());\r
+ \r
+ if (toolBar instanceof ToolBarContributionItem)\r
+ mgr = ((ToolBarContributionItem) toolBar).getToolBarManager();\r
+ if (mgr == null)\r
+ return;\r
+ \r
+ createCommands();\r
+ \r
+ mgr.markDirty();\r
+ \r
+ }\r
+ \r
+ private void createCommands() {\r
+ for (ToolbarCommandExtension ext : ToolBarCommandRegistry.getInstance().getExtensions(getToolbarId())) {\r
+ addCommand(ext);\r
+ }\r
+ }\r
+ \r
+ private void addCommand(ToolbarCommandExtension ext) {\r
+ if (DEBUG) System.out.println("Adding command to toolbar " +getToolbarId() + " " + ext);\r
+ String commandId = ext.commandId;\r
+ Command command = service.getCommand(commandId);\r
+ \r
+ String type = ext.type;\r
+ \r
+ State toggleState = command.getState(RegistryToggleState.STATE_ID);\r
+ State radioState = command.getState(RadioState.STATE_ID);\r
+ \r
+ String name = ext.name;\r
+ \r
+ \r
+ ImageDescriptor image = getImage(ext);\r
+ \r
+ CommandAction a = null;\r
+ if (type.equals("toggle") && toggleState != null) {\r
+ a = new CommandCheckboxAction(command,name,image);\r
+\r
+ stateRegistry.storeDefaultState(commandId);\r
+ } else if (radioState != null && ext.value != null) {\r
+ stateRegistry.storeDefaultState(commandId);\r
+ if (type.equals("radio")) {\r
+ a = new CommandRadioAction(command,name,ext.value,image);\r
+ } else if (type.equals("combo")) {\r
+ a = new CommandRadioAction(command,name,ext.value,image);\r
+ ComboContribution combo = menus.get(commandId);\r
+ if (REUSE && combo == null) {\r
+ combo = (ComboContribution)mgr.find(commandId);\r
+ if (combo != null) {\r
+ menus.put(commandId, combo);\r
+ items.add(combo);\r
+ a.getCommand().addCommandListener(this);\r
+ CommandStateRegistry.getInstance().addListener(commandId, this);\r
+ }\r
+ }\r
+ if (combo == null) {\r
+ combo = new ComboContribution();\r
+ combo.setId(commandId);\r
+ menus.put(commandId, combo);\r
+ items.add(combo);\r
+ mgr.add(combo);\r
+ a.getCommand().addCommandListener(this);\r
+ CommandStateRegistry.getInstance().addListener(commandId, this);\r
+ }\r
+ actions.add(commandId,a);\r
+ combo.addAction(a);\r
+ return;\r
+ }\r
+ } else if (type.equals("push")) {\r
+ a = new CommandPushAction(command,name,image);\r
+ } else {\r
+ if (DEBUG) System.out.println(ext + " is not valid.");\r
+ Logger.defaultLogError(ext + " is not valid.");\r
+ return;\r
+ }\r
+ IContributionItem item = null;\r
+ if (REUSE) {\r
+ String id = commandId;\r
+ if (ext.value != null)\r
+ id += "."+ext.value;\r
+ item = mgr.find(id);\r
+ if (item == null) {\r
+ item = new ActionContributionItem(a);\r
+ ((ActionContributionItem)item).setId(id);\r
+ } else {\r
+ if (DEBUG) System.out.println("Reusing " + ext);\r
+ a = (CommandAction)((ActionContributionItem)item).getAction();\r
+ }\r
+ } else {\r
+ item = new ActionContributionItem(a);\r
+ }\r
+ a.getCommand().addCommandListener(this);\r
+ actions.add(commandId,a);\r
+ items.add(item);\r
+ mgr.add(item);\r
+ CommandStateRegistry.getInstance().addListener(commandId, this);\r
+ }\r
+ \r
+ private ImageDescriptor getImage(ToolbarCommandExtension ext) {\r
+ ImageDescriptor image = null;\r
+ if (ext.image != null) {\r
+ String plugin = null;\r
+ String file = null;\r
+ if (ext.image.startsWith(PLATFORM)) {\r
+ String s = ext.image.substring(PLATFORM.length());\r
+ int i = s.indexOf("/");\r
+ plugin = s.substring(0,i);\r
+ file = s.substring(i+1);\r
+ } else {\r
+ plugin = ext.contributorId;\r
+ file = ext.image;\r
+ }\r
+ image = Activator.imageDescriptorFromPlugin(plugin, file);\r
+ }\r
+ return image;\r
+ }\r
+ \r
+\r
+ \r
+ @Override\r
+ public void commandChanged(CommandEvent commandEvent) {\r
+ if (commandEvent.isHandledChanged()||commandEvent.isEnabledChanged()) {\r
+ Command command = commandEvent.getCommand();\r
+ String commandId = command.getId();\r
+ for (CommandAction a : actions.getValues(commandId)) {\r
+ a.setEnabled(command.isHandled() && command.isEnabled());\r
+ }\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public void setActiveEditor(IEditorPart targetEditor) {\r
+ if (targetEditor == activePart)\r
+ return;\r
+ setContext(targetEditor);\r
+ }\r
+ \r
+ @Override\r
+ public void stateChanged(IWorkbenchPart part, String commandId, String state) {\r
+ // TODO : update only given command\r
+ if (settingState)\r
+ return;\r
+ if (part instanceof IEditorPart)\r
+ setContext((IEditorPart)part);\r
+ }\r
+ \r
+ private void setContext(IEditorPart part) {\r
+ this.activePart = part;\r
+ if (activePart != null && !parts.contains(activePart)) {\r
+ activePart.getSite().getPage().addPartListener(this);\r
+ }\r
+ if (part != null) {\r
+ for (String commandId : actions.getKeys()) {\r
+ for (CommandAction a : actions.getValues(commandId)) {\r
+ a.setEnabled(true);\r
+ }\r
+ ComboContribution menu = menus.get(commandId);\r
+ if (menu != null) {\r
+ menu.setEnabled(true);\r
+ }\r
+ }\r
+ updateActionBars(part);\r
+ } else {\r
+ for (String commandId : actions.getKeys()) {\r
+ for (CommandAction a : actions.getValues(commandId)) {\r
+ a.setEnabled(false);\r
+ }\r
+ ComboContribution menu = menus.get(commandId);\r
+ if (menu != null) \r
+ menu.setEnabled(false);\r
+ \r
+ }\r
+ }\r
+ }\r
+\r
+ private void updateActionBars(IEditorPart part) {\r
+ restoreActionStates();\r
+ part.getEditorSite().getActionBars().updateActionBars();\r
+ }\r
+ \r
+ @Override\r
+ public void dispose() {\r
+ if (DEBUG) System.out.println("ToolBarContributor.dispose()");\r
+ setActiveEditor(null);\r
+ if (mgr != null) {\r
+ if (!REUSE) {\r
+ for (IContributionItem item : items) {\r
+ mgr.remove(item);\r
+ item.dispose();\r
+ }\r
+ }\r
+ items.clear();\r
+ \r
+ for (String commandId : actions.getKeys()) {\r
+ for (CommandAction a : actions.getValues(commandId)) {\r
+ a.getCommand().removeCommandListener(this);\r
+ }\r
+ \r
+ }\r
+ actions.clear();\r
+ \r
+ // without this the contributed toolbar widgets would continue reserve the space even when they are destroyed.\r
+ // TODO : how to make the toolbar fix its layout?\r
+ // Note: Using REUSE flag alleviates the problem, since the widgets are not removed.\r
+ coolBarManager.update(true);\r
+ }\r
+ CommandStateRegistry.getInstance().removeListener(this);\r
+ super.dispose();\r
+ activePart = null;\r
+ }\r
+ \r
+ boolean settingState = false;\r
+ private void storeRadioActionState(CommandRadioAction action, boolean checked) {\r
+ if (activePart == null)\r
+ return;\r
+ settingState = true;\r
+ stateRegistry.setEditorState(activePart, action.getCommandId(), action.getValue());\r
+ settingState = false;\r
+ }\r
+ \r
+ private void storeToggleActionState(CommandAction action, boolean checked) {\r
+ if (activePart == null)\r
+ return;\r
+ settingState = true;\r
+ stateRegistry.setEditorState(activePart, action.getCommandId(), checked);\r
+ settingState = false;\r
+ }\r
+ \r
+ \r
+ \r
+ private void restoreActionStates() {\r
+ if (activePart == null)\r
+ return;\r
+ // toggles\r
+ Map<String,Boolean> defaultToggleStates = stateRegistry.getDefaultToggleStates();\r
+ for (String commandId : defaultToggleStates.keySet()) {\r
+ for (CommandAction a : actions.getValues(commandId)) {\r
+ a.setChecked(defaultToggleStates.get(commandId));\r
+ }\r
+ }\r
+ Map<String,Boolean> editorStates = stateRegistry.getEditorToggleStates(activePart);//toggleStates.get(activePart);\r
+ if (editorStates != null) {\r
+ for (String commandId : editorStates.keySet()) {\r
+ for (CommandAction a : actions.getValues(commandId)) {\r
+ a.setChecked(editorStates.get(commandId));\r
+ }\r
+ }\r
+ }\r
+ // radios\r
+ Map<String,String> defaultRadioStates = stateRegistry.getDefaultRadioStates();\r
+ for (String commandId : defaultRadioStates.keySet()) {\r
+ String defaultValue = defaultRadioStates.get(commandId);\r
+ for (CommandAction a : actions.getValues(commandId)) {\r
+ CommandRadioAction r = (CommandRadioAction)a;\r
+ r.setChecked(r.getValue().equals(defaultValue));\r
+ }\r
+ }\r
+ \r
+ Map<String,String> editorRadioStates = stateRegistry.getEditorRadioStates(activePart);//radioStates.get(activePart);\r
+ if (editorRadioStates != null) {\r
+ for (String commandId : editorRadioStates.keySet()) {\r
+ String defaultValue = editorRadioStates.get(commandId);\r
+ for (CommandAction a : actions.getValues(commandId)) {\r
+ CommandRadioAction r = (CommandRadioAction)a;\r
+ r.setChecked(r.getValue().equals(defaultValue));\r
+ }\r
+ }\r
+ }\r
+ \r
+ for (ComboContribution c : menus.values()) {\r
+ c.updateSelection();\r
+ }\r
+\r
+ }\r
+ \r
+ @Override\r
+ public void partActivated(IWorkbenchPart part) {\r
+\r
+ }\r
+ \r
+ @Override\r
+ public void partBroughtToTop(IWorkbenchPart part) {\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public void partClosed(IWorkbenchPart part) {\r
+ parts.remove(part);\r
+ stateRegistry.clearStates(part);\r
+ part.getSite().getPage().removePartListener(this);\r
+ if (parts.size() == 0) {\r
+ \r
+ }\r
+ if (part instanceof IEditorPart)\r
+ ((IEditorPart)part).getEditorSite().getActionBars().updateActionBars();\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public void partDeactivated(IWorkbenchPart part) {\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public void partOpened(IWorkbenchPart part) {\r
+ \r
+ }\r
+ \r
+ private boolean getToggleState(Command command) {\r
+ State toggleState = command.getState(RegistryToggleState.STATE_ID);\r
+ return (Boolean)toggleState.getValue();\r
+ }\r
+ \r
+ private abstract class CommandAction extends Action {\r
+ private Command command;\r
+ \r
+ public CommandAction(Command command, String name, ImageDescriptor image, int style) {\r
+ super(name,style);\r
+ this.command = command;\r
+ if (image != null)\r
+ setImageDescriptor(image);\r
+ }\r
+ \r
+ @Override\r
+ public void run() {\r
+ try {\r
+ handlerService.executeCommand(command.getId(), null);\r
+ } catch (Exception e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ \r
+ public Command getCommand() {\r
+ return command;\r
+ }\r
+ \r
+ public String getCommandId() {\r
+ return command.getId();\r
+ }\r
+ \r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (obj.getClass() != getClass())\r
+ return false;\r
+ CommandAction other= (CommandAction)obj;\r
+ if (!other.getCommandId().equals(getCommandId()))\r
+ return false;\r
+ return true;\r
+ }\r
+\r
+ \r
+ @Override\r
+ public int hashCode() {\r
+ return command.getId().hashCode();\r
+ }\r
+ }\r
+ \r
+ private class CommandCheckboxAction extends CommandAction {\r
+ \r
+ public CommandCheckboxAction(Command command, String name, ImageDescriptor image) {\r
+ super(command,name,image,Action.AS_CHECK_BOX);\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public void run() {\r
+ boolean checked = isChecked(); \r
+ storeToggleActionState(this, checked);\r
+ try {\r
+ if (checked == getToggleState(getCommand()))\r
+ HandlerUtil.toggleCommandState(getCommand());\r
+ } catch (ExecutionException e) {\r
+ e.printStackTrace();\r
+ }\r
+ super.run();\r
+ }\r
+ \r
+ }\r
+ \r
+ private class CommandRadioAction extends CommandAction {\r
+\r
+ private String value;\r
+ \r
+ public CommandRadioAction(Command command, String name, String value, ImageDescriptor image) {\r
+ super(command,name,image,Action.AS_RADIO_BUTTON);\r
+ this.value = value;\r
+ }\r
+ \r
+ @Override\r
+ public void run() {\r
+ boolean checked = isChecked(); \r
+ storeRadioActionState(this, checked);\r
+ try {\r
+ HandlerUtil.updateRadioState(getCommand(), value);\r
+ } catch (ExecutionException e) {\r
+ e.printStackTrace();\r
+ return;\r
+ }\r
+ super.run();\r
+ }\r
+ \r
+ public String getValue() {\r
+ return value;\r
+ }\r
+ \r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (obj.getClass() != getClass())\r
+ return false;\r
+ CommandRadioAction other= (CommandRadioAction)obj;\r
+ if (!other.getCommandId().equals(getCommandId()))\r
+ return false;\r
+ if (!other.value.equals(value))\r
+ return false;\r
+ return true;\r
+ }\r
+ \r
+ }\r
+\r
+ private class CommandPushAction extends CommandAction {\r
+ \r
+ public CommandPushAction(Command command, String name, ImageDescriptor image) {\r
+ super(command,name,image,Action.AS_PUSH_BUTTON);\r
+ }\r
+\r
+ }\r
+ \r
+ private class ComboContribution extends WorkbenchWindowControlContribution {\r
+ private TableCombo combo;\r
+ \r
+ private List<Action> actions = new ArrayList<Action>();\r
+ \r
+ @Override\r
+ protected Control createControl(Composite parent) {\r
+ Composite container = new Composite(parent, SWT.NONE);\r
+ GridLayout glContainer = new GridLayout(1, false);\r
+ glContainer.marginTop = 0;\r
+ glContainer.marginHeight = 0;\r
+ glContainer.marginWidth = 0;\r
+ container.setLayout(glContainer);\r
+ GridData glReader = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1);\r
+ combo = new TableCombo(container, SWT.BORDER | SWT.READ_ONLY);\r
+ combo.setLayoutData(glReader);\r
+\r
+ for (Action a : actions) {\r
+ TableItem item = new TableItem(combo.getTable(), SWT.NONE);\r
+ item.setText(a.getText());\r
+ if (a.getImageDescriptor() != null)\r
+ item.setImage(a.getImageDescriptor().createImage());\r
+ }\r
+ \r
+ combo.addSelectionListener(new SelectionListener() {\r
+ \r
+ @Override\r
+ public void widgetSelected(SelectionEvent e) {\r
+ int index = combo.getSelectionIndex();\r
+ if (index == -1)\r
+ return;\r
+ actions.get(index).run();\r
+ }\r
+ \r
+ @Override\r
+ public void widgetDefaultSelected(SelectionEvent e) {\r
+ \r
+ }\r
+ });\r
+ updateSelection();\r
+ return container;\r
+ }\r
+ \r
+ public boolean addAction(Action a) {\r
+ // old action must be replaced. (otherwise reused ComboContributor would use different instances to ToolBarContributor.) \r
+ actions.remove(a);\r
+ actions.add(a);\r
+ return true;\r
+ \r
+ }\r
+ \r
+ void updateSelection() {\r
+ if (combo == null)\r
+ return;\r
+ for (int i = 0; i < actions.size(); i++) {\r
+ if (actions.get(i).isChecked()) {\r
+ combo.select(i);\r
+ return;\r
+ }\r
+ }\r
+ }\r
+ \r
+ public void setEnabled(boolean enabled) {\r
+ if (combo != null)\r
+ combo.setEnabled(enabled);\r
+ }\r
+ }\r
+ \r
+}\r