bibliothek.gui.dock.station.toolbar.layout.PlaceholderToolbarGrid Maven / Gradle / Ivy
/*
* 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
* [email protected]
* FR - France
*
* Benjamin Sigg
* [email protected]
* CH - Switzerland
*/
package bibliothek.gui.dock.station.toolbar.layout;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.station.support.ConvertedPlaceholderListItem;
import bibliothek.gui.dock.station.support.PlaceholderList;
import bibliothek.gui.dock.station.support.PlaceholderList.Filter;
import bibliothek.gui.dock.station.support.PlaceholderList.Level;
import bibliothek.gui.dock.station.support.PlaceholderListItem;
import bibliothek.gui.dock.station.support.PlaceholderListItemAdapter;
import bibliothek.gui.dock.station.support.PlaceholderMap;
import bibliothek.gui.dock.station.support.PlaceholderMap.Key;
import bibliothek.gui.dock.station.support.PlaceholderStrategy;
import bibliothek.gui.dock.station.support.PlaceholderStrategyListener;
import bibliothek.gui.dock.station.toolbar.layout.GridPlaceholderList.Column;
import bibliothek.gui.dock.station.toolbar.layout.GridPlaceholderList.ColumnItem;
import bibliothek.util.Path;
/**
* A {@link PlaceholderToolbarGrid} behaves like a list of {@link PlaceholderList}s.
*
* @author Benjamin Sigg
* @param the kind of object that should be treated as {@link Dockable}
* @param the kind of object that should be treated as {@link DockStation}
* @param the type of item which represents a {@link Dockable}
*/
public abstract class PlaceholderToolbarGrid> {
/** defines the order of all columns (visible and invisible columns) */
private GridPlaceholderList columns;
/** the currently used strategy to detect invalid placeholders */
private PlaceholderStrategy strategy;
/**
* this listener is added to {@link #strategy} if {@link #bound} is
* true
*/
private final PlaceholderStrategyListener strategyListener = new PlaceholderStrategyListener(){
@Override
public void placeholderInvalidated( Set placeholders ){
purge();
}
};
/** tells whether this grid is currently used */
private boolean bound = false;
/**
* Initializes all fields of this object.
*/
protected void init(){
columns = createGrid();
}
/**
* Creates one column.
*
* @return a new, empty list
*/
protected abstract PlaceholderList createColumn();
/**
* Creates a new {@link GridPlaceholderList}.
*
* @return the new, empty grid
*/
protected abstract GridPlaceholderList createGrid();
/**
* Gets all placeholders that are associated with dockable
.
*
* @param dockable
* some element used by this grid
* @return the placeholders
*/
protected abstract Set getPlaceholders( D dockable );
/**
* Called if item
was added to the column column
.
* @param column a visible column
* @param columnIndex the index of the column
* @param item a new item of column
* @param itemIndex the index of the new item
*/
protected abstract void onInserted( PlaceholderList column, int columnIndex, P item, int itemIndex );
/**
* Called if item
was removed to the column column
.
* @param column a visible column
* @param columnIndex the index of the column
* @param item the removed item of column
* @param itemIndex the index the item had before removing
*/
protected abstract void onRemoved( PlaceholderList column, int columnIndex, P item, int itemIndex );
/**
* Called if a new column column
was added to this grid. The column may not contain any elements when
* this method is called.
* @param column the new column
* @param index the index of the new column
*/
protected abstract void onInserted( PlaceholderList column, int index );
/**
* Called if the column column
was removed from this grid. The column may still contain elements when it
* is removed.
* @param column the removed column
* @param index the index the column had before it was removed
*/
protected abstract void onRemoved( PlaceholderList column, int index );
/**
* Called if all columns were inserted at the same time. This means that the grid
* previously was completely empty.
*/
protected abstract void onInserted();
/**
* Called if all columns were removed from this grid
*/
protected abstract void onRemoved();
/**
* Removes all content from this grid.
*/
public void clear(){
purge();
for( final Column column : columns.dockables() ) {
column.getList().unbind();
column.getList().setStrategy( null );
}
columns.clear();
onRemoved();
}
/**
* Adds the item item
to the non-empty column
* column
into position line
. This method may add
* a new column in order to store item
.
*
* @param column
* the column in which to store item
* @param line
* the index within column
* @param item
* the item to store, not null
* @throws IllegalArgumentException
* if item
is null
* @throws IllegalStateException
* if there is no {@link PlaceholderStrategy} set
*/
public void insert( int column, int line, P item ){
if( item == null ) {
throw new IllegalArgumentException( "item must not be null" );
}
final PlaceholderList list = getColumn( column );
if( list == null ) {
insert( column, item, true );
}
else {
int index = Math.min( line, list.dockables().size() );
list.dockables().add( index, item );
ensureRemoved( list, item );
onInserted( list, column, item, index );
}
}
/**
* Adds the item item
to a new column, the new column will have
* the index columnIndex
. If columnIndex
is out of
* bounds, then the new column will be added as near as possible to the
* preferred position. This method will try to reuse an empty column, if one
* is available at the desired location.
*
* @param columnIndex
* the column to add
* @param item
* the item to store, not null
*/
public void insert( int columnIndex, P item ){
insert( columnIndex, item, true );
}
/**
* Adds the item item
to a new column, the new column will have
* the index columnIndex
. If columnIndex
is out of
* bounds, then the new column will be added as near as possible to the
* preferred position.
*
* @param columnIndex
* the column to add
* @param item
* the item to store, not null
* @param reuse
* if false
then a new column will be built in any
* case, if true
then this grid tries to reuse an
* existing yet empty column if possible
*/
public void insert( int columnIndex, P item, boolean reuse ){
final PlaceholderList columnList = createColumn();
final Column column = columns.createColumn( columnList );
boolean added = false;
int addedColumnIndex = -1;
if( reuse ) {
int baseIndex;
if( columns.dockables().size() > 0 ) {
baseIndex = columns.levelToBase( Math.max( 0, Math.min( columns.dockables().size() - 1, columnIndex ) ), Level.DOCKABLE );
baseIndex--;
}
else {
baseIndex = 0;
}
if( (baseIndex >= 0) && (baseIndex < columns.list().size()) ) {
final PlaceholderList, ?, Column>.Item columnItem = columns.list().get( baseIndex );
if( columnItem.getDockable() == null ) {
final PlaceholderMap map = columnItem.getPlaceholderMap();
if( map != null ) {
columnList.read( map, columns.getConverter() );
}
columnItem.setDockable( column );
addedColumnIndex = columns.dockables().indexOf( column );
onInserted( column.getList(), addedColumnIndex );
added = true;
}
}
}
columnList.dockables().add( item );
if( added ){
onInserted( columnList, addedColumnIndex, item, columnList.dockables().size()-1 );
}
else{
int index = Math.max( 0, Math.min( columnIndex, columns.dockables().size() ) );
columns.dockables().add( index, column );
onInserted( columnList, index );
onInserted( columnList, index, item, columnList.dockables().size()-1 );
}
if( bound ) {
columnList.setStrategy( strategy );
}
ensureRemoved( columnList, item );
}
/**
* Moves the item at sourceColumn/sourceLine
to
* destinationColumn/destinationLine
. The operation behaves as
* if the item would first be removed from the source position, and
* afterwards inserted at the destination position.
*
* @param sourceColumn
* the column in which to find the item, only includes non-empty
* columns
* @param sourceLine
* the line in the column in which to find the item
* @param destinationColumn
* the column in which to insert the item
* @param destinationLine
* the line at which to insert the item
* @param destinationLevel
* the level at which to find destinationColumn
,
* will be converted to an index from {@link Level#BASE}
* @throws IllegalArgumentException
* if any index is out of bounds
*/
public void move( int sourceColumn, int sourceLine, int destinationColumn, int destinationLine, Level destinationLevel ){
PlaceholderList source = columns.dockables().get( sourceColumn ).getList();
final Filter sourceList = source.dockables();
int destinationColumnIndex = -1;
if( destinationColumn == columns.size( destinationLevel ) ) {
destinationColumn = columns.size( Level.BASE );
}
else if( destinationColumn >= 0 ) {
destinationColumn = columns.levelToBase( destinationColumn, destinationLevel );
}
if( (sourceLine < 0) || (sourceLine >= sourceList.size()) ) {
throw new IllegalArgumentException( "sourceLine out of bounds: " + sourceLine );
}
if( (destinationColumn < -1) || (destinationColumn > columns.list().size()) ) {
throw new IllegalArgumentException( "destinationColumn out of bounds: " + destinationColumn );
}
final P value = sourceList.get( sourceLine );
PlaceholderList list;
if( (destinationColumn == -1) || (destinationColumn == columns.list().size()) ) {
list = createColumn();
if( destinationLine != 0 ) {
throw new IllegalArgumentException( "destinationLine is out of bounds: " + destinationLine );
}
final PlaceholderList, ColumnItem, Column>.Item item = columns.new Item( columns.createColumn( list ) );
if( destinationColumn == -1 ) {
columns.list().add( 0, item );
}
else {
columns.list().add( item );
}
destinationColumnIndex = columns.dockables().indexOf( item.getDockable() );
onInserted( list, destinationColumnIndex );
}
else {
final PlaceholderList, ?, Column>.Item item = columns.list().get( destinationColumn );
if( item.getDockable() == null ) {
list = createColumn();
if( destinationLine != 0 ) {
throw new IllegalArgumentException( "destinationLine is out of bounds: " + destinationLine );
}
item.setDockable( columns.createColumn( list ) );
destinationColumnIndex = columns.dockables().indexOf( item.getDockable() );
onInserted( list, destinationColumnIndex );
}
else {
list = item.getDockable().getList();
if( (destinationLine < 0) || (destinationLine > list.dockables().size()) ) {
throw new IllegalArgumentException( "destinationLine out of bounds: " + destinationLine );
}
destinationColumnIndex = columns.baseToLevel( destinationColumn, Level.DOCKABLE );
}
}
P moved = sourceList.get( sourceLine );
list.dockables().move( sourceList, sourceLine, destinationLine );
ensureRemoved( list, value );
onRemoved( source, sourceColumn, moved, sourceLine );
onInserted( list, destinationColumnIndex, moved, destinationLine );
purge();
}
/**
* Tries to put item
into this list at location
* placeholder
. If there is already an element at
* placeholder
, then the old item is silenlty removed and the
* new item inserted. This method may create a new non-empty column if
* necessary.
*
* @param placeholder
* the name of the item
* @param item
* the item to insert
* @return true
if insertion was a success, false
* otherwise
*/
public boolean put( Path placeholder, P item ){
final int listIndex = columns.getListIndex( placeholder );
if( listIndex == -1 ) {
return false;
}
final PlaceholderList, ?, Column>.Item listItem = columns.list().get( listIndex );
Column column = listItem.getDockable();
if( column != null ) {
int columnIndex = columns.dockables().indexOf( column );
// int removedIndex = column.getList().getDockableIndex( placeholder );
int replacingListIndex = column.getList().getListIndex( placeholder );
if( replacingListIndex >= 0 ){
P removed = column.getList().list().get( replacingListIndex ).getDockable();
int size = column.getList().dockables().size();
int removedIndex = column.getList().put( placeholder, item );
if( removed != null ){
onRemoved( column.getList(), columnIndex, removed, removedIndex );
if( size == 0 && item == null ){
onRemoved( column.getList(), columnIndex );
listItem.setDockable( null );
}
}
if( item != null ){
if( size == 0 ){
onInserted( column.getList(), columnIndex );
}
onInserted( column.getList(), columnIndex, item, column.getList().dockables().indexOf( item ) );
}
return true;
}
}
final PlaceholderMap map = listItem.getPlaceholderMap();
if( map != null ) {
final PlaceholderList list = createColumn();
list.read( map, columns.getConverter() );
column = columns.createColumn( list );
listItem.setDockable( column );
int columnIndex = columns.dockables().indexOf( column );
onInserted( list, columnIndex );
int insertIndex = column.getList().put( placeholder, item );
if( insertIndex == -1 ) {
listItem.setDockable( null );
onRemoved( list, columnIndex );
return false;
}
else {
listItem.setPlaceholderMap( null );
ensureRemoved( list, placeholder );
onInserted( list, columnIndex, item, insertIndex );
}
return true;
}
return false;
}
/**
* Stores the placeholder placeholder
in the designated column.
*
* @param column
* the column in which to add placeholder
, only
* includes non-empty columns
* @param line
* the line in which to add placeholder
* @param placeholder
* the placeholder to store
*/
public void addPlaceholder( int column, int line, Path placeholder ){
columns.dockables().addPlaceholder( column, placeholder );
final Column item = columns.dockables().get( column );
item.getList().dockables().addPlaceholder( line, placeholder );
ensureRemoved( item.getList(), placeholder );
}
/**
* Inserts placeholder
into column column
at line
. This
* method may create a new column if column
is as big as the grid.
* @param column the column into which to insert placeholder
, includes
* empty columns.
* @param line the line into which to insert placeholder
* @param placeholder the new placeholder, not null
*/
public void insertPlaceholder( int column, int line, Path placeholder ){
if( column == columns.list().size() ){
columns.list().insertPlaceholder( column, placeholder );
}
else{
columns.list().addPlaceholder( column, placeholder );
}
PlaceholderList, ColumnItem, Column>.Item item = columns.list().get( column );
if( item.getDockable() == null ){
item.setDockable( columns.createColumn( createColumn() ));
}
Filter.Item> lineList = item.getDockable().getList().list();
if( line == lineList.size() ){
lineList.insertPlaceholder( line, placeholder );
}
else{
lineList.addPlaceholder( line, placeholder );
}
}
/**
* Removes item
from this grid, but leaves a placeholder for
* the item.
* @param item the item to remove
* @return true
if item
was found and removed
*/
public boolean remove( P item ){
boolean result = false;
int columnIndex = -1;
for( final Column column : columns.dockables() ) {
columnIndex++;
int index = column.getList().dockables().indexOf( item );
if( index >= 0 ){
column.getList().remove( item );
onRemoved( column.getList(), columnIndex, item, index );
result = true;
}
}
purge();
return result;
}
private void ensureRemoved( PlaceholderList ignore, P item ){
final Set placeholders = getPlaceholders( item.asDockable() );
ensureRemoved( ignore, placeholders );
}
private void ensureRemoved( PlaceholderList ignore, Path placeholder ){
final Set set = new HashSet();
set.add( placeholder );
ensureRemoved( ignore, set );
}
private void ensureRemoved( PlaceholderList ignore, Set placeholders ){
final Iterator, ColumnItem, Column>.Item> iter = columns.list().iterator();
while( iter.hasNext() ) {
final PlaceholderList, ?, Column>.Item item = iter.next();
if( (item.getDockable() == null) || (item.getDockable().getList() != ignore) ) {
item.removeAll( placeholders );
if( (item.getPlaceholderSet() == null) && item.isPlaceholder() ) {
iter.remove();
}
}
}
for( final Column column : columns.dockables() ) {
if( column.getList() != ignore ) {
column.getList().removeAll( placeholders );
}
}
purge();
}
/**
* Tells in which non-empty column dockable
is.
*
* @param dockable
* the item to search
* @return the column of the dockable or -1
if not found
*/
public int getColumn( D dockable ){
int index = 0;
final Iterator> columns = columns();
while( columns.hasNext() ) {
for( final P item : columns.next().dockables() ) {
if( item.asDockable() == dockable ) {
return index;
}
}
index++;
}
return -1;
}
/**
* Gets the index of the first column that contains placeholder
* .
*
* @param placeholder
* the placeholder to search
* @return the first column with placeholder
or -1 if not
* found, this includes empty columns
*/
public int getColumn( Path placeholder ){
int index = 0;
for( final PlaceholderList, ?, Column>.Item item : columns.list() ) {
if( item.hasPlaceholder( placeholder ) ) {
return index;
}
index++;
}
return -1;
}
/**
* Tells at which position dockable
is within its column.
*
* @param dockable
* the item to search
* @return the location of dockable
*/
public int getLine( D dockable ){
final int column = getColumn( dockable );
if( column == -1 ) {
return -1;
}
return getLine( column, dockable );
}
/**
* Tells at which position dockable
is within the column
* column
*
* @param column
* the index of the non-empty column to search
* @param dockable
* the item to search
* @return the location of dockable
*/
public int getLine( int column, D dockable ){
final PlaceholderList list = getColumn( column );
int index = 0;
for( final P item : list.dockables() ) {
if( item.asDockable() == dockable ) {
return index;
}
index++;
}
return -1;
}
/**
* Tells at which line placeholder
apperas in the first column
* that contains placeholder
. This includes empty columns.
*
* @param placeholder
* the placeholder to search
* @return the line at which placeholder
was found
*/
public int getLine( Path placeholder ){
final int column = getColumn( placeholder );
if( column == -1 ) {
return -1;
}
return getLine( column, placeholder );
}
/**
* Tells at which line placeholder
appears in the column
* column
.
*
* @param column
* the index of the column, this includes empty columns
* @param placeholder
* the placeholder to search
* @return the index the item would have or -1 if placeholder
* was not found
*/
public int getLine( int column, Path placeholder ){
final PlaceholderList, ?, Column>.Item item = columns.list().get( column );
if( item.getDockable() == null ) {
if( item.hasPlaceholder( placeholder ) ) {
return 0;
}
else {
return -1;
}
}
else {
return item.getDockable().getList().list().indexOfPlaceholder( placeholder );
}
}
/**
* Tells whether this {@link PlaceholderToolbarGrid} knows a column which
* contains the placeholder placeholder
, this includes empty
* columns.
*
* @param placeholder
* the placeholder to search
* @return true
if placeholder
was found
*/
public boolean hasPlaceholder( Path placeholder ){
final int listIndex = columns.getListIndex( placeholder );
if( listIndex == -1 ) {
return false;
}
final PlaceholderList, ?, Column>.Item item = columns.list().get( listIndex );
final Column column = item.getDockable();
if( column != null ) {
return column.getList().hasPlaceholder( placeholder );
}
final PlaceholderMap map = item.getPlaceholderMap();
if( map != null ) {
for( final Key key : map.getPlaceholders() ) {
if( key.contains( placeholder ) ) {
return true;
}
}
}
return false;
}
/**
* Gets the total count of items stored in this grid.
*
* @return the total amount of items
*/
public int size(){
int sum = 0;
final Iterator> iter = columns();
while( iter.hasNext() ) {
sum += iter.next().dockables().size();
}
return sum;
}
/**
* Gets the index
'th item of this grid.
*
* @param index
* the index of the item
* @return the item
* @throws IllegalArgumentException
* if index
is not valid
*/
public P get( int index ){
if( index < 0 ) {
throw new IllegalArgumentException( "index must not be < 0" );
}
final Iterator> iter = columns();
while( iter.hasNext() ) {
final Filter dockables = iter.next().dockables();
final int size = dockables.size();
if( index < size ) {
return dockables.get( index );
}
else {
index -= size;
}
}
throw new IllegalArgumentException( "index must not be >= size" );
}
/**
* Gets the item that represents dockable
*
* @param dockable
* the dockable to search
* @return the item that represents dockable
or
* null
if not found
*/
public P get( D dockable ){
final Iterator
iter = items();
while( iter.hasNext() ) {
final P next = iter.next();
if( next.asDockable() == dockable ) {
return next;
}
}
return null;
}
/**
* Searches the item which is at the location of placeholder
.
*
* @param placeholder
* some placeholder that may or may not be known to this grid
* @return the item at placeholder
or null
either
* if placeholder
was not found or if there is no item
* stored
*/
public P get( Path placeholder ){
final int listIndex = columns.getListIndex( placeholder );
if( listIndex == -1 ) {
return null;
}
final PlaceholderList, ?, Column>.Item item = columns.list().get( listIndex );
final Column column = item.getDockable();
if( column == null ) {
return null;
}
return column.getList().getDockableAt( placeholder );
}
/**
* Gets an iterator over all columns, including the columns with no content.
* This does not include columns with no list (columns that consist only of
* placeholders).
*
* @return all columns
*/
protected Iterator> allColumns(){
return new Iterator>(){
private final Iterator> items = columns.dockables().iterator();
private PlaceholderList current;
private int currentIndex = -1;
@Override
public boolean hasNext(){
return items.hasNext();
};
@Override
public PlaceholderList next(){
current = items.next().getList();
currentIndex++;
return current;
}
@Override
public void remove(){
items.remove();
onRemoved( current, currentIndex-- );
}
};
}
/**
* Gets an iterator over all non-empty columns. The iterator does not
* support modifications nor is it concurrent.
*
* @return the iterator
*/
protected Iterator> columns(){
return new Iterator>(){
private final Iterator> items = columns.dockables().iterator();
private PlaceholderList next;
private void forward(){
next = null;
while( (next == null) && items.hasNext() ) {
final PlaceholderList column = items.next().getList();
if( column.dockables().size() > 0 ) {
next = column;
}
}
}
@Override
public boolean hasNext(){
if( (next == null) && items.hasNext() ) {
forward();
}
return next != null;
}
@Override
public PlaceholderList next(){
final PlaceholderList result = next;
forward();
return result;
}
@Override
public void remove(){
throw new UnsupportedOperationException();
}
};
}
/**
* Gets an iterator that visits all items of this grid.
*
* @return the iterator
*/
public Iterator items(){
return new Iterator
(){
private final Iterator> columns = columns();
private Iterator items = null;
private PlaceholderList currentList;
private int currentListIndex = -1;
private P currentItem;
private int currentItemIndex = -1;
private boolean requiresdPurge = false;
private void validate(){
while( ((items == null) || !items.hasNext()) && columns.hasNext() ) {
currentList = columns.next();
currentItemIndex = -1;
currentListIndex++;
items = currentList.dockables().iterator();
}
}
@Override
public boolean hasNext(){
validate();
boolean result = (items != null) && items.hasNext();
if( !result ){
if( requiresdPurge ){
purge();
requiresdPurge = false;
}
}
return result;
}
@Override
public P next(){
validate();
currentItem = items.next();
return currentItem;
}
@Override
public void remove(){
if( items == null ) {
throw new IllegalStateException( "no item selected" );
}
items.remove();
onRemoved( currentList, currentListIndex, currentItem, currentItemIndex-- );
requiresdPurge = true;
}
};
}
/**
* Gets the number of columns that are currently stored in this grid. Empty
* columns are excluded.
*
* @return the total number of non-empty columns
*/
public int getColumnCount(){
return columns.dockables().size();
}
/**
* Tells how many items are currently stored at the non-empty column with
* index column
.
*
* @param column
* the index of a non-empty column
* @return the size of the column
*/
public int getLineCount( int column ){
return columns.dockables().get( column ).getList().dockables().size();
}
/**
* Gets the total number of columns, this includes empty columns.
*
* @return the total number of columns
*/
public int getTotalColumnCount(){
return columns.list().size();
}
/**
* Gets an iterator over the contents of the index
'th non-empty
* column.
*
* @param index
* the index of the non-empty column
* @return the content of the non-empty column
*/
public Iterator getColumnContent( final int index ){
final PlaceholderList list = getColumn( index );
if( list == null ) {
throw new IllegalArgumentException( "index is out of bounds" );
}
return new Iterator(){
private Iterator
delegate = list.dockables().iterator();
private P current;
private int currentIndex = -1;
private boolean requiresPurge = false;
@Override
public boolean hasNext(){
boolean result = delegate.hasNext();
if( !result ){
if( requiresPurge ){
purge();
requiresPurge = false;
}
}
return result;
}
@Override
public P next(){
current = delegate.next();
currentIndex++;
return current;
}
@Override
public void remove(){
delegate.remove();
onRemoved( list, index, current, currentIndex-- );
}
};
}
/**
* Gets the non-empty column with index index
. Subclasses should not modify the returned list.
*
* @param index
* the index of the column
* @return the non-empty column or null
if no such column
* exists
*/
protected PlaceholderList getColumn( int index ){
if( index < 0 ) {
return null;
}
final Filter> dockables = columns.dockables();
if( index >= dockables.size() ) {
return null;
}
return dockables.get( index ).getList();
}
/**
* Informs this grid that it is actually used and that it should be allowed
* to add observers to various resources.
*/
public void bind(){
columns.bind();
final Iterator> columns = allColumns();
while( columns.hasNext() ) {
columns.next().bind();
}
if( !bound ) {
bound = true;
if( strategy != null ) {
strategy.addListener( strategyListener );
purge();
}
}
}
/**
* Informs this grid that it is no longer used and that is should remove any
* observers.
*/
public void unbind(){
columns.unbind();
final Iterator> columns = allColumns();
while( columns.hasNext() ) {
columns.next().unbind();
}
if( bound ) {
bound = false;
if( strategy != null ) {
strategy.removeListener( strategyListener );
}
}
}
/**
* Sets the {@link PlaceholderStrategy} which is to be used by this grid.
*
* @param strategy
* the new strategy, can be null
*/
public void setStrategy( PlaceholderStrategy strategy ){
if( (this.strategy != null) && bound ) {
this.strategy.removeListener( strategyListener );
}
this.strategy = strategy;
columns.setStrategy( strategy );
final Iterator> columns = allColumns();
while( columns.hasNext() ) {
columns.next().setStrategy( strategy );
}
if( (this.strategy != null) && bound ) {
this.strategy.addListener( strategyListener );
purge();
}
}
/**
* Gets the {@link PlaceholderStrategy} that is currently used by this grid.
*
* @return the strategy, can be null
*/
public PlaceholderStrategy getStrategy(){
return strategy;
}
/**
* Removes any dead element from {@link #columns}.
*/
private void purge(){
purge( false );
}
/**
* Removes any dead element from {@link #columns}.
* @param silent if true
then no events are fired when a column is removed
*/
private void purge( boolean silent ){
int index = -1;
for( final PlaceholderList, ColumnItem, Column>.Item item : columns.list() ) {
final Column column = item.getDockable();
if( column != null ) {
index++;
PlaceholderList list = column.getList();
if( list.dockables().size() == 0 ) {
item.setPlaceholderMap( list.toMap( new PlaceholderListItemAdapter>(){
@Override
public ConvertedPlaceholderListItem convert( int index, PlaceholderListItem dockable ){
throw new IllegalStateException( "the list is supposed to have no children, so this conversion method must never be called" );
}
} ) );
item.setDockable( null );
if( !silent ){
onRemoved( list, index-- );
}
}
}
}
}
/**
* Called by {@link #toMap(Map)}, this method should read persistent data
* from dockable
and write that data into item
.
*
* @param dockable
* the dockable to read
* @param item
* the item to write into
*/
protected abstract void fill( D dockable, ConvertedPlaceholderListItem item );
/**
* Converts this grid into a {@link PlaceholderMap} using
* identifiers
to remember which {@link Dockable} was a which
* position.
*
* @param identifiers
* identifiers for all children of the {@link DockStation} using
* this grid
* @return the map that persistently stores all data of this grid
*/
public PlaceholderMap toMap( final Map identifiers ){
columns.setConverter( new PlaceholderListItemAdapter(){
@Override
public ConvertedPlaceholderListItem convert( int index, P dockable ){
final Integer id = identifiers.get( dockable.asDockable() );
if( id == null ) {
return null;
}
final ConvertedPlaceholderListItem item = new ConvertedPlaceholderListItem();
item.putInt( "index", index );
item.putInt( "id", id.intValue() );
fill( dockable.asDockable(), item );
return item;
}
} );
try {
return columns.toMap( new PlaceholderListItemAdapter, Column>(){
@Override
public ConvertedPlaceholderListItem convert( int index, Column dockable ){
final ConvertedPlaceholderListItem item = new ConvertedPlaceholderListItem();
item.putInt( "index", index );
item.setPlaceholderMap( dockable.getPlaceholders() );
return item;
}
} );
}
finally {
columns.setConverter( null );
}
}
public void fromMap( PlaceholderMap map, final Map identifiers, final PlaceholderToolbarGridConverter converter ){
clear();
columns.setConverter( new PlaceholderListItemAdapter(){
@Override
public P convert( ConvertedPlaceholderListItem item ){
final Integer id = item.getInt( "id" );
final D dockable = identifiers.get( id );
if( dockable == null ) {
return null;
}
return converter.convert( dockable, item );
}
} );
try {
columns.read( map, new PlaceholderListItemAdapter, GridPlaceholderList.Column>(){
@Override
public GridPlaceholderList.Column convert( ConvertedPlaceholderListItem item ){
final PlaceholderList list = createColumn();
final PlaceholderMap map = item.getPlaceholderMap();
if( map == null ) {
return null;
}
list.read( map, columns.getConverter() );
return columns.createColumn( list );
}
@Override
public void added( GridPlaceholderList.Column dockable ){
for( final P item : dockable.getList().dockables() ) {
converter.added( item );
}
};
} );
purge(true);
}
finally {
columns.setConverter( null );
onInserted();
}
}
/**
* Converts this grid into a {@link PlaceholderMap}, if possible any
* {@link Dockable} is converted into a placeholder.
*
* @return the converted map
*/
public PlaceholderMap toMap(){
columns.setConverter( new PlaceholderListItemAdapter(){
@Override
public ConvertedPlaceholderListItem convert( int index, P dockable ){
final ConvertedPlaceholderListItem item = new ConvertedPlaceholderListItem();
fill( dockable.asDockable(), item );
if( item.getPlaceholder() == null && item.getPlaceholderMap() == null ) {
return null;
}
return item;
}
} );
try {
return columns.toMap( new PlaceholderListItemAdapter, Column>(){
@Override
public ConvertedPlaceholderListItem convert( int index, Column dockable ){
final ConvertedPlaceholderListItem item = new ConvertedPlaceholderListItem();
item.putInt( "index", index );
item.setPlaceholderMap( dockable.getPlaceholders() );
return item;
}
} );
}
finally {
columns.setConverter( null );
}
}
/**
* Replaces the content of this grid by a map that was written earlier using
* {@link #toMap()} or {@link #toMap(Map)}.
*
* @param map
* the map to read, not null
*/
public void fromMap( PlaceholderMap map ){
clear();
columns.read( map, new PlaceholderListItemAdapter, Column>(){
@Override
public Column convert( ConvertedPlaceholderListItem item ){
final PlaceholderMap map = item.getPlaceholderMap();
if( map == null ) {
return null;
}
final PlaceholderList content = createColumn();
content.read( map, columns.getConverter() );
return columns.createColumn( content );
}
} );
purge(true);
}
}