All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bibliothek.gui.dock.station.split.Node Maven / Gradle / Ivy

The newest version!
/*
 * 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
 * [email protected]
 * CH - Switzerland
 */

package bibliothek.gui.dock.station.split;

import java.awt.Dimension;
import java.awt.Rectangle;
import java.util.Map;

import bibliothek.gui.Dockable;
import bibliothek.gui.dock.SplitDockStation;
import bibliothek.gui.dock.SplitDockStation.Orientation;
import bibliothek.gui.dock.layout.location.AsideRequest;
import bibliothek.gui.dock.station.split.SplitDockPathProperty.Location;
import bibliothek.util.Path;

/**
 * A Node represents an element in the tree of a {@link SplitDockStation}.
 * Every node has two children. The areas of the children are separated by
 * a "divider", whose position can be changed.
 * @author Benjamin Sigg
 */
public class Node extends VisibleSplitNode implements Divideable{
    /** The area of the left child is either at the left or at the top of the area of this node. */
    private SplitNode left;
    /** The area of the right child is either at the right or at the bottom of the area of this node. */
    private SplitNode right;
    /** The location of the divider. It's the fraction of the area of the node which is given to the left child. */
    private double divider = 0.5;
    /** The order in which the children are aligned. */
    private Orientation orientation = Orientation.VERTICAL;
    /** The area of the divider between the two children */
    private Rectangle dividerBounds = new Rectangle();
    
    /** whether this node is visible or not */
    private boolean visible;
    
    /** whether {@link #visible} has any meaning */
    private boolean visibleCached = false;
    
    /**
     * Constructs a new node.
     * @param access the access to the owner-station of this node.
     * @param left the left child
     * @param right the right child
     * @param orientation how the children are aligned
     */
    public Node( SplitDockAccess access, SplitNode left, SplitNode right, Orientation orientation ){
        this( access, left, right );
        this.orientation = orientation;
    }
    
    /**
     * Constructs a new node.
     * @param access the access to the owner-station of this node.
     * @param left the left child
     * @param right the right child
     * @param orientation how the children are aligned
     * @param id the unique id of this node, can be -1
     */
    public Node( SplitDockAccess access, SplitNode left, SplitNode right, Orientation orientation, long id ){
    	this( access, left, right, id );
    	this.orientation = orientation;
    }
    
    /**
     * Constructs a new node.
     * @param access the access to the owner-station of this node
     * @param left the left child
     * @param right the right child
     */
    public Node( SplitDockAccess access, SplitNode left, SplitNode right ){
        super( access, -1 );
        setRight( right );
        setLeft( left );
    }
    
    /**
     * Constructs a new node.
     * @param access the access to the owner-station of this node
     * @param left the left child
     * @param right the right child
     * @param id the unique id of this node
     */
    public Node( SplitDockAccess access, SplitNode left, SplitNode right, long id ){
        super( access, id );
        setRight( right );
        setLeft( left );
    }
    
    /**
     * Constructs a new node.
     * @param access the access to the owner-station of this node
     */
    public Node( SplitDockAccess access ){
        super( access, -1 );
    }
    
    /**
     * Constructs a new node.
     * @param access the access to the owner-station of this node
     * @param id the unique id of this node
     */
    public Node( SplitDockAccess access, long id ){
        super( access, id );
    }
    
    /**
     * Sets the left child of  this node. The area of this child
     * will be in the left or the upper half of the area of this node.
* Note that setting the child to null does not delete * the child from the system, only a call to {@link SplitNode#delete(boolean)} * does that. * @param left the left child or null */ public void setLeft( SplitNode left ){ if( this.left != null ) this.left.setParent( null ); this.left = left; clearVisibility(); if( left != null ){ left.setParent( this ); } treeChanged(); if( left != null ){ ensureIdUniqueAsync(); } getAccess().getOwner().revalidate(); getAccess().getOwner().repaint(); getAccess().repositioned( this ); } /** * Gets the left child of this node. * @return the child * @see #setLeft(SplitNode) */ public SplitNode getLeft() { return left; } /** * Sets the right child of this node. The area of this child * will be in the right or the bottom half of the area of this * node.
* Note that setting the child to null does not delete * the child from the system, only a call to {@link SplitNode#delete(boolean)} * does that. * @param right the right child */ public void setRight( SplitNode right ){ if( this.right != null ) this.right.setParent( null ); this.right = right; clearVisibility(); if( right != null ){ right.setParent( this ); } treeChanged(); if( right != null ){ ensureIdUniqueAsync(); } getAccess().getOwner().revalidate(); getAccess().getOwner().repaint(); getAccess().repositioned( this ); } /** * Gets the right child of this node. * @return the child * @see #setRight(SplitNode) */ public SplitNode getRight() { return right; } @Override public int getChildLocation( SplitNode child ) { if( left == child ) return 0; if( right == child ) return 1; return -1; } @Override public void setChild( SplitNode child, int location ) { if( location == 0 ) setLeft( child ); else if( location == 1 ) setRight( child ); else throw new IllegalArgumentException( "Location not valid " + location ); } @Override public int getMaxChildrenCount(){ return 2; } @Override public SplitNode getChild( int location ){ switch( location ){ case 0: return getLeft(); case 1: return getRight(); default: return null; } } public Orientation getOrientation() { return orientation; } /** * Changes the orientation of this node. * @param orientation the new orientation */ public void setOrientation( Orientation orientation ) { if( orientation == null ) throw new NullPointerException( "orientation must not be null" ); this.orientation = orientation; getAccess().getOwner().revalidate(); } @Override public Dimension getMinimumSize() { boolean leftVisible = left == null || left.isVisible(); boolean rightVisible = right == null || right.isVisible(); Dimension minLeft = leftVisible ? left.getMinimumSize() : null; Dimension minRight = rightVisible ? right.getMinimumSize() : null; return getSize( minLeft, minRight ); } @Override public Dimension getPreferredSize(){ boolean leftVisible = left == null || left.isVisible(); boolean rightVisible = right == null || right.isVisible(); Dimension minLeft = leftVisible ? left.getPreferredSize() : null; Dimension minRight = rightVisible ? right.getPreferredSize() : null; return getSize( minLeft, minRight ); } private Dimension getSize( Dimension left, Dimension right ){ if( left != null && right != null ){ int divider = getAccess().getOwner().getDividerSize(); if( orientation == Orientation.HORIZONTAL ){ return new Dimension( left.width + divider + right.width, Math.max( left.height, right.height )); } else{ return new Dimension( Math.max( left.width, right.width ), left.height + divider + right.height ); } } else if( left != null ){ return left; } else if( right != null ){ return right; } else{ return new Dimension(); } } public void setDivider( double divider ){ if( this.divider != divider ){ this.divider = divider; getAccess().getOwner().revalidate(); getAccess().getOwner().repaint(); getAccess().repositioned( this ); } } public double getDivider() { return divider; } public double getActualDivider(){ return validateDivider( getDivider() ); } public double validateDivider( double divider ){ return getStation().getCurrentSplitLayoutManager().validateDivider( getStation(), divider, this ); } @Override public boolean isVisible(){ if( !visibleCached ){ visible = left == null || right == null || (left.isVisible() || right.isVisible()); visibleCached = true; } return visible; } private void clearVisibility(){ visibleCached = false; SplitNode parent = getParent(); if( parent instanceof Node ){ ((Node)parent).clearVisibility(); } } @Override public SplitNode getVisible(){ if( left != null && right != null ){ boolean leftVisible = left.isVisible(); boolean rightVisible = right.isVisible(); if( leftVisible && rightVisible ){ return this; } if( leftVisible ){ return left; } if( rightVisible ){ return right; } } return null; } @Override public boolean isOfUse(){ return true; } @Override public void updateBounds( double x, double y, double width, double height, double factorW, double factorH, boolean components ) { super.updateBounds( x, y, width, height, factorW, factorH, components ); boolean leftVisible = left == null || left.isVisible(); boolean rightVisible = right == null || right.isVisible(); if( leftVisible && !rightVisible ){ left.updateBounds( x, y, width, height, factorW, factorH, components ); } else if( !leftVisible && rightVisible ){ right.updateBounds( x, y, width, height, factorW, factorH, components ); } else if( leftVisible && rightVisible ){ double divider = getActualDivider(); int dividerSize = getAccess().getOwner().getDividerSize(); if( orientation == Orientation.HORIZONTAL ){ // Components are left and right double dividerWidth = factorW > 0 ? Math.max( 0, dividerSize / factorW) : 0.0; double dividerLocation = width * divider; if( left != null ) left.updateBounds( x, y, dividerLocation - dividerWidth/2, height, factorW, factorH, components ); if( right != null ) right.updateBounds( x + dividerLocation + dividerWidth/2, y, width - dividerLocation - dividerWidth/2, height, factorW, factorH, components ); } else{ double dividerHeight = factorH > 0 ? Math.max( 0, dividerSize / factorH ) : 0.0; double dividerLocation = height * divider; if( left != null) left.updateBounds( x, y, width, dividerLocation - dividerHeight / 2, factorW, factorH, components ); if( right != null) right.updateBounds( x, y + dividerLocation + dividerHeight / 2, width, height - dividerLocation - dividerHeight/2, factorW, factorH, components ); } } } @Override public void setBounds( double x, double y, double width, double height, double factorW, double factorH, boolean updateComponentBounds ){ super.setBounds( x, y, width, height, factorW, factorH, updateComponentBounds ); boolean leftVisible = left == null || left.isVisible(); boolean rightVisible = right == null || right.isVisible(); if( leftVisible && rightVisible ){ double divider = getActualDivider(); int dividerSize = getAccess().getOwner().getDividerSize(); if( orientation == Orientation.HORIZONTAL ){ // Components are left and right double dividerWidth = factorW > 0 ? Math.max( 0, dividerSize / factorW) : 0.0; double dividerLocation = width * divider; dividerBounds.setBounds( (int)(( x+dividerLocation-dividerWidth/2 )*factorW ), (int)( y*factorH ), dividerSize, (int)( height*factorH + 0.5 )); } else{ double dividerHeight = factorH > 0 ? Math.max( 0, dividerSize / factorH ) : 0.0; double dividerLocation = height * divider; dividerBounds.setBounds( (int)(x*factorW), (int)((y+dividerLocation-dividerHeight/2)*factorH ), (int)(width*factorW + 0.5), dividerSize ); } } } public Rectangle getDividerBounds( double divider, Rectangle bounds ){ if( bounds == null ) bounds = new Rectangle(); Root root = getRoot(); double factorW = root.getWidthFactor(); double factorH = root.getHeightFactor(); int dividerSize = getAccess().getOwner().getDividerSize(); if( orientation == Orientation.HORIZONTAL ){ // Components are left and right double dividerWidth = dividerSize / factorW; double dividerLocation = width * divider; bounds.setBounds( (int)(( x+dividerLocation-dividerWidth/2 )*factorW + 0.5 ), (int)( y*factorH + 0.5 ), dividerSize, (int)( height*factorH + 0.5 )); } else{ double dividerHeight = dividerSize / factorH; double dividerLocation = height * divider; bounds.setBounds( (int)(x*factorW + 0.5), (int)((y+dividerLocation-dividerHeight/2)*factorH + 0.5), (int)(width*factorW + 0.5), dividerSize ); } return bounds; } public double getDividerAt( int x, int y ){ Root root = getRoot(); if( orientation == Orientation.HORIZONTAL ){ double mx = x / root.getWidthFactor(); return (mx - this.x) / width; } else{ double my = y / root.getHeightFactor(); return (my - this.y) / height; } } @Override public PutInfo getPut( int x, int y, double factorW, double factorH, Dockable drop ){ boolean leftVisible = left == null || left.isVisible(); boolean rightVisible = right == null || right.isVisible(); if( leftVisible && rightVisible ){ if( orientation == Orientation.HORIZONTAL ){ if( x < (this.x + divider*width)*factorW ){ // left return left == null ? null : left.getPut( x, y, factorW, factorH, drop ); } else{ // right return right == null ? null : right.getPut( x, y, factorW, factorH, drop ); } } else{ if( y < (this.y + divider*height)*factorH ){ // top return left == null ? null : left.getPut( x, y, factorW, factorH, drop ); } else{ // bottom return right == null ? null : right.getPut( x, y, factorW, factorH, drop ); } } } else if( leftVisible ){ return left.getPut( x, y, factorW, factorH, drop ); } else if( rightVisible ){ return right.getPut( x, y, factorW, factorH, drop ); } else{ return null; } } @Override public boolean isInOverrideZone( int x, int y, double factorW, double factorH ) { boolean leftVisible = left == null || left.isVisible(); boolean rightVisible = right == null || right.isVisible(); if( leftVisible && rightVisible ){ if( orientation == Orientation.HORIZONTAL ){ if( x < (this.x + divider*width)*factorW ){ // left return left.isInOverrideZone( x, y, factorW, factorH ); } else{ // right return right.isInOverrideZone( x, y, factorW, factorH ); } } else{ if( y < (this.y + divider*height)*factorH ){ // top return left.isInOverrideZone( x, y, factorW, factorH ); } else{ // bottom return right.isInOverrideZone( x, y, factorW, factorH ); } } } else if( leftVisible ){ return left.isInOverrideZone( x, y, factorW, factorH ); } else if( rightVisible ){ return right.isInOverrideZone( x, y, factorW, factorH ); } else{ return false; } } @Override public void evolve( SplitDockTree.Key key, boolean checkValidity, Map linksToSet ){ SplitDockTree tree = key.getTree(); setPlaceholders( tree.getPlaceholders( key ) ); setPlaceholderMap( tree.getPlaceholderMap( key ) ); if( tree.isHorizontal( key )){ orientation = SplitDockStation.Orientation.HORIZONTAL; setLeft( create( tree.getLeft( key ), checkValidity, linksToSet )); setRight( create( tree.getRight( key ), checkValidity, linksToSet )); setDivider( tree.getDivider( key )); } else{ orientation = SplitDockStation.Orientation.VERTICAL; setLeft( create( tree.getTop( key ), checkValidity, linksToSet )); setRight( create( tree.getBottom( key ), checkValidity, linksToSet )); setDivider( tree.getDivider( key )); } } @Override public boolean insert( SplitDockPlaceholderProperty property, Dockable dockable ){ Path placeholder = property.getPlaceholder(); if( hasPlaceholder( placeholder )){ // that is ... unexpected, must have been a client. Split this node and assign // all remaining placeholders to a new leaf. Leaf leaf = create( dockable, -1 ); if( leaf == null ){ return false; } getAccess().getPlaceholderSet().set( null, placeholder, this ); leaf.setPlaceholders( getPlaceholders() ); Node node = createNode( -1 ); node.setDivider( 0.5 ); if( width > height ){ node.setOrientation( Orientation.HORIZONTAL ); } else{ node.setOrientation( Orientation.VERTICAL ); } SplitNode parent = getParent(); int location = parent.getChildLocation( this ); node.setLeft( leaf ); node.setRight( this ); parent.setChild( node, location ); leaf.setDockable( dockable, null ); return true; } if( left != null && left.insert( property, dockable )){ return true; } if( right != null && right.insert( property, dockable )){ return true; } return false; } @Override public boolean aside( AsideRequest request ){ boolean leftVisible = left == null || left.isVisible(); boolean rightVisible = right == null || right.isVisible(); if( leftVisible ){ return left.aside( request ); } else if( rightVisible ){ return right.aside( request ); } else{ return false; } } @Override public boolean aside( SplitDockPathProperty property, int depth, AsideRequest request ){ boolean leftVisible = left == null || left.isVisible(); boolean rightVisible = right == null || right.isVisible(); if( leftVisible && rightVisible ){ if( depth >= property.size() ){ return false; } else{ SplitDockPathProperty.Node node = property.getNode( depth ); if( needToExpand( node )){ if( request.getPlaceholder() != null ){ long placeholderId = getLeafId( property ); long splitId = getSplitId( property, depth ); Placeholder placeholder = createPlaceholder( splitId ); split( property, depth, placeholder, placeholderId ); placeholder.addPlaceholder( request.getPlaceholder() ); } return true; } else{ if( isLeftOrTop( node.getLocation() )){ return left.aside( property, depth+1, request ); } else{ return right.aside( property, depth+1, request ); } } } } else if( leftVisible ){ return left.aside( property, depth, request ); } else if( rightVisible ){ return right.aside( property, depth, request ); } else{ return false; } } @Override public boolean insert( SplitDockPathProperty property, int depth, Dockable dockable ) { boolean leftVisible = left == null || left.isVisible(); boolean rightVisible = right == null || right.isVisible(); if( leftVisible && rightVisible ){ if( depth >= property.size() ){ // there is no description where to put the element // try using the theoretical boundaries of the element return getAccess().drop( dockable, property.toLocation( this ), this ); } else{ SplitDockPathProperty.Node node = property.getNode( depth ); if( needToExpand( node ) ){ // split up this node long leafId = getLeafId( property ); long splitId = getSplitId( property, depth ); Leaf leaf = create( dockable, leafId ); if( leaf == null ) return false; split( property, depth, leaf, splitId ); leaf.setDockable( dockable, null ); return true; } else{ // forward the call to a child if( isLeftOrTop( node.getLocation() ) ){ return left.insert( property, depth+1, dockable ); } else{ return right.insert( property, depth+1, dockable ); } } } } else if( leftVisible ){ return left.insert( property, depth, dockable ); } else if( rightVisible ){ return right.insert( property, depth, dockable ); } else{ return false; } } private long getLeafId( SplitDockPathProperty property ){ long leafId = property.getLeafId(); SplitDockPathProperty.Node lastNode = null; if( leafId == -1 ){ lastNode = property.getLastNode(); if( lastNode != null ){ leafId = lastNode.getId(); } } return leafId; } private long getSplitId( SplitDockPathProperty property, int depth ){ SplitDockPathProperty.Node node = property.getNode( depth ); SplitDockPathProperty.Node lastNode = null; if( property.getLeafId() == -1 ){ lastNode = property.getLastNode(); } if( lastNode != node ){ if( depth > 0 ){ return property.getNode( depth-1 ).getId(); } else{ return node.getId(); } } return -1; } private boolean isLeftOrTop( Location location ){ return location == Location.LEFT || location == Location.TOP; } private boolean needToExpand( SplitDockPathProperty.Node node ){ boolean expand = // if this node is horizontal, but the path is vertical ( orientation == SplitDockStation.Orientation.HORIZONTAL && (node.getLocation() == SplitDockPathProperty.Location.TOP || node.getLocation() == SplitDockPathProperty.Location.BOTTOM )) || // ... or if this node is vertical, but the path is horizontal ( orientation == SplitDockStation.Orientation.VERTICAL && (node.getLocation() == SplitDockPathProperty.Location.LEFT || node.getLocation() == SplitDockPathProperty.Location.RIGHT )); if( node.getId() != -1 && node.getId() != getId() ){ expand = true; } return expand; } @Override public N submit( SplitTreeFactory factory ) { if( orientation == SplitDockStation.Orientation.HORIZONTAL ) return factory.horizontal( left.submit( factory ), right.submit( factory ), divider, getId(), getPlaceholders(), getPlaceholderMap(), isVisible() ); else return factory.vertical( left.submit( factory ), right.submit( factory ), divider, getId(), getPlaceholders(), getPlaceholderMap(), isVisible() ); } @Override public Leaf getLeaf( Dockable dockable ) { if( left != null && left.isVisible() ){ Leaf leaf = left.getLeaf( dockable ); if( leaf != null ) return leaf; } if( right != null && right.isVisible() ) return right.getLeaf( dockable ); else return null; } @Override public Node getDividerNode( int x, int y ){ boolean leftVisible = left == null || left.isVisible(); boolean rightVisible = right == null || right.isVisible(); if( leftVisible && rightVisible && dividerBounds.contains( x, y )) return this; if( left != null && leftVisible ){ Node node = left.getDividerNode( x, y ); if( node != null ) return node; } if( right != null && rightVisible ){ return right.getDividerNode( x, y ); } else{ return null; } } @Override public void visit( SplitNodeVisitor visitor ) { visitor.handleNode( this ); if( left != null ){ left.visit( visitor ); } if( right != null ){ right.visit( visitor ); } } @Override public void toString( int tabs, StringBuilder out ) { out.append( "Node[ "); out.append( orientation ); out.append( " , id=" ); out.append( getId() ); out.append( ", placeholders={" ); Path[] placeholders = getPlaceholders(); if( placeholders != null ){ for( int i = 0; i < placeholders.length; i++ ){ if( i > 0 ){ out.append( ", " ); } out.append( placeholders[i] ); } } out.append( "}]" ); out.append( '\n' ); for( int i = 0; i < tabs+1; i++ ) out.append( '\t' ); if( left == null ) out.append( "" ); else left.toString( tabs+1, out ); out.append( '\n' ); for( int i = 0; i < tabs+1; i++ ) out.append( '\t' ); if( right == null ) out.append( "" ); else right.toString( tabs+1, out ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy