| /******************************************************************************* |
| * 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(); |
| } |
| } |