/* * 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.themes.basic.action; import java.awt.Color; import java.awt.Component; import java.util.*; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; import bibliothek.gui.DockController; import bibliothek.gui.Dockable; import bibliothek.gui.dock.action.ActionContentModifier; import bibliothek.gui.dock.action.ButtonContentFilter; import bibliothek.gui.dock.action.ButtonContentFilterListener; import bibliothek.gui.dock.action.DockAction; import bibliothek.gui.dock.action.DockActionSource; import bibliothek.gui.dock.action.DropDownAction; import bibliothek.gui.dock.action.StandardDockAction; import bibliothek.gui.dock.action.dropdown.DropDownFilter; import bibliothek.gui.dock.action.dropdown.DropDownView; import bibliothek.gui.dock.action.view.ViewTarget; import bibliothek.gui.dock.event.DockActionSourceListener; import bibliothek.gui.dock.event.DropDownActionListener; import bibliothek.gui.dock.event.StandardDockActionListener; import bibliothek.gui.dock.themes.basic.action.dropdown.DropDownIcon; import bibliothek.gui.dock.themes.basic.action.dropdown.DropDownViewItem; import bibliothek.gui.dock.title.DockTitle.Orientation; import bibliothek.gui.dock.util.PropertyValue; /** * A handler connecting a {@link DropDownAction} with a {@link BasicDropDownButtonModel} * and its view. Clients should call the method {@link #setModel(BasicButtonModel)} to * connect model and handler. * @author Benjamin Sigg */ public class BasicDropDownButtonHandler extends AbstractBasicHandler<DropDownAction, BasicDropDownButtonModel> implements BasicDropDownButtonTrigger, BasicTitleViewItem<JComponent> { /** the current source of child-actions */ private DockActionSource source; /** a listener to the model */ private Listener listener = new Listener(); /** the currently selected item, can be <code>null</code> */ private DropDownItemHandle selection; /** the currently known actions */ private List<DockAction> actions = new ArrayList<DockAction>(); /** the views for the items of {@link #actions}. Not all actions have a view. */ private Map<DockAction, DropDownItemHandle> items = new HashMap<DockAction, DropDownItemHandle>(); /** the menu to show when the button is clicked */ private JPopupMenu menu = new JPopupMenu(); /** connection between current selection and filter */ private SelectionView selectionView = new SelectionView(); /** connection between filter and button */ private ButtonView buttonView = new ButtonView(); /** filters the properties of the action and its selection */ private DropDownFilter filter; /** the icon that indicates the button for opening the popup menu */ private DropDownIcon dropDownIcon; /** a filter telling whether the text of the action should be forwarded */ private PropertyValue<ButtonContentFilter> buttonContentFilter = new PropertyValue<ButtonContentFilter>( DockAction.BUTTON_CONTENT_FILTER ){ @Override protected void valueChanged( ButtonContentFilter oldValue, ButtonContentFilter newValue ){ if( isBound() ){ if( oldValue != null ){ oldValue.removeListener( buttonContentFilterListener ); oldValue.uninstall( getDockable(), getAction() ); } if( newValue != null ){ newValue.addListener( buttonContentFilterListener ); newValue.install( getDockable(), getAction() ); } } buttonView.updateText(); } }; /** this listener is added to the current {@link #buttonContentFilter} */ private ButtonContentFilterListener buttonContentFilterListener = new ButtonContentFilterListener(){ public void showTextChanged( ButtonContentFilter filter, Dockable dockable, DockAction action ){ if( (action == null || action == getAction()) && (dockable == null || dockable == getDockable())){ buttonView.updateText(); } } }; /** * Creates a new handler. * @param action the action to observe * @param dockable the element for which the <code>action</code> is shown */ public BasicDropDownButtonHandler( DropDownAction action, Dockable dockable ){ super( action, dockable ); } public void bind(){ DropDownAction action = getAction(); Dockable dockable = getDockable(); if( dropDownIcon != null ){ if( !dropDownIcon.isInitialized() ){ dropDownIcon.init( getDockable(), getAction(), this ); } dropDownIcon.setController( dockable.getController() ); } action.bind( dockable ); filter = action.getFilter( dockable ).createView( action, dockable, buttonView ); filter.bind(); source = action.getSubActions( dockable ); for( int i = 0, n = source.getDockActionCount(); i<n; i++ ){ DockAction sub = source.getDockAction( i ); add( i, sub ); } reset(); selection = items.get( action.getSelection( dockable ) ); if( selection != null ) selection.getView().setView( selectionView ); action.addDropDownActionListener( listener ); action.addDockActionListener( listener ); source.addDockActionSourceListener( listener ); getModel().setEnabled( action.isEnabled( dockable ) ); buttonContentFilter.setProperties( dockable.getController() ); super.bind(); buttonContentFilter.getValue().addListener( buttonContentFilterListener ); } public void unbind(){ DropDownAction action = getAction(); Dockable dockable = getDockable(); if( dropDownIcon != null ){ dropDownIcon.setController( null ); } action.removeDockActionListener( listener ); action.removeDropDownActionListener( listener ); source.removeDockActionSourceListener( listener ); for( int i = actions.size()-1; i >= 0; i-- ) remove( i ); menu.removeAll(); if( selection != null ){ selection.getView().setView( null ); } filter.unbind(); filter = null; action.unbind( dockable ); source = null; selection = null; items.clear(); actions.clear(); buttonContentFilter.getValue().removeListener( buttonContentFilterListener ); super.unbind(); buttonContentFilter.setProperties( (DockController)null ); getModel().setDockableRepresentative( null ); } /** * Gets an icon that can be used to represent an area that opens the popup menu when clicked. * @return an icon, not <code>null</code> */ public Icon getDropDownIcon(){ ensureDropDownIcon(); return dropDownIcon; } /** * Gets a disabled version of {@link #getDropDownIcon()}. * @return the icon, not <code>null</code> */ public Icon getDisabledDropDownIcon(){ ensureDropDownIcon(); return dropDownIcon.getDisabledIcon(); } private void ensureDropDownIcon(){ if( dropDownIcon == null ){ dropDownIcon = new DropDownIcon(); if( isBound() ){ dropDownIcon.init( getDockable(), getAction(), this ); dropDownIcon.setController( getDockable().getController() ); } } } public void setBackground( Color background ) { Component item = getItem(); if( item != null ) item.setBackground( background ); } public void setForeground( Color foreground ) { Component item = getItem(); if( item != null ) item.setForeground( foreground ); } public void setOrientation( Orientation orientation ){ getModel().setOrientation( orientation ); } /** * Adds an action into the list of all known actions. * @param index the location of the action * @param action the new action */ private void add( int index, DockAction action ){ Dockable dockable = getDockable(); actions.add( index, action ); DropDownViewItem item = action.createView( ViewTarget.DROP_DOWN, dockable.getController().getActionViewConverter(), dockable ); if( item != null ){ DropDownItemHandle entry = new DropDownItemHandle( action, item, dockable, getAction() ); entry.bind(); items.put( action, entry ); if( item.getItem() != null ){ menu.add( item.getItem() ); } } } /** * Removes an action from the list of all known actions. * @param index the location of the action */ private void remove( int index ){ DockAction action = actions.remove( index ); DropDownItemHandle item = items.remove( action ); if( item != null ){ if( item.getView().getItem() != null ){ menu.remove( item.getView().getItem() ); } item.unbind(); } } public JComponent getItem(){ return getModel().getOwner(); } public void triggered(){ BasicDropDownButtonModel model = getModel(); if( model.isMouseOverDropDown() ) popupTriggered(); else{ if( selection == null || !model.isSelectionEnabled() || !selection.getView().isTriggerable( true ) ) popupTriggered(); else{ if( selection.getView().isTriggerable( true ) ){ selection.getView().triggered(); } } } } /** * Shows the popup menu */ public void popupTriggered(){ BasicDropDownButtonModel model = getModel(); JComponent button = model.getOwner(); if( model.getOrientation().isHorizontal() ){ menu.show( button, 0, button.getHeight() ); } else{ menu.show( button, button.getWidth(), 0 ); } } /** * Update the look and feel of the menu */ public void updateUI(){ if( menu != null ) SwingUtilities.updateComponentTreeUI( menu ); } /** * Gets the view which contains information about the currently selected * action. * @return the information */ protected ButtonView getButtonView(){ return buttonView; } /** * Sets all values of the {@link #filter} to <code>null</code>. */ protected void reset(){ getModel().setSelectionEnabled( false ); if( filter != null ){ filter.clearIcons(); filter.setEnabled( true ); filter.setSelected( false ); filter.setText( null ); filter.setTooltip( null ); filter.setDockableRepresentation( null ); } update(); } /** * Updates the {@link #filter}. This might change some contents of the button. */ protected void update(){ if( filter != null ) filter.update( selection == null ? null : selection.getView() ); } /** * A set of properties which can be set by the selected action. * @author Benjamin Sigg */ protected class SelectionView implements DropDownView{ public void setEnabled( boolean enabled ){ getModel().setSelectionEnabled( enabled ); filter.setEnabled( enabled ); update(); } public ActionContentModifier[] getIconContexts(){ return filter.getIconContexts(); } public void clearIcons(){ filter.clearIcons(); update(); } public void setIcon( ActionContentModifier modifier, Icon icon ){ filter.setIcon( modifier, icon ); update(); } public void setSelected( boolean selected ){ filter.setSelected( selected ); update(); } public void setText( String text ){ filter.setText( text ); update(); } public void setTooltip( String tooltip ){ filter.setTooltip( tooltip ); update(); } public void setDockableRepresentation( Dockable dockable ){ filter.setDockableRepresentation( dockable ); update(); } } /** * A view that sends all values directly to the button. * @author Benjamin Sigg */ protected class ButtonView implements DropDownView{ private String text; public void setIcon( ActionContentModifier modifier, Icon icon ){ getModel().setIcon( modifier, icon ); } public ActionContentModifier[] getIconContexts(){ return getModel().getIconContexts(); } public void clearIcons(){ getModel().clearIcons(); } public void setEnabled( boolean enabled ){ getModel().setSelectionEnabled( enabled ); } public void setSelected( boolean selected ){ getModel().setSelected( selected ); } public void setText( String text ){ this.text = text; updateText(); } /** * Updates the text that is shown by the button, respects the {@link BasicDropDownButtonHandler#buttonContentFilter}. */ public void updateText(){ if( buttonContentFilter.getValue().showText( getDockable(), getAction() )){ getModel().setText( text ); } else{ getModel().setText( null ); } } public void setTooltip( String tooltip ){ getModel().setToolTipText( tooltip ); } public void setDockableRepresentation( Dockable dockable ){ if( isBound() ){ getModel().setDockableRepresentative( dockable ); } } } /** * A listener to the action that is handled by this handler * @author Benjamin Sigg */ private class Listener implements StandardDockActionListener, DropDownActionListener, DockActionSourceListener{ public void actionEnabledChanged( StandardDockAction action, Set<Dockable> dockables ){ Dockable dockable = getDockable(); if( dockables.contains( dockable )) getModel().setEnabled( action.isEnabled( dockable ) ); } public void actionRepresentativeChanged( StandardDockAction action, Set<Dockable> dockables ){ if( dockables.contains( getDockable() )) update(); } public void actionIconChanged( StandardDockAction action, ActionContentModifier modifier, Set<Dockable> dockables ){ if( dockables.contains( getDockable() )) update(); } public void actionTextChanged( StandardDockAction action, Set<Dockable> dockables ){ if( dockables.contains( getDockable() )) update(); } public void actionTooltipTextChanged( StandardDockAction action, Set<Dockable> dockables ){ if( dockables.contains( getDockable() )) update(); } public void selectionChanged( DropDownAction action, Set<Dockable> dockables, DockAction newSelection ){ if( selection != null ) selection.getView().setView( null ); reset(); selection = items.get( newSelection ); if( selection != null ) selection.getView().setView( selectionView ); getModel().changed(); } public void actionsAdded( DockActionSource source, int firstIndex, int lastIndex ){ for( int i = firstIndex; i <= lastIndex; i++ ) add( i, source.getDockAction( i )); } public void actionsRemoved( DockActionSource source, int firstIndex, int lastIndex ){ for( int i = lastIndex; i >= firstIndex; i-- ) remove( i ); } } }