| /******************************************************************************* |
| * Copyright (c) 2000, 2008 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); |
| } |
| |
| // Ensure we don't get a 'divide by zero' |
| if ((left+right) == 0) |
| return 0; |
| |
| // 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)); |
| } |
| } |
| } |