Package bibliothek.gui.dock.common.mode

Source Code of bibliothek.gui.dock.common.mode.CLocationModeManager

/*
* 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
* benjamin_sigg@gmx.ch
* CH - Switzerland
*/
package bibliothek.gui.dock.common.mode;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.common.CLocation;
import bibliothek.gui.dock.common.CStation;
import bibliothek.gui.dock.common.intern.CControlAccess;
import bibliothek.gui.dock.common.intern.CDockFrontend;
import bibliothek.gui.dock.common.intern.CDockFrontendListener;
import bibliothek.gui.dock.common.intern.CDockable;
import bibliothek.gui.dock.common.intern.CDockableAccess;
import bibliothek.gui.dock.common.intern.CSetting;
import bibliothek.gui.dock.common.intern.CommonDockable;
import bibliothek.gui.dock.facile.mode.CLocationModeSettings;
import bibliothek.gui.dock.facile.mode.Location;
import bibliothek.gui.dock.facile.mode.LocationMode;
import bibliothek.gui.dock.facile.mode.LocationModeManager;
import bibliothek.gui.dock.layout.DockableProperty;
import bibliothek.gui.dock.support.mode.AffectedSet;
import bibliothek.gui.dock.support.mode.AffectingRunnable;
import bibliothek.gui.dock.support.mode.ModeManager;
import bibliothek.gui.dock.support.mode.ModeManagerListener;
import bibliothek.gui.dock.support.mode.ModeSettings;
import bibliothek.gui.dock.support.mode.ModeSettingsConverter;
import bibliothek.gui.dock.support.mode.UndoableModeSettings;
import bibliothek.gui.dock.util.DockUtilities;
import bibliothek.gui.dock.util.IconManager;
import bibliothek.util.Path;
import bibliothek.util.container.Single;

/**
* {@link LocationModeManager} providing additional methods for working with
* {@link CLocation}s, {@link CommonDockable}s and other items specific to the
* <code>common</code> project.
* @author Benjamin Sigg
*
*/
public class CLocationModeManager extends LocationModeManager<CLocationMode>{
  /** the key used for the {@link IconManager} to read the {@link javax.swing.Icon} for the "minimize"-action */
    public static final String ICON_MANAGER_KEY_MINIMIZE = "locationmanager.minimize";
    /** the key used for the {@link IconManager} to read the {@link javax.swing.Icon} for the "maximize"-action */
    public static final String ICON_MANAGER_KEY_MAXIMIZE = "locationmanager.maximize";
    /** the key used for the {@link IconManager} to read the {@link javax.swing.Icon} for the "normalize"-action */
    public static final String ICON_MANAGER_KEY_NORMALIZE = "locationmanager.normalize";
    /** the key used for the {@link IconManager} to read the {@link javax.swing.Icon} for the "externalize"-action */
    public static final String ICON_MANAGER_KEY_EXTERNALIZE = "locationmanager.externalize";
    /** the key used for the {@link IconManager} to read the {@link javax.swing.Icon} for the "unexternalize"-action */
    public static final String ICON_MANAGER_KEY_UNEXTERNALIZE = "locationmanager.unexternalize";
    /** the key used for the {@link IconManager} to read the {@link javax.swing.Icon} for the "unmaximize externalized"-action */
    public static final String ICON_MANAGER_KEY_UNMAXIMIZE_EXTERNALIZED = "locationmanager.unmaximize_externalized";
   
  private CControlAccess control;
 
  private CNormalMode normalMode;
  private CMaximizedMode maximizedMode;
  private CMinimizedMode minimizedMode;
  private CExternalizedMode externalizedMode;
 
  /**
   * Creates a new manager.
   * @param control the control in whose realm this manager works
   */
  public CLocationModeManager( CControlAccess control ){
    super( control.getOwner().intern().getController() );
    this.control = control;

    setDoubleClickStrategy( new PreviousModeDoubleClickStrategy( this ) );
   
    minimizedMode = new CMinimizedMode( control.getOwner() );
    maximizedMode = new CMaximizedMode( control.getOwner() );
    normalMode = new CNormalMode( control.getOwner() );
    externalizedMode = new CExternalizedMode( control.getOwner() );
   
    putMode( minimizedMode );
    putMode( externalizedMode );
    putMode( normalMode );
    putMode( maximizedMode );
       
        addModeManagerListener(new ModeManagerListener<Location, LocationMode>(){
      public void dockableAdded( ModeManager<? extends Location, ? extends LocationMode> manager, Dockable dockable ){
        // ignore
      }

      public void dockableRemoved( ModeManager<? extends Location, ? extends LocationMode> manager, Dockable dockable ){
        // ignore
      }

      public void modeAdded( ModeManager<? extends Location, ? extends LocationMode> manager, LocationMode mode ){
        // ignore
      }

      public void modeChanged( ModeManager<? extends Location, ? extends LocationMode> manager, Dockable dockable, LocationMode oldMode, LocationMode newMode ){
        CDockableAccess access = CLocationModeManager.this.control.access( ((CommonDockable)dockable).getDockable() );
        if( access != null ){
          ExtendedMode mode = getMode(dockable);
          access.informMode( mode );
        }
      }

      public void modeRemoved( ModeManager<? extends Location, ? extends LocationMode> manager, LocationMode mode ){
        // ignore
      }
    });
  }
 
  /**
   * Direct access to the mode handling "normal" {@link Dockable}s.
   * @return the mode
   */
  public CNormalMode getNormalMode(){
    return normalMode;
  }

  /**
   * Direct access to the mode handling "maximized" {@link Dockable}s.
   * @return the mode
   */
  public CMaximizedMode getMaximizedMode(){
    return maximizedMode;
  }
 
  /**
   * Direct access to the mode handling "minimized" {@link Dockable}s.
   * @return the mode
   */
  public CMinimizedMode getMinimizedMode(){
    return minimizedMode;
  }
 
  /**
   * Direct access to the mode handling "externalized" {@link Dockable}s.
   * @return the mode
   */
  public CExternalizedMode getExternalizedMode(){
    return externalizedMode;
  }
 
 
  @Override
  protected boolean createEntryDuringRead( String key ){
    return control.shouldStore( key );
  }
 
  @Override
  public void remove( Dockable dockable ){
    if( dockable instanceof CommonDockable ){
      CDockable cdockable = ((CommonDockable)dockable).getDockable();
      String key = control.shouldStore( cdockable );
      if( key != null ){
        addEmpty( key );
      }
    }
    super.remove( dockable );
  }

    @Override
    public <B> ModeSettings<Location, B> createModeSettings( ModeSettingsConverter<Location, B> converter ){
      return new CLocationModeSettings<B>( converter );
    }
   
    @Override
    public void readSettings( ModeSettings<Location, ?> settings ){
        UndoableModeSettings undoable = new UndoableModeSettings(){
          public boolean createTemporaryDuringRead( String key ){
            return control.getRegister().isMultiId( key );
          }
        };
     
      final Runnable temporary = readSettings( settings, undoable );
      control.getOwner().intern().addListener( new CDockFrontendListener(){
      public void loading( CDockFrontend frontend, CSetting setting ){
        // ignore
      }
     
      public void loaded( CDockFrontend frontend, CSetting setting ){
        temporary.run();
        frontend.removeListener( this );
      }
    });
    }
   
    @Override
    public Runnable readSettings( ModeSettings<Location, ?> settings, UndoableModeSettings pending ){
      Runnable result = super.readSettings( settings, pending );
      if( settings instanceof CLocationModeSettings<?> ){
        CLocationModeSettings<?> locationSettings = (CLocationModeSettings<?>)settings;
        locationSettings.rescue( getMaximizedMode() );
      }
      return result;
    }
   
    /**
     * Tries to set the location of <code>dockable</code>. Does nothing if
     * <code>location</code> is invalid or requires information that is
     * not available. If <code>dockable</code> is a {@link CommonDockable} and
     * the {@link CLocationMode mode} respects {@link CLocationMode#respectWorkingAreas(DockStation) working-areas},
     * then the working-area is set or removed depending on the value of {@link CStation#isWorkingArea()}.
     * @param dockable the element to move
     * @param location the new location of <code>dockable</code>
     */
    public void setLocation( Dockable dockable, CLocation location ){
      ExtendedMode mode = location.findMode();
      if( mode == null )
        return;
     
      CLocationMode newMode = getMode( mode.getModeIdentifier() );
      if( newMode == null )
        return;
       
      String root = location.findRoot();
     
      if( root != null ){
        if( dockable instanceof CommonDockable ){
          CStation<?> station = control.getOwner().getStation( root );
          if( station != null ){
            if( newMode.respectWorkingAreas( station.getStation() )){
              if( station.isWorkingArea() ){
                ((CommonDockable)dockable).getDockable().setWorkingArea( station );
              }
              else{
                ((CommonDockable)dockable).getDockable().setWorkingArea( null );
              }
            }
          }
        }
       
          // easy solution: set the location, then change the mode
          setProperties( newMode, dockable, new Location( mode.getModeIdentifier(), root, location.findProperty() ) );
          apply( dockable, newMode, true );
      }
      else{
        apply( dockable, newMode, false );
      }
    }
   
    /**
     * Sets the default location of <code>dockable</code> when going into
     * <code>mode</code>. The properties set here will be overridden
     * as soon as the user drags <code>dockable</code> to another location (within
     * the same mode) or <code>dockable</code> is removed permanently.<br>
     * This method has no effect if <code>dockable</code> is already in
     * <code>mode</code>. There is also no effect if <code>dockable</code>
     * has not been registered at the {@link CLocationModeManager}.<br>
     * Note: it is the clients responsibility to ensure that <code>location</code>
     * and <code>mode</code> belong to each other.
     * @param dockable the element whose location will be set
     * @param mode the mode for which the location is to be set or <code>null</code>
     * @param location the new location
     * @throws IllegalArgumentException if either argument is <code>null</code> or
     * if {@link CLocation#findRoot()} or {@link CLocation#findProperty()} returns
     * <code>null</code>
     */
    public void setLocation( Dockable dockable, ExtendedMode mode, CLocation location ){
        if( dockable == null )
            throw new IllegalArgumentException( "dockable must not be null" );
       
        if( mode == null )
            throw new IllegalArgumentException( "mode must not be null" );
       
        if( location != null ){
          String root = location.findRoot();
          if( root == null )
              throw new IllegalArgumentException( "the location is not sufficient to find the root station" );
         
          DockableProperty property = location.findProperty();
          if( property == null )
              throw new IllegalArgumentException( "the location does not carry enough information to find the location of dockable" );
         
          ExtendedMode locationMode = location.findMode();
          if( locationMode == null ){
            throw new IllegalArgumentException( "the location does not carry enough information to find the mode of dockable" );
          }
         
          if( !mode.getModeIdentifier().equals( locationMode.getModeIdentifier() ))
            throw new IllegalArgumentException( "location and mode do not belong together, they do not have the same identifier" );
         
          setProperties( getMode( mode.getModeIdentifier() ), dockable, new Location( mode.getModeIdentifier(), root, property ) );
        }
        else{
          setProperties( getMode( mode.getModeIdentifier() ), dockable, null );
        }
    }
 
    /**
     * Gets an element describing the location of <code>dockable</code> as
     * good as possible.
     * @param dockable the element whose location should be searched
     * @return the location or <code>null</code> if no location was found
     */
    public CLocation getLocation( Dockable dockable ){
      CLocationMode mode = getCurrentMode( dockable );
      if( mode == null )
        return null;
     
      return mode.getCLocation( dockable );
    }
   
    /**
     * Assuming that <code>dockable</code> is currently not in mode <code>mode</code>,
     * then this method searches for the previously stored location of <code>dockable</code>.
     * Note that this method can't tell where <code>dockable</code> would be
     * shown if it never was in that mode and the client never specified the
     * location.
     * @param dockable the dockable whose location is searched
     * @param mode the mode which might be taken by <code>dockable</code>
     * @return the location or <code>null</code>
     * @throws IllegalArgumentException if any argument is <code>null</code>
     */
    public CLocation getLocation( Dockable dockable, ExtendedMode mode ){
        if( dockable == null )
            throw new IllegalArgumentException( "dockable must not be null" );
       
        if( mode == null )
            throw new IllegalArgumentException( "mode must not be null" );
       
        CLocationMode cmode = getMode( mode.getModeIdentifier() );
       
        Location location = getProperties( cmode, dockable );
        if( location == null )
            return null;
       
        return cmode.getCLocation( dockable, location );
    }
   
    /**
     * Tries to find the "optimal spot" where to put a new child onto <code>station</code>. In this
     * case the optimal spot is {@link CLocation#aside()} the latest focused child of <code>station</code>.
     * @param station the station where a {@link CDockable} is about to be dropped onto
     * @return the preferred location of the new child
     */
    public CLocation getDropLocation( CStation<?> station ){
      Dockable[] history = control.getOwner().getController().getFocusHistory().getHistory();
      for( int i = history.length-1; i >= 0; i-- ){
        Dockable next = history[i];
        if( next instanceof CommonDockable && next.asDockStation() != station.getStation() ){
          CDockable cnext = ((CommonDockable)next).getDockable();
         
          if( DockUtilities.isAncestor( station.getStation(), next )){
            boolean valid;
            if( station.isWorkingArea() ){
              valid = cnext.getWorkingArea() == station;
            }
            else{
              valid = cnext.getWorkingArea() == null;
            }
            if( valid ){
              CLocation location = cnext.getBaseLocation();
              if( location != null ){
                return location.aside();
              }
              }
          }
          if( cnext.getWorkingArea() == station ){
            CLocation location = cnext.getBaseLocation();
            if( location != null ){
              return location.aside();
            }
          }
        }
      }
      return station.getStationLocation();
    }
   
    @Override
    public void ensureValidLocation( Dockable dockable ){
      if( dockable instanceof CommonDockable ){
        ensureValidLocation( ((CommonDockable)dockable).getDockable() );
      }
    }
   
    /**
     * This method compares the current mode of <code>dockable</code> with its
     * availability set. If the current mode is not available, then <code>dockable</code>
     * is put into another mode (usually the {@link #getNormalMode() normal mode}).<br>
     * This method also checks the working area, provided that the current mode respects
     * the working-area settings.<br>
     * This method returns immediatelly if in {@link #isLayouting() layouting mode}
     * @param dockable the element whose mode is to be checked
     */
  public void ensureValidLocation( CDockable dockable ){
    if( isLayouting() )
      return;
   
        ExtendedMode mode = getMode( dockable.intern() );
        if( mode == ExtendedMode.NORMALIZED ){
            CStation<?> preferredArea = dockable.getWorkingArea();
            CStation<?> currentArea = findFirstParentWorkingArea( dockable.intern() );
           
            if( preferredArea != currentArea ){
                if( preferredArea == null ){
                  // the dockable is on a working-area, but should not be there
                  CLocation defaultLocation = getNormalMode().getDefaultLocation();
                    dockable.setLocation( defaultLocation );
                }
                else{
                  // reset the location
                    dockable.setLocation( preferredArea.getStationLocation() );
                }
            }
           
            mode = getMode( dockable.intern() );
        }
       
        // normalize the element if its current mode is not valid
        if( !isModeAvailable( dockable.intern(), mode )){
          dockable.setExtendedMode( ExtendedMode.NORMALIZED );
        }
  }

    /**
     * Ensures that all dockables are in a basic mode.<br>
     * This method returns immediately if in {@link #isLayouting() layouting mode}
     * @return <code>true</code> if at least one element was affected by changes,
     * <code>false</code> if nothing happened.
     */
  public boolean ensureBasicModes(){
    if( isLayouting() )
      return false;
   
    final Single<Boolean> result = new Single<Boolean>( false );
   
    runTransaction( new AffectingRunnable() {
      public void run( AffectedSet set ){
        for( Dockable dockable : listDockables() ){
          CLocationMode current = getCurrentMode( dockable );
          if( current != null && !current.isBasicMode() ){
            List<CLocationMode> modes = getModeHistory( dockable );
            CLocationMode next = null;
            for( int i = modes.size()-1; i >= 0 && next == null; i-- ){
              CLocationMode mode = modes.get( i );
              if( mode.isBasicMode() && isModeAvailable( dockable, mode.getExtendedMode() )){
                next = mode;
              }
            }
            if( next == null ){
              next = getNormalMode();
            }
           
            result.setA( true );
            setMode( dockable, next.getExtendedMode() );
          }
        }   
      }
    });
    return result.getA();
  }
 
  /**
   * Updates the location of all dockables that should be on a working-area
   * and that are currently in a mode that does not support working-areas. The history
   * of the elements is searched for the first mode which supports working-areas. If no
   * such mode is found, then the normal-mode is applied.
   */
  public void resetWorkingAreaChildren(){
    runTransaction( new AffectingRunnable() {
      public void run( AffectedSet set ){
        for( Dockable dockable : listDockables() ){
          if( dockable instanceof CommonDockable ){
            CDockable cdockable = ((CommonDockable)dockable).getDockable();
            resetWorkingArea( cdockable, set );
          }
        }
      }
    });
  }
 
  private void resetWorkingArea( CDockable dockable, AffectedSet set ){
    if( dockable.getWorkingArea() == null )
      return;
   
    DockStation parent = dockable.intern().getDockParent();
    if( parent == null )
      return;
   
    CLocationMode current = getCurrentMode( dockable.intern() );
    if( current == null )
      return;
   
    if( current.respectWorkingAreas( parent ))
      return;
   
    // need to reset
    List<Location> history = getPropertyHistory( dockable.intern() );
    CLocationMode next = null;
    for( int i = history.size()-1; i >= 0 && next == null; i-- ){
      Location check = history.get( i );
      Path path = check.getMode();
      String root = check.getRoot();
      if( path != null && root != null ){
        CLocationMode mode = getMode( path );
        if( mode != null ){
          CStation<?> station = control.getOwner().getStation( root );
          if( station != null ){
            if( mode.respectWorkingAreas( station.getStation() ) && mode.isRepresenting( station.getStation() )){
              if( isModeAvailable( dockable.intern(), mode.getExtendedMode() )){
                next = mode;
              }
            }
          }
        }
      }
    }
    if( next == null ){
      next = getNormalMode();
    }

    apply( dockable.intern(), next, set, false );
  }
 
  /**
   * Guesses the result of {@link #getCurrentMode(Dockable)} once a {@link Dockable} is
   * dropped onto {@link DockStation}. If more than one {@link LocationMode mode} is using
   * <code>parent</code>, then the guess might not always be correct.
   * @param parent some station
   * @return the mode its children are in, or <code>null</code> if no guess can be made
   */
  public ExtendedMode childsExtendedMode( DockStation parent ){
    while( parent != null ){
      CLocationMode mode = getRepresentingMode( parent );
      if( mode != null ){
        return mode.getExtendedMode();
      }
      Dockable dockable = parent.asDockable();
      if( dockable == null )
        return null;
      parent = dockable.getDockParent();
    }
   
    return null;
  }

  /**
     * Finds the first {@link CStation} in the path up to the root from
     * <code>dockable</code> wich is a working area.
     * @param dockable the element which might have a {@link CStation}
     * as parent.
     * @return the first found {@link CStation}.
     */
    private CStation<?> findFirstParentWorkingArea( Dockable dockable ){
        DockStation station = dockable.getDockParent();
        dockable = station == null ? null : station.asDockable();
       
        if( dockable != null )
            return getAreaOf( dockable );
        else
            return null;
    }
   
    /**
     * Searches <code>dockable</code> and its parent for the first {@link CStation}
     * that is a working area.
     * @param dockable the element whose working area is searched
     * @return the first working area or <code>null</code>
     */
    protected CStation<?> getAreaOf( Dockable dockable ){
        Map<DockStation, CStation<?>> stations = new HashMap<DockStation, CStation<?>>();
        for( CStation<?> station : control.getOwner().getStations() ){
            if( station.isWorkingArea() ){
                stations.put( station.getStation(), station );
            }
        }
       
        if( dockable.asDockStation() != null ){
            CStation<?> station = stations.get( dockable.asDockStation() );
            if( station != null )
                return station;
        }
       
        Dockable check = dockable;
        while( check != null ){
            DockStation parent = check.getDockParent();
            if( parent == null )
                check = null;
            else
                check = parent.asDockable();
           
            CStation<?> station = stations.get( parent );
            if( station != null )
                return station;
        }
       
        return null;
    }
   
    /**
     * Searches the {@link CLocationMode mode} which represents the mode of
     * the children of <code>station</code>. This method calls
     * {@link LocationMode#isRepresenting(DockStation)} with <code>station</code>,
     * but does not check the parents of <code>station</code>. Basic modes are preferred
     * over non-basic modes by this method.
     * @param station some station
     * @return the mode or <code>null</code> if nothing found
     */
    private CLocationMode getRepresentingMode( DockStation station ){
      Iterable<CLocationMode> modes = modes();
      CLocationMode first = null;
     
      for( CLocationMode mode : modes ){
        if( mode.isRepresenting( station )){
          if( mode.isBasicMode() )
            return mode;
          if( first == null )
            first = mode;
        }
      }
     
      return first;
    }
}
TOP

Related Classes of bibliothek.gui.dock.common.mode.CLocationModeManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.