/* * 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.extension.gui.dock.theme.bubble; import static bibliothek.gui.dock.station.flap.button.ButtonContent.IF_DOCKABLE; import static bibliothek.gui.dock.station.flap.button.ButtonContent.IF_STATION; import static bibliothek.gui.dock.station.flap.button.ButtonContent.TRUE; import java.awt.Color; import java.awt.Graphics; import java.awt.Insets; import java.awt.Point; import javax.swing.JComponent; import bibliothek.gui.DockController; import bibliothek.gui.Dockable; import bibliothek.gui.dock.FlapDockStation; import bibliothek.gui.dock.action.DockAction; import bibliothek.gui.dock.action.DockActionSource; import bibliothek.gui.dock.action.FilteredDockActionSource; import bibliothek.gui.dock.action.MultiDockActionSource; import bibliothek.gui.dock.action.StationChildrenActionSource; import bibliothek.gui.dock.station.flap.button.ButtonContent; import bibliothek.gui.dock.station.flap.button.ButtonContentFilter; import bibliothek.gui.dock.themes.basic.action.buttons.ButtonContentValue; import bibliothek.gui.dock.themes.color.TitleColor; import bibliothek.gui.dock.themes.font.TitleFont; import bibliothek.gui.dock.title.ActivityDockTitleEvent; import bibliothek.gui.dock.title.DockTitle; import bibliothek.gui.dock.title.DockTitleEvent; import bibliothek.gui.dock.title.DockTitleFactory; import bibliothek.gui.dock.title.DockTitleRequest; import bibliothek.gui.dock.title.DockTitleVersion; import bibliothek.gui.dock.util.PropertyValue; import bibliothek.gui.dock.util.color.ColorCodes; import bibliothek.gui.dock.util.font.DockFont; import bibliothek.util.Condition; import bibliothek.util.Path; /** * A {@link DockTitle} used for the buttons on a {@link FlapDockStation}. * @author Benjamin Sigg */ @ColorCodes({ "title.background.top.active.mouse.flap", "title.background.top.active.flap", "title.background.top.inactive.mouse.flap", "title.background.top.inactive.flap", "title.background.top.selected.mouse.flap", "title.background.top.selected.flap", "title.background.top.disabled.flap", "title.background.bottom.active.mouse.flap", "title.background.bottom.active.flap", "title.background.bottom.inactive.mouse.flap", "title.background.bottom.inactive.flap", "title.background.bottom.selected.mouse.flap", "title.background.bottom.selected.flap", "title.background.bottom.disabled.flap", "title.foreground.active.mouse.flap", "title.foreground.active.flap", "title.foreground.inactive.mouse.flap", "title.foreground.inactive.flap", "title.foreground.selected.mouse.flap", "title.foreground.selected.flap", "title.flap.active.knob.highlight", "title.flap.active.knob.shadow", "title.flap.active.mouse.knob.highlight", "title.flap.active.mouse.knob.shadow", "title.flap.inactive.knob.highlight", "title.flap.inactive.knob.shadow", "title.flap.inactive.mouse.knob.highlight", "title.flap.inactive.mouse.knob.shadow", "title.flap.selected.knob.highlight", "title.flap.selected.knob.shadow", "title.flap.selected.mouse.knob.highlight", "title.flap.selected.mouse.knob.shadow", "title.flap.disabled.knob.highlight", "title.flap.disabled.knob.shadow" }) public class BubbleButtonDockTitle extends AbstractBubbleDockTitle{ /** * A factory which creates new {@link BubbleButtonDockTitle}s. */ public static final DockTitleFactory FACTORY = new DockTitleFactory(){ public void install( DockTitleRequest request ){ // ignore } public void uninstall( DockTitleRequest request ){ // ignore } public void request( DockTitleRequest request ){ request.answer( new BubbleButtonDockTitle( request.getTarget(), request.getVersion() ) ); } }; /** amount of space required to paint the knob */ private final int KNOB_SIZE = 10; /** key for the color that is used to paint the knob */ public static final String ANIMATION_KEY_KNOB_HIGHLIGHT = "knob.highlight"; /** key for the color that is used to paint the knob */ public static final String ANIMATION_KEY_KNOB_SHADOW = "knob.shadow"; private ButtonContentValue behavior; /** tells what items to filter */ private PropertyValue<ButtonContentFilter> connector = new PropertyValue<ButtonContentFilter>( FlapDockStation.BUTTON_CONTENT_FILTER ) { protected void valueChanged( ButtonContentFilter oldValue, ButtonContentFilter newValue ){ if( behavior != null ){ updateActionSource( true ); } } }; private boolean selected = false; /** keeps all the {@link DockActionSource}s that have to be shown on this title */ private MultiDockActionSource allActionsSource = new MultiDockActionSource(); /** whether children are currently shown */ private boolean showChildren = false; /** wether actions are currently shown */ private boolean showActions = false; /** whether all actions should be painted or only a selection */ private boolean filterActions = false; /** * Creates a new title. * @param dockable the dockable for which this title will be shown * @param origin the {@link DockTitleVersion} which was used to create this title */ public BubbleButtonDockTitle( Dockable dockable, DockTitleVersion origin ) { if( origin != null ){ connector.setProperties( origin.getController() ); } behavior = new ButtonContentValue( new ButtonContent( TRUE, TRUE, IF_DOCKABLE, IF_STATION, TRUE, TRUE ) ){ @Override protected void propertyChanged(){ updateContent(); } }; init( dockable, origin, false ); allActionsSource.setSeparateSources( true ); updateContent(); } /** * Constructor that does nothing, subclasses should call {@link #init(Dockable, DockTitleVersion, boolean)} * to initialize the tile. */ protected BubbleButtonDockTitle(){ // nothing } @Override protected void init( Dockable dockable, DockTitleVersion origin, boolean showMiniButtons ) { super.init( dockable, origin, showMiniButtons ); initAnimation(); updateAnimation(); updateFonts(); } /** * Sets up the animation such that it can be started at any time. */ private void initAnimation(){ Path path = TitleColor.KIND_FLAP_BUTTON_COLOR; addColor( "title.background.top.active.mouse.flap", path, Color.RED ); addColor( "title.background.top.active.flap", path, Color.LIGHT_GRAY ); addColor( "title.background.top.inactive.mouse.flap", path, Color.BLUE ); addColor( "title.background.top.inactive.flap", path, Color.DARK_GRAY ); addColor( "title.background.top.selected.mouse.flap", path, Color.BLUE ); addColor( "title.background.top.selected.flap", path, Color.DARK_GRAY ); addColor( "title.background.top.disabled.flap", path, Color.DARK_GRAY ); addColor( "title.background.bottom.active.mouse.flap", path, Color.LIGHT_GRAY ); addColor( "title.background.bottom.active.flap", path, Color.WHITE ); addColor( "title.background.bottom.inactive.mouse.flap", path, Color.DARK_GRAY ); addColor( "title.background.bottom.inactive.flap", path, Color.BLACK ); addColor( "title.background.bottom.selected.mouse.flap", path, Color.DARK_GRAY ); addColor( "title.background.bottom.selected.flap", path, Color.BLACK ); addColor( "title.background.bottom.disabled.flap", path, Color.BLACK ); addColor( "title.foreground.active.mouse.flap", path, Color.BLACK ); addColor( "title.foreground.active.flap", path, Color.BLACK ); addColor( "title.foreground.inactive.mouse.flap", path, Color.WHITE ); addColor( "title.foreground.inactive.flap", path, Color.WHITE ); addColor( "title.foreground.selected.mouse.flap", path, Color.WHITE ); addColor( "title.foreground.selected.flap", path, Color.WHITE ); addColor( "title.flap.active.knob.highlight", path, Color.WHITE ); addColor( "title.flap.active.knob.shadow", path, Color.BLACK ); addColor( "title.flap.active.mouse.knob.highlight", path, Color.WHITE ); addColor( "title.flap.active.mouse.knob.shadow", path, Color.BLACK ); addColor( "title.flap.inactive.knob.highlight", path, Color.WHITE ); addColor( "title.flap.inactive.knob.shadow", path, Color.BLACK ); addColor( "title.flap.inactive.mouse.knob.highlight", path, Color.WHITE ); addColor( "title.flap.inactive.mouse.knob.shadow", path, Color.BLACK ); addColor( "title.flap.selected.knob.highlight", path, Color.WHITE ); addColor( "title.flap.selected.knob.shadow", path, Color.BLACK ); addColor( "title.flap.selected.mouse.knob.highlight", path, Color.WHITE ); addColor( "title.flap.selected.mouse.knob.shadow", path, Color.BLACK ); addColor( "title.flap.disabled.knob.highlight", path, Color.WHITE ); addColor( "title.flap.disabled.knob.shadow", path, Color.BLACK ); addConditionalFont( DockFont.ID_FLAP_BUTTON_ACTIVE, TitleFont.KIND_FLAP_BUTTON_FONT, new Condition(){ public boolean getState() { return isActive(); } }, null ); addConditionalFont( DockFont.ID_FLAP_BUTTON_SELECTED, TitleFont.KIND_FLAP_BUTTON_FONT, new Condition(){ public boolean getState() { return isSelected(); } }, null ); addConditionalFont( DockFont.ID_FLAP_BUTTON_INACTIVE, TitleFont.KIND_FLAP_BUTTON_FONT, new Condition(){ public boolean getState() { return !isActive(); } }, null ); } @Override public void bind(){ DockTitleVersion origin = getOrigin(); if( origin != null ){ behavior.setProperties( origin.getController() ); } behavior.setDockable( getDockable() ); super.bind(); } @Override public void unbind(){ behavior.setProperties( (DockController)null ); behavior.setDockable( null ); super.unbind(); } @Override public void changed( DockTitleEvent event ) { if( event instanceof ActivityDockTitleEvent ){ ActivityDockTitleEvent activity = (ActivityDockTitleEvent)event; selected = activity.isActive() || activity.isPreferred(); super.setActive( activity.isActive() ); updateAnimation(); updateFonts(); } else{ super.changed( event ); } } @Override public void setActive( boolean active ) { if( active != isActive() ){ super.setActive(active); selected = active; updateAnimation(); updateFonts(); } } @Override protected void updateAnimation(){ if( isDisabled() ){ String top = "title.background.top.disabled.flap"; String bottom = "title.background.bottom.disabled.flap"; String text = "title.foreground.inactive.flap"; String knobHighlight = "title.flap.disabled.knob.highlight"; String knobShadow = "title.flap.disabled.knob.shadow"; updateAnimation( ANIMATION_KEY_TEXT, text ); updateAnimation( ANIMATION_KEY_BACKGROUND_TOP, top ); updateAnimation( ANIMATION_KEY_BACKGROUND_BOTTOM, bottom ); updateAnimation( ANIMATION_KEY_KNOB_HIGHLIGHT, knobHighlight ); updateAnimation( ANIMATION_KEY_KNOB_SHADOW, knobShadow ); } else{ String postfix = ""; if( isActive() ){ if( isMouseOver() ) postfix = "active.mouse"; else postfix = "active"; } else if( isSelected() ){ if( isMouseOver() ) postfix = "selected.mouse"; else postfix = "selected"; } else{ if( isMouseOver() ) postfix = "inactive.mouse"; else postfix = "inactive"; } String top = "title.background.top." + postfix + ".flap"; String bottom = "title.background.bottom." + postfix + ".flap"; String text = "title.foreground." + postfix + ".flap"; String knobHighlight = "title.flap." + postfix + ".knob.highlight"; String knobShadow = "title.flap." + postfix + ".knob.shadow"; updateAnimation( ANIMATION_KEY_TEXT, text ); updateAnimation( ANIMATION_KEY_BACKGROUND_TOP, top ); updateAnimation( ANIMATION_KEY_BACKGROUND_BOTTOM, bottom ); updateAnimation( ANIMATION_KEY_KNOB_HIGHLIGHT, knobHighlight ); updateAnimation( ANIMATION_KEY_KNOB_SHADOW, knobShadow ); } } /** * Tells whether this title is selected, being focused implies being * selected. * @return <code>true</code> if this button is selected */ public boolean isSelected(){ return selected; } private void updateContent(){ updateIcon(); updateText(); updateActionSource( false ); if( behavior.isShowActions() || behavior.isShowChildren() ){ setShowMiniButtons( true ); } else{ setShowMiniButtons( false ); } revalidate(); repaint(); } @Override protected void updateIcon() { if( behavior.isShowIcon() ) super.updateIcon(); else setIcon( null ); } @Override protected void updateText() { if( behavior.isShowText() ) super.updateText(); else setText( "" ); } @Override protected DockActionSource getActionSourceFor( Dockable dockable ){ return allActionsSource; } private void updateActionSource( boolean force ){ boolean showChildren = behavior.isShowChildren(); boolean showActions = behavior.isShowActions(); boolean filterActions = behavior.isFilterActions(); if( force || this.showChildren != showChildren || this.showActions != showActions || this.filterActions != filterActions ){ allActionsSource.removeAll(); if( showChildren ){ allActionsSource.add( getChildrenActionSourceFor( getDockable() ) ); } if( showActions ){ if( filterActions ){ allActionsSource.add( createFilter( getDefaultActionSourceFor( getDockable() ) ) ); } else{ allActionsSource.add( getDefaultActionSourceFor( getDockable() ) ); } } this.showChildren = showChildren; this.showActions = showActions; } } /** * Creates a filter around <code>actions</code>, only the actions going through the filter * will be shown. * @param actions the actions to filter * @return the filter */ protected DockActionSource createFilter( DockActionSource actions ){ final ButtonContentFilter connector = this.connector.getValue(); return new FilteredDockActionSource( actions ){ protected boolean include( DockAction action ){ return connector.isButtonAction( action ); } }; } /** * Gets the "normal" actions for <code>dockable</code>. * @param dockable some item for which actions are required * @return the normal actions, may be a new {@link DockActionSource}, not <code>null</code> */ protected DockActionSource getDefaultActionSourceFor( Dockable dockable ){ return super.getActionSourceFor( dockable ); } /** * Gets the "special" children actions for <code>dockable</code> * @param dockable some item for which actions are required * @return the children actions, may be a new {@link DockActionSource}, not <code>null</code> */ protected DockActionSource getChildrenActionSourceFor( Dockable dockable ){ return new StationChildrenActionSource( dockable, null ); } @Override protected Insets getInnerInsets(){ Insets base = super.getInnerInsets(); if( behavior.isShowKnob() ){ if( getOrientation().isHorizontal() ){ base = new Insets( base.top, base.left + KNOB_SIZE, base.bottom, base.right ); } else{ base = new Insets( base.top + KNOB_SIZE, base.left, base.bottom, base.right ); } } return base; } @Override protected void paintForeground( Graphics g, JComponent component ){ // paint icon etc. super.paintForeground( g, component ); // paint knob if( behavior.isShowKnob() ){ Insets insets = getInnerInsets(); if( getOrientation().isHorizontal() ){ int x = insets.left - KNOB_SIZE + 3; int y1 = insets.top + 3; int y2 = getHeight() - insets.bottom - 4; g.setColor( getColor( ANIMATION_KEY_KNOB_HIGHLIGHT ) ); g.drawLine( x, y1+1, x, y2-1 ); g.drawLine( x+1, y1, x+1, y1 ); g.setColor( getColor( ANIMATION_KEY_KNOB_SHADOW ) ); g.drawLine( x+1, y2, x+1, y2 ); g.drawLine( x+2, y1+1, x+2, y2-1 ); } else{ int y = insets.top - KNOB_SIZE + 3; int x1 = insets.left + 3; int x2 = getWidth() - insets.right - 4; g.setColor( getColor( ANIMATION_KEY_KNOB_HIGHLIGHT ) ); g.drawLine( x1+1, y, x2-1, y ); g.drawLine( x1, y+1, x1, y+1 ); g.setColor( getColor( ANIMATION_KEY_KNOB_SHADOW ) ); g.drawLine( x1+1, y+2, x2-1, y+2 ); g.drawLine( x2, y+1, x2, y+2 ); } } } @Override public Point getPopupLocation( Point click, boolean popupTrigger ) { if( popupTrigger ) return click; return null; } @Override public void setOrientation( Orientation orientation ) { switch( orientation ){ case SOUTH_SIDED: case NORTH_SIDED: case FREE_HORIZONTAL: orientation = Orientation.FREE_HORIZONTAL; break; case EAST_SIDED: case WEST_SIDED: case FREE_VERTICAL: orientation = Orientation.FREE_VERTICAL; break; } super.setOrientation( orientation ); } }