Package com.peterhi.ui

Source Code of com.peterhi.ui.Surrounds

package com.peterhi.ui;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
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.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;

import com.peterhi.Util;

public final class ViewPanel extends Composite {

  public static final String ID = "id";
  public static final String TITLE = "title";
  public static final String DESCRIPTION = "description";
  public static final String ICON = "icon";
  public static final String REF_VIEW = "refView";
  public static final String POSITION = "position";
  public static final String EXTENT = "extent";
  public static final String BOUNDS = "bounds";
 
  static final int MIN_EXTENT = 100;
 
  private static final String ITEM = "item";
 
  public static enum Position {
    TAB_AFTER,
    TAB_BEFORE,
   
    SPLIT_LEFT,
    SPLIT_TOP,
    SPLIT_RIGHT,
    SPLIT_BOTTOM,
   
    DOCK_LEFTMOST,
    DOCK_TOPMOST,
    DOCK_RIGHTMOST,
    DOCK_BOTTOMMOST,
   
    AUTOHIDE_WEST,
    AUTOHIDE_NORTH,
    AUTOHIDE_EAST,
    AUTOHIDE_SOUTH,
   
    FLOAT;
   
    public boolean isTabbed() {
      return this == TAB_AFTER || this == TAB_BEFORE;
    }
   
    public boolean isSplit() {
      return this == SPLIT_LEFT || this == SPLIT_TOP ||
        this == SPLIT_RIGHT || this == SPLIT_BOTTOM;
    }
   
    public boolean isDocked() {
      return this == DOCK_LEFTMOST || this == DOCK_TOPMOST ||
        this == DOCK_RIGHTMOST || this == DOCK_BOTTOMMOST;
    }
   
    public boolean isAutohidden() {
      return this == AUTOHIDE_WEST || this == AUTOHIDE_NORTH ||
        this == AUTOHIDE_EAST || this == AUTOHIDE_SOUTH;
    }
   
    public boolean isFloated() {
      return this == FLOAT;
    }
   
    public boolean isPositiveDirection() {
      return this == TAB_AFTER || this == SPLIT_RIGHT ||
        this == SPLIT_BOTTOM || this == DOCK_RIGHTMOST ||
        this == DOCK_BOTTOMMOST || this == AUTOHIDE_EAST ||
        this == AUTOHIDE_SOUTH;
    }
   
    public boolean isNegativeDirection() {
      return this == TAB_BEFORE || this == SPLIT_LEFT ||
        this == SPLIT_TOP || this == DOCK_LEFTMOST ||
        this == DOCK_TOPMOST || this == AUTOHIDE_WEST ||
        this == AUTOHIDE_NORTH;
    }
   
    public boolean isHorizontal() {
      return this == SPLIT_LEFT || this == SPLIT_RIGHT ||
        this == DOCK_LEFTMOST || this == DOCK_RIGHTMOST ||
        this == AUTOHIDE_WEST || this == AUTOHIDE_EAST;
    }
   
    public boolean isVertical() {
      return this == SPLIT_TOP || this == SPLIT_BOTTOM ||
        this == DOCK_TOPMOST || this == DOCK_BOTTOMMOST ||
        this == AUTOHIDE_NORTH || this == AUTOHIDE_SOUTH;
    }
   
    public boolean isLeft() {
      return isHorizontal() && isNegativeDirection();
    }
   
    public boolean isTop() {
      return isVertical() && isNegativeDirection();
    }
   
    public boolean isRight() {
      return isHorizontal() && isPositiveDirection();
    }
   
    public boolean isBottom() {
      return isVertical() && isPositiveDirection();
    }
  }
 
  public static void main(String[] args) {
    Display display = Display.getDefault();
    Shell shell = new Shell(display);
    shell.setLayout(new FillLayout());
    ViewPanel panel = new ViewPanel(shell, SWT.NONE);
   
    shell.setSize(1152, 640);
   
    Label view0 = new Label(panel, SWT.NONE);
    {
      view0.setText("View 0");
      view0.setData(ID, "view0");
      view0.setData(TITLE, "View 0");
      view0.setData(DESCRIPTION, "Description of view0.");
      view0.setData(ICON, UiImages.T.about16());
      panel.add(view0);
    }
   
    Label view1 = new Label(panel, SWT.NONE);
    {
      view1.setText("View 1");
      view1.setData(ID, "view1");
      view1.setData(TITLE, "View 1");
      view1.setData(DESCRIPTION, "Description of view1.");
      view1.setData(REF_VIEW, view0);
      view1.setData(POSITION, Position.SPLIT_BOTTOM);
      panel.add(view1);
    }
   
    Label view2 = new Label(panel, SWT.NONE);
    {
      view2.setText("View 2");
      view2.setData(ID, "view2");
      view2.setData(TITLE, "View 2");
      view2.setData(DESCRIPTION, "Description of view2");
      view2.setData(REF_VIEW, view0);
      view2.setData(POSITION, Position.SPLIT_RIGHT);
      panel.add(view2);
    }
   
    Label view3 = new Label(panel, SWT.NONE);
    {
      view3.setText("View 3");
      view3.setData(ID, "view3");
      view3.setData(TITLE, "View 3");
      view3.setData(DESCRIPTION, "Description of view3");
      view3.setData(REF_VIEW, view1);
      view3.setData(POSITION, Position.SPLIT_LEFT);
      view3.setData(EXTENT, "0.33");
      panel.add(view3);
    }
   
    Label view4 = new Label(panel, SWT.NONE);
    {
      view4.setText("View 4");
      view4.setData(ID, "view4");
      view4.setData(TITLE, "View 4");
      view4.setData(DESCRIPTION, "Description of view4");
      view4.setData(REF_VIEW, view1);
      view4.setData(POSITION, Position.SPLIT_RIGHT);
      view4.setData(EXTENT, "0.50");
      panel.add(view4);
    }

    /*Label view0 = new Label(panel, SWT.NONE);
    {
      view0.setText("View 0");
      view0.setData(ID, "view0");
      view0.setData(TITLE, "View 0");
      view0.setData(DESCRIPTION, "Description of view 0.");
      view0.setData(ICON, UiImages.T.about16());
      panel.add(view0);
    }
   
    Label view1 = new Label(panel, SWT.NONE);
    {
      view1.setText("View 1");
      view1.setData(ID, "view1");
      view1.setData(TITLE, "View 1");
      view1.setData(DESCRIPTION, "Description of view 1.");
      view1.setData(REF_VIEW, view0);
      panel.add(view1);
    }
   
    Label view2 = new Label(panel, SWT.NONE);
    {
      view2.setText("View 2");
      view2.setData(ID, "view2");
      view2.setData(TITLE, "View 2");
      view2.setData(DESCRIPTION, "Description of view 2.");
      view2.setData(REF_VIEW, view1);
      view2.setData(POSITION, Position.TAB_BEFORE);
      panel.add(view2);
    }
   
    Label view3 = new Label(panel, SWT.NONE);
    {
      view3.setText("View 3");
      view3.setData(ID, "view3");
      view3.setData(TITLE, "View 3");
      view3.setData(DESCRIPTION, "Description of view 3.");
      view3.setData(REF_VIEW, view1);
      view3.setData(POSITION, Position.SPLIT_BOTTOM);
      view3.setData(EXTENT, "0.3");
      panel.add(view3);
    }
   
    Label view4 = new Label(panel, SWT.NONE);
    {
      view4.setText("View 4");
      view4.setData(ID, "view4");
      view4.setData(TITLE, "View 4");
      view4.setData(DESCRIPTION, "Description of view 4.");
      view4.setData(REF_VIEW, view1);
      view4.setData(POSITION, Position.SPLIT_RIGHT);
      view4.setData(EXTENT, "0.3");
      panel.add(view4);
    }
   
    Label view5 = new Label(panel, SWT.NONE);
    {
      view5.setText("View 5");
      view5.setData(ID, "view5");
      view5.setData(TITLE, "View 5");
      view5.setData(DESCRIPTION, "Description of view 5.");
      view5.setData(REF_VIEW, view2);
      view5.setData(POSITION, Position.SPLIT_LEFT);
      view5.setData(EXTENT, "0.4");
      panel.add(view5);
    }
   
    Label view6 = new Label(panel, SWT.NONE);
    {
      view6.setText("View 6");
      view6.setData(ID, "view6");
      view6.setData(TITLE, "View 6");
      view6.setData(DESCRIPTION, "Description of view 6.");
      view6.setData(REF_VIEW, view2);
      view6.setData(POSITION, Position.SPLIT_TOP);
      panel.add(view6);
    }*/
   
    shell.open();
   
    while (!shell.isDisposed()) {
      if (!display.readAndDispatch()) {
        display.sleep();
      }
    }
  }
 
  private final SplitDockPanel centerPanel;
  private final AutohidePanel leftPanel;
  private final AutohidePanel topPanel;
  private final AutohidePanel rightPanel;
  private final AutohidePanel bottomPanel;
  private int spacing;
 
  private Control activeView;
 
  public ViewPanel(Composite parent, int style) {
    super(parent, style);
   
    centerPanel = new SplitDockPanel(this, SWT.CENTER);
    leftPanel = new AutohidePanel(this, SWT.LEFT);
    topPanel = new AutohidePanel(this, SWT.TOP);
    rightPanel = new AutohidePanel(this, SWT.RIGHT);
    bottomPanel = new AutohidePanel(this, SWT.BOTTOM);
   
    spacing = 10;
   
    addListener(SWT.Resize, new Listener() {
      @Override
      public void handleEvent(Event event) {
        layout();
      }
    });
  }
 
  @Override
  public void layout(boolean changed) {
    Rectangle c = getClientArea();
   
    Point l = leftPanel.computeSize(SWT.DEFAULT, SWT.DEFAULT);
    Point t = topPanel.computeSize(SWT.DEFAULT, SWT.DEFAULT);
    Point r = rightPanel.computeSize(SWT.DEFAULT, SWT.DEFAULT);
    Point b = bottomPanel.computeSize(SWT.DEFAULT, SWT.DEFAULT);
   
    centerPanel.setBounds(c.x + l.x, c.y + t.y, c.width - l.x - r.x,
      c.height - t.y - b.y);
    leftPanel.setBounds(c.x, c.y + t.y, l.x, c.height - t.y - b.y);
    topPanel.setBounds(c.x + l.x, c.y, c.width - l.x - r.x, t.y);
    rightPanel.setBounds(c.x + c.width - r.x, c.y + t.y, r.x,
      c.height - t.y - b.y);
    bottomPanel.setBounds(c.x + l.x, c.y + c.height - b.y,
      c.width - l.x - r.x, b.y);
  }
 
  @Override
  public void setLayout(Layout layout) {
  }
 
  @Override
  public Point computeSize(int wHint, int hHint, boolean changed) {
    Point size = super.computeSize(wHint, hHint, changed);
    Point min = new Point(0, 0);
   
    Point l = leftPanel.computeSize(SWT.DEFAULT, SWT.DEFAULT);
    Point t = topPanel.computeSize(SWT.DEFAULT, SWT.DEFAULT);
    Point r = rightPanel.computeSize(SWT.DEFAULT, SWT.DEFAULT);
    Point b = bottomPanel.computeSize(SWT.DEFAULT, SWT.DEFAULT);
   
    min.x += l.x + r.x;
    min.y += t.y + b.y;
   
    if (size.x < min.x) {
      size.x = min.x;
    }
   
    if (size.y < min.y) {
      size.y = min.y;
    }
   
    return size;
  }
 
  public boolean add(Control view) {
    if (contains(view)) {
      setActiveView(view);
      return false;
    }
   
    String id = (String )view.getData(ID);
    String title = (String )view.getData(TITLE);
    String description = (String )view.getData(DESCRIPTION);
    Image icon = (Image )view.getData(ICON);
    Control refView = (Control )view.getData(REF_VIEW);
    Position position = (Position )view.getData(POSITION);
    String extentExpr = (String )view.getData(EXTENT);
    String boundsExpr = (String )view.getData(BOUNDS);
   
    if (Util.isEmpty(id)) {
      return false;
    }
   
    if (title == null) {
      title = id;
    }

    if (description == null) {
      description = "";
    }
   
    if (icon == null) {
      icon = UiImages.T.defaultView();
    }
   
    if (refView == null) {
      refView = getActiveView();
    }
   
    if (position == null) {
      position = Position.TAB_AFTER;
    }
   
    if (Util.isEmpty(extentExpr)) {
      extentExpr = "0.5";
    }
   
    Extent extent = new Extent(extentExpr);
   
    Bounds bounds = null;
   
    if (!Util.isEmpty(boundsExpr)) {
      bounds = new Bounds(boundsExpr);
    }
   
    if (isEmpty()) {
      addInitialView(view, title, description, icon);
    } else if (position.isTabbed()) {
      addTabbedView(view, refView, position, title, description, icon);
    } else if (position.isSplit()) {
      addSplitView(view, refView, position, extent, title,
        description, icon);
    }
   
    view.setData(TITLE, null);
    view.setData(DESCRIPTION, null);
    view.setData(ICON, null);
    view.setData(REF_VIEW, null);
    view.setData(POSITION, null);
    view.setData(EXTENT, null);
    view.setData(BOUNDS, null);
    setActiveView(view);
    return true;
  }
 
  public Control getActiveView() {
    return activeView;
  }
 
  public boolean setActiveView(Control view) {
    Object data = view.getData(ITEM);
   
    if (data instanceof CTabItem) {
      CTabItem item = (CTabItem )data;
      CTabFolder folder = item.getParent();
      folder.setSelection(item);
      return true;
    }
   
    return false;
  }
 
  public int getSpacing() {
    return spacing;
  }
 
  private boolean isEmpty() {
    Control[] views = getViews();
    return Util.isEmpty(views);
  }
 
  private boolean contains(Control v) {
    Control[] views = getViews();
   
    for (Control view : views) {
      if (view == v) {
        return true;
      }
    }
   
    return false;
  }
 
  private Control[] getViews() {
    List<Control> views = new ArrayList<Control>();
    Control[] children = centerPanel.getChildren();
   
    for (Control child : children) {
      TabPanel panel = (TabPanel )child;
      CTabFolder folder = panel.getTabFolder();
      CTabItem[] items = folder.getItems();
     
      for (CTabItem item : items) {
        Control control = item.getControl();
        views.add(control);
      }
    }
   
    return views.toArray(new Control[views.size()]);
  }
 
  private boolean addInitialView(Control view, String title,
    String description, Image icon) {
    Rectangle clientArea = centerPanel.getClientArea();
    TabPanel tab = new TabPanel(centerPanel, SWT.NONE);
    tab.setBounds(clientArea);
    addAfter(tab, view, null, title, description, icon);
    return true;
  }
 
  private boolean addTabbedView(Control view, Control refView,
    Position position, String title, String description, Image icon) {
    if (refView == null) {
      return false;
    }
   
    CTabItem item = (CTabItem )refView.getData(ITEM);
   
    if (item == null) {
      return false;
    }
   
    CTabFolder folder = item.getParent();
    TabPanel panel = (TabPanel )folder.getParent();
   
    if (position.isNegativeDirection()) {
      addBefore(panel, view, refView, title, description, icon);
    } else {
      addAfter(panel, view, refView, title, description, icon);
    }
   
    return true;
  }
 
  private boolean addSplitView(Control view, Control refView,
    Position position, Extent extent, String title, String description,
    Image icon) {
    CTabItem refItem = (CTabItem )refView.getData(ITEM);
    CTabFolder refFolder = refItem.getParent();
    TabPanel refPanel = (TabPanel )refFolder.getParent();
    Rectangle full = refPanel.getBounds();
    Rectangle old = new Rectangle(full.x, full.y, full.width, full.height);
    Rectangle neo = new Rectangle(full.x, full.y, full.width, full.height);

    int size = position.isVertical() ? full.height : full.width;
    size -= spacing;
    size = extent.getValue(size);
   
    if (size < MIN_EXTENT) {
      size = MIN_EXTENT;
    }
   
    if (position.isLeft()) {
      old.x += spacing + size;
      old.width -= spacing + size;
      neo.width = size;
    } else if (position.isTop()) {
      old.y += spacing + size;
      old.height -= spacing + size;
      neo.height = size;
    } else if (position.isBottom()) {
      old.height -= spacing + size;
      neo.y += old.height + spacing;
      neo.height = size;
    } else {
      old.width -= spacing + size;
      neo.x += old.width + spacing;
      neo.width = size;
    }
   
    if (old.width < MIN_EXTENT || old.height < MIN_EXTENT ||
      neo.width < MIN_EXTENT || neo.height < MIN_EXTENT) {
      return false;
    }
   
    TabPanel newPanel = new TabPanel(centerPanel, SWT.NONE);
    add(newPanel, view, title, description, icon, -1);
    refPanel.setBounds(old);
    newPanel.setBounds(neo);
    centerPanel.layout();
    return true;
  }
 
  private CTabItem addBefore(TabPanel tab, Control view, Control refView,
    String title, String description, Image icon) {
    CTabFolder folder = tab.getTabFolder();
    CTabItem item = (CTabItem )refView.getData(ITEM);
    int count = folder.getItemCount();
    int index = -1;
   
    if (item != null) {
      index = folder.indexOf(item);
    }
   
    if (index < 0) {
      index = count;
    }
   
    return add(tab, view, title, description, icon, index);
  }
 
  private CTabItem addAfter(TabPanel tab, Control view, Control refView,
    String title, String description, Image icon) {
    CTabFolder folder = tab.getTabFolder();
    CTabItem item = null;
   
    if (refView != null) {
      item = (CTabItem )refView.getData(ITEM);
    }
   
    int count = folder.getItemCount();
    int index = -1;
   
    if (item != null) {
      index = folder.indexOf(item);
    }
   
    if (index < 0) {
      index = count;
    }
   
    return add(tab, view, title, description, icon, index + 1);
  }
 
  private CTabItem add(TabPanel tab, Control view, String title,
    String description, Image icon, int index) {
    CTabFolder folder = tab.getTabFolder();
    int count = folder.getItemCount();
   
    if (index < 0 || index > count) {
      index = count;
    }
   
    CTabItem item = new CTabItem(folder, SWT.CLOSE, index);
    item.setText(title);
    item.setToolTipText(description);
    item.setImage(icon);
    view.setParent(folder);
    item.setControl(view);
    view.setData(ITEM, item);
    return item;
  }
}

final class AutohidePanel extends Composite {

  public AutohidePanel(Composite parent, int style) {
    super(parent, style);
    addListener(SWT.Resize, new Listener() {
      @Override
      public void handleEvent(Event event) {
        layout();
      }
    });
  }
 
  @Override
  public ViewPanel getParent() {
    return (ViewPanel )super.getParent();
  }
 
  @Override
  public boolean setParent(Composite parent) {
    if (!(parent instanceof ViewPanel)) {
      return false;
    }
   
    return super.setParent(parent);
  }
 
  @Override
  public void layout(boolean changed) {
  }
 
  @Override
  public void setLayout(Layout layout) {
  }
 
  @Override
  public Point computeSize(int wHint, int hHint, boolean changed) {
    Point size = super.computeSize(wHint, hHint, changed);
   
    if (isHorizontal()) {
      size.y = 2;
    } else if (isVertical()) {
      size.x = 2;
    }
   
    return size;
  }
 
  public boolean isLeft() {
    int style = getStyle();
    return (style & SWT.LEFT) == SWT.LEFT;
  }
 
  public boolean isTop() {
    int style = getStyle();
    return (style & SWT.TOP) == SWT.TOP;
  }
 
  public boolean isRight() {
    int style = getStyle();
    return (style & SWT.RIGHT) == SWT.RIGHT;
  }
 
  public boolean isBottom() {
    int style = getStyle();
    return (style & SWT.BOTTOM) == SWT.BOTTOM;
  }
 
  public boolean isHorizontal() {
    if (isTop()) {
      return true;
    }

    if (isBottom()) {
      return true;
    }
   
    int style = getStyle();
    return (style & SWT.HORIZONTAL) == SWT.HORIZONTAL;
  }
 
  public boolean isVertical() {
    if (isLeft()) {
      return true;
    }
   
    if (isRight()) {
      return true;
    }
   
    int style = getStyle();
    return (style & SWT.VERTICAL) == SWT.VERTICAL;
  }
}

final class SplitDockPanel extends Composite {

  private Rectangle oldClientArea;
  private Set<LWSplitBar> splitBars;
  private Set<LWSplitCorner> splitCorners;
 
  private Point down;
  private Point offset;
  private LWSplitGroup[] groups;
  private Set<TabPanel>[] affected;
  private Set<Rectangle>[] collided;
  private Map<TabPanel, Rectangle> downCache;
  private Integer minX;
  private Integer minY;
  private Integer maxX;
  private Integer maxY;
 
  public SplitDockPanel(ViewPanel parent, int style) {
    super(parent, style);
   
    addListener(SWT.Resize, new Listener() {
      @Override
      public void handleEvent(Event event) {
        layout();
      }
    });
   
    Listener cursorListener = new Listener() {
      @Override
      public void handleEvent(Event event) {
        if (down != null) {
          return;
        }
       
        Display d = event.display;
        LWSplitControl splitControl =
          findSplitControl(event.x, event.y);
       
        if (splitControl instanceof LWSplitCorner) {
          Cursor cursor = d.getSystemCursor(SWT.CURSOR_SIZEALL);
          setCursor(cursor);
        } else if (splitControl instanceof LWSplitBar) {
          LWSplitBar splitBar = (LWSplitBar )splitControl;
          Cursor cursor = d.getSystemCursor(splitBar.isVertical() ?
            SWT.CURSOR_SIZEWE : SWT.CURSOR_SIZENS);
          setCursor(cursor);
        }
      }
    };
    addListener(SWT.MouseEnter, cursorListener);
    addListener(SWT.MouseMove, cursorListener);
    addListener(SWT.MouseExit, new Listener() {
      @Override
      public void handleEvent(Event event) {
        Display d = event.display;
        Cursor cursor = d.getSystemCursor(SWT.CURSOR_ARROW);
        setCursor(cursor);
        groups = null;
      }
    });
   
    addListener(SWT.MouseDown, new Listener() {
      @SuppressWarnings("unchecked")
      @Override
      public void handleEvent(Event event) {
        LWSplitControl splitControl = findSplitControl(event.x, event.y);
        Rectangle cBounds = splitControl.bounds();
        down = new Point(event.x, event.y);
        offset = new Point(event.x - cBounds.x, event.y - cBounds.y);

        if ((event.stateMask & SWT.ALT) == SWT.ALT) {
          groups = makeGroups(splitControl);
        } else {
          groups = makeSimpleGroups(splitControl);
        }
       
        affected = new Set[groups.length * 2];
        collided = new Set[groups.length * 2];
        downCache = new HashMap<TabPanel, Rectangle>();
       
        for (int i = 0; i < groups.length; i++) {
          LWSplitGroup activeGroup = groups[i];
          System.arraycopy(affected(activeGroup), 0, affected,
            i * 2, 2);
          System.arraycopy(collided(activeGroup), 0, collided,
            i * 2, 2);
        }
       
        for (Set<TabPanel> affectedPanelSet : affected) {
          for (TabPanel affectedPanel : affectedPanelSet) {
            downCache.put(affectedPanel, affectedPanel.getBounds());
          }
        }
       
        for (int i = 0; i < groups.length; i++) {
          LWSplitGroup group = groups[i];
          Set<TabPanel> affectedBefore = affected[i * 2 + 0];
          Set<TabPanel> affectedAfter = affected[i * 2 + 1];
         
          for (TabPanel b : affectedBefore) {
            Rectangle bBounds = b.getBounds();
           
            if (group.isVertical()) {
              if (minX == null ||
                bBounds.x + ViewPanel.MIN_EXTENT > minX) {
                minX = bBounds.x + ViewPanel.MIN_EXTENT;
              }
            } else {
              if (minY == null ||
                bBounds.y + ViewPanel.MIN_EXTENT > minY) {
                minY = bBounds.y + ViewPanel.MIN_EXTENT;
              }
            }
          }
         
          for (TabPanel a : affectedAfter) {
            Rectangle aBounds = a.getBounds();
           
            if (group.isVertical()) {
              if (maxX == null || aBounds.x + aBounds.width -
                ViewPanel.MIN_EXTENT < maxX) {
                maxX = aBounds.x + aBounds.width -
                  ViewPanel.MIN_EXTENT;
              }
            } else {
              if (maxY == null || aBounds.y + aBounds.height -
                ViewPanel.MIN_EXTENT < maxY) {
                maxY = aBounds.y + aBounds.height -
                  ViewPanel.MIN_EXTENT;
              }
            }
          }
        }
      }
    });
   
    addListener(SWT.MouseMove, new Listener() {
      @Override
      public void handleEvent(Event event) {
        if (down == null) {
          return;
        }

        Map<TabPanel, Rectangle> moveCache = new HashMap<TabPanel, Rectangle>();

        for (Set<TabPanel> affectedPanelSet : affected) {
          for (TabPanel affectedPanel : affectedPanelSet) {
            moveCache.put(affectedPanel, affectedPanel.getBounds());
          }
        }
       
        int spacing = getParent().getSpacing();
        int locX = event.x - offset.x;
        int locY = event.y - offset.y;
       
        for (int i = 0; i < groups.length; i++) {
          LWSplitGroup activeGroup = groups[i];
          Set<Rectangle> before = collided[i * 2 + 0];
          Set<Rectangle> after = collided[i * 2 + 1];
         
          for (Rectangle b : before) {
            int bLeft = b.x;
            int bTop = b.y;
            int bRight = b.x + b.width;
            int bBottom = b.y + b.height;
           
            if (activeGroup.isVertical()) {
              if (locX <= bRight && locX + spacing >= bLeft) {
                event.x = bLeft + offset.x;
              }
            } else {
              if (locY <= bBottom && locY + spacing >= bTop) {
                event.y = bTop + offset.y;
              }
            }
          }
         
          for (Rectangle a : after) {
            int bLeft = a.x;
            int bTop = a.y;
            int bRight = a.x + a.width;
            int bBottom = a.y + a.height;
           
            if (activeGroup.isVertical()) {
              if (locX <= bRight && locX + spacing >= bLeft) {
                event.x = bLeft + offset.x;
              }
            } else {
              if (locY <= bBottom && locY + spacing >= bTop) {
                event.y = bTop + offset.y;
              }
            }
          }
        }
       
        if (minX != null && event.x < minX) {
          event.x = minX;
        }
       
        if (minY != null && event.y < minY) {
          event.y = minY;
        }
       
        if (maxX != null && event.x > maxX) {
          event.x = maxX;
        }
       
        if (maxY != null && event.y > maxY) {
          event.y = maxY;
        }
       
        int dx = event.x - down.x;
        int dy = event.y - down.y;
       
        for (int i = 0; i < groups.length; i++) {
          LWSplitGroup activeGroup = groups[i];
          Set<TabPanel> before = affected[i * 2 + 0];
          Set<TabPanel> after = affected[i * 2 + 1];
         
          for (TabPanel b : before) {
            Rectangle downCached = downCache.get(b);
            Rectangle moveCached = moveCache.get(b);
           
            if (activeGroup.isVertical()) {
              moveCached.width = downCached.width + dx;
            } else {
              moveCached.height = downCached.height + dy;
            }
          }
         
          for (TabPanel a : after) {
            Rectangle downCached = downCache.get(a);
            Rectangle moveCached = moveCache.get(a);
           
            if (activeGroup.isVertical()) {
              moveCached.x = downCached.x + dx;
              moveCached.width = downCached.width - dx;
            } else {
              moveCached.y = downCached.y + dy;
              moveCached.height = downCached.height - dy;
            }
          }
        }
       
        for (Set<TabPanel> affectedPanelSet : affected) {
          for (TabPanel affectedPanel : affectedPanelSet) {
            Rectangle moveCached = moveCache.get(affectedPanel);
            affectedPanel.setBounds(moveCached);
          }
        }
      }
    });
   
    addListener(SWT.MouseUp, new Listener() {
      @Override
      public void handleEvent(Event event) {
        down = null;
        offset = null;
        groups = null;
        affected = null;
        downCache = null;
        minX = null;
        minY = null;
        maxX = null;
        maxY = null;
        updateSplitControls();
      }
    });
  }
 
  private LWSplitGroup[] makeGroups(LWSplitControl control) {
    if (control instanceof LWSplitBar) {
      LWSplitBar splitBar = (LWSplitBar )control;
      Set<LWSplitBar> barsBefore = new HashSet<LWSplitBar>();
      Set<LWSplitCorner> cornersBefore = new HashSet<LWSplitCorner>();
      Set<LWSplitBar> barsAfter = new HashSet<LWSplitBar>();
      Set<LWSplitCorner> cornersAfter = new HashSet<LWSplitCorner>();
      adjacents(splitBar, splitBar.isVertical(), true, barsBefore,
        cornersBefore);
      adjacents(splitBar, splitBar.isVertical(), false, barsAfter,
        cornersAfter);
      Set<LWSplitBar> bars = new HashSet<LWSplitBar>();
      Set<LWSplitCorner> corners = new HashSet<LWSplitCorner>();
      bars.addAll(barsBefore);
      bars.addAll(barsAfter);
      corners.addAll(cornersBefore);
      corners.addAll(cornersAfter);
     
      bars.add(splitBar);
     
      return new LWSplitGroup[] {
        new LWSplitGroup(this, splitBar.getOrientation(), bars, corners)
      };
    } else if (control instanceof LWSplitCorner) {
      LWSplitCorner splitCorner = (LWSplitCorner )control;
      Set<LWSplitBar> hBarsBefore = new HashSet<LWSplitBar>();
      Set<LWSplitCorner> hCornersBefore = new HashSet<LWSplitCorner>();
      Set<LWSplitBar> hBarsAfter = new HashSet<LWSplitBar>();
      Set<LWSplitCorner> hCornersAfter = new HashSet<LWSplitCorner>();
      Set<LWSplitBar> vBarsBefore = new HashSet<LWSplitBar>();
      Set<LWSplitCorner> vCornersBefore = new HashSet<LWSplitCorner>();
      Set<LWSplitBar> vBarsAfter = new HashSet<LWSplitBar>();
      Set<LWSplitCorner> vCornersAfter = new HashSet<LWSplitCorner>();
      adjacents(splitCorner, false, true, hBarsBefore, hCornersBefore);
      adjacents(splitCorner, false, false, hBarsAfter, hCornersAfter);
      adjacents(splitCorner, true, true, vBarsBefore, vCornersBefore);
      adjacents(splitCorner, true, false, vBarsAfter, vCornersAfter);
      Set<LWSplitBar> hBars = new HashSet<LWSplitBar>();
      Set<LWSplitCorner> hCorners = new HashSet<LWSplitCorner>();
      Set<LWSplitBar> vBars = new HashSet<LWSplitBar>();
      Set<LWSplitCorner> vCorners = new HashSet<LWSplitCorner>();
      hBars.addAll(hBarsBefore);
      hBars.addAll(hBarsAfter);
      hCorners.addAll(hCornersBefore);
      hCorners.addAll(hCornersAfter);
      vBars.addAll(vBarsBefore);
      vBars.addAll(vBarsAfter);
      vCorners.addAll(vCornersBefore);
      vCorners.addAll(vCornersAfter);
     
      hCorners.add(splitCorner);
      vCorners.add(splitCorner);
     
      return new LWSplitGroup[] {
        new LWSplitGroup(this, SWT.HORIZONTAL, hBars, hCorners),
        new LWSplitGroup(this, SWT.VERTICAL, vBars, vCorners)
      };
    }
   
    return null;
  }
 
  private LWSplitGroup[] makeSimpleGroups(LWSplitControl control) {
    Rectangle clientArea = getClientArea();
   
    LWSplitGroup[] groups = makeGroups(control);
   
    for (int i = 0; i < groups.length; i++) {
      final LWSplitGroup group = groups[i];
     
      List<LWSplitBar> splitBars = new ArrayList<LWSplitBar>(
        group.splitBars());
      List<LWSplitCorner> splitCorners = new ArrayList<LWSplitCorner>(
        group.splitCorners());
     
      Collections.sort(splitBars, new Comparator<LWSplitBar>() {
        @Override
        public int compare(LWSplitBar b0, LWSplitBar b1) {
          if (group.isVertical()) {
            return b0.bounds().y - b1.bounds().y;
          } else {
            return b0.bounds().x - b1.bounds().x;
          }
        }
      });
     
      Collections.sort(splitCorners, new Comparator<LWSplitCorner>() {
        @Override
        public int compare(LWSplitCorner c0, LWSplitCorner c1) {
          if (group.isVertical()) {
            return c0.bounds().y - c1.bounds().y;
          } else {
            return c0.bounds().x - c1.bounds().x;
          }
        }
      });

      LWSplitCorner crossingBefore = crossingBefore(control,
        group.isVertical());
      LWSplitCorner crossingAfter = crossingAfter(control,
        group.isVertical());
     
      int min;
      int max;
     
      if (crossingBefore != null) {
        min = group.isVertical() ? crossingBefore.bounds().y :
          crossingBefore.bounds().x;
      } else {
        min = group.isVertical() ? clientArea.y :
          clientArea.x;
      }
     
      if (crossingAfter != null) {
        max = group.isVertical() ? crossingAfter.bounds().y :
          crossingAfter.bounds().x;
      } else {
        max = group.isVertical() ? clientArea.y + clientArea.height :
          clientArea.x + clientArea.width;
      }

      for (Iterator<LWSplitBar> itor = splitBars.iterator();
         itor.hasNext();) {
        LWSplitBar next = itor.next();
       
        if (group.isVertical()) {
          if (next.bounds().y < min || next.bounds().y > max) {
            itor.remove();
          }
        } else {
          if (next.bounds().x < min || next.bounds().x > max) {
            itor.remove();
          }
        }
      }
     
      for (Iterator<LWSplitCorner> itor = splitCorners.iterator();
         itor.hasNext();) {
        LWSplitCorner next = itor.next();
       
        if (group.isVertical()) {
          if (next.bounds().y < min || next.bounds().y > max) {
            itor.remove();
          }
        } else {
          if (next.bounds().x < min || next.bounds().x > max) {
            itor.remove();
          }
        }
      }
     
      groups[i] = new LWSplitGroup(this, group.getOrientation(),
        new HashSet<LWSplitBar>(splitBars),
        new HashSet<LWSplitCorner>(splitCorners));
    }
   
    return groups;
  }
 
  private LWSplitCorner crossingBefore(final LWSplitControl control,
    final boolean vertical) {
    SortedSet<LWSplitBar> splitBars =
      new TreeSet<LWSplitBar>(new Comparator<LWSplitBar>() {
      @Override
      public int compare(LWSplitBar b0, LWSplitBar b1) {
        if (vertical) {
          return b0.bounds().y - b1.bounds().y;
        } else {
          return b0.bounds().x - b1.bounds().x;
        }
      }
    });
   
    SortedSet<LWSplitCorner> splitCorners =
      new TreeSet<LWSplitCorner>(new Comparator<LWSplitCorner>() {
      @Override
      public int compare(LWSplitCorner b0, LWSplitCorner b1) {
        if (vertical) {
          return b0.bounds().y - b1.bounds().y;
        } else {
          return b0.bounds().x - b1.bounds().x;
        }
      }
    });
   
    adjacents(control, vertical, true,
      splitBars, splitCorners);
    LWSplitCorner[] array = splitCorners.toArray(
      new LWSplitCorner[splitCorners.size()]);
   
    for (int i = array.length - 1; i >= 0; i--) {
      LWSplitCorner item = array[i];
     
      if (isCrossing(item)) {
        return item;
      }
    }
   
    return null;
  }
 
  private LWSplitCorner crossingAfter(final LWSplitControl control,
    final boolean vertical) {
    SortedSet<LWSplitBar> splitBars =
      new TreeSet<LWSplitBar>(new Comparator<LWSplitBar>() {
      @Override
      public int compare(LWSplitBar b0, LWSplitBar b1) {
        if (vertical) {
          return b0.bounds().y - b1.bounds().y;
        } else {
          return b0.bounds().x - b1.bounds().x;
        }
      }
    });
   
    SortedSet<LWSplitCorner> splitCorners =
      new TreeSet<LWSplitCorner>(new Comparator<LWSplitCorner>() {
      @Override
      public int compare(LWSplitCorner b0, LWSplitCorner b1) {
        if (vertical) {
          return b0.bounds().y - b1.bounds().y;
        } else {
          return b0.bounds().x - b1.bounds().x;
        }
      }
    });
   
    adjacents(control, vertical, false,
      splitBars, splitCorners);
    LWSplitCorner[] array = splitCorners.toArray(
      new LWSplitCorner[splitCorners.size()]);
   
    for (int i = 0; i < array.length; i++) {
      LWSplitCorner item = array[i];
     
      if (isCrossing(item)) {
        return item;
      }
    }
   
    return null;
  }
 
  private boolean isCrossing(LWSplitCorner corner) {
    int spacing = getParent().getSpacing();
    Rectangle cBounds = corner.bounds();
    int cLeft = cBounds.x;
    int cTop = cBounds.y;
    int cRight = cBounds.x + cBounds.width;
    int cBottom = cBounds.y + cBounds.height;
   
    Point ptLeft = new Point(cLeft - spacing / 2, cTop + spacing / 2);
    Point ptTop = new Point(cLeft + spacing / 2, cTop - spacing / 2);
    Point ptRight = new Point(cRight + spacing / 2, cTop + spacing / 2);
    Point ptBottom = new Point(cLeft + spacing / 2, cBottom + spacing / 2);
    LWSplitControl ctlLeft = findSplitControl(ptLeft.x, ptLeft.y);
    LWSplitControl ctlTop = findSplitControl(ptTop.x, ptTop.y);
    LWSplitControl ctlRight = findSplitControl(ptRight.x, ptRight.y);
    LWSplitControl ctlBottom = findSplitControl(ptBottom.x, ptBottom.y);
    return ctlLeft != null && ctlTop != null && ctlRight != null &&
      ctlBottom != null;
  }
 
  private void adjacents(LWSplitControl current, boolean vertical,
    boolean negative, Set<LWSplitBar> splitBars,
    Set<LWSplitCorner> splitCorners) {
    LWSplitControl adjacent = adjacent(current, vertical, negative);
   
    if (adjacent == null) {
      return;
    }
   
    if (adjacent instanceof LWSplitBar) {
      LWSplitBar splitBar = (LWSplitBar )adjacent;
      splitBars.add(splitBar);
    } else if (adjacent instanceof LWSplitCorner) {
      LWSplitCorner splitCorner = (LWSplitCorner )adjacent;
      splitCorners.add(splitCorner);
    }
   
    adjacents(adjacent, vertical, negative, splitBars, splitCorners);
  }

  @SuppressWarnings("unchecked")
  private Set<LWSplitBar>[] affectedSplitBars(LWSplitGroup splitGroup) {
    Set<LWSplitBar>[] affected = new Set[] {
      new HashSet<LWSplitBar>(),
      new HashSet<LWSplitBar>()
    };
   
    Rectangle gBounds = splitGroup.getBounds();
    int gLeft = gBounds.x;
    int gTop = gBounds.y;
    int gRight = gBounds.x + gBounds.width;
    int gBottom = gBounds.y + gBounds.height;
   
    for (LWSplitBar splitBar : splitBars) {
      Rectangle sBounds = splitBar.bounds();
      int sLeft = sBounds.x;
      int sTop = sBounds.y;
      int sRight = sBounds.x + sBounds.width;
      int sBottom = sBounds.y + sBounds.height;
     
      if (splitGroup.isVertical()) {
        if (sTop < gBottom && sBottom > gTop) {
          if (sRight == gLeft) {
            affected[0].add(splitBar);
          }
         
          if (gRight == sLeft) {
            affected[1].add(splitBar);
          }
        }
      } else {
        if (sLeft < gRight && sRight > gLeft) {
          if (sBottom == gTop) {
            affected[0].add(splitBar);
          }
         
          if (gBottom == sTop) {
            affected[1].add(splitBar);
          }
        }
      }
    }
   
    return affected;
  }
 
  @SuppressWarnings("unchecked")
  private Set<Rectangle>[] collided(LWSplitGroup splitGroup) {
    Set<Rectangle>[] collided = new Set[] {
      new HashSet<Rectangle>(),
      new HashSet<Rectangle>()
    };

    int index = splitGroup.isVertical() ? 0 : 1;
    LWSplitControl min = splitGroup.getMin();
    LWSplitControl max = splitGroup.getMax();
   
    if (min instanceof LWSplitCorner) {
      LWSplitGroup minCollideGroup = makeGroups(min)[index];
      Set<LWSplitCorner> minCollideCorners = new HashSet<LWSplitCorner>(
        minCollideGroup.splitCorners());
     
      for (LWSplitCorner minCollideCorner : minCollideCorners) {
        collided[0].add(minCollideCorner.bounds());
      }
    }
   
    if (max instanceof LWSplitCorner) {
      LWSplitGroup maxCollideGroup = makeGroups(max)[index];
      Set<LWSplitCorner> maxCollideCorners = new HashSet<LWSplitCorner>(
        maxCollideGroup.splitCorners());
     
      for (LWSplitCorner maxCollideCorner : maxCollideCorners) {
        collided[1].add(maxCollideCorner.bounds());
      }
    }
   
    return collided;
  }
 
  @SuppressWarnings("unchecked")
  private Set<TabPanel>[] affected(LWSplitGroup splitGroup) {
    Set<TabPanel>[] affected = new Set[] {
      new HashSet<TabPanel>(),
      new HashSet<TabPanel>()
    };
   
    TabPanel[] children = getChildren();
    Rectangle gBounds = splitGroup.getBounds();
    int gLeft = gBounds.x;
    int gTop = gBounds.y;
    int gRight = gBounds.x + gBounds.width;
    int gBottom = gBounds.y + gBounds.height;
   
    for (TabPanel child : children) {
      Rectangle cBounds = child.getBounds();
      int cLeft = cBounds.x;
      int cTop = cBounds.y;
      int cRight = cBounds.x + cBounds.width;
      int cBottom = cBounds.y + cBounds.height;
     
      if (splitGroup.isVertical()) {
        if (cTop < gBottom && cBottom > gTop) {
          if (cRight == gLeft) {
            affected[0].add(child);
          }
         
          if (gRight == cLeft) {
            affected[1].add(child);
          }
        }
      } else {
        if (cLeft < gRight && cRight > gLeft) {
          if (cBottom == gTop) {
            affected[0].add(child);
          }
         
          if (gBottom == cTop) {
            affected[1].add(child);
          }
        }
      }
    }
   
    return affected;
  }
 
  private LWSplitControl adjacent(LWSplitControl splitControl0,
    boolean vertical, boolean negative) {
    Rectangle bounds0 = splitControl0.bounds();
    int left0 = bounds0.x;
    int top0 = bounds0.y;
    int right0 = bounds0.x + bounds0.width;
    int bottom0 = bounds0.y + bounds0.height;
    Set<LWSplitControl>[] splitControlSets = new Set[] {
      splitBars, splitCorners
    };
   
    for (Set<LWSplitControl> splitControlSet : splitControlSets) {
      for (LWSplitControl splitControl1 : splitControlSet) {
        if (splitControl1 == splitControl0) {
          continue;
        }
       
        Rectangle bounds1 = splitControl1.bounds();
        int left1 = bounds1.x;
        int top1 = bounds1.y;
        int right1 = bounds1.x + bounds1.width;
        int bottom1 = bounds1.y + bounds1.height;
       
        if (vertical) {
          if (left0 == left1) {
            if (negative) {
              if (top1 < bottom0 && bottom1 >= top0) {
                return splitControl1;
              }
            } else {
              if (top0 < bottom1 && bottom0 >= top1) {
                return splitControl1;
              }
            }
          }
        } else {
          if (top0 == top1) {
            if (negative) {
              if (left1 < right0 && right1 >= left0) {
                return splitControl1;
              }
            } else {
              if (left0 < right1 && right0 >= left1) {
                return splitControl1;
              }
            }
          }
        }
      }
    }
   
    return null;
  }
 
  @Override
  public ViewPanel getParent() {
    return (ViewPanel )super.getParent();
  }
 
  @Override
  public boolean setParent(Composite parent) {
    if (!(parent instanceof ViewPanel)) {
      return false;
    }
   
    return super.setParent(parent);
  }
 
  @Override
  public TabPanel[] getChildren() {
    Control[] controls = super.getChildren();
    TabPanel[] children = new TabPanel[controls.length];
   
    for (int i = 0; i < children.length; i++) {
      children[i] = (TabPanel )controls[i];
    }
   
    return children;
  }
 
  @Override
  public void layout(boolean changed) {
    Rectangle newClientArea = getClientArea();
   
    if (oldClientArea == null) {
      oldClientArea = new Rectangle(newClientArea.x, newClientArea.y,
        newClientArea.width, newClientArea.height);
    }
   
    if (oldClientArea.width != newClientArea.width ||
      oldClientArea.height != newClientArea.height) {
      zoom(oldClientArea, newClientArea);
    }
   
    updateSplitControls();
    oldClientArea = newClientArea;
  }
 
  @Override
  public void setLayout(Layout layout) {
  }
 
  @Override
  public Point computeSize(int wHint, int hHint, boolean changed) {
    return super.computeSize(wHint, hHint, changed);
  }
 
  private void zoom(Rectangle oldClientArea, Rectangle newClientArea) {
    Map<TabPanel, Rectangle> caches = new HashMap<TabPanel, Rectangle>();
    Map<TabPanel, Surrounds> surrounds = new HashMap<TabPanel, Surrounds>();
    TabPanel[] children = getChildren();
    int spacing = getParent().getSpacing();
   
    for (TabPanel child : children) {
      Set<TabPanel> lefts = findSurround(child, SWT.LEFT);
      Set<TabPanel> tops = findSurround(child, SWT.TOP);
      Set<TabPanel> rights = findSurround(child, SWT.RIGHT);
      Set<TabPanel> bottoms = findSurround(child, SWT.BOTTOM);
      surrounds.put(child, new Surrounds(lefts, tops, rights, bottoms));
    }
   
    for (TabPanel child : children) {
      Rectangle cache = child.getBounds();
      inflate(cache, oldClientArea, spacing / 2);
      caches.put(child, cache);
    }
   
    float xScale = 1.0f;
    float yScale = 1.0f;
   
    if (oldClientArea.width != 0 && newClientArea.width != 0) {
      xScale = (float )newClientArea.width / oldClientArea.width;
    }
   
    if (oldClientArea.height != 0 && newClientArea.height != 0) {
      yScale = (float )newClientArea.height / oldClientArea.height;
    }
   
    for (TabPanel child : children) {
      Rectangle cache = caches.get(child);
      cache.width = Math.round(cache.width * xScale);
      cache.height = Math.round(cache.height * yScale);
    }
   
    TabPanel upperLeft = findUpperLeft();
    lineUpPanels(upperLeft, caches, surrounds);
   
    for (TabPanel child : children) {
      Rectangle cBounds = caches.get(child);
      int cLeft = cBounds.x;
      int cTop = cBounds.y;
      int cRight = cBounds.x + cBounds.width;
      int cBottom = cBounds.y + cBounds.height;
      Surrounds surround = surrounds.get(child);
      Set<TabPanel> ls = surround.getLefts();
      Set<TabPanel> ts = surround.getTops();
      Set<TabPanel> rs = surround.getRights();
      Set<TabPanel> bs = surround.getBottoms();
     
      if (ls.size() == 1) {
        TabPanel l = ls.iterator().next();
        Rectangle lBounds = caches.get(l);
        int lRight = lBounds.x + lBounds.width;
       
        if (cLeft != lRight) {
          cLeft = lRight;
        }
      }
     
      if (ts.size() == 1) {
        TabPanel t = ts.iterator().next();
        Rectangle tBounds = caches.get(t);
        int tBottom = tBounds.y + tBounds.height;
       
        if (cTop != tBottom) {
          cTop = tBottom;
        }
      }
     
      if (rs.isEmpty()) {
        int ncaRight = newClientArea.x + newClientArea.width;
        int dx = ncaRight - cRight;
        cRight += dx;
      } else if (rs.size() == 1) {
        TabPanel r = rs.iterator().next();
        Rectangle rBounds = caches.get(r);
        int rLeft = rBounds.x;
       
        if (cRight != rLeft) {
          cRight = rLeft;
        }
      }
     
    if (bs.isEmpty()) {
        int ncaBottom = newClientArea.y + newClientArea.height;
        int dy = ncaBottom - cBottom;
        cBottom += dy;
      } else if (bs.size() == 1) {
        TabPanel b = bs.iterator().next();
        Rectangle bBounds = caches.get(b);
        int bTop = bBounds.y;
       
        if (cBottom != bTop) {
          cBottom = bTop;
        }
      }
     
      cBounds.x = cLeft;
      cBounds.y = cTop;
      cBounds.width = cRight - cLeft;
      cBounds.height = cBottom - cTop;
    }

    for (TabPanel child : children) {
      Rectangle cache = caches.get(child);
      inflate(cache, newClientArea, -spacing / 2);
      child.setBounds(cache);
    }
  }
 
  private void lineUpPanels(TabPanel current, Map<TabPanel, Rectangle> caches,
    Map<TabPanel, Surrounds> surrounds) {
    Rectangle cache = caches.get(current);
   
    Surrounds surround = surrounds.get(current);
    Set<TabPanel> rights = surround.getRights();
   
    for (TabPanel right : rights) {
      Rectangle rCache = caches.get(right);
      rCache.x = cache.x + cache.width;
      lineUpPanels(right, caches, surrounds);
    }
   
    Set<TabPanel> bottoms = surround.getBottoms();
   
    for (TabPanel bottom : bottoms) {
      Rectangle bCache = caches.get(bottom);
      bCache.y = cache.y + cache.height;
      lineUpPanels(bottom, caches, surrounds);
    }
  }
 
  private TabPanel findUpperLeft() {
    TabPanel[] children = getChildren();
   
    for (TabPanel child : children) {
      Rectangle bounds = child.getBounds();
     
      if (bounds.x == 0 && bounds.y == 0) {
        return child;
      }
    }
   
    return null;
  }
 
  private void inflate(Rectangle bounds, Rectangle clientArea, int amount) {
    int left = bounds.x;
    int top = bounds.y;
    int right = bounds.x + bounds.width;
    int bottom = bounds.y + bounds.height;
    int cLeft = clientArea.x;
    int cTop = clientArea.y;
    int cRight = clientArea.x + clientArea.width;
    int cBottom = clientArea.y + clientArea.height;
   
    if (left > cLeft) {
      left -= amount;
    }
   
    if (top > cTop) {
      top -= amount;
    }
   
    if (right < cRight) {
      right += amount;
    }
   
    if (bottom < cBottom) {
      bottom += amount;
    }
   
    bounds.x = left;
    bounds.y = top;
    bounds.width = right - left;
    bounds.height = bottom - top;
  }
 
  private LWSplitControl findSplitControl(int x, int y) {
    if (splitBars != null) {
      for (LWSplitBar splitBar : splitBars) {
        Rectangle bounds = splitBar.bounds();
       
        if (bounds.contains(x, y)) {
          return splitBar;
        }
      }
    }
   
    if (splitCorners != null) {
      for (LWSplitCorner splitCorner : splitCorners) {
        Rectangle bounds = splitCorner.bounds();
       
        if (bounds.contains(x, y)) {
          return splitCorner;
        }
      }
    }
   
    return null;
  }
 
  private void updateSplitControls() {
    splitBars = findSplitBars();
    splitCorners = findSplitCorners(splitBars);
  }
 
  private Set<LWSplitCorner> findSplitCorners(Set<LWSplitBar> splitBars) {
    Set<LWSplitCorner> corners = new HashSet<LWSplitCorner>();
    Rectangle clientArea = getClientArea();
    int spacing = getParent().getSpacing();
    int cLeft = clientArea.x;
    int cTop = clientArea.y;
    int cRight = clientArea.x + clientArea.width;
    int cBottom = clientArea.y + clientArea.height;
   
    for (LWSplitBar splitBar : splitBars) {
      Rectangle bounds = splitBar.bounds();
      int left = bounds.x;
      int top = bounds.y;
      int right = bounds.x + bounds.width;
      int bottom = bounds.y + bounds.height;
     
      if (splitBar.isVertical()) {
        if (top > cTop) {
          corners.add(new LWSplitCorner(this, new Rectangle(
            left, top - spacing, spacing, spacing)));
        }
       
        if (bottom < cBottom) {
          corners.add(new LWSplitCorner(this, new Rectangle(
            left, bottom, spacing, spacing)));
        }
      } else {
        if (left > cLeft) {
          corners.add(new LWSplitCorner(this, new Rectangle(
            left - spacing, top, spacing, spacing)));
        }
       
        if (right < cRight) {
          corners.add(new LWSplitCorner(this, new Rectangle(
            right, top, spacing, spacing)));
        }
      }
    }
   
    return corners;
  }
 
  private Set<LWSplitBar> findSplitBars() {
    Set<LWSplitBar> splitBars = new HashSet<LWSplitBar>();
    TabPanel[] children0 = getChildren();
    TabPanel[] children1 = getChildren();
    int spacing = getParent().getSpacing();
   
    for (TabPanel child0 : children0) {
      Rectangle bounds0 = child0.getBounds();
      int left0 = bounds0.x;
      int top0 = bounds0.y;
      int right0 = bounds0.x + bounds0.width;
      int bottom0 = bounds0.y + bounds0.height;
     
      for (TabPanel child1 : children1) {
        if (child0 == child1) {
          continue;
        }
       
        Rectangle bounds1 = child1.getBounds();
        int left1 = bounds1.x;
        int top1 = bounds1.y;
        int right1 = bounds1.x + bounds1.width;
        int bottom1 = bounds1.y + bounds1.height;

        if (right0 + spacing == left1 || right1 + spacing == left0) {
          if (top0 < bottom1 && bottom0 > top1) {
            int l = Math.min(right0, right1);
            int r = Math.max(left0, left1);
            int t = Math.max(top0, top1);
            int b = Math.min(bottom0, bottom1);
            int o = SWT.VERTICAL;
            Rectangle rt = new Rectangle(l, t, r - l, b - t);
            LWSplitBar s = new LWSplitBar(this, o, rt);
           
            splitBars.add(s);
          }
        }
       
        if (bottom0 + spacing == top1 || bottom1 + spacing == top0) {
          if (left0 < right1 && right0 > left1) {
            int l = Math.max(left0, left1);
            int r = Math.min(right0, right1);
            int t = Math.min(bottom0, bottom1);
            int b = Math.max(top0, top1);
            int o = SWT.HORIZONTAL;
            Rectangle rt = new Rectangle(l, t, r - l, b - t);
            LWSplitBar s = new LWSplitBar(this, o, rt);
           
            splitBars.add(s);
          }
        }
      }
    }
   
    return splitBars;
  }
 
  private Set<TabPanel> findSurround(TabPanel panel, int direction) {
    Set<TabPanel> panels = new HashSet<TabPanel>();
    Rectangle pBounds = panel.getBounds();
    int pLeft = pBounds.x;
    int pTop = pBounds.y;
    int pRight = pBounds.x + pBounds.width;
    int pBottom = pBounds.y + pBounds.height;
    int spacing = getParent().getSpacing();
    TabPanel[] children = getChildren();
   
    for (TabPanel child : children) {
      if (child == panel) {
        continue;
      }
     
      Rectangle cBounds = child.getBounds();
      int cLeft = cBounds.x;
      int cTop = cBounds.y;
      int cRight = cBounds.x + cBounds.width;
      int cBottom = cBounds.y + cBounds.height;
     
      if (direction == SWT.LEFT && cTop < pBottom && cBottom > pTop &&
        cRight + spacing == pLeft) {
        panels.add(child);
      }
     
      if (direction == SWT.TOP && cLeft < pRight && cRight > pLeft &&
        cBottom + spacing == pTop) {
        panels.add(child);
      }
     
      if (direction == SWT.RIGHT && pTop < cBottom && pBottom > cTop &&
        pRight + spacing == cLeft) {
        panels.add(child);
      }
     
      if (direction == SWT.BOTTOM && pLeft < cRight && pRight > cLeft &&
        pBottom + spacing == cTop) {
        panels.add(child);
      }
    }
   
    return panels;
  }
}

final class Surrounds {
  private final Set<TabPanel> lefts;
  private final Set<TabPanel> tops;
  private final Set<TabPanel> rights;
  private final Set<TabPanel> bottoms;
 
  public Surrounds(Set<TabPanel> lefts, Set<TabPanel> tops,
    Set<TabPanel> rights, Set<TabPanel> bottoms) {
    this.lefts = lefts;
    this.tops = tops;
    this.rights = rights;
    this.bottoms = bottoms;
  }
 
  public Set<TabPanel> getLefts() {
    return lefts;
  }

  public Set<TabPanel> getTops() {
    return tops;
  }
 
  public Set<TabPanel> getRights() {
    return rights;
  }
 
  public Set<TabPanel> getBottoms() {
    return bottoms;
  }
}

interface LWSplitControl {
  Rectangle bounds();
}

final class LWSplitBar implements LWSplitControl {
  private final SplitDockPanel parent;
  private final int orientation;
  private final Rectangle bounds;
 
  public LWSplitBar(SplitDockPanel parent, int orientation,
    Rectangle bounds) {
    this.parent = parent;
    this.orientation = orientation;
    this.bounds = bounds;
  }
 
  public SplitDockPanel getParent() {
    return parent;
  }
 
  public int getOrientation() {
    return orientation;
  }
 
  public boolean isHorizontal() {
    return (orientation & SWT.HORIZONTAL) == SWT.HORIZONTAL;
  }
 
  public boolean isVertical() {
    return (orientation & SWT.VERTICAL) == SWT.VERTICAL;
  }
 
  public Rectangle bounds() {
    return bounds;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((bounds == null) ? 0 : bounds.hashCode());
    result = prime * result + orientation;
    result = prime * result + ((parent == null) ? 0 : parent.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;
    LWSplitBar other = (LWSplitBar) obj;
    if (bounds == null) {
      if (other.bounds != null)
        return false;
    } else if (!bounds.equals(other.bounds))
      return false;
    if (orientation != other.orientation)
      return false;
    if (parent == null) {
      if (other.parent != null)
        return false;
    } else if (!parent.equals(other.parent))
      return false;
    return true;
  }
}

final class LWSplitCorner implements LWSplitControl {
  private final SplitDockPanel parent;
  private final Rectangle bounds;
 
  public LWSplitCorner(SplitDockPanel parent, Rectangle bounds) {
    this.parent = parent;
    this.bounds = bounds;
  }
 
  public SplitDockPanel getParent() {
    return parent;
  }
 
  public Rectangle bounds() {
    return bounds;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((bounds == null) ? 0 : bounds.hashCode());
    result = prime * result + ((parent == null) ? 0 : parent.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;
    LWSplitCorner other = (LWSplitCorner) obj;
    if (bounds == null) {
      if (other.bounds != null)
        return false;
    } else if (!bounds.equals(other.bounds))
      return false;
    if (parent == null) {
      if (other.parent != null)
        return false;
    } else if (!parent.equals(other.parent))
      return false;
    return true;
  }
}

final class LWSplitGroup {
  private final SplitDockPanel parent;
  private final int orientation;
  private final Set<LWSplitBar> splitBars;
  private final Set<LWSplitCorner> splitCorners;
 
  public LWSplitGroup(SplitDockPanel parent, int orientation,
    Set<LWSplitBar> splitBars, Set<LWSplitCorner> splitCorners) {
    this.parent = parent;
    this.orientation = orientation;
    this.splitBars = splitBars;
    this.splitCorners = splitCorners;
  }
 
  public SplitDockPanel getParent() {
    return parent;
  }
 
  public int getOrientation() {
    return orientation;
  }
 
  public boolean isHorizontal() {
    return orientation == SWT.HORIZONTAL;
  }
 
  public boolean isVertical() {
    return orientation == SWT.VERTICAL;
  }
 
  public Set<LWSplitBar> splitBars() {
    return splitBars;
  }
 
  public Set<LWSplitCorner> splitCorners() {
    return splitCorners;
  }
 
  public LWSplitControl getMax() {
    LWSplitControl max = null;
   
    for (LWSplitBar splitBar : splitBars) {
      if (max == null) {
        max = splitBar;
      } else {
        Rectangle mBounds = max.bounds();
        Rectangle bBounds = splitBar.bounds();
       
        if (isVertical() && bBounds.y > mBounds.y) {
          max = splitBar;
        } else if (isHorizontal() && bBounds.x > mBounds.x) {
          max = splitBar;
        }
      }
    }
   
    for (LWSplitCorner splitCorner : splitCorners) {
      if (max == null) {
        max = splitCorner;
      } else {
        Rectangle mBounds = max.bounds();
        Rectangle cBounds = splitCorner.bounds();
       
        if (isVertical() && cBounds.y > mBounds.y) {
          max = splitCorner;
        } else if (isHorizontal() && cBounds.x > mBounds.x) {
          max = splitCorner;
        }
      }
    }
   
    return max;
  }
 
  public LWSplitControl getMin() {
    LWSplitControl min = null;
   
    for (LWSplitBar splitBar : splitBars) {
      if (min == null) {
        min = splitBar;
      } else {
        Rectangle mBounds = min.bounds();
        Rectangle bBounds = splitBar.bounds();
       
        if (isVertical() && bBounds.y < mBounds.y) {
          min = splitBar;
        } else if (isHorizontal() && bBounds.x < mBounds.x) {
          min = splitBar;
        }
      }
    }
   
    for (LWSplitCorner splitCorner : splitCorners) {
      if (min == null) {
        min = splitCorner;
      } else {
        Rectangle mBounds = min.bounds();
        Rectangle cBounds = splitCorner.bounds();
       
        if (isVertical() && cBounds.y < mBounds.y) {
          min = splitCorner;
        } else if (isHorizontal() && cBounds.x < mBounds.x) {
          min = splitCorner;
        }
      }
    }
   
    return min;
  }
 
  public Rectangle getBounds() {
    boolean ready = false;
    int left = 0;
    int top = 0;
    int right = 0;
    int bottom = 0;
   
    for (LWSplitBar splitBar : splitBars) {
      if (!ready) {
        Rectangle bounds = splitBar.bounds();
        left = bounds.x;
        top = bounds.y;
        right = bounds.x + bounds.width;
        bottom = bounds.y + bounds.height;
        ready = true;
      }
    }
   
    for (LWSplitCorner splitCorner : splitCorners) {
      if (!ready) {
        Rectangle bounds = splitCorner.bounds();
        left = bounds.x;
        top = bounds.y;
        right = bounds.x + bounds.width;
        bottom = bounds.y + bounds.height;
        ready = true;
      }
    }
   
    if (!ready) {
      return new Rectangle(0, 0, 0, 0);
    }
   
    for (LWSplitBar splitBar : splitBars) {
      Rectangle bounds = splitBar.bounds();
      int bLeft = bounds.x;
      int bTop = bounds.y;
      int bRight = bounds.x + bounds.width;
      int bBottom = bounds.y + bounds.height;
     
      if (bLeft < left) {
        left = bLeft;
      }
     
      if (bTop < top) {
        top = bTop;
      }
     
      if (bRight > right) {
        right = bRight;
      }
     
      if (bBottom > bottom) {
        bottom = bBottom;
      }
    }
   
    for (LWSplitCorner splitCorner : splitCorners) {
      Rectangle bounds = splitCorner.bounds();
      int cLeft = bounds.x;
      int cTop = bounds.y;
      int cRight = bounds.x + bounds.width;
      int cBottom = bounds.y + bounds.height;
     
      if (cLeft < left) {
        left = cLeft;
      }
     
      if (cTop < top) {
        top = cTop;
      }
     
      if (cRight > right) {
        right = cRight;
      }
     
      if (cBottom > bottom) {
        bottom = cBottom;
      }
    }
   
    return new Rectangle(left, top, right - left, bottom - top);
  }
 
  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + orientation;
    result = prime * result + ((parent == null) ? 0 : parent.hashCode());
    result = prime * result
        + ((splitBars == null) ? 0 : splitBars.hashCode());
    result = prime * result
        + ((splitCorners == null) ? 0 : splitCorners.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;
    LWSplitGroup other = (LWSplitGroup) obj;
    if (orientation != other.orientation)
      return false;
    if (parent == null) {
      if (other.parent != null)
        return false;
    } else if (!parent.equals(other.parent))
      return false;
    if (splitBars == null) {
      if (other.splitBars != null)
        return false;
    } else if (!splitBars.equals(other.splitBars))
      return false;
    if (splitCorners == null) {
      if (other.splitCorners != null)
        return false;
    } else if (!splitCorners.equals(other.splitCorners))
      return false;
    return true;
  }
}

final class TabPanel extends Composite {

  private final CTabFolder tabFolder;
 
  public TabPanel(SplitDockPanel parent, int style) {
    super(parent, style);
    setLayout(new FillLayout());
    tabFolder = new CTabFolder(this, SWT.NONE);
    tabFolder.setBorderVisible(true);
  }
 
  @Override
  public TabPanel getParent() {
    return (TabPanel )super.getParent();
  }
 
  @Override
  public boolean setParent(Composite parent) {
    if (!(parent instanceof SplitDockPanel)) {
      return false;
    }
   
    return super.setParent(parent);
  }
 
  CTabFolder getTabFolder() {
    return tabFolder;
  }
 
  @Override
  public String toString() {
    String string = super.toString();
   
    if (tabFolder.getItemCount() > 0) {
      string = tabFolder.getItems()[0].getText();
    }
   
    return string;
  }
}

final class Bounds {
 
  private static final String REG_EXP = "[ ]*[,][ ]*|[ ]*[;][ ]*|[ ]+";
 
  private final Extent left;
  private final Extent top;
  private final Extent width;
  private final Extent height;
  private final Extent right;
  private final Extent bottom;
 
  public Bounds(String expr) {
    int open = expr.indexOf('(');
    int closed = expr.indexOf(')');
   
    if (open < 0) {
      open = 0;
    }
   
    if (closed < 0) {
      closed = expr.length();
    }
   
    String head = expr.substring(0, open);
    String body = expr.substring(open + 1, closed);
    String[] parts = body.split(REG_EXP);
   
    if (head.trim().equals("ltrb")) {
      left = new Extent(parts[0]);
      top = new Extent(parts[1]);
      width = null;
      height = null;
      right = new Extent(parts[2]);
      bottom = new Extent(parts[3]);
    } else {
      left = new Extent(parts[0]);
      top = new Extent(parts[1]);
      width = new Extent(parts[2]);
      height = new Extent(parts[3]);
      right = null;
      bottom = null;
    }
  }
 
  public Rectangle getBounds(Rectangle area) {
    if (area == null) {
      area = new Rectangle(0, 0, 0, 0);
    }
   
    int tl = area.x + left.getValue(area.width);
    int tt = area.y + top.getValue(area.height);
    int tr = 0;
    int tb = 0;
   
    if (right == null && bottom == null) {
      int tw = width.getValue(area.width);
      int th = height.getValue(area.height);
      tr = tl + tw;
      tb = tt + th;
    } else {
      tr = area.x + right.getValue(area.width);
      tb = area.y + bottom.getValue(area.height);
    }
   
    int fl = Math.min(tl, tr);
    int ft = Math.min(tt, tb);
    int fr = Math.max(tl, tr);
    int fb = Math.max(tt, tb);

    return new Rectangle(fl, ft, fr - fl, fb - ft);
  }
}

final class Extent {
  private final int value;
  private final boolean relativeMetrics;
  private final boolean negativeDirection;
 
  public Extent(String expr) {
    negativeDirection = expr.startsWith("-");
    relativeMetrics = expr.contains(".");
    float f = Math.abs(Float.parseFloat(expr));
   
    if (relativeMetrics) {
      f = f * 100;
      f = Math.max(f, 0);
      f = Math.min(f, 100);
    } else {
      f = Math.max(f, 0);
    }
   
    value = (int )f;
  }
 
  public int getValue(int length) {
    float abs = relativeMetrics ? (length * value / 100.0f) : value;
    return Math.round(negativeDirection ? (length - abs) : abs);
  }
}
TOP

Related Classes of com.peterhi.ui.Surrounds

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.