blob: 6006b03d891119f496481588411770b65318d5d1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2009 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.
*/
// RAP [rh] IDragOverListener disabled due to missing DnD support
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();
// RAP [rh] unused code: SashContainerDropTarget is disabled
// 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;
}
}
// RAP [rh] Disabled AbstractDropTarget due to missing DnD support
// 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();
}
// RAP [rh] unused code: was used by SashContainerDropTarget
// /**
// * 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
*/
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);
// RAP [rh] DnD support missing
// 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 {
// RAP [rh] DnD support missing
// 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
*/
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;
}
// RAP [rh] Disabled implementation of IDragOverListener#drag()
// /* (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;
// }
// RAP [rh] DnD support missing
// /**
// * @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());
// 'findPart' won't find fast views that don't exist in the main presentation
if (fpp != null || persp.isFastView(vRef)) {
// 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));
}
}
}