You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by st...@apache.org on 2015/03/20 15:22:37 UTC
[23/51] [abbrv] [partial] incubator-taverna-workbench git commit:
taverna-workbench-* -> taverna-*
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuComponent.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuComponent.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuComponent.java
new file mode 100644
index 0000000..5bc91c4
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuComponent.java
@@ -0,0 +1,277 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ * Modifications to the initial code base are copyright of their
+ * respective authors, or their employers as appropriate.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.ui.menu;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.swing.Action;
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBox;
+import javax.swing.JMenu;
+import javax.swing.JToolBar;
+import javax.swing.MenuElement;
+
+/**
+ * A menu component, including sub menus, toolbars, and menu items.
+ * <p>
+ * This is an {@link net.sf.taverna.t2.spi.SPIRegistry SPI}, and implementations
+ * should list their fully qualified classnames in
+ * <tt>META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</tt> to be
+ * discovered by the {@link MenuManager}.
+ *
+ * @author Stian Soiland-Reyes
+ * @author David Withers
+ */
+public interface MenuComponent {
+ /**
+ * The {@link Action} describing this menu item, used for creating the UI
+ * representation of this item.
+ * <p>
+ * As a minimum the action should contain a name, and optionally an icon, a
+ * description and a keyboard shortcut. For {@linkplain MenuType#action
+ * actions} and {@linkplain MenuType#toggle toggles} the {@link Action}'s
+ * {@link ActionListener#actionPerformed(ActionEvent)} method is called when
+ * the item is clicked/selected.
+ * <p>
+ * This action is ignored and should be <code>null</code> for items of type
+ * {@link MenuType#optionGroup} and {@link MenuType#custom}. The action is
+ * optional for {@linkplain MenuType#toolBar toolbars} and
+ * {@linkplain MenuType#section sections}, where the action's name would be
+ * used as a label.
+ *
+ * @return The {@link Action} describing this menu item, or
+ * <code>null</code> if the {@link #getType()} is
+ * {@link MenuType#section}, {@link MenuType#optionGroup} or
+ * {@link MenuType#custom}.
+ */
+ public Action getAction();
+
+ /**
+ * Get a custom {@link Component} to be inserted into the parent
+ * menu/toolbar.
+ * <p>
+ * Used instead of creating menu elements from the {@link #getAction()} if
+ * the {@link #getType()} is {@link MenuType#custom}. This can be used to
+ * include dynamic menus.
+ * <p>
+ * This value is ignored and should be <code>null</code> for all other types
+ * except {@link MenuType#custom}.
+ *
+ * @return A {@link Component} to be inserted into the parent menu/toolbar.
+ */
+ public Component getCustomComponent();
+
+ /**
+ * The {@link URI} to identify this menu item.
+ * <p>
+ * This identifier can be used with other menu item's {@link #getParentId()}
+ * if this item has a {@link #getType()} of {@link MenuType#menu},
+ * {@link MenuType#toolBar}, {@link MenuType#section} or
+ * {@link MenuType#optionGroup}.
+ * <p>
+ * Leaf menu items of {@link #getType()} {@link MenuType#toggle},
+ * {@link MenuType#custom} and {@link MenuType#action} don't need an
+ * identifier as they can't have children, and may return <code>null</code>
+ * instead. However, a valid identifier might be used to look up the
+ * MenuItem with {@link MenuManager#getComponentByURI(URI)}
+ * <p>
+ * <strong>Note:</strong> To avoid conflicts with other plugins, use a
+ * unique URI root that is related to the Java package name, for instance
+ * <code>http://cs.university.ac.uk/myplugin/2008/menu</code>, and use hash
+ * identifiers for each menu item, for instance
+ * <code>http://cs.university.ac.uk/myplugin/2008/menu#run</code> for a
+ * "Run" item. Use flat URI namespaces, don't base a child's URI on the
+ * parent's URI, as this might make it difficult to relocate the parent
+ * menu.
+ *
+ * @return The {@link URI} to identify this menu item.
+ */
+ public URI getId();
+
+ /**
+ * The {@link URI} of the parent menu item, as returned by the parent's
+ * {@link #getId()}.
+ * <p>
+ * If this is the {@link DefaultMenuBar#DEFAULT_MENU_BAR}, then this menu
+ * item will be one of the top level menus of the main application window,
+ * like "File" or "Edit", and must have {@link #getType()}
+ * {@link MenuType#menu}.
+ * <p>
+ * This value should be <code>null</code> if this item is of
+ * {@link #getType()} {@link MenuType#toolBar}, and could be
+ * <code>null</code> if this is an independent root menu of type
+ * {@link MenuType#menu} (to be used outside the main window).
+ * <p>
+ * <strong>Note:</strong> To avoid compile time and runtime dependency on
+ * the parent menu item, always construct this URI directly using
+ * {@link URI#create(String)}.
+ *
+ * @return The {@link URI} of the parent menu item.
+ */
+ public URI getParentId();
+
+ /**
+ * A hint on how to position this item below the parent.
+ * <p>
+ * Menu items within the same parent menu/group/toolBar are ordered
+ * according to this position hint. If several items have the same position
+ * hint, their internal order is undefined, although generally it will be
+ * the order in which they were loaded.
+ * <p>
+ * <strong>Tip:</strong> Number the position hints in BASIC style, such as
+ * 10, 20, etc. so that plugins can use position hint such as 19 or 21 to be
+ * immediately before or after your item.
+ *
+ * @return A position hint
+ */
+ public int getPositionHint();
+
+ /**
+ * The {@link MenuType type} of menu item.
+ * <p>
+ * In the simple case of a "File -> New" menu structure, the "File" menu
+ * item has a type of {@link MenuType#menu}, while the "New" has a type of
+ * {@link MenuType#action}.
+ * <p>
+ * The menu item can only have children (i.e., items with
+ * {@link #getParentId()} equalling to this item's {@link #getId()}) if the
+ * type is not a leaf type, i.e., not {@link MenuType#toggle} or
+ * {@link MenuType#action}.
+ *
+ * @return A {@link MenuType} to specify the role of this menu item.
+ */
+ public MenuType getType();
+
+ /**
+ * True if this menu component is to be included in the menu/toolbar.
+ *
+ * @return True is this menu component is to be included
+ */
+ public boolean isEnabled();
+
+ /**
+ * The type of menu item, such as {@link #action}, {@link #menu} or
+ * {@link #toolBar}.
+ * <p>
+ * Some types are {@linkplain #isParentType() parent types} - that means
+ * URIs to menu components of that type can be used as a
+ * {@linkplain MenuComponent#getParentId() parent id}.
+ *
+ * @author Stian Soiland-Reyes
+ *
+ */
+ public static enum MenuType {
+ /**
+ * A normal {@link Action} as part of a {@link #menu}, {@link #toolBar},
+ * {@link #section} or {@link #optionGroup}. Such menu items are leaf
+ * nodes, which no {@link MenuComponent}s can have this as it's
+ * {@link MenuComponent#getParentId()}. The action's
+ * {@link ActionListener#actionPerformed(ActionEvent)} will be called
+ * when choosing/clicking the menu item from the menu or toolBar.
+ */
+ action,
+ /**
+ * Provide a customised {@link MenuElement} from
+ * {@link MenuComponent#getCustomComponent()} that is to be used instead
+ * of creating an element from {@link MenuComponent#getAction()}.
+ */
+ custom,
+ /**
+ * A group containing mutually exclusive choices (as {@link #action}s),
+ * to be grouped in a {@link ButtonGroup}, separated using
+ * {@link JMenu#addSeparator()} or {@link JToolBar#addSeparator()} when
+ * needed. The {@link MenuComponent#getAction()} is ignored and should
+ * be <code>null</code>.
+ */
+ optionGroup,
+ /**
+ * A section of menu items within {@link #menu} or {@link #toolBar}.
+ * Sections are separated using {@link JMenu#addSeparator()} or
+ * {@link JToolBar#addSeparator()} when needed. The
+ * {@link MenuComponent#getAction()} is ignored and should be
+ * <code>null</code>.
+ */
+ section,
+ /**
+ * A (sub)menu that contain other menu items, including deeper
+ * {@link #menu}s. The {@link Action} from
+ * {@link MenuComponent#getAction()} is used to find the name, icon,
+ * etc., for the sub-menu, while its
+ * {@link ActionListener#actionPerformed(ActionEvent)} method is
+ * ignored. The {@link DefaultMenuBar} is the default top level menu,
+ * although others can be created with <code>null</code> as their
+ * parent.
+ */
+ menu,
+ /**
+ * A boolean toggle action, the action will be shown as a
+ * {@link JCheckBox} on a menu or toolBar. Such menu items are leaf
+ * nodes, which no {@link MenuComponent}s can have this as it's
+ * {@link MenuComponent#getParentId()}. The action's
+ * {@link ActionListener#actionPerformed(ActionEvent)} will be called
+ * when toggling the action.
+ */
+ toggle,
+ /**
+ * A toolBar containing {@link #optionGroup}s, {@link #toggle}s or
+ * {@link #action}s. The toolBar can be shown as a {@link JToolBar}. The
+ * {@link MenuComponent#getAction()} and
+ * {@link MenuComponent#getParentId()} are ignored and should be
+ * <code>null</code>.
+ */
+ toolBar;
+
+ private static final Set<MenuType> parentTypes = defineParentTypes();
+
+ /**
+ * True if the menu type is a parent type such as {@link #optionGroup},
+ * {@link #section}, {@link #menu} or {@link #toolBar}. If the type of a
+ * menu component is a a parent type it can (should) have children,
+ * i.e., the children has a {@link MenuComponent#getParentId()} that
+ * equals the parent's {@link MenuComponent#getId()}.
+ *
+ * @return True if the menu type is a parent type.
+ */
+ public boolean isParentType() {
+ return parentTypes.contains(this);
+ }
+
+ /**
+ * Create the set of {@link MenuType}s that {@link #isParentType()}
+ * would return <code>true</code> for.
+ *
+ * @return A {@link Set} of {@link MenuType}s.
+ */
+ private static Set<MenuType> defineParentTypes() {
+ HashSet<MenuType> types = new HashSet<>();
+ types.add(optionGroup);
+ types.add(section);
+ types.add(menu);
+ types.add(toolBar);
+ return types;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuManager.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuManager.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuManager.java
new file mode 100644
index 0000000..cca1bf0
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuManager.java
@@ -0,0 +1,339 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ * Modifications to the initial code base are copyright of their
+ * respective authors, or their employers as appropriate.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.ui.menu;
+
+import java.awt.Component;
+import java.lang.ref.WeakReference;
+import java.net.URI;
+import java.util.List;
+
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JToolBar;
+
+import uk.org.taverna.scufl2.api.core.Workflow;
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.ui.menu.MenuComponent.MenuType;
+import net.sf.taverna.t2.ui.menu.MenuManager.MenuManagerEvent;
+
+/**
+ * Create {@link JMenuBar}s and {@link JToolBar}s based on SPI instances of
+ * {@link MenuComponent}.
+ * <p>
+ * Elements of menus are discovered automatically using an {@link SPIRegistry}.
+ * The elements specify their internal relationship through
+ * {@link MenuComponent#getParentId()} and
+ * {@link MenuComponent#getPositionHint()}. {@link MenuComponent#getType()}
+ * specifies how the component is to be rendered or grouped.
+ * <p>
+ * The menu manager is {@link Observable}, you can
+ * {@linkplain #addObserver(Observer) add an observer} to be notified when the
+ * menus have changed, i.e. when {@link #update()} has been called, for instance
+ * when the {@link SPIRegistry} (which the menu manager observes) has been
+ * updated due to a plugin installation.
+ * <p>
+ * {@link #createMenuBar()} creates the default menu bar, ie. the menu bar
+ * containing all the items with {@link DefaultMenuBar#DEFAULT_MENU_BAR} as
+ * their parent. Alternate menu bars can be created using
+ * {@link #createMenuBar(URI)}.
+ * <p>
+ * Similary {@link #createToolBar()} creates the default tool bar, containing
+ * the items that has {@link DefaultToolBar#DEFAULT_TOOL_BAR} as their parent.
+ * Alternate toolbars can be created using {@link #createToolBar(URI)}.
+ * <p>
+ * The menu manager keeps weak references to the created (published) menu bars
+ * and tool bars, and will attempt to update them when {@link #update()} is
+ * called.
+ * <p>
+ * See the package level documentation for more information about how to specify
+ * menu elements.
+ *
+ * @author Stian Soiland-Reyes
+ */
+public interface MenuManager extends Observable<MenuManagerEvent> {
+ /**
+ * Add the items from the list of menu items to the parent menu with
+ * expansion sub-menus if needed.
+ * <p>
+ * If the list contains more than <tt>maxItemsInMenu</tt> items, a series of
+ * sub-menus will be created and added to the parentMenu instead, each
+ * containing a maximum of <tt>maxItemsInMenu</tt> items. (Note that if
+ * menuItems contains more than <tt>maxItemsInMenu*maxItemsInMenu</tt>
+ * items, there might be more than <tt>maxItemsInMenu</tt> sub-menus added
+ * to the parent).
+ * <p>
+ * The sub-menus are titled according to the {@link JMenuItem#getText()} of
+ * the first and last menu item it contains - assuming that they are already
+ * sorted.
+ * <p>
+ * The optional {@link ComponentFactory} headerItemFactory, if not
+ * <code>null</code>, will be invoked to create a header item that will be
+ * inserted on top of the sub-menus. This item does not count towards
+ * <tt>maxItemsInMenu</tt>.
+ * <p>
+ * Note that this is a utility method that does not mandate the use of the
+ * {@link MenuManager} structure for the menu.
+ *
+ * @param menuItems
+ * {@link JMenuItem}s to be inserted
+ * @param parentMenu
+ * Menu to insert items to
+ * @param maxItemsInMenu
+ * Maximum number of items in parent menu or created sub-menus
+ * @param headerItemFactory
+ * If not <code>null</code>, a {@link ComponentFactory} to create
+ * a header item to insert at top of created sub-menus
+ */
+ abstract void addMenuItemsWithExpansion(List<JMenuItem> menuItems,
+ JMenu parentMenu, int maxItemsInMenu,
+ ComponentFactory headerItemFactory);
+
+ /**
+ * Create a contextual menu for a selected object.
+ * <p>
+ * Items for the contextual menues are discovered in a similar to fashion as
+ * with {@link #createMenuBar()}, but using {@link DefaultContextualMenu} as
+ * the root.
+ * <p>
+ * Additionally, items implementing {@link ContextualMenuComponent} will be
+ * {@linkplain ContextualMenuComponent#setContextualSelection(Object, Object, Component)
+ * informed} about what is the current selection, as passed to this method.
+ * <p>
+ * Thus, the items can choose if they want to be
+ * {@link MenuComponent#isEnabled() visible} or not for a given selection,
+ * and return an action that is bound it to the selection.
+ *
+ * @param parent
+ * The parent object of the selected object, for instance a
+ * {@link Workflow}.
+ * @param selection
+ * The selected object which actions in the contextual menu
+ * relate to, for instance a {@link Processor}
+ * @param relativeToComponent
+ * A UI component which the returned {@link JPopupMenu} (and it's
+ * actions) is to be relative to, for instance as a parent of
+ * pop-up dialogues.
+ * @return An empty or populated {@link JPopupMenu} depending on the
+ * selected objects.
+ */
+ abstract JPopupMenu createContextMenu(Object parent, Object selection,
+ Component relativeToComponent);
+
+ /**
+ * Create the {@link JMenuBar} containing menu elements defining
+ * {@link DefaultMenuBar#DEFAULT_MENU_BAR} as their
+ * {@linkplain MenuComponent#getParentId() parent}.
+ * <p>
+ * A {@linkplain WeakReference weak reference} is kept in the menu manager
+ * to update the menubar if {@link #update()} is called (manually or
+ * automatically when the SPI is updated).
+ *
+ * @return A {@link JMenuBar} populated with the items belonging to the
+ * default menu bar
+ */
+ abstract JMenuBar createMenuBar();
+
+ /**
+ * Create the {@link JMenuBar} containing menu elements defining the given
+ * <code>id</code> as their {@linkplain MenuComponent#getParentId() parent}.
+ * <p>
+ * Note that the parent itself also needs to exist as a registered SPI
+ * instance og {@link MenuComponent#getType()} equal to
+ * {@link MenuType#menu}, for instance by subclassing {@link AbstractMenu}.
+ * <p>
+ * A {@linkplain WeakReference weak reference} is kept in the menu manager
+ * to update the menubar if {@link #update()} is called (manually or
+ * automatically when the SPI is updated).
+ *
+ * @param id
+ * The {@link URI} identifying the menu bar
+ * @return A {@link JMenuBar} populated with the items belonging to the
+ * given parent id.
+ */
+ abstract JMenuBar createMenuBar(URI id);
+
+ /**
+ * Create the {@link JToolBar} containing elements defining
+ * {@link DefaultToolBar#DEFAULT_TOOL_BAR} as their
+ * {@linkplain MenuComponent#getParentId() parent}.
+ * <p>
+ * A {@linkplain WeakReference weak reference} is kept in the menu manager
+ * to update the toolbar if {@link #update()} is called (manually or
+ * automatically when the SPI is updated).
+ *
+ * @return A {@link JToolBar} populated with the items belonging to the
+ * default tool bar
+ */
+ abstract JToolBar createToolBar();
+
+ /**
+ * Create the {@link JToolBar} containing menu elements defining the given
+ * <code>id</code> as their {@linkplain MenuComponent#getParentId() parent}.
+ * <p>
+ * Note that the parent itself also needs to exist as a registered SPI
+ * instance of {@link MenuComponent#getType()} equal to
+ * {@link MenuType#toolBar}, for instance by subclassing
+ * {@link AbstractToolBar}.
+ * <p>
+ * A {@linkplain WeakReference weak reference} is kept in the menu manager
+ * to update the toolbar if {@link #update()} is called (manually or
+ * automatically when the SPI is updated).
+ *
+ * @param id
+ * The {@link URI} identifying the tool bar
+ * @return A {@link JToolBar} populated with the items belonging to the
+ * given parent id.
+ */
+ abstract JToolBar createToolBar(URI id);
+
+ /**
+ * Get a menu item identified by the given URI.
+ * <p>
+ * Return the UI {@link Component} last created for a {@link MenuComponent},
+ * through {@link #createMenuBar()}, {@link #createMenuBar(URI)},
+ * {@link #createToolBar()} or {@link #createToolBar(URI)}.
+ * <p>
+ * For instance, if {@link #createMenuBar()} created a menu bar containing a
+ * "File" menu with {@link MenuComponent#getId() getId()} ==
+ * <code>http://example.com/menu#file</code>, calling:
+ *
+ * <pre>
+ * Component fileMenu = getComponentByURI(URI
+ * .create("http://example.com/menu#file"));
+ * </pre>
+ *
+ * would return the {@link JMenu} last created for "File". Note that "last
+ * created" could mean both the last call to {@link #createMenuBar()} and
+ * last call to {@link #update()} - which could have happened because the
+ * SPI registry was updated. To be notified when
+ * {@link #getComponentByURI(URI)} might return a new Component because the
+ * menues have been reconstructed, {@linkplain #addObserver(Observer) add an
+ * observer} to the MenuManager.
+ * <p>
+ * If the URI is unknown, has not yet been rendered as a {@link Component},
+ * or the Component is no longer in use outside the menu manager's
+ * {@linkplain WeakReference weak references}, <code>null</code> is returned
+ * instead.
+ *
+ * @see #getURIByComponent(Component)
+ * @param id
+ * {@link URI} of menu item as returned by
+ * {@link MenuComponent#getId()}
+ * @return {@link Component} as previously generated by
+ * {@link #createMenuBar()}/{@link #createToolBar()}, or
+ * <code>null</code> if the URI is unknown, or if the
+ * {@link Component} no longer exists.
+ */
+ public abstract Component getComponentByURI(URI id);
+
+ /**
+ * Get the URI of the {@link MenuComponent} this menu/toolbar
+ * {@link Component} was created from.
+ * <p>
+ * If the component was created by the MenuManager, through
+ * {@link #createMenuBar()}, {@link #createMenuBar(URI)},
+ * {@link #createToolBar()} or {@link #createToolBar(URI)}, the URI
+ * identifying the defining {@link MenuComponent} is returned. This will be
+ * the same URI as returned by {@link MenuComponent#getId()}.
+ * <p>
+ * Note that if {@link #update()} has been invoked, the {@link MenuManager}
+ * might have rebuilt the menu structure and replaced the components since
+ * the given <code>component</code> was created. The newest
+ * {@link Component} for the given URI can be retrieved using
+ * {@link #getComponentByURI(URI)}.
+ * <p>
+ * If the component is unknown, <code>null</code> is returned instead.
+ *
+ * @see #getComponentByURI(URI)
+ * @param component
+ * {@link Component} that was previously created by the
+ * {@link MenuManager}
+ * @return {@link URI} identifying the menu component, as returned by
+ * {@link MenuComponent#getId()}, or <code>null</code> if the
+ * component is unknown.
+ */
+ abstract URI getURIByComponent(Component component);
+
+ /**
+ * Update and rebuild the menu structure.
+ * <p>
+ * Rebuild menu structure as defined by the {@link MenuComponent}s retrieved
+ * from the MenuComponent {@link SPIRegistry}.
+ * <p>
+ * Rebuilds previously published menubars and toolbars created with
+ * {@link #createMenuBar()}, {@link #createMenuBar(URI)},
+ * {@link #createToolBar()} and {@link #createToolBar(URI)}. Note that the
+ * rebuild will do a removeAll() on the menubar/toolbar, so all components
+ * will be reconstructed. You can use {@link #getComponentByURI(URI)} to
+ * look up individual components within the menu and toolbars.
+ * <p>
+ * Note that the menu manager is observing the {@link SPIRegistry}, so if a
+ * plugin gets installed and the SPI registry is updated, this update method
+ * will be called by the SPI registry observer.
+ * <p>
+ * If there are several concurrent calls to {@link #update()}, the calls
+ * from the other thread will return immediately, while the first thread to
+ * get the synchronization lock on the menu manager will do the actual
+ * update. If you want to ensure that {@link #update()} does not return
+ * before the update has been performed fully, synchronize on the menu
+ * manager:
+ *
+ * <pre>
+ * MenuManager menuManager = MenuManager.getInstance();
+ * synchronized (menuManager) {
+ * menuManager.update();
+ * }
+ * doSomethingAfterUpdateFinished();
+ * </pre>
+ */
+ abstract void update();
+
+ /**
+ * Abstract class for events sent to {@linkplain Observer observers} of the
+ * menu manager.
+ *
+ * @see UpdatedMenuManagerEvent
+ * @author Stian Soiland-Reyes
+ */
+ static abstract class MenuManagerEvent {
+ }
+
+ /**
+ * Event sent to observers registered by
+ * {@link MenuManager#addObserver(Observer)} when the menus have been
+ * updated, i.e. when {@link MenuManager#update()} has been called.
+ */
+ static class UpdatedMenuManagerEvent extends MenuManagerEvent {
+ }
+
+ /**
+ * A factory for making {@link Component}s, in particular for making headers
+ * (like {@link JLabel}s) for
+ * {@link MenuManager#addMenuItemsWithExpansion(List, JMenu, int, ComponentFactory)}
+ */
+ interface ComponentFactory {
+ public Component makeComponent();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/package-info.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/package-info.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/package-info.java
new file mode 100644
index 0000000..4c86db5
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/package-info.java
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ * Modifications to the initial code base are copyright of their
+ * respective authors, or their employers as appropriate.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+/**
+ * An {@link net.sf.taverna.t2.spi.SPIRegistry SPI} based system for creating
+ * {@link javax.swing.JMenuBar menues} and {@link javax.swing.JToolBar toolbars}.
+ * <p>
+ * Each element of a menu and/or toolbar is created by making an SPI
+ * implementation class of {@link net.sf.taverna.t2.ui.menu.MenuComponent} and listing the fully qualified
+ * class name in the SPI description resource file
+ * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code>
+ * </p>
+ * <p>
+ * The {@link net.sf.taverna.t2.ui.menu.MenuManager} discovers all menu components using an SPI registry,
+ * and builds the {@link javax.swing.JMenuBar menu bar} using
+ * {@link net.sf.taverna.t2.ui.menu.MenuManager#createMenuBar()} or the
+ * {@link javax.swing.JToolBar toolbar} using
+ * {@link net.sf.taverna.t2.ui.menu.MenuManager#createToolBar()}.
+ * </p>
+ * <p>
+ * This allows plugins to provide actions (menu items) and submenues that can be
+ * inserted at any points in the generated menu. All parts of the menues are
+ * described through a parent/child relationship using
+ * {@link net.sf.taverna.t2.ui.menu.MenuComponent#getId()} and {@link net.sf.taverna.t2.ui.menu.MenuComponent#getParentId()}. The
+ * components are identified using {@link java.net.URI}s to avoid compile time
+ * dependencies, so a plugin can for instance add something to the existing
+ * "Edit" menu without depending on the actual implementation of the
+ * {@link net.sf.taverna.t2.ui.menu.MenuComponent} describing "Edit", as long as it refers to the same
+ * URI. The use of URIs instead of pure strings is to encourage the use of
+ * unique identifiers, for instance plugins should use an URI base that is
+ * derived from their package name to avoid collision with other plugins.
+ * </p>
+ * <p>
+ * A set of abstract classes, with a common parent {@link net.sf.taverna.t2.ui.menu.AbstractMenuItem},
+ * make it more convenient to create simple SPI implementations. Two default top
+ * level implementations {@link net.sf.taverna.t2.ui.menu.DefaultMenuBar} and {@link net.sf.taverna.t2.ui.menu.DefaultToolBar} can
+ * be used as parents for items that are to be included in
+ * {@link net.sf.taverna.t2.ui.menu.MenuManager#createMenuBar()} and {@link net.sf.taverna.t2.ui.menu.MenuManager#createToolBar()},
+ * but it's possible to have other parents - such menu trees would have to be
+ * created by providing the URI of the top level parent to
+ * {@link net.sf.taverna.t2.ui.menu.MenuManager#createMenuBar(URI)} or
+ * {@link net.sf.taverna.t2.ui.menu.MenuManager#createToolBar(URI)}.
+ * </p>
+ * <p>
+ * In the simplest form a menu structure can be built by subclassing
+ * {@link net.sf.taverna.t2.ui.menu.AbstractMenu} and {@link net.sf.taverna.t2.ui.menu.AbstractMenuAction}, but more complex menus
+ * can be built by including submenus (AbstractMenu with an AbstractMenu as a
+ * parent), grouping similar actions in a {@link net.sf.taverna.t2.ui.menu.AbstractMenuSection section},
+ * or making {@link net.sf.taverna.t2.ui.menu.AbstractMenuToggle toggle actions} and
+ * {@link net.sf.taverna.t2.ui.menu.AbstractMenuOptionGroup option groups}. You can add arbitrary "real"
+ * {@link javax.swing.JMenuBar} / {@link javax.swing.JToolBar} compatible items
+ * (such as {@link javax.swing.JMenu}s, {@link javax.swing.JMenuItem}s and
+ * {@link javax.swing.JButton}s) using
+ * {@link net.sf.taverna.t2.ui.menu.AbstractMenuCustom custom menu items}.
+ * </p>
+ *
+ * <p>
+ * Example showing how <code>File->Open</code> could be implemented using
+ * two SPI implementations net.sf.taverna.t2.ui.perspectives.hello.FileMenu and
+ * net.sf.taverna.t2.ui.perspectives.hello.FileOpenAction:
+ * </p>
+ *
+ * <pre>
+ * package net.sf.taverna.t2.ui.perspectives.hello;
+ *
+ * import java.net.URI;
+ *
+ * import net.sf.taverna.t2.ui.menu.AbstractMenu;
+ * import net.sf.taverna.t2.ui.menu.DefaultMenuBar;
+ *
+ * public class FileMenu extends AbstractMenu {
+ *
+ * private static final URI FILE_URI = URI
+ * .create("http://taverna.sf.net/2008/t2workbench/test#file");
+ *
+ * public FileMenu() {
+ * super(DefaultMenuBar.DEFAULT_MENU_BAR, 10, FILE_URI, "File");
+ * }
+ *
+ * }
+ * </pre>
+ * <pre>
+ * package net.sf.taverna.t2.ui.perspectives.hello;
+ *
+ * import java.awt.event.ActionEvent;
+ * import java.net.URI;
+ *
+ * import javax.swing.AbstractAction;
+ * import javax.swing.Action;
+ * import javax.swing.JOptionPane;
+ *
+ * import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+ *
+ * public class FileOpenAction extends AbstractMenuAction {
+ * public FileOpenAction() {
+ * super(URI.create("http://taverna.sf.net/2008/t2workbench/test#file"),
+ * 20);
+ * }
+ *
+ * @Override
+ * public Action createAction() {
+ * return new AbstractAction("Open") {
+ * public void actionPerformed(ActionEvent arg0) {
+ * JOptionPane.showMessageDialog(null, "Open");
+ * }
+ * };
+ * }
+ * }
+ * </pre>
+ *
+ * <p>
+ * The implementation of the {@link net.sf.taverna.t2.ui.menu.MenuManager} itself is discovered by an
+ * internal SPI registry through {@link net.sf.taverna.t2.ui.menu.MenuManager#getInstance()}. The menu
+ * manager is observing the SPI registry, so that any updates to the registry
+ * from installing plugins etc. are reflected in an automatic rebuild of the
+ * menus. This update can also be triggered manually by calling
+ * {@link net.sf.taverna.t2.ui.menu.MenuManager#update()}.
+ * </p>
+ *
+ * @author Stian Soiland-Reyes
+ *
+ */
+package net.sf.taverna.t2.ui.menu;
+
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-menu-api/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
new file mode 100644
index 0000000..c137386
--- /dev/null
+++ b/taverna-menu-api/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,4 @@
+net.sf.taverna.t2.ui.menu.DefaultMenuBar
+net.sf.taverna.t2.ui.menu.DefaultToolBar
+net.sf.taverna.t2.ui.menu.DefaultContextualMenu
+
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context-osgi.xml b/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context-osgi.xml
new file mode 100644
index 0000000..b27e860
--- /dev/null
+++ b/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context-osgi.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:beans="http://www.springframework.org/schema/beans"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd">
+
+ <service ref="DefaultMenuBar" auto-export="interfaces" />
+ <service ref="DefaultToolBar" auto-export="interfaces" />
+ <service ref="DefaultContextualMenu" auto-export="interfaces" />
+
+</beans:beans>
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context.xml
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context.xml b/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context.xml
new file mode 100644
index 0000000..734dbbe
--- /dev/null
+++ b/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+ <bean id="DefaultMenuBar" class="net.sf.taverna.t2.ui.menu.DefaultMenuBar" />
+ <bean id="DefaultToolBar" class="net.sf.taverna.t2.ui.menu.DefaultToolBar" />
+ <bean id="DefaultContextualMenu" class="net.sf.taverna.t2.ui.menu.DefaultContextualMenu" />
+
+</beans>
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/pom.xml b/taverna-menu-impl/pom.xml
new file mode 100644
index 0000000..b9898ed
--- /dev/null
+++ b/taverna-menu-impl/pom.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.taverna.workbench</groupId>
+ <artifactId>taverna-workbench</artifactId>
+ <version>3.1.0-incubating-SNAPSHOT</version>
+ </parent>
+ <artifactId>menu-impl</artifactId>
+ <packaging>bundle</packaging>
+ <name>Menu generation implementation</name>
+ <description>The main workbench ui</description>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Embed-Dependency>javahelp</Embed-Dependency>
+ <Import-Package>org.jdesktop.jdic.browser;resolution:=optional,*</Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>${project.parent.groupId}</groupId>
+ <artifactId>workbench-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.parent.groupId}</groupId>
+ <artifactId>menu-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.parent.groupId}</groupId>
+ <artifactId>helper-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>net.sf.taverna.t2.lang</groupId>
+ <artifactId>ui</artifactId>
+ <version>${t2.lang.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>uk.org.taverna.configuration</groupId>
+ <artifactId>taverna-app-configuration-api</artifactId>
+ <version>${taverna.configuration.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.help</groupId>
+ <artifactId>javahelp</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/ui/menu/impl/MenuManagerImpl.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/ui/menu/impl/MenuManagerImpl.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/ui/menu/impl/MenuManagerImpl.java
new file mode 100644
index 0000000..ee1fd3d
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/ui/menu/impl/MenuManagerImpl.java
@@ -0,0 +1,880 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ * Modifications to the initial code base are copyright of their
+ * respective authors, or their employers as appropriate.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.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 net.sf.taverna.t2.lang.ui.ShadedLabel.GREEN;
+import static net.sf.taverna.t2.ui.menu.AbstractMenuSection.SECTION_COLOR;
+import static net.sf.taverna.t2.ui.menu.DefaultContextualMenu.DEFAULT_CONTEXT_MENU;
+import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR;
+import static net.sf.taverna.t2.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 net.sf.taverna.t2.lang.observer.MultiCaster;
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.lang.observer.SwingAwareObserver;
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.ui.menu.AbstractMenuOptionGroup;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.menu.DesignOnlyAction;
+import net.sf.taverna.t2.ui.menu.DesignOrResultsAction;
+import net.sf.taverna.t2.ui.menu.MenuComponent;
+import net.sf.taverna.t2.ui.menu.MenuComponent.MenuType;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.selection.events.PerspectiveSelectionEvent;
+import net.sf.taverna.t2.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);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/AdvancedMenu.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/AdvancedMenu.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/AdvancedMenu.java
new file mode 100644
index 0000000..9a2f37b
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/AdvancedMenu.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ * Modifications to the initial code base are copyright of their
+ * respective authors, or their employers as appropriate.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.impl.menu;
+
+import static java.awt.event.KeyEvent.VK_A;
+import static javax.swing.Action.MNEMONIC_KEY;
+import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenu;
+
+public class AdvancedMenu extends AbstractMenu {
+ public static final URI ADVANCED_URI = URI
+ .create("http://taverna.sf.net/2008/t2workbench/menu#advanced");
+
+ public AdvancedMenu() {
+ super(DEFAULT_MENU_BAR, 1000, ADVANCED_URI, makeAction());
+ }
+
+ public static DummyAction makeAction() {
+ DummyAction action = new DummyAction("Advanced");
+ action.putValue(MNEMONIC_KEY, Integer.valueOf(VK_A));
+ return action;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/EditMenu.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/EditMenu.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/EditMenu.java
new file mode 100644
index 0000000..a15237c
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/EditMenu.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ * Modifications to the initial code base are copyright of their
+ * respective authors, or their employers as appropriate.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.impl.menu;
+
+import static java.awt.event.KeyEvent.VK_E;
+import static javax.swing.Action.MNEMONIC_KEY;
+import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenu;
+
+public class EditMenu extends AbstractMenu {
+ public EditMenu() {
+ super(DEFAULT_MENU_BAR, 20, URI
+ .create("http://taverna.sf.net/2008/t2workbench/menu#edit"),
+ makeAction());
+ }
+
+ public static DummyAction makeAction() {
+ DummyAction action = new DummyAction("Edit");
+ action.putValue(MNEMONIC_KEY, Integer.valueOf(VK_E));
+ return action;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FeedbackMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FeedbackMenuAction.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FeedbackMenuAction.java
new file mode 100644
index 0000000..6b6eb7c
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FeedbackMenuAction.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ * Modifications to the initial code base are copyright of their
+ * respective authors, or their employers as appropriate.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.impl.menu;
+
+import static java.awt.Desktop.getDesktop;
+import static net.sf.taverna.t2.workbench.ui.impl.menu.HelpMenu.HELP_URI;
+
+import java.awt.event.ActionEvent;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+
+import org.apache.log4j.Logger;
+
+/**
+ * MenuItem for feedback
+ *
+ * @author alanrw
+ */
+public class FeedbackMenuAction extends AbstractMenuAction {
+ private static Logger logger = Logger.getLogger(FeedbackMenuAction.class);
+
+ private static String FEEDBACK_URL = "http://www.taverna.org.uk/about/contact-us/feedback/";
+
+ public FeedbackMenuAction() {
+ super(HELP_URI, 20);
+ }
+
+ @Override
+ protected Action createAction() {
+ return new FeedbackAction();
+ }
+
+ @SuppressWarnings("serial")
+ private final class FeedbackAction extends AbstractAction {
+ private FeedbackAction() {
+ super("Contact us");
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ try {
+ getDesktop().browse(new URI(FEEDBACK_URL));
+ } catch (IOException e1) {
+ logger.error("Unable to open URL", e1);
+ } catch (URISyntaxException e1) {
+ logger.error("Invalid URL syntax", e1);
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FileMenu.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FileMenu.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FileMenu.java
new file mode 100644
index 0000000..61f963b
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FileMenu.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ * Modifications to the initial code base are copyright of their
+ * respective authors, or their employers as appropriate.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.impl.menu;
+
+import static java.awt.event.KeyEvent.VK_F;
+import static javax.swing.Action.MNEMONIC_KEY;
+import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenu;
+
+/**
+ * File menu
+ *
+ * @author Stian Soiland-Reyes
+ */
+public class FileMenu extends AbstractMenu {
+ public FileMenu() {
+ super(DEFAULT_MENU_BAR, 10, URI
+ .create("http://taverna.sf.net/2008/t2workbench/menu#file"),
+ makeAction());
+ }
+
+ public static DummyAction makeAction() {
+ DummyAction action = new DummyAction("File");
+ action.putValue(MNEMONIC_KEY, Integer.valueOf(VK_F));
+ return action;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/HelpMenu.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/HelpMenu.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/HelpMenu.java
new file mode 100644
index 0000000..c4169cc
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/HelpMenu.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ * Modifications to the initial code base are copyright of their
+ * respective authors, or their employers as appropriate.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.impl.menu;
+
+import static java.awt.event.KeyEvent.VK_H;
+import static javax.swing.Action.MNEMONIC_KEY;
+import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenu;
+
+public class HelpMenu extends AbstractMenu {
+ public static final URI HELP_URI = URI
+ .create("http://taverna.sf.net/2008/t2workbench/menu#help");
+
+ public HelpMenu() {
+ super(DEFAULT_MENU_BAR, 1024, HELP_URI, makeAction());
+ }
+
+ public static DummyAction makeAction() {
+ DummyAction action = new DummyAction("Help");
+ action.putValue(MNEMONIC_KEY, Integer.valueOf(VK_H));
+ return action;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/OnlineHelpMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/OnlineHelpMenuAction.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/OnlineHelpMenuAction.java
new file mode 100644
index 0000000..d091c8e
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/OnlineHelpMenuAction.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ * Modifications to the initial code base are copyright of their
+ * respective authors, or their employers as appropriate.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.ui.impl.menu;
+
+import static java.awt.event.KeyEvent.VK_F1;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static net.sf.taverna.t2.workbench.helper.Helper.displayDefaultHelp;
+import static net.sf.taverna.t2.workbench.ui.impl.menu.HelpMenu.HELP_URI;
+
+import java.awt.AWTEvent;
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+
+/**
+ * MenuItem for help
+ *
+ * @author alanrw
+ */
+public class OnlineHelpMenuAction extends AbstractMenuAction {
+ public OnlineHelpMenuAction() {
+ super(HELP_URI, 10);
+ }
+
+ @Override
+ protected Action createAction() {
+ return new OnlineHelpAction();
+ }
+
+ @SuppressWarnings("serial")
+ private final class OnlineHelpAction extends AbstractAction {
+ private OnlineHelpAction() {
+ super("Online help");
+ putValue(ACCELERATOR_KEY, getKeyStroke(VK_F1, 0));
+
+ }
+
+ /**
+ * When selected, use the Helper to display the default help.
+ */
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ displayDefaultHelp((AWTEvent) e);
+ // TODO change helper to bean?
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ShowLogsAndDataMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ShowLogsAndDataMenuAction.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ShowLogsAndDataMenuAction.java
new file mode 100644
index 0000000..308d51d
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ShowLogsAndDataMenuAction.java
@@ -0,0 +1,89 @@
+package net.sf.taverna.t2.workbench.ui.impl.menu;
+
+import static java.lang.Runtime.getRuntime;
+import static javax.swing.JOptionPane.INFORMATION_MESSAGE;
+import static javax.swing.JOptionPane.showInputDialog;
+import static net.sf.taverna.t2.workbench.MainWindow.getMainWindow;
+import static net.sf.taverna.t2.workbench.ui.impl.menu.AdvancedMenu.ADVANCED_URI;
+
+import java.awt.event.ActionEvent;
+import java.io.File;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+
+public class ShowLogsAndDataMenuAction extends AbstractMenuAction {
+ private static final String OPEN = "open";
+ private static final String EXPLORER = "explorer";
+ // TODO Consider using xdg-open instead of gnome-open
+ private static final String GNOME_OPEN = "gnome-open";
+ private static final String WINDOWS = "Windows";
+ private static final String MAC_OS_X = "Mac OS X";
+
+ private ApplicationConfiguration applicationConfiguration;
+
+ public ShowLogsAndDataMenuAction() {
+ super(ADVANCED_URI, 200);
+ }
+
+ private static Logger logger = Logger.getLogger(ShowLogsAndDataMenuAction.class);
+
+ @Override
+ protected Action createAction() {
+ return new AbstractAction("Show logs and data folder") {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ File logsAndDataDir = applicationConfiguration.getApplicationHomeDir();
+ showDirectory(logsAndDataDir, "Taverna logs and data folder");
+ }
+ };
+ }
+
+ public static void showDirectory(File dir, String title) {
+ String path = dir.getAbsolutePath();
+ String os = System.getProperty("os.name");
+ String cmd;
+ boolean isWindows = false;
+ if (os.equals(MAC_OS_X))
+ cmd = OPEN;
+ else if (os.startsWith(WINDOWS)) {
+ cmd = EXPLORER;
+ isWindows = true;
+ } else
+ // Assume Unix - best option is gnome-open
+ cmd = GNOME_OPEN;
+
+ String[] cmdArray = new String[2];
+ cmdArray[0] = cmd;
+ cmdArray[1] = path;
+ try {
+ Process exec = getRuntime().exec(cmdArray);
+ Thread.sleep(300);
+ exec.getErrorStream().close();
+ exec.getInputStream().close();
+ exec.getOutputStream().close();
+ exec.waitFor();
+ if (exec.exitValue() == 0 || isWindows && exec.exitValue() == 1)
+ // explorer.exe thinks 1 means success
+ return;
+ logger.warn("Exit value from " + cmd + " " + path + ": " + exec.exitValue());
+ } catch (Exception ex) {
+ logger.warn("Could not call " + cmd + " " + path, ex);
+ }
+ // Fall-back - just show a dialogue with the path
+ showInputDialog(getMainWindow(), "Copy path from below:", title,
+ INFORMATION_MESSAGE, null, null, path);
+ }
+
+ public void setApplicationConfiguration(ApplicationConfiguration applicationConfiguration) {
+ this.applicationConfiguration = applicationConfiguration;
+ }
+}