Package org.eclipse.ui.internal

Source Code of org.eclipse.ui.internal.PartSashContainer

/*******************************************************************************
* Copyright (c) 2000, 2007 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*     Cagatay Kavukcuoglu <cagatayk@acm.org>
*     - Fix for bug 10025 - Resizing views should not use height ratios
*     Chris Gross chris.gross@us.ibm.com Bug 107443
*******************************************************************************/
package org.eclipse.ui.internal;

import java.util.ArrayList;

import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.util.Geometry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.internal.dnd.AbstractDropTarget;
import org.eclipse.ui.internal.dnd.DragUtil;
import org.eclipse.ui.internal.dnd.IDragOverListener;
import org.eclipse.ui.internal.dnd.IDropTarget;
import org.eclipse.ui.internal.dnd.SwtUtil;

/**
* Abstract container that groups various layout
* parts (possibly other containers) together as
* a unit. Manages the placement and size of these
* layout part based on the location of sashes within
* the container.
*/
public abstract class PartSashContainer extends LayoutPart implements
        ILayoutContainer, IDragOverListener {

    protected Composite parent;

    protected ControlListener resizeListener;

    protected LayoutTree root;
   
    private Composite parentWidget;

    private LayoutPart zoomedPart;

    protected WorkbenchPage page;

    boolean active = false;
    boolean layoutDirty = false;

    /* Array of LayoutPart */
    protected ArrayList children = new ArrayList();
   
    private SashContainerDropTarget dropTarget;

    protected static class RelationshipInfo {
        protected LayoutPart part;

        protected LayoutPart relative;

        protected int relationship;

        /**
         * Preferred size for the left child (this would be the size, in pixels of the child
         * at the time the sash was last moved)
         */
        protected int left;

        /**
         * Preferred size for the right child (this would be the size, in pixels of the child
         * at the time the sash was last moved)
         */
        protected int right;

        /**
         * Computes the "ratio" for this container. That is, the ratio of the left side over
         * the sum of left + right. This is only used for serializing PartSashContainers in
         * a form that can be read by old versions of Eclipse. This can be removed if this
         * is no longer required.
         *
         * @return the pre-Eclipse 3.0 sash ratio
         */
        public float getRatio() {
            int total = left + right;
            if (total > 0) {
                return (float) left / (float) total;
            }
           
            return 0.5f;
        }
    }

    private class SashContainerDropTarget extends AbstractDropTarget {
        private int side;

        private int cursor;

        private LayoutPart targetPart;

        private LayoutPart sourcePart;

        public SashContainerDropTarget(LayoutPart sourcePart, int side, int cursor, LayoutPart targetPart) {
            this.setTarget(sourcePart, side, cursor, targetPart);
        }
       
        public void setTarget(LayoutPart sourcePart, int side, int cursor, LayoutPart targetPart) {
            this.side = side;
            this.targetPart = targetPart;
            this.sourcePart = sourcePart;
            this.cursor = cursor;
        }

        public void drop() {
            if (side != SWT.NONE) {
                LayoutPart visiblePart = sourcePart;

                if (sourcePart instanceof PartStack) {
                    visiblePart = getVisiblePart((PartStack) sourcePart);
                }

                dropObject(getVisibleParts(sourcePart), visiblePart,
                        targetPart, side);
            }
        }

        public Cursor getCursor() {
            return DragCursors.getCursor(DragCursors
                    .positionToDragCursor(cursor));
        }

        public Rectangle getSnapRectangle() {
            Rectangle targetBounds;

            if (targetPart != null) {
                targetBounds = DragUtil.getDisplayBounds(targetPart
                        .getControl());
            } else {
                targetBounds = DragUtil.getDisplayBounds(getParent());
            }

            if (side == SWT.CENTER || side == SWT.NONE) {
                return targetBounds;
            }

            int distance = Geometry.getDimension(targetBounds, !Geometry
                    .isHorizontal(side));

            return Geometry.getExtrudedEdge(targetBounds,
                    (int) (distance * getDockingRatio(sourcePart, targetPart)),
                    side);
        }
    }

    public PartSashContainer(String id, final WorkbenchPage page, Composite parentWidget) {
        super(id);
        this.page = page;
        this.parentWidget = parentWidget;
        resizeListener = new ControlAdapter() {
            public void controlResized(ControlEvent e) {
                resizeSashes();
            }
        };
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.ILayoutContainer#obscuredByZoom(org.eclipse.ui.internal.LayoutPart)
     */
    public boolean childObscuredByZoom(LayoutPart toTest) {
        LayoutPart zoomPart = getZoomedPart();
       
        if (zoomPart != null && toTest != zoomPart) {
            return true;
        }
        return isObscuredByZoom();
    }
   
    /**
     * Given an object associated with a drag (a PartPane or PartStack), this returns
     * the actual PartPanes being dragged.
     *
     * @param pane
     * @return
     */
    private PartPane[] getVisibleParts(LayoutPart pane) {
        if (pane instanceof PartPane) {
            return new PartPane[] { (PartPane) pane };
        } else if (pane instanceof PartStack) {
            PartStack stack = (PartStack) pane;

            LayoutPart[] children = stack.getChildren();
            ArrayList result = new ArrayList(children.length);
            for (int idx = 0; idx < children.length; idx++) {
                LayoutPart next = children[idx];
                if (next instanceof PartPane) {
                    result.add(next);
                }
            }

            return (PartPane[]) result.toArray(new PartPane[result.size()]);
        }

        return new PartPane[0];
    }

    /**
     * Find the sashs around the specified part.
     */
    public void findSashes(LayoutPart pane, PartPane.Sashes sashes) {
        if (root == null) {
            return;
        }
        LayoutTree part = root.find(pane);
        if (part == null) {
      return;
    }
        part.findSashes(sashes);
    }

    /**
     * Add a part.
     */
    public void add(LayoutPart child) {
        if (child == null) {
      return;
    }

        addEnhanced(child, SWT.RIGHT, 0.5f, findBottomRight());
    }

    /**
     * Add a new part relative to another. This should be used in place of <code>add</code>.
     * It differs as follows:
     * <ul>
     * <li>relationships are specified using SWT direction constants</li>
     * <li>the ratio applies to the newly added child -- not the upper-left child</li>
     * </ul>
     *
     * @param child new part to add to the layout
     * @param swtDirectionConstant one of SWT.TOP, SWT.BOTTOM, SWT.LEFT, or SWT.RIGHT
     * @param ratioForNewPart a value between 0.0 and 1.0 specifying how much space will be allocated for the newly added part
     * @param relative existing part indicating where the new child should be attached
     * @since 3.0
     */
    void addEnhanced(LayoutPart child, int swtDirectionConstant,
            float ratioForNewPart, LayoutPart relative) {
        int relativePosition = PageLayout
                .swtConstantToLayoutPosition(swtDirectionConstant);

        float ratioForUpperLeftPart;

        if (relativePosition == IPageLayout.RIGHT
                || relativePosition == IPageLayout.BOTTOM) {
            ratioForUpperLeftPart = 1.0f - ratioForNewPart;
        } else {
            ratioForUpperLeftPart = ratioForNewPart;
        }

        add(child, relativePosition, ratioForUpperLeftPart, relative);
    }

    /**
     * Add a part relative to another. For compatibility only. New code should use
     * addEnhanced, above.
     *
     * @param child the new part to add
     * @param relationship one of PageLayout.TOP, PageLayout.BOTTOM, PageLayout.LEFT, or PageLayout.RIGHT
     * @param ratio a value between 0.0 and 1.0, indicating how much space will be allocated to the UPPER-LEFT pane
     * @param relative part where the new part will be attached
     */
    public void add(LayoutPart child, int relationship, float ratio,
            LayoutPart relative) {
        boolean isHorizontal = (relationship == IPageLayout.LEFT || relationship == IPageLayout.RIGHT);

        LayoutTree node = null;
        if (root != null && relative != null) {
            node = root.find(relative);
        }

        Rectangle bounds;
        if (getParent() == null) {
            Control control = getPage().getClientComposite();
            if (control != null && !control.isDisposed()) {
                bounds = control.getBounds();
            } else {
                bounds = new Rectangle(0, 0, 800, 600);
            }
            bounds.x = 0;
            bounds.y = 0;
        } else {
            bounds = getBounds();
        }

        int totalSize = measureTree(bounds, node, isHorizontal);

        int left = (int) (totalSize * ratio);
        int right = totalSize - left;

        add(child, relationship, left, right, relative);
    }

    static int measureTree(Rectangle outerBounds, LayoutTree toMeasure,
            boolean horizontal) {
        if (toMeasure == null) {
            return Geometry.getDimension(outerBounds, horizontal);
        }

        LayoutTreeNode parent = toMeasure.getParent();
        if (parent == null) {
            return Geometry.getDimension(outerBounds, horizontal);
        }

        if (parent.getSash().isHorizontal() == horizontal) {
            return measureTree(outerBounds, parent, horizontal);
        }

        boolean isLeft = parent.isLeftChild(toMeasure);

        LayoutTree otherChild = parent.getChild(!isLeft);
        if (otherChild.isVisible()) {
            int left = parent.getSash().getLeft();
            int right = parent.getSash().getRight();
            int childSize = isLeft ? left : right;

            int bias = parent.getCompressionBias();

            // Normalize bias: 1 = we're fixed, -1 = other child is fixed
            if (isLeft) {
                bias = -bias;
            }

            if (bias == 1) {
                // If we're fixed, return the fixed size
                return childSize;
            } else if (bias == -1) {

                // If the other child is fixed, return the size of the parent minus the fixed size of the
                // other child
                return measureTree(outerBounds, parent, horizontal)
                        - (left + right - childSize);
            }

            // Else return the size of the parent, scaled appropriately
            return measureTree(outerBounds, parent, horizontal) * childSize
                    / (left + right);
        }

        return measureTree(outerBounds, parent, horizontal);
    }

    protected void addChild(RelationshipInfo info) {
        LayoutPart child = info.part;

        children.add(child);

        if (root == null) {
            root = new LayoutTree(child);
        } else {
            //Add the part to the tree.
            int vertical = (info.relationship == IPageLayout.LEFT || info.relationship == IPageLayout.RIGHT) ? SWT.VERTICAL
                    : SWT.HORIZONTAL;
            boolean left = info.relationship == IPageLayout.LEFT
                    || info.relationship == IPageLayout.TOP;
            LayoutPartSash sash = new LayoutPartSash(this, vertical);
            sash.setSizes(info.left, info.right);
            if ((parent != null) && !(child instanceof PartPlaceholder)) {
        sash.createControl(parent);
      }
            root = root.insert(child, left, sash, info.relative);
        }

        childAdded(child);

        if (active) {
            child.createControl(parent);
            child.setVisible(true);
            child.setContainer(this);
            resizeChild(child);
        }

    }

    /**
     * Adds the child using ratio and position attributes
     * from the specified placeholder without replacing
     * the placeholder
     *
     * FIXME: I believe there is a bug in computeRelation()
     * when a part is positioned relative to the editorarea.
     * We end up with a null relative and 0.0 for a ratio.
     */
    void addChildForPlaceholder(LayoutPart child, LayoutPart placeholder) {
        RelationshipInfo newRelationshipInfo = new RelationshipInfo();
        newRelationshipInfo.part = child;
        if (root != null) {
            newRelationshipInfo.relationship = IPageLayout.RIGHT;
            newRelationshipInfo.relative = root.findBottomRight();
            newRelationshipInfo.left = 200;
            newRelationshipInfo.right = 200;
        }

        // find the relationship info for the placeholder
        RelationshipInfo[] relationships = computeRelation();
        for (int i = 0; i < relationships.length; i++) {
            RelationshipInfo info = relationships[i];
            if (info.part == placeholder) {
                newRelationshipInfo.left = info.left;
                newRelationshipInfo.right = info.right;
                newRelationshipInfo.relationship = info.relationship;
                newRelationshipInfo.relative = info.relative;
            }
        }

        addChild(newRelationshipInfo);
        flushLayout();
    }

    /**
     * See ILayoutContainer#allowBorder
     */
    public boolean allowsBorder() {
        return true;
    }

    /**
     * Notification that a child layout part has been
     * added to the container. Subclasses may override
     * this method to perform any container specific
     * work.
     */
    protected void childAdded(LayoutPart child) {
      if (isDeferred()) {
        child.deferUpdates(true);
      }
    }

    /**
     * Notification that a child layout part has been
     * removed from the container. Subclasses may override
     * this method to perform any container specific
     * work.
     */
    protected void childRemoved(LayoutPart child) {
      if (isDeferred()) {
        child.deferUpdates(false);
      }
    }

    /**
     * Returns an array with all the relation ship between the
     * parts.
     */
    public RelationshipInfo[] computeRelation() {
        LayoutTree treeRoot = root;
        ArrayList list = new ArrayList();
        if (treeRoot == null) {
      return new RelationshipInfo[0];
    }
        RelationshipInfo r = new RelationshipInfo();
        r.part = treeRoot.computeRelation(list);
        list.add(0, r);
        RelationshipInfo[] result = new RelationshipInfo[list.size()];
        list.toArray(result);
        return result;
    }

    public void setActive(boolean isActive) {
        if (isActive == active) {
            return;
        }
       
        active = isActive;
       
        ArrayList children = (ArrayList) this.children.clone();
        for (int i = 0, length = children.size(); i < length; i++) {
            LayoutPart child = (LayoutPart) children.get(i);
           
            if (child instanceof PartStack) {
                PartStack stack = (PartStack) child;
                stack.setActive(isActive);
            }
        }
       
        if (isActive) {
           
            createControl(parentWidget);
           
            parent.addControlListener(resizeListener);

            DragUtil.addDragTarget(parent, this);
            DragUtil.addDragTarget(parent.getShell(), this);
           
            //ArrayList children = (ArrayList) this.children.clone();
            for (int i = 0, length = children.size(); i < length; i++) {
                LayoutPart child = (LayoutPart) children.get(i);
                child.setContainer(this);
                child.setVisible(zoomedPart == null || child == zoomedPart);
                if (!(child instanceof PartStack)) {
                    if (root != null) {
                        LayoutTree node = root.find(child);
                        if (node != null) {
                            node.flushCache();
                        }
                    }
                }
            }
           
            if (root != null) {
                //root.flushChildren();
                if (!isZoomed()) {
                    root.createControl(parent);
                }
            }
           
            resizeSashes();
        } else {
            DragUtil.removeDragTarget(parent, this);
            DragUtil.removeDragTarget(parent.getShell(), this);

            // remove all Listeners
            if (resizeListener != null && parent != null) {
                parent.removeControlListener(resizeListener);
            }

            if (children != null) {
                for (int i = 0, length = children.size(); i < length; i++) {
                    LayoutPart child = (LayoutPart) children.get(i);
                    child.setContainer(null);
                    if (child instanceof PartStack) {
                        child.setVisible(false);
                    }
                }
            }
           
            disposeSashes();
           
            //dispose();
        }
    }
   
    /**
     * @see LayoutPart#getControl
     */
    public void createControl(Composite parentWidget) {
        if (this.parent != null) {
      return;
    }

        parent = createParent(parentWidget);

        ArrayList children = (ArrayList) this.children.clone();
        for (int i = 0, length = children.size(); i < length; i++) {
            LayoutPart child = (LayoutPart) children.get(i);
            child.createControl(parent);
        }

    }

    /**
     * Subclasses override this method to specify
     * the composite to use to parent all children
     * layout parts it contains.
     */
    protected abstract Composite createParent(Composite parentWidget);

    /**
     * @see LayoutPart#dispose
     */
    public void dispose() {
        if (parent == null) {
      return;
    }

        if (children != null) {
            for (int i = 0, length = children.size(); i < length; i++) {
                LayoutPart child = (LayoutPart) children.get(i);

                // In PartSashContainer dispose really means deactivate, so we
                // only dispose PartTabFolders.
                if (child instanceof PartStack) {
          child.dispose();
        }
            }
        }
        disposeParent();
        this.parent = null;
    }

    /**
     * Subclasses override this method to dispose
     * of any swt resources created during createParent.
     */
    protected abstract void disposeParent();

    /**
     * Dispose all sashs used in this perspective.
     */
    public void disposeSashes() {
        if (root != null) {
            root.disposeSashes();
        }
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.LayoutPart#setVisible(boolean)
     */
    public void setVisible(boolean makeVisible) {
       
        if (makeVisible == getVisible()) {
            return;
        }
       
        if (!SwtUtil.isDisposed(this.parent)) {
            this.parent.setEnabled(makeVisible);
        }
        super.setVisible(makeVisible);
       
        ArrayList children = (ArrayList) this.children.clone();
        for (int i = 0, length = children.size(); i < length; i++) {
            LayoutPart child = (LayoutPart) children.get(i);
            child.setVisible(makeVisible && (zoomedPart == null || child == zoomedPart));
        }
    }
   
    /**
     * Return the most bottom right part or null if none.
     */
    public LayoutPart findBottomRight() {
        if (root == null) {
      return null;
    }
        return root.findBottomRight();
    }

    /**
     * @see LayoutPart#getBounds
     */
    public Rectangle getBounds() {
        return this.parent.getBounds();
    }

    /**
     * @see ILayoutContainer#getChildren
     */
    public LayoutPart[] getChildren() {
        LayoutPart[] result = new LayoutPart[children.size()];
        children.toArray(result);
        return result;
    }

    /**
     * @see LayoutPart#getControl
     */
    public Control getControl() {
        return this.parent;
    }

    public LayoutTree getLayoutTree() {
        return root;
    }

    /**
     * For themes.
     *
     * @return the current WorkbenchPage.
     */
    public WorkbenchPage getPage() {
        return page;
    }

    /**
     * Returns the composite used to parent all the
     * layout parts contained within.
     */
    public Composite getParent() {
        return parent;
    }

    protected boolean isChild(LayoutPart part) {
        return children.indexOf(part) >= 0;
    }

    /**
     * Returns whether this container is zoomed.
     */
    public boolean isZoomed() {
        return (zoomedPart != null);
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.LayoutPart#forceLayout(org.eclipse.ui.internal.LayoutPart)
     */
    public void resizeChild(LayoutPart childThatChanged) {
      if (root != null) {
        LayoutTree tree = root.find(childThatChanged);
       
        if (tree != null) {
          tree.flushCache();
        }
      }
     
        flushLayout();

    }

    /**
     * Remove a part.
     */
    public void remove(LayoutPart child) {
        if (child == getZoomedPart()) {
      childRequestZoomOut();
    }

        if (!isChild(child)) {
      return;
    }

        children.remove(child);
        if (root != null) {
            root = root.remove(child);
        }
        childRemoved(child);

        if (active) {
            child.setVisible(false);
            child.setContainer(null);
            flushLayout();
        }
    }

  /* (non-Javadoc)
   * @see org.eclipse.ui.internal.LayoutPart#forceLayout()
   */
  public void flushLayout() {
    layoutDirty = true;
    super.flushLayout();
   
    if (layoutDirty) {
      resizeSashes();
    }
  }
 
    /**
     * Replace one part with another.
     */
    public void replace(LayoutPart oldChild, LayoutPart newChild) {

        if (!isChild(oldChild)) {
            return;
        }
       
        if (oldChild == getZoomedPart()) {
            if (newChild instanceof PartPlaceholder) {
                childRequestZoomOut();
            } else {
                zoomedPart.setZoomed(false);
                zoomedPart = newChild;
                zoomedPart.setZoomed(true);
            }
        }

        children.remove(oldChild);
        children.add(newChild);

        childAdded(newChild);

        if (root != null) {
            LayoutTree leaf = null;

            leaf = root.find(oldChild);
            if (leaf != null) {
                leaf.setPart(newChild);
            }
        }

        childRemoved(oldChild);
        if (active) {
            oldChild.setVisible(false);
            oldChild.setContainer(null);
            newChild.createControl(parent);
            newChild.setContainer(this);
            newChild.setVisible(zoomedPart == null || zoomedPart == newChild);
            resizeChild(newChild);
        }
    }

    private void resizeSashes() {
      layoutDirty = false;
        if (!active) {
      return;
    }
       
        if (isZoomed()) {
            getZoomedPart().setBounds(parent.getClientArea());
        } else {
          if (root != null) {
              root.setBounds(parent.getClientArea());
          }
        }
    }

    /**
     * Returns the maximum size that can be utilized by this part if the given width and
     * height are available. Parts can overload this if they have a quantized set of preferred
     * sizes.
     *
     * @param width available horizontal space (pixels)
     * @return returns a new point where point.x is <= availableWidth and point.y is <= availableHeight
     */
    public int computePreferredSize(boolean width, int availableParallel, int availablePerpendicular, int preferredParallel) {
        if (isZoomed()) {
            return getZoomedPart().computePreferredSize(width, availableParallel, availablePerpendicular, preferredParallel);
        }
       
      if (root != null) {
        return root.computePreferredSize(width, availableParallel, availablePerpendicular, preferredParallel);
      }
           
      return preferredParallel;
    }
 
    public int getSizeFlags(boolean width) {
        if (isZoomed()) {
            return getZoomedPart().getSizeFlags(width);
        }
       
        if (root != null) {
            return root.getSizeFlags(width);
        }
       
        return 0;
    }
   
    /**
     * @see LayoutPart#setBounds
     */
    public void setBounds(Rectangle r) {
        this.parent.setBounds(r);
    }
   
    /**
     * Zoom in on a particular layout part.
     *
     * The implementation of zoom is quite simple.  When zoom occurs we create
     * a zoom root which only contains the zoom part.  We store the old
     * root in unzoomRoot and then active the zoom root.  When unzoom occurs
     * we restore the unzoomRoot and dispose the zoom root.
     *
     * Note: Method assumes we are active.
     */
    private void zoomIn(LayoutPart part) {
        // Sanity check.
        if (isZoomed()) {
      return;
    }

        // Hide the sashes
       root.disposeSashes();
       
        // Make all parts invisible except for the zoomed part
        LayoutPart[] children = getChildren();
        for (int i = 0; i < children.length; i++) {
            LayoutPart child = children[i];
            child.setVisible(child == part);
        }
       
        zoomedPart = part;
               
        // Notify the part that it has been zoomed
      part.setZoomed(true);
       
        // Remember that we need to trigger a layout
        layoutDirty = true;
    }

    /**
     * Returns the currently zoomed part or null if none
     *
     * @return the currently zoomed part or null if none
     * @since 3.1
     */
    public LayoutPart getZoomedPart() {
        return zoomedPart;
    }
   
    public void childRequestZoomIn(LayoutPart toZoom) {
        if (!SwtUtil.isDisposed(this.parent)) {
            this.parent.setRedraw(false);
        }
        try {
          zoomIn(toZoom);
         
          requestZoomIn();
         
          if (layoutDirty) {
              resizeSashes();
          }
        } finally {
            if (!SwtUtil.isDisposed(this.parent)) {
                this.parent.setRedraw(true);
            }
        }
    }
   
    public void childRequestZoomOut() {
        if (!SwtUtil.isDisposed(this.parent)) {
            this.parent.setRedraw(false);
        }
        try {
          zoomOut();
         
          requestZoomOut();
         
          if (layoutDirty) {
              resizeSashes();
          }
        } finally {
            if (!SwtUtil.isDisposed(this.parent)) {
                this.parent.setRedraw(true);
            }
        }
    }
   
    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.LayoutPart#setZoomed(boolean)
     */
    public void setZoomed(boolean isZoomed) {
        if (!isZoomed) {
            zoomOut();
        } else {
            if (!isZoomed()) {
                LayoutPart toZoom = pickPartToZoom();
               
                if (toZoom != null) {
                    zoomIn(toZoom);
                }
            }
        }
        super.setZoomed(isZoomed);
    }
   
    public LayoutPart pickPartToZoom() {
        return findBottomRight();
    }
   
    /**
     * Zoom out.
     *
     * See zoomIn for implementation details.
     *
     * Note: Method assumes we are active.
     */
    private void zoomOut() {     
        // Sanity check.
        if (!isZoomed()) {
      return;
    }
              
        LayoutPart zoomedPart = this.zoomedPart;
        this.zoomedPart = null;
        // Inform the part that it is no longer zoomed
        zoomedPart.setZoomed(false);
       
        // Make all children visible
        LayoutPart[] children = getChildren();
        for (int i = 0; i < children.length; i++) {
            LayoutPart child = children[i];
           
            child.setVisible(true);
        }
       
        // Recreate the sashes
        root.createControl(getParent());
       
        // Ensure that the part being un-zoomed will have its size refreshed.
        LayoutTree node = root.find(zoomedPart);
        node.flushCache();

        layoutDirty = true;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.dnd.IDragOverListener#drag(org.eclipse.swt.widgets.Control, java.lang.Object, org.eclipse.swt.graphics.Point, org.eclipse.swt.graphics.Rectangle)
     */
    public IDropTarget drag(Control currentControl, Object draggedObject,
            Point position, Rectangle dragRectangle) {
        if (!(draggedObject instanceof LayoutPart)) {
            return null;
        }

        final LayoutPart sourcePart = (LayoutPart) draggedObject;

        if (!isStackType(sourcePart) && !isPaneType(sourcePart)) {
            return null;
        }

        boolean differentWindows = sourcePart.getWorkbenchWindow() != getWorkbenchWindow();
        boolean editorDropOK = ((sourcePart instanceof EditorPane) &&
                      sourcePart.getWorkbenchWindow().getWorkbench() ==
                      getWorkbenchWindow().getWorkbench());
        if (differentWindows && !editorDropOK) {
            return null;
        }

        Rectangle containerBounds = DragUtil.getDisplayBounds(parent);
        LayoutPart targetPart = null;
        ILayoutContainer sourceContainer = isStackType(sourcePart) ? (ILayoutContainer) sourcePart
                : sourcePart.getContainer();

        // If this container has no visible children
        if (getVisibleChildrenCount(this) == 0) {
            return createDropTarget(sourcePart, SWT.CENTER, SWT.CENTER, null);           
        }
       
        if (containerBounds.contains(position)) {

            if (root != null) {
                targetPart = root.findPart(parent.toControl(position));
            }

            if (targetPart != null) {
                final Control targetControl = targetPart.getControl();

                final Rectangle targetBounds = DragUtil
                        .getDisplayBounds(targetControl);

                int side = Geometry.getClosestSide(targetBounds, position);
                int distance = Geometry.getDistanceFromEdge(targetBounds, position, side);
              
                // is the source coming from a standalone part
                boolean standalone = (isStackType(sourcePart)
                    && ((PartStack) sourcePart).isStandalone())
                  || (isPaneType(sourcePart)
                      && ((PartPane) sourcePart).getStack()!=null
                      && ((PartPane) sourcePart).getStack().isStandalone());
               
                // Only allow dropping onto an existing stack from different windows
                if (differentWindows && targetPart instanceof EditorStack) {
                    IDropTarget target = targetPart.getDropTarget(draggedObject, position);
                     return target;
                }
               
                // Reserve the 5 pixels around the edge of the part for the drop-on-edge cursor
                if (distance >= 5 && !standalone) {
                    // Otherwise, ask the part if it has any special meaning for this drop location
                    IDropTarget target = targetPart.getDropTarget(draggedObject, position);
                    if (target != null) {
                        return target;
                    }
                }
               
                if (distance > 30 && isStackType(targetPart) && !standalone) {
                    if (targetPart instanceof ILayoutContainer) {
                        ILayoutContainer targetContainer = (ILayoutContainer)targetPart;
                        if (targetContainer.allowsAdd(sourcePart)) {
                            side = SWT.CENTER;
                        }
                    }
                }
               
                // If the part doesn't want to override this drop location then drop on the edge
               
                // A "pointless drop" would be one that will put the dragged object back where it started.
                // Note that it should be perfectly valid to drag an object back to where it came from -- however,
                // the drop should be ignored.

                boolean pointlessDrop = isZoomed();

                if (sourcePart == targetPart) {
                    pointlessDrop = true;
                }

                if ((sourceContainer != null)
                        && (sourceContainer == targetPart)
                        && getVisibleChildrenCount(sourceContainer) <= 1) {
                    pointlessDrop = true;
                }

                if (side == SWT.CENTER
                        && sourcePart.getContainer() == targetPart) {
                    pointlessDrop = true;
                }

                int cursor = side;

                if (pointlessDrop) {
                    side = SWT.NONE;
                    cursor = SWT.CENTER;
                }

                return createDropTarget(sourcePart, side, cursor, targetPart);
            }
        } else {
          // We only allow dropping into a stack, not creating one
          if (differentWindows)
            return null;
         
            int side = Geometry.getClosestSide(containerBounds, position);

            boolean pointlessDrop = isZoomed();

            if ((isStackType(sourcePart) && sourcePart.getContainer() == this)
                    || (sourcePart.getContainer() != null
                       && isPaneType(sourcePart)
                       && getVisibleChildrenCount(sourcePart.getContainer()) <= 1)
                       && ((LayoutPart)sourcePart.getContainer()).getContainer() == this) {
                if (root == null || getVisibleChildrenCount(this) <= 1) {
                    pointlessDrop = true;
                }
            }

            int cursor = Geometry.getOppositeSide(side);

            if (pointlessDrop) {
                side = SWT.NONE;
            }

            return createDropTarget(sourcePart, side, cursor, null);
        }

        return null;
    }

    /**
     * @param sourcePart
     * @param targetPart
     * @param side
     * @param cursor
     * @return
     * @since 3.1
     */
    private SashContainerDropTarget createDropTarget(final LayoutPart sourcePart, int side, int cursor, LayoutPart targetPart) {
        if (dropTarget == null) {
            dropTarget = new SashContainerDropTarget(sourcePart, side, cursor,
                targetPart);
        } else {
            dropTarget.setTarget(sourcePart, side, cursor, targetPart);
        }
        return dropTarget;
    }

    /**
     * Returns true iff this PartSashContainer allows its parts to be stacked onto the given
     * container.
     *
     * @param container
     * @return
     */
    public abstract boolean isStackType(LayoutPart toTest);

    public abstract boolean isPaneType(LayoutPart toTest);

    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.PartSashContainer#dropObject(org.eclipse.ui.internal.LayoutPart, org.eclipse.ui.internal.LayoutPart, int)
     */
    protected void dropObject(PartPane[] toDrop, LayoutPart visiblePart,
            LayoutPart targetPart, int side) {
        getControl().setRedraw(false);

        // Targetpart is null if there isn't a part under the cursor (all the parts are
        // hidden or the container is empty). In this case, the actual side doesn't really
        // since we'll be the only visible container and will fill the entire space. However,
        // we can't leave it as SWT.CENTER since we can't stack if we don't have something
        // to stack on. In this case, we pick SWT.BOTTOM -- this will insert the new pane
        // below any currently-hidden parts.
        if (targetPart == null && side == SWT.CENTER) {
            side = SWT.BOTTOM;
        }
       
        if (side == SWT.CENTER) {
            if (isStackType(targetPart)) {
                PartStack stack = (PartStack) targetPart;
                for (int idx = 0; idx < toDrop.length; idx++) {
                    PartPane next = toDrop[idx];
                    stack(next, stack);
                }
            }
        } else {
            PartStack newPart = createStack();

            // if the toDrop array has 1 item propogate the stack
            // appearance
            if (toDrop.length == 1 && toDrop[0].getStack()!=null) {
                toDrop[0].getStack().copyAppearanceProperties(newPart);               
            }
           
            for (int idx = 0; idx < toDrop.length; idx++) {
                PartPane next = toDrop[idx];
                stack(next, newPart);
            }

            addEnhanced(newPart, side, getDockingRatio(newPart, targetPart),
                    targetPart);
        }

        if (visiblePart != null) {
            setVisiblePart(visiblePart.getContainer(), visiblePart);
        }

        getControl().setRedraw(true);
       
        if (visiblePart != null) {
            visiblePart.setFocus();
        }
    }

    protected abstract PartStack createStack();

    public void stack(LayoutPart newPart, ILayoutContainer container) {
        getControl().setRedraw(false);

    // Only deref the part if it is being referenced in -this- perspective
        Perspective persp = page.getActivePerspective();
        PerspectiveHelper pres = (persp != null) ? persp.getPresentation() : null;
      if (pres != null && newPart instanceof ViewPane) {
        ViewPane vp = (ViewPane) newPart;
        IViewReference vRef = vp.getViewReference();
        LayoutPart fpp = pres.findPart(vRef.getId(), vRef.getSecondaryId());
       
        if (fpp != null) {
              // Remove the part from old container.
              derefPart(newPart);
        }
      }
      else {
          // Remove the part from old container.
          derefPart(newPart);
      }
     
        // Reparent part and add it to the workbook
        newPart.reparent(getParent());
        container.add(newPart);
        getControl().setRedraw(true);

    }

    /**
     * @param container
     * @param visiblePart
     */
    protected abstract void setVisiblePart(ILayoutContainer container,
            LayoutPart visiblePart);

    /**
     * @param container
     * @return
     */
    protected abstract LayoutPart getVisiblePart(ILayoutContainer container);

    /**
     * @param sourcePart
     */
    protected void derefPart(LayoutPart sourcePart) {
        ILayoutContainer container = sourcePart.getContainer();
        if (container != null) {
            container.remove(sourcePart);
        }

        if (container instanceof LayoutPart) {
            if (isStackType((LayoutPart) container)) {
                PartStack stack = (PartStack) container;
                if (stack.getChildren().length == 0) {
                    remove(stack);
                    stack.dispose();
                }
            }
        }
    }

    protected int getVisibleChildrenCount(ILayoutContainer container) {
        // Treat null as an empty container
        if (container == null) {
            return 0;
        }

        LayoutPart[] children = container.getChildren();

        int count = 0;
        for (int idx = 0; idx < children.length; idx++) {
            if (!(children[idx] instanceof PartPlaceholder)) {
                count++;
            }
        }

        return count;
    }

    protected float getDockingRatio(LayoutPart dragged, LayoutPart target) {
        return 0.5f;
    }

    /**
     * Writes a description of the layout to the given string buffer.
     * This is used for drag-drop test suites to determine if two layouts are the
     * same. Like a hash code, the description should compare as equal iff the
     * layouts are the same. However, it should be user-readable in order to
     * help debug failed tests. Although these are english readable strings,
     * they should not be translated or equality tests will fail.
     *
     * @param buf
     */
    public void describeLayout(StringBuffer buf) {
        if (root == null) {
            return;
        }

        if (isZoomed()) {
            buf.append("zoomed "); //$NON-NLS-1$
            root.describeLayout(buf);
        } else {
            buf.append("layout "); //$NON-NLS-1$
            root.describeLayout(buf);
        }
    }

    /**
     * Adds a new child to the container relative to some part
     *
     * @param child
     * @param relationship
     * @param left preferred pixel size of the left/top child
     * @param right preferred pixel size of the right/bottom child
     * @param relative relative part
     */
    void add(LayoutPart child, int relationship, int left, int right,
            LayoutPart relative) {

        if (child == null) {
      return;
    }
        if (relative != null && !isChild(relative)) {
      return;
    }
        if (relationship < IPageLayout.LEFT
                || relationship > IPageLayout.BOTTOM) {
      relationship = IPageLayout.LEFT;
    }

        // store info about relative positions
        RelationshipInfo info = new RelationshipInfo();
        info.part = child;
        info.relationship = relationship;
        info.left = left;
        info.right = right;
        info.relative = relative;
        addChild(info);
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.ILayoutContainer#isZoomed(org.eclipse.ui.internal.LayoutPart)
     */
    public boolean childIsZoomed(LayoutPart toTest) {
        return toTest == getZoomedPart();
    }
   
    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.ILayoutContainer#allowsAutoFocus()
     */
    public boolean allowsAutoFocus() {
        return true;
    }
   
    /* (non-Javadoc)
   * @see org.eclipse.ui.internal.LayoutPart#startDeferringEvents()
   */
  protected void startDeferringEvents() {
    super.startDeferringEvents();
   
    LayoutPart[] deferredChildren = (LayoutPart[]) children.toArray(new LayoutPart[children.size()]);
    for (int i = 0; i < deferredChildren.length; i++) {
      LayoutPart child = deferredChildren[i];
     
      child.deferUpdates(true);
    }
  }
   
  /* (non-Javadoc)
   * @see org.eclipse.ui.internal.LayoutPart#handleDeferredEvents()
   */
  protected void handleDeferredEvents() {
    super.handleDeferredEvents();
   
    LayoutPart[] deferredChildren = (LayoutPart[]) children.toArray(new LayoutPart[children.size()]);
    for (int i = 0; i < deferredChildren.length; i++) {
      LayoutPart child = deferredChildren[i];
   
      child.deferUpdates(false);
    }     
  }
   
    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.LayoutPart#testInvariants()
     */
    public void testInvariants() {
        super.testInvariants();

        // If we have a parent container, ensure that we are displaying the zoomed appearance iff
        // our parent is zoomed in on us
        if (getContainer() != null) {
            Assert.isTrue((getZoomedPart() != null) == (getContainer().childIsZoomed(this)));
        }
       
        LayoutPart[] childArray = getChildren();

        for (int idx = 0; idx < childArray.length; idx++) {
            childArray[idx].testInvariants();
        }
       
        // If we're zoomed, ensure that we're actually zoomed into one of our children
        if (isZoomed()) {
            Assert.isTrue(children.contains(zoomedPart));
        }
    }
}
TOP

Related Classes of org.eclipse.ui.internal.PartSashContainer

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.