bibliothek.gui.dock.themes.ThemeManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of docking-frames-core Show documentation
Show all versions of docking-frames-core Show documentation
${project.name} is base or core library
The newest version!
/*
* 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) 2010 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
* [email protected]
* CH - Switzerland
*/
package bibliothek.gui.dock.themes;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import bibliothek.gui.DockController;
import bibliothek.gui.DockStation;
import bibliothek.gui.DockTheme;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.control.DockRegister;
import bibliothek.gui.dock.control.focus.DefaultFocusRequest;
import bibliothek.gui.dock.event.UIListener;
import bibliothek.gui.dock.station.Combiner;
import bibliothek.gui.dock.station.DisplayerFactory;
import bibliothek.gui.dock.station.StationPaint;
import bibliothek.gui.dock.station.span.SpanFactory;
import bibliothek.gui.dock.themes.basic.action.buttons.MiniButton;
import bibliothek.gui.dock.themes.border.BorderModifier;
import bibliothek.gui.dock.title.DockTitleManager;
import bibliothek.gui.dock.util.BackgroundPaint;
import bibliothek.gui.dock.util.DockProperties;
import bibliothek.gui.dock.util.Priority;
import bibliothek.gui.dock.util.PropertyKey;
import bibliothek.gui.dock.util.TypedPropertyUIScheme;
import bibliothek.gui.dock.util.TypedUIProperties;
import bibliothek.gui.dock.util.UIBridge;
import bibliothek.gui.dock.util.UIValue;
import bibliothek.gui.dock.util.extension.ExtensionName;
import bibliothek.util.ClientOnly;
import bibliothek.util.FrameworkOnly;
import bibliothek.util.Path;
/**
* The {@link ThemeManager} is responsible for collecting properties of the current {@link DockTheme} and redistribute them. The
* {@link ThemeManager} provides facilities for clients to modify and override properties of a theme without the need
* to access or change the {@link DockTheme} itself.
* @author Benjamin Sigg
*/
public class ThemeManager extends TypedUIProperties{
/** Identifier for a factory that creates {@link StationPaint}s. */
public static final Type STATION_PAINT_TYPE = new Type( "StationPaint" );
/** unique identifier for the basic {@link StationPaint} */
public static final String STATION_PAINT = "dock.paint";
/** Identifier for the type {@link Combiner} */
public static final Type COMBINER_TYPE = new Type( "Combiner" );
/** unique identifier for the basic {@link Combiner} */
public static final String COMBINER = "dock.combiner";
/** Identifier for the type {@link DisplayerFactory} */
public static final Type DISPLAYER_FACTORY_TYPE = new Type( "DisplayerFactory" );
/** unique identifier for the basic {@link DisplayerFactory} */
public static final String DISPLAYER_FACTORY = "dock.displayer";
/** Identifier for the type {@link BackgroundPaint} */
public static final Type BACKGROUND_PAINT_TYPE = new Type( "BackgroundPaint" );
/** unique identifier for the basic {@link BackgroundPaint} */
public static final String BACKGROUND_PAINT = "dock.background";
/** Identifier for the type {@link BorderModifier} */
public static final Type BORDER_MODIFIER_TYPE = new Type( "BorderModifier" );
/** unique identifier for the basic {@link BorderModifier} */
public static final String BORDER_MODIFIER = "dock.border";
/** Identifier for the type {@link SpanFactory} */
public static final Type SPAN_FACTORY_TYPE = new Type( "SpanFactory" );
/** unique identifier for the basic {@link SpanFactory} */
public static final String SPAN_FACTORY = "dock.spanFactory";
/** the controller owning the manager */
private DockController controller;
/** the current theme */
private DockTheme theme;
/** Listeners observing the ui */
private List uiListeners = new ArrayList();
/** a listener that is added to the {@link UIManager} and gets notified when the {@link LookAndFeel} changes */
private PropertyChangeListener lookAndFeelObserver = new PropertyChangeListener(){
public void propertyChange( PropertyChangeEvent evt ) {
if( "lookAndFeel".equals( evt.getPropertyName() )){
updateUI();
}
}
};
/** items to transfer directly from {@link DockProperties} to this
*/
private TypedPropertyUIScheme transfers;
/**
* Creates a new object
* @param controller the owner of this manager, not null
*/
public ThemeManager( DockController controller ){
super( controller );
if( controller == null ){
throw new IllegalArgumentException( "controller must not be null" );
}
this.controller = controller;
UIManager.addPropertyChangeListener( lookAndFeelObserver );
transfers = new TypedPropertyUIScheme( controller.getProperties() );
setScheme( Priority.THEME, transfers );
}
/**
* Initializes this managere, must be called exactly once.
*/
public void init(){
registerTypes();
link();
}
private void registerTypes(){
registerType( STATION_PAINT_TYPE );
registerType( COMBINER_TYPE );
registerType( DISPLAYER_FACTORY_TYPE );
registerType( BACKGROUND_PAINT_TYPE );
registerType( BORDER_MODIFIER_TYPE );
registerType( SPAN_FACTORY_TYPE );
}
private void link(){
link( DockTheme.STATION_PAINT, STATION_PAINT_TYPE, STATION_PAINT );
link( DockTheme.COMBINER, COMBINER_TYPE, COMBINER );
link( DockTheme.DISPLAYER_FACTORY, DISPLAYER_FACTORY_TYPE, DISPLAYER_FACTORY );
link( DockTheme.BACKGROUND_PAINT, BACKGROUND_PAINT_TYPE, BACKGROUND_PAINT );
link( DockTheme.BORDER_MODIFIER, BORDER_MODIFIER_TYPE, BORDER_MODIFIER );
link( DockTheme.SPAN_FACTORY, SPAN_FACTORY_TYPE, SPAN_FACTORY );
}
/**
* Destroys this manager and releases resources.
*/
@FrameworkOnly
public void kill(){
theme.uninstall( controller );
UIManager.removePropertyChangeListener( lookAndFeelObserver );
}
/**
* Creates a link between the property source
and the entry id
on the
* level {@link Priority#THEME}.
* @param the kind of property to read
* @param the kind of entry to write
* @param source the key of the property to read
* @param type the type of the entry to write
* @param id the identifier of the entry to write
*/
public void link( PropertyKey source, Type type, String id ){
transfers.link( source, type, id );
}
/**
* Disables a link between a property and the entry id
.
* @param the type
* @param type the type of the entry
* @param id the identifier of the entry to unlink
* @see #link(PropertyKey, TypedUIProperties.Type, String)
*/
public void unlink( Type type, String id ){
transfers.unlink( type, id );
}
/**
* Adds an {@link UIListener} to this manager, the listener gets
* notified when the graphical user interface needs an update because
* the {@link LookAndFeel} changed.
* @param listener the new listener
*/
public void addUIListener( UIListener listener ){
uiListeners.add( listener );
}
/**
* Removes a listener from this manager.
* @param listener the listener to remove
*/
public void removeUIListener( UIListener listener ){
uiListeners.remove( listener );
}
/**
* Gets all the available {@link UIListener}s.
* @return the list of listeners
*/
protected UIListener[] uiListeners(){
return uiListeners.toArray( new UIListener[ uiListeners.size() ]);
}
/**
* Informs all registered {@link UIListener}s that the user interface
* needs an update because the {@link LookAndFeel} changed.
* @see #addUIListener(UIListener)
* @see #removeUIListener(UIListener)
*/
public void updateUI(){
for( UIListener listener : uiListeners() )
listener.updateUI( controller );
}
/**
* Gets the current theme
* @return the theme
*/
public DockTheme getTheme() {
return theme;
}
/**
* Sets the theme of this manager. This method fires events on registered {@link UIListener}s
* and ensures that all {@link DockStation}s receive the update
* @param theme the new theme
*/
public void setTheme( DockTheme theme ){
if( theme == null )
throw new IllegalArgumentException( "Theme must not be null" );
if( this.theme != theme ){
for( UIListener listener : uiListeners() )
listener.themeWillChange( controller, this.theme, theme );
DockRegister register = controller.getRegister();
DockTheme oldTheme = this.theme;
Dockable focused = null;
try{
register.setStalled( true );
focused = controller.getFocusedDockable();
if( this.theme != null )
this.theme.uninstall( controller );
this.theme = theme;
ExtensionName name = new ExtensionName(
DockThemeExtension.DOCK_THEME_EXTENSION, DockThemeExtension.class, DockThemeExtension.THEME_PARAMETER, theme );
List extensions = controller.getExtensions().load( name );
theme.install( controller, extensions.toArray( new DockThemeExtension[ extensions.size() ] ) );
controller.getDockTitleManager().registerTheme( DockTitleManager.THEME_FACTORY_ID, theme.getTitleFactory( controller ) );
// update only those station which are registered to this controller
for( DockStation station : register.listDockStations() ){
if( station.getController() == controller ){
station.updateTheme();
}
}
}
finally{
register.setStalled( false );
}
controller.setFocusedDockable( new DefaultFocusRequest( focused, null, true ));
for( UIListener listener : uiListeners() )
listener.themeChanged( controller, oldTheme, theme );
}
}
/**
* Sets an algorithm to paint in the overlay panel of {@link DockStation}s. Possible
* identifiers can be, but are not restricted to:
*
* - {@value #STATION_PAINT}.flap
* - {@value #STATION_PAINT}.screen
* - {@value #STATION_PAINT}.split
* - {@value #STATION_PAINT}.stack
*
* @param id the identifier of the stations that should use vallue
* @param value the new algorithm or null
*/
public void setStationPaint( String id, StationPaint value ){
put( Priority.CLIENT, id, STATION_PAINT_TYPE, value );
}
/**
* Sets the {@link UIBridge} that will transfer properties to those {@link UIValue}s whose kind is either
* kind
or a child of kind
.
* @param kind the kind of {@link UIValue} bridge
will handle
* @param bridge the new bridge or null
*/
public void setStationPaintBridge( Path kind, UIBridge> bridge ){
if( bridge == null ){
unpublish( Priority.CLIENT, kind, STATION_PAINT_TYPE );
}
else{
publish( Priority.CLIENT, kind, STATION_PAINT_TYPE, bridge );
}
}
/**
* Sets a strategy how two {@link Dockable}s can be merged into a new {@link Dockable}.
* Valid identifiers may be, but are not restricted to:
*
* - {@value #COMBINER}.flap
* - {@value #COMBINER}.screen
* - {@value #COMBINER}.split
* - {@value #COMBINER}.stack
*
* @param id the identifier of the item that uses value
* @param value the new strategy, can be null
*/
public void setCombiner( String id, Combiner value ){
put( Priority.CLIENT, id, COMBINER_TYPE, value );
}
/**
* Sets the {@link UIBridge} that will transfer properties to those {@link UIValue}s whose kind is either
* kind
or a child of kind
.
* @param kind the kind of {@link UIValue} bridge
will handle
* @param bridge the new bridge or null
*/
public void setCombinerBridge( Path kind, UIBridge> bridge ){
if( bridge == null ){
unpublish( Priority.CLIENT, kind, COMBINER_TYPE );
}
else{
publish( Priority.CLIENT, kind, COMBINER_TYPE, bridge );
}
}
/**
* Sets a strategy how to display {@link Dockable}s on a {@link DockStation}. Valid
* identifiers can be, but are not restricted to:
*
* - {@value #DISPLAYER_FACTORY}.flap
* - {@value #DISPLAYER_FACTORY}.screen
* - {@value #DISPLAYER_FACTORY}.split
* - {@value #DISPLAYER_FACTORY}.stack
*
* @param id the identifier of the item that uses value
* @param value the new strategy, can be null
*/
public void setDisplayerFactory( String id, DisplayerFactory value ){
put( Priority.CLIENT, id, DISPLAYER_FACTORY_TYPE, value );
}
/**
* Sets the {@link UIBridge} that will transfer properties to those {@link UIValue}s whose kind is either
* kind
or a child of kind
.
* @param kind the kind of {@link UIValue} bridge
will handle
* @param bridge the new bridge or null
*/
public void setDisplayerFactoryBridge( Path kind, UIBridge> bridge ){
if( bridge == null ){
unpublish( Priority.CLIENT, kind, DISPLAYER_FACTORY_TYPE );
}
else{
publish( Priority.CLIENT, kind, DISPLAYER_FACTORY_TYPE, bridge );
}
}
/**
* Sets a strategy to tell how to animate empty spaces when drag and dropping a {@link Dockable}.
* Valid identifiers can be, but are not restricted to:
*
* - {@value #DISPLAYER_FACTORY}.flap
* - {@value #DISPLAYER_FACTORY}.split
* - {@value #DISPLAYER_FACTORY}.stack (currently not used)
* - {@value #DISPLAYER_FACTORY}.screen (currently not used)
*
* @param id the identifier of the item that uses value
* @param value the new strategy, can be null
*/
public void setSpanFactory( String id, SpanFactory value ){
put( Priority.CLIENT, id, SPAN_FACTORY_TYPE, value );
}
/**
* Sets the {@link UIBridge} that will transfer properties to those {@link UIValue}s whose kind is either
* kind
or a child of kind
.
* @param kind the kind of {@link UIValue} bridge
will handle
* @param bridge the new bridge or null
*/
public void setSpanFactoryBridge( Path kind, UIBridge> bridge ){
if( bridge == null ){
unpublish( Priority.CLIENT, kind, SPAN_FACTORY_TYPE );
}
else{
publish( Priority.CLIENT, kind, SPAN_FACTORY_TYPE, bridge );
}
}
/**
* Sets an algorithm that is used to paint the background of items which register an {@link UIValue} with
* an identifier of id
. Valid identifier can be, but are not restricted to:
*
* - {@value #BACKGROUND_PAINT}.action
* - {@value #BACKGROUND_PAINT}.displayer
* - {@value #BACKGROUND_PAINT}.dockable
* - {@value #BACKGROUND_PAINT}.station.flap
* - {@value #BACKGROUND_PAINT}.station.flap.window
* - {@value #BACKGROUND_PAINT}.station.screen
* - {@value #BACKGROUND_PAINT}.station.split
* - {@value #BACKGROUND_PAINT}.station.stack
* - {@value #BACKGROUND_PAINT}.tabPane
* - {@value #BACKGROUND_PAINT}.tabPane.child.menu
* - {@value #BACKGROUND_PAINT}.tabPane.child.tab
* - {@value #BACKGROUND_PAINT}.title
*
* @param id the identifier of the items that should use value
* @param value the new background algorithm, can be null
*/
@ClientOnly
public void setBackgroundPaint( String id, BackgroundPaint value ){
put( Priority.CLIENT, id, BACKGROUND_PAINT_TYPE, value );
}
/**
* Sets the {@link UIBridge} that will transfer properties to those {@link UIValue}s whose kind is either
* kind
or a child of kind
.
* @param kind the kind of {@link UIValue} bridge
will handle
* @param bridge the new bridge or null
*/
public void setBackgroundPaintBridge( Path kind, UIBridge> bridge ){
if( bridge == null ){
unpublish( Priority.CLIENT, kind, BACKGROUND_PAINT_TYPE );
}
else{
publish( Priority.CLIENT, kind, BACKGROUND_PAINT_TYPE, bridge );
}
}
/**
* Sets a strategy that is used to modify the border of various components.
* Valid identifiers can be, but are not restricted to:
*
* - {@link MiniButton#BORDER_KEY_NORMAL}
* - {@link MiniButton#BORDER_KEY_NORMAL_SELECTED}
* - {@link MiniButton#BORDER_KEY_MOUSE_OVER}
* - {@link MiniButton#BORDER_KEY_MOUSE_OVER_SELECTED}
* - {@link MiniButton#BORDER_KEY_MOUSE_PRESSED}
* - {@link MiniButton#BORDER_KEY_MOUSE_PRESSED_SELECTED}
* - {@value #BORDER_MODIFIER}.displayer.basic.base
* - {@value #BORDER_MODIFIER}.displayer.basic.content
* - {@value #BORDER_MODIFIER}.displayer.bubble
* - {@value #BORDER_MODIFIER}.displayer.eclipse.no_title.out
* - {@value #BORDER_MODIFIER}.displayer.eclipse.no_title.in
* - {@value #BORDER_MODIFIER}.displayer.eclipse
* - {@value #BORDER_MODIFIER}.displayer.eclipse.content
* - {@value #BORDER_MODIFIER}.screen.window
* - {@value #BORDER_MODIFIER}.stack.eclipse
* - {@value #BORDER_MODIFIER}.stack.eclipse.content
* - {@value #BORDER_MODIFIER}.title.button
* - {@value #BORDER_MODIFIER}.title.button.flat
* - {@value #BORDER_MODIFIER}.title.button.flat.hover
* - {@value #BORDER_MODIFIER}.title.button.flat.pressed
* - {@value #BORDER_MODIFIER}.title.button.flat.selected
* - {@value #BORDER_MODIFIER}.title.button.flat.selected.hover
* - {@value #BORDER_MODIFIER}.title.button.flat.selected.pressed
* - {@value #BORDER_MODIFIER}.title.button.selected
* - {@value #BORDER_MODIFIER}.title.button.pressed
* - {@value #BORDER_MODIFIER}.title.button.selected.pressed
* - {@value #BORDER_MODIFIER}.title.eclipse.button.flat
* - {@value #BORDER_MODIFIER}.title.flat
* - {@value #BORDER_MODIFIER}.title.station.basic
* - {@value #BORDER_MODIFIER}.title.tab
*
* @param id the identifier of the items that should use modifier
* @param modifier the new strategy, can be null
*/
public void setBorderModifier( String id, BorderModifier modifier ){
put( Priority.CLIENT, id, BORDER_MODIFIER_TYPE, modifier );
}
/**
* Sets the {@link UIBridge} that will transfer properties to those {@link UIValue}s whose kind is either
* kind
or a child of kind
.
* @param kind the kind of {@link UIValue} bridge
will handle
* @param bridge the new bridge or null
*/
public void setBorderModifierBridge( Path kind, UIBridge> bridge ){
if( bridge == null ){
unpublish( Priority.CLIENT, kind, BORDER_MODIFIER_TYPE );
}
else{
publish( Priority.CLIENT, kind, BORDER_MODIFIER_TYPE, bridge );
}
}
}