/*
* 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) 2012 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.station.split;
import java.awt.Rectangle;
import bibliothek.gui.DockController;
import bibliothek.gui.DockStation;
import bibliothek.gui.dock.SplitDockStation;
import bibliothek.gui.dock.station.span.Span;
import bibliothek.gui.dock.station.span.SpanCallback;
import bibliothek.gui.dock.station.span.SpanMode;
import bibliothek.gui.dock.station.span.SpanUsage;
import bibliothek.gui.dock.station.split.PutInfo.Put;
import bibliothek.gui.dock.themes.StationSpanFactoryValue;
import bibliothek.gui.dock.themes.ThemeManager;
/**
* This class is responsible for accessing and updating all {@link Span}s of a {@link SplitDockStation}.
* @author Benjamin Sigg
*/
public class SplitSpanStrategy {
private static final int LEFT = 0;
private static final int RIGHT = 1;
private static final int TOP = 2;
private static final int BOTTOM = 3;
private SplitDockStation station;
private StationSpanFactoryValue factory;
private PutInfo lastPut;
/**
* Creates a new strategy
* @param station the owner of this strategy, not <code>null</code>
*/
public SplitSpanStrategy( SplitDockStation station ){
this.station = station;
factory = new StationSpanFactoryValue( ThemeManager.SPAN_FACTORY + ".split", station ){
@Override
protected void changed(){
reset();
}
};
}
/**
* Mutates the {@link Span}s such that <code>info</code> shows up.
* @param info the current drop information or <code>null</code>
*/
public void setPut( PutInfo info ){
if( info != null ){
Put put = info.getPut();
if( put != Put.LEFT && put != Put.RIGHT && put != Put.TOP && put != Put.BOTTOM ){
info = null;
}
}
if( info != null && info.willHaveNoEffect() ){
return;
}
if( info != null && lastPut != null ){
if( info.getPut() == lastPut.getPut() && info.getNode() == lastPut.getNode()){
return;
}
}
if( lastPut != null ){
Span[] spans = getSpans( lastPut );
if( spans != null ){
spans[ putToPosition( lastPut.getPut() )].mutate( SpanMode.OFF );
}
}
lastPut = info;
if( info != null ){
Put put = info.getPut();
Span[] spans = getSpans( info );
if( spans != null ){
int position = putToPosition( put );
double divider = info.getDivider();
int size = 100;
SplitNode node = info.getNode();
if( node.getParent() == station.getRoot() ){
node = node.getParent();
}
switch( put ){
case LEFT:
size = (int)(node.getSize().width * divider);
break;
case RIGHT:
size = (int)(node.getSize().width * (1-divider));
break;
case TOP:
size = (int)(node.getSize().height * divider);
break;
case BOTTOM:
size = (int)(node.getSize().height * (1-divider));
break;
}
spans[ position ].configureSize( SpanMode.OPEN, size );
spans[ position ].mutate( SpanMode.OPEN );
}
}
}
/**
* Tells the index of the {@link Span} at side <code>put</code>.
* @param put one of the non-combining puts
* @return the index of the span
* @throws IllegalArgumentException if <code>put</code> does not describe one
* of the sides of a {@link SplitNode}
*/
public int putToPosition( Put put ){
switch( put ){
case LEFT:
return LEFT;
case RIGHT:
return RIGHT;
case TOP:
return TOP;
case BOTTOM:
return BOTTOM;
default:
throw new IllegalArgumentException( "not a side: " + put );
}
}
/**
* Gets the {@link Span}s that are used when <code>put</code> is active.
* @param put the drag and drop operation which may be active
* @return the {@link Span}s that would expand if <code>put</code> is active, can be <code>null</code>
*/
public Span[] getSpans( PutInfo put ){
SplitNode node = put.getNode();
if( node instanceof Leaf ){
return ((Leaf)node).getSpans();
}
if( node instanceof Root ){
return ((Root)node).getSpans();
}
else if( node.getParent() instanceof Root ){
return ((Root)node.getParent()).getSpans();
}
return null;
}
/**
* Immediately resets all {@link Span}s to have a size of <code>0</code>.
*/
public void unsetPut(){
if( lastPut != null ){
Span[] spans = getSpans( lastPut );
lastPut = null;
if( spans != null ){
for( int i = 0; i < 4; i++ ){
spans[i].set( SpanMode.OFF );
}
}
}
}
private void reset(){
station.getRoot().visit( new SplitNodeVisitor(){
public void handleRoot( Root root ){
root.createSpans();
}
public void handlePlaceholder( Placeholder placeholder ){
// ignore
}
public void handleNode( Node node ){
// ignore
}
public void handleLeaf( Leaf leaf ){
leaf.createSpans();
}
} );
}
/**
* Gets the factory which is responsible for creating new {@link Span}s.
* @return the factory
*/
public StationSpanFactoryValue getFactory(){
return factory;
}
/**
* Sets the {@link DockController} which is used by the {@link SplitDockStation}.
* @param controller the controller in whose realm this strategy works
*/
public void setController( DockController controller ){
factory.setController( controller );
}
/**
* Creates four {@link Span}s, one for each side of <code>leaf</code>.
* @param node the node which requires {@link Span}s
* @return the new set of {@link Span}s or <code>null</code>
*/
public Span[] createSpans( final SpanSplitNode node ){
if( factory.get() == null ){
return null;
}
Span[] result = new Span[4];
for( int i = 0; i < 4; i++ ){
result[i] = createSpan( i, node );
}
return result;
}
private Span createSpan( final int position, final SpanSplitNode node ){
return factory.get().create( new SpanCallback(){
public void resized(){
node.onSpanResize();
}
public boolean isVertical(){
return position == TOP || position == BOTTOM;
}
public boolean isHorizontal(){
return position == LEFT || position == RIGHT;
}
public DockStation getStation(){
return station;
}
public SpanUsage getUsage(){
return SpanUsage.INSERTING;
}
} );
}
/**
* Creates a new {@link Rectangle} within <code>bounds</code>, using
* <code>spans</code> to create some insets.
* @param bounds the boundaries to shrink
* @param node the node to evaluate
* @return the smaller boundaries
*/
public Rectangle modifyBounds( Rectangle bounds, SpanSplitNode node ){
Span[] spans = node.getSpans();
if( spans == null ){
return bounds;
}
Rectangle copy = new Rectangle( bounds );
int left = spans[LEFT].getSize();
int right = spans[RIGHT].getSize();
int top = spans[TOP].getSize();
int bottom = spans[BOTTOM].getSize();
copy.x += left;
copy.width -= left + right;
copy.y += top;
copy.height -= top + bottom;
return copy;
}
}