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

bibliothek.gui.dock.common.mode.station.CSplitDockStationHandle Maven / Gradle / Ivy

Go to download

DockingFrames is an open source Java Swing docking framework, licenced under LGPL 2.1. This is the same distribution as the original distribution (http://www.docking-frames.org/), only reinstalled in maven

There is a newer version: 1.1.2p20b.fix-1
Show 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) 2009 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.common.mode.station;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import bibliothek.gui.DockController;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.SplitDockStation;
import bibliothek.gui.dock.common.CLocation;
import bibliothek.gui.dock.common.CStation;
import bibliothek.gui.dock.common.group.CGroupMovement;
import bibliothek.gui.dock.common.intern.CommonDockable;
import bibliothek.gui.dock.common.intern.station.CSplitDockStation;
import bibliothek.gui.dock.common.location.CMaximizedLocation;
import bibliothek.gui.dock.common.mode.CLocationMode;
import bibliothek.gui.dock.common.mode.CLocationModeManager;
import bibliothek.gui.dock.common.mode.CMaximizedModeArea;
import bibliothek.gui.dock.common.mode.CNormalModeArea;
import bibliothek.gui.dock.common.mode.ExtendedMode;
import bibliothek.gui.dock.common.util.CDockUtilities;
import bibliothek.gui.dock.control.relocator.DockRelocatorEvent;
import bibliothek.gui.dock.control.relocator.VetoableDockRelocatorAdapter;
import bibliothek.gui.dock.control.relocator.VetoableDockRelocatorListener;
import bibliothek.gui.dock.event.SplitDockListener;
import bibliothek.gui.dock.facile.mode.Location;
import bibliothek.gui.dock.facile.mode.LocationMode;
import bibliothek.gui.dock.facile.mode.LocationModeEvent;
import bibliothek.gui.dock.facile.mode.MaximizedMode;
import bibliothek.gui.dock.facile.mode.MaximizedModeArea;
import bibliothek.gui.dock.facile.mode.ModeArea;
import bibliothek.gui.dock.facile.mode.ModeAreaListener;
import bibliothek.gui.dock.facile.mode.NormalModeArea;
import bibliothek.gui.dock.layout.DockableProperty;
import bibliothek.gui.dock.station.split.DockableSplitDockTree;
import bibliothek.gui.dock.station.split.SplitDockFullScreenProperty;
import bibliothek.gui.dock.support.mode.AffectedSet;
import bibliothek.gui.dock.support.mode.AffectingRunnable;
import bibliothek.gui.dock.util.DockUtilities;
import bibliothek.util.Path;

/**
 * Combination of {@link CMaximizedModeArea}, {@link CNormalModeArea} and a
 * {@link SplitDockStation}.
 * @author Benjamin Sigg
 */
public class CSplitDockStationHandle{
	/** the station which is handled by this handle */
	private CStation station;
	
	/** normal-mode */
	private Normal normal = new Normal();
	/** maximized-mode */
	private Maximal maximal = new Maximal();
	
	/** the mode which is accessing this handler */
	private LocationMode normalMode;
	
	/** the mode which is accessing this handler */
	private MaximizedMode maximizedMode;
	
	/** the manager in whose realm this handle is used */
	private CLocationModeManager manager;
	
	/** the listeners added to this {@link ModeArea} */
	private List listeners = new ArrayList();
	
	private SplitDockListener fullScreenListener = new SplitDockListener() {
		public void fullScreenDockableChanged( SplitDockStation station, Dockable oldFullScreen, Dockable newFullScreen ){
			Set affected = new HashSet();
			if( oldFullScreen != null )
				affected.add( oldFullScreen );
			if( newFullScreen != null )
				affected.add( newFullScreen );
			
			ModeAreaListenerWrapper[] array = listeners.toArray( new ModeAreaListenerWrapper[ listeners.size() ] );
			for( ModeAreaListenerWrapper listener : array ){
				listener.fire( affected );
			}
		}
	};
	
	/**
	 * This listener calls {@link MaximizedMode#unmaximize(Dockable, AffectedSet)} if some element is dropped
	 * onto this station.
	 */
	private VetoableDockRelocatorListener relocatorListener = new VetoableDockRelocatorAdapter() {
		@Override
		public void dropped( DockRelocatorEvent event ){
			if( !event.isMove() ){
				MaximizedModeArea next = maximizedMode.getNextMaximizeArea( event.getTarget() );
				if( next == maximal ){
					manager.runTransaction( new AffectingRunnable() {
						public void run( AffectedSet set ){
							maximizedMode.unmaximize( (DockStation)getStation(), set );		
						}
					});
				}
			}
		}
	};
	
	/**
	 * Creates a new handle.
	 * @param station the station to handle
	 * @param manager the manager in whose realm this handle is used
	 */
	public CSplitDockStationHandle( CStation station, CLocationModeManager manager ){
		this.station = station;
		this.manager = manager;
	}
	
	/**
	 * Adds listener to this handle, the listener will be invoked if the current
	 * fullscreen-Dockable of the {@link SplitDockStation} changed.
	 * @param listener the new listener
	 */
	protected void add( ModeAreaListenerWrapper listener ){
		if( listener == null )
			throw new IllegalArgumentException( "listener must not be empty" );
		if( listeners.isEmpty() ){
			station.getStation().addSplitDockStationListener( fullScreenListener );
		}
		listeners.add( listener );
	}
	
	/**
	 * Removes listener from this handle.
	 * @param listener the listener to remove
	 */
	protected void remove( ModeAreaListenerWrapper listener ){
		listeners.remove( listener );
		if( listeners.isEmpty() ){
			station.getStation().removeSplitDockStationListener( fullScreenListener );
		}
	}
	
	/**
	 * Gets the station which is managed by this handle.
	 * @return the station
	 */
	public SplitDockStation getStation(){
		return station.getStation();
	}
	
	/**
	 * Gets the {@link CStation} which is managed by this handle.
	 * @return the station
	 */
	public CStation getCStation(){
		return station;
	}
	
	/**
	 * Returns this as {@link NormalModeArea}
	 * @return a representation of this
	 */
	public CNormalModeArea asNormalModeArea(){
		return normal;
	}
	
	/**
	 * Returns this as {@link MaximizedModeArea}
	 * @return a representation of this
	 */
	public CMaximizedModeArea asMaximziedModeArea(){
		return maximal;
	}
	
	/**
	 * Gets the mode which should be used to unmaximize children.
	 * @return the mode to unmaximize children
	 */
	protected LocationMode getNormalMode(){
		return normalMode;
	}
	
	private Path normalModeIdentifier(){
		return normalExtendedMode().getModeIdentifier();
	}
	
	private ExtendedMode normalExtendedMode(){
		return getNormalMode().getExtendedMode();
	}
	
	
	/**
	 * Ensures that dockable is a child of this
	 * station.
	 * @param dockable the element to drop, must not yet be a child of this station
	 * @throws IllegalStateException if dockable already
	 * is a child of this station.
	 */
	public void dropAside( Dockable dockable ){
		if( dockable.getDockParent() == station.getStation() )
			throw new IllegalStateException( "dockable already a child" );
		
		DockableSplitDockTree tree = getStation().createTree();
		if( tree.getRoot() == null )
			tree.root( dockable );
		else{
			tree.root( tree.horizontal( tree.put( dockable ), tree.unroot() ) );
		}
		getStation().dropTree( tree, false );
	}
	
	/**
	 * A wrapper for a {@link ModeAreaListener}.
	 * @author Benjamin Sigg
	 */
	protected static class ModeAreaListenerWrapper{
		/** the listener */
		private ModeAreaListener listener;
		/** the area */
		private ModeArea area;
		
		public ModeAreaListenerWrapper( ModeArea area, ModeAreaListener listener ){
			this.area = area;
			this.listener = listener;
		}
		
		/**
		 * Calls {@link ModeAreaListener#internalLocationChange(ModeArea, Set)} with
		 * dockables as set and {@link #area} as area.
		 * @param dockables the set of changed elements
		 */
		public void fire( Set dockables ){
			listener.internalLocationChange( area, dockables );
		}
		
		@Override
		public boolean equals( Object obj ){
			if( obj == this ) {
				return true;
			}

			if (obj == null) {
				return false;
			}

			if( this.getClass() == obj.getClass() ){
				ModeAreaListenerWrapper other = (ModeAreaListenerWrapper)obj;
				return other.area.equals( area ) && other.listener.equals( listener );
			}
			return false;
		}
		
		@Override
		public int hashCode(){
			return area.hashCode() ^ listener.hashCode();
		}
	}
	
	/**
	 * Represents the {@link SplitDockStation} as {@link CNormalModeArea}.
	 * @author Benjamin Sigg
	 */
	protected class Normal implements CNormalModeArea{
		public void setMode( LocationMode mode ){
			normalMode = mode;
		}
		
		public void addModeAreaListener( ModeAreaListener listener ){
			add( new ModeAreaListenerWrapper( this, listener ) );
		}
		
		public void removeModeAreaListener( ModeAreaListener listener ){
			remove(  new ModeAreaListenerWrapper( this, listener ) );	
		}
		
		public boolean autoDefaultArea() {
			return true;
		}
		
		public boolean isLocationRoot(){
			return true;
		}
		
		public void setController( DockController controller ){
			// ignore	
		}
		
		public boolean isNormalModeChild( Dockable dockable ){
			if( !isChild( dockable )){
				return false;
			}
			if( getStation().getFullScreen() == dockable){
				return false;
			}
			if( !isWorkingAreaValid( dockable )){
				return false;
			}
			return true;
		}
		
		private boolean isWorkingAreaValid( Dockable dockable ){
			if( dockable instanceof CommonDockable ){
				CStation workingArea = ((CommonDockable)dockable).getDockable().getWorkingArea();
				if( workingArea == null ){
					return CDockUtilities.getFirstWorkingArea( station ) == null;
				}
				else{
					return CDockUtilities.getFirstWorkingArea( station ) == workingArea;
				}
			}
			return true;
		}

		public DockableProperty getLocation( Dockable child ){
			return DockUtilities.getPropertyChain( getStation(), child );
		}

		public String getUniqueId(){
			return station.getUniqueId();
		}

		public boolean isChild( Dockable dockable ){
			return dockable.getDockParent() == getStation() && !maximal.isChild( dockable );
		}

		public SplitDockStation getStation(){
			return station.getStation();
		}
		
		public boolean respectWorkingAreas(){
			return true;
		}
		
		public boolean setLocation( Dockable dockable, DockableProperty location, AffectedSet set ){
			set.add( dockable );
			
			if( dockable.getDockParent() == station.getStation() ){
				if( location != null ){
					cleanFullscreen( set );
					getStation().move( dockable, location );
					return true;
				}
			}
			else{
				boolean acceptable = DockUtilities.acceptable( getStation(), dockable );
				if( acceptable ){
					if( dockable.getDockParent() != null ){
						dockable.getDockParent().drag( dockable );
					}
					
					cleanFullscreen( set );
					
					if( location != null ){
						if( !getStation().drop( dockable, location )){
							location = null;
						}
					}
					if( location == null ){
						if( !DockUtilities.isAncestor( station.getStation(), dockable )){
							getStation().drop( dockable );
						}
					}
					return true;
				}
			}
			return false;
		}
		
		private void cleanFullscreen( AffectedSet set ){
			Dockable fullscreen = getStation().getFullScreen();
			if( fullscreen != null ){
				maximal.setMaximized( fullscreen, false, null, set );
			}
		}
		
		public CLocation getCLocation( Dockable dockable ){
			DockableProperty property = DockUtilities.getPropertyChain( getStation(), dockable );
			return station.getStationLocation().expandProperty( station.getStation().getController(), property );
		}
		
		
		public CLocation getCLocation( Dockable dockable, Location location ){
			DockableProperty property = location.getLocation();
			if( property == null )
				return station.getStationLocation();
			
			return station.getStationLocation().expandProperty( station.getStation().getController(), property );
		}
		
		public CLocation getBaseLocation(){
			return station.getStationLocation();
		}
		
		public boolean isWorkingArea(){
			return station.isWorkingArea();
		}
	}
	
	/**
	 * Represents a {@link SplitDockStation} as a {@link CMaximizedModeArea}.
	 * @author Benjamin Sigg
	 */
	protected class Maximal implements CMaximizedModeArea{
		/** the controller in whose realm this area works */
		private DockController controller;
		
		public void addModeAreaListener( ModeAreaListener listener ){
			add( new ModeAreaListenerWrapper( this, listener ) );
		}
		
		public void removeModeAreaListener( ModeAreaListener listener ){
			remove( new ModeAreaListenerWrapper( this, listener ) );	
		}
		
		public void setMode( LocationMode mode ){
			if( maximizedMode != null && mode != null )
				throw new IllegalStateException( "handle already in use" );
			maximizedMode = (MaximizedMode)mode;
		}
		
		public void setController( DockController controller ){
			if( this.controller != null ){
				this.controller.getRelocator().removeVetoableDockRelocatorListener( relocatorListener );
			}
			this.controller = controller;
			if( controller != null ){
				controller.getRelocator().addVetoableDockRelocatorListener( relocatorListener );
			}
		}
		
		public DockableProperty getLocation( Dockable child ){
			DockableProperty property = DockUtilities.getPropertyChain( getStation(), child );
			SplitDockFullScreenProperty result = new SplitDockFullScreenProperty();
			result.setSuccessor( property.getSuccessor() );
			return result;
		}
		
		public boolean autoDefaultArea() {
			return true;
		}
		
		public boolean isLocationRoot(){
			return true;
		}
		
		public LocationMode getUnmaximizedMode(){
			return getNormalMode();
		}
		
		public void prepareApply( Dockable dockable, AffectedSet affected ){
			CLocationMode normal = manager.getMode( normalModeIdentifier() );
			if( normal != null ){
				manager.apply( dockable, normal, affected, false );
			}
		}
		
		public void prepareApply( Dockable dockable, Location history, AffectedSet set ){
			boolean remaximize = history != null && history.getLocation() instanceof SplitDockFullScreenProperty; 
			
			if( !remaximize ){
				if( manager.getMode( dockable ) != normalExtendedMode() ){
					CLocationMode normal = manager.getMode( normalModeIdentifier() );
					if( normal != null ){
						CGroupMovement movement = maximizedMode.getManager().getGroupBehavior().prepare( manager, dockable, normal.getExtendedMode() );
						if( movement != null ){
							manager.apply( dockable, normal.getExtendedMode(), movement );
						}
					}
				}
			}
		}
		
		public Runnable onApply( LocationModeEvent event ){
			if( event.isDone() )
				return null;
			
			Location location = event.getLocation();
        	Dockable dockable = event.getDockable();
        	
			DockableProperty property = location == null ? null : location.getLocation();
			
			if( event.getMode().getUniqueIdentifier().equals( normalModeIdentifier() )){
				// try to set the mode prematurely
		        if( property != null ){
		        	if( property.getSuccessor() == null ){
		            	CLocationMode last = manager.getCurrentMode( dockable );
		            	CLocationMode secondLast = manager.getPreviousMode( dockable );
		            	
		                if( last != null && secondLast != null ){
		                	if( normalModeIdentifier().equals( secondLast.getUniqueIdentifier() ) &&
		                			MaximizedMode.IDENTIFIER.equals( last.getUniqueIdentifier() )){
		                    
		                		MaximizedModeArea area = maximizedMode.get( location.getRoot() );
		                		
		                        if( area == this ){
		                            area.setMaximized( dockable, false, null, event.getAffected() );
		                            event.done(true);
		                            return null;
		                        }
		                    }
		                }
		            }
		        }
		    }
			
	        // if the element is about to become a child of this station, ensure
	        // this station does not show a maximized element
	        if( location != null && getMaximized() != null ){
	        	Map roots = manager.getRepresentations( location.getRoot() );
	        	for( DockStation station : roots.values() ){
	        		if( DockUtilities.isAncestor( getStation(), station )){
	        			maximizedMode.unmaximize( this, event.getAffected() );	
	        			break;
	        		}
	        	}
	        }
	        
	        // if this station currently shows dockable as maximized element, ensure it is no longer maximized
			if( maximizedMode != null && event.getMode().getUniqueIdentifier().equals( normalModeIdentifier() )){
				MaximizedModeArea area = maximizedMode.getMaximizeArea( dockable );
				if( area == this ){
					maximizedMode.unmaximize( dockable, event.getAffected() );
				}
			}
	        
	        return null;
		}
		
		public Runnable onApply( final LocationModeEvent event, final Dockable replacement ){
			if( event.isDone() )
				return null;
			
			if( !event.getMode().getUniqueIdentifier().equals( normalModeIdentifier() )){
				maximizedMode.unmaximize( getStation().getFullScreen(), event.getAffected() );

		        return new Runnable() {
					public void run(){
						if( replacement != null && replacement.getDockParent() != null ){
							maximizedMode.maximize( Maximal.this, replacement, event.getAffected() );
						}		
					}
				};
			}
			
			return null;
		}
		
		public String getUniqueId(){
			return station.getUniqueId();
		}
		
		public boolean isChild( Dockable dockable ){
			return getStation().getFullScreen() == dockable;
		}
		
		public SplitDockStation getStation(){
			return station.getStation();
		}
		
		public boolean respectWorkingAreas(){
			return false;
		}
		
		public Dockable[] getMaximized(){
			Dockable dockable = getStation().getFullScreen();
			if( dockable == null ){
				return null;
			}
			return new Dockable[]{ dockable };
		}

		public void setMaximized( Dockable dockable, boolean maximized, Location location, AffectedSet set ){
			SplitDockStation station = getStation();
			
			if( !maximized ){
				if( station.getFullScreen() != null && DockUtilities.isAncestor( station.getFullScreen(), dockable )){
					station.setFullScreen( null );
				}
			}
			else{
				DockableProperty property = location == null ? null : location.getLocation();
				
				if( property instanceof SplitDockFullScreenProperty ){
					if( getMaximized() != null ){
						if( getStation().drop( dockable, property ) ){
			        		return;
			        	}
					}
				}
				
				if( dockable.getDockParent() == station ){
					station.setFullScreen( dockable );
		        }
		        else{
		            if( dockable.getDockParent() != null )
		                dockable.getDockParent().drag( dockable );
	
		            dropAside( dockable );
		            station.setFullScreen( dockable );
		        }
			}
			
			set.add( dockable );
		}
		
		public boolean isRepresenting( DockStation station ){
			return station == CSplitDockStationHandle.this.station.getStation();
		}
		
		public CLocation getCLocation( Dockable dockable ){
			DockableProperty property = DockUtilities.getPropertyChain( getStation(), dockable );
			return getCLocation( property );
		}
		
		public CLocation getCLocation( Dockable dockable, Location location ){
			DockableProperty property = location.getLocation();
			return getCLocation( property );
		}
		
		private CLocation getCLocation( DockableProperty property ){
			CLocation stationLocation = station.getStationLocation();
			CMaximizedLocation result = new CMaximizedLocation( stationLocation.findRoot() );
			
			if( property != null ){
				property = property.getSuccessor();
			}
			if( property != null ){
				return result.expandProperty( station.getStation().getController(), property );
			}
			else{
				return result;
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy