/*
* 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.intern.ui;
import java.util.HashSet;
import java.util.Set;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.DockElement;
import bibliothek.gui.dock.accept.DockAcceptance;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CStation;
import bibliothek.gui.dock.common.group.CGroupMovement;
import bibliothek.gui.dock.common.intern.CControlAccess;
import bibliothek.gui.dock.common.intern.CDockable;
import bibliothek.gui.dock.common.intern.CommonDockable;
import bibliothek.gui.dock.common.intern.station.CommonDockStation;
import bibliothek.gui.dock.common.mode.CLocationMode;
import bibliothek.gui.dock.common.mode.CLocationModeManager;
import bibliothek.gui.dock.common.mode.ExtendedMode;
import bibliothek.gui.dock.util.DockUtilities;
import bibliothek.gui.dock.util.DockUtilities.DockVisitor;
import bibliothek.util.FrameworkOnly;
/**
* A {@link DockAcceptance} ensuring that the {@link CDockable#getWorkingArea()}
* property is respected.
* @author Benjamin Sigg
*/
@FrameworkOnly
public class WorkingAreaAcceptance implements DockAcceptance {
/** access to the inner parts of the {@link CControl} */
private CControlAccess control;
/**
* Creates a new acceptance
* @param control access to the {@link CControl}
*/
public WorkingAreaAcceptance( CControlAccess control ){
this.control = control;
}
public boolean accept( DockStation parent, Dockable child, Dockable next ) {
if( accept( parent, next ) ){
return getWorkingArea( child, true ) == getWorkingArea( next, true );
}
else {
return false;
}
}
public boolean accept( DockStation parent, Dockable child ) {
CLocationModeManager manager = control.getLocationManager();
if( manager.isOnTransaction() ){
CGroupMovement action = manager.getCurrentAction();
if( action == null || action.forceAccept( parent, child )){
return true;
}
}
ExtendedMode extendedMode = manager.childsExtendedMode( parent );
if( extendedMode == null ){
extendedMode = manager.getMode( child );
if( extendedMode == null ){
return true;
}
}
CLocationMode mode = manager.getMode( extendedMode.getModeIdentifier() );
if( mode == null )
return true;
if( !mode.respectWorkingAreas( parent ) ){
return true;
}
CStation<?> area = searchArea( parent );
return match( area, child );
}
/**
* Searches the first {@link CStation} with the working-area property set to <code>true</code> in the path to the root.
* @param element some element
* @return the first {@link CStation} that occurs on the path from
* <code>element</code> to the root and which is a working area
*/
private CStation<?> searchArea( DockElement element ){
DockStation station = element.asDockStation();
Dockable dockable = element.asDockable();
while( dockable != null || station != null ){
if( station instanceof CommonDockStation<?, ?>){
CStation<?> cstation = ((CommonDockStation<?,?>)station).getStation();
if( cstation.isWorkingArea() )
return cstation;
}
dockable = station == null ? null : station.asDockable();
station = dockable == null ? null : dockable.getDockParent();
}
return null;
}
/**
* Checks all {@link CDockable}s and compares their
* {@link CDockable#getWorkingArea() working area}
* with <code>area</code>.
* @param area a possible new parent
* @param dockable the root of the tree of elements to test
* @return <code>true</code> if all elements have <code>area</code> as
* preferred parent, <code>false</code> otherwise
*/
private boolean match( CStation<?> area, Dockable dockable ){
if( dockable instanceof CommonDockable ){
CStation<?> expectedWorkingArea = getWorkingArea( dockable, false );
CDockable self = ((CommonDockable)dockable).getDockable();
if( expectedWorkingArea != area && expectedWorkingArea != self )
// if we are moving a CWorkingArea around, then 'exectedWorkingArea == self'
return false;
}
DockStation station = dockable.asDockStation();
if( station != null ){
if( dockable instanceof CommonDockable ){
CStation<?> cstation = ((CommonDockable)dockable).getStation();
if( cstation != null && cstation.isWorkingArea() ){
return true;
}
}
return match( area, station );
}
else
return true;
}
private CStation<?> getWorkingArea( Dockable dockable, boolean excludeSelf ){
final Set<CStation<?>> workingAreas = new HashSet<CStation<?>>();
DockUtilities.visit( dockable, new DockVisitor() {
@Override
public void handleDockable( Dockable dockable ) {
if( dockable instanceof CommonDockable ){
CDockable fdockable = ((CommonDockable)dockable).getDockable();
CStation<?> workingArea = fdockable.getWorkingArea();
if( workingArea != null ){
workingAreas.add( workingArea );
}
}
}
});
if( dockable instanceof CommonDockable ){
CStation<?> station = ((CommonDockable) dockable).getDockable().asStation();
if( station != null && station.isWorkingArea() ){
workingAreas.remove( station );
}
}
if( workingAreas.size() == 1 ){
return workingAreas.iterator().next();
}
else{
return null;
}
}
/**
* Checks all {@link CDockable}s and compares their
* {@link CDockable#getWorkingArea() working area}
* with <code>area</code>.
* @param area a possible new parent
* @param station the root of the tree of elements to test
* @return <code>true</code> if all elements have <code>area</code> as
* preferred parent, <code>false</code> otherwise
*/
private boolean match( CStation<?> area, DockStation station ){
for( int i = 0, n = station.getDockableCount(); i < n; i++ ){
boolean result = match( area, station.getDockable( i ));
if( !result )
return false;
}
return true;
}
}