/* * Bibliothek - DockingFrames * Library built on Java/Swing, allows the user to "drag and drop" * panels containing any Swing-Component the developer likes to add. * * Copyright (C) 2007 Benjamin Sigg * * This library 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 library 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Benjamin Sigg * benjamin_sigg@gmx.ch * CH - Switzerland */ package bibliothek.gui.dock.action.view; import java.util.HashMap; import java.util.Map; import javax.swing.*; import bibliothek.gui.DockTheme; import bibliothek.gui.Dockable; import bibliothek.gui.dock.action.*; import bibliothek.gui.dock.action.actions.SeparatorAction; import bibliothek.gui.dock.themes.basic.action.*; import bibliothek.gui.dock.themes.basic.action.buttons.BasicMiniButton; import bibliothek.gui.dock.themes.basic.action.buttons.DropDownMiniButton; import bibliothek.gui.dock.themes.basic.action.buttons.MiniButton; import bibliothek.gui.dock.themes.basic.action.dropdown.ButtonDropDownHandler; import bibliothek.gui.dock.themes.basic.action.dropdown.DropDownViewItem; import bibliothek.gui.dock.themes.basic.action.dropdown.SelectableDropDownHandler; import bibliothek.gui.dock.themes.basic.action.dropdown.SubDropDownHandler; import bibliothek.gui.dock.themes.basic.action.menu.*; import bibliothek.gui.dock.title.DockTitle; import bibliothek.util.container.Tuple; /** * The <code>ActionViewConverter</code> transforms {@link DockAction DockActions} into * views like buttons or menu-items.<br> * Every application has a set <code>DockAction</code>s. In order to create a view for an * action, the {@link ActionType} of the <code>DockAction</code> must be known. The * {@link ActionType} tells how the action normally behaves. Some types are * already defined, for example the {@link ActionType#BUTTON} behaves like a * button: once the action is triggered, it does something, and later the action * can be triggered again. There are several <code>DockAction</code>s which act like * a button, but their internal organization differs a lot.<br> * On the other hand, every application has a set of platforms which want to * display a <code>DockAction</code>. A platform might be a popup-menu, or one of the * many {@link DockTitle}s. Since some platforms need the same * visualization of <code>DockAction</code>s (i.e. a popup-menu and a normal menu both need * menu-items), the platforms are grouped. Every group is identified by a * {@link ViewTarget}. There are already some <code>ViewTarget</code>s defined, i.e. the * {@link ViewTarget#TITLE} is used for the group of <code>DockTitle</code>s.<br> * The <code>ActionViewConverter</code> must known how to create a view for all possible * pairs of {@link ActionType}s and {@link ViewTarget}s. In order to do so, he has a set * of {@link ViewGenerator ViewGenerators}. Each <code>ViewGenerator</code> is used to handle * one pair of <code>ActionType</code> and <code>ViewTarget</code>.<br> * The <code>ActionViewConverter</code> has three slots for each pair. There can be a * <code>ViewGenerator</code> in every slot. The slots have different priority and meaning. * Whenever a <code>ViewGenerator</code> for a pair is needed, the slots are searched for the * first non-<code>null</code> value with the highest priority. The meaning * of the three slots are: * <ul> * <li>Client: slot that might be filled by client-code. This slot has the * highest priority and will be used whenever possible.</li> * <li>Theme: slot that might be filled by a {@link DockTheme}. This slot * is only used if the client-slot is empty.</li> * <li>Default: the slot that will be used if the other two slots are empty</li> * </ul> * <br> * <b>Note:</b> if a client creates new <code>ActionType</code>s or new <code>ViewTarget</code>s, he has * to provide the <code>ViewGenerator</code>s for all new possible pairs. That includes pairs * where one partner is a predefined <code>ActionType</code> or <code>ViewTarget</code>. * @author Benjamin Sigg */ public class ActionViewConverter { /** the converters known to this ActionViewConverter */ private Map<Tuple<ActionType<?>, ViewTarget<?>>, Entry<?,?>> converters = new HashMap<Tuple<ActionType<?>, ViewTarget<?>>, Entry<?,?>>(); /** * Creates a new ActionViewConverter */ public ActionViewConverter(){ // Converter for menu putDefault( ActionType.BUTTON, ViewTarget.MENU, new ViewGenerator<ButtonDockAction, MenuViewItem<JComponent>>(){ public MenuViewItem<JComponent> create( ActionViewConverter converter, ButtonDockAction action, Dockable dockable ){ return new ButtonMenuHandler( action, dockable ); } }); putDefault( ActionType.CHECK, ViewTarget.MENU, new ViewGenerator<SelectableDockAction, MenuViewItem<JComponent>>(){ public MenuViewItem<JComponent> create( ActionViewConverter converter, SelectableDockAction action, Dockable dockable ){ return new SelectableMenuHandler( action, dockable, new JCheckBoxMenuItem() ); } }); putDefault( ActionType.MENU, ViewTarget.MENU, new ViewGenerator<MenuDockAction, MenuViewItem<JComponent>>(){ public MenuViewItem<JComponent> create( ActionViewConverter converter, MenuDockAction action, Dockable dockable ){ return new MenuMenuHandler( action, dockable ); } }); putDefault( ActionType.RADIO, ViewTarget.MENU, new ViewGenerator<SelectableDockAction, MenuViewItem<JComponent>>(){ public MenuViewItem<JComponent> create( ActionViewConverter converter, SelectableDockAction action, Dockable dockable ){ return new SelectableMenuHandler( action, dockable, new JRadioButtonMenuItem() ); } }); putDefault( ActionType.SEPARATOR, ViewTarget.MENU, new ViewGenerator<SeparatorAction, MenuViewItem<JComponent>>(){ public MenuViewItem<JComponent> create( ActionViewConverter converter, SeparatorAction action, Dockable dockable ){ if( action.shouldDisplay( ViewTarget.MENU )) return new BasicSeparatorHandler( new JPopupMenu.Separator(), action ); else return null; } }); putDefault( ActionType.DROP_DOWN, ViewTarget.MENU, new ViewGenerator<DropDownAction, MenuViewItem<JComponent>>(){ public MenuViewItem<JComponent> create( ActionViewConverter converter, DropDownAction action, Dockable dockable ){ return new DropDownMenuHandler( action, dockable ); } }); // Converter for title putDefault( ActionType.BUTTON, ViewTarget.TITLE, new ViewGenerator<ButtonDockAction, BasicTitleViewItem<JComponent>>(){ public BasicTitleViewItem<JComponent> create( ActionViewConverter converter, ButtonDockAction action, Dockable dockable ){ BasicButtonHandler handler = new BasicButtonHandler( action, dockable ); MiniButton<BasicButtonModel> button = new BasicMiniButton( handler, handler ); handler.setModel( button.getModel() ); return handler; } }); putDefault( ActionType.CHECK, ViewTarget.TITLE, new ViewGenerator<SelectableDockAction, BasicTitleViewItem<JComponent>>(){ public BasicTitleViewItem<JComponent> create( ActionViewConverter converter, SelectableDockAction action, Dockable dockable ){ BasicSelectableHandler.Check handler = new BasicSelectableHandler.Check( action, dockable ); MiniButton<BasicButtonModel> button = new BasicMiniButton( handler, handler ); handler.setModel( button.getModel() ); return handler; } }); putDefault( ActionType.MENU, ViewTarget.TITLE, new ViewGenerator<MenuDockAction, BasicTitleViewItem<JComponent>>(){ public BasicTitleViewItem<JComponent> create( ActionViewConverter converter, MenuDockAction action, Dockable dockable ){ BasicMenuHandler handler = new BasicMenuHandler( action, dockable ); MiniButton<BasicButtonModel> button = new BasicMiniButton( handler, handler ); handler.setModel( button.getModel() ); return handler; } }); putDefault( ActionType.RADIO, ViewTarget.TITLE, new ViewGenerator<SelectableDockAction, BasicTitleViewItem<JComponent>>(){ public BasicTitleViewItem<JComponent> create( ActionViewConverter converter, SelectableDockAction action, Dockable dockable ){ BasicSelectableHandler.Radio handler = new BasicSelectableHandler.Radio( action, dockable ); MiniButton<BasicButtonModel> button = new BasicMiniButton( handler, handler ); handler.setModel( button.getModel() ); return handler; } }); putDefault( ActionType.SEPARATOR, ViewTarget.TITLE, new ViewGenerator<SeparatorAction, BasicTitleViewItem<JComponent>>(){ public BasicTitleViewItem<JComponent> create( ActionViewConverter converter, SeparatorAction action, Dockable dockable ){ if( action.shouldDisplay( ViewTarget.TITLE )) return new BasicSeparatorHandler( new JSeparator(), action ); else return null; } }); putDefault( ActionType.DROP_DOWN, ViewTarget.TITLE, new ViewGenerator<DropDownAction, BasicTitleViewItem<JComponent>>(){ public BasicTitleViewItem<JComponent> create( ActionViewConverter converter, DropDownAction action, Dockable dockable ){ BasicDropDownButtonHandler handler = new BasicDropDownButtonHandler( action, dockable ); DropDownMiniButton button = new DropDownMiniButton( handler ); handler.setModel( button.getModel() ); return handler; } }); // Converter for drop down buttons putDefault( ActionType.BUTTON, ViewTarget.DROP_DOWN, new ViewGenerator<ButtonDockAction, DropDownViewItem>(){ public DropDownViewItem create( ActionViewConverter converter, ButtonDockAction action, Dockable dockable ){ return new ButtonDropDownHandler( action, dockable, new JMenuItem() ); } }); putDefault( ActionType.CHECK, ViewTarget.DROP_DOWN, new ViewGenerator<SelectableDockAction, DropDownViewItem>(){ public DropDownViewItem create( ActionViewConverter converter, SelectableDockAction action, Dockable dockable ){ return new SelectableDropDownHandler( action, dockable, new JCheckBoxMenuItem() ); } }); putDefault( ActionType.RADIO, ViewTarget.DROP_DOWN, new ViewGenerator<SelectableDockAction, DropDownViewItem>(){ public DropDownViewItem create( ActionViewConverter converter, SelectableDockAction action, Dockable dockable ){ return new SelectableDropDownHandler( action, dockable, new JRadioButtonMenuItem() ); } }); putDefault( ActionType.SEPARATOR, ViewTarget.DROP_DOWN, new ViewGenerator<SeparatorAction, DropDownViewItem>(){ public DropDownViewItem create( ActionViewConverter converter, SeparatorAction action, Dockable dockable ){ if( action.shouldDisplay( ViewTarget.DROP_DOWN )) return new SubDropDownHandler( new BasicSeparatorHandler( new JPopupMenu.Separator(), action )); else return null; } }); putDefault( ActionType.DROP_DOWN, ViewTarget.DROP_DOWN, new ViewGenerator<DropDownAction, DropDownViewItem>(){ public DropDownViewItem create( ActionViewConverter converter, DropDownAction action, Dockable dockable ){ return new SubDropDownHandler( action.createView( ViewTarget.MENU, converter, dockable ) ); } }); putDefault( ActionType.MENU, ViewTarget.DROP_DOWN, new ViewGenerator<MenuDockAction, DropDownViewItem>(){ public DropDownViewItem create( ActionViewConverter converter, MenuDockAction action, Dockable dockable ){ return new SubDropDownHandler( action.createView( ViewTarget.MENU, converter, dockable ) ); } }); } /** * Registers a new {@link ViewGenerator} to this ActionViewConverter. The * generator will have the high priority. * @param <A> the type of view created by the converter * @param <D> the type of action needed as input for the converter * @param action the type of actions needed as input * @param target the platform for which <code>converter</code> creates output * @param generator the generator to store, may be <code>null</code> */ public <A, D extends DockAction> void putClient( ActionType<D> action, ViewTarget<A> target, ViewGenerator<D,A> generator ){ if( action == null ) throw new IllegalArgumentException( "Action must not be null" ); if( target == null ) throw new IllegalArgumentException( "Target must not be null" ); Entry<D,A> entry = getEntry( action, target ); entry.clientGenerator = generator; } /** * Registers a new {@link ViewGenerator} to this ActionViewConverter. The * generator will have the normal priority. * @param <A> the type of view created by the converter * @param <D> the type of action needed as input for the converter * @param action the type of actions needed as input * @param target the platform for which <code>converter</code> creates output * @param generator the generator to store, may be <code>null</code> */ public <A, D extends DockAction> void putTheme( ActionType<D> action, ViewTarget<A> target, ViewGenerator<D,A> generator ){ if( action == null ) throw new IllegalArgumentException( "Action must not be null" ); if( target == null ) throw new IllegalArgumentException( "Target must not be null" ); Entry<D,A> entry = getEntry( action, target ); entry.themeGenerator = generator; } /** * Registers a new {@link ViewGenerator} to this ActionViewConverter. The * generator will have the low priority. * @param <A> the type of view created by the converter * @param <D> the type of action needed as input for the converter * @param action the type of actions needed as input * @param target the platform for which <code>converter</code> creates output * @param generator the generator to store, may be <code>null</code> */ public <A, D extends DockAction> void putDefault( ActionType<D> action, ViewTarget<A> target, ViewGenerator<D,A> generator ){ if( action == null ) throw new IllegalArgumentException( "Action must not be null" ); if( target == null ) throw new IllegalArgumentException( "Target must not be null" ); Entry<D,A> entry = getEntry( action, target ); entry.defaultGenerator = generator; } /** * Creates and sets up a new view. This method does nothing more than * calling the method {@link DockAction#createView(ViewTarget, ActionViewConverter, Dockable) createView} * of {@link DockAction}. * @param <A> the type of the view * @param action the action for which a view is created * @param target the target platform, where the view will be shown * @param dockable the Dockable for which the action is used * @return the new view or <code>null</code> if nothing should be shown * @throws IllegalArgumentException if an unknown argument is used */ public <A> A createView( DockAction action, ViewTarget<A> target, Dockable dockable ){ return action.createView( target, this, dockable ); } /** * Creates and sets up a new view. * @param <A> the type of the view * @param <D> the type of action to convert * @param type the type of action * @param action the action for which a view is created * @param target the target platform, where the view will be shown * @param dockable the Dockable for which the action is used * @return the new view or <code>null</code> if nothing should be shown * @throws IllegalArgumentException if an unknown argument is used */ public <A, D extends DockAction> A createView( ActionType<D> type, D action, ViewTarget<A> target, Dockable dockable ){ ViewGenerator<D,A> converter = getConverter( type, target ); if( converter == null ) throw new IllegalArgumentException( "That combination is not known: " + type + " " + target ); return converter.create( this, action, dockable ); } /** * Searches a converter for the given <code>action</code> and <code>target</code>. * @param <A> the type that the converter will produce * @param <D> the type of action needed as input * @param action the action that will be transformed * @param target the target platform * @return the converter or <code>null</code> if no converter is found */ protected <A, D extends DockAction> ViewGenerator<D,A> getConverter( ActionType<D> action, ViewTarget<? super A> target ){ Entry<D, A> entry = getEntry( action, target ); if( entry.clientGenerator != null ) return entry.clientGenerator; if( entry.themeGenerator != null ) return entry.themeGenerator; return entry.defaultGenerator; } /** * Searches an entry for the given <code>action</code> and <code>target</code>. * @param <A> the type that the converter will produce * @param <D> the type of action needed as input * @param action the action that will be transformed * @param target the target platform * @return the converter or <code>null</code> if no converter is found */ @SuppressWarnings( "unchecked" ) private <A, D extends DockAction> Entry<D, A> getEntry( ActionType<D> action, ViewTarget<? super A> target ){ Entry<?,?> result = converters.get( new Tuple<ActionType<?>, ViewTarget<?>>( action, target )); if( result == null ){ result = new Entry<D,A>(); converters.put( new Tuple<ActionType<?>, ViewTarget<?>>( action, target ), result ); } return (Entry<D, A>)result; } /** * A set of generators. * @author Benjamin Sigg * * @param <D> the actions needed as input for the generators * @param <A> the output view of the generators */ private static class Entry<D extends DockAction, A>{ /** generator provided by the client */ public ViewGenerator<D, A> clientGenerator; /** generator provided by a {@link DockTheme} */ public ViewGenerator<D, A> themeGenerator; /** default generator, used if no other generator is known */ public ViewGenerator<D, A> defaultGenerator; } }