/*
* 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.perspective.mode;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import bibliothek.gui.DockStation;
import bibliothek.gui.dock.common.intern.CControlAccess;
import bibliothek.gui.dock.common.mode.CLocationModeManager;
import bibliothek.gui.dock.common.mode.ExtendedMode;
import bibliothek.gui.dock.common.perspective.CDockablePerspective;
import bibliothek.gui.dock.common.perspective.CPerspective;
import bibliothek.gui.dock.common.perspective.CStationPerspective;
import bibliothek.gui.dock.common.perspective.CommonElementPerspective;
import bibliothek.gui.dock.common.perspective.LocationHistory;
import bibliothek.gui.dock.common.perspective.MultipleCDockablePerspective;
import bibliothek.gui.dock.common.perspective.SingleCDockablePerspective;
import bibliothek.gui.dock.facile.mode.CLocationModeSettings;
import bibliothek.gui.dock.facile.mode.Location;
import bibliothek.gui.dock.facile.mode.LocationSettingConverter;
import bibliothek.gui.dock.layout.DockableProperty;
import bibliothek.gui.dock.perspective.PerspectiveDockable;
import bibliothek.gui.dock.perspective.PerspectiveElement;
import bibliothek.gui.dock.perspective.PerspectiveStation;
import bibliothek.gui.dock.support.mode.ModeSetting;
import bibliothek.gui.dock.support.mode.ModeSettingFactory;
import bibliothek.gui.dock.support.mode.ModeSettings;
import bibliothek.gui.dock.util.DockUtilities;
import bibliothek.util.Path;
/**
* Represents a {@link CLocationModeManager} and offers methods to find the current location and
* {@link ExtendedMode} of a {@link CDockablePerspective}.
* @author Benjamin Sigg
*/
public class LocationModeManagerPerspective {
/** the owner of this manager */
private CPerspective perspective;
/** all the modes known to this manager */
private Map<ExtendedMode, LocationModePerspective> modes = new HashMap<ExtendedMode, LocationModePerspective>();
/** all the factories that creates {@link ModeSetting}s */
private Map<Path, ModeSettingFactory<Location>> settingFactories = new HashMap<Path, ModeSettingFactory<Location>>();
/**
* Creates a new manager
* @param perspective the owner of this manager, not <code>null</code>
* @param control the control which is wrapped by <code>perspective</code>
*/
public LocationModeManagerPerspective( CPerspective perspective, CControlAccess control ){
this.perspective = perspective;
for( ModeSettingFactory<Location> factory : control.getLocationManager().getFactories() ){
settingFactories.put( factory.getModeId(), factory );
}
}
/**
* Gets the {@link ExtendedMode} whose identifier is equal to
* <code>identifier</code>.
* @param identifier some identifier of a mode
* @return the mode or <code>null</code> if not found
*/
public ExtendedMode getMode( Path identifier ){
for( ExtendedMode mode : modes.keySet() ){
if( mode.getModeIdentifier().equals( identifier )){
return mode;
}
}
return null;
}
/**
* Adds <code>mode</code> to this manager.
* @param mode the additional mode
*/
public void addMode( LocationModePerspective mode ){
modes.put( mode.getIdentifier(), mode );
mode.setPerspective( perspective );
}
/**
* Gets the location-mode with the identifier <code>mode</code>.
* @param mode the identifier
* @return the mode or <code>null</code> if not found
*/
public LocationModePerspective getMode( ExtendedMode mode ){
return modes.get( mode );
}
/**
* Gets an array containing all the modes known to this manager.
* @return all the modes
*/
public ExtendedMode[] getModes(){
return modes.keySet().toArray( new ExtendedMode[ modes.size() ] );
}
/**
* Gets the current mode of <code>dockable</code>.
* @param dockable the element whose location is searched
* @return the mode of <code>dockable</code> or <code>null</code> if not found
*/
public ExtendedMode getMode( PerspectiveDockable dockable ){
while( dockable != null ){
for( LocationModePerspective mode : modes.values() ){
if( mode.isCurrentMode( dockable )){
return mode.getIdentifier();
}
}
PerspectiveStation parent = dockable.getParent();
dockable = parent == null ? null : parent.asDockable();
}
return null;
}
/**
* Gets the mode which could have a child in the given location.
* @param root the identifier of the root {@link DockStation}
* @param location the location of some dockable
* @return the matching mode or <code>null</code> if not found
*/
public ExtendedMode getMode( String root, DockableProperty location ){
for( LocationModePerspective mode : modes.values() ){
if( mode.isCurrentMode( root, location )){
return mode.getIdentifier();
}
}
return null;
}
/**
* Gets the first {@link CStationPerspective} that is a root station and
* that is a parent of <code>dockable</code>.
* @param dockable some element whose root-station is searched
* @return the root-station or <code>null</code> if not found
*/
public CStationPerspective getRoot( PerspectiveDockable dockable ){
while( dockable != null ){
PerspectiveStation parent = dockable.getParent();
if( parent == null ){
return null;
}
if( parent instanceof CommonElementPerspective ){
CStationPerspective station = ((CommonElementPerspective)parent).getElement().asStation();
if( perspective.getStation( station.getUniqueId() ) != null ){
return station;
}
}
dockable = parent.asDockable();
}
return null;
}
/**
* Gets the current location of <code>dockable</code>.
* @param dockable the element whose location is searched
* @return the location or <code>null</code> if not found
*/
public Location getLocation( CDockablePerspective dockable ){
return getLocation( dockable.intern().asDockable() );
}
/**
* Gets the current location of <code>dockable</code>.
* @param dockable the element whose location is searched
* @return the location or <code>null</code> if not found
*/
public Location getLocation( PerspectiveDockable dockable ){
CStationPerspective root = getRoot( dockable );
if( root == null ){
return null;
}
ExtendedMode mode = getMode( dockable );
if( mode == null ){
return null;
}
DockableProperty location = DockUtilities.getPropertyChain( root.intern().asStation(), dockable );
if( location == null ){
return null;
}
return new Location( mode.getModeIdentifier(), root.getUniqueId(), location, false );
}
/**
* Writes the contents of this {@link LocationModeManagerPerspective} into a new {@link ModeSettings}.
* @param control access to factories that may be used for writing the contents
* @return the contents of this object
*/
public ModeSettings<Location, Location> writeModes( CControlAccess control ){
ModeSettings<Location, Location> modes = new CLocationModeSettings<Location>( new LocationSettingConverter( control.getOwner().getController() ) );
LocationModeManagerPerspective manager = perspective.getLocationManager();
Iterator<PerspectiveElement> elements = perspective.elements();
while( elements.hasNext() ){
PerspectiveElement next = elements.next();
if( next instanceof CommonElementPerspective ){
CDockablePerspective dockable = ((CommonElementPerspective)next).getElement().asDockable();
if( dockable != null ){
String id = null;
if( dockable instanceof SingleCDockablePerspective ){
id = control.getRegister().toSingleId( ((SingleCDockablePerspective)dockable).getUniqueId() );
}
else if( dockable instanceof MultipleCDockablePerspective){
id = ((MultipleCDockablePerspective)dockable).getUniqueId();
if( id != null ){
// id == null should never happen
id = control.getRegister().toMultiId( id );
}
else{
throw new IllegalStateException( "detected dockable with no identifier" );
}
}
if( id != null ){
ExtendedMode current = manager.getMode( dockable.intern().asDockable() );
LocationHistory history = dockable.getLocationHistory();
List<Path> order = history.getOrder();
Map<Path, Location> locations = history.getLocations();
modes.add( id, current == null ? null : current.getModeIdentifier(), locations, order );
}
}
}
}
// mode settings
for( ExtendedMode mode : manager.getModes() ){
ModeSettingFactory<Location> factory = settingFactories.get( mode.getModeIdentifier() );
if( factory != null ){
ModeSetting<Location> setting = factory.create();
getMode( mode ).writeSetting( setting );
modes.add( setting );
}
}
return modes;
}
/**
* Reads the contents of <code>modes</code> and applies it to the dockables of <code>cperspective</code>.
* @param modes the settings to read
* @param cperspective the perspective for which to read the setting
* @param control the control associated with <code>cperspective</code>
*/
public void readModes( ModeSettings<Location, ?> modes, CPerspective cperspective, CControlAccess control ){
Iterator<PerspectiveElement> elements = cperspective.elements();
while( elements.hasNext() ){
PerspectiveElement next = elements.next();
if( next instanceof CommonElementPerspective ){
CDockablePerspective dockable = ((CommonElementPerspective)next).getElement().asDockable();
if( dockable != null ){
String id = null;
if( dockable instanceof SingleCDockablePerspective ){
id = ((SingleCDockablePerspective)dockable).getUniqueId();
id = control.getRegister().toSingleId( id );
}
else if( dockable instanceof MultipleCDockablePerspective ){
id = ((MultipleCDockablePerspective)dockable).getUniqueId();
id = control.getRegister().toMultiId( id );
}
if( id != null ){
int index = modes.indexOf( id );
if( index != -1 ){
Path[] order = modes.getHistory( index );
Map<Path, Location> locations = modes.getProperties( index );
LocationHistory history = dockable.getLocationHistory();
for( Path item : order ){
ExtendedMode mode = getMode( item );
if( mode == null ){
throw new IllegalStateException( "unknown ExtendedMode with id='" + item + "'" );
}
Location location = locations.get( item );
if( location != null ){
history.add( mode, location );
}
}
}
}
}
}
}
// mode settings
for( ExtendedMode mode : getModes() ){
ModeSetting<Location> modeSetting = modes.getSettings( mode.getModeIdentifier() );
if( modeSetting != null ){
getMode( mode ).readSetting( modeSetting );
}
}
}
}