/* * 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) 2010 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.layout; import java.util.ArrayList; import java.util.List; /** * This default implementation of a {@link LocationEstimationMap} works directly with the tree built * by {@link DockLayoutComposition}s. It is to be used as follows:<br> * <ul> * <li>Create an initial map with the root {@link DockLayoutComposition}.</li> * <li>Recursively traverse the map down using {@link #subMap(int)} until a leaf is reached.</li> * <li>Call the various <code>setLocation</code>-methods.</li> * <li>Use {@link #finish()} once all locations for a node is set.</li> * <li>Set the location of the next level... until there are no levels left.</li> * </ul> * @author Benjamin Sigg */ public class DefaultLocationEstimationMap implements LocationEstimationMap { /** the root */ private Node root; /** * Creates a new map * @param root the representation of this station */ public DefaultLocationEstimationMap( DockLayoutComposition root ){ this( new Node( root, false ) ); } private DefaultLocationEstimationMap( Node root ){ this.root = root; } /** * Prepares this map for estimating locations. Preparation of the map means that all current locations * are stored and can be used for comparison when calling {@link #finish()}. */ public void prepare(){ root.prepare(); } /** * Returns a newly created map that centers around the child with index <code>childIndex</code>. * @param childIndex the index of the child * @return the new map */ public DefaultLocationEstimationMap subMap( int childIndex ){ return new DefaultLocationEstimationMap( root.children.get( childIndex ) ); } /** * Gets the element which is represented by this map. * @return the element */ public DockLayoutComposition getRoot(){ return root.composition; } /** * Closes up this node. If the location of a leaf changed, its previous location is set as successor. * Otherwise if the location of a child changed, set the new location and use the old location as successor. * Otherwise invalidate the leaf as its location will never be completed. * @see #prepare() */ public void finish(){ /* * If location of leaf changed: set old location as successor * If location of parent changed: set current location as successor and replace * If no location changed: leafs gets invalidated */ for( Node child : root.children ){ DockableProperty newParentLocation = child.validLocation(); for( Node leaf : child.leafs ){ leaf.finish( newParentLocation ); } for( Node grandchild : child.children ){ grandchild.finish( newParentLocation ); } } } public int getChildCount(){ return root.children.size(); } public DockLayoutInfo getChild( int childIndex ){ if( childIndex < 0 || childIndex >= getChildCount() ){ return null; } return root.children.get( childIndex ).composition.getLayout(); } public int getSubChildCount( int childIndex ){ return root.children.get( childIndex ).leafs.size(); } public DockLayoutInfo getSubChild( int childIndex, int subChildIndex ){ return root.children.get( childIndex ).leafs.get( subChildIndex ).composition.getLayout(); } public void setLocation( int childIndex, DockableProperty location ){ if( location != null ){ getChild( childIndex ).setLocation( location ); } } public void setLocation( int childIndex, int subChildIndex, DockableProperty location ){ if( location != null ){ getSubChild( childIndex, subChildIndex ).setLocation( location ); } } /** * A wrapper around a {@link DockLayoutInfo} and its children and leafs. * @author Benjamin Sigg */ private static class Node{ /** the content of this leaf */ public final DockLayoutComposition composition; private DockableProperty oldLocation; private boolean valid = true; public final List<Node> children = new ArrayList<Node>(); public final List<Node> leafs; public Node( DockLayoutComposition composition, boolean collectLeafs ){ if( collectLeafs ){ leafs = new ArrayList<Node>(); } else{ leafs = null; } this.composition = composition; for( DockLayoutComposition child : composition.getChildren() ){ Node childNode = new Node( child, true ); children.add( childNode ); if( collectLeafs ){ if( childNode.isLeaf() ){ leafs.add( childNode ); } else{ leafs.addAll( childNode.leafs ); } } } } public boolean isLeaf(){ return children.isEmpty(); } public void prepare(){ if( valid ){ oldLocation = composition.getLayout().getLocation(); for( Node child : children ){ child.prepare(); } } } public void finish( DockableProperty newParentLocation ){ if( valid ){ DockableProperty newLeafLocation = validLocation(); if( newLeafLocation == null && newParentLocation != null ){ newLeafLocation = newParentLocation.copy(); } if( newLeafLocation != null ){ newLeafLocation.setSuccessor( oldLocation ); composition.getLayout().setLocation( newLeafLocation ); } else{ invalidate(); } } } public void invalidate(){ valid = false; } public DockableProperty validLocation(){ DockableProperty location = composition.getLayout().getLocation(); if( location != oldLocation ){ return location; } return null; } } }