/*
* 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
* benjamin_sigg@gmx.ch
* CH - Switzerland
*/
package bibliothek.gui.dock.util.icon;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.Icon;
import bibliothek.gui.DockController;
import bibliothek.gui.DockUI;
import bibliothek.gui.dock.station.stack.tab.TabMenuDockIcon;
import bibliothek.gui.dock.themes.icon.TabMenuOverflowIconBridge;
import bibliothek.gui.dock.util.DockUtilities;
import bibliothek.gui.dock.util.UIProperties;
import bibliothek.gui.dock.util.UIScheme;
import bibliothek.gui.dock.util.UISchemeEvent;
import bibliothek.util.Path;
/**
* This default implementation of an {@link UIScheme} for {@link Icon}s reads an ini-file
* which consists of "key=icon-path" pairs, and loads all the icons described in that
* ini file when needed.
* @author Benjamin Sigg
*/
public class DefaultIconScheme extends AbstractIconScheme {
private Map<String, Icon> icons;
private Map<Path, DockIconBridge> bridges;
/**
* A helper class describing a source for icons
* @author Benjamin Sigg
*/
public static class IconResource{
private String fileName;
private String path;
private ClassLoader loader;
/**
* Creates a new source for icons
* @param fileName the name of the file that contains "key=path" values
* @param path the prefix of the paths that are found in the file
* @param loader the {@link ClassLoader} to load the icons
*/
public IconResource( String fileName, String path, ClassLoader loader ){
if( fileName == null ){
throw new IllegalArgumentException( "fileName must not be null" );
}
this.fileName = fileName;
this.path = path;
if( loader == null ){
loader = DefaultIconScheme.class.getClassLoader();
}
this.loader = loader;
}
/**
* Gets the name of the file with the "key=path" values.
* @return the file name
*/
public String getFileName(){
return fileName;
}
/**
* Gets the prefix of the paths that are found in the icons file.
* @return the prefix, can be <code>null</code>
*/
public String getPath(){
return path;
}
/**
* Gets the {@link ClassLoader} which should be used to load files
* @return the class loader
*/
public ClassLoader getLoader(){
return loader;
}
}
/**
* Creates a new scheme loading first the contents of the ini file <code>file</code> and
* then the icons that are found by analyzing the content of <code>file</code>.<br>
* If no file is found, then this scheme just remains empty.
* @param file the file to read
* @param controller the {@link DockController} in whose realm this scheme will be used
*/
public DefaultIconScheme( String file, DockController controller ){
this( file, DefaultIconScheme.class.getClassLoader(), controller );
}
/**
* Creates a new scheme loading first the contents of the ini file <code>file</code> and
* then the icons that are found by analyzing the content of <code>file</code>.<br>
* If no file is found, then this scheme just remains empty.
* @param file the file to read
* @param loader the {@link ClassLoader} whose {@link ClassLoader#getResource(String)} method
* will be used to load any files
* @param controller the {@link DockController} in whose realm this scheme will be used
*/
public DefaultIconScheme( String file, ClassLoader loader, DockController controller ){
this( controller, new IconResource( file, null, loader ));
}
/**
* Creates a new scheme loading icons from all the specified resources.
* @param controller the {@link DockController} in whose realm this scheme will be used
* @param resources a list of files with "key=path" lines telling key and path of the icons to load. If a key
* appears more than once, then the last occurrence of the key wins
*/
public DefaultIconScheme( DockController controller, IconResource... resources ){
super( controller );
icons = new HashMap<String, Icon>();
bridges = new HashMap<Path, DockIconBridge>();
initHardcoded();
for( int i = resources.length-1; i >= 0; i-- ){
icons.putAll( DockUtilities.loadIcons( resources[i].getFileName(), resources[i].getPath(), icons.keySet(), resources[i].getLoader() ) );
}
}
/**
* Called by the constructor of this class, initializes some hard coded icons.
*/
protected void initHardcoded(){
setBridge( TabMenuDockIcon.KIND_TAB_MENU, new TabMenuOverflowIconBridge() );
setIcon( DockUI.OVERFLOW_MENU_ICON, new Icon(){
public int getIconHeight(){
return 7;
}
public int getIconWidth(){
return 9;
}
public void paintIcon( Component c, Graphics g, int x, int y ){
g = g.create();
((Graphics2D)g).setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g.setColor( c.getForeground() );
g.fillPolygon(
new int[]{ x + 1, x + 8, x + 4 },
new int[]{ y + 1, y + 1, y + 6 }, 3 );
g.dispose();
}
});
}
public DockIconBridge getBridge( Path name, UIProperties<Icon, DockIcon, DockIconBridge> properties ){
return bridges.get( name );
}
public Icon getResource( String name, UIProperties<Icon, DockIcon, DockIconBridge> properties ){
return icons.get( name );
}
/**
* Changes the icon with name <code>name</code> to <code>icon</code>. Please note that if
* <code>name</code> was {@link #link(bibliothek.gui.dock.util.PropertyKey, String) linked}, the newly
* set value may be overridden again.
* @param name the unique key of the icon
* @param icon the new icon, can be <code>null</code>
*/
public void setIcon( String name, Icon icon ){
changed( name, icon );
}
/**
* Sets the {@link DockIconBridge} with type <code>type</code>.
* @param type the unique identifier of the type that should be handled by the new bridge
* @param bridge the new bridge or <code>null</code>
*/
public void setBridge( final Path type, DockIconBridge bridge ){
if( bridge == null ){
bridges.remove( type );
}
else{
bridges.put( type, bridge );
}
fire( new UISchemeEvent<Icon, DockIcon, DockIconBridge>(){
public Collection<Path> changedBridges( Set<Path> names ){
List<Path> result = new ArrayList<Path>();
if( names == null || names.contains( type )){
result.add( type );
}
return result;
}
public Collection<String> changedResources( Set<String> names ){
return Collections.emptySet();
}
public UIScheme<Icon, DockIcon, DockIconBridge> getScheme(){
return DefaultIconScheme.this;
}
});
}
@Override
protected void changed( final String id, Icon icon ){
if( icon == null ){
icons.remove( id );
}
else{
icons.put( id, icon );
}
fire( new UISchemeEvent<Icon, DockIcon, DockIconBridge>(){
public UIScheme<Icon, DockIcon, DockIconBridge> getScheme(){
return DefaultIconScheme.this;
}
public Collection<String> changedResources( Set<String> names ){
List<String> list = new ArrayList<String>( 1 );
if( names == null || names.contains( id )){
list.add( id );
}
return list;
}
public Collection<Path> changedBridges( Set<Path> names ){
return Collections.emptySet();
}
});
}
}