Package com.peterhi.ui.obsolete

Source Code of com.peterhi.ui.obsolete.DockContainer$Grip

package com.peterhi.ui.obsolete;

import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;

import com.peterhi.ui.UI;

public final class DockContainer extends Composite implements Listener {

  public static enum Position {
    LEFT,
    TOP,
    RIGHT,
    BOTTOM;
   
    public Orientation getOrientation() {
      if (this == TOP) {
        return Orientation.VERTICAL;
      }
     
      if (this == BOTTOM) {
        return Orientation.VERTICAL;
      }
     
      return Orientation.HORIZONTAL;
    }
   
    public boolean isVertical() {
      return getOrientation().isVertical();
    }
   
    public boolean isPositiveDirection() {
      if (this == RIGHT) {
        return true;
      }
     
      if (this == BOTTOM) {
        return true;
      }
     
      return false;
    }
  }
 
  public static enum Orientation {
    HORIZONTAL,
    VERTICAL;
   
    public boolean isVertical() {
      if (this == VERTICAL) {
        return true;
      }
     
      return false;
    }
  }
 
  static enum TouchResult {
    NONE,
    BEFORE,
    AFTER
  }
 
  public static final class Specification {
    private final Control reference;
    private final Position position;
    private final float proportion;
   
    public Specification(Control reference, Position position, float proportion) {
      if (position == null) {
        throw new NullPointerException("Position is null.");
      }
     
      if (proportion <= 0.0f) {
        throw new IllegalArgumentException(MessageFormat.format("Proportion {0} is less than or equal to zero.", Float.toString(proportion)));
      }
     
      if (proportion >= 1.0f) {
        throw new IllegalArgumentException(MessageFormat.format("Proportion {0} is greater than or equal to one.", Float.toString(proportion)));
      }
     
      this.reference = reference;
      this.position = position;
      this.proportion = proportion;
    }
   
    public Control getReference() {
      return reference;
    }
   
    public Position getPosition() {
      return position;
    }
   
    public Orientation getOrientation() {
      return position.getOrientation();
    }
   
    public boolean isVertical() {
      return position.isVertical();
    }
   
    public float getProportion() {
      return proportion;
    }
  }
 
  public static final class ControlAlreadyDockedException extends Exception {

    private static final long serialVersionUID = -7572638040253760184L;

    public ControlAlreadyDockedException() {
      super();
    }

    public ControlAlreadyDockedException(String message, Throwable cause) {
      super(message, cause);
    }

    public ControlAlreadyDockedException(String message) {
      super(message);
    }

    public ControlAlreadyDockedException(Throwable cause) {
      super(cause);
    }
  }
 
  public static final class NoSuchReferenceControlException extends Exception {

    private static final long serialVersionUID = 2887015206953145133L;

    public NoSuchReferenceControlException() {
      super();
    }

    public NoSuchReferenceControlException(String message, Throwable cause) {
      super(message, cause);
    }

    public NoSuchReferenceControlException(String message) {
      super(message);
    }

    public NoSuchReferenceControlException(Throwable cause) {
      super(cause);
    }
  }
 
  public static final class InsufficientControlSizeException extends Exception {

    private static final long serialVersionUID = -4126025456097998310L;

    public InsufficientControlSizeException() {
      super();
    }

    public InsufficientControlSizeException(String message, Throwable cause) {
      super(message, cause);
    }

    public InsufficientControlSizeException(String message) {
      super(message);
    }

    public InsufficientControlSizeException(Throwable cause) {
      super(cause);
    }
  }
 
  static final class XYComparator implements Comparator<Object> {

    private static XYComparator xComparator;
    private static XYComparator yComparator;
   
    public static XYComparator getInstance(Orientation orientation) {
      XYComparator instance;
     
      if (orientation.isVertical()) {
        if (yComparator == null) {
          yComparator = new XYComparator(orientation);
        }
       
        instance = yComparator;
      } else {
        if (xComparator == null) {
          xComparator = new XYComparator(orientation);
        }
       
        instance = xComparator;
      }

      return instance;
    }
   
    private final Orientation orientation;
   
    protected XYComparator(Orientation orientation) {
      if (orientation == null) {
        throw new NullPointerException();
      }
     
      this.orientation = orientation;
    }
   
    public Orientation getOrientation() {
      return orientation;
    }
   
    public boolean isVertical() {
      return orientation.isVertical();
    }
   
    @Override
    public int compare(Object object, Object other) {
      Bounds bounds = getBounds(object);
      Bounds otherBounds = getBounds(other);
      int result = isVertical() ? (bounds.getTop() - otherBounds.getTop()) : (bounds.getLeft() - otherBounds.getLeft());
     
      if (result == 0) {
        result = isVertical() ? (bounds.getLeft() - otherBounds.getLeft()) : (bounds.getTop() - otherBounds.getTop());
      }
     
      if (result == 0) {
        result = object.hashCode() - other.hashCode();
      }
     
      return result;
    }
   
    private Bounds getBounds(Object object) {
      try {
        Class<?> type = object.getClass();
        Method getBounds = type.getMethod("getBounds");
        Rectangle rectangle = (Rectangle )getBounds.invoke(object);
        Bounds bounds = Bounds.fromRectangle(rectangle);
        return bounds;
      } catch (RuntimeException ex) {
        throw ex;
      } catch (Exception ex) {
        throw new RuntimeException(ex);
      }
    }
  }
 
  abstract static class Sizer {
   
    private final DockContainer parent;
    private final Rectangle bounds;
   
    public Sizer(DockContainer parent, Rectangle bounds) {
      if (parent == null) {
        throw new NullPointerException();
      }
     
      if (bounds == null) {
        throw new NullPointerException();
      }
     
      this.parent = parent;
      this.bounds = bounds;
    }
   
    public final DockContainer getParent() {
      return parent;
    }
   
    public final Rectangle getBounds() {
      return bounds;
    }
   
    public abstract Group[] getGroups();

    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result
          + ((bounds == null) ? 0 : bounds.hashCode());
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      Sizer other = (Sizer) obj;
      if (bounds == null) {
        if (other.bounds != null)
          return false;
      } else if (!bounds.equals(other.bounds))
        return false;
      return true;
    }
   
    protected final Group getGroup(Orientation orientation) {
      XYComparator comp = XYComparator.getInstance(orientation);
      SortedSet<Sizer> sizers = new TreeSet<Sizer>(comp);
      getGroup(this, orientation, false, sizers);
      getGroup(this, orientation, true, sizers);
      Sizer[] ref = sizers.toArray(new Sizer[sizers.size()]);
     
      if (ref[0] instanceof Grip) {
        sizers.remove(ref[0]);
      }
     
      if (ref[ref.length - 1] instanceof Grip) {
        sizers.remove(ref[ref.length - 1]);
      }
     
      Group group = new Group(parent, sizers.toArray(new Sizer[sizers.size()]), orientation);
      return group;
    }
   
    private void getGroup(Sizer current, Orientation orientation, boolean positiveDirection, SortedSet<Sizer> sizers) {
      if (current == null) {
        throw new NullPointerException();
      }
     
      if (orientation == null) {
        throw new NullPointerException();
      }
     
      if (sizers == null) {
        throw new NullPointerException();
      }
     
      Bounds clientArea = Bounds.fromRectangle(parent.getClientArea());
      Bounds bounds = Bounds.fromRectangle(current.getBounds());
      sizers.add(current);
     
      if (orientation.isVertical()) {
        if (positiveDirection) {
          if (bounds.getBottom() >= clientArea.getBottom()) {
            return;
          }
        } else {
          if (bounds.getTop() <= clientArea.getTop()) {
            return;
          }
        }
      } else {
        if (positiveDirection) {
          if (bounds.getRight() >= clientArea.getRight()) {
            return;
          }
        } else {
          if (bounds.getLeft() <= clientArea.getLeft()) {
            return;
          }
        }
      }
     
      Sizer[] others = parent.getSizers();
     
      if (orientation.isVertical()) {
        for (Sizer other : others) {
          if (current.equals(other)) {
            continue;
          }
         
          if (other instanceof Split) {
            Split split = (Split )other;
           
            if (split.getOrientation() != orientation) {
              continue;
            }
          }
         
          Bounds otherBounds = Bounds.fromRectangle(other.getBounds());
         
          if (bounds.getLeft() != otherBounds.getLeft()) {
            continue;
          }
         
          if (bounds.getRight() != otherBounds.getRight()) {
            continue;
          }
         
          if (bounds.getTop() > otherBounds.getBottom() || bounds.getBottom() < otherBounds.getTop()) {
            continue;
          }
         
          if (positiveDirection) {
            if (otherBounds.getTop() > bounds.getTop()) {
              getGroup(other, orientation, positiveDirection, sizers);
            }
          } else {
            if (bounds.getTop() > otherBounds.getTop()) {
              getGroup(other, orientation, positiveDirection, sizers);
            }
          }
        }
      } else {
        for (Sizer other : others) {
          if (current.equals(other)) {
            continue;
          }
         
          if (other instanceof Split) {
            Split split = (Split )other;
           
            if (split.getOrientation() != orientation) {
              continue;
            }
          }
         
          Bounds otherBounds = Bounds.fromRectangle(other.getBounds());
         
          if (bounds.getTop() != otherBounds.getTop()) {
            continue;
          }
         
          if (bounds.getBottom() != otherBounds.getBottom()) {
            continue;
          }
         
          if (bounds.getLeft() > otherBounds.getRight() || bounds.getRight() < otherBounds.getLeft()) {
            continue;
          }
         
          if (positiveDirection) {
            if (otherBounds.getLeft() > bounds.getLeft()) {
              getGroup(other, orientation, positiveDirection, sizers);
            }
          } else {
            if (bounds.getLeft() > otherBounds.getLeft()) {
              getGroup(other, orientation, positiveDirection, sizers);
            }
          }
        }
      }
    }
  }
 
  static final class Split extends Sizer {

    private final Orientation orientation;
   
    public Split(DockContainer parent, Rectangle bounds, Orientation orientation) {
      super(parent, bounds);
     
      if (orientation == null) {
        throw new NullPointerException();
      }
     
      this.orientation = orientation;
    }
   
    public Orientation getOrientation() {
      return orientation;
    }
   
    public boolean isVertical() {
      return orientation.isVertical();
    }
   
    @Override
    public Group[] getGroups() {
      return new Group[] { getGroup(orientation) };
    }

    @Override
    public int hashCode() {
      final int prime = 31;
      int result = super.hashCode();
      result = prime * result
          + ((orientation == null) ? 0 : orientation.hashCode());
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (!super.equals(obj))
        return false;
      if (getClass() != obj.getClass())
        return false;
      Split other = (Split) obj;
      if (orientation != other.orientation)
        return false;
      return true;
    }
  }
 
  static final class Grip extends Sizer {

    public Grip(DockContainer parent, Rectangle bounds) {
      super(parent, bounds);
    }
   
    @Override
    public Group[] getGroups() {
      return new Group[] { getGroup(Orientation.HORIZONTAL),
        getGroup(Orientation.VERTICAL) };
    }
  }
 
  static final class Group {
    private final DockContainer parent;
    private final Sizer[] sizers;
    private final Orientation orientation;
   
    public Group(DockContainer parent, Sizer[] sizers, Orientation orientation) {
      if (parent == null) {
        throw new NullPointerException();
      }
     
      if (sizers == null) {
        throw new NullPointerException();
      }
     
      if (orientation == null) {
        throw new NullPointerException();
      }
     
      if (sizers.length <= 0) {
        throw new IllegalArgumentException();
      }

      this.parent = parent;
      this.orientation = orientation;
      this.sizers = Arrays.copyOf(sizers, sizers.length);
      XYComparator comp = XYComparator.getInstance(orientation);
      Arrays.sort(this.sizers, comp);
    }
   
    public DockContainer getParent() {
      return parent;
    }
   
    public Orientation getOrientation() {
      return orientation;
    }
   
    public boolean isVertical() {
      return orientation.isVertical();
    }
   
    public int size() {
      return sizers.length;
    }
   
    public Sizer get(int index) {
      return sizers[index];
    }
   
    public int indexOf(Sizer sizer) {
      for (int i = 0; i < sizers.length; i++) {
        Sizer other = sizers[i];
       
        if (other.equals(sizer)) {
          return i;
        }
      }
     
      return -1;
    }
   
    public boolean contains(Sizer sizer) {
      int index = indexOf(sizer);
      return index != -1;
    }
   
    public Sizer[] getSizers() {
      return Arrays.copyOf(sizers, sizers.length);
    }
   
    public Split[] getSplits() {
      XYComparator comp = XYComparator.getInstance(getOrientation());
      SortedSet<Split> splits = new TreeSet<Split>(comp);
     
      for (Sizer other : sizers) {
        if (other instanceof Split) {
          Split split = (Split )other;
          splits.add(split);
        }
      }
     
      return splits.toArray(new Split[splits.size()]);
    }
   
    public Grip[] getGrips() {
      XYComparator comp = XYComparator.getInstance(getOrientation());
      SortedSet<Grip> grips = new TreeSet<Grip>(comp);
     
      for (Sizer other : sizers) {
        if (other instanceof Grip) {
          Grip grip = (Grip )other;
          grips.add(grip);
        }
      }
     
      return grips.toArray(new Grip[grips.size()]);
    }
   
    public Rectangle getBounds() {
      Bounds bounds = Bounds.newInstance();
     
      for (int i = 0; i < sizers.length; i++) {
        Sizer sizer = sizers[i];
        Bounds sizerBounds = Bounds.fromRectangle(sizer.getBounds());
       
        if (i == 0) {
          bounds.setLeft(sizerBounds.getLeft(), false);
          bounds.setTop(sizerBounds.getTop(), false);
          bounds.setRight(sizerBounds.getRight(), false);
          bounds.setBottom(sizerBounds.getBottom(), false);
        } else {
          if (sizerBounds.getLeft() < bounds.getLeft()) {
            bounds.setLeft(bounds.getLeft(), false);
          }

          if (sizerBounds.getTop() < bounds.getTop()) {
            bounds.setTop(bounds.getTop(), false);
          }
         
          if (sizerBounds.getRight() > bounds.getRight()) {
            bounds.setRight(sizerBounds.getRight(), false);
          }

          if (sizerBounds.getBottom() > bounds.getBottom()) {
            bounds.setBottom(sizerBounds.getBottom(), false);
          }
        }
      }
     
      return bounds.toRectangle();
    }
   
    public Control[][] getAffects() {
      XYComparator comp = XYComparator.getInstance(getOrientation());
      SortedSet<Control> before = new TreeSet<Control>(comp);
      SortedSet<Control> after = new TreeSet<Control>(comp);
      Control[] controls = parent.getControls();
     
      for (Control control : controls) {
        Bounds controlBounds = Bounds.fromRectangle(control.getBounds());
        TouchResult result = getTouchResult(controlBounds);
       
        if (result == TouchResult.BEFORE) {
          before.add(control);
        } else if (result == TouchResult.AFTER) {
          after.add(control);
        }
      }
     
      return new Control[][] {
        before.toArray(new Control[before.size()]),
        after.toArray(new Control[after.size()])
      };
    }
   
    public Group[][] getConflicts() {
      XYComparator comp = XYComparator.getInstance(isVertical() ? Orientation.HORIZONTAL : Orientation.VERTICAL);
      SortedSet<Group> before = new TreeSet<Group>(comp);
      SortedSet<Group> after = new TreeSet<Group>(comp);
      Group[] groups = parent.getGroups();
      Bounds bounds = Bounds.fromRectangle(getBounds());
     
      for (Group group : groups) {
        if (equals(group)) {
          continue;
        }
       
        if (getOrientation() != group.getOrientation()) {
          continue;
        }
       
        Bounds groupBounds = Bounds.fromRectangle(group.getBounds());
       
        if (isVertical()) {
          if (bounds.getTop() <= groupBounds.getBottom() && bounds.getBottom() >= groupBounds.getTop()) {
            if (groupBounds.getRight() <= bounds.getLeft()) {
              before.add(group);
            }
           
            if (bounds.getRight() <= groupBounds.getLeft()) {
              after.add(group);
            }
          }
        } else {
          if (bounds.getLeft() <= groupBounds.getRight() && bounds.getRight() >= groupBounds.getLeft()) {
            if (groupBounds.getBottom() <= bounds.getTop()) {
              before.add(group);
            }
           
            if (bounds.getBottom() <= groupBounds.getTop()) {
              after.add(group);
            }
          }
        }
      }
     
      return new Group[][] {
        before.toArray(new Group[before.size()]),
        after.toArray(new Group[after.size()])
      };
    }
   
    public Group[][] getConnects() {
      XYComparator comp = XYComparator.getInstance(getOrientation());
      SortedSet<Group> before = new TreeSet<Group>(comp);
      SortedSet<Group> after = new TreeSet<Group>(comp);
      Group[] groups = parent.getGroups();
     
      for (Group group : groups) {
        if (equals(group)) {
          continue;
        }
       
        if (getOrientation() == group.getOrientation()) {
          continue;
        }
       
        Bounds groupBounds = Bounds.fromRectangle(group.getBounds());
        TouchResult result = getTouchResult(groupBounds);
       
        if (result == TouchResult.BEFORE) {
          before.add(group);
        } else if (result == TouchResult.AFTER) {
          after.add(group);
        }
      }
     
      return new Group[][] {
        before.toArray(new Group[before.size()]),
        after.toArray(new Group[after.size()])
      };
    }
   
    public Group[][] getSnaps() {
      Bounds bounds = Bounds.fromRectangle(getBounds());
      Point first = new Point(0, 0);
      Point last = new Point(0, 0);
     
      if (isVertical()) {
        first.x = bounds.getCenterX();
        first.y = bounds.getTop() - bounds.getWidth() / 2;
        last.x = bounds.getCenterX();
        last.y = bounds.getBottom() + bounds.getWidth() / 2;
      } else {
        first.x = bounds.getLeft() - bounds.getHeight() / 2;
        first.y = bounds.getCenterY();
        last.x = bounds.getRight() + bounds.getHeight() / 2;
        last.y = bounds.getCenterY();
      }
     
      XYComparator comp = XYComparator.getInstance(isVertical() ? Orientation.HORIZONTAL : Orientation.VERTICAL);
      SortedSet<Group> before = new TreeSet<Group>(comp);
      SortedSet<Group> after = new TreeSet<Group>(comp);
      Group[] groups = parent.getGroups();
     
      for (Group group : groups) {
        if (getOrientation() == group.getOrientation()) {
          continue;
        }
       
        Rectangle groupBounds = group.getBounds();

        if (groupBounds.contains(first)) {
          Group[][] connected = group.getConnects();
          Group[] groupsBefore = connected[0];
         
          for (Group groupBefore : groupsBefore) {
            before.add(groupBefore);
          }
        } else if (groupBounds.contains(last)) {
          Group[][] connected = group.getConnects();
          Group[] groupsAfter = connected[1];
         
          for (Group groupAfter : groupsAfter) {
            after.add(groupAfter);
          }
        }
      }
     
      before.remove(this);
      after.remove(this);

      return new Group[][] {
        before.toArray(new Group[before.size()]),
        after.toArray(new Group[after.size()])
      };
    }
   
    public Group[] getIntersects() {
      XYComparator comp = XYComparator.getInstance(getOrientation());
      SortedSet<Group> intersected = new TreeSet<Group>(comp);
      Group[] groups = parent.getGroups();
      Rectangle bounds = getBounds();
     
      for (Group group : groups) {
        if (equals(group)) {
          continue;
        }
       
        if (getOrientation() == group.getOrientation()) {
          continue;
        }
       
        Rectangle groupBounds = group.getBounds();
       
        if (bounds.intersects(groupBounds)) {
          intersected.add(group);
        }
      }
     
      return intersected.toArray(new Group[intersected.size()]);
    }
   
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result
          + ((orientation == null) ? 0 : orientation.hashCode());
      result = prime * result + Arrays.hashCode(sizers);
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      Group other = (Group) obj;
      if (orientation != other.orientation)
        return false;
      if (!Arrays.equals(sizers, other.sizers))
        return false;
      return true;
    }
   
    private TouchResult getTouchResult(Bounds controlBounds) {
      if (controlBounds == null) {
        throw new NullPointerException();
      }
     
      Bounds bounds = Bounds.fromRectangle(getBounds());
      TouchResult result = TouchResult.NONE;
     
      if (isVertical()) {
        if (controlBounds.getTop() <= bounds.getBottom() && controlBounds.getBottom() >= bounds.getTop()) {
          if (controlBounds.getRight() == bounds.getLeft()) {
            result = TouchResult.BEFORE;
          }
         
          if (bounds.getRight() == controlBounds.getLeft()) {
            result = TouchResult.AFTER;
          }
        }
      } else {
        if (controlBounds.getLeft() <= bounds.getRight() && controlBounds.getRight() >= bounds.getLeft()) {
          if (controlBounds.getBottom() == bounds.getTop()) {
            result = TouchResult.BEFORE;
          }
         
          if (bounds.getBottom() == controlBounds.getTop()) {
            result = TouchResult.AFTER;
          }
        }
      }

      return result;
    }
  }
 
  static class DragGroupInfo {
    private final Group group;
    private final Rectangle cachedBounds = new Rectangle(0, 0, 0, 0);
    private Control[][] cachedAffects;
    private Group[][] cachedConflicts;
    private Group[][] cachedConnects;
    private Group[][] cachedSnaps;
    private Group[] cachedIntersects;
   
    public DragGroupInfo(Group group) {
      this.group = group;
    }
   
    public Group getGroup() {
      return group;
    }
   
    public Rectangle getCachedBounds() {
      return new Rectangle(cachedBounds.x, cachedBounds.y, cachedBounds.width, cachedBounds.height);
    }
   
    public void setCachedBounds(Rectangle value) {
      if (value == null) {
        throw new NullPointerException();
      }
     
      cachedBounds.x = value.x;
      cachedBounds.y = value.y;
      cachedBounds.width = value.width;
      cachedBounds.height = value.height;
    }
   
    public Control[][] getCachedAffects() {
      return cachedAffects;
    }
   
    public void setCachedAffects(Control[][] value) {
      cachedAffects = value;
    }
   
    public Group[][] getCachedConflicts() {
      return cachedConflicts;
    }
   
    public void setCachedConflicts(Group[][] value) {
      cachedConflicts = value;
    }
   
    public Group[][] getCachedConnects() {
      return cachedConnects;
    }
   
    public void setCachedConnects(Group[][] value) {
      cachedConnects = value;
    }
   
    public Group[][] getCachedSnaps() {
      return cachedSnaps;
    }
   
    public void setCachedSnaps(Group[][] value) {
      cachedSnaps = value;
    }
   
    public Group[] getCachedIntersects() {
      return cachedIntersects;
    }
   
    public void setCachedIntersects(Group[] value) {
      cachedIntersects = value;
    }

    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + ((group == null) ? 0 : group.hashCode());
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      DragGroupInfo other = (DragGroupInfo) obj;
      if (group == null) {
        if (other.group != null)
          return false;
      } else if (!group.equals(other.group))
        return false;
      return true;
    }
  }
 
  static class DragInfo {
    private final Map<Control, Bounds> controlBoundsCached = new HashMap<Control, Bounds>();
    private DragGroupInfo[] groupInfos;
    private final Point location = new Point(0, 0);
    private final Point offset = new Point(0, 0);
   
    public DragInfo() {
    }
   
    public Control[] getControlsCached() {
      return controlBoundsCached.keySet().toArray(new Control[controlBoundsCached.size()]);
    }
   
    public void putControlBoundsCached(Control control, Bounds bounds) {
      controlBoundsCached.put(control, bounds);
    }
   
    public Bounds getControlBoundsCached(Control control) {
      return controlBoundsCached.get(control);
    }
   
    public DragGroupInfo[] getGroupInfos() {
      return groupInfos;
    }
   
    public void setGroupInfos(DragGroupInfo[] value) {
      if (value == null) {
        throw new NullPointerException();
      }
     
      groupInfos = value;
    }
   
    public boolean contains(Group group) {
      return getGroupInfo(group) != null;
    }
   
    public DragGroupInfo getGroupInfo(Group group) {
      for (DragGroupInfo groupInfo : groupInfos) {
        if (groupInfo.getGroup().equals(group)) {
          return groupInfo;
        }
      }
     
      return null;
    }
   
    public Point getLocation() {
      return new Point(location.x, location.y);
    }
   
    public void setLocation(Point value) {
      if (value == null) {
        throw new NullPointerException();
      }
     
      location.x = value.x;
      location.y = value.y;
    }
   
    public Point getOffset() {
      return new Point(offset.x, offset.y);
    }
   
    public void setOffset(Point value) {
      if (value == null) {
        throw new NullPointerException();
      }

      offset.x = value.x;
      offset.y = value.y;
    }
  }
 
  private static final int BAR_SIZE = 20;
  private static final int MIN_SIZE = 50;

  private final List<Control> controls = new ArrayList<Control>();
  private Bounds oldClient;
  private DragInfo dragInfo;
 
  public DockContainer(Composite parent, int style) {
    super(parent, style);
    addListener(SWT.Resize, this);
    addListener(SWT.MouseEnter, this);
    addListener(SWT.MouseExit, this);
    addListener(SWT.MouseDown, this);
    addListener(SWT.MouseMove, this);
    addListener(SWT.MouseUp, this);
  }
 
  public void setMinimumShellSize(Shell shell) {
    Rectangle bounds = shell.getBounds();
    Rectangle clientArea = shell.getClientArea();
    int xDiff = bounds.width - clientArea.width;
    int yDiff = bounds.height - clientArea.height;
    Point minSize = computeSize(0, 0);
    minSize.x += xDiff;
    minSize.y += yDiff;
    shell.setMinimumSize(minSize);
  }

  @Override
  public void handleEvent(Event event) {
    if (event.widget == this && event.type == SWT.Resize) {
      if (oldClient == null) {
        oldClient = Bounds.fromRectangle(getClientArea());
      }
     
      layout();
      oldClient = Bounds.fromRectangle(getClientArea());
    }
   
    if (event.widget == this && event.type == SWT.MouseEnter) {
      updateCursor(event.x, event.y);
    }
   
    if (event.widget == this && event.type == SWT.MouseExit) {
      updateCursor(event.x, event.y);
    }

    if (event.widget == this && event.type == SWT.MouseDown && event.button == 1 && dragInfo == null) {
      beginDrag(event.x, event.y, ((event.stateMask & SWT.SHIFT) != SWT.SHIFT));
    }
   
    if (event.widget == this && event.type == SWT.MouseMove) {
      updateCursor(event.x, event.y);
     
      if (dragInfo != null) {
        doDrag(event.x, event.y);
      }
    }
   
    if (event.widget == this && event.type == SWT.MouseUp && event.button == 1 && dragInfo != null) {
      endDrag();
    }
   
    if (event.widget != this && event.widget instanceof Control && event.type == SWT.Dispose) {
      Control control = (Control )event.widget;
      undock(control);
    }
  }
 
  private void beginDrag(int x, int y, boolean detach) {
    dragInfo = new DragInfo();
    dragInfo.setLocation(new Point(x, y));
   
    Sizer[] sizers = getSizersAt(x, y);
    Point offset = new Point(0, 0);
   
    for (Sizer sizer : sizers) {
      Bounds bounds = Bounds.fromRectangle(sizer.getBounds());
      offset.x = x - bounds.getLeft();
      offset.y = y - bounds.getTop();
      break;
    }
   
    dragInfo.setOffset(offset);
   
    Group[] groups = getGroupsAt(x, y);
    Set<DragGroupInfo> groupInfos = new HashSet<DragGroupInfo>();
   
    for (Group group : groups) {
      if (detach) {
        group = detachGroup(group, x, y);
      }
     
      DragGroupInfo groupInfo = new DragGroupInfo(group);
      Rectangle bounds = group.getBounds();
      Control[][] affected = group.getAffects();
     
      for (Control[] array : affected) {
        for (Control item : array) {
          Bounds controlBounds = Bounds.fromRectangle(item.getBounds());
          dragInfo.putControlBoundsCached(item, controlBounds);
        }
      }
     
      Group[][] conflicted = group.getConflicts();
      Group[][] connected = group.getConnects();
      Group[][] snapped = group.getSnaps();
      Group[] intersected = group.getIntersects();
     
      groupInfo.setCachedBounds(bounds);
      groupInfo.setCachedAffects(affected);
      groupInfo.setCachedConflicts(conflicted);
      groupInfo.setCachedConnects(connected);
      groupInfo.setCachedSnaps(snapped);
      groupInfo.setCachedIntersects(intersected);
     
      groupInfos.add(groupInfo);
    }
   
    dragInfo.setGroupInfos(groupInfos.toArray(new DragGroupInfo[groupInfos.size()]));
  }
 
  private void doDrag(int x, int y) {
    Point newLocation = new Point(x, y);
    Point oldLocation = dragInfo.getLocation();
    Point offset = dragInfo.getOffset();
    newLocation.x -= offset.x;
    newLocation.y -= offset.y;
    normalizeLocation(newLocation);
    newLocation.x += offset.x;
    newLocation.y += offset.y;
    Point moved = new Point(newLocation.x - oldLocation.x, newLocation.y - oldLocation.y);
    DragGroupInfo[] groupInfos = dragInfo.getGroupInfos();
   
    for (DragGroupInfo groupInfo : groupInfos) {
      Group group = groupInfo.getGroup();
      Control[][] affects = groupInfo.getCachedAffects();
      Control[] affectsBefore = affects[0];
      Control[] affectsAfter = affects[1];
     
      for (Control affectBefore : affectsBefore) {
        Bounds bounds = dragInfo.getControlBoundsCached(affectBefore);
       
        if (group.isVertical()) {
          bounds.setRight(bounds.getRight() + moved.x, false);
        } else {
          bounds.setBottom(bounds.getBottom() + moved.y, false);
        }
       
        dragInfo.putControlBoundsCached(affectBefore, bounds);
      }
     
      for (Control affectAfter : affectsAfter) {
        Bounds bounds = dragInfo.getControlBoundsCached(affectAfter);
       
        if (group.isVertical()) {
          bounds.setLeft(bounds.getLeft() + moved.x, false);
        } else {
          bounds.setTop(bounds.getTop() + moved.y, false);
        }
       
        dragInfo.putControlBoundsCached(affectAfter, bounds);
      }
    }
   
    Control[] controls = dragInfo.getControlsCached();
   
    for (Control control : controls) {
      Bounds bounds = dragInfo.getControlBoundsCached(control);
      control.setBounds(bounds.toRectangle());
    }
   
    dragInfo.setLocation(newLocation);
  }
 
  private void endDrag() {
    dragInfo = null;
    layout();
  }
 
  private Group detachGroup(Group oldGroup, int x, int y) {
    Group[] intersects = oldGroup.getIntersects();
    XYComparator comp = XYComparator.getInstance(oldGroup.getOrientation());
    SortedSet<Sizer> sizers = new TreeSet<Sizer>(comp);
    Bounds boundary = Bounds.fromRectangle(getClientArea());
   
    if (oldGroup.isVertical()) {
      for (int i = 0; i < intersects.length; i++) {
        Group intersect = intersects[i];
        Bounds xsect = Bounds.fromRectangle(intersect.getBounds());
       
        if (xsect.getBottom() <= y) {
          boundary.setTop(xsect.getBottom(), false);
        }
      }
     
      for (int i = intersects.length - 1; i >= 0; i--) {
        Group intersect = intersects[i];
        Bounds xsect = Bounds.fromRectangle(intersect.getBounds());
       
        if (xsect.getTop() >= y) {
          boundary.setBottom(xsect.getTop(), false);
        }
      }
     
      for (int i = 0; i < oldGroup.size(); i++) {
        Sizer sizer = oldGroup.get(i);
        Bounds sizerBounds = Bounds.fromRectangle(sizer.getBounds());
       
        if (sizerBounds.getTop() >= boundary.getTop() && sizerBounds.getBottom() <= boundary.getBottom()) {
          sizers.add(sizer);
        }
      }
    } else {
      for (int i = 0; i < intersects.length; i++) {
        Group intersect = intersects[i];
        Bounds xsect = Bounds.fromRectangle(intersect.getBounds());
       
        if (xsect.getRight() <= x) {
          boundary.setLeft(xsect.getRight(), false);
        }
      }
     
      for (int i = intersects.length - 1; i >= 0; i--) {
        Group intersect = intersects[i];
        Bounds xsect = Bounds.fromRectangle(intersect.getBounds());
       
        if (xsect.getLeft() >= x) {
          boundary.setRight(xsect.getLeft(), false);
        }
      }
     
      for (int i = 0; i < oldGroup.size(); i++) {
        Sizer sizer = oldGroup.get(i);
        Bounds sizerBounds = Bounds.fromRectangle(sizer.getBounds());
       
        if (sizerBounds.getLeft() >= boundary.getLeft() && sizerBounds.getRight() <= boundary.getRight()) {
          sizers.add(sizer);
        }
      }
    }
   
    Group newGroup = new Group(this, sizers.toArray(new Sizer[sizers.size()]), oldGroup.getOrientation());
    return newGroup;
  }
 
  private void normalizeLocation(Point location) {
    DragGroupInfo[] groupInfos = dragInfo.getGroupInfos();
   
    for (DragGroupInfo groupInfo : groupInfos) {
      Group group = groupInfo.getGroup();
      Group[][] snaps = groupInfo.getCachedSnaps();
     
      for (Group[] array : snaps) {
        for (Group item : array) {
          Bounds bounds = Bounds.fromRectangle(item.getBounds());
         
          if (group.isVertical()) {
            if (location.x < bounds.getRight() && location.x + BAR_SIZE > bounds.getLeft()) {
              location.x = bounds.getLeft();
            }
          } else {
            if (location.y < bounds.getBottom() && location.y + BAR_SIZE > bounds.getTop()) {
              location.y = bounds.getTop();
            }
          }
        }
      }
    }
   
    for (DragGroupInfo groupInfo : groupInfos) {
      Group group = groupInfo.getGroup();
      Bounds bounds = Bounds.fromRectangle(groupInfo.getCachedBounds());
     
      if (group.isVertical()) {
        if (location.x < bounds.getRight() && location.x + BAR_SIZE > bounds.getLeft()) {
          location.x = bounds.getLeft();
        }
      } else {
        if (location.y < bounds.getBottom() && location.y + BAR_SIZE > bounds.getTop()) {
          location.y = bounds.getTop();
        }
      }
    }
   
    Bounds bounds = Bounds.fromRectangle(getClientArea());
   
    for (DragGroupInfo groupInfo : groupInfos) {
      Group group = groupInfo.getGroup();
      Group[][] conflicts = groupInfo.getCachedConflicts();
      Group[] conflictsBefore = conflicts[0];
      Group[] conflictsAfter = conflicts[1];
     
      if (conflictsBefore.length > 0) {
        Group conflictBefore = conflictsBefore[conflictsBefore.length - 1];
        Bounds boundsBefore = Bounds.fromRectangle(conflictBefore.getBounds());
       
        if (group.isVertical()) {
          if (bounds.getLeft() < boundsBefore.getRight()) {
            bounds.setLeft(boundsBefore.getRight(), false);
          }
        } else {
          if (bounds.getTop() < boundsBefore.getBottom()) {
            bounds.setTop(boundsBefore.getBottom(), false);
          }
        }
      }
     
      if (conflictsAfter.length > 0) {
        Group conflictAfter = conflictsAfter[0];
        Bounds boundsAfter = Bounds.fromRectangle(conflictAfter.getBounds());
       
        if (group.isVertical()) {
          if (bounds.getRight() > boundsAfter.getLeft()) {
            bounds.setRight(boundsAfter.getLeft(), false);
          }
        } else {
          if (bounds.getBottom() > boundsAfter.getTop()) {
            bounds.setBottom(boundsAfter.getTop(), false);
          }
        }
      }
    }
   
    bounds.setRight(bounds.getRight() - BAR_SIZE, false);
    bounds.setBottom(bounds.getBottom() - BAR_SIZE, false);
    bounds.setLeft(bounds.getLeft() + MIN_SIZE, false);
    bounds.setTop(bounds.getTop() + MIN_SIZE, false);
    bounds.setRight(bounds.getRight() - MIN_SIZE, false);
    bounds.setBottom(bounds.getBottom() - MIN_SIZE, false);

    if (location.x > bounds.getRight()) {
      location.x = bounds.getRight();
    }
   
    if (location.x < bounds.getLeft()) {
      location.x = bounds.getLeft();
    }
   
    if (location.y > bounds.getBottom()) {
      location.y = bounds.getBottom();
    }
   
    if (location.y < bounds.getTop()) {
      location.y = bounds.getTop();
    }
  }
 
  public Control[] getControls() {
    Control[] array = controls.toArray(new Control[controls.size()]);
    XYComparator comp = XYComparator.getInstance(Orientation.HORIZONTAL);
    Arrays.sort(array, comp);
    return controls.toArray(array);
  }
 
  @Override
  public void setLayout(Layout layout) {
   
  }

  @Override
  public Point computeSize(int widthHint, int heightHint, boolean changed) {
    Point computedSize = super.computeSize(widthHint, heightHint, changed);
    Control xMin = null;
    Control yMin = null;
    int minWidth = 0;
    int minHeight = 0;
    Control[] controls = getControls();
    Arrays.sort(controls, XYComparator.getInstance(Orientation.HORIZONTAL));
   
    for (Control control : controls) {
      Rectangle bounds = control.getBounds();
     
      if (xMin == null) {
        xMin = control;
        minWidth = bounds.width;
      } else if (bounds.width < minWidth) {
        xMin = control;
        minWidth = bounds.width;
      }
     
      if (yMin == null) {
        yMin = control;
        minHeight = bounds.height;
      } else if (bounds.height < minHeight) {
        yMin = control;
        minHeight = bounds.height;
      }
    }
   
    Rectangle clientArea = getClientArea();
   
    if (xMin != null) {
      float xProportion = (float )MIN_SIZE / (float )minWidth;
      minWidth = Math.round(clientArea.width * xProportion);
    }
   
    if (yMin != null) {
      float yProportion = (float )MIN_SIZE / (float )minHeight;
      minHeight = Math.round(clientArea.height * yProportion);
     
    }

    if (xMin != null && computedSize.x < minWidth) {
      computedSize.x = minWidth;
    }
   
    if (yMin != null && computedSize.y < minHeight) {
      computedSize.y = minHeight;
    }
   
    return computedSize;
  }

  @Override
  public void layout(boolean changed) {
    try {
      Bounds newClient = Bounds.fromRectangle(getClientArea());
      Map<Control, Bounds> controlBounds = computeLayoutBounds(newClient);
     
      for (Control key : controlBounds.keySet()) {
        Bounds value = controlBounds.get(key);
        key.setBounds(value.toRectangle());
      }
    } catch (RuntimeException ex) {
      ex.printStackTrace();
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
 
  public boolean isDockable(Control control, Specification specification) {
    if (control == null) {
      throw new NullPointerException("Control is null.");
    }
   
    try {
      Map<Control, Rectangle> controlBounds = computeDockBounds(control, specification);
      return !controlBounds.isEmpty();
    } catch (Exception ex) {
      return false;
    }
  }
 
  public void dock(Control control, Specification specification) {
    if (control == null) {
      throw new NullPointerException("Control is null.");
    }
   
    if (controls.contains(control)) {
      throw new IllegalArgumentException();
    }

    try {
      Map<Control, Rectangle> controlBounds = computeDockBounds(control, specification);
     
      if (controlBounds.isEmpty()) {
        throw new IllegalArgumentException(MessageFormat.format("The control {0} is not dockable.", control));
      }
     
      for (Control key : controlBounds.keySet()) {
        Rectangle value = controlBounds.get(key);
        key.setBounds(value);
      }
     
      controls.add(control);
      control.addListener(SWT.Dispose, this);
    } catch (RuntimeException ex) {
      throw ex;
    } catch (Exception ex) {
      throw new IllegalArgumentException(MessageFormat.format("Invalid arguments while docking control {0}.", control), ex);
    }
  }
 
  public void undock(Control control) {
    if (control == null) {
      throw new NullPointerException();
    }
   
    if (!controls.contains(control)) {
      throw new IllegalArgumentException();
    }

    Bounds bounds = Bounds.fromRectangle(control.getBounds());
    Control[] left = getControlsNear(control, Position.LEFT);
    Control[] top = getControlsNear(control, Position.TOP);
    Control[] right = getControlsNear(control, Position.RIGHT);
    Control[] bottom = getControlsNear(control, Position.BOTTOM);
    boolean done = false;
   
    if (!done && left.length > 0) {
      Bounds leftTop = Bounds.fromRectangle(left[0].getBounds());
      Bounds leftBottom = Bounds.fromRectangle(left[left.length - 1].getBounds());
     
      if (leftTop.getTop() == bounds.getTop() && leftBottom.getBottom() == bounds.getBottom()) {
        for (Control item : left) {
          Bounds itemBounds = Bounds.fromRectangle(item.getBounds());
          itemBounds.setRight(bounds.getRight(), false);
          item.setBounds(itemBounds.toRectangle());
        }
       
        done = true;
      }
    }
   
    if (!done && top.length > 0) {
      Bounds topLeft = Bounds.fromRectangle(top[0].getBounds());
      Bounds topRight = Bounds.fromRectangle(top[top.length - 1].getBounds());
     
      if (topLeft.getLeft() == bounds.getLeft() && topRight.getRight() == bounds.getRight()) {
        for (Control item : top) {
          Bounds itemBounds = Bounds.fromRectangle(item.getBounds());
          itemBounds.setBottom(bounds.getBottom(), false);
          item.setBounds(itemBounds.toRectangle());
        }
       
        done = true;
      }
    }
   
    if (!done && right.length > 0) {
      Bounds rightTop = Bounds.fromRectangle(right[0].getBounds());
      Bounds rightBottom = Bounds.fromRectangle(right[right.length - 1].getBounds());
     
      if (rightTop.getTop() == bounds.getTop() && rightBottom.getBottom() == bounds.getBottom()) {
        for (Control item : right) {
          Bounds itemBounds = Bounds.fromRectangle(item.getBounds());
          itemBounds.setLeft(bounds.getLeft(), false);
          item.setBounds(itemBounds.toRectangle());
        }
       
        done = true;
      }
    }
   
    if (!done && bottom.length > 0) {
      Bounds bottomLeft = Bounds.fromRectangle(bottom[0].getBounds());
      Bounds bottomRight = Bounds.fromRectangle(bottom[bottom.length - 1].getBounds());
     
      if (bottomLeft.getLeft() == bounds.getLeft() && bottomRight.getRight() == bounds.getRight()) {
        for (Control item : bottom) {
          Bounds itemBounds = Bounds.fromRectangle(item.getBounds());
          itemBounds.setTop(bounds.getTop(), false);
          item.setBounds(itemBounds.toRectangle());
        }
       
        done = true;
      }
    }
   
    control.removeListener(SWT.Dispose, this);
    controls.remove(control);
    control.setBounds(0, 0, 0, 0);
  }
 
  Split[] getSplits() {
    Control[] controls = getControls();
    SortedSet<Split> splits = new TreeSet<Split>(XYComparator.getInstance(Orientation.HORIZONTAL));
   
    for (Control control : controls) {
      Bounds bounds = Bounds.fromRectangle(control.getBounds());
     
      for (Control other : controls) {
        Bounds otherBounds = Bounds.fromRectangle(other.getBounds());
        Bounds splitBounds = Bounds.newInstance();
       
        if (bounds.getLeft() <= otherBounds.getRight() && bounds.getRight() >= otherBounds.getLeft()) {
          splitBounds.setLeft(Math.max(bounds.getLeft(), otherBounds.getLeft()), false);
          splitBounds.setTop(Math.min(bounds.getBottom(), otherBounds.getBottom()), false);
          splitBounds.setRight(Math.min(bounds.getRight(), otherBounds.getRight()), false);
          splitBounds.setBottom(Math.max(bounds.getTop(), otherBounds.getTop()), false);
         
          if (splitBounds.isValid() && splitBounds.getHeight() == BAR_SIZE) {
            Split split = new Split(this, splitBounds.toRectangle(), Orientation.HORIZONTAL);
            splits.add(split);
          }
        } else if (bounds.getTop() <= otherBounds.getBottom() && bounds.getBottom() >= otherBounds.getTop()) {
          splitBounds.setLeft(Math.min(bounds.getRight(), otherBounds.getRight()), false);
          splitBounds.setTop(Math.max(bounds.getTop(), otherBounds.getTop()), false);
          splitBounds.setRight(Math.max(bounds.getLeft(), otherBounds.getLeft()), false);
          splitBounds.setBottom(Math.min(bounds.getBottom(), otherBounds.getBottom()), false);
         
          if (splitBounds.isValid() && splitBounds.getWidth() == BAR_SIZE) {
            Split split = new Split(this, splitBounds.toRectangle(), Orientation.VERTICAL);
            splits.add(split);
          }
        }
      }
    }
   
    return splits.toArray(new Split[splits.size()]);
  }
 
  Grip[] getGrips() {
    Split[] splits = getSplits();
    Bounds clientArea = Bounds.fromRectangle(getClientArea());
    SortedSet<Grip> grips = new TreeSet<Grip>(XYComparator.getInstance(Orientation.HORIZONTAL));
   
    for (Split split : splits) {
      Bounds bounds = Bounds.fromRectangle(split.getBounds());
      Grip before = null;
      Grip after = null;
     
      if (split.isVertical()) {
        if (bounds.getTop() > clientArea.getTop()) {
          Bounds rBefore = Bounds.fromXYWidthHeight(bounds.getLeft(), bounds.getTop() - BAR_SIZE, BAR_SIZE, BAR_SIZE);
          before = new Grip(this, rBefore.toRectangle());
        }
       
        if (bounds.getBottom() < clientArea.getBottom()) {
          Bounds rAfter = Bounds.fromXYWidthHeight(bounds.getLeft(), bounds.getBottom(), BAR_SIZE, BAR_SIZE);
          after = new Grip(this, rAfter.toRectangle());
        }
      } else {
        if (bounds.getLeft() > clientArea.getLeft()) {
          Bounds rBefore = Bounds.fromXYWidthHeight(bounds.getLeft() - BAR_SIZE, bounds.getTop(), BAR_SIZE, BAR_SIZE);
          before = new Grip(this, rBefore.toRectangle());
        }

        if (bounds.getRight() < clientArea.getRight()) {
          Bounds rAfter = Bounds.fromXYWidthHeight(bounds.getRight(), bounds.getTop(), BAR_SIZE, BAR_SIZE);
          after = new Grip(this, rAfter.toRectangle());
        }
      }
     
      if (before != null) {
        grips.add(before);
      }
     
      if (after != null) {
        grips.add(after);
      }
    }
   
    return grips.toArray(new Grip[grips.size()]);
  }
 
  Sizer[] getSizers() {
    Split[] splits = getSplits();
    Grip[] grips = getGrips();
    XYComparator xComp = XYComparator.getInstance(Orientation.HORIZONTAL);
    SortedSet<Sizer> sizers = new TreeSet<Sizer>(xComp);
   
    for (Split split : splits) {
      sizers.add(split);
    }
   
    for (Grip grip : grips) {
      sizers.add(grip);
    }
   
    return sizers.toArray(new Sizer[sizers.size()]);
  }
 
  Group[] getGroups() {
    Split[] splits = getSplits();
    XYComparator xComp = XYComparator.getInstance(Orientation.HORIZONTAL);
    SortedSet<Group> groups = new TreeSet<Group>(xComp);

    for (Split split : splits) {
      Group[] array = split.getGroups();
     
      for (Group item : array) {
        groups.add(item);
      }
    }
   
    return groups.toArray(new Group[groups.size()]);
  }
 
  private Control[] getControlsNear(Control control, Position position) {
    if (control == null) {
      throw new NullPointerException();
    }
   
    if (position == null) {
      throw new NullPointerException();
    }
   
    Bounds bounds = Bounds.fromRectangle(control.getBounds());
    XYComparator comp = XYComparator.getInstance(position.isVertical() ? Orientation.HORIZONTAL : Orientation.VERTICAL);
    SortedSet<Control> controlsNear = new TreeSet<Control>(comp);
    Control[] controls = getControls();
   
    for (Control aControl : controls) {
      if (aControl == control) {
        continue;
      }
     
      Bounds aBounds = Bounds.fromRectangle(aControl.getBounds());

      if (position.isVertical()) {
        if (aBounds.getLeft() <= bounds.getRight() && aBounds.getRight() >= bounds.getLeft()) {
          if (position == Position.TOP) {
            if (aBounds.getBottom() + BAR_SIZE == bounds.getTop()) {
              controlsNear.add(aControl);
            }
          } else if (position == Position.BOTTOM) {
            if (bounds.getBottom() + BAR_SIZE == aBounds.getTop()) {
              controlsNear.add(aControl);
            }
          }
        }
      } else {
        if (aBounds.getTop() <= bounds.getBottom() && aBounds.getBottom() >= bounds.getTop()) {
          if (position == Position.LEFT) {
            if (aBounds.getRight() + BAR_SIZE == bounds.getLeft()) {
              controlsNear.add(aControl);
            }
          } else if (position == Position.RIGHT) {
            if (bounds.getRight() + BAR_SIZE == aBounds.getLeft()) {
              controlsNear.add(aControl);
            }
          }
        }
      }
    }
   
    return controlsNear.toArray(new Control[controlsNear.size()]);
  }
 
  private Sizer[] getSizersAt(int x, int y) {
    XYComparator xComp = XYComparator.getInstance(Orientation.HORIZONTAL);
    SortedSet<Sizer> sizersAt = new TreeSet<Sizer>(xComp);
    Sizer[] sizers = getSizers();
   
    for (Sizer sizer : sizers) {
      if (sizer.getBounds().contains(x, y)) {
        sizersAt.add(sizer);
      }
    }
   
    return sizersAt.toArray(new Sizer[sizersAt.size()]);
  }
 
  private Group[] getGroupsAt(int x, int y) {
    Sizer[] sizers = getSizersAt(x, y);
    XYComparator xComp = XYComparator.getInstance(Orientation.HORIZONTAL);
    SortedSet<Group> groupsAt = new TreeSet<Group>(xComp);

    for (Sizer sizer : sizers) {
      Group[] groups = sizer.getGroups();
     
      for (Group group : groups) {
        groupsAt.add(group);
      }
    }
   
    return groupsAt.toArray(new Group[groupsAt.size()]);
  }
 
  private void updateCursor(int x, int y) {
    int id = SWT.CURSOR_ARROW;
   
    if (dragInfo != null) {
      DragGroupInfo[] groupInfos = dragInfo.getGroupInfos();
     
      if (groupInfos.length == 1) {
        Group group = groupInfos[0].getGroup();
        id = group.isVertical() ? SWT.CURSOR_SIZEWE : SWT.CURSOR_SIZENS;
      } else if (groupInfos.length > 1) {
        id = SWT.CURSOR_SIZEALL;
      }
    } else {
      Sizer[] sizers = getSizers();
     
      for (Sizer sizer : sizers) {
        Rectangle bounds = sizer.getBounds();
       
        if (bounds.contains(x, y)) {
          if (sizer instanceof Grip) {
            id = SWT.CURSOR_SIZEALL;
          } else if (sizer instanceof Split) {
            Split split = (Split )sizer;
            id = split.isVertical() ? SWT.CURSOR_SIZEWE : SWT.CURSOR_SIZENS;
          }
         
          break;
        }
      }
    }
   
    Cursor cursor = getDisplay().getSystemCursor(id);
    setCursor(cursor);
  }
 
  private Map<Control, Bounds> computeLayoutBounds(Bounds newClient) throws InsufficientControlSizeException {
    XYComparator xComp = XYComparator.getInstance(Orientation.HORIZONTAL);
    XYComparator yComp = XYComparator.getInstance(Orientation.VERTICAL);
    Control[] controls = getControls();
    Arrays.sort(controls, xComp);
    SortedMap<Control, SortedSet<Control>> xNexts = new TreeMap<Control, SortedSet<Control>>(xComp);
    SortedMap<Control, SortedSet<Control>> yNexts = new TreeMap<Control, SortedSet<Control>>(yComp);
    SortedSet<Control> xEnds = new TreeSet<Control>(yComp);
    SortedSet<Control> yEnds = new TreeSet<Control>(xComp);
    Map<Control, Bounds> controlBounds = new HashMap<Control, Bounds>();
   
    for (Control control : controls) {
      Bounds bounds = Bounds.fromRectangle(control.getBounds());
      controlBounds.put(control, bounds);
    }
   
    for (Control control : controls) {
      Bounds bounds = controlBounds.get(control);
     
      if (bounds.getRight() == oldClient.getRight()) {
        xEnds.add(control);
      } else for (Control x : controls) {
        if (x == control) {
          continue;
        }
       
        Bounds xBounds = controlBounds.get(x);
       
        if (bounds.getTop() > xBounds.getBottom() || bounds.getBottom() < xBounds.getTop()) {
          continue;
        }
       
        if (bounds.getRight() + BAR_SIZE != xBounds.getLeft()) {
          continue;
        }
       
        SortedSet<Control> xNext = xNexts.get(control);
       
        if (xNext == null) {
          xNext = new TreeSet<Control>(yComp);
          xNexts.put(control, xNext);
        }
       
        xNext.add(x);
      }
     
      if (bounds.getBottom() == oldClient.getBottom()) {
        yEnds.add(control);
      } else for (Control y : controls) {
        if (y == control) {
          continue;
        }
       
        Bounds yBounds = controlBounds.get(y);
       
        if (bounds.getLeft() > yBounds.getRight() || bounds.getRight() < yBounds.getLeft()) {
          continue;
        }
       
        if (bounds.getBottom() + BAR_SIZE != yBounds.getTop()) {
          continue;
        }
       
        SortedSet<Control> yNext = yNexts.get(control);
       
        if (yNext == null) {
          yNext = new TreeSet<Control>(xComp);
          yNexts.put(control, yNext);
        }
       
        yNext.add(y);
      }
    }
   
    for (Control control : controls) {
      Bounds bounds = controlBounds.get(control);
     
      if (bounds.getLeft() > oldClient.getLeft()) {
        bounds.setWidth(bounds.getWidth() + BAR_SIZE / 2, false);
      }

      if (bounds.getTop() > oldClient.getTop()) {
        bounds.setHeight(bounds.getHeight() + BAR_SIZE / 2, false);
      }

      if (bounds.getRight() < oldClient.getRight()) {
        bounds.setWidth(bounds.getWidth() + BAR_SIZE / 2, true);
      }
     
      if (bounds.getBottom() < oldClient.getBottom()) {
        bounds.setHeight(bounds.getHeight() + BAR_SIZE / 2, true);
      }
    }
   
    float xProportion = (float )newClient.getWidth() / (float )oldClient.getWidth();
    float yProportion = (float )newClient.getHeight() / (float )oldClient.getHeight();
   
    for (Control control : controls) {
      Bounds bounds = controlBounds.get(control);
      bounds.setWidth(Math.round(bounds.getWidth() * xProportion), true);
      bounds.setHeight(Math.round(bounds.getHeight() * yProportion), true);
    }
   
    for (Control x : xNexts.keySet()) {
      Bounds xBounds = controlBounds.get(x);
      SortedSet<Control> nexts = xNexts.get(x);
     
      for (Control next : nexts) {
        Bounds nextBounds = controlBounds.get(next);
        nextBounds.setLeft(xBounds.getRight(), true);
      }
    }
   
    for (Control y : yNexts.keySet()) {
      Bounds yBounds = controlBounds.get(y);
      SortedSet<Control> nexts = yNexts.get(y);
     
      for (Control next : nexts) {
        Bounds nextBounds = controlBounds.get(next);
        nextBounds.setTop(yBounds.getBottom(), true);
      }
    }
   
    for (Control x : xEnds) {
      Bounds xBounds = controlBounds.get(x);
      int diff = newClient.getRight() - xBounds.getRight();
      xBounds.setWidth(xBounds.getWidth() + diff, true);
    }
   
    for (Control y: yEnds) {
      Bounds yBounds = controlBounds.get(y);
      int diff = newClient.getBottom() - yBounds.getBottom();
      yBounds.setHeight(yBounds.getHeight() + diff, true);
    }
   
    for (Control control : controls) {
      Bounds bounds = controlBounds.get(control);
     
      if (bounds.getLeft() > newClient.getLeft()) {
        bounds.setWidth(bounds.getWidth() - BAR_SIZE / 2, false);
      }
     
      if (bounds.getTop() > newClient.getTop()) {
        bounds.setHeight(bounds.getHeight() - BAR_SIZE / 2, false);
      }

      if (bounds.getRight() < newClient.getRight()) {
        bounds.setWidth(bounds.getWidth() - BAR_SIZE / 2, true);
      }
     
      if (bounds.getBottom() < newClient.getBottom()) {
        bounds.setHeight(bounds.getHeight() - BAR_SIZE / 2, true);
      }
    }
   
    return controlBounds;
  }
 
  private Map<Control, Rectangle> computeDockBounds(Control control, Specification specification) throws InsufficientControlSizeException, ControlAlreadyDockedException, NoSuchReferenceControlException {
    if (control == null) {
      throw new NullPointerException("Control is null.");
    }
   
    Composite parent = control.getParent();
   
    if (parent != this) {
      throw new SWTException(SWT.ERROR_INVALID_PARENT, MessageFormat.format("Cannot compute bounds for control {0}.", control));
    }
   
    if (controls.contains(control)) {
      throw new ControlAlreadyDockedException(MessageFormat.format("The control {0} is already docked.", control));
    }
   
    Map<Control, Rectangle> controlBounds = new HashMap<Control, Rectangle>();
   
    if (controls.isEmpty()) {
      Rectangle newBounds = getClientArea();
     
      if (newBounds.width < MIN_SIZE) {
        throw new InsufficientControlSizeException(MessageFormat.format("Insufficient width for the control {0}.", control));
      }
     
      if (newBounds.height < MIN_SIZE) {
        throw new InsufficientControlSizeException(MessageFormat.format("Insufficient height for the control {0}.", control));
      }
     
      controlBounds.put(control, newBounds);
    } else {
      if (specification == null) {
        throw new NullPointerException("Specification is null.");
      }

      Control reference = specification.getReference();
     
      if (!controls.contains(reference)) {
        throw new NoSuchReferenceControlException(MessageFormat.format("No such reference control {0}.", reference));
      }
     
      Rectangle refBounds = reference.getBounds();
      Rectangle newBounds = new Rectangle(refBounds.x, refBounds.y, refBounds.width, refBounds.height);
      Rectangle oldBounds = new Rectangle(refBounds.x, refBounds.y, refBounds.width, refBounds.height);
     
      if (specification.isVertical()) {
        int height = refBounds.height - BAR_SIZE;
        newBounds.height = Math.round(height * specification.getProportion());
        oldBounds.height = height - newBounds.height;
      } else {
        int width = refBounds.width - BAR_SIZE;
        newBounds.width = Math.round(width * specification.getProportion());
        oldBounds.width = width - newBounds.width;
      }

      Position position = specification.getPosition();
     
      if (position == Position.TOP) {
        oldBounds.y = refBounds.y + refBounds.height - oldBounds.height;
      } else if (position == Position.RIGHT) {
        newBounds.x = refBounds.x + refBounds.width - newBounds.width;
      } else if (position == Position.BOTTOM) {
        newBounds.y = refBounds.y + refBounds.height - newBounds.height;
      } else {
        oldBounds.x = refBounds.x + refBounds.width - oldBounds.width;
      }
     
      if (newBounds.width < MIN_SIZE) {
        throw new InsufficientControlSizeException(MessageFormat.format("Insufficient width for the control {0}.", control));
      }
     
      if (newBounds.height < MIN_SIZE) {
        throw new InsufficientControlSizeException(MessageFormat.format("Insufficient height for the control {0}.", control));
      }
     
      if (oldBounds.width < MIN_SIZE) {
        throw new InsufficientControlSizeException(MessageFormat.format("Insufficient width for the control {0}.", reference));
      }
     
      if (oldBounds.height < MIN_SIZE) {
        throw new InsufficientControlSizeException(MessageFormat.format("Insufficient height for the control {0}.", reference));
      }
     
      controlBounds.put(control, newBounds);
      controlBounds.put(reference, oldBounds);
    }
   
    return controlBounds;
  }
 
  public static void main(String[] args) throws Exception {
    Display display = Display.getDefault();
    final Shell shell = new Shell(display);
    final DockContainer container = new DockContainer(shell, SWT.NONE);
    shell.addListener(SWT.Resize, new Listener() {
      @Override
      public void handleEvent(Event event) {
        container.setMinimumShellSize(shell);
      }
    });
    shell.setLayout(new FillLayout());
    shell.setSize(1366, 768);
    {
      Listener listener = new Listener() {
        @Override
        public void handleEvent(Event event) {
//          container.undock((Control )event.widget);
          UI.dispose(event.widget);
        }
      };
     
      /*Button view0 = new Button(container, SWT.NONE);
      view0.setText("View 0");
      container.dock(view0, null);
     
      Button view1 = new Button(container, SWT.NONE);
      view1.setText("View 1");
      container.dock(view1, new Specification(view0, Position.RIGHT, 0.75f));
     
      Button view2 = new Button(container, SWT.NONE);
      view2.setText("View 2");
      container.dock(view2, new Specification(view1, Position.RIGHT, 0.667f));
     
      Button view3 = new Button(container, SWT.NONE);
      view3.setText("View 3");
      container.dock(view3, new Specification(view2, Position.RIGHT, 0.5f));*/
     
      /*Button view0 = new Button(container, SWT.NONE);
      view0.setText("View 0");
      container.dock(view0, null);
     
      Button view1 = new Button(container, SWT.NONE);
      view1.setText("View 1");
      container.dock(view1, new Specification(view0, Position.RIGHT, 0.3f));
     
      Button view2 = new Button(container, SWT.NONE);
      view2.setText("View 2");
      container.dock(view2, new Specification(view1, Position.BOTTOM, 0.6f));*/
     
      /*Button view0 = new Button(container, SWT.NONE);
      view0.setText("View 0");
      container.dock(view0, null);
     
      Button view1 = new Button(container, SWT.NONE);
      view1.setText("View 1");
      container.dock(view1, new Specification(view0, Position.BOTTOM, 0.5f));
     
      Button view2 = new Button(container, SWT.NONE);
      view2.setText("View 2");
      container.dock(view2, new Specification(view0, Position.LEFT, 0.497f));
     
      Button view3 = new Button(container, SWT.NONE);
      view3.setText("View 3");
      container.dock(view3, new Specification(view1, Position.RIGHT, 0.497f));*/
     
      Button view0 = new Button(container, SWT.NONE);
      view0.setText("View 0");
      view0.addListener(SWT.Selection, listener);
      container.dock(view0, null);
     
      Button view1 = new Button(container, SWT.NONE);
      view1.setText("View 1");
      view1.addListener(SWT.Selection, listener);
      container.dock(view1, new Specification(view0, Position.BOTTOM, 0.5f));
     
      Button view2 = new Button(container, SWT.NONE);
      view2.setText("View 2");
      view2.addListener(SWT.Selection, listener);
      container.dock(view2, new Specification(view0, Position.LEFT, 0.2f));
     
      Button view3 = new Button(container, SWT.NONE);
      view3.setText("View 3");
      view3.addListener(SWT.Selection, listener);
      container.dock(view3, new Specification(view1, Position.RIGHT, 0.2f));
     
      Button view4 = new Button(container, SWT.NONE);
      view4.setText("View 4");
      view4.addListener(SWT.Selection, listener);
      container.dock(view4, new Specification(view0, Position.LEFT, 0.3f));
     
      Button view5 = new Button(container, SWT.NONE);
      view5.setText("View 5");
      view5.addListener(SWT.Selection, listener);
      container.dock(view5, new Specification(view1, Position.RIGHT, 0.3f));
     
      /*Button view0 = new Button(container, SWT.NONE);
      view0.setText("View 0");
      container.dock(view0, null);
     
      Button view1 = new Button(container, SWT.NONE);
      view1.setText("View 1");
      container.dock(view1, new Specification(view0, Position.BOTTOM, 0.5f));
     
      Button view2 = new Button(container, SWT.NONE);
      view2.setText("View 2");
      container.dock(view2, new Specification(view0, Position.LEFT, 0.5f));
     
      Button view3 = new Button(container, SWT.NONE);
      view3.setText("View 3");
      container.dock(view3, new Specification(view1, Position.RIGHT, 0.5f));
     
      Button view4 = new Button(container, SWT.NONE);
      view4.setText("View 4");
      container.dock(view4, new Specification(view3, Position.RIGHT, 0.5f));*/
    }
    container.setMinimumShellSize(shell);
    shell.open();

    while (!shell.isDisposed()) {
      if (!display.readAndDispatch()) {
        display.sleep();
      }
    }
  }
}
TOP

Related Classes of com.peterhi.ui.obsolete.DockContainer$Grip

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.
, 'pageview');