/* * 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) 2008 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; import java.awt.Color; import java.awt.Graphics; import java.awt.Insets; import java.awt.Point; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.border.BevelBorder; import javax.swing.event.MouseInputAdapter; 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.ThemeManager; 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.AbstractDockTitle; import bibliothek.gui.dock.title.ActivityDockTitleEvent; import bibliothek.gui.dock.title.DockTitleEvent; 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 static bibliothek.gui.dock.station.flap.button.ButtonContent.*; /** * This title changes its border whenever the active-state changes. * @author Benjamin Sigg */ @ColorCodes({ "title.flap.active", "title.flap.active.text", "title.flap.inactive", "title.flap.inactive.text", "title.flap.selected", "title.flap.selected.text", "title.flap.active.knob.highlight", "title.flap.active.knob.shadow", "title.flap.inactive.knob.highlight", "title.flap.inactive.knob.shadow", "title.flap.selected.knob.highlight", "title.flap.selected.knob.shadow" }) public class BasicButtonDockTitle extends AbstractDockTitle { /** amount of space required to paint the knob */ protected final int KNOB_SIZE = 10; /** whether the mouse is currently pressed or not */ private boolean mousePressed = false; /** whether this button is selected on its owner or not */ private boolean selected = false; /** tells what items to paint */ protected 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 ); } } }; /** 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; /** the color used for the background when active */ private TitleColor activeColor = new BasicTitleColor( "title.flap.active", null ); /** the color used for the foreground when active */ private TitleColor activeTextColor = new BasicTitleColor( "title.flap.active.text", null ); /** the color used for background when inactive */ private TitleColor inactiveColor = new BasicTitleColor( "title.flap.inactive", null ); /** the color used for foreground when inactive */ private TitleColor inactiveTextColor = new BasicTitleColor( "title.flap.inactive.text", null ); /** the color used for background when selected */ private TitleColor selectedColor = new BasicTitleColor( "title.flap.selected", null ); /** the color used for foreground when selected */ private TitleColor selectedTextColor = new BasicTitleColor( "title.flap.selected.text", null ); /** the color used for the bright side of the knob if active */ private TitleColor knobActiveHighlightColor = new BasicTitleColor( "title.flap.active.knob.highlight", null ); /** the color used for the dark side of the knob if active */ private TitleColor knobActiveShadowColor = new BasicTitleColor( "title.flap.active.knob.shadow", null ); /** the color used for the bright side of the knob if inactive */ private TitleColor knobInactiveHighlightColor = new BasicTitleColor( "title.flap.inactive.knob.highlight", null ); /** the color used for the dark side of the knob if inactive */ private TitleColor knobInactiveShadowColor = new BasicTitleColor( "title.flap.inactive.knob.shadow", null ); /** the color used for the bright side of the knob if selected */ private TitleColor knobSelectedHighlightColor = new BasicTitleColor( "title.flap.selected.knob.highlight", null ); /** the color used for the dark side of the knob if selected */ private TitleColor knobSelectedShadowColor = new BasicTitleColor( "title.flap.selected.knob.shadow", null ); /** keeps all the {@link DockActionSource}s that have to be shown on this title */ private MultiDockActionSource allActionsSource = new MultiDockActionSource(); /** * Constructs a new title * @param dockable the {@link Dockable} for which this title is created * @param origin the version which was used to create this title */ public BasicButtonDockTitle( Dockable dockable, DockTitleVersion origin ) { super(); behavior = new ButtonContentValue( new ButtonContent( TRUE, TRUE, IF_DOCKABLE, IF_STATION, TRUE, TRUE ) ){ @Override protected void propertyChanged(){ updateContent(); } }; init( dockable, origin, false ); changeBorder(); addMouseInputListener( new MouseInputAdapter(){ @Override public void mousePressed( MouseEvent e ){ mousePressed = (e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK ) != 0; changeBorder(); } @Override public void mouseReleased( MouseEvent e ){ mousePressed = (e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK ) != 0; changeBorder(); } }); addColor( activeColor ); addColor( activeTextColor ); addColor( inactiveColor ); addColor( inactiveTextColor ); addColor( selectedColor ); addColor( selectedTextColor ); addColor( knobActiveHighlightColor ); addColor( knobActiveShadowColor ); addColor( knobInactiveHighlightColor ); addColor( knobInactiveShadowColor ); addColor( knobSelectedHighlightColor ); addColor( knobSelectedShadowColor ); 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 ); allActionsSource.setSeparateSources( true ); updateContent(); } @Override public void bind(){ DockTitleVersion origin = getOrigin(); if( origin != null ){ connector.setProperties( origin.getController() ); behavior.setProperties( origin.getController() ); } behavior.setDockable( getDockable() ); super.bind(); } @Override public void unbind(){ connector.setProperties( (DockController)null ); behavior.setProperties( (DockController)null ); behavior.setDockable( null ); super.unbind(); } private void updateContent(){ updateIcon(); updateText(); updateActionSource( false ); if( behavior.isShowActions() || behavior.isShowChildren() ){ setShowMiniButtons( true ); } else{ setShowMiniButtons( false ); } revalidate(); repaint(); } @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 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 void updateIcon() { if( behavior.isShowIcon() ) super.updateIcon(); else setIcon( null ); } @Override protected void updateText() { if( behavior.isShowText() ){ super.updateText(); } else{ setText( "" ); } } @Override protected void paintForeground( Graphics g, JComponent component ){ // paint icon (if there is any) paintIcon( g, component ); // paint knob (if there is any) 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( knobActiveHighlightColor, knobInactiveHighlightColor, knobSelectedHighlightColor ) ); g.drawLine( x, y1, x, y2 ); g.drawLine( x, y1, x+2, y1 ); g.setColor( getColor( knobActiveShadowColor, knobInactiveShadowColor, knobSelectedShadowColor ) ); g.drawLine( x, y2, x+2, y2 ); g.drawLine( x+2, y1+1, x+2, y2 ); } else{ int y = insets.top - KNOB_SIZE + 3; int x1 = insets.left + 3; int x2 = getWidth() - insets.right - 4; g.setColor( getColor( knobActiveHighlightColor, knobInactiveHighlightColor, knobSelectedHighlightColor ) ); g.drawLine( x1, y, x2, y ); g.drawLine( x1, y, x1, y+2 ); g.setColor( getColor( knobActiveShadowColor, knobInactiveShadowColor, knobSelectedShadowColor ) ); g.drawLine( x1+1, y+2, x2, y+2 ); g.drawLine( x2, y, x2, y+2 ); } } } @Override public void setActive( boolean active ) { if( active != isActive() ){ super.setActive(active); selected = active; updateLayout(); } } @Override public void changed( DockTitleEvent event ) { if( event instanceof ActivityDockTitleEvent ){ ActivityDockTitleEvent activity = (ActivityDockTitleEvent)event; super.setActive( activity.isActive() ); selected = activity.isActive() || activity.isPreferred(); updateLayout(); } else{ super.changed( event ); } } @Override public Point getPopupLocation( Point click, boolean popupTrigger ){ if( popupTrigger ) return click; return null; } /** * Tells whether the mouse is currently pressed or not. * @return <code>true</code> if the mouse is pressed */ protected boolean isMousePressed(){ return mousePressed; } /** * Whether this button is selected or not. * @return <code>true</code> if selected, <code>false</code> otherwise */ public boolean isSelected() { return selected; } /** * Updates various elements of this title such that the current state * is met. */ protected void updateLayout(){ changeBorder(); updateColors(); updateFonts(); } /** * Exchanges the current border. */ protected void changeBorder(){ if( selected && mousePressed ){ setBorder( ThemeManager.BORDER_MODIFIER + ".title.button.selected.pressed", BorderFactory.createBevelBorder( BevelBorder.RAISED )); } else if( selected ){ setBorder( ThemeManager.BORDER_MODIFIER + ".title.button.selected", BorderFactory.createBevelBorder( BevelBorder.LOWERED )); } else if( mousePressed ){ setBorder( ThemeManager.BORDER_MODIFIER + ".title.button.pressed", BorderFactory.createBevelBorder( BevelBorder.LOWERED )); } else{ setBorder( ThemeManager.BORDER_MODIFIER + ".title.button", BorderFactory.createBevelBorder( BevelBorder.RAISED )); } } /** * Updates the colors of this title. */ protected void updateColors(){ if( isActive() ){ setBackground( activeColor.color() ); setForeground( activeTextColor.color() ); } else if( selected ){ setBackground( selectedColor.color() ); setForeground( selectedTextColor.color() ); } else{ setBackground( inactiveColor.color() ); setForeground( inactiveTextColor.color() ); } } private Color getColor( TitleColor active, TitleColor inactive, TitleColor selected ){ if( isActive() ){ return active.color(); } else if( this.selected ){ return selected.color(); } else{ return inactive.color(); } } /** * Gets the color that is used as foreground if the title is focused. * @return the color, might be <code>null</code> */ public Color getActiveTextColor(){ return activeTextColor.color(); } /** * Sets the color that is used as foreground if the title is focused. * @param color the new color, <code>null</code> to reset the property */ public void setActiveTextColor( Color color ){ activeTextColor.setValue( color ); } /** * Gets the color that is used as background if the title is focused. * @return the color, might be <code>null</code> */ public Color getActiveColor(){ return activeColor.color(); } /** * Sets the color that is used as background if the title is focused. * @param color the new color, <code>null</code> to reset the property */ public void setActiveColor( Color color ){ activeColor.setValue( color ); } /** * Gets the color that is used as foreground if the title is selected. * @return the color, might be <code>null</code> */ public Color getSelectedTextColor(){ return selectedTextColor.color(); } /** * Sets the color that is used as foreground if the title is selected. * @param color the new color, <code>null</code> to reset the property */ public void setSelectedTextColor( Color color ){ selectedTextColor.setValue( color ); } /** * Gets the color that is used as background if the title is selected. * @return the color, might be <code>null</code> */ public Color getSelectedColor(){ return selectedColor.color(); } /** * Sets the color that is used as background if the title is selected. * @param color the new color, <code>null</code> to reset the property */ public void setSelectedColor( Color color ){ selectedColor.setValue( color ); } /** * Gets the color that is used as foreground * @return the color, might be <code>null</code> */ public Color getInactiveTextColor(){ return inactiveTextColor.color(); } /** * Sets the color that is used as foreground * @param color the new color, <code>null</code> to reset the property */ public void setInactiveTextColor( Color color ){ inactiveTextColor.setValue( color ); } /** * Gets the color that is used as background * @return the color, might be <code>null</code> */ public Color getInactiveColor(){ return inactiveColor.color(); } /** * Sets the color that is used as background * @param color the new color, <code>null</code> to reset the property */ public void setInactiveColor( Color color ){ inactiveColor.setValue( color ); } /** * A implementation of {@link TitleColor} that calls <code>repaint</code> * when the color changes. * @author Benjamin Sigg */ private class BasicTitleColor extends TitleColor{ /** * Creates a new color * @param id the id of the color * @param backup a backup color */ public BasicTitleColor( String id, Color backup ){ super( id, TitleColor.KIND_FLAP_BUTTON_COLOR, BasicButtonDockTitle.this, backup ); } @Override protected void changed( Color oldColor, Color newColor ) { updateColors(); } } }