All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bibliothek.gui.dock.themes.ThemeManager Maven / Gradle / Ivy

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 ); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy