/*
* 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 Herve Guillaume, 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
*
* Herve Guillaume
* rvguillaume@hotmail.com
* FR - France
*
* Benjamin Sigg
* benjamin_sigg@gmx.ch
* CH - Switzerland
*/
package bibliothek.gui.dock.station.toolbar.group;
import bibliothek.gui.DockController;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.Orientation;
import bibliothek.gui.dock.ToolbarGroupDockStation;
import bibliothek.gui.dock.station.span.Span;
import bibliothek.gui.dock.station.span.SpanCallback;
import bibliothek.gui.dock.station.span.SpanFactory;
import bibliothek.gui.dock.station.span.SpanMode;
import bibliothek.gui.dock.station.span.SpanUsage;
import bibliothek.gui.dock.station.support.PlaceholderListItem;
import bibliothek.gui.dock.station.toolbar.layout.DockablePlaceholderToolbarGrid;
import bibliothek.gui.dock.themes.StationSpanFactoryValue;
import bibliothek.gui.dock.themes.ThemeManager;
/**
* This class keeps track of the currently used {@link Span}s of a {@link ToolbarGroupDockStation}. To
* be exact: the class offers a {@link Span} for each gap between columns or lines, and is able to update
* the size of the spans when knowing the current drag and drop operation.
* @author Benjamin Sigg
*/
public abstract class ToolbarGroupSpanStrategy<P extends PlaceholderListItem<Dockable>> {
/** all columns and cells that are currently available */
private DockablePlaceholderToolbarGrid<P> grid;
/** the size of the gaps between columns */
private Span[] columnSpans = new Span[]{};
/** the size of the gaps between lines */
private Span[][] lineSpans = new Span[][]{};
/** the factory responsible for creating new {@link Span}s */
private StationSpanFactoryValue factory;
/** the station using this strategy */
private ToolbarGroupDockStation station;
/** tells the minimum size for each gap */
private ToolbarGroupDividerStrategy dividers = ToolbarGroupDividerStrategy.NULL;
private int currentColumn = -1;
private int currentLine = -1;
/**
* Creates a new strategy
* @param grid all the columns and theirs cells
* @param station the station that is using this strategy
*/
public ToolbarGroupSpanStrategy( DockablePlaceholderToolbarGrid<P> grid, ToolbarGroupDockStation station ){
this.grid = grid;
this.station = station;
factory = new StationSpanFactoryValue( ThemeManager.SPAN_FACTORY + ".toolbar.group", station ){
@Override
protected void changed(){
reset( true );
}
};
}
/**
* Sets the strategy for painting between the {@link Dockable}s. The strategy is required
* to find the minimum size of all gaps.
* @param dividers the new strategy, can be <code>null</code>
*/
public void setDividers( ToolbarGroupDividerStrategy dividers ){
if( dividers == null ){
dividers = ToolbarGroupDividerStrategy.NULL;
}
this.dividers = dividers;
handleResized();
}
/**
* Called if the size of a {@link Span} changed and the station must be resized.
*/
protected abstract void handleResized();
/**
* Sets the {@link DockController} in whose realm this strategy works, allows to access
* the current {@link SpanFactory}.
* @param controller the new controller, can be <code>null</code>
*/
public void setController( DockController controller ){
factory.setController( controller );
}
/**
* Tests which columns and cells currently exist, and may create or delete {@link Span}
* if necessary.
* @param force if <code>true</code>, then the {@link Span}s get recreated even if the number of
* columns and lines did not change
*/
public void reset( boolean force ){
if( force || isOutdated() ){
columnSpans = new Span[ grid.getColumnCount()+1 ];
for( int i = 0; i < columnSpans.length; i++ ){
columnSpans[i] = createSpan( true );
}
lineSpans = new Span[ grid.getColumnCount() ][];
for( int i = 0; i < lineSpans.length; i++ ){
lineSpans[i] = new Span[ grid.getLineCount( i )+1 ];
for( int j = 0; j < lineSpans[i].length; j++ ){
lineSpans[i][j] = createSpan( false );
}
}
currentColumn = -1;
currentLine = -1;
}
}
private boolean isOutdated(){
if( columnSpans.length != grid.getColumnCount()+1 ){
return true;
}
for( int i = 0; i < lineSpans.length; i++ ){
if( lineSpans[i].length != grid.getLineCount( i ) + 1 ){
return true;
}
}
return false;
}
private Span createSpan( final boolean column ){
return factory.create( new SpanCallback(){
@Override
public void resized(){
handleResized();
}
@Override
public boolean isVertical(){
return (station.getOrientation() == Orientation.HORIZONTAL) ^ column;
}
@Override
public boolean isHorizontal(){
return (station.getOrientation() == Orientation.VERTICAL) ^ column;
}
@Override
public SpanUsage getUsage(){
return SpanUsage.INSERTING;
}
@Override
public DockStation getStation(){
return station;
}
});
}
/**
* Gets the size of the gap left of the column <code>index</code>. If there are <code>n</code>
* columns, <code>index=n</code> will return the size of the right most gap.
* @param index the index of a column
* @return the size of the gap
*/
public int getColumn( int index ){
return Math.max( columnSpans[ index ].getSize(), dividers.getColumn( index ));
}
/**
* Gets the size of the gap top of the cell <code>index</code> of <code>column</code>. If there
* are <code>n</code> cells, then <code>index=n</code> will return the size of the bottom most gap.
* @param column the column in which to search
* @param index the index of the cell
* @return the size of the gap
*/
public int getLine( int column, int index ){
return Math.max( lineSpans[ column ][ index ].getSize(), dividers.getLine( column, index ));
}
/**
* Mutates the {@link Span}s to present an insertion into column <code>column</code>.
* @param column the column where an item is inserted
*/
public void mutate( int column ){
mutate( column, -1 );
}
/**
* Mutates the {@link Span}s to present an insertion into <code>column</code> at <code>line</code>.
* @param column the column where the insertion is happening
* @param line the line into which an item is inserted
*/
public void mutate( int column, int line ){
if( currentColumn != column || currentLine != line ){
currentColumn = column;
currentLine = line;
if( line == -1 ){
for( int i = 0; i < columnSpans.length; i++ ){
if( i == column ){
columnSpans[ i ].mutate( SpanMode.OPEN );
}
else{
columnSpans[ i ].mutate( SpanMode.OFF );
}
}
for( Span[] spans : lineSpans ){
for( Span span : spans ){
span.mutate( SpanMode.OFF );
}
}
}
else{
for( Span span : columnSpans ){
span.mutate( SpanMode.OFF );
}
for( int i = 0; i < lineSpans.length; i++ ){
for( int j = 0; j < lineSpans[i].length; j++ ){
if( i == column && j == line ){
lineSpans[i][j].mutate( SpanMode.OPEN );
}
else{
lineSpans[i][j].mutate( SpanMode.OFF );
}
}
}
}
}
}
}