| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.apache.taverna.ui.menu.impl; |
| |
| import static java.lang.Math.min; |
| import static javax.help.CSH.setHelpIDString; |
| import static javax.swing.Action.NAME; |
| import static javax.swing.Action.SHORT_DESCRIPTION; |
| import static org.apache.taverna.lang.ui.ShadedLabel.GREEN; |
| import static org.apache.taverna.ui.menu.AbstractMenuSection.SECTION_COLOR; |
| import static org.apache.taverna.ui.menu.DefaultContextualMenu.DEFAULT_CONTEXT_MENU; |
| import static org.apache.taverna.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR; |
| import static org.apache.taverna.ui.menu.DefaultToolBar.DEFAULT_TOOL_BAR; |
| |
| import java.awt.Color; |
| import java.awt.Component; |
| import java.lang.ref.WeakReference; |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.WeakHashMap; |
| |
| import javax.swing.AbstractButton; |
| import javax.swing.Action; |
| import javax.swing.ButtonGroup; |
| import javax.swing.JButton; |
| import javax.swing.JCheckBoxMenuItem; |
| import javax.swing.JMenu; |
| import javax.swing.JMenuBar; |
| import javax.swing.JMenuItem; |
| import javax.swing.JPopupMenu; |
| import javax.swing.JRadioButtonMenuItem; |
| import javax.swing.JToggleButton; |
| import javax.swing.JToolBar; |
| import javax.swing.border.EmptyBorder; |
| |
| import org.apache.taverna.lang.observer.MultiCaster; |
| import org.apache.taverna.lang.observer.Observable; |
| import org.apache.taverna.lang.observer.Observer; |
| import org.apache.taverna.lang.observer.SwingAwareObserver; |
| import org.apache.taverna.lang.ui.ShadedLabel; |
| import org.apache.taverna.ui.menu.AbstractMenuAction; |
| import org.apache.taverna.ui.menu.AbstractMenuOptionGroup; |
| import org.apache.taverna.ui.menu.ContextualMenuComponent; |
| import org.apache.taverna.ui.menu.ContextualSelection; |
| import org.apache.taverna.ui.menu.DesignOnlyAction; |
| import org.apache.taverna.ui.menu.DesignOrResultsAction; |
| import org.apache.taverna.ui.menu.MenuComponent; |
| import org.apache.taverna.ui.menu.MenuComponent.MenuType; |
| import org.apache.taverna.ui.menu.MenuManager; |
| import org.apache.taverna.workbench.selection.SelectionManager; |
| import org.apache.taverna.workbench.selection.events.PerspectiveSelectionEvent; |
| import org.apache.taverna.workbench.selection.events.SelectionManagerEvent; |
| |
| import org.apache.log4j.Logger; |
| |
| /** |
| * Implementation of {@link MenuManager}. |
| * |
| * @author Stian Soiland-Reyes |
| */ |
| public class MenuManagerImpl implements MenuManager { |
| private static Logger logger = Logger.getLogger(MenuManagerImpl.class); |
| |
| private boolean needsUpdate; |
| /** |
| * Cache used by {@link #getURIByComponent(Component)} |
| */ |
| private WeakHashMap<Component, URI> componentToUri; |
| /** |
| * {@link MenuElementComparator} used for sorting menu components from the |
| * SPI registry. |
| */ |
| private MenuElementComparator menuElementComparator = new MenuElementComparator(); |
| /** |
| * Map of {@link URI} to it's discovered children. Populated by |
| * {@link #findChildren()}. |
| */ |
| private HashMap<URI, List<MenuComponent>> menuElementTree; |
| /** |
| * Multicaster to distribute messages to {@link Observer}s of this menu |
| * manager. |
| */ |
| private MultiCaster<MenuManagerEvent> multiCaster; |
| /** |
| * Lock for {@link #update()} |
| */ |
| private final Object updateLock = new Object(); |
| /** |
| * True if {@link #doUpdate()} is running, subsequents call to |
| * {@link #update()} will return immediately. |
| */ |
| private boolean updating; |
| /** |
| * Cache used by {@link #getComponentByURI(URI)} |
| */ |
| private Map<URI, WeakReference<Component>> uriToComponent; |
| /** |
| * Map from {@link URI} to defining {@link MenuComponent}. Children are in |
| * {@link #menuElementTree}. |
| */ |
| private Map<URI, MenuComponent> uriToMenuElement; |
| // Note: Not reset by #resetCollections() |
| private Map<URI, List<WeakReference<Component>>> uriToPublishedComponents = new HashMap<>(); |
| private List<MenuComponent> menuComponents = new ArrayList<>(); |
| |
| /** |
| * Construct the MenuManagerImpl. Observes the SPI registry and does an |
| * initial {@link #update()}. |
| */ |
| public MenuManagerImpl() { |
| multiCaster = new MultiCaster<>(this); |
| needsUpdate = true; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void addMenuItemsWithExpansion(List<JMenuItem> menuItems, |
| JMenu parentMenu, int maxItemsInMenu, |
| ComponentFactory headerItemFactory) { |
| if (menuItems.size() <= maxItemsInMenu) { |
| // Just add them directly |
| for (JMenuItem menuItem : menuItems) |
| parentMenu.add(menuItem); |
| return; |
| } |
| int index = 0; |
| while (index < menuItems.size()) { |
| int toIndex = min(menuItems.size(), index + maxItemsInMenu); |
| if (toIndex == menuItems.size() - 1) |
| // Don't leave a single item left for the last subMenu |
| toIndex--; |
| List<JMenuItem> subList = menuItems.subList(index, toIndex); |
| JMenuItem firstItem = subList.get(0); |
| JMenuItem lastItem = subList.get(subList.size() - 1); |
| JMenu subMenu = new JMenu(firstItem.getText() + " ... " |
| + lastItem.getText()); |
| if (headerItemFactory != null) |
| subMenu.add(headerItemFactory.makeComponent()); |
| for (JMenuItem menuItem : subList) |
| subMenu.add(menuItem); |
| parentMenu.add(subMenu); |
| index = toIndex; |
| } |
| } |
| |
| @Override |
| public void addObserver(Observer<MenuManagerEvent> observer) { |
| multiCaster.addObserver(observer); |
| } |
| |
| @Override |
| public JPopupMenu createContextMenu(Object parent, Object selection, |
| Component relativeToComponent) { |
| ContextualSelection contextualSelection = new ContextualSelection( |
| parent, selection, relativeToComponent); |
| JPopupMenu popupMenu = new JPopupMenu(); |
| populateContextMenu(popupMenu, DEFAULT_CONTEXT_MENU, |
| contextualSelection); |
| registerComponent(DEFAULT_CONTEXT_MENU, popupMenu, true); |
| return popupMenu; |
| } |
| |
| @Override |
| public JMenuBar createMenuBar() { |
| return createMenuBar(DEFAULT_MENU_BAR); |
| } |
| |
| @Override |
| public JMenuBar createMenuBar(URI id) { |
| JMenuBar menuBar = new JMenuBar(); |
| if (needsUpdate) |
| update(); |
| populateMenuBar(menuBar, id); |
| registerComponent(id, menuBar, true); |
| return menuBar; |
| } |
| |
| @Override |
| public JToolBar createToolBar() { |
| return createToolBar(DEFAULT_TOOL_BAR); |
| } |
| |
| @Override |
| public JToolBar createToolBar(URI id) { |
| JToolBar toolbar = new JToolBar(); |
| if (needsUpdate) |
| update(); |
| populateToolBar(toolbar, id); |
| registerComponent(id, toolbar, true); |
| return toolbar; |
| } |
| |
| @Override |
| public synchronized Component getComponentByURI(URI id) { |
| WeakReference<Component> componentRef = uriToComponent.get(id); |
| if (componentRef == null) |
| return null; |
| // Might also be null it reference has gone dead |
| return componentRef.get(); |
| } |
| |
| @Override |
| public List<Observer<MenuManagerEvent>> getObservers() { |
| return multiCaster.getObservers(); |
| } |
| |
| @Override |
| public synchronized URI getURIByComponent(Component component) { |
| return componentToUri.get(component); |
| } |
| |
| @Override |
| public void removeObserver(Observer<MenuManagerEvent> observer) { |
| multiCaster.removeObserver(observer); |
| } |
| |
| @Override |
| public void update() { |
| synchronized (updateLock) { |
| if (updating && !needsUpdate) |
| return; |
| updating = true; |
| } |
| try { |
| doUpdate(); |
| } finally { |
| synchronized (updateLock) { |
| updating = false; |
| needsUpdate = false; |
| } |
| } |
| } |
| |
| public void update(Object service, Map<?, ?> properties) { |
| needsUpdate = true; |
| update(); |
| } |
| |
| /** |
| * Add a {@link JMenu} to the list of components as described by the menu |
| * component. If there are no children, the menu is not added. |
| * |
| * @param components |
| * List of components where to add the created {@link JMenu} |
| * @param menuComponent |
| * The {@link MenuComponent} definition for this menu |
| * @param isToolbar |
| * True if the list of components is to be added to a toolbar |
| */ |
| private void addMenu(List<Component> components, |
| MenuComponent menuComponent, MenuOptions menuOptions) { |
| URI menuId = menuComponent.getId(); |
| if (menuOptions.isToolbar()) { |
| logger.warn("Can't have menu " + menuComponent |
| + " within toolBar element"); |
| return; |
| } |
| MenuOptions childOptions = new MenuOptions(menuOptions); |
| List<Component> subComponents = makeComponents(menuId, childOptions); |
| if (subComponents.isEmpty()) { |
| logger.warn("No sub components found for menu " + menuId); |
| return; |
| } |
| |
| JMenu menu = new JMenu(menuComponent.getAction()); |
| for (Component menuItem : subComponents) |
| if (menuItem == null) |
| menu.addSeparator(); |
| else |
| menu.add(menuItem); |
| registerComponent(menuId, menu); |
| components.add(menu); |
| } |
| |
| /** |
| * Add <code>null</code> to the list of components, meaning that a separator |
| * is to be created. Subsequent separators are ignored, and if there are no |
| * components on the list already no separator will be added. |
| * |
| * @param components |
| * List of components |
| */ |
| private void addNullSeparator(List<Component> components) { |
| if (components.isEmpty()) |
| // Don't start with a separator |
| return; |
| if (components.get(components.size() - 1) == null) |
| // Already a separator in last position |
| return; |
| components.add(null); |
| } |
| |
| /** |
| * Add an {@link AbstractMenuOptionGroup option group} to the list of |
| * components |
| * |
| * @param components |
| * List of components where to add the created {@link JMenu} |
| * @param optionGroupId |
| * The {@link URI} identifying the option group |
| * @param isToolbar |
| * True if the option group is to be added to a toolbar |
| */ |
| private void addOptionGroup(List<Component> components, URI optionGroupId, |
| MenuOptions menuOptions) { |
| MenuOptions childOptions = new MenuOptions(menuOptions); |
| childOptions.setOptionGroup(true); |
| |
| List<Component> buttons = makeComponents(optionGroupId, childOptions); |
| addNullSeparator(components); |
| if (buttons.isEmpty()) { |
| logger.warn("No sub components found for option group " |
| + optionGroupId); |
| return; |
| } |
| ButtonGroup buttonGroup = new ButtonGroup(); |
| |
| for (Component button : buttons) { |
| if (button instanceof AbstractButton) |
| buttonGroup.add((AbstractButton) button); |
| else |
| logger.warn("Component of button group " + optionGroupId |
| + " is not an AbstractButton: " + button); |
| if (button == null) { |
| logger.warn("Separator found within button group"); |
| addNullSeparator(components); |
| } else |
| components.add(button); |
| } |
| addNullSeparator(components); |
| } |
| |
| /** |
| * Add a section to a list of components. |
| * |
| * @param components |
| * List of components |
| * @param sectionId |
| * The {@link URI} identifying the section |
| * @param menuOptions |
| * {@link MenuOptions options} for creating the menu |
| */ |
| private void addSection(List<Component> components, URI sectionId, |
| MenuOptions menuOptions) { |
| List<Component> childComponents = makeComponents(sectionId, menuOptions); |
| |
| MenuComponent sectionDef = uriToMenuElement.get(sectionId); |
| addNullSeparator(components); |
| if (childComponents.isEmpty()) { |
| logger.warn("No sub components found for section " + sectionId); |
| return; |
| } |
| Action sectionAction = sectionDef.getAction(); |
| if (sectionAction != null) { |
| String sectionLabel = (String) sectionAction.getValue(NAME); |
| if (sectionLabel != null) { |
| // No separators before the label |
| stripTrailingNullSeparator(components); |
| Color labelColor = (Color) sectionAction.getValue(SECTION_COLOR); |
| if (labelColor == null) |
| labelColor = GREEN; |
| ShadedLabel label = new ShadedLabel(sectionLabel, labelColor); |
| components.add(label); |
| } |
| } |
| for (Component childComponent : childComponents) |
| if (childComponent == null) { |
| logger.warn("Separator found within section " + sectionId); |
| addNullSeparator(components); |
| } else |
| components.add(childComponent); |
| addNullSeparator(components); |
| } |
| |
| /** |
| * Remove the last <code>null</code> separator from the list of components |
| * if it's present. |
| * |
| * @param components |
| * List of components |
| */ |
| private void stripTrailingNullSeparator(List<Component> components) { |
| if (!components.isEmpty()) { |
| int lastIndex = components.size() - 1; |
| if (components.get(lastIndex) == null) |
| components.remove(lastIndex); |
| } |
| } |
| |
| /** |
| * Perform the actual update, called by {@link #update()}. Reset all the |
| * collections, refresh from SPI, modify any previously published components |
| * and notify any observers. |
| */ |
| protected synchronized void doUpdate() { |
| resetCollections(); |
| findChildren(); |
| updatePublishedComponents(); |
| multiCaster.notify(new UpdatedMenuManagerEvent()); |
| } |
| |
| /** |
| * Find all children for all known menu components. Populates |
| * {@link #uriToMenuElement}. |
| * |
| */ |
| protected void findChildren() { |
| for (MenuComponent menuElement : menuComponents) { |
| uriToMenuElement.put(menuElement.getId(), menuElement); |
| logger.debug("Found menu element " + menuElement.getId() + " " |
| + menuElement); |
| if (menuElement.getParentId() == null) |
| continue; |
| List<MenuComponent> siblings = menuElementTree.get(menuElement |
| .getParentId()); |
| if (siblings == null) { |
| siblings = new ArrayList<>(); |
| synchronized (menuElementTree) { |
| menuElementTree.put(menuElement.getParentId(), siblings); |
| } |
| } |
| siblings.add(menuElement); |
| } |
| // if (uriToMenuElement.isEmpty()) { |
| // logger.error("No menu elements found, check classpath/Raven/SPI"); |
| // } |
| } |
| |
| /** |
| * Get the children which have the given URI specified as their parent, or |
| * an empty list if no children exist. |
| * |
| * @param id |
| * The {@link URI} of the parent |
| * @return The {@link List} of {@link MenuComponent} which have the given |
| * parent |
| */ |
| protected List<MenuComponent> getChildren(URI id) { |
| List<MenuComponent> children = null; |
| synchronized (menuElementTree) { |
| children = menuElementTree.get(id); |
| if (children != null) |
| children = new ArrayList<>(children); |
| } |
| if (children == null) |
| children = Collections.<MenuComponent> emptyList(); |
| else |
| Collections.sort(children, menuElementComparator); |
| return children; |
| } |
| |
| /** |
| * Make the list of Swing {@link Component}s that are the children of the |
| * given {@link URI}. |
| * |
| * @param id |
| * The {@link URI} of the parent which children are to be made |
| * @param menuOptions |
| * Options of the created menu, for instance |
| * {@link MenuOptions#isToolbar()}. |
| * @return A {@link List} of {@link Component}s that can be added to a |
| * {@link JMenuBar}, {@link JMenu} or {@link JToolBar}. |
| */ |
| protected List<Component> makeComponents(URI id, MenuOptions menuOptions) { |
| List<Component> components = new ArrayList<>(); |
| for (MenuComponent childElement : getChildren(id)) { |
| if (childElement instanceof ContextualMenuComponent) |
| ((ContextualMenuComponent) childElement) |
| .setContextualSelection(menuOptions |
| .getContextualSelection()); |
| /* |
| * Important - check this AFTER setContextualSelection so the item |
| * can change it's enabled-state if needed. |
| */ |
| if (!childElement.isEnabled()) |
| continue; |
| MenuType type = childElement.getType(); |
| Action action = childElement.getAction(); |
| URI childId = childElement.getId(); |
| if (type.equals(MenuType.action)) { |
| if (action == null) { |
| logger.warn("Skipping invalid action " + childId + " for " |
| + id); |
| continue; |
| } |
| |
| Component actionComponent; |
| if (menuOptions.isOptionGroup()) { |
| if (menuOptions.isToolbar()) { |
| actionComponent = new JToggleButton(action); |
| toolbarizeButton((AbstractButton) actionComponent); |
| } else |
| actionComponent = new JRadioButtonMenuItem(action); |
| } else { |
| if (menuOptions.isToolbar()) { |
| actionComponent = new JButton(action); |
| toolbarizeButton((AbstractButton) actionComponent); |
| } else |
| actionComponent = new JMenuItem(action); |
| } |
| registerComponent(childId, actionComponent); |
| components.add(actionComponent); |
| } else if (type.equals(MenuType.toggle)) { |
| if (action == null) { |
| logger.warn("Skipping invalid toggle " + childId + " for " |
| + id); |
| continue; |
| } |
| Component toggleComponent; |
| if (menuOptions.isToolbar()) |
| toggleComponent = new JToggleButton(action); |
| else |
| toggleComponent = new JCheckBoxMenuItem(action); |
| registerComponent(childId, toggleComponent); |
| components.add(toggleComponent); |
| } else if (type.equals(MenuType.custom)) { |
| Component customComponent = childElement.getCustomComponent(); |
| if (customComponent == null) { |
| logger.warn("Skipping null custom component " + childId |
| + " for " + id); |
| continue; |
| } |
| registerComponent(childId, customComponent); |
| components.add(customComponent); |
| } else if (type.equals(MenuType.optionGroup)) |
| addOptionGroup(components, childId, menuOptions); |
| else if (type.equals(MenuType.section)) |
| addSection(components, childId, menuOptions); |
| else if (type.equals(MenuType.menu)) |
| addMenu(components, childElement, menuOptions); |
| else { |
| logger.warn("Skipping invalid/unknown type " + type + " for " |
| + id); |
| continue; |
| } |
| } |
| stripTrailingNullSeparator(components); |
| return components; |
| } |
| |
| /** |
| * Fill the specified menu bar with the menu elements that have the given |
| * URI as their parent. |
| * <p> |
| * Existing elements on the menu bar will be removed. |
| * |
| * @param menuBar |
| * The {@link JMenuBar} to update |
| * @param id |
| * The {@link URI} of the menu bar |
| */ |
| protected void populateMenuBar(JMenuBar menuBar, URI id) { |
| menuBar.removeAll(); |
| MenuComponent menuDef = uriToMenuElement.get(id); |
| if (menuDef == null) |
| throw new IllegalArgumentException("Unknown menuBar " + id); |
| if (!menuDef.getType().equals(MenuType.menu)) |
| throw new IllegalArgumentException("Element " + id |
| + " is not a menu, but a " + menuDef.getType()); |
| MenuOptions menuOptions = new MenuOptions(); |
| for (Component component : makeComponents(id, menuOptions)) |
| if (component == null) |
| logger.warn("Ignoring separator in menu bar " + id); |
| else |
| menuBar.add(component); |
| } |
| |
| /** |
| * Fill the specified menu bar with the menu elements that have the given |
| * URI as their parent. |
| * <p> |
| * Existing elements on the menu bar will be removed. |
| * |
| * @param popupMenu |
| * The {@link JPopupMenu} to update |
| * @param id |
| * The {@link URI} of the menu bar |
| * @param contextualSelection |
| * The current selection for the context menu |
| */ |
| protected void populateContextMenu(JPopupMenu popupMenu, URI id, |
| ContextualSelection contextualSelection) { |
| popupMenu.removeAll(); |
| MenuComponent menuDef = uriToMenuElement.get(id); |
| if (menuDef == null) |
| throw new IllegalArgumentException("Unknown menuBar " + id); |
| if (!menuDef.getType().equals(MenuType.menu)) |
| throw new IllegalArgumentException("Element " + id |
| + " is not a menu, but a " + menuDef.getType()); |
| MenuOptions menuOptions = new MenuOptions(); |
| menuOptions.setContextualSelection(contextualSelection); |
| for (Component component : makeComponents(id, menuOptions)) |
| if (component == null) |
| popupMenu.addSeparator(); |
| else |
| popupMenu.add(component); |
| } |
| |
| /** |
| * Fill the specified tool bar with the elements that have the given URI as |
| * their parent. |
| * <p> |
| * Existing elements on the tool bar will be removed. |
| * |
| * @param toolbar |
| * The {@link JToolBar} to update |
| * @param id |
| * The {@link URI} of the tool bar |
| */ |
| protected void populateToolBar(JToolBar toolbar, URI id) { |
| toolbar.removeAll(); |
| MenuComponent toolbarDef = uriToMenuElement.get(id); |
| if (toolbarDef == null) |
| throw new IllegalArgumentException("Unknown toolBar " + id); |
| if (!toolbarDef.getType().equals(MenuType.toolBar)) |
| throw new IllegalArgumentException("Element " + id |
| + " is not a toolBar, but a " + toolbarDef.getType()); |
| if (toolbarDef.getAction() != null) { |
| String name = (String) toolbarDef.getAction().getValue(Action.NAME); |
| toolbar.setName(name); |
| } else |
| toolbar.setName(""); |
| MenuOptions menuOptions = new MenuOptions(); |
| menuOptions.setToolbar(true); |
| for (Component component : makeComponents(id, menuOptions)) { |
| if (component == null) { |
| toolbar.addSeparator(); |
| continue; |
| } |
| if (component instanceof JButton) { |
| JButton toolbarButton = (JButton) component; |
| toolbarButton.putClientProperty("hideActionText", true); |
| } |
| toolbar.add(component); |
| } |
| } |
| |
| /** |
| * Register a component that has been created. Such a component can be |
| * resolved through {@link #getComponentByURI(URI)}. |
| * |
| * @param id |
| * The {@link URI} that defined the component |
| * @param component |
| * The {@link Component} that was created. |
| */ |
| protected synchronized void registerComponent(URI id, Component component) { |
| registerComponent(id, component, false); |
| } |
| |
| /** |
| * Register a component that has been created. Such a component can be |
| * resolved through {@link #getComponentByURI(URI)}. |
| * |
| * @param id |
| * The {@link URI} that defined the component |
| * @param component |
| * The {@link Component} that was created. |
| * @param published |
| * <code>true</code> if the component has been published through |
| * {@link #createMenuBar()} or similar, and is to be |
| * automatically updated by later calls to {@link #update()}. |
| */ |
| protected synchronized void registerComponent(URI id, Component component, |
| boolean published) { |
| uriToComponent.put(id, new WeakReference<>(component)); |
| componentToUri.put(component, id); |
| if (published) { |
| List<WeakReference<Component>> publishedComponents = uriToPublishedComponents |
| .get(id); |
| if (publishedComponents == null) { |
| publishedComponents = new ArrayList<>(); |
| uriToPublishedComponents.put(id, publishedComponents); |
| } |
| publishedComponents.add(new WeakReference<>(component)); |
| } |
| setHelpStringForComponent(component, id); |
| } |
| |
| /** |
| * Reset all collections |
| * |
| */ |
| protected synchronized void resetCollections() { |
| menuElementTree = new HashMap<>(); |
| componentToUri = new WeakHashMap<>(); |
| uriToMenuElement = new HashMap<>(); |
| uriToComponent = new HashMap<>(); |
| } |
| |
| /** |
| * Set javax.help string to identify the component for later references to |
| * the help document. Note that the component (ie. the |
| * {@link AbstractMenuAction} must have an ID for an registration to take |
| * place. |
| * |
| * @param component |
| * The {@link Component} to set help string for |
| * @param componentId |
| * The {@link URI} to be used as identifier |
| */ |
| protected void setHelpStringForComponent(Component component, |
| URI componentId) { |
| if (componentId != null) { |
| String helpId = componentId.toASCIIString(); |
| setHelpIDString(component, helpId); |
| } |
| } |
| |
| /** |
| * Make an {@link AbstractButton} be configured in a "toolbar-like" way, for |
| * instance showing only the icon. |
| * |
| * @param actionButton |
| * Button to toolbarise |
| */ |
| protected void toolbarizeButton(AbstractButton actionButton) { |
| Action action = actionButton.getAction(); |
| if (action.getValue(SHORT_DESCRIPTION) == null) |
| action.putValue(SHORT_DESCRIPTION, action.getValue(NAME)); |
| actionButton.setBorder(new EmptyBorder(0, 2, 0, 2)); |
| // actionButton.setHorizontalTextPosition(JButton.CENTER); |
| // actionButton.setVerticalTextPosition(JButton.BOTTOM); |
| if (action.getValue(Action.SMALL_ICON) != null) { |
| // Don't show the text |
| actionButton.putClientProperty("hideActionText", true); |
| // Since hideActionText seems to be broken in Java 5 and/or OS X |
| actionButton.setText(null); |
| } |
| } |
| |
| /** |
| * Update all components that have been published using |
| * {@link #createMenuBar()} and similar. Content of such components will be |
| * removed and replaced by fresh components. |
| */ |
| protected void updatePublishedComponents() { |
| for (Entry<URI, List<WeakReference<Component>>> entry : uriToPublishedComponents |
| .entrySet()) |
| for (WeakReference<Component> reference : entry.getValue()) { |
| URI id = entry.getKey(); |
| Component component = reference.get(); |
| if (component == null) |
| continue; |
| if (component instanceof JToolBar) |
| populateToolBar((JToolBar) component, id); |
| else if (component instanceof JMenuBar) |
| populateMenuBar((JMenuBar) component, id); |
| else |
| logger.warn("Could not update published component " + id |
| + ": " + component.getClass()); |
| } |
| } |
| |
| public void setMenuComponents(List<MenuComponent> menuComponents) { |
| this.menuComponents = menuComponents; |
| } |
| |
| public void setSelectionManager(SelectionManager selectionManager) { |
| selectionManager.addObserver(new SelectionManagerObserver()); |
| } |
| |
| /** |
| * {@link Comparator} that can order {@link MenuComponent}s by their |
| * {@link MenuComponent#getPositionHint()}. |
| */ |
| protected static class MenuElementComparator implements |
| Comparator<MenuComponent> { |
| @Override |
| public int compare(MenuComponent a, MenuComponent b) { |
| return a.getPositionHint() - b.getPositionHint(); |
| } |
| } |
| |
| /** |
| * Various options for |
| * {@link MenuManagerImpl#makeComponents(URI, MenuOptions)} and friends. |
| * |
| * @author Stian Soiland-Reyes |
| */ |
| public static class MenuOptions { |
| private boolean isToolbar = false; |
| private boolean isOptionGroup = false; |
| private ContextualSelection contextualSelection = null; |
| |
| public ContextualSelection getContextualSelection() { |
| return contextualSelection; |
| } |
| |
| public void setContextualSelection( |
| ContextualSelection contextualSelection) { |
| this.contextualSelection = contextualSelection; |
| } |
| |
| public MenuOptions(MenuOptions original) { |
| this.isOptionGroup = original.isOptionGroup(); |
| this.isToolbar = original.isToolbar(); |
| this.contextualSelection = original.getContextualSelection(); |
| } |
| |
| public MenuOptions() { |
| } |
| |
| @Override |
| protected MenuOptions clone() { |
| return new MenuOptions(this); |
| } |
| |
| public boolean isToolbar() { |
| return isToolbar; |
| } |
| |
| public void setToolbar(boolean isToolbar) { |
| this.isToolbar = isToolbar; |
| } |
| |
| public boolean isOptionGroup() { |
| return isOptionGroup; |
| } |
| |
| public void setOptionGroup(boolean isOptionGroup) { |
| this.isOptionGroup = isOptionGroup; |
| } |
| } |
| |
| private final class SelectionManagerObserver extends |
| SwingAwareObserver<SelectionManagerEvent> { |
| private static final String DESIGN_PERSPECTIVE_ID = "net.sf.taverna.t2.ui.perspectives.design.DesignPerspective"; |
| private static final String RESULTS_PERSPECTIVE_ID = "net.sf.taverna.t2.ui.perspectives.results.ResultsPerspective"; |
| |
| @Override |
| public void notifySwing(Observable<SelectionManagerEvent> sender, |
| SelectionManagerEvent message) { |
| if (!(message instanceof PerspectiveSelectionEvent)) |
| return; |
| handlePerspectiveSelect((PerspectiveSelectionEvent) message); |
| } |
| |
| private void handlePerspectiveSelect(PerspectiveSelectionEvent event) { |
| String perspectiveID = event.getSelectedPerspective().getID(); |
| boolean isDesign = DESIGN_PERSPECTIVE_ID.equals(perspectiveID); |
| boolean isResults = RESULTS_PERSPECTIVE_ID.equals(perspectiveID); |
| |
| for (MenuComponent menuComponent : menuComponents) |
| if (!(menuComponent instanceof ContextualMenuComponent)) { |
| Action action = menuComponent.getAction(); |
| if (action instanceof DesignOnlyAction) |
| action.setEnabled(isDesign); |
| else if (action instanceof DesignOrResultsAction) |
| action.setEnabled(isDesign || isResults); |
| } |
| } |
| } |
| } |