/* * 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.extension.gui.dock.preference; import java.util.ArrayList; import java.util.List; import bibliothek.gui.DockController; import bibliothek.util.Path; import bibliothek.util.PathCombiner; /** * A preference model that envelops other models and uses their preferences. This model * makes use of a {@link PathCombiner} to create unique identifiers for all preferences of its * sub-models. * @author Benjamin Sigg */ public class MergedPreferenceModel extends AbstractPreferenceModel{ private List<Model> models = new ArrayList<Model>(); /** how to create the result of {@link #getPath(int)} */ private PathCombiner combiner = PathCombiner.UNIQUE; private PreferenceModelListener listener = new PreferenceModelListener(){ public void preferenceAdded( PreferenceModel model, int beginIndex, int endIndex ){ int begin = indexAt( model, beginIndex ); int end = begin + (endIndex - beginIndex); firePreferenceAdded( begin, end ); } public void preferenceChanged( PreferenceModel model, int beginIndex, int endIndex ){ int begin = indexAt( model, beginIndex ); int end = begin + (endIndex - beginIndex); firePreferenceChanged( begin, end ); } public void preferenceRemoved( PreferenceModel model, int beginIndex, int endIndex ){ int begin = indexAt( model, beginIndex ); int end = begin + (endIndex - beginIndex); firePreferenceRemoved( begin, end ); } }; /** * Creates a new model * @param controller the controller in whose realm this model is used */ public MergedPreferenceModel( DockController controller ){ super( controller ); } /** * Creates a new path. * @param combiner tells how to combine the path of a model and of * its preferences in {@link #getPath(int)}. Not <code>null</code>. * @param controller the controller in whose realm this model is used */ public MergedPreferenceModel( PathCombiner combiner, DockController controller ){ super( controller ); if( combiner == null ) throw new IllegalArgumentException( "combiner must not be null" ); this.combiner = combiner; } /** * Adds <code>model</code> at the end of this model. * @param model the additional model * @param path the location of the new model * @see #insert(int, PreferenceModel, Path) */ public void add( PreferenceModel model, Path path ){ insert( models.size(), model, path ); } /** * Inserts a new submodel into this model. * @param index the location of the new model * @param model the new model * @param path the path of the new model, the path must be unique compared * to the paths of any other model. */ public void insert( int index, PreferenceModel model, Path path ){ if( this == model ) throw new IllegalArgumentException( "model must not be this" ); for( Model check : models ){ if( check.model == model ) throw new IllegalArgumentException( "can't add a model twice" ); if( check.path.equals( path )) throw new IllegalArgumentException( "there is already a model with the path " + path ); } Model insert = new Model(); insert.model = model; insert.path = path; models.add( index, insert ); if( hasListeners() ) model.addPreferenceModelListener( listener ); int size = model.getSize(); if( size > 0 ){ int begin = 0; for( int i = 0; i < index; i++ ){ begin += models.get( i ).model.getSize(); } firePreferenceAdded( begin, begin+size-1 ); } } /** * Removes the <code>index'th</code> model of this merged model. * @param index the location of a child */ public void remove( int index ){ Model model = models.remove( index ); if( hasListeners() ) model.model.removePreferenceModelListener( listener ); int size = model.model.getSize(); if( size > 0 ){ int begin = 0; for( int i = 0; i < index; i++ ){ begin += models.get( i ).model.getSize(); } firePreferenceRemoved( begin, begin+size-1 ); } } /** * Removes <code>model</code> from this merged model. * @param model the model to remove */ public void remove( MergedPreferenceModel model ){ int index = indexOf( model ); if( index >= 0 ) remove( index ); } /** * Removes the model with the path <code>path</code>. * @param path some path */ public void remove( Path path ){ int index = indexOf( path ); if( index >= 0 ) remove( index ); } /** * Removes all children from this model. */ public void clear(){ int size = getSize(); if( hasListeners() ){ for( Model model : models ){ model.model.removePreferenceModelListener( listener ); } } models.clear(); if( size > 0 ){ firePreferenceRemoved( 0, size-1 ); } } /** * Gets the index of <code>model</code>. * @param model some model to search * @return the index or -1 if not found */ public int indexOf( PreferenceModel model ){ int i = 0; for( Model check : models ){ if( check.model == model ) return i; i++; } return -1; } /** * Gets the index of <code>path</code>. * @param path the path of some model * @return the index or -1 if not found */ public int indexOf( Path path ){ int i = 0; for( Model check : models ){ if( check.path.equals( path ) ) return i; i++; } return -1; } /** * Gets the <code>index</code>'th model of this merged model. * @param index some index * @return a child of this model */ public PreferenceModel getModel( int index ){ return models.get( index ).model; } /** * Gets the model which was stored using the key <code>path</code>. * @param path the path of the model * @return the model or <code>null</code> if not found */ public PreferenceModel getModel( Path path ){ int index = indexOf( path ); if( index < 0 ){ return null; } return getModel( index ); } @Override public void read() { for( Model model : models ){ model.model.read(); } } @Override public void write() { for( Model model : models ){ model.model.write(); } } @Override public void addPreferenceModelListener( PreferenceModelListener listener ) { boolean hadListeners = hasListeners(); super.addPreferenceModelListener( listener ); if( hasListeners() && !hadListeners ){ for( Model model : models ){ model.model.addPreferenceModelListener( this.listener ); } } } @Override public void removePreferenceModelListener( PreferenceModelListener listener ) { boolean hadListeners = hasListeners(); super.removePreferenceModelListener( listener ); if( !hasListeners() && hadListeners ){ for( Model model : models ){ model.model.removePreferenceModelListener( this.listener ); } } } public int getSize() { int size = 0; for( Model model : models ){ size += model.model.getSize(); } return size; } public String getLabel( int index ) { Index local = indexAt( index ); if( local == null ) throw new ArrayIndexOutOfBoundsException( index ); return local.model.model.getLabel( local.index ); } @Override public String getDescription( int index ) { Index local = indexAt( index ); if( local == null ) throw new ArrayIndexOutOfBoundsException( index ); return local.model.model.getDescription( local.index ); } public Object getValueInfo(int index) { Index local = indexAt( index ); if( local == null ) throw new ArrayIndexOutOfBoundsException( index ); return local.model.model.getValueInfo( local.index ); } public Object getValue( int index ) { Index local = indexAt( index ); if( local == null ) throw new ArrayIndexOutOfBoundsException( index ); return local.model.model.getValue( local.index ); } public void setValue( int index, Object value ) { Index local = indexAt( index ); if( local == null ) throw new ArrayIndexOutOfBoundsException( index ); local.model.model.setValue( local.index, value ); } public Path getTypePath( int index ) { Index local = indexAt( index ); if( local == null ) throw new ArrayIndexOutOfBoundsException( index ); return local.model.model.getTypePath( local.index ); } public Path getPath( int index ) { Index local = indexAt( index ); if( local == null ) throw new ArrayIndexOutOfBoundsException( index ); return combiner.combine( local.model.path, local.model.model.getPath( local.index ) ); } @Override public boolean isNatural( int index ) { Index local = indexAt( index ); if( local == null ) throw new ArrayIndexOutOfBoundsException( index ); return local.model.model.isNatural( local.index ); } @Override public void setValueNatural( int index ) { Index local = indexAt( index ); if( local == null ) throw new ArrayIndexOutOfBoundsException( index ); local.model.model.setValueNatural( local.index ); } /** * Gets the model and the index that <code>globalIndex</code> describe in * this model. * @param globalIndex some global index * @return the local index */ protected Index indexAt( int globalIndex ){ for( Model model : models ){ int size = model.model.getSize(); if( globalIndex < size ) return new Index( model, globalIndex ); else globalIndex -= size; } return null; } /** * Finds the global index if <code>index</code> is part of <code>model</code>. * @param model a child of this model * @param index an index in <code>model</code> * @return the global index */ protected int indexAt( PreferenceModel model, int index ){ for( Model check : models ){ if( check.model == model ) return index; index += check.model.getSize(); } return index; } /** * Describes an index in one of the children of a {@link MergedPreferenceModel}. * @author Benjamin Sigg */ protected static class Index{ public Model model; public int index; public Index( Model model, int index ){ this.model = model; this.index = index; } } /** * A sub-model entry of a {@link MergedPreferenceModel}. * @author Benjamin Sigg */ private static class Model{ public PreferenceModel model; public Path path; } }