/*
* 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) 2013 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.behavior;
import bibliothek.gui.DockController;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.DockHierarchyLock;
import bibliothek.gui.dock.ScreenDockStation;
import bibliothek.gui.dock.SplitDockStation;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGridArea;
import bibliothek.gui.dock.common.CStation;
import bibliothek.gui.dock.common.intern.CControlAccess;
import bibliothek.gui.dock.common.mode.station.ExternalizedCSplitDockStationHandler;
import bibliothek.gui.dock.control.DockRegister;
import bibliothek.gui.dock.event.DockRegisterAdapter;
import bibliothek.gui.dock.event.DockRegisterListener;
import bibliothek.gui.dock.event.DockStationAdapter;
import bibliothek.util.Path;
import bibliothek.util.Todo;
import bibliothek.util.Todo.Compatibility;
import bibliothek.util.Todo.Priority;
import bibliothek.util.Todo.Version;
/**
* This {@link CStation} is intended to be set between a {@link ScreenDockStation} and a {@link Dockable}.
* It can clean up itself when it is no longer required.<br>
* This station will allow its children to be "maximized" on itself. Additionally the station can be "maximized" itself,
* but it cannot be "normalized" or "minimized" (it always remains floating on the screen).
* @author Benjamin Sigg
*/
@Todo(priority=Priority.MAJOR, target=Version.VERSION_1_1_2, compatibility=Compatibility.COMPATIBLE,
description="The 'unmaximize' button appears at the wrong location. And during drag and drop layout information is lost" +
" due to the listener that inserts the station after the dockable was inserted. Also perspectives will not yet work.")
public class ExternalizingCGridArea extends CGridArea {
/** The type of this area, returned by {@link #getTypeId()} */
public static final Path TYPE_ID = new Path( "dock", "ExternalizingCGridArea" );
/** Whether {@link #autoRemove()} may ever succeed */
private boolean autoRemove = true;
/** Every unique identifier of a {@link ExternalizingCGridArea} starts with this string */
public static final String UNIQUE_ID_PREFIX = "dock.ExternalizingCGridArea.";
/**
* Creates a new area.
* @param control the owner of this area
*/
public ExternalizingCGridArea( CControl control ){
this( control, createUniqueIdentifier( control ));
}
/**
* Creates a new area.
* @param control the owner of this area
* @param uniqueId the unique identifier of this station
*/
public ExternalizingCGridArea( CControl control, String uniqueId ){
super( control, uniqueId );
getStation().addDockStationListener( new AutoRemover() );
setTitleShown( true );
setMaximizingArea( true );
}
private static String createUniqueIdentifier( CControl control ){
int count = 0;
String id;
do{
id = UNIQUE_ID_PREFIX + System.currentTimeMillis() + "." + count;
count++;
}while( control.getSingleDockable( id ) != null );
return id;
}
@Override
protected ExternalizedCSplitDockStationHandler createSplitDockStationHandle( CControl control ){
return new ExternalizedCSplitDockStationHandler( this, control.getLocationManager() );
}
@Override
protected ExternalizedCSplitDockStationHandler getModeManagerHandle(){
return (ExternalizedCSplitDockStationHandler) super.getModeManagerHandle();
}
@Override
protected void install( CControlAccess access ){
super.install( access );
access.getLocationManager().getExternalizedMode().add( getModeManagerHandle().asExternalized() );
}
@Override
protected void uninstall( CControlAccess access ){
super.uninstall( access );
access.getLocationManager().getExternalizedMode().remove( getModeManagerHandle().asExternalized().getUniqueId() );
}
@Override
public boolean isMaximizable(){
return true;
}
@Override
public boolean isExternalizable(){
return true;
}
@Override
public boolean isMinimizable(){
return false;
}
@Override
public boolean isNormalizeable(){
return false;
}
@Override
protected boolean isNormalizingArea(){
return false;
}
public Path getTypeId(){
return TYPE_ID;
}
/**
* Sets whether this station can automatically delete itself from the application if the number of children
* drops to <code>0</code>.<br>
* The default value of this property is <code>true</code>.
* @param autoRemove whether automatic cleanup is enabled
*/
public void setAutoRemove( boolean autoRemove ){
this.autoRemove = autoRemove;
}
/**
* Tells whether this station can automatically remove itself from its parent and the {@link CControl}.
* @return whether automatic cleanup is enabled
* @see #setAutoRemove(boolean)
*/
public boolean isAutoRemove(){
return autoRemove;
}
/**
* Queues up a call to {@link #autoRemove()}
*/
protected void tryAutoRemove(){
CControl control = getControl();
if( control != null ){
DockHierarchyLock lock = control.getController().getHierarchyLock();
lock.onRelease( new Runnable(){
public void run(){
autoRemove();
}
} );
}
}
/**
* Removes this station from its parent and from the {@link CControl} if it no longer has any children,
* and if the layout is not currently frozen.
*/
private void autoRemove(){
if( !autoRemove ){
return;
}
CControl control = getControl();
if( control != null ){
DockRegister register = control.getController().getRegister();
if( register.isStalled() ){
register.addDockRegisterListener( new DelayedAutoRemove() );
}
else{
SplitDockStation station = getStation();
if( station.getDockableCount() == 0 ){
DockStation parent = station.getDockParent();
if( parent != null ){
parent.drag( station );
}
control.removeDockable( this );
control.removeStation( this );
}
}
}
}
/**
* A listener that is added to the {@link DockStation} that is used as delegate by this {@link CStation}. If the
* last child of the station is removed, then {@link ExternalizingCGridArea#tryAutoRemove()} is called.
* @author Benjamin Sigg
*/
private class AutoRemover extends DockStationAdapter{
@Override
public void dockableRemoved( DockStation station, Dockable dockable ){
if( station.getDockableCount() == 0 ){
tryAutoRemove();
}
}
}
/**
* Waits for the event {@link DockRegisterListener#registerUnstalled(bibliothek.gui.DockController)}, then
* calls {@link ExternalizingCGridArea#tryAutoRemove()}.
* @author Benjamin Sigg
*/
private class DelayedAutoRemove extends DockRegisterAdapter{
@Override
public void registerUnstalled( DockController controller ){
controller.getRegister().removeDockRegisterListener( this );
tryAutoRemove();
}
}
}