/* * 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) 2007 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; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import javax.swing.JFrame; import javax.swing.JPanel; import bibliothek.gui.Dockable; import bibliothek.gui.dock.FlapDockStation; import bibliothek.gui.dock.FlapDockStation.Direction; import bibliothek.gui.dock.SplitDockStation; import bibliothek.gui.dock.common.intern.CDockable; import bibliothek.gui.dock.common.location.CBaseLocation; import bibliothek.gui.dock.common.location.CMinimizedLocation; import bibliothek.gui.dock.common.location.Side; import bibliothek.gui.dock.common.mode.ExtendedMode; import bibliothek.util.Path; /** * A component that is normally set into the center of the * main- {@link JFrame}. This component can display * and manage some {@link CDockable}s.<br> * This component contains in the center a {@link SplitDockStation} allowing * to show several {@link CDockable}s at the same time. At each border a * {@link FlapDockStation} allows to show "minimized" {@link CDockable}s.<br> * Note: clients should not create {@link CContentArea}s directly, they should * use {@link CControl#getContentArea()} to get the default content area, or * {@link CControl#createContentArea(String)} to create a new content area. * @author Benjamin Sigg */ public class CContentArea extends JPanel implements CStationContainer{ /** The result of {@link CStation#getTypeId()} for the center station */ public static final Path TYPE_ID_CENTER = new Path( "dock", "CContentArea", "center" ); /** The result of {@link CStation#getTypeId()} for the minimize stations */ public static final Path TYPE_ID_MINIMIZE = new Path( "dock", "CContentArea", "minimize" ); /** * References a corner of a panel. * @author Benjamin Sigg */ public static enum Corner{ /** the lower right corner */ SOUTH_EAST, /** the lower left corner */ SOUTH_WEST, /** the higher right corner */ NORTH_EAST, /** the higher left corner */ NORTH_WEST; } /** the child in the center */ private CenterStation center; /** the child at the north border */ private MinimizeStation north; /** the child at the south border */ private MinimizeStation south; /** the child at the east border */ private MinimizeStation east; /** the child at the west border */ private MinimizeStation west; /** the components in the corners */ private Component[] cornerComponents = new Component[8]; /** an identifier for this center */ private String uniqueId; /** access to the controller which uses this area */ private CControl control; /** the set of stations on this content area */ private CStation<?>[] stations; /** * Creates a new content area. * @param control the control for which this area will be used * @param uniqueId a unique identifier of this center */ public CContentArea( CControl control, String uniqueId ){ this.control = control; this.uniqueId = uniqueId; CBaseLocation base = new CBaseLocation( this ); center = new CenterStation( getCenterIdentifier(), base.normal() ); north = new MinimizeStation( getNorthIdentifier(), new CMinimizedLocation( base, Side.NORTH ) ); south = new MinimizeStation( getSouthIdentifier(), new CMinimizedLocation( base, Side.SOUTH ) ); east = new MinimizeStation( getEastIdentifier(), new CMinimizedLocation( base, Side.EAST ) ); west = new MinimizeStation( getWestIdentifier(), new CMinimizedLocation( base, Side.WEST ) ); center.getStation().setExpandOnDoubleclick( false ); north.setDirection( Direction.SOUTH ); south.setDirection( Direction.NORTH ); east.setDirection( Direction.WEST ); west.setDirection( Direction.EAST ); setLayout( new BorderLayout() ); add( center.getStation(), BorderLayout.CENTER ); add( north, BorderLayout.NORTH ); add( south, BorderLayout.SOUTH ); add( east, BorderLayout.EAST ); add( west, BorderLayout.WEST ); stations = new CStation[]{ north, south, east, west, center }; } /** * Adds additional stations to the {@link #getStations() array} of {@link CStation}. This method * should only be called by subclasses. * @param stations the additional stations to store */ protected void addStations( CStation<?>... stations ){ CStation<?>[] temp = new CStation<?>[ this.stations.length + stations.length ]; System.arraycopy( this.stations, 0, temp, 0, this.stations.length ); System.arraycopy( stations, 0, temp, this.stations.length, stations.length ); this.stations = temp; } /** * Gets the unique id of this center. * @return the unique id */ public String getUniqueId(){ return uniqueId; } public Component getComponent(){ return this; } public void addStationContainerListener( CStationContainerListener listener ){ // ignore, the CContentArea is not mutable hence the listener will never be called } public void removeStationContainerListener( CStationContainerListener listener ){ // ignore } /** * Gets the {@link CControl} for which this content area was created. * @return the owner of this area */ public CControl getControl(){ return control; } /** * Gets an independent array of all stations that are used on this * {@link CContentArea}. * @return the list of stations */ public CStation<?>[] getStations(){ CStation<?>[] copy = new CStation[ stations.length ]; System.arraycopy( stations, 0, copy, 0, stations.length ); return copy; } public int getStationCount(){ return stations.length; } public CStation<?> getStation( int index ){ return stations[index]; } /** * Gets the index of <code>child</code>. * @param child a child {@link CStation} of this area. * @return the index of <code>child</code> or -1 if not found */ public int indexOf( CStation<?> child ){ for( int i = 0; i < stations.length; i++ ){ if( stations[i] == child ){ return i; } } return -1; } public CStation<?> getDefaultStation(){ return center; } public CStation<?> getDefaultStation( ExtendedMode mode ){ if( mode == ExtendedMode.MINIMIZED ){ return north; } if( mode == ExtendedMode.NORMALIZED ){ return center; } if( mode == ExtendedMode.MAXIMIZED ){ return center; } return null; } /** * Exchanges all the {@link CDockable}s on the center panel by * the elements of <code>grid</code>. * @param grid a grid containing some new {@link Dockable}s */ public void deploy( CGrid grid ){ getCenter().dropTree( grid.toTree() ); } /** * Puts <code>component</code> in one corner of this area. * @param component the component, can be <code>null</code> * @param corner the corner into which to put <code>component</code> * @param horizontal whether <code>component</code> should be horizontally * or vertically. */ public void setCornerComponent( Component component, Corner corner, boolean horizontal ){ int index = corner.ordinal() * 2; if( horizontal ) index++; if( cornerComponents[ index ] != null ){ switch( corner ){ case NORTH_WEST: if( horizontal ){ north.remove( cornerComponents[ index ] ); } else{ west.remove( cornerComponents[ index ] ); } break; case NORTH_EAST: if( horizontal ){ north.remove( cornerComponents[ index ] ); } else{ east.remove( cornerComponents[ index ] ); } break; case SOUTH_WEST: if( horizontal ){ south.remove( cornerComponents[ index ] ); } else{ west.remove( cornerComponents[ index ] ); } break; case SOUTH_EAST: if( horizontal ){ south.remove( cornerComponents[ index ] ); } else{ east.remove( cornerComponents[ index ] ); } break; } } cornerComponents[ index ] = component; if( component != null ){ switch( corner ){ case NORTH_WEST: if( horizontal ){ north.add( component, BorderLayout.WEST ); } else{ west.add( component, BorderLayout.NORTH ); } break; case NORTH_EAST: if( horizontal ){ north.add( component, BorderLayout.EAST ); } else{ east.add( component, BorderLayout.NORTH ); } break; case SOUTH_WEST: if( horizontal ){ south.add( component, BorderLayout.WEST ); } else{ west.add( component, BorderLayout.SOUTH ); } break; case SOUTH_EAST: if( horizontal ){ south.add( component, BorderLayout.EAST ); } else{ east.add( component, BorderLayout.SOUTH ); } break; } } } /** * Gets the component of a corner. * @param corner the corner in which to search * @param horizontal whether the component is horizontally or vertically * @return the component or <code>null</code> */ public Component getCornerComponent( Corner corner, boolean horizontal ){ int index = corner.ordinal() * 2; if( horizontal ) index++; return cornerComponents[ index ]; } /** * Sets the minimum size of the four areas in which minimized {@link Dockable}s * are shown. Clients could also call <code>get'Side'().setMinimumSize( size )</code>.<br> * There is no method <code>getMinimumAreaSize</code> because the result might * not be the same for all stations. * @param size the new minimum size or <code>null</code> to revert to the default * value. * @see FlapDockStation#setMinimumSize(Dimension) * @see FlapDockStation#MINIMUM_SIZE */ public void setMinimumAreaSize( Dimension size ){ north.getStation().setMinimumSize( size ); south.getStation().setMinimumSize( size ); west.getStation().setMinimumSize( size ); east.getStation().setMinimumSize( size ); } /** * Gets the station in the center of this {@link CContentArea}. * @return the central station */ public SplitDockStation getCenter(){ return center.getStation(); } /** * Gets the station in the center of this {@link CContentArea}. * @return the central station */ public CGridArea getCenterArea(){ return center; } /** * Gets the station in the north of this {@link CContentArea} * @return the station in the north */ public FlapDockStation getNorth(){ return north.getStation(); } /** * Gets the station in the north of this {@link CContentArea} * @return the station in the north */ public CMinimizeArea getNorthArea(){ return north; } /** * Gets the station in the south of this {@link CContentArea} * @return the station in the south */ public FlapDockStation getSouth(){ return south.getStation(); } /** * Gets the station in the south of this {@link CContentArea} * @return the station in the south */ public CMinimizeArea getSouthArea(){ return south; } /** * Gets the station in the east of this {@link CContentArea} * @return the station in the east */ public FlapDockStation getEast(){ return east.getStation(); } /** * Gets the station in the east of this {@link CContentArea} * @return the station in the east */ public CMinimizeArea getEastArea(){ return east; } /** * Gets the station in the west of this {@link CContentArea} * @return the station in the west */ public FlapDockStation getWest(){ return west.getStation(); } /** * Gets the station in the west of this {@link CContentArea} * @return the station in the west */ public CMinimizeArea getWestArea(){ return west; } /** * Gets the global identifier for the panel in the center. * @return the identifier */ public String getCenterIdentifier(){ return getCenterIdentifier( uniqueId ); } /** * Creates the global identifier of a panel in the center. * @param uniqueCenterId the unique if of the owning {@link CContentArea}. * @return the global identifier */ public static String getCenterIdentifier( String uniqueCenterId ){ return uniqueCenterId + " center"; } /** * Gets the global identifier for the panel in the north. * @return the identifier */ public String getNorthIdentifier(){ return getNorthIdentifier( uniqueId ); } /** * Creates the global identifier of a panel in the north. * @param uniqueCenterId the unique id of the owning {@link CContentArea}. * @return the global identifier */ public static String getNorthIdentifier( String uniqueCenterId ){ return uniqueCenterId + " north"; } /** * Gets the global identifier for the panel in the south. * @return the identifier */ public String getSouthIdentifier(){ return getSouthIdentifier( uniqueId ); } /** * Creates the global identifier of a panel in the south. * @param uniqueCenterId the unique id of the owning {@link CContentArea}. * @return the global identifier */ public static String getSouthIdentifier( String uniqueCenterId ){ return uniqueCenterId + " south"; } /** * Gets the global identifier for the panel in the east. * @return the identifier */ public String getEastIdentifier(){ return getEastIdentifier( uniqueId ); } /** * Creates the global identifier of a panel in the east. * @param uniqueCenterId the unique id of the owning {@link CContentArea}. * @return the global identifier */ public static String getEastIdentifier( String uniqueCenterId ){ return uniqueCenterId + " east"; } /** * Gets the global identifier for the panel in the west. * @return the identifier */ public String getWestIdentifier(){ return getWestIdentifier( uniqueId ); } /** * Creates the global identifier of a panel in the west. * @param uniqueCenterId the unique id of the owning {@link CContentArea}. * @return the global identifier */ public static String getWestIdentifier( String uniqueCenterId ){ return uniqueCenterId + " west"; } public CStation<?> getMatchingStation( CStationContainer container, CStation<?> station ){ if( container == this ){ return station; } if( container instanceof CContentArea ){ CContentArea other = (CContentArea)container; if( other.getStationCount() == getStationCount() ){ int index = other.indexOf( station ); if( index != -1 ){ return getStation( index ); } } } return null; } /** * A wrapper around the {@link FlapDockStation}s which represent the minimize * areas. * @author Benjamin Sigg */ private class MinimizeStation extends CMinimizeArea{ private CLocation location; public MinimizeStation( String id, CLocation location ){ this.location = location; init( control, id ); } public Path getTypeId(){ return TYPE_ID_MINIMIZE; } @Override public CLocation getStationLocation(){ return location; } } /** * A wrapper around the {@link SplitDockStation} that sits in the middle * of the area. * @author Benjamin Sigg */ private class CenterStation extends CGridArea { private CLocation location; public CenterStation( String id, CLocation location ){ super( control, id ); this.location = location; } public Path getTypeId(){ return TYPE_ID_CENTER; } @Override public CLocation getStationLocation(){ return location; } } }