/* * 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; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import bibliothek.gui.DockFrontend; import bibliothek.gui.DockStation; import bibliothek.gui.Dockable; import bibliothek.gui.dock.DockElement; import bibliothek.gui.dock.common.CControl; import bibliothek.gui.dock.common.CStation; import bibliothek.gui.dock.common.CWorkingArea; import bibliothek.gui.dock.common.MultipleCDockable; import bibliothek.gui.dock.common.MultipleCDockableFactory; import bibliothek.gui.dock.common.SingleCDockable; import bibliothek.gui.dock.common.intern.CControlAccess; import bibliothek.gui.dock.common.intern.CDockFrontend; import bibliothek.gui.dock.common.intern.CDockable; import bibliothek.gui.dock.common.intern.CSetting; import bibliothek.gui.dock.common.intern.CommonDockable; import bibliothek.gui.dock.common.intern.CommonMultipleDockableFactory; import bibliothek.gui.dock.common.intern.CommonSingleDockableFactory; import bibliothek.gui.dock.common.intern.CommonSingleDockableLayout; import bibliothek.gui.dock.common.intern.RootStationAdjacentFactory; import bibliothek.gui.dock.common.intern.station.CommonDockStationFactory; import bibliothek.gui.dock.common.mode.ExtendedMode; import bibliothek.gui.dock.facile.mode.Location; import bibliothek.gui.dock.facile.mode.LocationSettingConverter; import bibliothek.gui.dock.frontend.DockFrontendPerspective; import bibliothek.gui.dock.frontend.FrontendPerspectiveCache; import bibliothek.gui.dock.frontend.RegisteringDockFactory; import bibliothek.gui.dock.frontend.Setting; import bibliothek.gui.dock.layout.DockLayout; import bibliothek.gui.dock.layout.DockLayoutComposition; import bibliothek.gui.dock.layout.DockSituation; import bibliothek.gui.dock.layout.DockableProperty; import bibliothek.gui.dock.layout.PropertyTransformer; import bibliothek.gui.dock.perspective.Perspective; import bibliothek.gui.dock.perspective.PerspectiveElement; import bibliothek.gui.dock.perspective.PerspectiveStation; import bibliothek.gui.dock.perspective.PredefinedPerspective; import bibliothek.gui.dock.support.mode.ModeSettings; import bibliothek.gui.dock.support.mode.ModeSettingsConverter; import bibliothek.util.ClientOnly; import bibliothek.util.FrameworkOnly; import bibliothek.util.Path; import bibliothek.util.Version; import bibliothek.util.xml.XElement; import bibliothek.util.xml.XException; /** * A {@link CControlPerspective} is a wrapper around a {@link CControl} allowing * access to various {@link CPerspective}s. * @author Benjamin Sigg */ @ClientOnly public class CControlPerspective { private CControlAccess control; /** * Creates a new wrapper * @param control the control whose perspectives are modified */ public CControlPerspective( CControlAccess control ){ if( control == null ){ throw new IllegalArgumentException( "control must not be null" ); } this.control = control; } /** * Gets the {@link CControl} in whose realm this {@link CControlPerspective} operates. * @return the owner of this perspective */ public CControl getControl(){ return control.getOwner(); } /** * Creates a new {@link CPerspective} that is set up with all the stations of the {@link CControl}. * There are no {@link Dockable}s stored in the new perspective. * @return the new perspective */ public CPerspective createEmptyPerspective(){ CPerspective perspective = new CPerspective( control ); for( CStation<?> station : control.getOwner().getStations() ){ perspective.addStation( station.createPerspective() ); } return perspective; } /** * Gets a perspective that matches the current layout of the application. * @param includeWorkingAreas whether {@link Dockable}s that are managed by a working-area should be * included in the layout or not * @return the current perspective */ public CPerspective getPerspective( boolean includeWorkingAreas ){ Setting setting = control.getOwner().intern().getSetting( !includeWorkingAreas ); return convert( (CSetting)setting, includeWorkingAreas ); } /** * Gets the names of all the perspectives that are available. * @return all the names */ public String[] getNames(){ return control.getOwner().layouts(); } /** * Gets the perspective which represents a layout that was stored using {@link CControl#save(String)}. * @param name the name of the stored layout * @return the perspective or <code>null</code> if <code>name</code> was not found */ public CPerspective getPerspective( String name ){ return getPerspective( name, false ); } /** * Gets the perspective which represents a layout that was stored using {@link CControl#save(String)}. * @param name the name of the stored layout * @param includeWorkingAreas whether the content of working areas should be included (requires that * the layout was saved in the first place) * @return the perspective or <code>null</code> if <code>name</code> was not found */ public CPerspective getPerspective( String name, boolean includeWorkingAreas ){ Setting setting = control.getOwner().intern().getSetting( name ); if( setting == null ){ return null; } return convert( (CSetting)setting, includeWorkingAreas ); } /** * Changes the layout of the associated {@link CControl} such that it matches <code>perspective</code>. * @param perspective the perspective to apply, not <code>null</code> * @param includeWorkingAreas whether {@link Dockable}s that are managed by a working-area should be * included in the layout or not */ public void setPerspective( CPerspective perspective, boolean includeWorkingAreas ){ control.getOwner().intern().setSetting( convert( perspective, includeWorkingAreas ), !includeWorkingAreas ); } /** * Stores <code>perspective</code> as a layout that can be selected by the user by calling * {@link CControl#load(String)}.<br> * The contents of working areas are ignored by this method. * @param name the name of the layout * @param perspective the new layout, not <code>null</code> */ public void setPerspective( String name, CPerspective perspective ){ setPerspective( name, perspective, false ); } /** * Stores <code>perspective</code> as a layout that can be selected by the user by calling * {@link CControl#load(String)}. * @param name the name of the layout * @param perspective the new layout, not <code>null</code> * @param includeWorkingAreas whether the contents of working areas should be stored as well */ public void setPerspective( String name, CPerspective perspective, boolean includeWorkingAreas ){ control.getOwner().intern().setSetting( name, convert( perspective, includeWorkingAreas ) ); } /** * Deletes the perspective with name <code>name</code>. * @param name the name of the perspective */ public void removePerspective( String name ){ control.getOwner().delete( name ); } /** * Renames the perspective <code>source</code> to <code>destination</code>. If there is already a * layout with name <code>destination</code> it will be overridden. This operation works directly on the * {@link CControl}, already existing {@link CPerspective}s will not be affected by invoking this method. * @param source the name of the source * @param destination the name of the destination * @throws IllegalArgumentException if <code>source</code> does not point to an existing layout * @throws IllegalArgumentException if either <code>source</code> or <code>destination</code> are <code>null</code> */ public void renamePerspective( String source, String destination ){ if( source == null ){ throw new IllegalArgumentException( "source is null" ); } if( destination == null ){ throw new IllegalArgumentException( "destination is null" ); } CDockFrontend frontend = control.getOwner().intern(); Setting layout = frontend.getSetting( source ); if( layout == null ){ throw new IllegalArgumentException( "no perspective registered with name '" + source + "'" ); } frontend.setSetting( destination, layout ); frontend.delete( source ); if( source.equals( frontend.getCurrentSetting() )){ frontend.setCurrentSettingName( destination ); } } /** * Writes the contents of <code>perspective</code> into <code>root</code> using the factories provided * by this {@link CControlPerspective}. * @param root the element to write into, not <code>null</code> * @param perspective the perspective to write, not <code>null</code> */ public void writeXML( XElement root, CPerspective perspective ){ writeXML( root, perspective, true ); } /** * Writes the contents of <code>perspective</code> into <code>root</code> using the factories provided * by this {@link CControlPerspective}. * @param root the element to write into, not <code>null</code> * @param perspective the perspective to write, not <code>null</code> * @param includeWorkingAreas whether the output contains information about children of {@link CStation#isWorkingArea() working areas} * (<code>includeWorkingAreas = true</code>) or not (<code>includeWorkingAreas = false</code>) */ public void writeXML( XElement root, CPerspective perspective, boolean includeWorkingAreas ){ perspective.storeLocations(); DockFrontendPerspective frontend = conversion( perspective, includeWorkingAreas ); Perspective conversion = frontend.getPerspective(); Map<String, DockLayoutComposition> stations = new HashMap<String, DockLayoutComposition>(); for( String key : perspective.getStationKeys() ){ CStationPerspective station = perspective.getStation( key ); if( station.asDockable() == null || station.asDockable().getParent() == null ){ stations.put( key, conversion.convert( station.intern() )); } } conversion.getSituation().writeCompositionsXML( stations, root.addElement( "stations" ) ); // Store the last location of all known elements XElement xinvisible = root.addElement( "invisible" ); PropertyTransformer transformer = frontend.getPropertyTransformer(); for( String key : perspective.getDockableKeys() ){ CDockablePerspective dockable = perspective.getDockable( key ); Location location = getInvisibleLocation( dockable ); if( location != null ){ XElement xdockable = xinvisible.addElement( "dockable" ); xdockable.addString( "key", key ); if( dockable.getParent() == null ){ conversion.getSituation().writeCompositionXML( conversion.convert( dockable.intern() ), xdockable.addElement( "content" ) ); } XElement xlocation = xdockable.addElement( "location" ); xlocation.addString( "root", location.getRoot() ); xlocation.addString( "mode", dockable.getLocationHistory().getLastMode().getModeIdentifier().toString() ); xlocation.addBoolean( "applicationDefined", location.isApplicationDefined() ); transformer.writeXML( location.getLocation(), xlocation ); } } // store more location information ModeSettings<Location, ?> settings = perspective.getLocationManager().writeModes( control ); settings.writeXML( root.addElement( "modes" ) ); } /** * Writes the contents of <code>perspective</code> into <code>out</code> using the factories provided * by this {@link CControlPerspective}. * @param out the stream to write into, not <code>null</code> * @param perspective the perspective to write, not <code>null</code> * @throws IOException if <code>out</code> is not writeable */ public void write( DataOutputStream out, CPerspective perspective ) throws IOException{ write( out, perspective, true ); } /** * Writes the contents of <code>perspective</code> into <code>out</code> using the factories provided * by this {@link CControlPerspective}. * @param out the stream to write into, not <code>null</code> * @param perspective the perspective to write, not <code>null</code> * @param includeWorkingAreas whether the output contains information about children of {@link CStation#isWorkingArea() working areas} * (<code>includeWorkingAreas = true</code>) or not (<code>includeWorkingAreas = false</code>) * @throws IOException if <code>out</code> is not writeable */ public void write( DataOutputStream out, CPerspective perspective, boolean includeWorkingAreas ) throws IOException{ perspective.storeLocations(); Version.write( out, Version.VERSION_1_1_2 ); DockFrontendPerspective frontend = conversion( perspective, includeWorkingAreas ); Perspective conversion = frontend.getPerspective(); Map<String, DockLayoutComposition> stations = new HashMap<String, DockLayoutComposition>(); for( String key : perspective.getStationKeys() ){ CStationPerspective station = perspective.getStation( key ); stations.put( key, conversion.convert( station.intern() )); } conversion.getSituation().writeCompositions( stations, out ); // Store the last location of all known elements String[] keys = perspective.getDockableKeys(); out.writeInt( keys.length ); PropertyTransformer transformer = frontend.getPropertyTransformer(); for( String key : keys ){ CDockablePerspective dockable = perspective.getDockable( key ); Location location = getInvisibleLocation( dockable ); if( location != null ){ out.writeBoolean( true ); out.writeUTF( key ); if( dockable.getParent() == null ){ out.writeBoolean( true ); conversion.getSituation().writeComposition( conversion.convert( dockable.intern() ), out ); } else{ out.writeBoolean( false ); } out.writeUTF( location.getRoot() ); out.writeUTF( dockable.getLocationHistory().getLastMode().getModeIdentifier().toString() ); out.writeBoolean( location.isApplicationDefined() ); transformer.write( location.getLocation(), out ); } else{ out.writeBoolean( false ); } } // write more location information ModeSettings<Location, ?> settings = perspective.getLocationManager().writeModes( control ); settings.write( out ); } /** * Converts <code>perspective</code> into a {@link CSetting}. * @param perspective the perspective to convert * @param includeWorkingAreas whether the children of {@link CWorkingArea}s should be stored as well * @return the converted perspective */ public CSetting write( CPerspective perspective, boolean includeWorkingAreas ){ return convert( perspective, includeWorkingAreas ); } /** * Emulates a call to {@link CControl#readXML(XElement)} and returns all the layouts that are stored * within <code>root</code>. * @param root the root xml element of a file * @return all the layouts and settings stored in <code>root</code> * @throws XException if <code>root</code> is not well formed */ public CControlPerspectiveBlop readAllXML( XElement root ) throws XException{ CControlPerspectiveBlop blop = new CControlPerspectiveBlop( this ); blop.readXML( root ); return blop; } /** * Creates a new {@link CPerspective} using the information stored in <code>root</code>. While this method * uses the factories provided by this {@link CControlPerspective}, the new {@link CPerspective} is not registered * anywhere. It is the clients responsibility to call {@link #setPerspective(String, CPerspective)} or * {@link #setPerspective(CPerspective, boolean)} to actually use the result of this method. * @param root the element which contains information about a perspective * @return the new perspective * @throws XException if the structure of <code>root</code> is not as expected */ public CPerspective readXML( XElement root ) throws XException{ return readXML( root, true ); } /** * Creates a new {@link CPerspective} using the information stored in <code>root</code>. While this method * uses the factories provided by this {@link CControlPerspective}, the new {@link CPerspective} is not registered * anywhere. It is the clients responsibility to call {@link #setPerspective(String, CPerspective)} or * {@link #setPerspective(CPerspective, boolean)} to actually use the result of this method. * @param root the element which contains information about a perspective * @return the new perspective * @param includeWorkingAreas whether the perspective contains information about children of {@link CStation#isWorkingArea() working areas} * (<code>includeWorkingAreas = true</code>) or not (<code>includeWorkingAreas = false</code>). This parameter should have the same value as was used * when calling {@link #write(DataOutputStream, CPerspective, boolean)}. * @throws XException if the structure of <code>root</code> is not as expected */ public CPerspective readXML( XElement root, boolean includeWorkingAreas ) throws XException{ CPerspective perspective = createEmptyPerspective(); PerspectiveElementFactory factory = new PerspectiveElementFactory( perspective ); DockFrontendPerspective frontend = wrap( perspective, includeWorkingAreas, factory ); Perspective conversion = frontend.getPerspective(); for( Map.Entry<String, MultipleCDockableFactory<?, ?>> item : control.getRegister().getFactories().entrySet() ){ conversion.getSituation().add( new CommonMultipleDockableFactory( item.getKey(), item.getValue(), control, perspective ) ); } XElement xstations = root.getElement( "stations" ); if( xstations == null ){ throw new XException( "missing element 'stations'" ); } Map<String, DockLayoutComposition> stations = conversion.getSituation().readCompositionsXML( xstations ); factory.setStations( stations ); for( DockLayoutComposition composition : stations.values() ){ PerspectiveElement station = conversion.convert( composition ); if( station instanceof CommonElementPerspective ){ CStationPerspective stationPerspective = ((CommonElementPerspective)station).getElement().asStation(); if( stationPerspective != null ){ perspective.addStation( stationPerspective ); } } } perspective.storeLocations(); // read the last known location of all elements XElement xinvisible = root.getElement( "invisible" ); if( xinvisible != null ){ PropertyTransformer transformer = frontend.getPropertyTransformer(); for( XElement xdockable : xinvisible.getElements( "dockable" )){ String key = xdockable.getString( "key" ); CDockablePerspective dockable = perspective.getDockable( key ); if( dockable == null ){ XElement xcontent = xdockable.getElement( "content" ); if( xcontent != null ){ PerspectiveElement element = conversion.convert( conversion.getSituation().readCompositionXML( xcontent ) ); if( element instanceof CommonElementPerspective ){ dockable = ((CommonElementPerspective)element).getElement().asDockable(); if( dockable != null ){ perspective.putDockable( dockable ); } } } } if( dockable != null ){ XElement xlocation = xdockable.getElement( "location" ); String locationRoot = xlocation.getString( "root" ); DockableProperty location = transformer.readXML( xlocation ); Path mode = new Path( xlocation.getString( "mode" )); boolean applicationDefined = false; if( xlocation.attributeExists( "applicationDefined" )){ applicationDefined = xlocation.getBoolean( "applicationDefined" ); } ExtendedMode extendedMode = perspective.getLocationManager().getMode( mode ); if( extendedMode != null ){ dockable.getLocationHistory().add( extendedMode, new Location( mode, locationRoot, location, applicationDefined ) ); } } } } XElement xmodes = root.getElement( "modes" ); if( xmodes == null ){ throw new XException( "missing element 'modes'" ); } ModeSettingsConverter<Location, Location> converter = new LocationSettingConverter( control.getOwner().getController() ); ModeSettings<Location, Location> modes = control.getOwner().getLocationManager().createModeSettings( converter ); modes.readXML( xmodes ); perspective.getLocationManager().readModes( modes, perspective, control ); return perspective; } /** * Emulates a call to {@link CControl#read(DataInputStream)} and returns all the layouts that are stored * in the stream <code>in</code>. * @param in the bytes of some layout file * @return all the layouts and settings that can be read from <code>in</code> * @throws IOException if there is a problem reading <code>in</code> or if <code>in</code> is not well formed */ public CControlPerspectiveBlop readAll( DataInputStream in ) throws IOException{ CControlPerspectiveBlop blop = new CControlPerspectiveBlop( this ); blop.read( in ); return blop; } /** * Creates a new {@link CPerspective} using the information stored in <code>in</code>. While this method * uses the factories provided by this {@link CControlPerspective}, the new {@link CPerspective} is not registered * anywhere. It is the clients responsibility to call {@link #setPerspective(String, CPerspective)} or * {@link #setPerspective(CPerspective, boolean)} to actually use the result of this method. * @param in the stream to read data from * @return the new perspective * @throws IOException if <code>in</code> is not readable or in the wrong format */ public CPerspective read( DataInputStream in ) throws IOException{ return read( in, true ); } /** * Creates a new {@link CPerspective} using the information stored in <code>in</code>. While this method * uses the factories provided by this {@link CControlPerspective}, the new {@link CPerspective} is not registered * anywhere. It is the clients responsibility to call {@link #setPerspective(String, CPerspective)} or * {@link #setPerspective(CPerspective, boolean)} to actually use the result of this method. * @param in the stream to read data from * @param includeWorkingAreas whether the perspective contains information about children of {@link CStation#isWorkingArea() working areas} * (<code>includeWorkingAreas = true</code>) or not (<code>includeWorkingAreas = false</code>). This parameter should have the same value as was used * when calling {@link #write(DataOutputStream, CPerspective, boolean)}. * @return the new perspective * @throws IOException if <code>in</code> is not readable or in the wrong format */ public CPerspective read( DataInputStream in, boolean includeWorkingAreas ) throws IOException{ Version version = Version.read( in ); boolean version111 = version.equals( Version.VERSION_1_1_1 ); boolean version111a = version.equals( Version.VERSION_1_1_1a ); boolean version112 = version.equals( Version.VERSION_1_1_2 ); if( !version111 && !version111a && !version112 ){ throw new IOException( "unknown version: " + version ); } CPerspective perspective = createEmptyPerspective(); PerspectiveElementFactory factory = new PerspectiveElementFactory( perspective ); DockFrontendPerspective frontend = wrap( perspective, includeWorkingAreas, factory ); Perspective conversion = frontend.getPerspective(); for( Map.Entry<String, MultipleCDockableFactory<?, ?>> item : control.getRegister().getFactories().entrySet() ){ conversion.getSituation().add( new CommonMultipleDockableFactory( item.getKey(), item.getValue(), control, perspective ) ); } Map<String, DockLayoutComposition> stations = conversion.getSituation().readCompositions( in ); factory.setStations( stations ); for( DockLayoutComposition composition : stations.values() ){ PerspectiveElement station = conversion.convert( composition ); if( station instanceof CommonElementPerspective ){ CStationPerspective stationPerspective = ((CommonElementPerspective)station).getElement().asStation(); if( stationPerspective != null ){ perspective.addStation( stationPerspective ); } } } if( version111a || version112 ){ perspective.storeLocations(); PropertyTransformer transformer = frontend.getPropertyTransformer(); for( int i = 0, n = in.readInt(); i<n; i++ ){ if( in.readBoolean() ){ String key = in.readUTF(); DockLayoutComposition composition = null; if( in.readBoolean() ){ composition = conversion.getSituation().readComposition( in ); } CDockablePerspective dockable = perspective.getDockable( key ); if( dockable == null && composition != null ){ PerspectiveElement element = conversion.convert( composition ); if( element instanceof CommonElementPerspective ){ dockable = ((CommonElementPerspective)element).getElement().asDockable(); if( dockable != null ){ perspective.putDockable( dockable ); } } } String locationRoot = in.readUTF(); String modeId = in.readUTF(); boolean applicationDefined = false; if( version112 ){ applicationDefined = in.readBoolean(); } DockableProperty location = transformer.read( in ); if( dockable != null ){ Path mode = new Path( modeId ); ExtendedMode extendedMode = perspective.getLocationManager().getMode( mode ); if( extendedMode != null ){ dockable.getLocationHistory().add( extendedMode, new Location( mode, locationRoot, location, applicationDefined ) ); } } } } } ModeSettingsConverter<Location, Location> converter = new LocationSettingConverter( control.getOwner().getController() ); ModeSettings<Location, Location> modes = control.getOwner().getLocationManager().createModeSettings( converter ); modes.read( in ); perspective.getLocationManager().readModes( modes, perspective, control ); return perspective; } /** * Creates a new {@link CPerspective} and fills it using the information from <code>setting</code>. * @param setting the layout to convert * @param includeWorkingAreas whether the layout contains information about children of {@link CWorkingArea}s * @return the layout of <code>setting</code> */ public CPerspective read( CSetting setting, boolean includeWorkingAreas ){ return convert( setting, includeWorkingAreas ); } private CSetting convert( CPerspective perspective, boolean includeWorkingAreas ){ perspective.storeLocations(); DockFrontendPerspective frontend = conversion( perspective, includeWorkingAreas ); Perspective conversion = frontend.getPerspective(); CSetting setting = new CSetting(); // layout for( String key : perspective.getStationKeys() ){ CStationPerspective station = perspective.getStation( key ); if( station.asDockable() == null || station.asDockable().getParent() == null ){ setting.putRoot( key, conversion.convert( station.intern() ) ); } } // invisible items (storing location of visible items as well) for( String key : perspective.getDockableKeys() ){ CDockablePerspective dockable = perspective.getDockable( key ); Location location = getInvisibleLocation( dockable ); if( location != null ){ setting.addInvisible( key, location.getRoot(), null, location.getLocation() ); } } ModeSettings<Location, Location> settings = perspective.getLocationManager().writeModes( control ); setting.setModes( settings ); return setting; } private CPerspective convert( CSetting setting, boolean includeWorkingAreas ){ CPerspective cperspective = createEmptyPerspective(); PerspectiveElementFactory factory = new PerspectiveElementFactory( cperspective ); DockFrontendPerspective frontend = wrap( cperspective, includeWorkingAreas, factory ); Perspective conversion = frontend.getPerspective(); for( Map.Entry<String, MultipleCDockableFactory<?, ?>> item : control.getRegister().getFactories().entrySet() ){ conversion.getSituation().add( new CommonMultipleDockableFactory( item.getKey(), item.getValue(), control, cperspective ) ); } // registered dockables Map<String, DockLayoutComposition> stations = new HashMap<String, DockLayoutComposition>(); for( String root : setting.getRootKeys() ){ stations.put( root, setting.getRoot( root ) ); } factory.setStations( stations ); for( DockLayoutComposition composition : stations.values() ){ PerspectiveElement station = conversion.convert( composition ); if( station instanceof CommonElementPerspective ){ CStationPerspective stationPerspective = ((CommonElementPerspective)station).getElement().asStation(); if( stationPerspective != null ){ cperspective.addStation( stationPerspective ); } } } // invisible dockables for( int i = 0, n = setting.getInvisibleCount(); i < n; i++ ){ DockLayoutComposition composition = setting.getInvisibleLayout( i ); if( composition != null ){ PerspectiveElement element = conversion.convert( composition ); if( element instanceof CommonElementPerspective ){ CDockablePerspective dockable = ((CommonElementPerspective)element).getElement().asDockable(); if( dockable != null ){ DockableProperty location = setting.getInvisibleLocation( i ); String root = setting.getInvisibleRoot( i ); ExtendedMode mode = cperspective.getLocationManager().getMode( root, location ); if( mode != null ){ dockable.getLocationHistory().add( mode, new Location( mode.getModeIdentifier(), root, location, false ) ); } cperspective.putDockable( dockable ); } } } } // location information ModeSettings<Location, Location> modes = setting.getModes(); cperspective.getLocationManager().readModes( modes, cperspective, control ); return cperspective; } private Location getInvisibleLocation( CDockablePerspective dockable ){ LocationHistory history = dockable.getLocationHistory(); List<Path> order = history.getOrder(); if( !order.isEmpty() ){ Path mode = order.get( order.size()-1 ); Location location = history.getLocations().get( mode ); return location; } return null; } /** * Creates a new {@link DockFrontendPerspective} which uses the settings from <code>perspective</code> to read * and write layouts. This method adds {@link CommonSingleDockableFactory}, {@link CommonMultipleDockableFactory} and * {@link CommonDockStationFactory} to the perspective.<br> * Clients usually have no need to call this method. * @param perspective the perspective whose settings should be used for reading or writing a layout * @param includeWorkingAreas whether the contents of {@link CStation#isWorkingArea() working areas} * should be included in the layout or not * @return the new builder */ @FrameworkOnly public DockFrontendPerspective conversion( CPerspective perspective, boolean includeWorkingAreas ){ DockFrontendPerspective conversion = wrap( perspective, includeWorkingAreas ); DockSituation situation = conversion.getPerspective().getSituation(); for( Map.Entry<String, MultipleCDockableFactory<?, ?>> item : control.getRegister().getFactories().entrySet() ){ situation.add( new CommonMultipleDockableFactory( item.getKey(), item.getValue(), control, perspective ) ); } return conversion; } private DockFrontendPerspective wrap( CPerspective perspective, boolean includeWorkingAreas ){ PerspectiveElementFactory factory = new PerspectiveElementFactory( perspective ); return wrap( perspective, includeWorkingAreas, factory ); } private DockFrontendPerspective wrap( CPerspective perspective, boolean includeWorkingAreas, PerspectiveElementFactory factory ){ DockFrontendPerspective frontend = control.getOwner().intern().getPerspective( !includeWorkingAreas, factory ); PredefinedPerspective inner = frontend.getPerspective(); factory.setBasePerspective( inner ); CommonSingleDockableFactory singleDockableFactory = new CommonSingleDockableFactory( control.getOwner(), perspective ); inner.getSituation().add( singleDockableFactory ); inner.getSituation().addBackup( new RegisteringDockFactory<CommonDockable, CommonElementPerspective, CommonSingleDockableLayout>( control.getOwner().intern(), singleDockableFactory ) ); inner.getSituation().add( new CommonDockStationFactory( control.getOwner(), factory, singleDockableFactory ) ); return frontend; } /** * Helper class for converting {@link DockElement}s to {@link PerspectiveElement}s. * @author Benjamin Sigg */ private class PerspectiveElementFactory implements FrontendPerspectiveCache{ private CPerspective perspective; private Perspective basePerspective; private Map<String, SingleCDockablePerspective> dockables = new HashMap<String, SingleCDockablePerspective>(); private Map<String, DockLayoutComposition> stations; /** * Creates a new factory. * @param perspective the perspective for which items are required */ public PerspectiveElementFactory( CPerspective perspective ){ this.perspective = perspective; Iterator<PerspectiveElement> elements = perspective.elements(); while( elements.hasNext() ){ PerspectiveElement element = elements.next(); if( element instanceof SingleCDockablePerspective ){ SingleCDockablePerspective dockable = (SingleCDockablePerspective) element; dockables.put( dockable.getUniqueId(), dockable ); } } } public void setStations( Map<String, DockLayoutComposition> stations ){ this.stations = stations; } /** * Sets the {@link Perspective} which is using this cache. * @param basePerspective the perspective using this cache, not <code>null</code> */ public void setBasePerspective( Perspective basePerspective ){ this.basePerspective = basePerspective; } public PerspectiveElement get( String id, DockElement element, boolean isRootStation ){ if( isRootStation ){ return perspective.getStation( id ).intern(); } else if( element instanceof CommonDockable ){ CDockable dockable = ((CommonDockable)element).getDockable(); if( dockable.asStation() != null ){ CStationPerspective station = perspective.getStation( dockable.asStation().getUniqueId() ); if( station == null ){ throw new IllegalArgumentException( "Found a non-root CStation that is not registered: " + dockable.asStation().getUniqueId() ); } return station.intern(); } if( dockable instanceof SingleCDockable ){ String key = ((SingleCDockable)dockable).getUniqueId(); SingleCDockablePerspective result = dockables.get( key ); if( result == null ){ result = new SingleCDockablePerspective( key ); dockables.put( key, result ); } return result.intern(); } if( dockable instanceof MultipleCDockable ){ return null; } } throw new IllegalArgumentException( "The intern DockFrontend of the CControl has elements registered that are not SingleCDockables: " + id + "=" + element ); } @SuppressWarnings("unchecked") public PerspectiveElement get( String id, boolean rootStation ){ String key = id; if( !rootStation && control.getRegister().isSingleId( id )){ key = control.getRegister().singleToNormalId( id ); } // maybe a station DockLayoutComposition root = null; if( stations != null ){ root = stations.get( key ); } if( root == null ){ root = getPredefinedStation( key, basePerspective.getSituation() ); } Path stationType = null; if( root != null ){ // really a station DockLayout<Path> layout = (DockLayout<Path>)root.getAdjacent( RootStationAdjacentFactory.FACTORY_ID ); if( layout != null){ stationType = layout.getData(); } CStationPerspective station = perspective.getStation( key ); if( station == null ){ station = control.getOwner().getMissingPerspectiveStrategy().createStation( key, stationType ); if( station != null ){ perspective.addStation( station ); station.setRoot( rootStation ); } } if( station == null ){ return null; } return station.intern(); } else if( control.getRegister().isSingleId( id )){ // maybe a dockable SingleCDockablePerspective result = dockables.get( key ); if( result == null ){ result = new SingleCDockablePerspective( key ); dockables.put( key, result ); } return result.intern(); } return null; } /** * Tries to find the layout of a {@link DockStation} which was predefined with the unique * identifier <code>id</code>. This method recursively searches through the entire tree of elements and * also finds stations that are registered as {@link SingleCDockable} or {@link MultipleCDockable}. * @param id the identifier to search * @param situation algorithms used to extract information from {@link DockLayoutComposition}s * @return the layout or <code>null</code> if not found */ protected DockLayoutComposition getPredefinedStation( String id, DockSituation situation ){ if( stations != null ){ for( DockLayoutComposition station : stations.values() ){ DockLayoutComposition result = getPredefinedStation( id, station, situation ); if( result != null ){ return result; } } } return null; } private DockLayoutComposition getPredefinedStation( String id, DockLayoutComposition current, DockSituation situation ){ // check self String currentId = situation.getIdentifier( current ); if( currentId != null ){ if( id.length() == DockFrontend.ROOT_KEY_PREFIX.length()+id.length() && currentId.startsWith( DockFrontend.ROOT_KEY_PREFIX ) && currentId.endsWith( id )){ return current; } if( currentId.startsWith( DockFrontend.DOCKABLE_KEY_PREFIX )){ currentId = currentId.substring( DockFrontend.DOCKABLE_KEY_PREFIX.length() ); if( control.getRegister().isSingleId( currentId )){ currentId = control.getRegister().singleToNormalId( currentId ); } else if( control.getRegister().isMultiId( currentId )){ currentId = control.getRegister().multiToNormalId( currentId ); } if( currentId.equals( id )){ return current; } } } // check children List<DockLayoutComposition> children = current.getChildren(); if( children != null ){ for( DockLayoutComposition child : children ){ DockLayoutComposition result = getPredefinedStation( id, child, situation ); if( result != null ){ return result; } } } return null; } public String get( PerspectiveElement element ){ for( String key : perspective.getStationKeys() ){ CStationPerspective station = perspective.getStation( key ); if( station.intern() == element ){ return key; } } if( element instanceof CommonElementPerspective ){ CElementPerspective celement = ((CommonElementPerspective)element).getElement(); if( celement instanceof SingleCDockablePerspective ){ return control.getRegister().toSingleId( ((SingleCDockablePerspective)celement).getUniqueId() ); } } return null; } public boolean isRootStation( PerspectiveStation element ){ for( String key : perspective.getStationKeys() ){ CStationPerspective station = perspective.getStation( key ); if( station.intern() == element ){ return true; } } return false; } } }