/*
* 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.common.intern;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.MultipleCDockable;
import bibliothek.gui.dock.common.SingleCDockable;
import bibliothek.gui.dock.common.event.CControlListener;
import bibliothek.gui.dock.station.support.PlaceholderStrategy;
import bibliothek.gui.dock.station.support.PlaceholderStrategyListener;
import bibliothek.util.Path;
/**
* This strategy assigns a unique identifier to all {@link CDockable}s that
* are registered at a {@link CControl}.
* @author Benjamin Sigg
*/
public class CPlaceholderStrategy implements PlaceholderStrategy {
/**
* Gets the placeholder that is used for the {@link SingleCDockable} with
* unique identifier <code>id</code>.
* @param id the unique identifer of a {@link SingleCDockable}
* @return the placeholder for that dockable
*/
public static Path getSingleDockablePlaceholder( String id ){
return new Path( "dock", "single", id );
}
/**
* Gets the placeholder that is used for the {@link MultipleCDockable} with
* unique identifier <code>id</code>.
* @param id the unique identifer of a {@link MultipleCDockable}
* @return the placeholder for that dockable
*/
public static Path getMultipleDockablePlaceholder( String id ){
return new Path( "dock", "multi", id );
}
/** the owner of this strategy */
private CControl control;
/** all the listeners that are registered */
private List<PlaceholderStrategyListener> listeners = new ArrayList<PlaceholderStrategyListener>();
/** a map containing placeholders for various {@link MultipleCDockable}s */
private Map<MultipleCDockable, Path> multiplePlaceholders = new HashMap<MultipleCDockable, Path>();
private CControlListener controlListener = new CControlListener() {
public void removed( CControl control, CDockable dockable ){
if( dockable instanceof SingleCDockable ){
String id = ((SingleCDockable)dockable).getUniqueId();
Path check = getSingleDockablePlaceholder( id );
if( !isValidPlaceholder( check )){
fireInvalidated( check );
}
}
else if( dockable instanceof MultipleCDockable ){
Path check = multiplePlaceholders.remove( dockable );
if( check != null ){
if( !isValidPlaceholder( check )){
fireInvalidated( check );
}
}
}
}
public void opened( CControl control, CDockable dockable ){
// ignore
}
public void closed( CControl control, CDockable dockable ){
// ignore
}
public void added( CControl control, CDockable dockable ){
// ignore
}
};
/** placeholders of {@link MultipleCDockable}s that need to be checked */
private Set<Path> pendingChecks = null;
private CDockFrontendListener frontendListener = new CDockFrontendListener(){
public void loading( CDockFrontend frontend, CSetting setting ){
pendingChecks = new HashSet<Path>();
}
public void loaded( CDockFrontend frontend, CSetting setting ){
if( pendingChecks != null ){
Set<Path> pendingChecks = CPlaceholderStrategy.this.pendingChecks;
CPlaceholderStrategy.this.pendingChecks = null;
for( Path check : pendingChecks ){
if( !isValidPlaceholder( check )){
fireInvalidated( check );
}
}
}
}
};
/**
* Creates a new strategy
* @param control the control in whose realm this strategy operates
*/
public CPlaceholderStrategy( CControl control ){
this.control = control;
}
public void addListener( PlaceholderStrategyListener listener ){
if( listener == null ){
throw new IllegalArgumentException( "listener must not be null" );
}
if( listeners.isEmpty() ){
control.addControlListener( this.controlListener );
control.intern().addListener( this.frontendListener );
}
listeners.add( listener );
}
public void removeListener( PlaceholderStrategyListener listener ){
listeners.remove( listener );
if( listeners.isEmpty() ){
control.removeControlListener( this.controlListener );
control.intern().removeListener( frontendListener );
frontendListener.loaded( null, null );
}
}
protected void fireInvalidated( Path placeholder ){
Set<Path> placeholders = new HashSet<Path>();
placeholders.add( placeholder );
placeholders = Collections.unmodifiableSet( placeholders );
for( PlaceholderStrategyListener listener : listeners.toArray( new PlaceholderStrategyListener[ listeners.size() ] )){
listener.placeholderInvalidated( placeholders );
}
}
public Path getPlaceholderFor( Dockable dockable ){
if( !(dockable instanceof CommonDockable) ){
return null;
}
CDockable cdockable = ((CommonDockable)dockable).getDockable();
CControlAccess controlAccess = cdockable.getControlAccess();
if( controlAccess == null || controlAccess.getOwner() != control ){
return null;
}
if( cdockable instanceof SingleCDockable ){
String id = ((SingleCDockable)cdockable).getUniqueId();
return getSingleDockablePlaceholder( id );
}
if( cdockable instanceof MultipleCDockable ){
CDockableAccess access = controlAccess.access( cdockable );
if( access != null ){
Path result = getMultipleDockablePlaceholder( controlAccess.getRegister().multiToNormalId( access.getUniqueId() ) );
multiplePlaceholders.put( (MultipleCDockable)cdockable, result );
return result;
}
}
return null;
}
public boolean isValidPlaceholder( Path placeholder ){
if( placeholder.getSegmentCount() != 3 ){
return true;
}
if( !placeholder.getSegment( 0 ).equals( "dock" ) ){
return true;
}
if( placeholder.getSegment( 1 ).equals( "single" )){
String id = placeholder.getSegment( 2 );
// if the element is registered, then it is available...
if( control.getSingleDockable( id ) != null ){
return true;
}
// if there is a backup factory, then the client expects this element to exist
if( control.getSingleDockableFactory( id ) != null ){
return true;
}
// maybe the client installed a strategy for handling missing dockables
if( control.getMissingStrategy().shouldStoreSingle( id ) ){
return true;
}
}
if( placeholder.getSegment( 1 ).equals( "multi" ) ){
if( pendingChecks != null ){
pendingChecks.add( placeholder );
return true;
}
String id = placeholder.getSegment( 2 );
// if the element exists, then the placeholder can remain
if( control.getMultipleDockable( id ) != null ){
return true;
}
// if there is a strategy, then the placeholder may remain
if( control.getMissingStrategy().shouldStoreMulti( id )){
return true;
}
}
// the client does not want to store information about this element
return false;
}
public void install( DockStation station ){
// ignore
}
public void uninstall( DockStation station ){
// ignore
}
}