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

bibliothek.gui.dock.station.split.Leaf 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) 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.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.util.Map;

import javax.swing.JComponent;

import bibliothek.gui.DockController;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.DockHierarchyLock;
import bibliothek.gui.dock.SplitDockStation;
import bibliothek.gui.dock.accept.DockAcceptance;
import bibliothek.gui.dock.layout.DockableProperty;
import bibliothek.gui.dock.layout.location.AsideAnswer;
import bibliothek.gui.dock.layout.location.AsideRequest;
import bibliothek.gui.dock.station.DockableDisplayer;
import bibliothek.gui.dock.station.StationChildHandle;
import bibliothek.gui.dock.station.span.Span;
import bibliothek.gui.dock.station.split.PutInfo.Put;
import bibliothek.gui.dock.station.support.PlaceholderMap;
import bibliothek.gui.dock.station.support.PlaceholderStrategy;
import bibliothek.gui.dock.title.DockTitle;
import bibliothek.util.Path;

/**
 * Represents a leaf in the tree that is the structure of a {@link SplitDockStation}.
 * A leaf also represents a single {@link Dockable} which is shown
 * on the owner-station.
 * @author Benjamin Sigg
 */
public class Leaf extends SpanSplitNode{
	/** Information about the element that is shown by this leaf */
    private StationChildHandle handle;
    
    /**
     * Creates a new leaf.
     * @param access the access to the private functions of the owning {@link SplitDockStation}
     */
    public Leaf( SplitDockAccess access ){
        this( access, -1 );
    }
    
    /**
     * Creates a new leaf.
     * @param access the access to the private functions of the owning {@link SplitDockStation}
     * @param id the unique id of this leaf
     */
    public Leaf( SplitDockAccess access, long id ){
        super( access, id );
    }
    
    /**
     * Sets the element of this leaf.
     * @param handle the element
     */
    public void setHandle( StationChildHandle handle ){
		this.handle = handle;
	}
    
    @Override
    public Dimension getMinimumSize() {
    	SplitDockStation station = getStation();
    	DockableDisplayer displayer = null;
    	if( handle != null ){
    		displayer = handle.getDisplayer();
    	}
    	
    	if( displayer == null ){
	    	Dimension minimum;
	    	if( station == null ){
	    		minimum = new Dimension( 0, 0 );
	    	}
	    	else{
	    		minimum = station.getMinimumLeafSize();
	    	}
    		return minimum;
    	}
    	
    	return displayer.getComponent().getMinimumSize();
    }
    
    @Override
    public Dimension getPreferredSize(){
    	if( handle == null )
    		return new Dimension( 0, 0 );
    	
    	DockableDisplayer displayer = handle.getDisplayer();
    	if( displayer == null )
    		return new Dimension( 0, 0 );
    	return displayer.getComponent().getPreferredSize();
    }
    
    @Override
    public int getChildLocation( SplitNode child ) {
        return -1;
    }
    
    @Override
    public void setChild( SplitNode child, int location ) {
        throw new IllegalStateException( "can't add children to a leaf" );
    }
    
    @Override
    public int getMaxChildrenCount(){
    	return 0;
    }
    
    @Override
    public SplitNode getChild( int location ){
    	return null;
    }
    
    /**
     * Sets the element of this leaf. This method ensures that dockable
     * is registered in the {@link DockStation}
     * @param dockable the new element or null to remove the
     * old {@link Dockable}
     * @param token if null, then a token will be acquired by this method
     * and this method will fire events, otherwise this methods is executed silently. Clients should
     * use null.
     */
    public void setDockable( Dockable dockable, DockHierarchyLock.Token token ){
    	setDockable( dockable, token, true, false );
    }
    
    /**
     * Sets the element of this leaf. This method ensures that dockable
     * is registered in the {@link DockStation}
     * @param dockable the new element or null to remove the
     * old {@link Dockable}
     * @param token if null, then a token will be acquired by this method
     * and this method will fire events, otherwise this methods is executed silently
     * @param updatePlaceholders if true, the placeholder list of this leaf is
     * automatically updated
     * @param storePlaceholderMap if true, the current {@link PlaceholderMap} is
     * replaced by the map provided by the current {@link Dockable} which is a {@link DockStation}
     */
    public void setDockable( Dockable dockable, DockHierarchyLock.Token token, boolean updatePlaceholders, boolean storePlaceholderMap ){
    	if( handle != null ){
    		if( updatePlaceholders ){
    			getAccess().getPlaceholderSet().set( this, handle.getDockable() );
    		}
    		
    		if( storePlaceholderMap ){
    			DockStation station = handle.getDockable().asDockStation();
    			if( station == null ){
    				throw new IllegalStateException( "no station as child but storePlaceholderMap is set" );
    			}
    			setPlaceholderMap( station.getPlaceholders() );
    		}
    		
    		getAccess().removeHandle( handle, token );
    		handle = null;
    	}
    	
        if( dockable != null ){
        	handle = getAccess().newHandle( dockable );
            
        	if( updatePlaceholders ){
        		getAccess().getPlaceholderSet().set( this, dockable );
        	}
        	
        	getAccess().addHandle( handle, token );
        }
        
        treeChanged();
    }
    
    /**
     * Gets the {@link Dockable} which is shown on the displayer
     * of this leaf.
     * @return the Dockable
     */
    public Dockable getDockable() {
        return handle == null ? null : handle.getDockable();
    }
    
    
    /**
     * Gets the displayer of this leaf.
     * @return the displayer
     */
    public DockableDisplayer getDisplayer(){
        return handle == null ? null : handle.getDisplayer();
    }
    
    /**
     * Gets the handle which is responsible for the current {@link Dockable}.
     * @return the handle, might be null
     */
    public StationChildHandle getDockableHandle(){
		return handle;
	}
    
    @Override
    public boolean isVisible(){
	    return true;
    }
    
    @Override
    public SplitNode getVisible(){
	    return this;
    }
    
    @Override
    public boolean isOfUse(){
    	if( !getAccess().isTreeAutoCleanupEnabled() ){
    		return true;
    	}
    	return handle != null || hasPlaceholders();
    }
    
    /**
     * Disconnects this leaf from its {@link Dockable}. This leaf either deletes
     * itself or replaces itself with a {@link Placeholder}.
     * @param keepCurrent if true, the placeholder of the current
     * {@link Dockable} is added to the set of the placeholders, otherwise the placeholder
     * is removed. 
     */
    public void placehold( boolean keepCurrent ){
    	Dockable dockable = getDockable();

    	if( dockable != null ){
    		SplitDockAccess access = getAccess();
        	PlaceholderStrategy strategy = access.getOwner().getPlaceholderStrategy();
        	if( strategy != null ){
        		updatePlaceholders( dockable, keepCurrent, strategy );
        	}
	    	
	    	DockStation station = dockable.asDockStation();
	    	if( station != null && keepCurrent ){
	    		setPlaceholderMap( station.getPlaceholders() );
	    	}
    	}
    	if( hasPlaceholders() ){
    		Placeholder placeholder = createPlaceholder( getId() );
    		placeholder.setPlaceholders( getPlaceholders() );
    		movePlaceholderMap( placeholder );
    		replace( placeholder );
    	}
    	else{
    		delete( true );
    	}
    }
    
    private void updatePlaceholders( Dockable dockable, boolean keep, PlaceholderStrategy strategy ){
    	Path placeholder = strategy.getPlaceholderFor( dockable );
    	if( placeholder != null ){
    		if( !keep ){
    			getAccess().getPlaceholderSet().set( null, placeholder );
    		}
    		else {
    			getAccess().getPlaceholderSet().set( this, placeholder );
    		}
    	}
    	
    	DockStation station = dockable.asDockStation();
    	if( station != null ){
    		for( int i = 0, n = station.getDockableCount(); ibounds. Depending on the position of the title bounds
     * is moved, shrunk only horizontally or vertically.
     * @param bounds some boundaries, usually describing the boundaries of this {@link Leaf} but
     * any {@link Rectangle} can be modified by this method
     * @return a copy of bounds where the size of the title (if there is any) has been removed
     */
    protected Rectangle removeTitle( Rectangle bounds ){
    	DockableDisplayer displayer = getDisplayer();
    	bounds = new Rectangle( bounds );
    	
        if( displayer.getTitle() != null ){
            if( displayer.getTitleLocation() == DockableDisplayer.Location.TOP ){
                int height = displayer.getTitle().getComponent().getHeight();
                bounds.y += height;
                bounds.height -= height;
            }
            else if( displayer.getTitleLocation() == DockableDisplayer.Location.BOTTOM ){
                int height = displayer.getTitle().getComponent().getHeight();
                bounds.height -= height;
            }
            else if( displayer.getTitleLocation() == DockableDisplayer.Location.LEFT ){
                int width = displayer.getTitle().getComponent().getWidth();
                bounds.x += width;
                bounds.width -= width;
            }
            else if( displayer.getTitleLocation() == DockableDisplayer.Location.RIGHT ){
                int width = displayer.getTitle().getComponent().getWidth();
                bounds.width -= width;                
            }
        }
        
        return bounds;
    }

    /**
     * Tells whether the position of the mouse x/y would result in a drag and drop operation where
     * {@link Put#CENTER} is appropriate.
     * @param bounds the boundaries of the {@link Dockable}, this {@link Leaf} or any other representation of
     * the {@link Dockable}
     * @param x the x-coordinate of the mouse
     * @param y the y-coordinate of the mouse
     * @return whether the location of the mouse would allow a combination of the {@link Dockable}s
     */
    protected boolean isCenterPut( Rectangle bounds, int x, int y ){
        float sideSnapSize = getAccess().getOwner().getSideSnapSize();
        
        return x > bounds.x + sideSnapSize*bounds.width && 
            x < bounds.x + bounds.width - sideSnapSize*bounds.width &&
            y > bounds.y + sideSnapSize*bounds.height &&
            y < bounds.y + bounds.height - sideSnapSize*bounds.height;
    }
    
    /**
     * Tells whether the position of the mouse x/y would result in a drag and drop operation where
     * {@link Put#TITLE} is appropriate.
     * @param bounds the boundaries of the {@link Dockable}, this {@link Leaf} or any other representation of
     * the {@link Dockable}
     * @param x the x-coordinate of the mouse
     * @param y the y-coordinate of the mouse
     * @return whether the location of the mouse would allow a combination of the {@link Dockable}s
     */
    protected boolean isTitlePut( Rectangle bounds, int x, int y ){
    	DockableDisplayer displayer = getDisplayer();
    	
        if( displayer.getTitle() != null ){
        	if( displayer.getTitleLocation() == DockableDisplayer.Location.TOP ){
                return y <= bounds.y;
            }
            else if( displayer.getTitleLocation() == DockableDisplayer.Location.BOTTOM ){
                return y >= bounds.y+bounds.height;
            }
            else if( displayer.getTitleLocation() == DockableDisplayer.Location.LEFT ){
                return x <= bounds.x;
            }
            else if( displayer.getTitleLocation() == DockableDisplayer.Location.RIGHT ){
                return x >= bounds.x + bounds.width;
            }
        }
        
        return false;
    }
    
    /**
     * Assuming the mouse at x/y is within bounds, this method calculates which one
     * of the non-combining {@link Put}s describe the situation best. The method creates and validates a new
     * {@link PutInfo}.
     * @param bounds the boundaries of the {@link Dockable}, the {@link Leaf} or any other representation of the
     * {@link Dockable}.
     * @param x the x-coordinate of the mouse
     * @param y the y-coordinate of the mouse
     * @param drop the item that is about to be dropped
     * @param centered whether the mouse position alone would usually require one of the combining {@link Put}s
     * @return the new drag and drop operation, or null if the suggested operation is not valid
     */
    protected PutInfo createSidePut( Rectangle bounds, int x, int y, Dockable drop, boolean centered ){
        if( above( bounds.x, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height, x, y )){
            if( above( bounds.x, bounds.y + bounds.height, bounds.x + bounds.width, bounds.y, x, y ))
                return getAccess().validatePutInfo( new PutInfo( this, PutInfo.Put.TOP, drop, centered ));
            else
                return getAccess().validatePutInfo(  new PutInfo( this, PutInfo.Put.RIGHT, drop, centered ));
        }
        else{
            if( above( bounds.x, bounds.y + bounds.height, bounds.x + bounds.width, bounds.y, x, y ))
                return getAccess().validatePutInfo(  new PutInfo( this, PutInfo.Put.LEFT, drop, centered ));
            else
                return getAccess().validatePutInfo(  new PutInfo( this, PutInfo.Put.BOTTOM, drop, centered ));
        }
    }
    
    @Override
    public boolean isInOverrideZone( int x, int y, double factorW, double factorH ){
        float sideSnapSize = getAccess().getOwner().getSideSnapSize();
        Rectangle bounds = getBounds();
        
        if( x > bounds.x + sideSnapSize*bounds.width && 
            x < bounds.x + bounds.width - sideSnapSize*bounds.width &&
            y > bounds.y + sideSnapSize*bounds.height &&
            y < bounds.y + bounds.height - sideSnapSize*bounds.height ){
            
            return false;
        }
        
        return true;
    }
    
    @Override
    public void evolve( SplitDockTree.Key key, boolean checkValidity, Map linksToSet ){
    	setPlaceholders( key.getTree().getPlaceholders( key ) );
    	setPlaceholderMap( key.getTree().getPlaceholderMap( key ) );
    }
    
    @Override
    public boolean insert( SplitDockPlaceholderProperty property, Dockable dockable ){
    	Path placeholder = property.getPlaceholder();
    	if( hasPlaceholder( placeholder )){
            // try to melt with child
            DockStation station = getDockable().asDockStation();
            DockableProperty stationLocation = property.getSuccessor();
            if( station != null && stationLocation != null ){
                if( dockable.accept( station ) && station.accept( dockable )){
                    DockController controller = getAccess().getOwner().getController();
                    DockAcceptance acceptance = controller == null ? null : controller.getAcceptance();
                    if( acceptance == null || acceptance.accept( station, dockable )){
                        boolean done = station.drop( dockable, stationLocation );
                        if( done ){
                        	getAccess().getPlaceholderSet().set( null, placeholder, this );
                            return true;
                        }
                    }
                }
            }
            
            // try using the theoretical boundaries of the element
            SplitDockProperty selfLocation = new SplitDockProperty( getX(), getY(), getWidth(), getHeight() );
            selfLocation.setSuccessor( property.getSuccessor() );
            boolean done = getAccess().drop( dockable, selfLocation, this );
            if( done ){
            	removePlaceholder( placeholder );
            }
            return done;
    	}
    	return false;
    }
    
    @Override
    public boolean aside( AsideRequest request ){
    	if( request.getPlaceholder() != null ){
    		addPlaceholder( request.getPlaceholder() );
	    	DockStation station = getDockable().asDockStation();
	    	if( station == null ){
	    		AsideAnswer answer = request.forward( getStation().getCombiner(), getPlaceholderMap() );
	    		if( answer.isCanceled() ){
	    			return false;
	    		}
	    		setPlaceholderMap( answer.getLayout() );
	    	}
	    	else{
	    		AsideAnswer answer = request.forward( station );
	    		if( answer.isCanceled() ){
	    			return false;
	    		}
	    	}
    	}
    	return true;
    }
    
    @Override
    public boolean aside( SplitDockPathProperty property, int index, AsideRequest request ){
    	if( request.getPlaceholder() != null ){
    		if( index < property.size() ){
    			DockStation station = getDockable().asDockStation();
	    		if( station == null ){
		    		Placeholder placeholder = createPlaceholder( property.getLeafId() );
		    		split( property, index, placeholder );
		    		return placeholder.aside( request );
	    		}
	    	}
	    	else{
	    		return aside( request );
	    	}
    	}
    	return true;
    }
    
    @Override
    public boolean insert( SplitDockPathProperty property, int depth, Dockable dockable ) {
        if( depth < property.size() ){
            // split up the leaf
        	Leaf leaf = create( dockable, property.getLeafId() );
        	if( leaf == null ){
        		return false;
        	}
        	split( property, depth, leaf );
        	leaf.setDockable( dockable, null );
        	return true;
        }
        else{
            // try to melt with child
            DockStation station = getDockable().asDockStation();
            DockableProperty stationLocation = property.getSuccessor();
            if( station != null && stationLocation != null ){
                if( dockable.accept( station ) && station.accept( dockable )){
                    DockController controller = getAccess().getOwner().getController();
                    DockAcceptance acceptance = controller == null ? null : controller.getAcceptance();
                    if( acceptance == null || acceptance.accept( station, dockable )){
                        boolean done = station.drop( dockable, stationLocation );
                        if( done )
                            return true;
                    }
                }
            }
            
            // try using the theoretical boundaries of the element
            return getAccess().drop( dockable, property.toLocation( this ), this );
        }
    }
    
    @Override
    public  N submit( SplitTreeFactory factory ){
    	PlaceholderMap map = getPlaceholderMap();
    	if( map == null ){
    		Dockable dockable = getDockable();
    		if( dockable != null ){
    			DockStation station = dockable.asDockStation();
    			if( station != null ){
    				map = station.getPlaceholders();
    			}
    		}
    	}
    	
        return factory.leaf( getDockable(), getId(), getPlaceholders(), map );
    }
        
    @Override
    public Leaf getLeaf( Dockable dockable ) {
    	Dockable mine = getDockable();
    	
    	if( mine != null && dockable == getDockable() ){
    		return this;
    	}
    	else{
    		return null;
    	}
    }
    
    @Override
    public Node getDividerNode( int x, int y ){
        return null;
    }
    
    @Override
    public void visit( SplitNodeVisitor visitor ) {
        visitor.handleLeaf( this );
    }
    
    @Override
    public void toString( int tabs, StringBuilder out ) {
    	Dockable dockable = getDockable();
        out.append( "Leaf[ " );
        if( dockable != null ){
            out.append( dockable.getTitleText() );
            out.append( ", " );
        }
        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].toString() );
        	}
        }
        out.append( "}, " );
        out.append( "id=" );
        out.append( getId() );
        out.append( ", bounds=" );
        out.append( getBounds() );
        out.append( " ]" );
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy