/*
    VLDocking Framework 2.1
    Copyright VLSOLUTIONS, 2004-2006

    email : info@vlsolutions.com
------------------------------------------------------------------------
This software is distributed under the CeCILL license, a GNU GPL-compatible
license adapted to french law.
French and English license headers are provided at the begining of
the source files of this software application.
------------------------------------------------------------------------
LICENCE CeCILL (FRENCH VERSION).
------------------------------------------------------------------------
Ce logiciel est un programme informatique servant à améliorer les interfaces
homme-machine d'applications Java basées sur Swing, en leur apportant un
ensemble de fonctions relatives au dockage des composants.

Ce logiciel est régi par la licence CeCILL soumise au droit français et
respectant les principes de diffusion des logiciels libres. Vous pouvez
utiliser, modifier et/ou redistribuer ce programme sous les conditions
de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
sur le site "http://www.cecill.info".

En contrepartie de l'accessibilité au code source et des droits de copie,
de modification et de redistribution accordés par cette licence, il n'est
offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,
seule une responsabilité restreinte pèse sur l'auteur du programme,  le
titulaire des droits patrimoniaux et les concédants successifs.

A cet égard  l'attention de l'utilisateur est attirée sur les risques
associés au chargement,  à l'utilisation,  à la modification et/ou au
développement et à la reproduction du logiciel par l'utilisateur étant
donné sa spécificité de logiciel libre, qui peut le rendre complexe à
manipuler et qui le réserve donc à des développeurs et des professionnels
avertis possédant  des  connaissances  informatiques approfondies.  Les
utilisateurs sont donc invités à charger  et  tester  l'adéquation  du
logiciel à leurs besoins dans des conditions permettant d'assurer la
sécurité de leurs systèmes et ou de leurs données et, plus généralement,
à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.

Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
pris connaissance de la licence CeCILL, et que vous en avez accepté les
termes.

------------------------------------------------------------------------
CeCILL License (ENGLISH VERSION)
------------------------------------------------------------------------

This software is a computer program whose purpose is to enhance Human-Computer
Interfaces written in Java with the Swing framework, providing them a set of
functions related to component docking.

This software is governed by the CeCILL  license under French law and
abiding by the rules of distribution of free software.  You can  use,
modify and/ or redistribute the software under the terms of the CeCILL
license as circulated by CEA, CNRS and INRIA at the following URL
"http://www.cecill.info".

As a counterpart to the access to the source code and  rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty  and the software's author,  the holder of the
economic rights,  and the successive licensors  have only  limited
liability.

In this respect, the user's attention is drawn to the risks associated
with loading,  using,  modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean  that it is complicated to manipulate,  and  that  also
therefore means  that it is reserved for developers  and  experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or
data to be ensured and,  more generally, to use and operate it in the
same conditions as regards security.

The fact that you are presently reading this means that you have had
knowledge of the CeCILL license and that you accept its terms.

*/


package com.vlsolutions.swing.docking;

import com.vlsolutions.swing.docking.event.DockingActionAddDockableEvent;
import com.vlsolutions.swing.docking.event.DockingActionEvent;
import com.vlsolutions.swing.docking.event.DockingActionSplitComponentEvent;
import java.awt.*;
import javax.swing.*;

/** This class provides an algorithm for finding (and then inserting) the most suitable place to
 * show a dockable, based on a relative positionning.
 *
 * @author Lilian Chamontin, vlsolutions.
 * @version 1.0
 * @update 2005/10/10 Lilian Chamontin : improved heuristics of resizing  (best matching of the appropriate SplitContainer)
 * @update 2005/10/10 Lilian Chamontin : improved again heuristics of resizing
 */
class RelativeDockingUtilities {
  /* package class */
  
 /* This class is an extraction of DockingDesktop - we will enhance (and complexify)
  * the algorithm later,
  * and should not mess up with DockingDesktop source code.
  *
  *
  */
  
  private RelativeDockingUtilities() {
  }
  
  /** try to find the best suitable SplitContainer from where insertion will
   * be done.
   * */
  private static SplitContainer findBestContainer(Container ancestorContainer, RelativeDockablePosition position){
    
    // convert relative positionning to current positionning (depending
    // on the current size of dockingPanel)
    int x = (int) (position.getX() * ancestorContainer.getWidth());
    int y = (int) (position.getY() * ancestorContainer.getHeight());
    int w = (int) (position.getWidth() * ancestorContainer.getWidth());
    int h = (int) (position.getHeight() * ancestorContainer.getHeight());
    
    int centerX = x + w / 2;
    int centerY = y + h / 2;
    
    Component centerComp = ancestorContainer.findComponentAt(centerX, centerY);
    // find the splitcontainer containing centerComp,
    // and ensure this split is linked to ancestorContainer by a hierarchy of split containers
    // (to avoid selecting a split inside a sub-container)
    Component splitComp = centerComp;
    boolean found = false;
    while (splitComp != null && splitComp != ancestorContainer && ! found){
      splitComp = splitComp.getParent();
      if (splitComp instanceof SplitContainer){
        // ok we've found a split : now let's check if there's a direct split hierarchy
        // between this one and the ancestor container
        Component up = splitComp.getParent();
        while (up instanceof SplitContainer){
          up = up.getParent();
        }
        if (up == ancestorContainer){
          found = true;
        }
      }
    }
    
    if (splitComp instanceof SplitContainer) { //2006/09/12
      /* Try to find a larger splitcontainer that still fits well
       * with the position.
       * for example :
       *
       * [1|___2_____]
       * [1|[3|[4|5]]]
       *
       * if we remove "2", what remains is [1|[3|[4|5]]]
       * and on restoring, the selected split is [4|5], although [3|[4|5]] would be
       * a much better choice.
       *
       * to do that, we try to find the parent with the best "width" and "height"
       *
       */
      float EPSILON = 0.05f;
      found = true;
      while (found && splitComp.getParent() instanceof SplitContainer){
        found = false;
        float widthRatio =  splitComp.getWidth() / (float) w;
        float heightRatio = splitComp.getHeight() / (float) h;
        
        SplitContainer parentSplit = (SplitContainer) splitComp.getParent();
        float pWidthRatio = parentSplit.getWidth() / (float) w;
        float pHeightRatio = parentSplit.getHeight() / (float) h;
        
        boolean sameOrientation = ((SplitContainer)splitComp).getOrientation() == parentSplit.getOrientation();
        if (sameOrientation){
          if (parentSplit.getOrientation() == SplitContainer.HORIZONTAL_SPLIT){
            // two horizontal, which one has the best width ?
            if (Math.abs(pWidthRatio-1) < Math.abs(widthRatio-1)){
              //parents width is better : we upgrade splitcomp
              splitComp = splitComp.getParent();
              found = true; // loop again
            }
          } else {
            // vertical == same width, is parent height better ?
            if (Math.abs(pHeightRatio-1) < Math.abs(heightRatio-1)){
              //height is better : we upgrade splitcomp
              splitComp = splitComp.getParent();
              found = true; // loop again
            }
          }
        } else { // opposite orientation
          if (parentSplit.getOrientation() == SplitContainer.HORIZONTAL_SPLIT){
            // so split is V and parent is H
            // has parent a better width ratio ?
            if (Math.abs(pWidthRatio-1) < Math.abs(widthRatio-1)){
              //parents width is better : we upgrade splitcomp
              splitComp = splitComp.getParent();
              found = true; // loop again
            }
          } else {
            if (Math.abs(pHeightRatio-1) < Math.abs(heightRatio-1)){
              //height is better : we upgrade splitcomp
              splitComp = splitComp.getParent();
              found = true; // loop again
            }
          }
        }      
      }
      
      
    }
    
    
    
    /*while (splitComp != null && splitComp != ancestorContainer
        && ! (splitComp instanceof SplitContainer)) {
      splitComp = splitComp.getParent();
    }*/
    
    if (splitComp instanceof SplitContainer) {
      // try to enhance the selection when there are global anchors //2005/10/10
      // we're now looking for a split container whose anchors would better match
      // the ones of the dockable position
      
//          boolean anchoredTop = Math.abs( y - dockingPanel.getY()) < 5;
//          boolean anchoredLeft =Math.abs(x - dockingPanel.getX()) < 5;
//          boolean anchoredBottom = Math.abs(y + h - dockingPanel.getY() - dockingPanel.getHeight()) < 5;
//          boolean anchoredRight = Math.abs(x + w - dockingPanel.getX() - dockingPanel.getWidth()) < 5;
      /*boolean anchoredTop = y < 5; //2005/11/08 enhanced !
      boolean anchoredLeft = x < 5;
      boolean anchoredBottom = Math.abs(y + h - ancestorContainer.getHeight()) < 5;
      boolean anchoredRight = Math.abs(x + w - ancestorContainer.getWidth()) < 5;*/
      
      int anchors = position.getAnchors();
      boolean anchoredTop = (anchors & AnchorConstraints.ANCHOR_TOP)>0;
      boolean anchoredLeft = (anchors & AnchorConstraints.ANCHOR_LEFT)>0;
      boolean anchoredBottom = (anchors & AnchorConstraints.ANCHOR_BOTTOM)>0;
      boolean anchoredRight = (anchors & AnchorConstraints.ANCHOR_RIGHT)>0;
      
      
      return findBetterContainer((SplitContainer)splitComp, ancestorContainer, anchoredTop, anchoredLeft, anchoredBottom, anchoredRight);
      
    } else {
      return null;
    }
  }
  
  /** try to find a SplitContainer up in the hierarchy, satisfying the global anchors */
  private static SplitContainer findBetterContainer(SplitContainer split,
      Container dockingPanel, boolean anchoredTop,
      boolean anchoredLeft, boolean anchoredBottom, boolean anchoredRight){
    // we already have found a splitContainer, but it can be subOptimal in some cases
    // for instance when we want to anchor a component at the bottom of the container (full width),
    // and there are two horizontal splits there, we might return the internal split (not full width),
    // and not the external (full width) one.
    // this method will try to find a splitcontainer upper in the hierarchy, with better anchors
      /*System.out.println("global anchors " + anchoredTop + " "
             + anchoredLeft + " " + anchoredBottom + " " + anchoredRight);*/
    boolean [] globalAnchors = {anchoredTop, anchoredLeft, anchoredBottom, anchoredRight};
    int globalCount = 0; // number of anchors to be found (at least)
    for (int i=0; i < 4; i++){
      if (globalAnchors[i]){
        globalCount++;
      }
    }
    
    SplitContainer betterSplit = split;
    boolean [] splitAnchors = new boolean[4];
    while (true){
      int contacts = findAnchors(split, dockingPanel);
      splitAnchors[0] = (contacts & AnchorConstraints.ANCHOR_TOP) > 0;
      splitAnchors[1] = (contacts & AnchorConstraints.ANCHOR_LEFT) > 0;
      splitAnchors[2] = (contacts & AnchorConstraints.ANCHOR_BOTTOM) > 0;
      splitAnchors[3] = (contacts & AnchorConstraints.ANCHOR_RIGHT) > 0;
      int count = 0;
      for (int i=0; i < 4; i++){
        if (globalAnchors[i] && splitAnchors[i]){
          count ++;
        }
      }
      if (count >= globalCount){
        return split;
      } else {
        if (split.getParent() instanceof SplitContainer){
          split = (SplitContainer) split.getParent();
        } else {
          return split; // last split container
        }
      }
    }
  }
  
  /** builds an array used to find anchors of a component relative to its ancestor container */
/*  private static boolean [] findAnchors(Component comp, Container container){
    Rectangle r = SwingUtilities.convertRectangle(comp,
        new Rectangle(0, 0, comp.getWidth(), comp.getHeight()),
        container);
    int x = container.getX();
    int y = container.getY();
    int w = container.getWidth();
    int h = container.getHeight();
    boolean [] anchors = new boolean [4];
    anchors[0] = Math.abs( y - r.y) < 5;
    anchors[1] = Math.abs(x - r.x) < 5;
    anchors[2] = Math.abs(y + h - r.y - r.height) < 30;
    anchors[3] = Math.abs(x + w - r.x - r.width) < 5;
    return anchors;
 
  }
 */
  
  /** Horizontally divide split and resize the new split
   *
   * @since 2004/04/24
   * */
  private static void hSplitAndResize(Component base,
      Component left, Component right, double proportion){
    Container parent = base.getParent();
    SplitContainer newSplit = new SplitContainer(JSplitPane.
        HORIZONTAL_SPLIT);
    DockingUtilities.replaceChild(parent, base, newSplit);
    newSplit.setLeftComponent( left);
    newSplit.setRightComponent(right);
    SwingUtilities.invokeLater(new SplitResizer(newSplit, proportion));
  }
  
  /** Vertically divide split and resize the new split
   *
   * @since 2004/04/24
   * */
  private static void vSplitAndResize(Component base,
      Component top, Component bottom, double proportion){
    Container parent = base.getParent();
    SplitContainer newSplit = new SplitContainer(JSplitPane.
        VERTICAL_SPLIT);
    DockingUtilities.replaceChild(parent, base, newSplit);
    newSplit.setTopComponent( top);
    newSplit.setBottomComponent(bottom);
    SwingUtilities.invokeLater(new SplitResizer(newSplit, proportion));
  }
  
  /** look up the split hierarchy to find which borders a component is touching
   *
   * @param base                the component to find anchors for
   * @param ancestorContainer   the top level ancestor used to stop searching
   */
  public static int findAnchors(Component base, Container ancestorContainer){
    int contact = AnchorConstraints.ANCHOR_TOP|AnchorConstraints.ANCHOR_LEFT
        |AnchorConstraints.ANCHOR_BOTTOM|AnchorConstraints.ANCHOR_RIGHT;
    
    Component comp = base.getParent();
    Component child = base;
    while (comp != null && comp != ancestorContainer && contact != 0){
      if (comp instanceof SplitContainer){
        SplitContainer sc = (SplitContainer) comp;
        if (sc.getOrientation() == JSplitPane.VERTICAL_SPLIT){
          if (sc.getTopComponent() == child){
            contact = contact & (~AnchorConstraints.ANCHOR_BOTTOM); // not at bottom
          } else {
            contact = contact & (~AnchorConstraints.ANCHOR_TOP); // not on top
          }
        } else {
          if (sc.getLeftComponent() == child){
            contact = contact & (~AnchorConstraints.ANCHOR_RIGHT); // not right
          } else {
            contact = contact & (~AnchorConstraints.ANCHOR_LEFT); // not left
          }
        }
      }
      child = comp;
      comp = comp.getParent();
    }
    return contact;
  }
  
  /** Tries to find the best position to insert an hidden dockable, and insert it
   * @deprecated  use getInsertionDockingAction / applyDockingAction instead
   */
  public static SingleDockableContainer insertDockable(Container relativeAncestorContainer,
      Dockable dockable, RelativeDockablePosition position) {
    
    if (position == null){
      // for safety
      position = new RelativeDockablePosition(0,0.8,0.5,0.2);
    }
    SingleDockableContainer dockableContainer = null;
    if (relativeAncestorContainer.getComponentCount() == 0){ // empty docking panel      
      dockableContainer = DockableContainerFactory.getFactory().
          createDockableContainer(dockable, DockableContainerFactory.PARENT_DESKTOP);
      
      // default central insertion
      relativeAncestorContainer.add((Component)dockableContainer, BorderLayout.CENTER);
      relativeAncestorContainer.invalidate(); // 2005/05/04
      relativeAncestorContainer.validate();
      relativeAncestorContainer.repaint();
    } else {
      dockableContainer = DockableContainerFactory.getFactory().
          createDockableContainer(dockable, DockableContainerFactory.PARENT_SPLIT_CONTAINER);
      
      // convert relative positionning to current positionning (depending
      // on the current size of dockingPanel)
      int x = (int) (position.getX() * relativeAncestorContainer.getWidth());
      int y = (int) (position.getY() * relativeAncestorContainer.getHeight());
      int w = (int) (position.getWidth() * relativeAncestorContainer.getWidth());
      int h = (int) (position.getHeight() * relativeAncestorContainer.getHeight());
      int centerX = x + w / 2;
      int centerY = y + h / 2;
      
      SplitContainer split = findBestContainer(relativeAncestorContainer, position);
      if (split != null){
        // ok we've found one
        /*Rectangle splitRect = SwingUtilities.convertRectangle(split,
            new Rectangle(0, 0, split.getWidth(), split.getHeight()),
            relativeAncestorContainer);*/
        //int contacts = findAnchors(split, relativeAncestorContainer);
        
        int contacts = position.getAnchors(); // we use the anchors that
        // were computed on closing
        
        
        // heuristics : try to find an anchor, and which component(left/right)
        // is nearer of center
        /*boolean anchoredTop = Math.abs( y - splitRect.y) < 5;
        boolean anchoredLeft = Math.abs(x - splitRect.x) < 5;
        boolean anchoredBottom = Math.abs(y + h - splitRect.y - splitRect.height) < 30;
        boolean anchoredRight = Math.abs(x + w - splitRect.x - splitRect.width) < 5;*/
        
        boolean anchoredTop = (contacts & AnchorConstraints.ANCHOR_TOP)>0;
        boolean anchoredLeft =(contacts & AnchorConstraints.ANCHOR_LEFT)>0;
        boolean anchoredBottom = (contacts & AnchorConstraints.ANCHOR_BOTTOM)>0;
        boolean anchoredRight = (contacts & AnchorConstraints.ANCHOR_RIGHT)>0;
        
//          System.out.println("anchors : " + anchoredTop + ", "+ anchoredLeft + ", "
//              + anchoredBottom + ", " + anchoredRight);
        
        Component left = split.getLeftComponent();
        Component right = split.getRightComponent();
        Point leftCenter = SwingUtilities.convertPoint(left,
            left.getWidth() / 2,
            left.getHeight() / 2, relativeAncestorContainer);
        Point rightCenter = SwingUtilities.convertPoint(right,
            right.getWidth() / 2,
            right.getHeight() / 2, relativeAncestorContainer);
        int leftDist = (leftCenter.x - x) * (leftCenter.x - x)
        + (leftCenter.y - y) * (leftCenter.y);
        int rightDist = (rightCenter.x - x) * (rightCenter.x - x)
        + (rightCenter.y - y) * (rightCenter.y);
        
        Point bestCenter;
        Component bestComp;
        if (leftDist < rightDist) {
          bestCenter = leftCenter;
          bestComp = left;
        } else {
          bestCenter = rightCenter;
          bestComp = right;
        }
        
        
        //2006/09/12
        /* If the split matches the size of the component, then
         * we'll have to split it (and not split one of its children)
         */
        float splitWidthRatio = Math.abs(1 - split.getWidth() / (float)w);
        float splitHeightRatio = Math.abs(1 - split.getHeight() / (float)h);
        
        /** this will happen only when anchors are not too strict (like top+right)
         * for wider anchors (left-top-right) this ration is not used as
         * superflous
         */
        
        
        
        
        
        if (split.getOrientation() == JSplitPane.VERTICAL_SPLIT) {
          
          
          Component top = split.getTopComponent();
          Component bottom = split.getBottomComponent();
          int topH = top.getHeight();
          int bottomH = bottom.getHeight();
          // proportions used when splitting a vertical split vertically
          float proportionTopH = h / (float)topH;
          float proportionBottomH = h / (float)bottomH;
          if (proportionTopH >= 0.6f){ // not too big
            proportionTopH = 0.6f;
          }
          if (proportionBottomH >= 0.6f){ // not too big
            proportionBottomH = 0.6f;
          }
          // proportions used when splitting a vertical split horizontally
          int splitW = split.getWidth();
          float proportionW = w / (float) splitW;
          if (proportionW >= 0.8f){ // not too big
            proportionW = 0.8f;
          }
          
          if (anchoredTop) {
            if (anchoredLeft) {
              if (anchoredBottom) { // TLB = full left
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    HORIZONTAL_SPLIT);
                DockingUtilities.replaceChild(split.getParent(), split,
                    newSplit);
                newSplit.setLeftComponent( (Component) dockableContainer);
                newSplit.setRightComponent(split);
                
                SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionW));
              } else {
                // Top + left
                if (splitWidthRatio < 0.1f && split.getHeight() > h){//2006/09/12
                  // almost the same width, but the split is taller : we split it
                  // vertically
                  SplitContainer newSplit = new SplitContainer(JSplitPane.
                      VERTICAL_SPLIT);
                  DockingUtilities.replaceChild(split.getParent(), split,
                      newSplit);
                  newSplit.setTopComponent( (Component) dockableContainer);
                  newSplit.setBottomComponent(split);
                  SwingUtilities.invokeLater(new SplitResizer(newSplit, h / (float)split.getHeight()));
                } else {
                  SplitContainer newSplit = new SplitContainer(JSplitPane.
                      HORIZONTAL_SPLIT);
                  DockingUtilities.replaceChild(split, top, newSplit);
                  newSplit.setLeftComponent( (Component) dockableContainer);
                  newSplit.setRightComponent(top);
                  SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionW));
                }
              }
            } else if (anchoredRight) { // TR
              if (anchoredBottom) { // TRB = full right
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    HORIZONTAL_SPLIT);
                DockingUtilities.replaceChild(split.getParent(), split,
                    newSplit);
                newSplit.setRightComponent( (Component) dockableContainer);
                newSplit.setLeftComponent(split);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, 1-proportionW));
              } else {
                if (splitWidthRatio < 0.1f && split.getHeight() > h){ // 2006/09/12
                  // almost the same width, but the split is taller : we split it
                  // vertically
                  SplitContainer newSplit = new SplitContainer(JSplitPane.
                      VERTICAL_SPLIT);
                  DockingUtilities.replaceChild(split.getParent(), split,
                      newSplit);
                  newSplit.setTopComponent( (Component) dockableContainer);
                  newSplit.setBottomComponent(split);
                  SwingUtilities.invokeLater(new SplitResizer(newSplit, h / (float)split.getHeight()));
                } else {
                  SplitContainer newSplit = new SplitContainer(JSplitPane.
                      HORIZONTAL_SPLIT);
                  DockingUtilities.replaceChild(split, top, newSplit);
                  newSplit.setLeftComponent(top);
                  newSplit.setRightComponent( (Component) dockableContainer);
                  SwingUtilities.invokeLater(new SplitResizer(newSplit, 1-proportionW));
                }
              }
            } else {
              // just anchored top
              SplitContainer newSplit = new SplitContainer(JSplitPane.
                  VERTICAL_SPLIT);
              DockingUtilities.replaceChild(split, top, newSplit);
              newSplit.setTopComponent( (Component) dockableContainer);
              newSplit.setBottomComponent(top);
              SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionTopH));
            }
          } else if (anchoredBottom) { // but not anchoredTop
            if (anchoredLeft) { //BL
              if (anchoredRight) { //BLR = full bottom
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    VERTICAL_SPLIT);
                DockingUtilities.replaceChild(split, bottom, newSplit);
                newSplit.setTopComponent(bottom);
                newSplit.setBottomComponent( (Component) dockableContainer);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, 1-proportionBottomH));
              } else {
                if (splitWidthRatio < 0.1f && split.getHeight() > h){ // 2006/09/12
                  // almost the same width, but the split is taller : we split it
                  // vertically
                  SplitContainer newSplit = new SplitContainer(JSplitPane.
                      VERTICAL_SPLIT);
                  DockingUtilities.replaceChild(split.getParent(), split,
                      newSplit);
                  newSplit.setBottomComponent( (Component) dockableContainer);
                  newSplit.setTopComponent(split);
                  SwingUtilities.invokeLater(new SplitResizer(newSplit, 1- h / (float)split.getHeight()));
                } else {
                  SplitContainer newSplit = new SplitContainer(JSplitPane.
                      HORIZONTAL_SPLIT);
                  DockingUtilities.replaceChild(split, bottom, newSplit);
                  newSplit.setLeftComponent( (Component) dockableContainer);
                  newSplit.setRightComponent(bottom);
                  SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionW));
                }
              }
            } else if (anchoredRight) { // BR
              if (splitWidthRatio < 0.1f && split.getHeight() > h){ // 2006/09/12
                // almost the same width, but the split is taller : we split it
                // vertically
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    VERTICAL_SPLIT);
                DockingUtilities.replaceChild(split.getParent(), split,
                    newSplit);
                newSplit.setBottomComponent( (Component) dockableContainer);
                newSplit.setTopComponent(split);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, 1 - h / (float)split.getHeight()));
              } else {
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    HORIZONTAL_SPLIT);
                DockingUtilities.replaceChild(split, bottom, newSplit);
                newSplit.setLeftComponent(bottom);
                newSplit.setRightComponent( (Component) dockableContainer);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, 1-proportionW));
              }
            } else {
              // just anchored bottom
              SplitContainer newSplit = new SplitContainer(JSplitPane.
                  VERTICAL_SPLIT);
              DockingUtilities.replaceChild(split, bottom, newSplit);
              newSplit.setTopComponent(bottom);
              newSplit.setBottomComponent( (Component) dockableContainer);
              SwingUtilities.invokeLater(new SplitResizer(newSplit, 1-proportionBottomH));
            }
          } else if (anchoredLeft) { // not anchored on top / bottom
            if (anchoredRight){ //2005/10/10
              // left + right on a vertical split, means we have to insert a new component
              // in between
              int yTop = y;
              int yBottom = split.getHeight() - y - h;
              int splitTop = top.getHeight();
              int splitBottom = bottom.getHeight();
              
              SplitContainer newSplit = new SplitContainer(JSplitPane.
                  VERTICAL_SPLIT);
              DockingUtilities.replaceChild(split, bestComp, newSplit);
              
              if (bestComp == top){
                newSplit.setTopComponent(bestComp);
                newSplit.setBottomComponent((Component) dockableContainer);
                SwingUtilities.invokeLater(new SplitResizer(split,
                    (split.getHeight() - yBottom)/(float) split.getHeight()));
                SwingUtilities.invokeLater(new SplitResizer(newSplit,
                    yTop/(float) split.getHeight()));
              } else {
                newSplit.setBottomComponent(bestComp);
                newSplit.setTopComponent((Component) dockableContainer);
                SwingUtilities.invokeLater(new SplitResizer(split, yTop/(float) split.getHeight()));
                SwingUtilities.invokeLater(new SplitResizer(newSplit, h/(float) splitBottom));
              }
              
            } else {// only left
              SplitContainer newSplit = new SplitContainer(JSplitPane.
                  HORIZONTAL_SPLIT);
              DockingUtilities.replaceChild(split, bestComp, newSplit);
              newSplit.setRightComponent(bestComp);
              newSplit.setLeftComponent( (Component) dockableContainer);
              SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionW));
            }
          } else if (anchoredRight) {
            SplitContainer newSplit = new SplitContainer(JSplitPane.
                HORIZONTAL_SPLIT);
            DockingUtilities.replaceChild(split, bestComp, newSplit);
            newSplit.setLeftComponent(bestComp);
            newSplit.setRightComponent( (Component) dockableContainer);
            SwingUtilities.invokeLater(new SplitResizer(newSplit, 1-proportionW));
          } else { // not anchored at all, split verticaly and add
            // todo : check how it's done now for the split-h part and do the same here
            SplitContainer newSplit = new SplitContainer(JSplitPane.
                VERTICAL_SPLIT);
            DockingUtilities.replaceChild(split, bestComp, newSplit);
            if (bestCenter.y < centerY) {
              newSplit.setTopComponent(bestComp);
              newSplit.setBottomComponent( (Component) dockableContainer);
              SwingUtilities.invokeLater(new SplitResizer(newSplit, 1-proportionBottomH));
            } else {
              newSplit.setBottomComponent(bestComp);
              newSplit.setTopComponent( (Component) dockableContainer);
              SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionTopH));
            }
          }
        } else {
          
          
          // horizontal split
          // proportions used when splitting a horizontal split horizontally
          float proportionLeftW = w / (float)left.getWidth();
          float proportionRightW = w / (float)right.getWidth();
          if (proportionLeftW >= 0.8f){ // not too big
            proportionLeftW = 0.8f;
          }
          if (proportionRightW >= 0.8f){ // not too big
            proportionRightW = 0.8f;
          }
          // proportions used when splitting a horizontal split vertically
          int splitH = split.getHeight();
          float proportionH = h / (float) splitH;
          if (proportionH >= 0.6f){ // not too big
            proportionH = 0.6f;
          }
          
          
          
          if (anchoredTop) {
            if (anchoredLeft) {
              if (anchoredBottom) { // TLB = full left
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    HORIZONTAL_SPLIT);
                DockingUtilities.replaceChild(split, left,
                    newSplit);
                newSplit.setLeftComponent( (Component) dockableContainer);
                newSplit.setRightComponent(left);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionLeftW));
              } else if (anchoredRight){ // TLR = full top
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    VERTICAL_SPLIT);
                DockingUtilities.replaceChild(split.getParent(), split, newSplit);
                newSplit.setTopComponent( (Component) dockableContainer);
                newSplit.setBottomComponent(split);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionH));
              } else { // just anchored top/left
                if (splitWidthRatio < 0.1f && split.getHeight() > h){ // 2006/09/12
                  // almost the same width, but the split is taller : we split it
                  // vertically
                  SplitContainer newSplit = new SplitContainer(JSplitPane.
                      VERTICAL_SPLIT);
                  DockingUtilities.replaceChild(split.getParent(), split,
                      newSplit);
                  newSplit.setTopComponent( (Component) dockableContainer);
                  newSplit.setBottomComponent(split);
                  SwingUtilities.invokeLater(new SplitResizer(newSplit, h / (float)split.getHeight()));
                } else {
                  // otherwise we just split vertically on the left side
                  SplitContainer newSplit = new SplitContainer(JSplitPane.
                      VERTICAL_SPLIT);
                  DockingUtilities.replaceChild(split, left,
                      newSplit);
                  newSplit.setTopComponent( (Component) dockableContainer);
                  newSplit.setBottomComponent(left);
                  SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionH));
                }
              }
            } else if (anchoredRight) { // Top but not left
              if (anchoredBottom) { // top + right + bottom == full right
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    HORIZONTAL_SPLIT);
                DockingUtilities.replaceChild(split, right,
                    newSplit);
                newSplit.setRightComponent( (Component) dockableContainer);
                newSplit.setLeftComponent(right);
                
                SwingUtilities.invokeLater(new SplitResizer(newSplit, 1-proportionRightW));
                
              } else { // top + right
                if (splitWidthRatio < 0.1f && split.getHeight() > h){ // 2006/09/12
                  // almost the same width, but the split is taller : we split it
                  // vertically
                  SplitContainer newSplit = new SplitContainer(JSplitPane.
                      VERTICAL_SPLIT);
                  DockingUtilities.replaceChild(split.getParent(), split,
                      newSplit);
                  newSplit.setTopComponent( (Component) dockableContainer);
                  newSplit.setBottomComponent(split);
                  SwingUtilities.invokeLater(new SplitResizer(newSplit, h / (float)split.getHeight()));
                } else {
                  SplitContainer newSplit = new SplitContainer(JSplitPane.
                      VERTICAL_SPLIT);
                  DockingUtilities.replaceChild(split, right, newSplit);
                  newSplit.setBottomComponent(right);
                  newSplit.setTopComponent( (Component) dockableContainer);
                  SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionH));
                }
              }
            } else if (anchoredBottom){ //2005/10/10
              // top + bottom : create a horizontal split
              // as we are inserting a vertical element into a horizontal split
              // we have to adjust the width on both sides
              int xLeft = x;
              int xRight = split.getWidth() - x - w;
              int splitLeft = left.getWidth();
              int splitRight = right.getWidth();
              
              SplitContainer newSplit = new SplitContainer(JSplitPane.
                  HORIZONTAL_SPLIT);
              DockingUtilities.replaceChild(split, bestComp, newSplit);
              
              if (bestComp == left){
                newSplit.setLeftComponent(bestComp);
                newSplit.setRightComponent((Component) dockableContainer);
                SwingUtilities.invokeLater(new SplitResizer(split, (split.getWidth() - xRight)/(float) split.getWidth()));
                SwingUtilities.invokeLater(new SplitResizer(newSplit, xLeft/(float) split.getWidth()));
              } else {
                newSplit.setRightComponent(bestComp);
                newSplit.setLeftComponent((Component) dockableContainer);
                SwingUtilities.invokeLater(new SplitResizer(split, xLeft/(float) split.getWidth()));
                SwingUtilities.invokeLater(new SplitResizer(newSplit, w/(float) splitRight));
              }
            } else {
              // just anchored top
              if (splitWidthRatio < 0.1f && split.getHeight() > h){ // 2006/09/12
                // almost the same width, but the split is taller : we split it
                // vertically
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    VERTICAL_SPLIT);
                DockingUtilities.replaceChild(split.getParent(), split,
                    newSplit);
                newSplit.setTopComponent( (Component) dockableContainer);
                newSplit.setBottomComponent(split);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, h / (float)split.getHeight()));
              } else {
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    VERTICAL_SPLIT);
                DockingUtilities.replaceChild(split, bestComp, newSplit);
                newSplit.setTopComponent( (Component) dockableContainer);
                newSplit.setBottomComponent(bestComp);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionH));
              }
            }
          } else if (anchoredBottom) { // but not anchoredTop
            if (anchoredLeft) {
              if (anchoredRight) { // BLR == full bottom
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    VERTICAL_SPLIT);
                DockingUtilities.replaceChild(split.getParent(), split, newSplit);
                newSplit.setTopComponent(split);
                newSplit.setBottomComponent( (Component) dockableContainer);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, 1-proportionH));
              } else { // bottom + left
                if (splitWidthRatio < 0.1f && split.getHeight() > h){ // 2006/09/12
                  // almost the same width, but the split is taller : we split it
                  // vertically
                  SplitContainer newSplit = new SplitContainer(JSplitPane.
                      VERTICAL_SPLIT);
                  DockingUtilities.replaceChild(split.getParent(), split,
                      newSplit);
                  newSplit.setBottomComponent( (Component) dockableContainer);
                  newSplit.setTopComponent(split);
                  SwingUtilities.invokeLater(new SplitResizer(newSplit, 1 - h / (float)split.getHeight()));
                } else {
                  SplitContainer newSplit = new SplitContainer(JSplitPane.
                      VERTICAL_SPLIT);
                  DockingUtilities.replaceChild(split, left, newSplit);
                  newSplit.setBottomComponent( (Component) dockableContainer);
                  newSplit.setTopComponent(left);
                  SwingUtilities.invokeLater(new SplitResizer(newSplit, 1-proportionH));
                }
              }
            } else if (anchoredRight) { // bottom + right
              if (splitWidthRatio < 0.1f && split.getHeight() > h){ // 2006/09/12
                // almost the same width, but the split is taller : we split it
                // vertically
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    VERTICAL_SPLIT);
                DockingUtilities.replaceChild(split.getParent(), split,
                    newSplit);
                newSplit.setBottomComponent( (Component) dockableContainer);
                newSplit.setTopComponent(split);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, 1- h / (float)split.getHeight()));
              } else {
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    VERTICAL_SPLIT);
                DockingUtilities.replaceChild(split, right, newSplit);
                newSplit.setTopComponent(right);
                newSplit.setBottomComponent( (Component) dockableContainer);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, 1-proportionH));
              }
            } else {
              // just anchored bottom
              if (splitWidthRatio < 0.1f && split.getHeight() > h){ // 2006/09/12
                // almost the same width, but the split is taller : we split it
                // vertically
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    VERTICAL_SPLIT);
                DockingUtilities.replaceChild(split.getParent(), split,
                    newSplit);
                newSplit.setBottomComponent( (Component) dockableContainer);
                newSplit.setTopComponent(split);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, 1- h / (float)split.getHeight()));
              } else {
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    VERTICAL_SPLIT);
                DockingUtilities.replaceChild(split, bestComp, newSplit);
                newSplit.setTopComponent(bestComp);
                newSplit.setBottomComponent( (Component) dockableContainer);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, 1-proportionH));
              }
            }
          } else if (anchoredLeft) { // not anchored on top / bottom
            if (anchoredRight){
              // left + right... we have to split vertically
              SplitContainer newSplit = new SplitContainer(JSplitPane.
                  VERTICAL_SPLIT);
              DockingUtilities.replaceChild(split.getParent(), split,
                  newSplit);
              newSplit.setTopComponent( (Component) dockableContainer);
              newSplit.setBottomComponent(split); // todo : check this case :  why is the new component on top
              SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionH));
            } else {
              // just left
              if (splitWidthRatio < 0.1f && split.getHeight() > h){ // 2006/09/12
                // almost the same width, but the split is taller : we split it
                // vertically
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    VERTICAL_SPLIT);
                DockingUtilities.replaceChild(split.getParent(), split,
                    newSplit);
                newSplit.setTopComponent( (Component) dockableContainer);
                newSplit.setBottomComponent(split);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, h / (float)split.getHeight()));
              } else {
                SplitContainer newSplit = new SplitContainer(JSplitPane.
                    HORIZONTAL_SPLIT);
                DockingUtilities.replaceChild(split, left, newSplit);
                newSplit.setRightComponent(left);
                newSplit.setLeftComponent( (Component) dockableContainer);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionLeftW));
              }
            }
          } else if (anchoredRight) {
            if (splitWidthRatio < 0.1f && split.getHeight() > h){ // 2006/09/12
              // almost the same width, but the split is taller : we split it
              // vertically
              SplitContainer newSplit = new SplitContainer(JSplitPane.
                  VERTICAL_SPLIT);
              DockingUtilities.replaceChild(split.getParent(), split,
                  newSplit);
              newSplit.setTopComponent( (Component) dockableContainer);
              newSplit.setBottomComponent(split);
              SwingUtilities.invokeLater(new SplitResizer(newSplit, h / (float)split.getHeight()));
            } else {              
              SplitContainer newSplit = new SplitContainer(JSplitPane.
                  HORIZONTAL_SPLIT);
              DockingUtilities.replaceChild(split, right, newSplit);
              newSplit.setLeftComponent(right);
              newSplit.setRightComponent( (Component) dockableContainer);
              SwingUtilities.invokeLater(new SplitResizer(newSplit, 1-proportionRightW));
            }
          } else { // not anchored at all
            // this part can be improved :
            //  - we know we're into an horizontal split
            //  - we can guess if the component was horizontaly splitted or vertically,
            //    and on which side of the split if was (bestComp)
            
            
            float widthIfSplitH = bestComp.getWidth()/2;
            float heightIfSplitH = bestComp.getHeight();
            float widthIfSplitV = bestComp.getWidth();
            float heightIfSplitV = bestComp.getHeight()/2;
            // now which case keeps the best proportions ?
            float whH = widthIfSplitH / heightIfSplitH;
            float whV = widthIfSplitV / heightIfSplitV;
            float whBefore = w / (float)h;
            if (Math.abs(whBefore - whV) < Math.abs(whBefore - whH)){
              // nearer of V
              SplitContainer newSplit = new SplitContainer(JSplitPane.
                  VERTICAL_SPLIT);
              DockingUtilities.replaceChild(split, bestComp, newSplit);
              if (bestCenter.y < centerY) {
                newSplit.setTopComponent(bestComp);
                newSplit.setBottomComponent( (Component) dockableContainer);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, 1-proportionH));
              } else {
                newSplit.setBottomComponent(bestComp);
                newSplit.setTopComponent( (Component) dockableContainer);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionH));
              }
            } else {
              SplitContainer newSplit = new SplitContainer(JSplitPane.
                  HORIZONTAL_SPLIT);
              DockingUtilities.replaceChild(split, bestComp, newSplit);
              if (bestCenter.x < centerX) {
                newSplit.setLeftComponent(bestComp);
                newSplit.setRightComponent( (Component) dockableContainer);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionRightW));
              } else {
                newSplit.setRightComponent(bestComp);
                newSplit.setLeftComponent( (Component) dockableContainer);
                SwingUtilities.invokeLater(new SplitResizer(newSplit, proportionLeftW));
              }
            }
            
          }
        }
      } else { // 2004/04/01 (this part was missing) ------------------------------
        // one component, but not a split container
        // heuristics : try to find an anchor
        boolean anchoredTop = y < 5;
        boolean anchoredLeft = x < 5;
        boolean anchoredBottom = Math.abs(y + h - relativeAncestorContainer.getHeight()) < 5;
        boolean anchoredRight = Math.abs(x + w - relativeAncestorContainer.getWidth()) < 5;
        Component singleComp = relativeAncestorContainer.getComponent(0);
        
        float proportionW = w / (float) relativeAncestorContainer.getWidth();
        if (proportionW >= 0.8f){ // not too big
          proportionW = 0.8f;
        }
        float proportionH = h / (float) relativeAncestorContainer.getHeight();
        if (proportionH >= 0.6f){ // not too big
          proportionH = 0.6f;
        }
        
        
        if (anchoredTop){
          if (anchoredLeft){
            if (anchoredBottom){
              hSplitAndResize(singleComp, (Component) dockableContainer, singleComp, proportionW);
            } else if (anchoredRight){ // top + left + right == TOP
              vSplitAndResize(singleComp, (Component) dockableContainer, singleComp, proportionH);
            } else { // top + left... what should we do ?
              if (w > h){
                vSplitAndResize(singleComp, (Component) dockableContainer, singleComp, proportionH);
              } else {
                hSplitAndResize(singleComp, (Component) dockableContainer, singleComp, proportionW);
              }
            }
          } else if (anchoredRight){ // top + right (not left)
            if (anchoredBottom){ // top + right + bottom == on the right
              hSplitAndResize(singleComp, singleComp, (Component) dockableContainer, 1-proportionW);
            } else { // top + right
              if (w > h){ // on top
                vSplitAndResize(singleComp, (Component) dockableContainer, singleComp, proportionH);
              } else { // on the right
                hSplitAndResize(singleComp, singleComp, (Component) dockableContainer, 1-proportionW);
              }
            }
          } else { // top only
            vSplitAndResize(singleComp, (Component) dockableContainer, singleComp, proportionH);
          }
        } else if (anchoredLeft){ //left (but not top)
          if (anchoredBottom){ // left + bot
            if (anchoredRight){ // == bottom
              vSplitAndResize(singleComp, singleComp, (Component) dockableContainer, 1-proportionH);
            } else { // left + bottom
              if (w > h){ // bottom
                vSplitAndResize(singleComp, singleComp, (Component) dockableContainer, 1-proportionH);
              } else { // on the left
                hSplitAndResize(singleComp, (Component) dockableContainer, singleComp, proportionW);
              }
            }
          } else if (anchoredRight){ // left + right, but not top/bottom...
            if (centerY < relativeAncestorContainer.getHeight()/2){ // center is upper part
              vSplitAndResize(singleComp, (Component) dockableContainer, singleComp, proportionH);
            } else { // lower part
              vSplitAndResize(singleComp, singleComp, (Component) dockableContainer, 1-proportionH);
            }
          } else { // left only
            hSplitAndResize(singleComp, (Component) dockableContainer, singleComp, proportionW);
          }
        } else if (anchoredBottom){ // bottom, but not top/left
          if (anchoredRight){
            if (w > h){ // bottom
              vSplitAndResize(singleComp, singleComp, (Component) dockableContainer, 1-proportionH);
            } else { // on the right
              hSplitAndResize(singleComp, singleComp, (Component) dockableContainer, 1-proportionW);
            }
          } else { // just bottom
            vSplitAndResize(singleComp, singleComp, (Component) dockableContainer, 1-proportionH);
          }
        } else if (anchoredRight){ // just right
          hSplitAndResize(singleComp, singleComp, (Component) dockableContainer, 1-proportionW);
        } else { // no anchors
          if (centerY < relativeAncestorContainer.getHeight()/2){ // center is upper part
            vSplitAndResize(singleComp, (Component) dockableContainer, singleComp, proportionH);
          } else {
            vSplitAndResize(singleComp, singleComp, (Component) dockableContainer, 1-proportionH);
          }
          
        }
      } // 2004/04/01 (end of missing part) ------------------------------
    }
    return dockableContainer;
    
    
    //DockingActionEvent action = getInsertionDockingAction(relativeAncestorContainer, dockable, position);
    //return applyDockingAction(dockable, action);
    
    
    
  }
  
  /** returns a docking action event corresponding to the insertion point and method
   * of the dockable.
   */
  public static DockingActionEvent getInsertionDockingAction(Container relativeAncestorContainer,
      Dockable dockable, DockableState dockableState, DockableState newState){
    RelativeDockablePosition position = dockableState.getPosition();
    DockingDesktop desktop = dockableState.getDesktop();
    int initialState = dockableState.getState();
    int nextState = newState.getState();
    
    if (position == null){
      // for safety
      position = new RelativeDockablePosition(0,0.8,0.5,0.2);
    }
    
    SingleDockableContainer dockableContainer = null;
    if (relativeAncestorContainer.getComponentCount() == 0){ // empty docking panel
      return new DockingActionAddDockableEvent(desktop, dockable,
          initialState, nextState, relativeAncestorContainer);
    } else {
      // convert relative positionning to current positionning (depending
      // on the current size of dockingPanel)
      int x = (int) (position.getX() * relativeAncestorContainer.getWidth());
      int y = (int) (position.getY() * relativeAncestorContainer.getHeight());
      int w = (int) (position.getWidth() * relativeAncestorContainer.getWidth());
      int h = (int) (position.getHeight() * relativeAncestorContainer.getHeight());
      int centerX = x + w / 2;
      int centerY = y + h / 2;
      
      SplitContainer split = findBestContainer(relativeAncestorContainer, position);
      if (split != null){
        // ok we've found one
        Rectangle splitRect = SwingUtilities.convertRectangle(split,
            new Rectangle(0, 0, split.getWidth(), split.getHeight()),
            relativeAncestorContainer);
        
        // heuristics : try to find an anchor, and which component(left/right)
        // is nearer of center
        boolean anchoredTop = Math.abs( y - splitRect.y) < 5;
        boolean anchoredLeft = Math.abs(x - splitRect.x) < 5;
        boolean anchoredBottom = Math.abs(y + h - splitRect.y - splitRect.height) < 30/*5*/;
        boolean anchoredRight = Math.abs(x + w - splitRect.x - splitRect.width) < 5;
        
        
        Component left = split.getLeftComponent();
        Component right = split.getRightComponent();
        Point leftCenter = SwingUtilities.convertPoint(left,
            left.getWidth() / 2,
            left.getHeight() / 2, relativeAncestorContainer);
        Point rightCenter = SwingUtilities.convertPoint(right,
            right.getWidth() / 2,
            right.getHeight() / 2, relativeAncestorContainer);
        int leftDist = (leftCenter.x - x) * (leftCenter.x - x)
        + (leftCenter.y - y) * (leftCenter.y);
        int rightDist = (rightCenter.x - x) * (rightCenter.x - x)
        + (rightCenter.y - y) * (rightCenter.y);
        
        Point bestCenter;
        Component bestComp;
        if (leftDist < rightDist) {
          bestCenter = leftCenter;
          bestComp = left;
        } else {
          bestCenter = rightCenter;
          bestComp = right;
        }
        
        
        if (split.getOrientation() == JSplitPane.VERTICAL_SPLIT) {
          
          Component top = split.getTopComponent();
          Component bottom = split.getBottomComponent();
          int topH = top.getHeight();
          int bottomH = bottom.getHeight();
          // proportions used when splitting a vertical split vertically
          float proportionTopH = h / (float)topH;
          float proportionBottomH = h / (float)bottomH;
          if (proportionTopH >= 0.6f){ // not too big
            proportionTopH = 0.6f;
          }
          if (proportionBottomH >= 0.6f){ // not too big
            proportionBottomH = 0.6f;
          }
          // proportions used when splitting a vertical split horizontally
          int splitW = split.getWidth();
          float proportionW = w / (float) splitW;
          if (proportionW >= 0.8f){ // not too big
            proportionW = 0.8f;
          }
          
          if (anchoredTop) {
            if (anchoredLeft) {
              if (anchoredBottom) {
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, split, DockingConstants.SPLIT_LEFT, proportionW);
              } else {
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, top, DockingConstants.SPLIT_LEFT, proportionW);
              }
            } else if (anchoredRight) {
              if (anchoredBottom) {
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, split, DockingConstants.SPLIT_RIGHT, 1-proportionW);
              } else {
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, top, DockingConstants.SPLIT_RIGHT, 1-proportionW);
              }
            } else {
              // just anchored top
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, top, DockingConstants.SPLIT_TOP, proportionTopH);
            }
          } else if (anchoredBottom) { // but not anchoredTop
            if (anchoredLeft) {
              if (anchoredRight) {
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, bottom, DockingConstants.SPLIT_BOTTOM, 1-proportionBottomH);
              } else {
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, bottom, DockingConstants.SPLIT_LEFT, proportionW);
              }
            } else if (anchoredRight) {
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, bottom, DockingConstants.SPLIT_RIGHT, 1-proportionW);
            } else {
              // just anchored bottom
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, bottom, DockingConstants.SPLIT_BOTTOM, 1-proportionBottomH);
            }
          } else if (anchoredLeft) { // not anchored on top / bottom
            if (anchoredRight){ //2005/10/10
              // left + right on a vertical split, means we have to insert a new component
              // in between
              int yTop = y;
              int yBottom = split.getHeight() - y - h;
              int splitTop = top.getHeight();
              int splitBottom = bottom.getHeight();
              
              /*SplitContainer newSplit = new SplitContainer(JSplitPane.
                  VERTICAL_SPLIT);
              DockingUtilities.replaceChild(split, bestComp, newSplit);*/
              
              if (bestComp == top){
                float hParent =( split.getHeight() - yBottom)/(float) split.getHeight() ;
                float hChild = yTop/(float) split.getHeight();
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, bestComp, DockingConstants.SPLIT_BOTTOM, hChild, hParent);
              } else {
                float hChild =  h/(float) splitBottom;
                float hParent = yTop/(float) split.getHeight();
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, bestComp, DockingConstants.SPLIT_TOP, hChild, hParent);
              }
            } else {// only left
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, bestComp, DockingConstants.SPLIT_LEFT, proportionW);
            }
          } else if (anchoredRight) {
            return new DockingActionSplitComponentEvent(desktop, dockable,
                initialState, nextState, bestComp, DockingConstants.SPLIT_RIGHT, 1-proportionW);
          } else { // not anchored at all, split verticaly and add
            
            SplitContainer newSplit = new SplitContainer(JSplitPane.
                VERTICAL_SPLIT);
            DockingUtilities.replaceChild(split, bestComp, newSplit);
            if (bestCenter.y < centerY) {
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, bestComp, DockingConstants.SPLIT_BOTTOM, 1-proportionBottomH);
            } else {
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, bestComp, DockingConstants.SPLIT_TOP, proportionTopH);
            }
          }
        } else {
          
          
          // horizontal split
          // proportions used when splitting a horizontal split horizontally
          float proportionLeftW = w / (float)left.getWidth();
          float proportionRightW = w / (float)right.getWidth();
          if (proportionLeftW >= 0.8f){ // not too big
            proportionLeftW = 0.8f;
          }
          if (proportionRightW >= 0.8f){ // not too big
            proportionRightW = 0.8f;
          }
          // proportions used when splitting a horizontal split vertically
          int splitH = split.getHeight();
          float proportionH = h / (float) splitH;
          if (proportionH >= 0.6f){ // not too big
            proportionH = 0.6f;
          }
          
          if (anchoredTop) {
            if (anchoredLeft) {
              if (anchoredBottom) {
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, left, DockingConstants.SPLIT_LEFT, proportionLeftW);
              } else if (anchoredRight){
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, split, DockingConstants.SPLIT_TOP, proportionH);
              } else { // just anchored top/left
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, left, DockingConstants.SPLIT_TOP, proportionH);
              }
            } else if (anchoredRight) {
              if (anchoredBottom) { // top + right + bottom
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, right, DockingConstants.SPLIT_RIGHT, 1-proportionRightW);
              } else { // top + right
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, right, DockingConstants.SPLIT_TOP, proportionH);
              }
            } else if (anchoredBottom){ //2005/10/10
              // top + bottom : create a horizontal split
              // as we are inserting a vertical element into a horizontal split
              // we have to adjust the width on both sides
              int xLeft = x;
              int xRight = split.getWidth() - x - w;
              int splitLeft = left.getWidth();
              int splitRight = right.getWidth();
              
              /*SplitContainer newSplit = new SplitContainer(JSplitPane.
                  HORIZONTAL_SPLIT);
              DockingUtilities.replaceChild(split, bestComp, newSplit);*/
              
              if (bestComp == left){
                float parentW = (split.getWidth() - xRight)/(float) split.getWidth();
                float childW = xLeft/(float) split.getWidth();
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, bestComp, DockingConstants.SPLIT_RIGHT, childW, parentW);
              } else {
                float parentW = xLeft/(float) split.getWidth();
                float childW =  w/(float) splitRight;
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, bestComp, DockingConstants.SPLIT_LEFT, childW, parentW);
              }
            } else {
              // just anchored top
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, bestComp, DockingConstants.SPLIT_TOP, proportionH);
            }
          } else if (anchoredBottom) { // but not anchoredTop
            if (anchoredLeft) {
              if (anchoredRight) {
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, split, DockingConstants.SPLIT_BOTTOM, 1-proportionH);
                
              } else { // bottom + left
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, left, DockingConstants.SPLIT_BOTTOM, 1-proportionH);
              }
            } else if (anchoredRight) { // bottom + right
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, right, DockingConstants.SPLIT_BOTTOM, 1-proportionH);
            } else {
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, bestComp, DockingConstants.SPLIT_BOTTOM, 1-proportionH);
            }
          } else if (anchoredLeft) { // not anchored on top / bottom
            return new DockingActionSplitComponentEvent(desktop, dockable,
                initialState, nextState, left, DockingConstants.SPLIT_LEFT, proportionLeftW);
          } else if (anchoredRight) {
            return new DockingActionSplitComponentEvent(desktop, dockable,
                initialState, nextState, right, DockingConstants.SPLIT_RIGHT, 1-proportionRightW);
          } else { // not anchored at all, split verticaly and add
            /*SplitContainer newSplit = new SplitContainer(JSplitPane.
                VERTICAL_SPLIT);
            DockingUtilities.replaceChild(split, bestComp, newSplit);*/
            if (bestCenter.y < centerY) {
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, bestComp, DockingConstants.SPLIT_BOTTOM, 1-proportionH);
            } else {
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, bestComp, DockingConstants.SPLIT_TOP, proportionH);
            }
          }
        }
      } else { // 2004/04/01 (this part was missing) ------------------------------
        // one component, but not a split container
        // heuristics : try to find an anchor
        boolean anchoredTop = y < 5;
        boolean anchoredLeft = x < 5;
        boolean anchoredBottom = Math.abs(y + h - relativeAncestorContainer.getHeight()) < 5;
        boolean anchoredRight = Math.abs(x + w - relativeAncestorContainer.getWidth()) < 5;
        Component singleComp = relativeAncestorContainer.getComponent(0);
        
        float proportionW = w / (float) relativeAncestorContainer.getWidth();
        if (proportionW >= 0.8f){ // not too big
          proportionW = 0.8f;
        }
        float proportionH = h / (float) relativeAncestorContainer.getHeight();
        if (proportionH >= 0.6f){ // not too big
          proportionH = 0.6f;
        }
        
        
        if (anchoredTop){
          if (anchoredLeft){
            if (anchoredBottom){
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, singleComp, DockingConstants.SPLIT_LEFT, proportionW);
            } else if (anchoredRight){ // top + left + right == TOP
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, singleComp, DockingConstants.SPLIT_TOP, proportionH);
            } else { // top + left... what should we do ?
              if (w > h){ // vsplit
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, singleComp, DockingConstants.SPLIT_TOP, proportionH);
              } else { // hsplit
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, singleComp, DockingConstants.SPLIT_LEFT, proportionW);
              }
            }
          } else if (anchoredRight){ // top + right (not left)
            if (anchoredBottom){ // top + right + bottom == on the right
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, singleComp, DockingConstants.SPLIT_RIGHT, 1-proportionW);
            } else { // top + right
              if (w > h){ // on top
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, singleComp, DockingConstants.SPLIT_TOP, proportionH);
              } else { // on the right
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, singleComp, DockingConstants.SPLIT_RIGHT, 1-proportionW);
              }
            }
          } else { // top only
            return new DockingActionSplitComponentEvent(desktop, dockable,
                initialState, nextState, singleComp, DockingConstants.SPLIT_TOP, proportionH);
          }
        } else if (anchoredLeft){ //left (but not top)
          if (anchoredBottom){ // left + bot
            if (anchoredRight){ // == bottom
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, singleComp, DockingConstants.SPLIT_BOTTOM, 1-proportionH);
            } else { // left + bottom
              if (w > h){ // bottom
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, singleComp, DockingConstants.SPLIT_BOTTOM, 1-proportionH);
              } else { // on the left
                return new DockingActionSplitComponentEvent(desktop, dockable,
                    initialState, nextState, singleComp, DockingConstants.SPLIT_LEFT, proportionW);
              }
            }
          } else if (anchoredRight){ // left + right, but not top/bottom...
            if (centerY < relativeAncestorContainer.getHeight()/2){ // center is upper part
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, singleComp, DockingConstants.SPLIT_TOP, proportionH);
            } else { // lower part
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, singleComp, DockingConstants.SPLIT_BOTTOM, 1-proportionH);
            }
          } else { // left only
            return new DockingActionSplitComponentEvent(desktop, dockable,
                initialState, nextState, singleComp, DockingConstants.SPLIT_LEFT, proportionW);
          }
        } else if (anchoredBottom){ // bottom, but not top/left
          if (anchoredRight){
            if (w > h){ // bottom
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, singleComp, DockingConstants.SPLIT_BOTTOM, 1-proportionH);
            } else { // on the right
              return new DockingActionSplitComponentEvent(desktop, dockable,
                  initialState, nextState, singleComp, DockingConstants.SPLIT_RIGHT, 1-proportionW);
            }
          } else { // just bottom
            return new DockingActionSplitComponentEvent(desktop, dockable,
                initialState, nextState, singleComp, DockingConstants.SPLIT_BOTTOM, 1-proportionH);
          }
        } else if (anchoredRight){ // just right
          return new DockingActionSplitComponentEvent(desktop, dockable,
              initialState, nextState, singleComp, DockingConstants.SPLIT_RIGHT, 1-proportionW);
        } else { // no anchors
          if (centerY < relativeAncestorContainer.getHeight()/2){ // center is upper part
            return new DockingActionSplitComponentEvent(desktop, dockable,
                initialState, nextState, singleComp, DockingConstants.SPLIT_TOP, proportionH);
          } else {
            return new DockingActionSplitComponentEvent(desktop, dockable,
                initialState, nextState, singleComp, DockingConstants.SPLIT_BOTTOM, 1-proportionH);
          }
        }
      }
    }
  }
  
  public static SingleDockableContainer applyDockingAction(Dockable dockable, DockingActionEvent action){
    SingleDockableContainer dockableContainer;
    switch (action.getActionType()){
      case DockingActionEvent.ACTION_ADD_DOCKABLE:
        DockingActionAddDockableEvent addAction = (DockingActionAddDockableEvent) action;
        dockableContainer = DockableContainerFactory.getFactory().
            createDockableContainer(dockable, DockableContainerFactory.PARENT_DESKTOP);
        Container relativeAncestorContainer = addAction.getParentContainer();
        // default central insertion
        relativeAncestorContainer.add((Component)dockableContainer, BorderLayout.CENTER);
        relativeAncestorContainer.invalidate(); // 2005/05/04
        relativeAncestorContainer.validate();
        relativeAncestorContainer.repaint();
        return dockableContainer;
      case DockingActionEvent.ACTION_SPLIT_COMPONENT:
        dockableContainer = DockableContainerFactory.getFactory().
            createDockableContainer(dockable, DockableContainerFactory.PARENT_SPLIT_CONTAINER);
        DockingActionSplitComponentEvent splitAction = (DockingActionSplitComponentEvent) action;
        Component base = splitAction.getBase();
        float div = splitAction.getDividorLocation();
        DockingConstants.Split splitPosition = splitAction.getSplitPosition();
        float parentDiv = splitAction.getParentDividorLocation();
        switch (splitPosition.value()){
          case DockingConstants.INT_SPLIT_TOP:
            vSplitAndResize(base, (Container) dockableContainer, base, div);
            break;
          case DockingConstants.INT_SPLIT_LEFT:
            hSplitAndResize(base, (Container) dockableContainer, base, div);
            break;
          case DockingConstants.INT_SPLIT_BOTTOM:
            vSplitAndResize(base, base, (Container) dockableContainer, div);
            break;
          case DockingConstants.INT_SPLIT_RIGHT:
            hSplitAndResize(base, base, (Container) dockableContainer, div);
            break;
        }
        if (parentDiv != -1 && base.getParent() instanceof SplitContainer){
          SplitContainer parent = (SplitContainer) base.getParent();
          new SplitResizer(parent, parentDiv);
        }
        return dockableContainer;
      default:
        throw new IllegalArgumentException("Action type not managed : "+ action.getActionType());
    }
    
    
  }
}